mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-01 04:57:23 +00:00
Implement the enum_cases function
The implementation contains an optimized implementation of the function for the common case of using a string literal as the argument. It will validate the enum existence during compilation and compile the code to use `MyEnum::cases()` directly.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
# 3.11.1 (2024-XX-XX)
|
||||
# 3.12.0 (2024-XX-XX)
|
||||
|
||||
* Fix integration tests when a test has more than on data/expect section and deprecations
|
||||
* Add the `enum_cases` function
|
||||
|
||||
# 3.11.0 (2024-08-08)
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
``enum_cases``
|
||||
==============
|
||||
|
||||
.. versionadded:: 3.12
|
||||
|
||||
The ``enum_cases`` function was added in Twig 3.12.
|
||||
|
||||
``enum_cases`` returns the list of cases for a given enum:
|
||||
|
||||
.. code-block:: twig
|
||||
|
||||
{% for case in enum_cases('App\\MyEnum') %}
|
||||
{{ case.value }}
|
||||
{% endfor %}
|
||||
|
||||
When using a string literal for the ``enum`` argument, it will be validated during compile time to be a valid enum name.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``enum``: The FQCN of the enum
|
||||
@@ -10,6 +10,7 @@ Functions
|
||||
cycle
|
||||
date
|
||||
dump
|
||||
enum_cases
|
||||
html_classes
|
||||
include
|
||||
max
|
||||
|
||||
@@ -45,6 +45,7 @@ use Twig\Node\Expression\Binary\SpaceshipBinary;
|
||||
use Twig\Node\Expression\Binary\StartsWithBinary;
|
||||
use Twig\Node\Expression\Binary\SubBinary;
|
||||
use Twig\Node\Expression\Filter\DefaultFilter;
|
||||
use Twig\Node\Expression\FunctionNode\EnumCasesFunction;
|
||||
use Twig\Node\Expression\NullCoalesceExpression;
|
||||
use Twig\Node\Expression\Test\ConstantTest;
|
||||
use Twig\Node\Expression\Test\DefinedTest;
|
||||
@@ -246,6 +247,7 @@ final class CoreExtension extends AbstractExtension
|
||||
new TwigFunction('date', [$this, 'convertDate']),
|
||||
new TwigFunction('include', [self::class, 'include'], ['needs_environment' => true, 'needs_context' => true, 'is_safe' => ['all']]),
|
||||
new TwigFunction('source', [self::class, 'source'], ['needs_environment' => true, 'is_safe' => ['all']]),
|
||||
new TwigFunction('enum_cases', [self::class, 'enumCases'], ['node_class' => EnumCasesFunction::class]),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1449,6 +1451,26 @@ final class CoreExtension extends AbstractExtension
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of cases of the enum.
|
||||
*
|
||||
* @template T of \UnitEnum
|
||||
*
|
||||
* @param class-string<T> $enum
|
||||
*
|
||||
* @return list<T>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function enumCases(string $enum): array
|
||||
{
|
||||
if (!enum_exists($enum)) {
|
||||
throw new RuntimeError(\sprintf('Enum "%s" does not exist.', $enum));
|
||||
}
|
||||
|
||||
return $enum::cases();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the ability to get constants from instances as well as class/global constants.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Twig\Node\Expression\FunctionNode;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\FunctionExpression;
|
||||
|
||||
class EnumCasesFunction extends FunctionExpression
|
||||
{
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$arguments = $this->getNode('arguments');
|
||||
if ($arguments->hasNode('enum')) {
|
||||
$firstArgument = $arguments->getNode('enum');
|
||||
} elseif ($arguments->hasNode('0')) {
|
||||
$firstArgument = $arguments->getNode('0');
|
||||
} else {
|
||||
$firstArgument = null;
|
||||
}
|
||||
|
||||
if (!$firstArgument instanceof ConstantExpression || \count($arguments) !== 1) {
|
||||
parent::compile($compiler);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$value = $firstArgument->getAttribute('value');
|
||||
|
||||
if (!\is_string($value)) {
|
||||
throw new SyntaxError('The first argument of the "enum_cases" function must be a string.', $this->getTemplateLine(), $this->getSourceContext());
|
||||
}
|
||||
|
||||
if (!enum_exists($value)) {
|
||||
throw new SyntaxError(\sprintf('The first argument of the "enum_cases" function must be the name of an enum, "%s" given.', $value), $this->getTemplateLine(), $this->getSourceContext());
|
||||
}
|
||||
|
||||
$compiler->raw(\sprintf('%s::cases()', $value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Twig\Tests;
|
||||
|
||||
enum DummyBackedEnum: string
|
||||
{
|
||||
case FOO = 'foo';
|
||||
case BAR = 'bar';
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Twig\Tests;
|
||||
|
||||
enum DummyUnitEnum
|
||||
{
|
||||
case BAR;
|
||||
case BAZ;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
--TEST--
|
||||
"enum_cases" function with invalid dynamic enum class
|
||||
--CONDITION--
|
||||
\PHP_VERSION_ID >= 80100
|
||||
--TEMPLATE--
|
||||
{% set from_variable = 'Twig\\Tests\\NonExistentEnum' %}
|
||||
{% for c in enum_cases(from_variable) %}
|
||||
{{~ c.name }}
|
||||
{% endfor %}
|
||||
--DATA--
|
||||
return []
|
||||
--EXCEPTION--
|
||||
Twig\Error\RuntimeError: Enum "Twig\Tests\NonExistentEnum" does not exist in "index.twig" at line 3.
|
||||
@@ -0,0 +1,10 @@
|
||||
--TEST--
|
||||
"enum_cases" function with invalid enum class
|
||||
--CONDITION--
|
||||
\PHP_VERSION_ID >= 80100
|
||||
--TEMPLATE--
|
||||
{% for c in enum_cases('Twig\\Tests\\NonExistentEnum') %}
|
||||
{{~ c.name }}
|
||||
{% endfor %}
|
||||
--EXCEPTION--
|
||||
Twig\Error\SyntaxError: The first argument of the "enum_cases" function must be the name of an enum, "Twig\Tests\NonExistentEnum" given in "index.twig" at line 2.
|
||||
@@ -0,0 +1,10 @@
|
||||
--TEST--
|
||||
"enum_cases" function with missing \ escaping
|
||||
--CONDITION--
|
||||
\PHP_VERSION_ID >= 80100
|
||||
--TEMPLATE--
|
||||
{% for c in enum_cases('Twig\Tests\DummyBackedEnum') %}
|
||||
{{~ c.name }}
|
||||
{% endfor %}
|
||||
--EXCEPTION--
|
||||
Twig\Error\SyntaxError: The first argument of the "enum_cases" function must be the name of an enum, "TwigTestsDummyBackedEnum" given in "index.twig" at line 2.
|
||||
@@ -0,0 +1,10 @@
|
||||
--TEST--
|
||||
"enum_cases" function with invalid literal type
|
||||
--CONDITION--
|
||||
\PHP_VERSION_ID >= 80100
|
||||
--TEMPLATE--
|
||||
{% for c in enum_cases(13) %}
|
||||
{{~ c.name }}
|
||||
{% endfor %}
|
||||
--EXCEPTION--
|
||||
Twig\Error\SyntaxError: The first argument of the "enum_cases" function must be a string in "index.twig" at line 2.
|
||||
@@ -0,0 +1,24 @@
|
||||
--TEST--
|
||||
"enum_cases" function
|
||||
--CONDITION--
|
||||
\PHP_VERSION_ID >= 80100
|
||||
--TEMPLATE--
|
||||
{% for c in enum_cases('Twig\\Tests\\DummyBackedEnum') %}
|
||||
{{~ c.name }}: {{ c.value }}
|
||||
{% endfor %}
|
||||
{% for c in enum_cases('Twig\\Tests\\DummyUnitEnum') %}
|
||||
{{~ c.name }}
|
||||
{% endfor %}
|
||||
{% set from_variable='Twig\\Tests\\DummyUnitEnum' %}
|
||||
{% for c in enum_cases(from_variable) %}
|
||||
{{~ c.name }}
|
||||
{% endfor %}
|
||||
--DATA--
|
||||
return []
|
||||
--EXPECT--
|
||||
FOO: foo
|
||||
BAR: bar
|
||||
BAR
|
||||
BAZ
|
||||
BAR
|
||||
BAZ
|
||||
Reference in New Issue
Block a user