From fc16a92aa1212bfc66269f4849472e1c7ad10bd2 Mon Sep 17 00:00:00 2001 From: Milan Ricoul Date: Wed, 4 Nov 2026 16:21:58 +0100 Subject: [PATCH 1/7] refactor(Icon): gate icon hooks on WP 7.1 APIs in boot() Skip registering init callbacks when icon registry functions are unavailable instead of repeating function_exists checks in each handler. --- inc/Services/Icon.php | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/inc/Services/Icon.php b/inc/Services/Icon.php index 1eb02558..36348ba3 100644 --- a/inc/Services/Icon.php +++ b/inc/Services/Icon.php @@ -23,8 +23,15 @@ public function register( Service_Container $container ): void {} * @param Service_Container $container Service container. */ public function boot( Service_Container $container ): void { + if ( ! function_exists( 'wp_register_icon_collection' ) || ! function_exists( 'wp_register_icon' ) ) { + return; + } + add_action( 'init', [ $this, 'register_icon_collections' ] ); - add_action( 'init', [ $this, 'unregister_core_icon_collection' ], 20 ); + + if ( function_exists( 'wp_unregister_icon_collection' ) ) { + add_action( 'init', [ $this, 'unregister_core_icon_collection' ], 20 ); + } } /** @@ -40,10 +47,6 @@ public function get_service_name(): string { * @return void */ public function register_icon_collections(): void { - if ( ! function_exists( 'wp_register_icon_collection' ) || ! function_exists( 'wp_register_icon' ) ) { - return; - } - $collections = apply_filters( 'bff_icon_collections', [ @@ -73,10 +76,6 @@ public function register_icon_collections(): void { * @return void */ public function unregister_core_icon_collection(): void { - if ( ! function_exists( 'wp_unregister_icon_collection' ) ) { - return; - } - if ( ! apply_filters( 'bff_unregister_core_icon_collection', true ) ) { return; } @@ -91,10 +90,6 @@ public function unregister_core_icon_collection(): void { * @return void */ private function register_collection( string $collection_slug, array $collection_args ): void { - if ( ! function_exists( 'wp_register_icon_collection' ) ) { - return; - } - $source_subdir = isset( $collection_args['source_subdir'] ) ? (string) $collection_args['source_subdir'] : ''; if ( '' === $source_subdir ) { @@ -163,10 +158,6 @@ private function resolve_icons_directory( string $source_subdir ): ?string { * @return void */ private function register_icon_file( string $collection_slug, string $svg_file ): void { - if ( ! function_exists( 'wp_register_icon' ) ) { - return; - } - $real_file = realpath( $svg_file ); if ( false === $real_file || ! is_readable( $real_file ) ) { From b92149a028c20fadb3f92c7e7af615c1483189d3 Mon Sep 17 00:00:00 2001 From: Milan Ricoul Date: Wed, 4 Nov 2026 16:22:03 +0100 Subject: [PATCH 2/7] fix(Icon): derive icon slugs with sanitize_title() Align slug normalization with WP_Icons_Registry so underscores and mixed-case filenames are preserved instead of stripped or silently merged. --- inc/Services/Icon.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/Services/Icon.php b/inc/Services/Icon.php index 36348ba3..8a7b090c 100644 --- a/inc/Services/Icon.php +++ b/inc/Services/Icon.php @@ -165,7 +165,7 @@ private function register_icon_file( string $collection_slug, string $svg_file ) } $icon_slug = pathinfo( $real_file, PATHINFO_FILENAME ); - $icon_slug = preg_replace( '/[^a-z0-9-]/', '', strtolower( (string) $icon_slug ) ); + $icon_slug = sanitize_title( (string) $icon_slug ); if ( '' === $icon_slug ) { return; From 479331e9be7fb84238d223288cef798c89d01409 Mon Sep 17 00:00:00 2001 From: Milan Ricoul Date: Wed, 4 Nov 2026 16:22:10 +0100 Subject: [PATCH 3/7] perf(Icon): rely on core for SVG file path resolution Pass glob paths directly to wp_register_icon instead of realpath and is_readable on every init; directory safety stays in resolve_icons_directory(). --- inc/Services/Icon.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/inc/Services/Icon.php b/inc/Services/Icon.php index 8a7b090c..eb93e9bd 100644 --- a/inc/Services/Icon.php +++ b/inc/Services/Icon.php @@ -158,13 +158,7 @@ private function resolve_icons_directory( string $source_subdir ): ?string { * @return void */ private function register_icon_file( string $collection_slug, string $svg_file ): void { - $real_file = realpath( $svg_file ); - - if ( false === $real_file || ! is_readable( $real_file ) ) { - return; - } - - $icon_slug = pathinfo( $real_file, PATHINFO_FILENAME ); + $icon_slug = pathinfo( $svg_file, PATHINFO_FILENAME ); $icon_slug = sanitize_title( (string) $icon_slug ); if ( '' === $icon_slug ) { @@ -183,7 +177,7 @@ private function register_icon_file( string $collection_slug, string $svg_file ) 'bff_icon_properties', [ 'label' => $label, - 'file_path' => $real_file, + 'file_path' => $svg_file, ], $icon_name, $collection_slug From d72f966b78ff6fb4c525e2484ac88a67bd3c5c93 Mon Sep 17 00:00:00 2001 From: Milan Ricoul Date: Wed, 4 Nov 2026 16:22:21 +0100 Subject: [PATCH 4/7] perf(Icon): cache collection SVG file lists with transients Avoid glob on every request by storing file paths in a transient keyed by directory mtime; bypass the cache when WP_DEBUG is enabled. --- inc/Services/Icon.php | 50 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/inc/Services/Icon.php b/inc/Services/Icon.php index eb93e9bd..10ed5497 100644 --- a/inc/Services/Icon.php +++ b/inc/Services/Icon.php @@ -110,15 +110,57 @@ private function register_collection( string $collection_slug, array $collection return; } + $svg_files = $this->get_collection_svg_files( $icons_directory ); + + foreach ( $svg_files as $svg_file ) { + $this->register_icon_file( $collection_slug, $svg_file ); + } + } + + /** + * Returns SVG file paths for a collection directory, with transient caching in production. + * + * @param string $icons_directory Validated absolute path to a collection icons directory. + * + * @return list + */ + private function get_collection_svg_files( string $icons_directory ): array { + if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { + return $this->glob_svg_files( $icons_directory ); + } + + $directory_mtime = filemtime( $icons_directory ); + if ( false === $directory_mtime ) { + $directory_mtime = 0; + } + + $cache_key = 'bff_icon_svg_files_' . md5( $icons_directory . ':' . (string) $directory_mtime ); + $cached = get_transient( $cache_key ); + + if ( is_array( $cached ) ) { + return $cached; + } + + $svg_files = $this->glob_svg_files( $icons_directory ); + + set_transient( $cache_key, $svg_files, WEEK_IN_SECONDS ); + + return $svg_files; + } + + /** + * @param string $icons_directory Validated absolute path to a collection icons directory. + * + * @return list + */ + private function glob_svg_files( string $icons_directory ): array { $svg_files = glob( $icons_directory . '/*.svg' ); if ( false === $svg_files ) { - return; + return []; } - foreach ( $svg_files as $svg_file ) { - $this->register_icon_file( $collection_slug, $svg_file ); - } + return $svg_files; } /** From dd07254bafa2be43ec684c36f6a5906b3f46d597 Mon Sep 17 00:00:00 2001 From: Milan Ricoul Date: Wed, 4 Nov 2026 16:22:34 +0100 Subject: [PATCH 5/7] fix(Icon): skip registering collections with no SVG files Register icon collections only after glob finds at least one SVG so empty dist/images folders do not appear in the block picker. --- inc/Services/Icon.php | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/inc/Services/Icon.php b/inc/Services/Icon.php index 10ed5497..94048cbf 100644 --- a/inc/Services/Icon.php +++ b/inc/Services/Icon.php @@ -96,14 +96,6 @@ private function register_collection( string $collection_slug, array $collection return; } - wp_register_icon_collection( - $collection_slug, - [ - 'label' => $collection_args['label'] ?? $collection_slug, - 'description' => $collection_args['description'] ?? '', - ] - ); - $icons_directory = $this->resolve_icons_directory( $source_subdir ); if ( null === $icons_directory ) { @@ -112,6 +104,18 @@ private function register_collection( string $collection_slug, array $collection $svg_files = $this->get_collection_svg_files( $icons_directory ); + if ( [] === $svg_files ) { + return; + } + + wp_register_icon_collection( + $collection_slug, + [ + 'label' => $collection_args['label'] ?? $collection_slug, + 'description' => $collection_args['description'] ?? '', + ] + ); + foreach ( $svg_files as $svg_file ) { $this->register_icon_file( $collection_slug, $svg_file ); } From a3a176a1ac6e7169e69946955413e38b876b7155 Mon Sep 17 00:00:00 2001 From: Milan Ricoul Date: Wed, 4 Nov 2026 17:29:28 +0100 Subject: [PATCH 6/7] refactor(Icon): drop redundant unregister API existence check Co-authored-by: Cursor --- inc/Services/Icon.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/inc/Services/Icon.php b/inc/Services/Icon.php index 94048cbf..92c8ca4f 100644 --- a/inc/Services/Icon.php +++ b/inc/Services/Icon.php @@ -28,10 +28,7 @@ public function boot( Service_Container $container ): void { } add_action( 'init', [ $this, 'register_icon_collections' ] ); - - if ( function_exists( 'wp_unregister_icon_collection' ) ) { - add_action( 'init', [ $this, 'unregister_core_icon_collection' ], 20 ); - } + add_action( 'init', [ $this, 'unregister_core_icon_collection' ], 20 ); } /** From bb513f4e5472f5ce26b8bb9de9f0164732fd50a8 Mon Sep 17 00:00:00 2001 From: Milan Ricoul Date: Wed, 4 Nov 2026 17:29:39 +0100 Subject: [PATCH 7/7] perf(Icon): skip SVG list cache via wp_get_development_mode() --- inc/Services/Icon.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inc/Services/Icon.php b/inc/Services/Icon.php index 92c8ca4f..79073b23 100644 --- a/inc/Services/Icon.php +++ b/inc/Services/Icon.php @@ -119,14 +119,14 @@ private function register_collection( string $collection_slug, array $collection } /** - * Returns SVG file paths for a collection directory, with transient caching in production. + * Returns SVG file paths for a collection directory, with transient caching outside theme development mode. * * @param string $icons_directory Validated absolute path to a collection icons directory. * * @return list */ private function get_collection_svg_files( string $icons_directory ): array { - if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { + if ( in_array( wp_get_development_mode(), [ 'theme', 'all' ], true ) ) { return $this->glob_svg_files( $icons_directory ); }