diff --git a/composer.json b/composer.json
index a79ab7213..751aaac86 100644
--- a/composer.json
+++ b/composer.json
@@ -74,6 +74,15 @@
"font face install",
"font family",
"font family install",
+ "icon",
+ "icon collection",
+ "icon collection get",
+ "icon collection is-registered",
+ "icon collection list",
+ "icon get",
+ "icon is-registered",
+ "icon list",
+ "icon render",
"menu",
"menu create",
"menu delete",
diff --git a/entity-command.php b/entity-command.php
index 52bc0a4cd..e16b522a3 100644
--- a/entity-command.php
+++ b/entity-command.php
@@ -140,3 +140,14 @@
WP_CLI::add_command( 'font collection', 'Font_Collection_Command', $wpcli_entity_font_version_check );
WP_CLI::add_command( 'font family', 'Font_Family_Command', $wpcli_entity_font_version_check );
WP_CLI::add_command( 'font face', 'Font_Face_Command', $wpcli_entity_font_version_check );
+
+$wpcli_entity_icon_version_check = array(
+ 'before_invoke' => function () {
+ if ( Utils\wp_version_compare( '7.1', '<' ) ) {
+ WP_CLI::error( 'Requires WordPress 7.1 or greater.' );
+ }
+ },
+);
+
+WP_CLI::add_command( 'icon', 'Icon_Command', $wpcli_entity_icon_version_check );
+WP_CLI::add_command( 'icon collection', 'Icon_Collection_Command', $wpcli_entity_icon_version_check );
diff --git a/features/icon-collection.feature b/features/icon-collection.feature
new file mode 100644
index 000000000..88e2ec49b
--- /dev/null
+++ b/features/icon-collection.feature
@@ -0,0 +1,110 @@
+Feature: Manage WordPress icon collections
+
+ Background:
+ Given a WP install
+
+ @require-wp-7.1
+ Scenario: Listing icon collections
+ When I run `wp icon collection list --fields=slug,label,description`
+ Then STDOUT should be a table containing rows:
+ | slug | label | description |
+ | core | WordPress | Default icon collection. |
+
+ @require-wp-7.1
+ Scenario: Listing icon collections in other formats
+ When I run `wp icon collection list --format=ids`
+ Then STDOUT should contain:
+ """
+ core
+ """
+
+ When I run `wp icon collection list --fields=slug,label --format=json`
+ Then STDOUT should be JSON containing:
+ """
+ [{"slug":"core","label":"WordPress"}]
+ """
+
+ When I run `wp icon collection list --format=count`
+ Then STDOUT should be a number
+
+ @require-wp-7.1
+ Scenario: Getting an icon collection
+ When I run `wp icon collection get core --fields=slug,label,description`
+ Then STDOUT should be a table containing rows:
+ | Field | Value |
+ | slug | core |
+ | label | WordPress |
+ | description | Default icon collection. |
+
+ When I run `wp icon collection get core --field=label`
+ Then STDOUT should be:
+ """
+ WordPress
+ """
+
+ @require-wp-7.1
+ Scenario: Counting the icons of a collection
+ When I run `wp icon collection get core --field=count`
+ Then STDOUT should be a number
+
+ @require-wp-7.1
+ Scenario: Getting a non-existent icon collection
+ When I try `wp icon collection get nonexistent-collection`
+ Then the return code should be 1
+ And STDERR should contain:
+ """
+ doesn't exist
+ """
+
+ @require-wp-7.1
+ Scenario: Checking whether an icon collection is registered
+ When I try `wp icon collection is-registered nonexistent-collection`
+ Then the return code should be 1
+
+ When I run `wp icon collection is-registered core`
+ Then the return code should be 0
+
+ @require-wp-7.1
+ Scenario: Listing a collection registered by a plugin
+ Given a wp-content/mu-plugins/test-icons.php file:
+ """
+ 'Test Icons',
+ 'description' => 'A collection for testing.',
+ )
+ );
+
+ wp_register_icon(
+ 'test-icons/square',
+ array(
+ 'label' => 'Square',
+ 'content' => ' ',
+ )
+ );
+ }
+ );
+ """
+
+ When I run `wp icon collection list --fields=slug,label,description,count`
+ Then STDOUT should be a table containing rows:
+ | slug | label | description | count |
+ | test-icons | Test Icons | A collection for testing. | 1 |
+
+ When I run `wp icon collection is-registered test-icons`
+ Then the return code should be 0
+
+ @less-than-wp-7.1
+ Scenario: Icon collection commands fail on WordPress < 7.1
+ When I try `wp icon collection list`
+ Then the return code should be 1
+ And STDERR should contain:
+ """
+ Requires WordPress 7.1 or greater
+ """
diff --git a/features/icon.feature b/features/icon.feature
new file mode 100644
index 000000000..107ac3e1b
--- /dev/null
+++ b/features/icon.feature
@@ -0,0 +1,205 @@
+Feature: Manage WordPress SVG icons
+
+ Background:
+ Given a WP install
+
+ @require-wp-7.1
+ Scenario: Listing icons
+ When I run `wp icon list --collection=core --format=count`
+ Then STDOUT should be a number
+
+ When I run `wp icon list --search=arrow-down --field=name`
+ Then STDOUT should contain:
+ """
+ core/arrow-down
+ """
+
+ When I run `wp icon list --format=ids`
+ Then STDOUT should contain:
+ """
+ core/plus
+ """
+
+ @require-wp-7.1
+ Scenario: Listing icons matches names and labels
+ When I run `wp icon list --search="Arrow Down" --fields=name,label,collection`
+ Then STDOUT should be a table containing rows:
+ | name | label | collection |
+ | core/arrow-down | Arrow Down | core |
+
+ @require-wp-7.1
+ Scenario: Listing icons of a non-existent collection
+ When I try `wp icon list --collection=nonexistent-collection`
+ Then the return code should be 1
+ And STDERR should contain:
+ """
+ doesn't exist
+ """
+
+ @require-wp-7.1
+ Scenario: Getting an icon
+ When I run `wp icon get core/plus --fields=name,label,collection`
+ Then STDOUT should be a table containing rows:
+ | Field | Value |
+ | name | core/plus |
+ | label | Plus |
+ | collection | core |
+
+ When I run `wp icon get core/plus --field=content`
+ Then STDOUT should contain:
+ """
+ 'Test Icons',
+ 'description' => 'A collection for testing.',
+ )
+ );
+
+ wp_register_icon(
+ 'test-icons/square',
+ array(
+ 'label' => 'Square',
+ 'content' => ' ',
+ )
+ );
+ }
+ );
+ """
+
+ When I run `wp icon list --collection=test-icons --fields=name,label,collection`
+ Then STDOUT should be a table containing rows:
+ | name | label | collection |
+ | test-icons/square | Square | test-icons |
+
+ When I run `wp icon get test-icons/square --field=content`
+ Then STDOUT should contain:
+ """
+ M4 4h16v16H4z
+ """
+
+ When I run `wp icon get test-icons/square --field=file_path`
+ Then STDOUT should not contain:
+ """
+ .svg
+ """
+
+ When I run `wp icon render test-icons/square --size=16`
+ Then STDOUT should contain:
+ """
+ width="16"
+ """
+
+ @less-than-wp-7.1
+ Scenario: Icon commands fail on WordPress < 7.1
+ When I try `wp icon list`
+ Then the return code should be 1
+ And STDERR should contain:
+ """
+ Requires WordPress 7.1 or greater
+ """
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
index 834afe6a0..976ab7754 100644
--- a/phpcs.xml.dist
+++ b/phpcs.xml.dist
@@ -63,6 +63,7 @@
*/src/Comment(_Meta)?_Command\.php$
*/src/Font(_Collection|_Face|_Family)?_Command\.php$
*/src/Font_Namespace\.php$
+ */src/Icon(_Collection)?_Command\.php$
*/src/Menu(_Item|_Location)?_Command\.php$
*/src/Network_Meta_Command\.php$
*/src/Network_Namespace\.php$
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index 05ea1c2eb..c1aa55f5b 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -46,6 +46,24 @@ parameters:
- src/Font_Collection_Command.php
- src/Font_Family_Command.php
+ # `wp icon` and `wp icon collection` abort on WordPress < 7.1 via `before_invoke`.
+ -
+ identifier: WPCompat.methodNotAvailable
+ message: '#^WP_Icon_Collections_Registry::get_instance\(\) is only available since WordPress version 7\.1\.0\.$#'
+ paths:
+ - src/Icon_Collection_Command.php
+ - src/Icon_Command.php
+ -
+ identifier: WPCompat.methodNotAvailable
+ message: '#^WP_Icons_Registry::get_instance\(\) is only available since WordPress version 7\.0\.0\.$#'
+ paths:
+ - src/Icon_Collection_Command.php
+ - src/Icon_Command.php
+ -
+ identifier: WPCompat.functionNotAvailable
+ message: '#^wp_get_icon\(\) is only available since WordPress version 7\.1\.0\.$#'
+ path: src/Icon_Command.php
+
# `wp user application-password` aborts on WordPress < 5.6 via `before_invoke`.
-
identifier: WPCompat.methodNotAvailable
@@ -111,3 +129,19 @@ parameters:
-
identifier: WPCompat.parameterNotAvailable.wploadalloptions.forcecache
path: src/Option_Command.php
+
+ # The Icons API introduced in WordPress 7.0 and 7.1 is not covered by the WordPress
+ # stubs yet, so PHPStan does not know these symbols at all. `reportUnmatched` keeps
+ # the entries from turning into errors themselves once the stubs catch up.
+ -
+ identifier: class.notFound
+ message: '#^Call to static method get_instance\(\) on an unknown class WP_(Icons_Registry|Icon_Collections_Registry)\.$#'
+ reportUnmatched: false
+ paths:
+ - src/Icon_Collection_Command.php
+ - src/Icon_Command.php
+ -
+ identifier: function.notFound
+ message: '#^Function wp_get_icon not found\.$#'
+ reportUnmatched: false
+ path: src/Icon_Command.php
diff --git a/src/Icon_Collection_Command.php b/src/Icon_Collection_Command.php
new file mode 100644
index 000000000..fd299a7ed
--- /dev/null
+++ b/src/Icon_Collection_Command.php
@@ -0,0 +1,260 @@
+]
+ * : Prints the value of a single field for each collection.
+ *
+ * [--fields=]
+ * : Limit the output to specific collection fields.
+ *
+ * [--format=]
+ * : Render output in a particular format.
+ * ---
+ * default: table
+ * options:
+ * - table
+ * - csv
+ * - json
+ * - count
+ * - yaml
+ * - ids
+ * ---
+ *
+ * ## AVAILABLE FIELDS
+ *
+ * These fields will be displayed by default for each collection:
+ *
+ * * slug
+ * * label
+ * * description
+ * * count
+ *
+ * ## EXAMPLES
+ *
+ * # List all icon collections
+ * $ wp icon collection list
+ * +------+-----------+--------------------------+-------+
+ * | slug | label | description | count |
+ * +------+-----------+--------------------------+-------+
+ * | core | WordPress | Default icon collection. | 88 |
+ * +------+-----------+--------------------------+-------+
+ *
+ * # List collections in JSON format
+ * $ wp icon collection list --fields=slug,label --format=json
+ * [{"slug":"core","label":"WordPress"}]
+ *
+ * @subcommand list
+ *
+ * @param string[] $args Positional arguments.
+ * @param array $assoc_args Associative arguments.
+ */
+ public function list_( $args, $assoc_args ) {
+ $collections = WP_Icon_Collections_Registry::get_instance()->get_all_registered();
+ $counts = $this->get_icon_counts();
+
+ $items = [];
+
+ foreach ( $collections as $collection ) {
+ $items[] = $this->format_collection( $collection, $counts );
+ }
+
+ $formatter = $this->get_formatter( $assoc_args );
+
+ if ( 'ids' === $formatter->format ) {
+ $formatter->display_items( wp_list_pluck( $items, 'slug' ) );
+ return;
+ }
+
+ $formatter->display_items( $items );
+ }
+
+ /**
+ * Gets details about a registered icon collection.
+ *
+ * ## OPTIONS
+ *
+ *
+ * : Icon collection slug.
+ *
+ * [--field=]
+ * : Instead of returning the whole collection, returns the value of a single field.
+ *
+ * [--fields=]
+ * : Limit the output to specific fields. Defaults to all fields.
+ *
+ * [--format=]
+ * : Render output in a particular format.
+ * ---
+ * default: table
+ * options:
+ * - table
+ * - csv
+ * - json
+ * - yaml
+ * ---
+ *
+ * ## AVAILABLE FIELDS
+ *
+ * These fields will be displayed by default for the specified collection:
+ *
+ * * slug
+ * * label
+ * * description
+ * * count
+ *
+ * ## EXAMPLES
+ *
+ * # Get details of a specific collection
+ * $ wp icon collection get core
+ * +-------------+--------------------------+
+ * | Field | Value |
+ * +-------------+--------------------------+
+ * | slug | core |
+ * | label | WordPress |
+ * | description | Default icon collection. |
+ * | count | 88 |
+ * +-------------+--------------------------+
+ *
+ * # Get the label field only
+ * $ wp icon collection get core --field=label
+ * WordPress
+ *
+ * @param string[] $args Positional arguments.
+ * @param array $assoc_args Associative arguments.
+ */
+ public function get( $args, $assoc_args ) {
+ $slug = $args[0];
+ $collection = WP_Icon_Collections_Registry::get_instance()->get_registered( $slug );
+
+ if ( null === $collection ) {
+ WP_CLI::error( "Icon collection {$slug} doesn't exist." );
+ }
+
+ $formatter = $this->get_formatter( $assoc_args );
+ $formatter->display_item( $this->format_collection( $collection, $this->get_icon_counts() ) );
+ }
+
+ /**
+ * Checks if an icon collection is registered.
+ *
+ * ## OPTIONS
+ *
+ *
+ * : Icon collection slug.
+ *
+ * ## EXAMPLES
+ *
+ * # Bash script for checking if an icon collection is registered, with fallback.
+ *
+ * if wp icon collection is-registered core 2>/dev/null; then
+ * # Icon collection is registered. Do something.
+ * else
+ * # Fallback if collection is not registered.
+ * fi
+ *
+ * @subcommand is-registered
+ *
+ * @param string[] $args Positional arguments.
+ * @param array $assoc_args Associative arguments.
+ */
+ public function is_registered( $args, $assoc_args ) {
+ $slug = $args[0];
+
+ if ( ! WP_Icon_Collections_Registry::get_instance()->is_registered( $slug ) ) {
+ WP_CLI::halt( 1 );
+ }
+
+ WP_CLI::halt( 0 );
+ }
+
+ /**
+ * Counts the registered icons per collection.
+ *
+ * @return array
+ */
+ private function get_icon_counts(): array {
+ $counts = [];
+
+ foreach ( WP_Icons_Registry::get_instance()->get_registered_icons() as $icon ) {
+ $slug = $this->to_string( $icon['collection'] ?? '' );
+
+ if ( '' === $slug ) {
+ continue;
+ }
+
+ $counts[ $slug ] = ( $counts[ $slug ] ?? 0 ) + 1;
+ }
+
+ return $counts;
+ }
+
+ /**
+ * @param array $collection Registered icon collection properties.
+ * @param array $counts Number of registered icons, keyed by collection slug.
+ * @return array
+ */
+ private function format_collection( array $collection, array $counts ): array {
+ $slug = $this->to_string( $collection['slug'] ?? '' );
+
+ return [
+ 'slug' => $slug,
+ 'label' => $this->to_string( $collection['label'] ?? '' ),
+ 'description' => $this->to_string( $collection['description'] ?? '' ),
+ 'count' => $counts[ $slug ] ?? 0,
+ ];
+ }
+
+ /**
+ * @param mixed $value Value to convert.
+ */
+ private function to_string( $value ): string {
+ return is_string( $value ) ? $value : '';
+ }
+
+ /**
+ * @param array $assoc_args Associative arguments.
+ */
+ private function get_formatter( &$assoc_args ): Formatter {
+ return new Formatter( $assoc_args, $this->fields, 'icon-collection' );
+ }
+}
diff --git a/src/Icon_Command.php b/src/Icon_Command.php
new file mode 100644
index 000000000..d58ff9714
--- /dev/null
+++ b/src/Icon_Command.php
@@ -0,0 +1,377 @@
+ plus.svg
+ *
+ * @package wp-cli
+ */
+class Icon_Command extends WP_CLI_Command {
+
+ /**
+ * Fields shown by default when listing icons.
+ *
+ * The SVG markup is left out on purpose, as it does not render well
+ * across many rows.
+ */
+ private $fields = [
+ 'name',
+ 'label',
+ 'collection',
+ ];
+
+ /**
+ * Fields shown by default when getting a single icon.
+ */
+ private $item_fields = [
+ 'name',
+ 'label',
+ 'collection',
+ 'content',
+ ];
+
+ /**
+ * Lists registered icons.
+ *
+ * ## OPTIONS
+ *
+ * [--collection=]
+ * : Only list icons belonging to the given collection.
+ *
+ * [--search=]
+ * : Only list icons whose name or label matches the given term.
+ *
+ * [--field=]
+ * : Prints the value of a single field for each icon.
+ *
+ * [--fields=]
+ * : Limit the output to specific icon fields.
+ *
+ * [--format=]
+ * : Render output in a particular format.
+ * ---
+ * default: table
+ * options:
+ * - table
+ * - csv
+ * - json
+ * - count
+ * - yaml
+ * - ids
+ * ---
+ *
+ * ## AVAILABLE FIELDS
+ *
+ * These fields will be displayed by default for each icon:
+ *
+ * * name
+ * * label
+ * * collection
+ *
+ * These fields are optionally available:
+ *
+ * * content
+ * * file_path
+ *
+ * ## EXAMPLES
+ *
+ * # List all icons of a specific collection
+ * $ wp icon list --collection=core
+ * +-----------------+------------+------------+
+ * | name | label | collection |
+ * +-----------------+------------+------------+
+ * | core/arrow-down | Arrow Down | core |
+ * | core/plus | Plus | core |
+ * +-----------------+------------+------------+
+ *
+ * # Search for icons by name or label
+ * $ wp icon list --search=arrow-down --field=name
+ * core/arrow-down-left
+ * core/arrow-down-right
+ * core/arrow-down
+ *
+ * # Count the icons of a collection
+ * $ wp icon list --collection=core --format=count
+ * 88
+ *
+ * @subcommand list
+ *
+ * @param string[] $args Positional arguments.
+ * @param array $assoc_args Associative arguments.
+ */
+ public function list_( $args, $assoc_args ) {
+ $collection = Utils\get_flag_value( $assoc_args, 'collection' );
+ $search = $this->to_string( Utils\get_flag_value( $assoc_args, 'search', '' ) );
+
+ if ( null !== $collection ) {
+ $collection = $this->to_string( $collection );
+
+ if ( ! WP_Icon_Collections_Registry::get_instance()->is_registered( $collection ) ) {
+ WP_CLI::error( "Icon collection {$collection} doesn't exist." );
+ }
+ }
+
+ $items = [];
+
+ foreach ( WP_Icons_Registry::get_instance()->get_registered_icons( $search ) as $icon ) {
+ if ( null !== $collection && ( $icon['collection'] ?? '' ) !== $collection ) {
+ continue;
+ }
+
+ $items[] = $this->format_icon( $icon );
+ }
+
+ $formatter = new Formatter( $assoc_args, $this->fields, 'icon' );
+
+ if ( 'ids' === $formatter->format ) {
+ $formatter->display_items( wp_list_pluck( $items, 'name' ) );
+ return;
+ }
+
+ $formatter->display_items( $items );
+ }
+
+ /**
+ * Gets details about a registered icon.
+ *
+ * ## OPTIONS
+ *
+ *
+ * : Namespaced icon name, in the form "collection/icon-name".
+ *
+ * [--field=]
+ * : Instead of returning the whole icon, returns the value of a single field.
+ *
+ * [--fields=]
+ * : Limit the output to specific fields.
+ *
+ * [--format=]
+ * : Render output in a particular format.
+ * ---
+ * default: table
+ * options:
+ * - table
+ * - csv
+ * - json
+ * - yaml
+ * ---
+ *
+ * ## AVAILABLE FIELDS
+ *
+ * These fields will be displayed by default for the specified icon:
+ *
+ * * name
+ * * label
+ * * collection
+ * * content
+ *
+ * This field is optionally available:
+ *
+ * * file_path
+ *
+ * ## EXAMPLES
+ *
+ * # Get details of a specific icon
+ * $ wp icon get core/plus --fields=name,label,collection
+ * +------------+-----------+
+ * | Field | Value |
+ * +------------+-----------+
+ * | name | core/plus |
+ * | label | Plus |
+ * | collection | core |
+ * +------------+-----------+
+ *
+ * # Get the SVG markup of an icon, as it was registered
+ * $ wp icon get core/plus --field=content
+ *
+ *
+ *
+ *
+ * # Find out where an icon is loaded from
+ * $ wp icon get core/plus --field=file_path
+ * /var/www/html/wp-includes/images/icon-library/plus.svg
+ *
+ * @param string[] $args Positional arguments.
+ * @param array $assoc_args Associative arguments.
+ */
+ public function get( $args, $assoc_args ) {
+ $name = $args[0];
+ $icon = WP_Icons_Registry::get_instance()->get_registered_icon( $name );
+
+ if ( null === $icon ) {
+ WP_CLI::error( "Icon {$name} doesn't exist." );
+ }
+
+ $formatter = new Formatter( $assoc_args, $this->item_fields, 'icon' );
+ $formatter->display_item( $this->format_icon( $icon ) );
+ }
+
+ /**
+ * Checks if an icon is registered.
+ *
+ * ## OPTIONS
+ *
+ *
+ * : Namespaced icon name, in the form "collection/icon-name".
+ *
+ * ## EXAMPLES
+ *
+ * # Bash script for checking if an icon is registered, with fallback.
+ *
+ * if wp icon is-registered core/plus 2>/dev/null; then
+ * # Icon is registered. Do something.
+ * else
+ * # Fallback if icon is not registered.
+ * fi
+ *
+ * @subcommand is-registered
+ *
+ * @param string[] $args Positional arguments.
+ * @param array $assoc_args Associative arguments.
+ */
+ public function is_registered( $args, $assoc_args ) {
+ $name = $args[0];
+
+ if ( ! WP_Icons_Registry::get_instance()->is_registered( $name ) ) {
+ WP_CLI::halt( 1 );
+ }
+
+ WP_CLI::halt( 0 );
+ }
+
+ /**
+ * Renders the SVG markup of a registered icon.
+ *
+ * Unlike `wp icon get --field=content`, which prints the icon as it
+ * was registered, this applies the same sizing and accessibility attributes
+ * that WordPress applies when rendering the icon on the front end.
+ *
+ * ## OPTIONS
+ *
+ *
+ * : Namespaced icon name, in the form "collection/icon-name".
+ *
+ * [--size=]
+ * : Width and height of the icon in pixels. Pass "none" to leave the
+ * intrinsic dimensions of the SVG untouched.
+ * ---
+ * default: 24
+ * ---
+ *
+ * [--class=]
+ * : Additional CSS class names, separated by spaces.
+ *
+ * [--label=]
+ * : Accessible label for the icon. Without it, the icon is rendered as
+ * decorative and hidden from assistive technology.
+ *
+ * ## EXAMPLES
+ *
+ * # Render an icon at its default size of 24 pixels
+ * $ wp icon render core/plus
+ *
+ *
+ *
+ *
+ * # Render a larger icon with a CSS class and an accessible label
+ * $ wp icon render core/plus --size=48 --class=my-icon --label="Add item" > plus.svg
+ *
+ * # Keep the intrinsic dimensions of the SVG
+ * $ wp icon render core/plus --size=none
+ *
+ * @param string[] $args Positional arguments.
+ * @param array $assoc_args Associative arguments.
+ */
+ public function render( $args, $assoc_args ) {
+ $name = $args[0];
+
+ if ( ! WP_Icons_Registry::get_instance()->is_registered( $name ) ) {
+ WP_CLI::error( "Icon {$name} doesn't exist." );
+ }
+
+ $raw_size = Utils\get_flag_value( $assoc_args, 'size', 24 );
+ $size = null;
+
+ if ( 'none' !== $raw_size ) {
+ if ( is_int( $raw_size ) ) {
+ $size = $raw_size;
+ } elseif ( is_string( $raw_size ) && ctype_digit( $raw_size ) ) {
+ $size = (int) $raw_size;
+ }
+
+ if ( null === $size || $size < 1 ) {
+ WP_CLI::error( 'The --size argument must be a positive integer or "none".' );
+ }
+ }
+
+ $markup = wp_get_icon(
+ $name,
+ [
+ 'size' => $size,
+ 'class' => $this->to_string( Utils\get_flag_value( $assoc_args, 'class', '' ) ),
+ 'label' => $this->to_string( Utils\get_flag_value( $assoc_args, 'label', '' ) ),
+ ]
+ );
+
+ if ( '' === $markup ) {
+ WP_CLI::error( "Icon {$name} doesn't contain valid SVG markup." );
+ }
+
+ WP_CLI::line( $markup );
+ }
+
+ /**
+ * @param array $icon Registered icon properties.
+ * @return array
+ */
+ private function format_icon( array $icon ): array {
+ return [
+ 'name' => $this->to_string( $icon['name'] ?? '' ),
+ 'label' => $this->to_string( $icon['label'] ?? '' ),
+ 'collection' => $this->to_string( $icon['collection'] ?? '' ),
+ 'content' => $this->to_string( $icon['content'] ?? '' ),
+ 'file_path' => $this->to_string( $icon['file_path'] ?? '' ),
+ ];
+ }
+
+ /**
+ * @param mixed $value Value to convert.
+ */
+ private function to_string( $value ): string {
+ return is_string( $value ) ? $value : '';
+ }
+}