From 3c8063cac4421cbbe2939ef1d1e761f5e6c917e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 28 Mar 2024 14:43:38 +0100 Subject: [PATCH] [TwigBridge] Add `emojify` twig filter --- src/Symfony/Bridge/Twig/CHANGELOG.md | 5 ++ .../Bridge/Twig/Extension/EmojiExtension.php | 49 +++++++++++++++++++ .../Tests/Extension/EmojiExtensionTest.php | 33 +++++++++++++ .../Bridge/Twig/UndefinedCallableHandler.php | 1 + src/Symfony/Bridge/Twig/composer.json | 1 + .../Compiler/ExtensionPass.php | 9 ++++ .../TwigBundle/Resources/config/twig.php | 3 ++ src/Symfony/Bundle/TwigBundle/composer.json | 2 +- 8 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Bridge/Twig/Extension/EmojiExtension.php create mode 100644 src/Symfony/Bridge/Twig/Tests/Extension/EmojiExtensionTest.php diff --git a/src/Symfony/Bridge/Twig/CHANGELOG.md b/src/Symfony/Bridge/Twig/CHANGELOG.md index cc2d334538c34..a3cbe34260027 100644 --- a/src/Symfony/Bridge/Twig/CHANGELOG.md +++ b/src/Symfony/Bridge/Twig/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.1 +--- + + * Add `emojify` twig filter + 7.0 --- diff --git a/src/Symfony/Bridge/Twig/Extension/EmojiExtension.php b/src/Symfony/Bridge/Twig/Extension/EmojiExtension.php new file mode 100644 index 0000000000000..8581056e0699b --- /dev/null +++ b/src/Symfony/Bridge/Twig/Extension/EmojiExtension.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Extension; + +use Symfony\Component\Emoji\EmojiTransliterator; +use Twig\Extension\AbstractExtension; +use Twig\TwigFilter; + +/** + * @author Grégoire Pineau + */ +final class EmojiExtension extends AbstractExtension +{ + private static array $transliterators = []; + + public function __construct() + { + if (!class_exists(EmojiTransliterator::class)) { + throw new \LogicException('You cannot use the "emojify" filter as the "Emoji" component is not installed. Try running "composer require symfony/emoji".'); + } + } + + public function getFilters(): array + { + return [ + new TwigFilter('emojify', $this->emojify(...)), + ]; + } + + public function emojify(string $string, string $catalog = 'slack'): string + { + try { + $tr = self::$transliterators[$catalog] ??= EmojiTransliterator::create($catalog, EmojiTransliterator::REVERSE); + } catch (\IntlException $e) { + throw new \LogicException(sprintf('The emoji catalog "%s" is not available.', $catalog), previous: $e); + } + + return (string) $tr->transliterate($string); + } +} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/EmojiExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/EmojiExtensionTest.php new file mode 100644 index 0000000000000..917026fd69a0b --- /dev/null +++ b/src/Symfony/Bridge/Twig/Tests/Extension/EmojiExtensionTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Extension; + +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Twig\Extension\EmojiExtension; + +class EmojiExtensionTest extends TestCase +{ + /** + * @testWith ["🅰️", ":a:"] + * ["🅰️", ":a:", "slack"] + * ["🅰", ":a:", "github"] + */ + public function testEmojify(string $expected, string $string, ?string $catalog = null) + { + $extension = new EmojiExtension(); + if ($catalog) { + $this->assertSame($expected, $extension->emojify($string, $catalog)); + } else { + $this->assertSame($expected, $extension->emojify($string)); + } + } +} diff --git a/src/Symfony/Bridge/Twig/UndefinedCallableHandler.php b/src/Symfony/Bridge/Twig/UndefinedCallableHandler.php index 0ae2893cb8adf..c6498f6325c08 100644 --- a/src/Symfony/Bridge/Twig/UndefinedCallableHandler.php +++ b/src/Symfony/Bridge/Twig/UndefinedCallableHandler.php @@ -37,6 +37,7 @@ class UndefinedCallableHandler 'asset_version' => 'asset', 'importmap' => 'asset-mapper', 'dump' => 'debug-bundle', + 'emoji' => 'emoji', 'encore_entry_link_tags' => 'webpack-encore-bundle', 'encore_entry_script_tags' => 'webpack-encore-bundle', 'expression' => 'expression-language', diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 71fb6a2f8af23..90eb69ed0c645 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -27,6 +27,7 @@ "symfony/asset": "^6.4|^7.0", "symfony/asset-mapper": "^6.4|^7.0", "symfony/dependency-injection": "^6.4|^7.0", + "symfony/emoji": "^7.1", "symfony/finder": "^6.4|^7.0", "symfony/form": "^6.4|^7.0", "symfony/html-sanitizer": "^6.4|^7.0", diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php index 58aa921686204..2cae636081212 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php @@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Emoji\EmojiTransliterator; use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Workflow\Workflow; @@ -31,6 +32,10 @@ public function process(ContainerBuilder $container): void $container->removeDefinition('twig.extension.assets'); } + if (!class_exists(EmojiTransliterator::class)) { + $container->removeDefinition('twig.extension.emoji'); + } + if (!class_exists(Expression::class)) { $container->removeDefinition('twig.extension.expression'); } @@ -125,6 +130,10 @@ public function process(ContainerBuilder $container): void $container->getDefinition('twig.extension.expression')->addTag('twig.extension'); } + if ($container->hasDefinition('twig.extension.emoji')) { + $container->getDefinition('twig.extension.emoji')->addTag('twig.extension'); + } + if (!class_exists(Workflow::class) || !$container->has('workflow.registry')) { $container->removeDefinition('workflow.twig_extension'); } else { diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.php b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.php index e1b47de245580..02631d28c39a4 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.php +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.php @@ -17,6 +17,7 @@ use Symfony\Bridge\Twig\ErrorRenderer\TwigErrorRenderer; use Symfony\Bridge\Twig\EventListener\TemplateAttributeListener; use Symfony\Bridge\Twig\Extension\AssetExtension; +use Symfony\Bridge\Twig\Extension\EmojiExtension; use Symfony\Bridge\Twig\Extension\ExpressionExtension; use Symfony\Bridge\Twig\Extension\HtmlSanitizerExtension; use Symfony\Bridge\Twig\Extension\HttpFoundationExtension; @@ -115,6 +116,8 @@ ->set('twig.extension.expression', ExpressionExtension::class) + ->set('twig.extension.emoji', EmojiExtension::class) + ->set('twig.extension.htmlsanitizer', HtmlSanitizerExtension::class) ->args([tagged_locator('html_sanitizer', 'sanitizer')]) diff --git a/src/Symfony/Bundle/TwigBundle/composer.json b/src/Symfony/Bundle/TwigBundle/composer.json index 88c1dd5b85415..bfee47e03d990 100644 --- a/src/Symfony/Bundle/TwigBundle/composer.json +++ b/src/Symfony/Bundle/TwigBundle/composer.json @@ -20,7 +20,7 @@ "composer-runtime-api": ">=2.1", "symfony/config": "^6.4|^7.0", "symfony/dependency-injection": "^6.4|^7.0", - "symfony/twig-bridge": "^6.4|^7.0", + "symfony/twig-bridge": "^7.1", "symfony/http-foundation": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", "twig/twig": "^3.0.4"