[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ -> class-wp-block-metadata-registry.php (source)

   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  }


Generated : Thu Nov 21 08:20:01 2024 Cross-referenced by PHPXref