mirror of
https://github.com/danog/Valinor.git
synced 2024-11-26 20:24:40 +01:00
fix: handle classes in a case-sensitive way in type parser
This commit is contained in:
parent
6414e9cf14
commit
2540741171
@ -6,14 +6,12 @@ namespace CuyZ\Valinor\Type\Parser\Lexer;
|
|||||||
|
|
||||||
use CuyZ\Valinor\Type\Parser\Lexer\Token\Token;
|
use CuyZ\Valinor\Type\Parser\Lexer\Token\Token;
|
||||||
use CuyZ\Valinor\Utility\Reflection\ClassAliasParser;
|
use CuyZ\Valinor\Utility\Reflection\ClassAliasParser;
|
||||||
|
use CuyZ\Valinor\Utility\Reflection\Reflection;
|
||||||
use ReflectionClass;
|
use ReflectionClass;
|
||||||
|
|
||||||
use ReflectionFunction;
|
use ReflectionFunction;
|
||||||
|
|
||||||
use Reflector;
|
use Reflector;
|
||||||
|
|
||||||
use function class_exists;
|
use function strtolower;
|
||||||
use function interface_exists;
|
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
final class AliasLexer implements TypeLexer
|
final class AliasLexer implements TypeLexer
|
||||||
@ -43,7 +41,7 @@ final class AliasLexer implements TypeLexer
|
|||||||
{
|
{
|
||||||
$alias = ClassAliasParser::get()->resolveAlias($symbol, $this->reflection);
|
$alias = ClassAliasParser::get()->resolveAlias($symbol, $this->reflection);
|
||||||
|
|
||||||
if ($alias !== $symbol) {
|
if (strtolower($alias) !== strtolower($symbol)) {
|
||||||
return $alias;
|
return $alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +75,7 @@ final class AliasLexer implements TypeLexer
|
|||||||
|
|
||||||
$full = $namespace . '\\' . $symbol;
|
$full = $namespace . '\\' . $symbol;
|
||||||
|
|
||||||
if (class_exists($full) || interface_exists($full)) {
|
if (Reflection::classOrInterfaceExists($full)) {
|
||||||
return $full;
|
return $full;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,11 +29,10 @@ use CuyZ\Valinor\Type\Parser\Lexer\Token\Token;
|
|||||||
use CuyZ\Valinor\Type\Parser\Lexer\Token\UnionToken;
|
use CuyZ\Valinor\Type\Parser\Lexer\Token\UnionToken;
|
||||||
use CuyZ\Valinor\Type\Parser\Lexer\Token\UnknownSymbolToken;
|
use CuyZ\Valinor\Type\Parser\Lexer\Token\UnknownSymbolToken;
|
||||||
use CuyZ\Valinor\Utility\Polyfill;
|
use CuyZ\Valinor\Utility\Polyfill;
|
||||||
|
use CuyZ\Valinor\Utility\Reflection\Reflection;
|
||||||
use UnitEnum;
|
use UnitEnum;
|
||||||
|
|
||||||
use function class_exists;
|
|
||||||
use function filter_var;
|
use function filter_var;
|
||||||
use function interface_exists;
|
|
||||||
use function is_numeric;
|
use function is_numeric;
|
||||||
use function strtolower;
|
use function strtolower;
|
||||||
use function substr;
|
use function substr;
|
||||||
@ -109,7 +108,8 @@ final class NativeLexer implements TypeLexer
|
|||||||
return new EnumNameToken($symbol);
|
return new EnumNameToken($symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (class_exists($symbol) || interface_exists($symbol)) {
|
if (Reflection::classOrInterfaceExists($symbol)) {
|
||||||
|
/** @var class-string $symbol */
|
||||||
return new ClassNameToken($symbol);
|
return new ClassNameToken($symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,9 +11,8 @@ use CuyZ\Valinor\Type\Type;
|
|||||||
use CuyZ\Valinor\Type\Types\Exception\CannotCastValue;
|
use CuyZ\Valinor\Type\Types\Exception\CannotCastValue;
|
||||||
use CuyZ\Valinor\Type\Types\Exception\InvalidClassString;
|
use CuyZ\Valinor\Type\Types\Exception\InvalidClassString;
|
||||||
use CuyZ\Valinor\Type\Types\Exception\InvalidUnionOfClassString;
|
use CuyZ\Valinor\Type\Types\Exception\InvalidUnionOfClassString;
|
||||||
|
use CuyZ\Valinor\Utility\Reflection\Reflection;
|
||||||
|
|
||||||
use function class_exists;
|
|
||||||
use function interface_exists;
|
|
||||||
use function is_object;
|
use function is_object;
|
||||||
use function is_string;
|
use function is_string;
|
||||||
use function method_exists;
|
use function method_exists;
|
||||||
@ -51,7 +50,7 @@ final class ClassStringType implements StringType, CompositeType
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! class_exists($value) && ! interface_exists($value)) {
|
if (! Reflection::classOrInterfaceExists($value)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,8 +18,10 @@ use ReflectionUnionType;
|
|||||||
use Reflector;
|
use Reflector;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
|
||||||
|
use function class_exists;
|
||||||
use function get_class;
|
use function get_class;
|
||||||
use function implode;
|
use function implode;
|
||||||
|
use function interface_exists;
|
||||||
use function preg_match_all;
|
use function preg_match_all;
|
||||||
use function preg_replace;
|
use function preg_replace;
|
||||||
use function spl_object_hash;
|
use function spl_object_hash;
|
||||||
@ -39,6 +41,18 @@ final class Reflection
|
|||||||
/** @var array<string, ReflectionFunction> */
|
/** @var array<string, ReflectionFunction> */
|
||||||
private static array $functionReflection = [];
|
private static array $functionReflection = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::class($name)->name === ltrim($name, '\\');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param class-string $className
|
* @param class-string $className
|
||||||
* @return ReflectionClass<object>
|
* @return ReflectionClass<object>
|
||||||
@ -132,7 +146,7 @@ final class Reflection
|
|||||||
|
|
||||||
if (PHP_VERSION_ID >= 8_00_00 && $reflection->isPromoted()) {
|
if (PHP_VERSION_ID >= 8_00_00 && $reflection->isPromoted()) {
|
||||||
$type = self::parseDocBlock(
|
$type = self::parseDocBlock(
|
||||||
// @phpstan-ignore-next-line / parameter is promoted so class exists for sure
|
// @phpstan-ignore-next-line / parameter is promoted so class exists for sure
|
||||||
self::sanitizeDocComment($reflection->getDeclaringClass()->getProperty($reflection->name)),
|
self::sanitizeDocComment($reflection->getDeclaringClass()->getProperty($reflection->name)),
|
||||||
sprintf('@%s?var\s+%s', self::TOOL_EXPRESSION, self::TYPE_EXPRESSION)
|
sprintf('@%s?var\s+%s', self::TOOL_EXPRESSION, self::TYPE_EXPRESSION)
|
||||||
);
|
);
|
||||||
|
@ -563,6 +563,11 @@ final class NativeLexerTest extends TestCase
|
|||||||
'transformed' => 'array{foo: string}',
|
'transformed' => 'array{foo: string}',
|
||||||
'type' => ShapedArrayType::class,
|
'type' => ShapedArrayType::class,
|
||||||
],
|
],
|
||||||
|
'Shaped array with key equal to class name' => [
|
||||||
|
'raw' => 'array{stdclass: string}',
|
||||||
|
'transformed' => 'array{stdclass: string}',
|
||||||
|
'type' => ShapedArrayType::class,
|
||||||
|
],
|
||||||
'Iterable type' => [
|
'Iterable type' => [
|
||||||
'raw' => 'iterable',
|
'raw' => 'iterable',
|
||||||
'transformed' => 'iterable',
|
'transformed' => 'iterable',
|
||||||
|
@ -38,6 +38,9 @@ final class ShapedArrayValuesMappingTest extends IntegrationTest
|
|||||||
1337,
|
1337,
|
||||||
42.404,
|
42.404,
|
||||||
],
|
],
|
||||||
|
'shapedArrayWithClassNameAsKey' => [
|
||||||
|
'stdclass' => 'foo',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ([ShapedArrayValues::class, ShapedArrayValuesWithConstructor::class] as $class) {
|
foreach ([ShapedArrayValues::class, ShapedArrayValuesWithConstructor::class] as $class) {
|
||||||
@ -55,6 +58,7 @@ final class ShapedArrayValuesMappingTest extends IntegrationTest
|
|||||||
self::assertSame('bar', $result->advancedShapedArray['mandatoryString']);
|
self::assertSame('bar', $result->advancedShapedArray['mandatoryString']);
|
||||||
self::assertSame(1337, $result->advancedShapedArray[0]);
|
self::assertSame(1337, $result->advancedShapedArray[0]);
|
||||||
self::assertSame(42.404, $result->advancedShapedArray[1]);
|
self::assertSame(42.404, $result->advancedShapedArray[1]);
|
||||||
|
self::assertSame('foo', $result->shapedArrayWithClassNameAsKey['stdclass']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,6 +104,9 @@ class ShapedArrayValues
|
|||||||
|
|
||||||
/** @var array{0: int, float, optionalString?: string, mandatoryString: string} */
|
/** @var array{0: int, float, optionalString?: string, mandatoryString: string} */
|
||||||
public array $advancedShapedArray;
|
public array $advancedShapedArray;
|
||||||
|
|
||||||
|
/** @var array{stdclass: string} */
|
||||||
|
public array $shapedArrayWithClassNameAsKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ShapedArrayValuesWithConstructor extends ShapedArrayValues
|
class ShapedArrayValuesWithConstructor extends ShapedArrayValues
|
||||||
@ -114,6 +121,7 @@ class ShapedArrayValuesWithConstructor extends ShapedArrayValues
|
|||||||
* bar: int
|
* bar: int
|
||||||
* } $shapedArrayOnSeveralLines
|
* } $shapedArrayOnSeveralLines
|
||||||
* @param array{0: int, float, optionalString?: string, mandatoryString: string} $advancedShapedArray
|
* @param array{0: int, float, optionalString?: string, mandatoryString: string} $advancedShapedArray
|
||||||
|
* @param array{stdclass: string} $shapedArrayWithClassNameAsKey
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
array $basicShapedArrayWithStringKeys,
|
array $basicShapedArrayWithStringKeys,
|
||||||
@ -121,7 +129,8 @@ class ShapedArrayValuesWithConstructor extends ShapedArrayValues
|
|||||||
array $shapedArrayWithObject,
|
array $shapedArrayWithObject,
|
||||||
array $shapedArrayWithOptionalValue,
|
array $shapedArrayWithOptionalValue,
|
||||||
array $shapedArrayOnSeveralLines,
|
array $shapedArrayOnSeveralLines,
|
||||||
array $advancedShapedArray
|
array $advancedShapedArray,
|
||||||
|
array $shapedArrayWithClassNameAsKey
|
||||||
) {
|
) {
|
||||||
$this->basicShapedArrayWithStringKeys = $basicShapedArrayWithStringKeys;
|
$this->basicShapedArrayWithStringKeys = $basicShapedArrayWithStringKeys;
|
||||||
$this->basicShapedArrayWithIntegerKeys = $basicShapedArrayWithIntegerKeys;
|
$this->basicShapedArrayWithIntegerKeys = $basicShapedArrayWithIntegerKeys;
|
||||||
@ -129,5 +138,6 @@ class ShapedArrayValuesWithConstructor extends ShapedArrayValues
|
|||||||
$this->shapedArrayWithOptionalValue = $shapedArrayWithOptionalValue;
|
$this->shapedArrayWithOptionalValue = $shapedArrayWithOptionalValue;
|
||||||
$this->shapedArrayOnSeveralLines = $shapedArrayOnSeveralLines;
|
$this->shapedArrayOnSeveralLines = $shapedArrayOnSeveralLines;
|
||||||
$this->advancedShapedArray = $advancedShapedArray;
|
$this->advancedShapedArray = $advancedShapedArray;
|
||||||
|
$this->shapedArrayWithClassNameAsKey = $shapedArrayWithClassNameAsKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user