diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 54095a8d37ae5..95a0cf8e54cdd 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.2 +--- + + * Add `binary_flags` parameter and argument types in `XmlFileLoader` + 7.1 --- diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index f2f464305f7ff..4404d45c234a5 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -623,6 +623,10 @@ private function getArgumentsAsPhp(\DOMElement $node, string $name, string $file case 'constant': $arguments[$key] = \constant(trim($arg->nodeValue)); break; + case 'binary_flags': + $constants = \array_map(\constant(...), \array_map(trim(...), \explode('|', $arg->nodeValue))); + $arguments[$key] = \array_reduce($constants, static fn ($carry, $item) => $carry | $item, 0); + break; default: $arguments[$key] = XmlUtils::phpize($trim ? trim($arg->nodeValue) : $arg->nodeValue); } diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index c071e3466613c..4679bb55f02f4 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -342,6 +342,7 @@ + @@ -354,6 +355,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooClassWithFlags.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooClassWithFlags.php new file mode 100644 index 0000000000000..bbee77cae6413 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooClassWithFlags.php @@ -0,0 +1,14 @@ + + + + Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithFlags::FLAG_A + + + + + Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithFlags::FLAG_A | Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithFlags::FLAG_C + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_invalid_binary_flags.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_invalid_binary_flags.xml new file mode 100644 index 0000000000000..add1782de2ec5 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_invalid_binary_flags.xml @@ -0,0 +1,9 @@ + + + + + + Symfony\Component\DependencyInjection\Tests\Fixtures\FooClassWithFlags::WRONG_FLAG + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 64df6cc7f79b5..b0fcec8dd7773 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -40,6 +40,7 @@ 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\FooClassWithFlags; use Symfony\Component\DependencyInjection\Tests\Fixtures\FooUnitEnum; use Symfony\Component\DependencyInjection\Tests\Fixtures\FooWithAbstractArgument; use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy; @@ -964,6 +965,26 @@ public function testInvalidEnumeration() $loader->load('services_with_invalid_enumeration.xml'); } + public function testBinaryFlags() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_with_binary_flags.xml'); + $container->compile(); + + $definition = $container->getDefinition(FooClassWithFlags::class); + $this->assertSame([FooClassWithFlags::FLAG_A | FooClassWithFlags::FLAG_C], $definition->getArguments()); + } + + public function testInvalidBinaryFlags() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + + $this->expectException(\Error::class); + $loader->load('services_with_invalid_binary_flags.xml'); + } + public function testInstanceOfAndChildDefinition() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/Yaml/CHANGELOG.md b/src/Symfony/Component/Yaml/CHANGELOG.md index 05b23cd7b06fe..15bf4feb54401 100644 --- a/src/Symfony/Component/Yaml/CHANGELOG.md +++ b/src/Symfony/Component/Yaml/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Deprecate parsing duplicate mapping keys whose value is `null` + * Add support for binary flags with the `!!binary_flags` tag 7.1 --- diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index 58d389866f52e..241722fc6b750 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -681,6 +681,15 @@ private static function evaluateScalar(string $scalar, int $flags, array &$refer return (float) substr($scalar, 8); case str_starts_with($scalar, '!!binary '): return self::evaluateBinaryScalar(substr($scalar, 9)); + case str_starts_with($scalar, '!!binary_flags '): + if (self::$constantSupport) { + return self::evaluateBinaryFlags(substr($scalar, 15)); + } + if (self::$exceptionOnInvalidType) { + throw new ParseException(\sprintf('The string "%s" could not be parsed as binary flags. Did you forget to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); + } + + return null; } throw new ParseException(\sprintf('The string "%s" could not be parsed as it uses an unsupported built-in tag.', $scalar), self::$parsedLineNumber, $scalar, self::$parsedFilename); @@ -797,6 +806,28 @@ public static function evaluateBinaryScalar(string $scalar): string return base64_decode($parsedBinaryData, true); } + public static function evaluateBinaryFlags(string $flags): int + { + $constants = \explode('|', $flags); + $result = 0; + + foreach ($constants as $rawConstant) { + $rawConstant = \trim($rawConstant); + if (!\defined($rawConstant)) { + throw new ParseException(\sprintf('The constant "%s" is not defined.', $rawConstant), self::$parsedLineNumber + 1, $flags, self::$parsedFilename); + } + + $constant = \constant($rawConstant); + if (!\is_int($constant)) { + throw new ParseException(\sprintf('The constant "%s" is not an integer.', $rawConstant), self::$parsedLineNumber + 1, $flags, self::$parsedFilename); + } + + $result |= $constant; + } + + return $result; + } + private static function isBinaryString(string $value): bool { return !preg_match('//u', $value) || preg_match('/[^\x00\x07-\x0d\x1B\x20-\xff]/', $value); diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php index e58ca51149c14..6d29f9a05d3f4 100644 --- a/src/Symfony/Component/Yaml/Tests/InlineTest.php +++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php @@ -119,6 +119,42 @@ public function testParsePhpEnumThrowsExceptionOnInvalidType() Inline::parse('!php/enum SomeEnum::Foo', Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE); } + public function testParseSingleBinaryFlag() + { + $this->assertSame(Yaml::PARSE_CONSTANT, Inline::parse('!!binary_flags Symfony\Component\Yaml\Yaml::PARSE_CONSTANT', Yaml::PARSE_CONSTANT)); + } + + public function testParseMultipleBinaryFlags() + { + $this->assertSame(Yaml::PARSE_CONSTANT | Yaml::PARSE_CUSTOM_TAGS, Inline::parse('!!binary_flags Symfony\Component\Yaml\Yaml::PARSE_CONSTANT | Symfony\Component\Yaml\Yaml::PARSE_CUSTOM_TAGS', Yaml::PARSE_CONSTANT)); + } + + public function testParseBinaryFlagsThrowsExceptionWhenUndefined() + { + $this->expectException(ParseException::class); + $this->expectExceptionMessage('The constant "WRONG_CONSTANT" is not defined'); + Inline::parse('!!binary_flags WRONG_CONSTANT', Yaml::PARSE_CONSTANT); + } + + public function testParseBinaryFlagsThrowsExceptionWhenNotAnInt() + { + $this->expectException(ParseException::class); + $this->expectExceptionMessage('The constant "DIRECTORY_SEPARATOR" is not an integer'); + Inline::parse('!!binary_flags DIRECTORY_SEPARATOR', Yaml::PARSE_CONSTANT); + } + + public function testParseBinaryFlagsWithoutFlags() + { + $this->assertNull(Inline::parse('!!binary_flags PHP_INT_MAX')); + } + + public function testParseBinaryFlagsWithoutOptionToParseConstants() + { + $this->expectException(ParseException::class); + $this->expectExceptionMessageMatches('/The string "!!binary_flags PHP_INT_MAX" could not be parsed as binary flags.*/'); + Inline::parse('!!binary_flags PHP_INT_MAX', Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE); + } + /** * @dataProvider getTestsForDump */