Commit 11e24d90c5 for wordpress.org

commit 11e24d90c5d99f12ed3e6dfad730dce04fb8cc0b
Author: wildworks <wildworks@git.wordpress.org>
Date:   Tue Sep 29 11:36:48 2026 +0000

    Icons: Support keyword-based search in the icons registry.

    Registered icons can now define optional search keywords, and icon search in the registry and the REST API matches them as well as the icon's name and label.

    Developed in: https://github.com/WordPress/wordpress-develop/pull/13672

    Props n8finch, tayyiba66, wildworks.
    Fixes #66158.

    Built from https://develop.svn.wordpress.org/trunk@63999


    git-svn-id: http://core.svn.wordpress.org/trunk@63162 1a063a9b-81f0-0310-95a4-ce76da25c4cd

diff --git a/wp-includes/class-wp-icons-registry.php b/wp-includes/class-wp-icons-registry.php
index 5a0296a920..745e369e7c 100644
--- a/wp-includes/class-wp-icons-registry.php
+++ b/wp-includes/class-wp-icons-registry.php
@@ -46,22 +46,24 @@ class WP_Icons_Registry {
 	 *
 	 * @since 7.0.0
 	 * @since 7.1.0 The icon name must be namespaced in the form "collection/icon-name".
-	 * @since 7.2.0 Added the `public` property.
+	 * @since 7.2.0 Added the `public` and `keywords` properties.
 	 *
 	 * @param string $icon_name       Namespaced icon name in the form "collection/icon-name"
 	 *                                (e.g. "core/arrow-left").
 	 * @param array  $icon_properties {
 	 *     List of properties for the icon.
 	 *
-	 *     @type string $label     Required. A human-readable label for the icon.
-	 *     @type string $content   Optional. SVG markup for the icon.
-	 *                             If not provided, the content will be retrieved from the `file_path` if set.
-	 *                             If both `content` and `file_path` are not set, the icon will not be registered.
-	 *     @type string $file_path Optional. The full path to the file containing the icon content.
-	 *     @type bool   $public    Optional. Whether the icon is exposed through the REST API, and
-	 *                             therefore selectable in the editor's icon picker. Non-public icons
-	 *                             stay available to server-side code via {@see wp_get_icon()}.
-	 *                             Default true.
+	 *     @type string   $label     Required. A human-readable label for the icon.
+	 *     @type string   $content   Optional. SVG markup for the icon.
+	 *                               If not provided, the content will be retrieved from the `file_path` if set.
+	 *                               If both `content` and `file_path` are not set, the icon will not be registered.
+	 *     @type string   $file_path Optional. The full path to the file containing the icon content.
+	 *     @type bool     $public    Optional. Whether the icon is exposed through the REST API, and
+	 *                               therefore selectable in the editor's icon picker. Non-public icons
+	 *                               stay available to server-side code via {@see wp_get_icon()}.
+	 *                               Default true.
+	 *     @type string[] $keywords  Optional. Additional search terms for the icon, matched by
+	 *                               `get_registered_icons()` alongside the name and label.
 	 * }
 	 * @return bool True if the icon was registered with success and false otherwise.
 	 */
@@ -106,7 +108,7 @@ class WP_Icons_Registry {
 			return false;
 		}

-		$allowed_keys = array_fill_keys( array( 'label', 'content', 'file_path', 'public' ), 1 );
+		$allowed_keys = array_fill_keys( array( 'label', 'content', 'file_path', 'public', 'keywords' ), 1 );
 		foreach ( array_keys( $icon_properties ) as $key ) {
 			if ( ! array_key_exists( $key, $allowed_keys ) ) {
 				_doing_it_wrong(
@@ -153,6 +155,28 @@ class WP_Icons_Registry {
 			return false;
 		}

+		if ( array_key_exists( 'keywords', $icon_properties ) ) {
+			if ( ! is_array( $icon_properties['keywords'] ) ) {
+				_doing_it_wrong(
+					__METHOD__,
+					__( 'Icon keywords must be an array of strings.' ),
+					'7.2.0'
+				);
+				return false;
+			}
+
+			foreach ( $icon_properties['keywords'] as $keyword ) {
+				if ( ! is_string( $keyword ) ) {
+					_doing_it_wrong(
+						__METHOD__,
+						__( 'Icon keywords must be an array of strings.' ),
+						'7.2.0'
+					);
+					return false;
+				}
+			}
+		}
+
 		if (
 			( ! isset( $icon_properties['content'] ) && ! isset( $icon_properties['file_path'] ) ) ||
 			( isset( $icon_properties['content'] ) && isset( $icon_properties['file_path'] ) )
@@ -401,23 +425,53 @@ class WP_Icons_Registry {
 		return $icon;
 	}

+	/**
+	 * Determines whether an icon matches a search term.
+	 *
+	 * The term is matched case-insensitively against the icon's name, its label,
+	 * and any of its keywords.
+	 *
+	 * @since 7.2.0
+	 *
+	 * @param array  $icon   Registered icon properties.
+	 * @param string $search Search term.
+	 * @return bool True if the icon matches the search term, false otherwise.
+	 */
+	protected function icon_matches_search( $icon, $search ) {
+		if ( false !== stripos( $icon['name'], $search ) ) {
+			return true;
+		}
+
+		if ( false !== stripos( $icon['label'], $search ) ) {
+			return true;
+		}
+
+		foreach ( $icon['keywords'] ?? array() as $keyword ) {
+			if ( false !== stripos( $keyword, $search ) ) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
 	/**
 	 * Retrieves all registered icons.
 	 *
 	 * @since 7.0.0
 	 * @since 7.1.0 Search also matches icon labels.
+	 * @since 7.2.0 Search also matches icon keywords.
 	 *
-	 * @param string $search Optional. Search term by which to filter the icons.
+	 * @param string $search Optional. Search term matched against each icon's name,
+	 *                       label, and keywords. Default empty string, which returns
+	 *                       every registered icon.
 	 * @return array[] Array of arrays containing the registered icon properties.
 	 */
 	public function get_registered_icons( $search = '' ) {
 		$icons = array();

 		foreach ( $this->registered_icons as $icon ) {
-			if ( ! empty( $search )
-				&& false === stripos( $icon['name'], $search )
-				&& false === stripos( $icon['label'] ?? '', $search )
-			) {
+			if ( ! empty( $search ) && ! $this->icon_matches_search( $icon, $search ) ) {
 				continue;
 			}

diff --git a/wp-includes/icons.php b/wp-includes/icons.php
index 4c906ad939..e6916aaed1 100644
--- a/wp-includes/icons.php
+++ b/wp-includes/icons.php
@@ -41,7 +41,7 @@ function wp_unregister_icon_collection( $slug ) {
  * Registers a new icon.
  *
  * @since 7.1.0
- * @since 7.2.0 Added the `public` property.
+ * @since 7.2.0 Added the `public` and `keywords` properties.
  *
  * @param string $icon_name Namespaced icon name in the form "collection/icon-name"
  *                          (e.g. "my-plugin/arrow-left"). The "core" collection is
@@ -51,15 +51,17 @@ function wp_unregister_icon_collection( $slug ) {
  * @param array  $args      {
  *     List of properties for the icon.
  *
- *     @type string $label     Required. A human-readable label for the icon.
- *     @type string $content   Optional. SVG markup for the icon.
- *                             If not provided, the content will be retrieved from the `file_path` if set.
- *                             If both `content` and `file_path` are not set, the icon will not be registered.
- *     @type string $file_path Optional. The full path to the file containing the icon content.
- *     @type bool   $public    Optional. Whether the icon is exposed through the REST API, and
- *                             therefore selectable in the editor's icon picker. Non-public icons
- *                             stay available to server-side code via {@see wp_get_icon()}.
- *                             Default true.
+ *     @type string   $label     Required. A human-readable label for the icon.
+ *     @type string   $content   Optional. SVG markup for the icon.
+ *                               If not provided, the content will be retrieved from the `file_path` if set.
+ *                               If both `content` and `file_path` are not set, the icon will not be registered.
+ *     @type string   $file_path Optional. The full path to the file containing the icon content.
+ *     @type bool     $public    Optional. Whether the icon is exposed through the REST API, and
+ *                               therefore selectable in the editor's icon picker. Non-public icons
+ *                               stay available to server-side code via {@see wp_get_icon()}.
+ *                               Default true.
+ *     @type string[] $keywords  Optional. Additional search terms for the icon, matched by
+ *                               `get_registered_icons()` alongside the name and label.
  * }
  * @return bool True if the icon was registered successfully, else false.
  */
@@ -146,6 +148,10 @@ function _wp_register_default_icons() {
 			$icon_args['public'] = $icon_data['public'];
 		}

+		if ( isset( $icon_data['keywords'] ) ) {
+			$icon_args['keywords'] = $icon_data['keywords'];
+		}
+
 		wp_register_icon( 'core/' . $icon_name, $icon_args );
 	}
 }
diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-icons-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-icons-controller.php
index 4578df9d28..610cc58230 100644
--- a/wp-includes/rest-api/endpoints/class-wp-rest-icons-controller.php
+++ b/wp-includes/rest-api/endpoints/class-wp-rest-icons-controller.php
@@ -229,8 +229,12 @@ class WP_REST_Icons_Controller extends WP_REST_Controller {
 	/**
 	 * Prepare a raw icon before it gets output in a REST API response.
 	 *
+	 * Adds `collection` and `keywords` fields to the base response while keeping
+	 * the namespaced icon name (e.g. `core/arrow-left`) as the `name` field.
+	 *
 	 * @since 7.0.0
 	 * @since 7.1.0 Added the `collection` field.
+	 * @since 7.2.0 Added the `keywords` field.
 	 *
 	 * @param array           $item    Raw icon as registered, before any changes.
 	 * @param WP_REST_Request $request Request object.
@@ -251,6 +255,14 @@ class WP_REST_Icons_Controller extends WP_REST_Controller {
 			}
 		}

+		/*
+		 * Keywords are optional at registration time, but the field is always
+		 * present in the response so consumers do not have to handle its absence.
+		 */
+		if ( rest_is_field_included( 'keywords', $fields ) ) {
+			$data['keywords'] = isset( $item['keywords'] ) ? array_values( $item['keywords'] ) : array();
+		}
+
 		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
 		$data    = $this->add_additional_fields_to_object( $data, $request );
 		$data    = $this->filter_response_by_context( $data, $context );
@@ -262,6 +274,7 @@ class WP_REST_Icons_Controller extends WP_REST_Controller {
 	 *
 	 * @since 7.0.0
 	 * @since 7.1.0 Added the `collection` property.
+	 * @since 7.2.0 Added the `keywords` property.
 	 *
 	 * @return array Item schema data.
 	 */
@@ -299,6 +312,15 @@ class WP_REST_Icons_Controller extends WP_REST_Controller {
 					'readonly'    => true,
 					'context'     => array( 'view', 'edit', 'embed' ),
 				),
+				'keywords'   => array(
+					'description' => __( 'Additional search terms for the icon.' ),
+					'type'        => 'array',
+					'items'       => array(
+						'type' => 'string',
+					),
+					'readonly'    => true,
+					'context'     => array( 'view', 'edit', 'embed' ),
+				),
 			),
 		);

diff --git a/wp-includes/version.php b/wp-includes/version.php
index df9911ba10..ca6b872f97 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
  *
  * @global string $wp_version
  */
-$wp_version = '7.2-alpha-63998';
+$wp_version = '7.2-alpha-63999';

 /**
  * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.