From 88c69c0ec02fb35ff7e9da9f4c76218857f256a9 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 10 Jun 2021 09:06:30 +0200 Subject: [PATCH] [DependencyInjection] Add support of PHP enumerations --- .../DependencyInjection/Dumper/PhpDumper.php | 2 ++ .../DependencyInjection/Dumper/XmlDumper.php | 5 ++++ .../DependencyInjection/Dumper/YamlDumper.php | 2 ++ .../Tests/Dumper/PhpDumperTest.php | 25 ++++++++++++++++ .../Tests/Dumper/XmlDumperTest.php | 19 ++++++++++++ .../Tests/Dumper/YamlDumperTest.php | 19 ++++++++++++ .../Fixtures/FooClassWithEnumAttribute.php | 18 ++++++++++++ .../Tests/Fixtures/FooUnitEnum.php | 8 +++++ .../xml/services_with_enumeration.xml | 9 ++++++ .../xml/services_with_invalid_enumeration.xml | 9 ++++++ .../yaml/services_with_enumeration.yml | 10 +++++++ .../services_with_invalid_enumeration.yml | 10 +++++++ .../Tests/Loader/XmlFileLoaderTest.php | 28 ++++++++++++++++++ .../Tests/Loader/YamlFileLoaderTest.php | 29 +++++++++++++++++++ src/Symfony/Component/Yaml/Inline.php | 2 ++ .../Yaml/Tests/Fixtures/FooUnitEnum.php | 8 +++++ .../Component/Yaml/Tests/InlineTest.php | 9 ++++++ 17 files changed, 212 insertions(+) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooClassWithEnumAttribute.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooUnitEnum.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_enumeration.xml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_invalid_enumeration.xml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_enumeration.yml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_invalid_enumeration.yml create mode 100644 src/Symfony/Component/Yaml/Tests/Fixtures/FooUnitEnum.php diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 711b631d54..3ea0b1b93f 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -1812,6 +1812,8 @@ EOF; return $code; } + } elseif ($value instanceof \UnitEnum) { + return sprintf('\%s::%s', \get_class($value), $value->name); } elseif (\is_object($value) || \is_resource($value)) { throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index 02a501bd2c..8017fc579a 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -313,6 +313,9 @@ class XmlDumper extends Dumper $element->setAttribute('type', 'binary'); $text = $this->document->createTextNode(self::phpToXml(base64_encode($value))); $element->appendChild($text); + } elseif ($value instanceof \UnitEnum) { + $element->setAttribute('type', 'constant'); + $element->appendChild($this->document->createTextNode(self::phpToXml($value))); } else { if (\in_array($value, ['null', 'true', 'false'], true)) { $element->setAttribute('type', 'string'); @@ -366,6 +369,8 @@ class XmlDumper extends Dumper return 'false'; case $value instanceof Parameter: return '%'.$value.'%'; + case $value instanceof \UnitEnum: + return sprintf('%s::%s', \get_class($value), $value->name); case \is_object($value) || \is_resource($value): throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); default: diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index c055a68612..5dce997d9d 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -286,6 +286,8 @@ class YamlDumper extends Dumper return $this->getExpressionCall((string) $value); } elseif ($value instanceof Definition) { return new TaggedValue('service', (new Parser())->parse("_:\n".$this->addService('_', $value), Yaml::PARSE_CUSTOM_TAGS)['_']['_']); + } elseif ($value instanceof \UnitEnum) { + return new TaggedValue('php/const', sprintf('%s::%s', \get_class($value), $value->name)); } elseif (\is_object($value) || \is_resource($value)) { throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index b46fbf937b..3468e35a94 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -40,6 +40,8 @@ use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\DependencyInjection\Tests\Compiler\Foo; use Symfony\Component\DependencyInjection\Tests\Compiler\Wither; use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition; +use Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithEnumAttribute; +use Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum; use Symfony\Component\DependencyInjection\Tests\Fixtures\ScalarFactory; use Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator; use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition1; @@ -1208,6 +1210,29 @@ class PhpDumperTest extends TestCase $this->assertInstanceOf(\stdClass::class, $container->get('bar')); } + /** + * @requires PHP 8.1 + */ + public function testDumpHandlesEnumeration() + { + $container = new ContainerBuilder(); + $container + ->register('foo', FooClassWithEnumAttribute::class) + ->setPublic(true) + ->addArgument(FooUnitEnum::BAR); + + $container->compile(); + + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump([ + 'class' => 'Symfony_DI_PhpDumper_Test_Enumeration', + ])); + + $container = new \Symfony_DI_PhpDumper_Test_Enumeration(); + + $this->assertSame(FooUnitEnum::BAR, $container->get('foo')->getBar()); + } + public function testUninitializedSyntheticReference() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php index dda18a3062..18caa150f2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php @@ -21,6 +21,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Dumper\XmlDumper; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithEnumAttribute; +use Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum; class XmlDumperTest extends TestCase { @@ -249,4 +251,21 @@ class XmlDumperTest extends TestCase $this->assertEquals(file_get_contents(self::$fixturesPath.'/xml/services_abstract.xml'), $dumper->dump()); } + + /** + * @requires PHP 8.1 + */ + public function testDumpHandlesEnumeration() + { + $container = new ContainerBuilder(); + $container + ->register(FooClassWithEnumAttribute::class, FooClassWithEnumAttribute::class) + ->setPublic(true) + ->addArgument(FooUnitEnum::BAR); + + $container->compile(); + $dumper = new XmlDumper($container); + + $this->assertEquals(file_get_contents(self::$fixturesPath.'/xml/services_with_enumeration.xml'), $dumper->dump()); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php index b359f668d7..9a973afe69 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php @@ -22,6 +22,8 @@ use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Dumper\YamlDumper; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithEnumAttribute; +use Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum; use Symfony\Component\Yaml\Parser; use Symfony\Component\Yaml\Yaml; @@ -129,6 +131,23 @@ class YamlDumperTest extends TestCase $this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services_with_service_closure.yml', $dumper->dump()); } + /** + * @requires PHP 8.1 + */ + public function testDumpHandlesEnumeration() + { + $container = new ContainerBuilder(); + $container + ->register(FooClassWithEnumAttribute::class, FooClassWithEnumAttribute::class) + ->setPublic(true) + ->addArgument(FooUnitEnum::BAR); + + $container->compile(); + $dumper = new YamlDumper($container); + + $this->assertEquals(file_get_contents(self::$fixturesPath.'/yaml/services_with_enumeration.yml'), $dumper->dump()); + } + private function assertEqualYamlStructure(string $expected, string $yaml, string $message = '') { $parser = new Parser(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooClassWithEnumAttribute.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooClassWithEnumAttribute.php new file mode 100644 index 0000000000..3b2235efdd --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooClassWithEnumAttribute.php @@ -0,0 +1,18 @@ +bar = $bar; + } + + public function getBar(): FooUnitEnum + { + return $this->bar; + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooUnitEnum.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooUnitEnum.php new file mode 100644 index 0000000000..d51cf9c995 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooUnitEnum.php @@ -0,0 +1,8 @@ + + + + + + Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum::BAR + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_invalid_enumeration.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_invalid_enumeration.xml new file mode 100644 index 0000000000..8864e6d892 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_invalid_enumeration.xml @@ -0,0 +1,9 @@ + + + + + + Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum::BAZ + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_enumeration.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_enumeration.yml new file mode 100644 index 0000000000..46bf505d44 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_enumeration.yml @@ -0,0 +1,10 @@ + +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + public: true + synthetic: true + Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithEnumAttribute: + class: Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithEnumAttribute + public: true + arguments: [!php/const 'Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum::BAR'] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_invalid_enumeration.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_invalid_enumeration.yml new file mode 100644 index 0000000000..b9f74e0f46 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_invalid_enumeration.yml @@ -0,0 +1,10 @@ + +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + public: true + synthetic: true + Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithEnumAttribute: + class: Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithEnumAttribute + public: true + arguments: [!php/const 'Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum::BAZ'] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 80d3393010..ef00b2f721 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -37,6 +37,8 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Tests\Fixtures\Bar; use Symfony\Component\DependencyInjection\Tests\Fixtures\BarInterface; use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; +use Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithEnumAttribute; +use Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum; use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy; use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype; use Symfony\Component\ExpressionLanguage\Expression; @@ -827,6 +829,32 @@ class XmlFileLoaderTest extends TestCase $this->assertSame(['foo' => [[]], 'bar' => [[]]], $definition->getTags()); } + /** + * @requires PHP 8.1 + */ + public function testEnumeration() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_with_enumeration.xml'); + $container->compile(); + + $definition = $container->getDefinition(FooClassWithEnumAttribute::class); + $this->assertSame([FooUnitEnum::BAR], $definition->getArguments()); + } + + /** + * @requires PHP 8.1 + */ + public function testInvalidEnumeration() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + + $this->expectException(\Error::class); + $loader->load('services_with_invalid_enumeration.xml'); + } + public function testInstanceOfAndChildDefinitionNotAllowed() { $this->expectException(InvalidArgumentException::class); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index f367bf3452..910e7b2a24 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -37,6 +37,8 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Tests\Fixtures\Bar; use Symfony\Component\DependencyInjection\Tests\Fixtures\BarInterface; use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; +use Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithEnumAttribute; +use Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum; use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy; use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype; use Symfony\Component\ExpressionLanguage\Expression; @@ -909,6 +911,33 @@ class YamlFileLoaderTest extends TestCase $this->assertNull($iteratorArgument->getIndexAttribute()); } + /** + * @requires PHP 8.1 + */ + public function testEnumeration() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_with_enumeration.yml'); + $container->compile(); + + $definition = $container->getDefinition(FooClassWithEnumAttribute::class); + $this->assertSame([FooUnitEnum::BAR], $definition->getArguments()); + } + + /** + * @requires PHP 8.1 + */ + public function testInvalidEnumeration() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The constant "Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum::BAZ" is not defined'); + $loader->load('services_with_invalid_enumeration.yml'); + } + public function testReturnsClone() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index a317bb0438..10ed14fe51 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -127,6 +127,8 @@ class Inline return self::dumpNull($flags); case $value instanceof \DateTimeInterface: return $value->format('c'); + case $value instanceof \UnitEnum: + return sprintf('!php/const %s::%s', \get_class($value), $value->name); case \is_object($value): if ($value instanceof TaggedValue) { return '!'.$value->getTag().' '.self::dump($value->getValue(), $flags); diff --git a/src/Symfony/Component/Yaml/Tests/Fixtures/FooUnitEnum.php b/src/Symfony/Component/Yaml/Tests/Fixtures/FooUnitEnum.php new file mode 100644 index 0000000000..59092e27e8 --- /dev/null +++ b/src/Symfony/Component/Yaml/Tests/Fixtures/FooUnitEnum.php @@ -0,0 +1,8 @@ +assertSame($expected, Inline::dump($dateTime)); } + /** + * @requires PHP 8.1 + */ + public function testDumpUnitEnum() + { + $this->assertSame("!php/const Symfony\Component\Yaml\Tests\Fixtures\FooUnitEnum::BAR", Inline::dump(FooUnitEnum::BAR)); + } + public function getDateTimeDumpTests() { $tests = [];