[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Block Metadata Registry 4 * 5 * @package WordPress 6 * @subpackage Blocks 7 * @since 6.7.0 8 */ 9 10 /** 11 * Class used for managing block metadata collections. 12 * 13 * The WP_Block_Metadata_Registry allows plugins to register metadata for large 14 * collections of blocks (e.g., 50-100+) using a single PHP file. This approach 15 * reduces the need to read and decode multiple `block.json` files, enhancing 16 * performance through opcode caching. 17 * 18 * @since 6.7.0 19 */ 20 class WP_Block_Metadata_Registry { 21 22 /** 23 * Container for storing block metadata collections. 24 * 25 * Each entry maps a base path to its corresponding metadata and callback. 26 * 27 * @since 6.7.0 28 * @var array<string, array<string, mixed>> 29 */ 30 private static $collections = array(); 31 32 /** 33 * Caches the last matched collection path for performance optimization. 34 * 35 * @since 6.7.0 36 * @var string|null 37 */ 38 private static $last_matched_collection = null; 39 40 /** 41 * Stores the WordPress 'wp-includes' directory path. 42 * 43 * @since 6.7.0 44 * @var string|null 45 */ 46 private static $wpinc_dir = null; 47 48 /** 49 * Stores the normalized WordPress plugin directory path. 50 * 51 * @since 6.7.0 52 * @var string|null 53 */ 54 private static $plugin_dir = null; 55 56 /** 57 * Registers a block metadata collection. 58 * 59 * This method allows registering a collection of block metadata from a single 60 * manifest file, improving performance for large sets of blocks. 61 * 62 * The manifest file should be a PHP file that returns an associative array, where 63 * the keys are the block identifiers (without their namespace) and the values are 64 * the corresponding block metadata arrays. The block identifiers must match the 65 * parent directory name for the respective `block.json` file. 66 * 67 * Example manifest file structure: 68 * ``` 69 * return array( 70 * 'example-block' => array( 71 * 'title' => 'Example Block', 72 * 'category' => 'widgets', 73 * 'icon' => 'smiley', 74 * // ... other block metadata 75 * ), 76 * 'another-block' => array( 77 * 'title' => 'Another Block', 78 * 'category' => 'formatting', 79 * 'icon' => 'star-filled', 80 * // ... other block metadata 81 * ), 82 * // ... more block metadata entries 83 * ); 84 * ``` 85 * 86 * @since 6.7.0 87 * 88 * @param string $path The absolute base path for the collection ( e.g., WP_PLUGIN_DIR . '/my-plugin/blocks/' ). 89 * @param string $manifest The absolute path to the manifest file containing the metadata collection. 90 * @return bool True if the collection was registered successfully, false otherwise. 91 */ 92 public static function register_collection( $path, $manifest ) { 93 $path = wp_normalize_path( rtrim( $path, '/' ) ); 94 95 $wpinc_dir = self::get_wpinc_dir(); 96 $plugin_dir = self::get_plugin_dir(); 97 98 // Check if the path is valid: 99 if ( str_starts_with( $path, $plugin_dir ) ) { 100 // For plugins, ensure the path is within a specific plugin directory and not the base plugin directory. 101 $relative_path = substr( $path, strlen( $plugin_dir ) + 1 ); 102 $plugin_name = strtok( $relative_path, '/' ); 103 104 if ( empty( $plugin_name ) || $plugin_name === $relative_path ) { 105 _doing_it_wrong( 106 __METHOD__, 107 __( 'Block metadata collections can only be registered for a specific plugin. The provided path is neither a core path nor a valid plugin path.' ), 108 '6.7.0' 109 ); 110 return false; 111 } 112 } elseif ( ! str_starts_with( $path, $wpinc_dir ) ) { 113 // If it's neither a plugin directory path nor within 'wp-includes', the path is invalid. 114 _doing_it_wrong( 115 __METHOD__, 116 __( 'Block metadata collections can only be registered for a specific plugin. The provided path is neither a core path nor a valid plugin path.' ), 117 '6.7.0' 118 ); 119 return false; 120 } 121 122 if ( ! file_exists( $manifest ) ) { 123 _doing_it_wrong( 124 __METHOD__, 125 __( 'The specified manifest file does not exist.' ), 126 '6.7.0' 127 ); 128 return false; 129 } 130 131 self::$collections[ $path ] = array( 132 'manifest' => $manifest, 133 'metadata' => null, 134 ); 135 136 return true; 137 } 138 139 /** 140 * Retrieves block metadata for a given block within a specific collection. 141 * 142 * This method uses the registered collections to efficiently lookup 143 * block metadata without reading individual `block.json` files. 144 * 145 * @since 6.7.0 146 * 147 * @param string $file_or_folder The path to the file or folder containing the block. 148 * @return array|null The block metadata for the block, or null if not found. 149 */ 150 public static function get_metadata( $file_or_folder ) { 151 $path = self::find_collection_path( $file_or_folder ); 152 if ( ! $path ) { 153 return null; 154 } 155 156 $collection = &self::$collections[ $path ]; 157 158 if ( null === $collection['metadata'] ) { 159 // Load the manifest file if not already loaded 160 $collection['metadata'] = require $collection['manifest']; 161 } 162 163 // Get the block name from the path. 164 $block_name = self::default_identifier_callback( $file_or_folder ); 165 166 return isset( $collection['metadata'][ $block_name ] ) ? $collection['metadata'][ $block_name ] : null; 167 } 168 169 /** 170 * Finds the collection path for a given file or folder. 171 * 172 * @since 6.7.0 173 * 174 * @param string $file_or_folder The path to the file or folder. 175 * @return string|null The collection path if found, or null if not found. 176 */ 177 private static function find_collection_path( $file_or_folder ) { 178 if ( empty( $file_or_folder ) ) { 179 return null; 180 } 181 182 // Check the last matched collection first, since block registration usually happens in batches per plugin or theme. 183 $path = wp_normalize_path( rtrim( $file_or_folder, '/' ) ); 184 if ( self::$last_matched_collection && str_starts_with( $path, self::$last_matched_collection ) ) { 185 return self::$last_matched_collection; 186 } 187 188 $collection_paths = array_keys( self::$collections ); 189 foreach ( $collection_paths as $collection_path ) { 190 if ( str_starts_with( $path, $collection_path ) ) { 191 self::$last_matched_collection = $collection_path; 192 return $collection_path; 193 } 194 } 195 return null; 196 } 197 198 /** 199 * Checks if metadata exists for a given block name in a specific collection. 200 * 201 * @since 6.7.0 202 * 203 * @param string $file_or_folder The path to the file or folder containing the block metadata. 204 * @return bool True if metadata exists for the block, false otherwise. 205 */ 206 public static function has_metadata( $file_or_folder ) { 207 return null !== self::get_metadata( $file_or_folder ); 208 } 209 210 /** 211 * Default identifier function to determine the block identifier from a given path. 212 * 213 * This function extracts the block identifier from the path: 214 * - For 'block.json' files, it uses the parent directory name. 215 * - For directories, it uses the directory name itself. 216 * - For empty paths, it returns an empty string. 217 * 218 * For example: 219 * - Path: '/wp-content/plugins/my-plugin/blocks/example/block.json' 220 * Identifier: 'example' 221 * - Path: '/wp-content/plugins/my-plugin/blocks/another-block' 222 * Identifier: 'another-block' 223 * 224 * This default behavior matches the standard WordPress block structure. 225 * 226 * @since 6.7.0 227 * 228 * @param string $path The file or folder path to determine the block identifier from. 229 * @return string The block identifier, or an empty string if the path is empty. 230 */ 231 private static function default_identifier_callback( $path ) { 232 // Ensure $path is not empty to prevent unexpected behavior. 233 if ( empty( $path ) ) { 234 return ''; 235 } 236 237 if ( str_ends_with( $path, 'block.json' ) ) { 238 // Return the parent directory name if it's a block.json file. 239 return basename( dirname( $path ) ); 240 } 241 242 // Otherwise, assume it's a directory and return its name. 243 return basename( $path ); 244 } 245 246 /** 247 * Gets the WordPress 'wp-includes' directory path. 248 * 249 * @since 6.7.0 250 * 251 * @return string The WordPress 'wp-includes' directory path. 252 */ 253 private static function get_wpinc_dir() { 254 if ( ! isset( self::$wpinc_dir ) ) { 255 self::$wpinc_dir = wp_normalize_path( ABSPATH . WPINC ); 256 } 257 return self::$wpinc_dir; 258 } 259 260 /** 261 * Gets the normalized WordPress plugin directory path. 262 * 263 * @since 6.7.0 264 * 265 * @return string The normalized WordPress plugin directory path. 266 */ 267 private static function get_plugin_dir() { 268 if ( ! isset( self::$plugin_dir ) ) { 269 self::$plugin_dir = wp_normalize_path( WP_PLUGIN_DIR ); 270 } 271 return self::$plugin_dir; 272 } 273 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Thu Nov 21 08:20:01 2024 | Cross-referenced by PHPXref |