[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

   1  <?php
   2  
   3  /**
   4   * Icons API: WP_Icons_Registry class
   5   *
   6   * @package WordPress
   7   * @since 7.0.0
   8   */
   9  
  10  /**
  11   * Core class used for interacting with registered icons.
  12   *
  13   * @since 7.0.0
  14   */
  15  class WP_Icons_Registry {
  16      /**
  17       * Registered icons array.
  18       *
  19       * @since 7.0.0
  20       * @var array[]
  21       */
  22      protected $registered_icons = array();
  23  
  24      /**
  25       * Container for the main instance of the class.
  26       *
  27       * @since 7.0.0
  28       * @var WP_Icons_Registry|null
  29       */
  30      protected static $instance = null;
  31  
  32      /**
  33       * Constructor.
  34       *
  35       * WP_Icons_Registry is a singleton class, so keep this protected.
  36       *
  37       * For 7.0, the Icons Registry is closed for third-party icon registry,
  38       * serving only a subset of core icons.
  39       *
  40       * These icons are defined in @wordpress/packages (Gutenberg repository) as
  41       * SVG files and as entries in a single manifest file. On init, the
  42       * registry is loaded with those icons listed in the manifest.
  43       *
  44       * @since 7.0.0
  45       */
  46  	protected function __construct() {
  47          $icons_directory = __DIR__ . '/images/icon-library/';
  48          $manifest_path   = __DIR__ . '/assets/icon-library-manifest.php';
  49  
  50          if ( ! is_readable( $manifest_path ) ) {
  51              wp_trigger_error(
  52                  __METHOD__,
  53                  __( 'Core icon collection manifest is missing or unreadable.' )
  54              );
  55              return;
  56          }
  57  
  58          $collection = include $manifest_path;
  59  
  60          if ( empty( $collection ) ) {
  61              wp_trigger_error(
  62                  __METHOD__,
  63                  __( 'Core icon collection manifest is empty or invalid.' )
  64              );
  65              return;
  66          }
  67  
  68          foreach ( $collection as $icon_name => $icon_data ) {
  69              if (
  70                  empty( $icon_data['filePath'] )
  71                  || ! is_string( $icon_data['filePath'] )
  72              ) {
  73                  _doing_it_wrong(
  74                      __METHOD__,
  75                      __( 'Core icon collection manifest must provide valid a "filePath" for each icon.' ),
  76                      '7.0.0'
  77                  );
  78                  return;
  79              }
  80  
  81              $this->register(
  82                  'core/' . $icon_name,
  83                  array(
  84                      'label'     => $icon_data['label'],
  85                      'file_path' => $icons_directory . $icon_data['filePath'],
  86                  )
  87              );
  88          }
  89      }
  90  
  91      /**
  92       * Registers an icon.
  93       *
  94       * @since 7.0.0
  95       *
  96       * @param string $icon_name       Icon name including namespace.
  97       * @param array  $icon_properties {
  98       *     List of properties for the icon.
  99       *
 100       *     @type string $label     Required. A human-readable label for the icon.
 101       *     @type string $content   Optional. SVG markup for the icon.
 102       *                             If not provided, the content will be retrieved from the `file_path` if set.
 103       *                             If both `content` and `file_path` are not set, the icon will not be registered.
 104       *     @type string $file_path Optional. The full path to the file containing the icon content.
 105       * }
 106       * @return bool True if the icon was registered with success and false otherwise.
 107       */
 108  	protected function register( $icon_name, $icon_properties ) {
 109          if ( ! isset( $icon_name ) || ! is_string( $icon_name ) ) {
 110              _doing_it_wrong(
 111                  __METHOD__,
 112                  __( 'Icon name must be a string.' ),
 113                  '7.0.0'
 114              );
 115              return false;
 116          }
 117  
 118          if ( preg_match( '/[A-Z]/', $icon_name ) ) {
 119              _doing_it_wrong(
 120                  __METHOD__,
 121                  __( 'Icon names must not contain uppercase characters.' ),
 122                  '7.1.0'
 123              );
 124              return false;
 125          }
 126  
 127          $name_matcher = '/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/';
 128          if ( ! preg_match( $name_matcher, $icon_name ) ) {
 129              _doing_it_wrong(
 130                  __METHOD__,
 131                  __( 'Icon names must contain a namespace prefix. Example: my-plugin/my-custom-icon' ),
 132                  '7.1.0'
 133              );
 134              return false;
 135          }
 136  
 137          if ( $this->is_registered( $icon_name ) ) {
 138              _doing_it_wrong(
 139                  __METHOD__,
 140                  __( 'Icon is already registered.' ),
 141                  '7.1.0'
 142              );
 143              return false;
 144          }
 145  
 146          $allowed_keys = array_fill_keys( array( 'label', 'content', 'file_path' ), 1 );
 147          foreach ( array_keys( $icon_properties ) as $key ) {
 148              if ( ! array_key_exists( $key, $allowed_keys ) ) {
 149                  _doing_it_wrong(
 150                      __METHOD__,
 151                      sprintf(
 152                          // translators: %s is the name of any user-provided key
 153                          __( 'Invalid icon property: "%s".' ),
 154                          $key
 155                      ),
 156                      '7.0.0'
 157                  );
 158                  return false;
 159              }
 160          }
 161  
 162          if ( ! isset( $icon_properties['label'] ) || ! is_string( $icon_properties['label'] ) ) {
 163              _doing_it_wrong(
 164                  __METHOD__,
 165                  __( 'Icon label must be a string.' ),
 166                  '7.0.0'
 167              );
 168              return false;
 169          }
 170  
 171          if (
 172              ( ! isset( $icon_properties['content'] ) && ! isset( $icon_properties['file_path'] ) ) ||
 173              ( isset( $icon_properties['content'] ) && isset( $icon_properties['file_path'] ) )
 174          ) {
 175              _doing_it_wrong(
 176                  __METHOD__,
 177                  __( 'Icons must provide either `content` or `file_path`.' ),
 178                  '7.0.0'
 179              );
 180              return false;
 181          }
 182  
 183          if ( isset( $icon_properties['content'] ) ) {
 184              if ( ! is_string( $icon_properties['content'] ) ) {
 185                  _doing_it_wrong(
 186                      __METHOD__,
 187                      __( 'Icon content must be a string.' ),
 188                      '7.0.0'
 189                  );
 190                  return false;
 191              }
 192  
 193              $sanitized_icon_content = $this->sanitize_icon_content( $icon_properties['content'] );
 194              if ( empty( $sanitized_icon_content ) ) {
 195                  _doing_it_wrong(
 196                      __METHOD__,
 197                      __( 'Icon content does not contain valid SVG markup.' ),
 198                      '7.0.0'
 199                  );
 200                  return false;
 201              }
 202          }
 203  
 204          $icon = array_merge(
 205              $icon_properties,
 206              array( 'name' => $icon_name )
 207          );
 208  
 209          $this->registered_icons[ $icon_name ] = $icon;
 210  
 211          return true;
 212      }
 213  
 214      /**
 215       * Sanitizes the icon SVG content.
 216       *
 217       * Logic borrowed from twentytwenty.
 218       * @see twentytwenty_get_theme_svg
 219       *
 220       * @since 7.0.0
 221       *
 222       * @param string $icon_content The icon SVG content to sanitize.
 223       * @return string The sanitized icon SVG content.
 224       */
 225  	protected function sanitize_icon_content( $icon_content ) {
 226          $allowed_tags = array(
 227              'svg'     => array(
 228                  'class'       => true,
 229                  'xmlns'       => true,
 230                  'width'       => true,
 231                  'height'      => true,
 232                  'viewbox'     => true,
 233                  'aria-hidden' => true,
 234                  'role'        => true,
 235                  'focusable'   => true,
 236              ),
 237              'path'    => array(
 238                  'fill'      => true,
 239                  'fill-rule' => true,
 240                  'd'         => true,
 241                  'transform' => true,
 242              ),
 243              'polygon' => array(
 244                  'fill'      => true,
 245                  'fill-rule' => true,
 246                  'points'    => true,
 247                  'transform' => true,
 248                  'focusable' => true,
 249              ),
 250          );
 251          return wp_kses( $icon_content, $allowed_tags );
 252      }
 253  
 254      /**
 255       * Retrieves the content of a registered icon.
 256       *
 257       * @since 7.0.0
 258       *
 259       * @param string $icon_name Icon name including namespace.
 260       * @return string|null The content of the icon, if found.
 261       */
 262  	protected function get_content( $icon_name ) {
 263          if ( ! isset( $this->registered_icons[ $icon_name ]['content'] ) ) {
 264              $content = file_get_contents(
 265                  $this->registered_icons[ $icon_name ]['file_path']
 266              );
 267              $content = $this->sanitize_icon_content( $content );
 268  
 269              if ( empty( $content ) ) {
 270                  wp_trigger_error(
 271                      __METHOD__,
 272                      __( 'Icon content does not contain valid SVG markup.' )
 273                  );
 274                  return null;
 275              }
 276  
 277              $this->registered_icons[ $icon_name ]['content'] = $content;
 278          }
 279          return $this->registered_icons[ $icon_name ]['content'];
 280      }
 281  
 282      /**
 283       * Retrieves an array containing the properties of a registered icon.
 284       *
 285       * @since 7.0.0
 286       *
 287       * @param string $icon_name Icon name including namespace.
 288       * @return array|null Registered icon properties or `null` if the icon is not registered.
 289       */
 290  	public function get_registered_icon( $icon_name ) {
 291          if ( ! $this->is_registered( $icon_name ) ) {
 292              return null;
 293          }
 294  
 295          $icon            = $this->registered_icons[ $icon_name ];
 296          $icon['content'] = $icon['content'] ?? $this->get_content( $icon_name );
 297  
 298          return $icon;
 299      }
 300  
 301      /**
 302       * Retrieves all registered icons.
 303       *
 304       * @since 7.0.0
 305       *
 306       * @param string $search Optional. Search term by which to filter the icons.
 307       * @return array[] Array of arrays containing the registered icon properties.
 308       */
 309  	public function get_registered_icons( $search = '' ) {
 310          $icons = array();
 311  
 312          foreach ( $this->registered_icons as $icon ) {
 313              if ( ! empty( $search )
 314                  && false === stripos( $icon['name'], $search )
 315                  && false === stripos( $icon['label'] ?? '', $search )
 316              ) {
 317                  continue;
 318              }
 319  
 320              $icon['content'] = $icon['content'] ?? $this->get_content( $icon['name'] );
 321              $icons[]         = $icon;
 322          }
 323  
 324          return $icons;
 325      }
 326  
 327      /**
 328       * Checks if an icon is registered.
 329       *
 330       * @since 7.0.0
 331       *
 332       * @param string $icon_name Icon name including namespace.
 333       * @return bool True if the icon is registered, false otherwise.
 334       */
 335  	public function is_registered( $icon_name ) {
 336          return isset( $this->registered_icons[ $icon_name ] );
 337      }
 338  
 339      /**
 340       * Utility method to retrieve the main instance of the class.
 341       *
 342       * The instance will be created if it does not exist yet.
 343       *
 344       * @since 7.0.0
 345       *
 346       * @return WP_Icons_Registry The main instance.
 347       */
 348  	public static function get_instance() {
 349          if ( null === self::$instance ) {
 350              self::$instance = new self();
 351          }
 352  
 353          return self::$instance;
 354      }
 355  }


Generated : Sat Jul 4 08:20:12 2026 Cross-referenced by PHPXref