diff --git a/src/Mapper/Object/Factory/ConstructorObjectBuilderFactory.php b/src/Mapper/Object/Factory/ConstructorObjectBuilderFactory.php index 15232c1b..4c6bed8a 100644 --- a/src/Mapper/Object/Factory/ConstructorObjectBuilderFactory.php +++ b/src/Mapper/Object/Factory/ConstructorObjectBuilderFactory.php @@ -24,6 +24,7 @@ use CuyZ\Valinor\Type\Types\ClassStringType; use CuyZ\Valinor\Type\Types\EnumType; use CuyZ\Valinor\Type\Types\NativeStringType; +use CuyZ\Valinor\Utility\Reflection\Reflection; use function array_key_exists; use function array_values; @@ -176,7 +177,10 @@ private function filteredConstructors(): array foreach ($this->constructors as $constructor) { $function = $constructor->definition; - if (enum_exists($function->class ?? '') && in_array($function->name, ['from', 'tryFrom'], true)) { + if ($function->class + && Reflection::enumExists($function->class) + && in_array($function->name, ['from', 'tryFrom'], true) + ) { continue; } diff --git a/src/Mapper/Object/Factory/ReflectionObjectBuilderFactory.php b/src/Mapper/Object/Factory/ReflectionObjectBuilderFactory.php index d7f1391d..187f11bc 100644 --- a/src/Mapper/Object/Factory/ReflectionObjectBuilderFactory.php +++ b/src/Mapper/Object/Factory/ReflectionObjectBuilderFactory.php @@ -6,15 +6,14 @@ use CuyZ\Valinor\Definition\ClassDefinition; use CuyZ\Valinor\Mapper\Object\ReflectionObjectBuilder; - -use function enum_exists; +use CuyZ\Valinor\Utility\Reflection\Reflection; /** @internal */ final class ReflectionObjectBuilderFactory implements ObjectBuilderFactory { public function for(ClassDefinition $class): array { - if (enum_exists($class->name)) { + if (Reflection::enumExists($class->name)) { return []; } diff --git a/src/Type/Parser/Factory/LexingTypeParserFactory.php b/src/Type/Parser/Factory/LexingTypeParserFactory.php index facde920..186e5ee8 100644 --- a/src/Type/Parser/Factory/LexingTypeParserFactory.php +++ b/src/Type/Parser/Factory/LexingTypeParserFactory.php @@ -8,6 +8,7 @@ use CuyZ\Valinor\Type\Parser\Factory\Specifications\TypeParserSpecification; use CuyZ\Valinor\Type\Parser\Lexer\AdvancedClassLexer; use CuyZ\Valinor\Type\Parser\Lexer\NativeLexer; +use CuyZ\Valinor\Type\Parser\Lexer\ObjectLexer; use CuyZ\Valinor\Type\Parser\LexingParser; use CuyZ\Valinor\Type\Parser\TypeParser; @@ -22,20 +23,23 @@ public function get(TypeParserSpecification ...$specifications): TypeParser return $this->nativeParser ??= $this->nativeParser(); } - $lexer = new NativeLexer(); + $lexer = new ObjectLexer(); $lexer = new AdvancedClassLexer($lexer, $this); foreach ($specifications as $specification) { $lexer = $specification->transform($lexer); } + $lexer = new NativeLexer($lexer); + return new LexingParser($lexer); } private function nativeParser(): TypeParser { - $lexer = new NativeLexer(); + $lexer = new ObjectLexer(); $lexer = new AdvancedClassLexer($lexer, $this); + $lexer = new NativeLexer($lexer); $parser = new LexingParser($lexer); return new CachedParser($parser); diff --git a/src/Type/Parser/Lexer/NativeLexer.php b/src/Type/Parser/Lexer/NativeLexer.php index c1ce8e58..3f8340a9 100644 --- a/src/Type/Parser/Lexer/NativeLexer.php +++ b/src/Type/Parser/Lexer/NativeLexer.php @@ -6,7 +6,6 @@ use CuyZ\Valinor\Type\Parser\Lexer\Token\ArrayToken; use CuyZ\Valinor\Type\Parser\Lexer\Token\CallableToken; -use CuyZ\Valinor\Type\Parser\Lexer\Token\ClassNameToken; use CuyZ\Valinor\Type\Parser\Lexer\Token\ClassStringToken; use CuyZ\Valinor\Type\Parser\Lexer\Token\ClosingBracketToken; use CuyZ\Valinor\Type\Parser\Lexer\Token\ClosingCurlyBracketToken; @@ -14,7 +13,6 @@ use CuyZ\Valinor\Type\Parser\Lexer\Token\ColonToken; use CuyZ\Valinor\Type\Parser\Lexer\Token\CommaToken; use CuyZ\Valinor\Type\Parser\Lexer\Token\DoubleColonToken; -use CuyZ\Valinor\Type\Parser\Lexer\Token\EnumNameToken; use CuyZ\Valinor\Type\Parser\Lexer\Token\FloatValueToken; use CuyZ\Valinor\Type\Parser\Lexer\Token\IntegerToken; use CuyZ\Valinor\Type\Parser\Lexer\Token\IntegerValueToken; @@ -29,9 +27,6 @@ use CuyZ\Valinor\Type\Parser\Lexer\Token\QuoteToken; use CuyZ\Valinor\Type\Parser\Lexer\Token\Token; use CuyZ\Valinor\Type\Parser\Lexer\Token\UnionToken; -use CuyZ\Valinor\Type\Parser\Lexer\Token\UnknownSymbolToken; -use CuyZ\Valinor\Utility\Reflection\Reflection; -use UnitEnum; use function filter_var; use function is_numeric; @@ -40,6 +35,8 @@ /** @internal */ final class NativeLexer implements TypeLexer { + public function __construct(private TypeLexer $delegate) {} + public function tokenize(string $symbol): Token { if (NativeToken::accepts($symbol)) { @@ -83,16 +80,6 @@ public function tokenize(string $symbol): Token return new FloatValueToken((float)$symbol); } - if (enum_exists($symbol)) { - /** @var class-string $symbol */ - return new EnumNameToken($symbol); - } - - if (Reflection::classOrInterfaceExists($symbol)) { - /** @var class-string $symbol */ - return new ClassNameToken($symbol); - } - - return new UnknownSymbolToken($symbol); + return $this->delegate->tokenize($symbol); } } diff --git a/src/Type/Parser/Lexer/ObjectLexer.php b/src/Type/Parser/Lexer/ObjectLexer.php new file mode 100644 index 00000000..7f016fbf --- /dev/null +++ b/src/Type/Parser/Lexer/ObjectLexer.php @@ -0,0 +1,31 @@ + $symbol */ + return new EnumNameToken($symbol); + } + + if (Reflection::classOrInterfaceExists($symbol)) { + /** @var class-string $symbol */ + return new ClassNameToken($symbol); + } + + return new UnknownSymbolToken($symbol); + } +} diff --git a/src/Utility/Reflection/Reflection.php b/src/Utility/Reflection/Reflection.php index 5d26e148..36878a45 100644 --- a/src/Utility/Reflection/Reflection.php +++ b/src/Utility/Reflection/Reflection.php @@ -23,8 +23,10 @@ use function array_filter; use function array_map; use function class_exists; +use function enum_exists; use function implode; use function interface_exists; +use function ltrim; use function spl_object_hash; use function str_contains; @@ -37,16 +39,26 @@ final class Reflection /** @var array */ private static array $functionReflection = []; + /** @var array */ + private static array $classOrInterfaceExists = []; + + /** @var array */ + private static array $enumExists = []; + /** * Case-sensitive implementation of `class_exists` and `interface_exists`. */ public static function classOrInterfaceExists(string $name): bool { - if (! class_exists($name) && ! interface_exists($name)) { - return false; - } + // @infection-ignore-all / We don't need to test the cache + return self::$classOrInterfaceExists[$name] ??= (class_exists($name) || interface_exists($name)) + && self::class($name)->name === ltrim($name, '\\'); + } - return self::class($name)->name === ltrim($name, '\\'); + public static function enumExists(string $name): bool + { + // @infection-ignore-all / We don't need to test the cache + return self::$enumExists[$name] ??= enum_exists($name); } /** diff --git a/tests/Functional/Type/Parser/Lexer/NativeLexerTest.php b/tests/Functional/Type/Parser/Lexer/NativeLexerTest.php index 9f9e3850..cda6bdd5 100644 --- a/tests/Functional/Type/Parser/Lexer/NativeLexerTest.php +++ b/tests/Functional/Type/Parser/Lexer/NativeLexerTest.php @@ -42,6 +42,7 @@ use CuyZ\Valinor\Type\Parser\Exception\Scalar\IntegerRangeMissingMinValue; use CuyZ\Valinor\Type\Parser\Exception\Scalar\InvalidClassStringSubType; use CuyZ\Valinor\Type\Parser\Lexer\NativeLexer; +use CuyZ\Valinor\Type\Parser\Lexer\ObjectLexer; use CuyZ\Valinor\Type\Parser\LexingParser; use CuyZ\Valinor\Type\Parser\TypeParser; use CuyZ\Valinor\Type\StringType; @@ -89,7 +90,7 @@ protected function setUp(): void { parent::setUp(); - $lexer = new NativeLexer(); + $lexer = new NativeLexer(new ObjectLexer()); $this->parser = new LexingParser($lexer); } diff --git a/tests/Unit/Type/Parser/Lexer/NativeLexerTest.php b/tests/Unit/Type/Parser/Lexer/NativeLexerTest.php index fd3fb5c7..289bf3ab 100644 --- a/tests/Unit/Type/Parser/Lexer/NativeLexerTest.php +++ b/tests/Unit/Type/Parser/Lexer/NativeLexerTest.php @@ -6,6 +6,7 @@ use CuyZ\Valinor\Tests\Fixture\Enum\PureEnum; use CuyZ\Valinor\Type\Parser\Lexer\NativeLexer; +use CuyZ\Valinor\Type\Parser\Lexer\ObjectLexer; use CuyZ\Valinor\Type\Parser\Lexer\Token\ArrayToken; use CuyZ\Valinor\Type\Parser\Lexer\Token\ClassNameToken; use CuyZ\Valinor\Type\Parser\Lexer\Token\ClassStringToken; @@ -41,7 +42,7 @@ protected function setUp(): void { parent::setUp(); - $this->lexer = new NativeLexer(); + $this->lexer = new NativeLexer(new ObjectLexer()); } /**