From 5fb92b5bb5f45ca943ade96b4bab2786ea356c8c Mon Sep 17 00:00:00 2001 From: Maxcastel Date: Fri, 23 Oct 2026 16:00:13 +0200 Subject: [PATCH 1/2] fix(metadata): parse YAML booleans with filter_var --- .../Extractor/ResourceExtractorTrait.php | 2 +- .../Extractor/YamlPropertyExtractor.php | 2 +- .../Extractor/YamlPropertyExtractorTest.php | 29 +++++++++++++++++++ .../properties_boolean_strings.yml | 5 ++++ 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 src/Metadata/Tests/Extractor/YamlPropertyExtractorTest.php create mode 100644 src/Metadata/Tests/Fixtures/FileConfigurations/properties_boolean_strings.yml diff --git a/src/Metadata/Extractor/ResourceExtractorTrait.php b/src/Metadata/Extractor/ResourceExtractorTrait.php index 871ea17dcce..15bfbfe2892 100644 --- a/src/Metadata/Extractor/ResourceExtractorTrait.php +++ b/src/Metadata/Extractor/ResourceExtractorTrait.php @@ -72,7 +72,7 @@ private function phpize(\SimpleXMLElement|array|null $resource, string $key, str return (bool) XmlUtils::phpize($resource[$key]); } - return \in_array($resource[$key], ['1', 'true', 1, true], false); + return \is_bool($resource[$key]) ? $resource[$key] : filter_var($resource[$key], \FILTER_VALIDATE_BOOLEAN); } throw new InvalidArgumentException(\sprintf('The property "%s" must be a "%s", "%s" given.', $key, $type, \gettype($resource[$key]))); diff --git a/src/Metadata/Extractor/YamlPropertyExtractor.php b/src/Metadata/Extractor/YamlPropertyExtractor.php index 1b95ed8a873..e45df890ee7 100644 --- a/src/Metadata/Extractor/YamlPropertyExtractor.php +++ b/src/Metadata/Extractor/YamlPropertyExtractor.php @@ -126,7 +126,7 @@ private function phpize(?array $resource, string $key, string $type, mixed $defa 'bool|string' => \in_array($resource[$key], ['1', '0', 1, 0, 'true', 'false', true, false], true) ? $this->phpize($resource, $key, 'bool') : $this->phpize($resource, $key, 'string'), 'string' => (string) $resource[$key], 'integer' => (int) $resource[$key], - 'bool' => \in_array($resource[$key], ['1', 'true', 1, true], false), + 'bool' => \is_bool($resource[$key]) ? $resource[$key] : filter_var($resource[$key], \FILTER_VALIDATE_BOOLEAN), default => throw new InvalidArgumentException(\sprintf('The property "%s" must be a "%s", "%s" given.', $key, $type, \gettype($resource[$key]))), }; } diff --git a/src/Metadata/Tests/Extractor/YamlPropertyExtractorTest.php b/src/Metadata/Tests/Extractor/YamlPropertyExtractorTest.php new file mode 100644 index 00000000000..977da7c5d61 --- /dev/null +++ b/src/Metadata/Tests/Extractor/YamlPropertyExtractorTest.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Metadata\Tests\Extractor; + +use ApiPlatform\Metadata\Extractor\YamlPropertyExtractor; +use ApiPlatform\Metadata\Tests\Fixtures\ApiResource\FileConfigDummy; +use PHPUnit\Framework\TestCase; + +final class YamlPropertyExtractorTest extends TestCase +{ + public function testBooleanStrings(): void + { + $properties = (new YamlPropertyExtractor([__DIR__.'/../Fixtures/FileConfigurations/properties_boolean_strings.yml']))->getProperties(); + + $this->assertFalse($properties[FileConfigDummy::class]['foo']['readable']); + $this->assertTrue($properties[FileConfigDummy::class]['foo']['writable']); + } +} diff --git a/src/Metadata/Tests/Fixtures/FileConfigurations/properties_boolean_strings.yml b/src/Metadata/Tests/Fixtures/FileConfigurations/properties_boolean_strings.yml new file mode 100644 index 00000000000..f9299a6fccc --- /dev/null +++ b/src/Metadata/Tests/Fixtures/FileConfigurations/properties_boolean_strings.yml @@ -0,0 +1,5 @@ +properties: + 'ApiPlatform\Metadata\Tests\Fixtures\ApiResource\FileConfigDummy': + 'foo': + readable: 'false' + writable: 'yes' From 18698867b3fc6b8a45bef0b8e1df22a9f52c7a0d Mon Sep 17 00:00:00 2001 From: Maxcastel Date: Fri, 23 Oct 2026 16:01:24 +0200 Subject: [PATCH 2/2] fix(metadata): keep YAML input and output arrays --- src/Metadata/Extractor/YamlResourceExtractor.php | 16 ++++++++++++++-- .../Tests/Extractor/YamlExtractorTest.php | 8 ++++++++ .../Tests/Extractor/yaml/scalar-types.yaml | 6 ++++++ 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 src/Metadata/Tests/Extractor/yaml/scalar-types.yaml diff --git a/src/Metadata/Extractor/YamlResourceExtractor.php b/src/Metadata/Extractor/YamlResourceExtractor.php index f73d3a6290a..c1056055f20 100644 --- a/src/Metadata/Extractor/YamlResourceExtractor.php +++ b/src/Metadata/Extractor/YamlResourceExtractor.php @@ -161,8 +161,8 @@ private function buildBase(array $resource): array 'securityPostDenormalizeMessage' => $this->phpize($resource, 'securityPostDenormalizeMessage', 'string'), 'securityPostValidation' => $this->resolveExpressionPlaceholder($this->phpize($resource, 'securityPostValidation', 'string', resolve: false)), 'securityPostValidationMessage' => $this->phpize($resource, 'securityPostValidationMessage', 'string'), - 'input' => $this->phpize($resource, 'input', 'bool|string'), - 'output' => $this->phpize($resource, 'output', 'bool|string'), + 'input' => $this->buildInputOutput($resource, 'input'), + 'output' => $this->buildInputOutput($resource, 'output'), 'normalizationContext' => $this->buildArrayValue($resource, 'normalizationContext'), 'denormalizationContext' => $this->buildArrayValue($resource, 'denormalizationContext'), 'collectDenormalizationErrors' => $this->phpize($resource, 'collectDenormalizationErrors', 'bool'), @@ -306,6 +306,18 @@ private function buildMercure(array $resource): array|bool|string|null return $resource['mercure']; } + /** + * @return array{class?: string, name?: string}|bool|string|null + */ + private function buildInputOutput(array $resource, string $key): array|bool|string|null + { + if (\is_array($resource[$key] ?? null)) { + return $resource[$key]; + } + + return $this->phpize($resource, $key, 'bool|string'); + } + private function buildMessenger(array $resource): bool|array|string|null { if (!\array_key_exists('messenger', $resource)) { diff --git a/src/Metadata/Tests/Extractor/YamlExtractorTest.php b/src/Metadata/Tests/Extractor/YamlExtractorTest.php index 62d820e1c84..fea2e23d773 100644 --- a/src/Metadata/Tests/Extractor/YamlExtractorTest.php +++ b/src/Metadata/Tests/Extractor/YamlExtractorTest.php @@ -581,6 +581,14 @@ public function testInputAndOutputAreStrings(): void $this->assertSame(Program::class.'Output', $resources[Program::class][0]['operations'][0]['output']); } + public function testScalarTypes(): void + { + $resource = (new YamlResourceExtractor([__DIR__.'/yaml/scalar-types.yaml']))->getResources()[Program::class][0]; + + $this->assertFalse($resource['paginationEnabled']); + $this->assertSame(['class' => Program::class, 'name' => 'ProgramInput'], $resource['input']); + } + public function testExtendingYamlResourcesByMultipleYamlFiles(): void { $extractor = new YamlResourceExtractor([ diff --git a/src/Metadata/Tests/Extractor/yaml/scalar-types.yaml b/src/Metadata/Tests/Extractor/yaml/scalar-types.yaml new file mode 100644 index 00000000000..1c78bd425c5 --- /dev/null +++ b/src/Metadata/Tests/Extractor/yaml/scalar-types.yaml @@ -0,0 +1,6 @@ +resources: + ApiPlatform\Metadata\Tests\Fixtures\ApiResource\Program: + - paginationEnabled: 'false' + input: + class: ApiPlatform\Metadata\Tests\Fixtures\ApiResource\Program + name: ProgramInput