diff --git a/src/wp-includes/class-wp-icons-registry.php b/src/wp-includes/class-wp-icons-registry.php index 2a109fb64147a..1140d0784af3b 100644 --- a/src/wp-includes/class-wp-icons-registry.php +++ b/src/wp-includes/class-wp-icons-registry.php @@ -20,6 +20,14 @@ class WP_Icons_Registry { */ protected $registered_icons = array(); + /** + * Resolved path of the core icon library directory. + * + * @since 7.2.0 + * @var string|false|null + */ + private $core_icon_library_directory = null; + /** * Container for the main instance of the class. * @@ -367,6 +375,7 @@ protected function sanitize_icon_content( $icon_content ) { * Retrieves the content of a registered icon. * * @since 7.0.0 + * @since 7.2.0 Core icons skip sanitization. * * @param string $icon_name Icon name including namespace. * @return string|null The content of the icon, if found. @@ -390,7 +399,12 @@ protected function get_content( $icon_name ) { return null; } - $content = $this->sanitize_icon_content( file_get_contents( $icon_path ) ); + $content = file_get_contents( $icon_path ); + + // Core icons are trusted, so skip the costly sanitization. + if ( ! $this->is_core_icon_file( $icon_path ) ) { + $content = $this->sanitize_icon_content( $content ); + } if ( empty( $content ) ) { wp_trigger_error( @@ -405,6 +419,22 @@ protected function get_content( $icon_name ) { return $this->registered_icons[ $icon_name ]['content']; } + /** + * Determines whether an icon file is a core icon. + * + * @since 7.2.0 + * + * @param string $icon_path Icon file path, resolved with `realpath()`. + * @return bool Whether the file is in the core icon library. + */ + private function is_core_icon_file( $icon_path ) { + if ( null === $this->core_icon_library_directory ) { + $this->core_icon_library_directory = realpath( ABSPATH . WPINC . '/images/icon-library' ); + } + + return is_string( $this->core_icon_library_directory ) && str_starts_with( $icon_path, $this->core_icon_library_directory . DIRECTORY_SEPARATOR ); + } + /** * Retrieves an array containing the properties of a registered icon. * diff --git a/tests/phpunit/tests/icons/wpGetIcon.php b/tests/phpunit/tests/icons/wpGetIcon.php index 4b1b7eed6c1eb..51dcfd04d1fd0 100644 --- a/tests/phpunit/tests/icons/wpGetIcon.php +++ b/tests/phpunit/tests/icons/wpGetIcon.php @@ -47,11 +47,11 @@ public function test_wp_get_icon_returns_empty_string_for_unknown_icon() { /** * @ticket 64847 + * @ticket 66338 */ public function test_wp_get_icon_default_attributes() { $output = wp_get_icon( 'core/plus' ); - // WP_HTML_Tag_Processor lowercases attribute names. - $this->assertStringContainsString( 'viewbox="0 0 24 24"', $output ); + $this->assertStringContainsStringIgnoringCase( 'viewbox="0 0 24 24"', $output ); $this->assertStringContainsString( 'width="24"', $output ); $this->assertStringContainsString( 'height="24"', $output ); $this->assertStringContainsString( 'aria-hidden="true"', $output ); diff --git a/tests/phpunit/tests/icons/wpIconsRegistry.php b/tests/phpunit/tests/icons/wpIconsRegistry.php index 787364e8ebd58..946aef96cee15 100644 --- a/tests/phpunit/tests/icons/wpIconsRegistry.php +++ b/tests/phpunit/tests/icons/wpIconsRegistry.php @@ -499,6 +499,39 @@ public function test_get_content_reads_from_valid_file_path() { $this->assertStringContainsString( 'create_temp_icon_file( '' ); + + $this->registry->register( + 'test-collection/from-file', + array( + 'label' => 'From File', + 'file_path' => $path, + ) + ); + + $icon = $this->registry->get_registered_icon( 'test-collection/from-file' ); + $this->assertStringContainsString( 'assertStringNotContainsString( 'original_registry->get_registered_icon( 'core/pencil' ); + $this->assertSame( file_get_contents( ABSPATH . WPINC . '/images/icon-library/pencil.svg' ), $icon['content'] ); + } + /** * Provides icon files that cannot yield valid content. *