From 91b3fba4d4fe600bd606be17263ca520e2606c0d Mon Sep 17 00:00:00 2001 From: Andreas Sundqvist Date: Fri, 10 Jan 2025 15:43:21 +0100 Subject: [PATCH] Fix issue with special characters in constant/enum-case names --- src/PhpConstant.php | 1 + src/Renderer/Php7Renderer.php | 5 ++- src/ValueObject/EnumCase.php | 8 +++- tests/Renderer/PhpConstantTest.php | 8 ++++ tests/Renderer/PhpEnumTest.php | 33 +++++++++++++++++ ...mTest.testCaseWithSpecialCharacters.result | 37 +++++++++++++++++++ ...testCaseWithSpecialCharactersNative.result | 7 ++++ 7 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 tests/Renderer/resources/PhpEnumTest.testCaseWithSpecialCharacters.result create mode 100644 tests/Renderer/resources/PhpEnumTest.testCaseWithSpecialCharactersNative.result diff --git a/src/PhpConstant.php b/src/PhpConstant.php index dcc2448..d5aeef8 100644 --- a/src/PhpConstant.php +++ b/src/PhpConstant.php @@ -41,6 +41,7 @@ public function getName(): string $currentName = $this->identifier; $sanitizedName = (string)preg_replace('/^(\d)/', '_$0', $currentName); $sanitizedName = str_replace('-', '_', $sanitizedName); + $sanitizedName = (string)preg_replace('/[^A-Za-z0-9_]/', '_s_', $sanitizedName); if ($this->case === self::CASE_NONE) { return $sanitizedName; diff --git a/src/Renderer/Php7Renderer.php b/src/Renderer/Php7Renderer.php index b573101..f4e1410 100644 --- a/src/Renderer/Php7Renderer.php +++ b/src/Renderer/Php7Renderer.php @@ -129,8 +129,9 @@ public function renderEnum(PhpEnum $enum): array foreach ($enum->getCases() as $case) { $value = $case instanceof EnumBackedCase ? $case->getValue() : null; - $enum->addConstant(PhpConstant::public($case->getName(), $value)->setCase(PhpConstant::CASE_NONE)); - $tryFromCtorSource[] = 'if ($value === self::' . $case->getName() . ') {'; + $const = PhpConstant::public($case->getName(), $value)->setCase(PhpConstant::CASE_NONE); + $enum->addConstant($const); + $tryFromCtorSource[] = 'if ($value === self::' . $const->getName() . ') {'; $tryFromCtorSource[] = ['return new self($value);']; $tryFromCtorSource[] = '}'; } diff --git a/src/ValueObject/EnumCase.php b/src/ValueObject/EnumCase.php index cec39d5..4bea02c 100644 --- a/src/ValueObject/EnumCase.php +++ b/src/ValueObject/EnumCase.php @@ -10,6 +10,12 @@ public function __construct( public function getName(): string { - return $this->name; + $currentName = $this->name; + $sanitizedName = str_replace('-', '_', $currentName); + $sanitizedName = str_replace(' ', '', ucwords(str_replace('_', ' ', $sanitizedName))); + $sanitizedName = (string)preg_replace('/[^A-Za-z0-9_]/', '_s_', $sanitizedName); + $sanitizedName = (string)preg_replace('/^(\d)/', '_$0', $sanitizedName); + + return $sanitizedName; } } diff --git a/tests/Renderer/PhpConstantTest.php b/tests/Renderer/PhpConstantTest.php index 9c68f9d..07fdf5e 100644 --- a/tests/Renderer/PhpConstantTest.php +++ b/tests/Renderer/PhpConstantTest.php @@ -100,4 +100,12 @@ public function testUpperCaseWithLeadingDigit(): void $this->assertSame(['public const _3DS = \'3DS\';'], $render->renderConstant($const)); } + + public function testSanitizeConstNameFromSpecialCharacters(): void + { + $const = PhpConstant::public(identifier: '>2m'); + $render = new Php7Renderer(); + + $this->assertSame(['public const _S_2M = \'>2m\';'], $render->renderConstant($const)); + } } diff --git a/tests/Renderer/PhpEnumTest.php b/tests/Renderer/PhpEnumTest.php index 0a7975d..01425b6 100644 --- a/tests/Renderer/PhpEnumTest.php +++ b/tests/Renderer/PhpEnumTest.php @@ -8,6 +8,7 @@ use Stefna\PhpCodeBuilder\PhpFile; use Stefna\PhpCodeBuilder\Renderer\Php74Renderer; use Stefna\PhpCodeBuilder\Renderer\Php81Renderer; +use Stefna\PhpCodeBuilder\Renderer\Php8Renderer; use Stefna\PhpCodeBuilder\ValueObject\EnumBackedCase; use Stefna\PhpCodeBuilder\ValueObject\EnumCase; use Stefna\PhpCodeBuilder\ValueObject\Type; @@ -76,4 +77,36 @@ public function testRenderEnumInFile(): void $this->assertSourceResult($renderer->render($file), 'PhpEnumTest.' . __FUNCTION__); } + + public function testCaseWithSpecialCharacters(): void + { + $renderer = new Php8Renderer(); + $enum = new PhpEnum( + 'Test', + Type::fromString('string'), + cases: [ + new EnumBackedCase('2m', '2m'), + new EnumBackedCase('>2m', '>2m'), + ], + ); + $file = PhpFile::createFromClass($enum); + + $this->assertSourceResult($renderer->render($file), 'PhpEnumTest.' . __FUNCTION__); + } + + public function testCaseWithSpecialCharactersNative(): void + { + $renderer = new Php81Renderer(); + $enum = new PhpEnum( + 'Test', + Type::fromString('string'), + cases: [ + new EnumBackedCase('2m', '2m'), + new EnumBackedCase('>2m', '>2m'), + ], + ); + $file = PhpFile::createFromClass($enum); + + $this->assertSourceResult($renderer->render($file), 'PhpEnumTest.' . __FUNCTION__); + } } diff --git a/tests/Renderer/resources/PhpEnumTest.testCaseWithSpecialCharacters.result b/tests/Renderer/resources/PhpEnumTest.testCaseWithSpecialCharacters.result new file mode 100644 index 0000000..84bb609 --- /dev/null +++ b/tests/Renderer/resources/PhpEnumTest.testCaseWithSpecialCharacters.result @@ -0,0 +1,37 @@ +2m'; + + + private function __construct( + public string $value, + ) {} + + public static function from(string $value): self + { + $self = self::tryFrom($value); + if ($self) { + return $self; + } + throw new \ValueError('Enum not found: ' . $value); + } + + public static function tryFrom(string $value): ?self + { + if ($value === self::_2m) { + return new self($value); + } + if ($value === self::_s_2m) { + return new self($value); + } + return null; + } + + public function __toString(): string + { + return (string)$this->value; + } +} diff --git a/tests/Renderer/resources/PhpEnumTest.testCaseWithSpecialCharactersNative.result b/tests/Renderer/resources/PhpEnumTest.testCaseWithSpecialCharactersNative.result new file mode 100644 index 0000000..6a7a9c7 --- /dev/null +++ b/tests/Renderer/resources/PhpEnumTest.testCaseWithSpecialCharactersNative.result @@ -0,0 +1,7 @@ +2m'; +}