-
-
Notifications
You must be signed in to change notification settings - Fork 392
Description
PHP 8.4 Compatibility Issue in Symfony UX Icons v2.31.0
Description
After upgrading to PHP 8.4, Symfony UX Icons throws a TypeError when attempting to render icons or warm the icon cache.
Error Messages
Template Rendering Error:
Symfony\UX\Icons\Registry\CacheIconRegistry::get(): Return value must be of type Symfony\UX\Icons\Icon, Closure returned
Cache Warming Error:
TypeError: Cannot assign Closure to property Symfony\UX\Icons\Iconify::$sets of type ArrayObject at vendor/symfony/ux-icons/src/Iconify.php:204
Environment
- PHP Version: 8.4.0
- Symfony UX Icons Version: v2.31.0
- Symfony Version: 7.3
Root Cause
PHP 8.4 enforces stricter type checking. When using Symfony's cache component with the get() method and a callback, the cache is returning the Closure itself instead of executing it and returning the result. This causes type mismatches when trying to assign the Closure to typed properties (ArrayObject) or return it from typed methods (Icon).
The issue occurs in two files:
src/Registry/CacheIconRegistry.php- line 37-41src/Iconify.php- line 204
Reproduction Steps
- Upgrade to PHP 8.4
- Use
composer updateto install/update Symfony UX Icons - Attempt to render an icon in a Twig template:
<twig:ux:icon name="bi:bell-fill" /> - OR run:
php bin/console ux:icons:warm-cache
Both will fail with the errors mentioned above.
Proposed Fix
File: src/Registry/CacheIconRegistry.php
Before:
public function get(string $name, bool $refresh = false): Icon
{
if (!Icon::isValidName($name)) {
throw new IconNotFoundException(\sprintf('The icon name "%s" is not valid.', $name));
}
return $this->cache->get(
Icon::nameToId($name),
fn () => $this->inner->get($name),
beta: $refresh ? \INF : null,
);
}After:
public function get(string $name, bool $refresh = false): Icon
{
if (!Icon::isValidName($name)) {
throw new IconNotFoundException(\sprintf('The icon name "%s" is not valid.', $name));
}
$result = $this->cache->get(
Icon::nameToId($name),
fn () => $this->inner->get($name),
beta: $refresh ? \INF : null,
);
// PHP 8.4 compatibility: ensure we return Icon, not Closure
return $result instanceof \Closure ? $result() : $result;
}File: src/Iconify.php
Before:
private function sets(): \ArrayObject
{
return $this->sets ??= $this->cache->get('iconify-sets', function () {
$response = $this->http()->request('GET', '/collections');
return new \ArrayObject($response->toArray());
});
}After:
private function sets(): \ArrayObject
{
if (!isset($this->sets)) {
$result = $this->cache->get('iconify-sets', function () {
$response = $this->http()->request('GET', '/collections');
return new \ArrayObject($response->toArray());
});
// PHP 8.4 compatibility: ensure we get ArrayObject, not Closure
$this->sets = $result instanceof \Closure ? $result() : $result;
}
return $this->sets;
}Testing
After applying these fixes:
- Icons render correctly in templates
php bin/console ux:icons:warm-cachecompletes successfully- No performance impact observed
- Backwards compatible with PHP 8.1-8.3
Additional Notes
This fix handles the case where the cache returns a Closure by detecting it and executing it to get the actual value. This maintains compatibility with both the current behavior and potential future fixes in Symfony's cache component.