allow vendord
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m3s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 32s
Build, Push and Deploy / deploy-production (push) Has been skipped
Build, Push and Deploy / cleanup (push) Successful in 1s
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m3s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 32s
Build, Push and Deploy / deploy-production (push) Has been skipped
Build, Push and Deploy / cleanup (push) Successful in 1s
This commit is contained in:
74
vendor/cuyz/valinor/src/Definition/AttributeDefinition.php
vendored
Normal file
74
vendor/cuyz/valinor/src/Definition/AttributeDefinition.php
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition;
|
||||
|
||||
use Closure;
|
||||
use ReflectionClass;
|
||||
use ReflectionFunction;
|
||||
use ReflectionMethod;
|
||||
use ReflectionParameter;
|
||||
use ReflectionProperty;
|
||||
|
||||
/** @internal */
|
||||
final class AttributeDefinition
|
||||
{
|
||||
/** @var callable */
|
||||
private mixed $callable;
|
||||
|
||||
public function __construct(
|
||||
public readonly ClassDefinition $class,
|
||||
|
||||
/** @var null|list<array<scalar>|scalar> */
|
||||
public readonly ?array $arguments,
|
||||
|
||||
/** @var array{'closure'}
|
||||
* | array{'class', class-string}
|
||||
* | array{'property', class-string, string}
|
||||
* | array{'method', class-string, string}
|
||||
* | array{'methodParameter', class-string, string, int}
|
||||
* | array{'closureParameter', int}
|
||||
*/
|
||||
public readonly array $reflectionParts,
|
||||
public readonly int $attributeIndex,
|
||||
) {}
|
||||
|
||||
public function forCallable(callable $callable): self
|
||||
{
|
||||
$self = clone $this;
|
||||
$self->callable = $callable;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
/**
|
||||
* There are two ways of instantiating an attribute:
|
||||
*
|
||||
* 1. If the attribute's arguments contain only scalar, we can directly
|
||||
* instantiate the attribute using its class and arguments.
|
||||
* 2. If the attribute arguments contain any object or callable, we are
|
||||
* forced to use reflection to instantiate it, as it is not possible to
|
||||
* properly compile it.
|
||||
*
|
||||
* The first solution is by far more performant, so we prefer using it when
|
||||
* possible.
|
||||
*/
|
||||
public function instantiate(): object
|
||||
{
|
||||
if ($this->arguments !== null) {
|
||||
return new ($this->class->type->className())(...$this->arguments);
|
||||
}
|
||||
|
||||
$reflection = match ($this->reflectionParts[0]) {
|
||||
'class' => new ReflectionClass($this->reflectionParts[1]),
|
||||
'property' => new ReflectionProperty($this->reflectionParts[1], $this->reflectionParts[2]),
|
||||
'method' => new ReflectionMethod($this->reflectionParts[1], $this->reflectionParts[2]),
|
||||
'methodParameter' => (new ReflectionMethod($this->reflectionParts[1], $this->reflectionParts[2]))->getParameters()[$this->reflectionParts[3]],
|
||||
'closure' => new ReflectionFunction(Closure::fromCallable($this->callable)),
|
||||
'closureParameter' => new ReflectionParameter(Closure::fromCallable($this->callable), $this->reflectionParts[1]),
|
||||
};
|
||||
|
||||
return $reflection->getAttributes()[$this->attributeIndex]->newInstance();
|
||||
}
|
||||
}
|
||||
98
vendor/cuyz/valinor/src/Definition/Attributes.php
vendored
Normal file
98
vendor/cuyz/valinor/src/Definition/Attributes.php
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition;
|
||||
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use Traversable;
|
||||
|
||||
use function array_filter;
|
||||
use function array_map;
|
||||
use function count;
|
||||
use function is_a;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @implements IteratorAggregate<AttributeDefinition>
|
||||
*/
|
||||
final class Attributes implements IteratorAggregate, Countable
|
||||
{
|
||||
private static self $empty;
|
||||
|
||||
/** @var list<AttributeDefinition> */
|
||||
private array $attributes;
|
||||
|
||||
/**
|
||||
* @no-named-arguments
|
||||
*/
|
||||
public function __construct(AttributeDefinition ...$attributes)
|
||||
{
|
||||
$this->attributes = $attributes;
|
||||
}
|
||||
|
||||
public static function empty(): self
|
||||
{
|
||||
return self::$empty ??= new self();
|
||||
}
|
||||
|
||||
public function forCallable(callable $callable): self
|
||||
{
|
||||
return new self(...array_map(
|
||||
fn (AttributeDefinition $attribute) => $attribute->forCallable($callable),
|
||||
$this->attributes,
|
||||
));
|
||||
}
|
||||
|
||||
public function has(string $className): bool
|
||||
{
|
||||
foreach ($this->attributes as $attribute) {
|
||||
if (is_a($attribute->class->type->className(), $className, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable(AttributeDefinition): bool $callback
|
||||
*/
|
||||
public function filter(callable $callback): self
|
||||
{
|
||||
return new self(
|
||||
...array_filter($this->attributes, $callback)
|
||||
);
|
||||
}
|
||||
|
||||
public function merge(self $other): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->attributes = [...$this->attributes, ...$other->attributes];
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<AttributeDefinition>
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Traversable<AttributeDefinition>
|
||||
*/
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
yield from $this->attributes;
|
||||
}
|
||||
}
|
||||
22
vendor/cuyz/valinor/src/Definition/ClassDefinition.php
vendored
Normal file
22
vendor/cuyz/valinor/src/Definition/ClassDefinition.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition;
|
||||
|
||||
use CuyZ\Valinor\Type\ObjectType;
|
||||
|
||||
/** @internal */
|
||||
final class ClassDefinition
|
||||
{
|
||||
public function __construct(
|
||||
/** @var class-string */
|
||||
public readonly string $name,
|
||||
public readonly ObjectType $type,
|
||||
public readonly Attributes $attributes,
|
||||
public readonly Properties $properties,
|
||||
public readonly Methods $methods,
|
||||
public readonly bool $isFinal,
|
||||
public readonly bool $isAbstract,
|
||||
) {}
|
||||
}
|
||||
63
vendor/cuyz/valinor/src/Definition/FunctionDefinition.php
vendored
Normal file
63
vendor/cuyz/valinor/src/Definition/FunctionDefinition.php
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition;
|
||||
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use CuyZ\Valinor\Type\Types\Generics;
|
||||
use CuyZ\Valinor\Utility\TypeHelper;
|
||||
|
||||
/** @internal */
|
||||
final class FunctionDefinition
|
||||
{
|
||||
public function __construct(
|
||||
/** @var non-empty-string */
|
||||
public readonly string $name,
|
||||
/** @var non-empty-string */
|
||||
public readonly string $signature,
|
||||
public readonly Attributes $attributes,
|
||||
/** @var non-empty-string|null */
|
||||
public readonly ?string $fileName,
|
||||
/** @var class-string|null */
|
||||
public readonly ?string $class,
|
||||
public readonly bool $isStatic,
|
||||
public readonly bool $isClosure,
|
||||
public readonly Parameters $parameters,
|
||||
public readonly Type $returnType,
|
||||
) {}
|
||||
|
||||
public function forCallable(callable $callable): self
|
||||
{
|
||||
return new self(
|
||||
$this->name,
|
||||
$this->signature,
|
||||
$this->attributes->forCallable($callable),
|
||||
$this->fileName,
|
||||
$this->class,
|
||||
$this->isStatic,
|
||||
$this->isClosure,
|
||||
$this->parameters->forCallable($callable),
|
||||
$this->returnType
|
||||
);
|
||||
}
|
||||
|
||||
public function assignGenerics(Generics $generics): self
|
||||
{
|
||||
if ($generics->items === []) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return new self(
|
||||
$this->name,
|
||||
$this->signature,
|
||||
$this->attributes,
|
||||
$this->fileName,
|
||||
$this->class,
|
||||
$this->isStatic,
|
||||
$this->isClosure,
|
||||
$this->parameters->assignGenerics($generics),
|
||||
TypeHelper::assignVacantTypes($this->returnType, $generics->items),
|
||||
);
|
||||
}
|
||||
}
|
||||
20
vendor/cuyz/valinor/src/Definition/FunctionObject.php
vendored
Normal file
20
vendor/cuyz/valinor/src/Definition/FunctionObject.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition;
|
||||
|
||||
/** @internal */
|
||||
final class FunctionObject
|
||||
{
|
||||
public readonly FunctionDefinition $definition;
|
||||
|
||||
/** @var callable */
|
||||
public readonly mixed $callback;
|
||||
|
||||
public function __construct(FunctionDefinition $definition, callable $callback)
|
||||
{
|
||||
$this->definition = $definition;
|
||||
$this->callback = $callback;
|
||||
}
|
||||
}
|
||||
69
vendor/cuyz/valinor/src/Definition/FunctionsContainer.php
vendored
Normal file
69
vendor/cuyz/valinor/src/Definition/FunctionsContainer.php
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition;
|
||||
|
||||
use Countable;
|
||||
use CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository;
|
||||
use IteratorAggregate;
|
||||
use Traversable;
|
||||
|
||||
use function array_keys;
|
||||
use function count;
|
||||
use function iterator_to_array;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @implements IteratorAggregate<string|int, FunctionObject>
|
||||
*/
|
||||
final class FunctionsContainer implements IteratorAggregate, Countable
|
||||
{
|
||||
/** @var array<FunctionObject> */
|
||||
private array $functions = [];
|
||||
|
||||
public function __construct(
|
||||
private FunctionDefinitionRepository $functionDefinitionRepository,
|
||||
/** @var array<callable> */
|
||||
private array $callables
|
||||
) {}
|
||||
|
||||
public function has(string|int $key): bool
|
||||
{
|
||||
return isset($this->callables[$key]);
|
||||
}
|
||||
|
||||
public function get(string|int $key): FunctionObject
|
||||
{
|
||||
return $this->function($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<FunctionObject>
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return iterator_to_array($this);
|
||||
}
|
||||
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
foreach (array_keys($this->callables) as $key) {
|
||||
yield $key => $this->function($key);
|
||||
}
|
||||
}
|
||||
|
||||
private function function(string|int $key): FunctionObject
|
||||
{
|
||||
return $this->functions[$key] ??= new FunctionObject(
|
||||
$this->functionDefinitionRepository->for($this->callables[$key]),
|
||||
$this->callables[$key]
|
||||
);
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->callables);
|
||||
}
|
||||
}
|
||||
23
vendor/cuyz/valinor/src/Definition/MethodDefinition.php
vendored
Normal file
23
vendor/cuyz/valinor/src/Definition/MethodDefinition.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition;
|
||||
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
|
||||
/** @internal */
|
||||
final class MethodDefinition
|
||||
{
|
||||
public function __construct(
|
||||
/** @var non-empty-string */
|
||||
public readonly string $name,
|
||||
/** @var non-empty-string */
|
||||
public readonly string $signature,
|
||||
public readonly Attributes $attributes,
|
||||
public readonly Parameters $parameters,
|
||||
public readonly bool $isStatic,
|
||||
public readonly bool $isPublic,
|
||||
public readonly Type $returnType
|
||||
) {}
|
||||
}
|
||||
60
vendor/cuyz/valinor/src/Definition/Methods.php
vendored
Normal file
60
vendor/cuyz/valinor/src/Definition/Methods.php
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition;
|
||||
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @implements IteratorAggregate<string, MethodDefinition>
|
||||
*/
|
||||
final class Methods implements IteratorAggregate, Countable
|
||||
{
|
||||
/** @var MethodDefinition[] */
|
||||
private array $methods = [];
|
||||
|
||||
public function __construct(MethodDefinition ...$methods)
|
||||
{
|
||||
foreach ($methods as $method) {
|
||||
$this->methods[$method->name] = $method;
|
||||
}
|
||||
}
|
||||
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return isset($this->methods[$name]);
|
||||
}
|
||||
|
||||
public function get(string $name): MethodDefinition
|
||||
{
|
||||
return $this->methods[$name];
|
||||
}
|
||||
|
||||
public function hasConstructor(): bool
|
||||
{
|
||||
return $this->has('__construct');
|
||||
}
|
||||
|
||||
public function constructor(): MethodDefinition
|
||||
{
|
||||
return $this->get('__construct');
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->methods);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Traversable<string, MethodDefinition>
|
||||
*/
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
yield from $this->methods;
|
||||
}
|
||||
}
|
||||
56
vendor/cuyz/valinor/src/Definition/ParameterDefinition.php
vendored
Normal file
56
vendor/cuyz/valinor/src/Definition/ParameterDefinition.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition;
|
||||
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use CuyZ\Valinor\Type\Types\Generics;
|
||||
use CuyZ\Valinor\Utility\TypeHelper;
|
||||
|
||||
/** @internal */
|
||||
final class ParameterDefinition
|
||||
{
|
||||
public function __construct(
|
||||
/** @var non-empty-string */
|
||||
public readonly string $name,
|
||||
/** @var non-empty-string */
|
||||
public readonly string $signature,
|
||||
public readonly Type $type,
|
||||
public readonly Type $nativeType,
|
||||
public readonly bool $isOptional,
|
||||
public readonly bool $isVariadic,
|
||||
public readonly mixed $defaultValue,
|
||||
public readonly Attributes $attributes
|
||||
) {}
|
||||
|
||||
public function forCallable(callable $callable): self
|
||||
{
|
||||
return new self(
|
||||
$this->name,
|
||||
$this->signature,
|
||||
$this->type,
|
||||
$this->nativeType,
|
||||
$this->isOptional,
|
||||
$this->isVariadic,
|
||||
$this->defaultValue,
|
||||
$this->attributes->forCallable($callable)
|
||||
);
|
||||
}
|
||||
|
||||
public function assignGenerics(Generics $generics): self
|
||||
{
|
||||
assert($generics->items !== []);
|
||||
|
||||
return new self(
|
||||
$this->name,
|
||||
$this->signature,
|
||||
TypeHelper::assignVacantTypes($this->type, $generics->items),
|
||||
$this->nativeType,
|
||||
$this->isOptional,
|
||||
$this->isVariadic,
|
||||
$this->defaultValue,
|
||||
$this->attributes
|
||||
);
|
||||
}
|
||||
}
|
||||
88
vendor/cuyz/valinor/src/Definition/Parameters.php
vendored
Normal file
88
vendor/cuyz/valinor/src/Definition/Parameters.php
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition;
|
||||
|
||||
use Countable;
|
||||
use CuyZ\Valinor\Type\Types\Generics;
|
||||
use IteratorAggregate;
|
||||
use Traversable;
|
||||
|
||||
use function array_map;
|
||||
use function array_values;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @implements IteratorAggregate<string, ParameterDefinition>
|
||||
*/
|
||||
final class Parameters implements IteratorAggregate, Countable
|
||||
{
|
||||
/** @var array<non-empty-string, ParameterDefinition> */
|
||||
private array $parameters = [];
|
||||
|
||||
public function __construct(ParameterDefinition ...$parameters)
|
||||
{
|
||||
foreach ($parameters as $parameter) {
|
||||
$this->parameters[$parameter->name] = $parameter;
|
||||
}
|
||||
}
|
||||
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return isset($this->parameters[$name]);
|
||||
}
|
||||
|
||||
public function get(string $name): ParameterDefinition
|
||||
{
|
||||
return $this->parameters[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int<0, max> $index
|
||||
*/
|
||||
public function at(int $index): ParameterDefinition
|
||||
{
|
||||
return array_values($this->parameters)[$index];
|
||||
}
|
||||
|
||||
public function assignGenerics(Generics $generics): self
|
||||
{
|
||||
return new self(
|
||||
...array_map(
|
||||
static fn (ParameterDefinition $parameter) => $parameter->assignGenerics($generics),
|
||||
$this->parameters,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<non-empty-string, ParameterDefinition>
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->parameters);
|
||||
}
|
||||
|
||||
public function forCallable(callable $callable): self
|
||||
{
|
||||
return new self(...array_map(
|
||||
fn (ParameterDefinition $parameter) => $parameter->forCallable($callable),
|
||||
$this->parameters
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Traversable<string, ParameterDefinition>
|
||||
*/
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
yield from $this->parameters;
|
||||
}
|
||||
}
|
||||
50
vendor/cuyz/valinor/src/Definition/Properties.php
vendored
Normal file
50
vendor/cuyz/valinor/src/Definition/Properties.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition;
|
||||
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @implements IteratorAggregate<string, PropertyDefinition>
|
||||
*/
|
||||
final class Properties implements IteratorAggregate, Countable
|
||||
{
|
||||
/** @var PropertyDefinition[] */
|
||||
private array $properties = [];
|
||||
|
||||
public function __construct(PropertyDefinition ...$properties)
|
||||
{
|
||||
foreach ($properties as $property) {
|
||||
$this->properties[$property->name] = $property;
|
||||
}
|
||||
}
|
||||
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return isset($this->properties[$name]);
|
||||
}
|
||||
|
||||
public function get(string $name): PropertyDefinition
|
||||
{
|
||||
return $this->properties[$name];
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Traversable<string, PropertyDefinition>
|
||||
*/
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
yield from $this->properties;
|
||||
}
|
||||
}
|
||||
24
vendor/cuyz/valinor/src/Definition/PropertyDefinition.php
vendored
Normal file
24
vendor/cuyz/valinor/src/Definition/PropertyDefinition.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition;
|
||||
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
|
||||
/** @internal */
|
||||
final class PropertyDefinition
|
||||
{
|
||||
public function __construct(
|
||||
/** @var non-empty-string */
|
||||
public readonly string $name,
|
||||
/** @var non-empty-string */
|
||||
public readonly string $signature,
|
||||
public readonly Type $type,
|
||||
public readonly Type $nativeType,
|
||||
public readonly bool $hasDefaultValue,
|
||||
public readonly mixed $defaultValue,
|
||||
public readonly bool $isPublic,
|
||||
public readonly Attributes $attributes
|
||||
) {}
|
||||
}
|
||||
23
vendor/cuyz/valinor/src/Definition/Repository/AttributesRepository.php
vendored
Normal file
23
vendor/cuyz/valinor/src/Definition/Repository/AttributesRepository.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository;
|
||||
|
||||
use CuyZ\Valinor\Definition\AttributeDefinition;
|
||||
use ReflectionClass;
|
||||
use ReflectionFunction;
|
||||
use ReflectionMethod;
|
||||
use ReflectionParameter;
|
||||
use ReflectionProperty;
|
||||
use Reflector;
|
||||
|
||||
/** @internal */
|
||||
interface AttributesRepository
|
||||
{
|
||||
/**
|
||||
* @param ReflectionClass<covariant object>|ReflectionProperty|ReflectionMethod|ReflectionFunction|ReflectionParameter $reflection
|
||||
* @return list<AttributeDefinition>
|
||||
*/
|
||||
public function for(Reflector $reflection): array;
|
||||
}
|
||||
68
vendor/cuyz/valinor/src/Definition/Repository/Cache/CompiledClassDefinitionRepository.php
vendored
Normal file
68
vendor/cuyz/valinor/src/Definition/Repository/Cache/CompiledClassDefinitionRepository.php
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Cache;
|
||||
|
||||
use CuyZ\Valinor\Cache\Cache;
|
||||
use CuyZ\Valinor\Cache\CacheEntry;
|
||||
use CuyZ\Valinor\Definition\ClassDefinition;
|
||||
use CuyZ\Valinor\Definition\Repository\Cache\Compiler\ClassDefinitionCompiler;
|
||||
use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
|
||||
use CuyZ\Valinor\Type\ObjectType;
|
||||
use CuyZ\Valinor\Utility\Reflection\Reflection;
|
||||
|
||||
use function is_string;
|
||||
|
||||
/** @internal */
|
||||
final class CompiledClassDefinitionRepository implements ClassDefinitionRepository
|
||||
{
|
||||
public function __construct(
|
||||
private ClassDefinitionRepository $delegate,
|
||||
/** @var Cache<ClassDefinition> */
|
||||
private Cache $cache,
|
||||
private ClassDefinitionCompiler $compiler,
|
||||
) {}
|
||||
|
||||
public function for(ObjectType $type): ClassDefinition
|
||||
{
|
||||
// @infection-ignore-all
|
||||
$key = "class-definition-\0" . $type->toString();
|
||||
|
||||
$entry = $this->cache->get($key);
|
||||
|
||||
if ($entry) {
|
||||
return $entry;
|
||||
}
|
||||
|
||||
$class = $this->delegate->for($type);
|
||||
|
||||
$code = 'fn () => ' . $this->compiler->compile($class);
|
||||
$filesToWatch = $this->filesToWatch($type);
|
||||
|
||||
$this->cache->set($key, new CacheEntry($code, $filesToWatch));
|
||||
|
||||
/** @var ClassDefinition */
|
||||
return $this->cache->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<non-empty-string>
|
||||
*/
|
||||
private function filesToWatch(ObjectType $type): array
|
||||
{
|
||||
$reflection = Reflection::class($type->className());
|
||||
|
||||
$fileNames = [];
|
||||
|
||||
do {
|
||||
$fileName = $reflection->getFileName();
|
||||
|
||||
if (is_string($fileName)) {
|
||||
$fileNames[] = $fileName;
|
||||
}
|
||||
} while ($reflection = $reflection->getParentClass());
|
||||
|
||||
return $fileNames;
|
||||
}
|
||||
}
|
||||
47
vendor/cuyz/valinor/src/Definition/Repository/Cache/CompiledFunctionDefinitionRepository.php
vendored
Normal file
47
vendor/cuyz/valinor/src/Definition/Repository/Cache/CompiledFunctionDefinitionRepository.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Cache;
|
||||
|
||||
use CuyZ\Valinor\Cache\Cache;
|
||||
use CuyZ\Valinor\Cache\CacheEntry;
|
||||
use CuyZ\Valinor\Definition\FunctionDefinition;
|
||||
use CuyZ\Valinor\Definition\Repository\Cache\Compiler\FunctionDefinitionCompiler;
|
||||
use CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository;
|
||||
use CuyZ\Valinor\Utility\Reflection\Reflection;
|
||||
|
||||
/** @internal */
|
||||
final class CompiledFunctionDefinitionRepository implements FunctionDefinitionRepository
|
||||
{
|
||||
public function __construct(
|
||||
private FunctionDefinitionRepository $delegate,
|
||||
/** @var Cache<FunctionDefinition> */
|
||||
private Cache $cache,
|
||||
private FunctionDefinitionCompiler $compiler,
|
||||
) {}
|
||||
|
||||
public function for(callable $function): FunctionDefinition
|
||||
{
|
||||
$reflection = Reflection::function($function);
|
||||
|
||||
// @infection-ignore-all
|
||||
$key = "function-definition-\0" . $reflection->getFileName() . ':' . $reflection->getStartLine() . '-' . $reflection->getEndLine();
|
||||
|
||||
$entry = $this->cache->get($key);
|
||||
|
||||
if ($entry) {
|
||||
return $entry->forCallable($function);
|
||||
}
|
||||
|
||||
$definition = $this->delegate->for($function);
|
||||
|
||||
$code = 'fn () => ' . $this->compiler->compile($definition);
|
||||
$filesToWatch = $definition->fileName ? [$definition->fileName] : [];
|
||||
|
||||
$this->cache->set($key, new CacheEntry($code, $filesToWatch));
|
||||
|
||||
/** @var FunctionDefinition */
|
||||
return $this->cache->get($key)?->forCallable($function);
|
||||
}
|
||||
}
|
||||
68
vendor/cuyz/valinor/src/Definition/Repository/Cache/Compiler/AttributesCompiler.php
vendored
Normal file
68
vendor/cuyz/valinor/src/Definition/Repository/Cache/Compiler/AttributesCompiler.php
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Cache\Compiler;
|
||||
|
||||
use CuyZ\Valinor\Definition\Attributes;
|
||||
|
||||
use function array_map;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function var_export;
|
||||
|
||||
/** @internal */
|
||||
final class AttributesCompiler
|
||||
{
|
||||
public function __construct(private ClassDefinitionCompiler $classDefinitionCompiler) {}
|
||||
|
||||
public function compile(Attributes $attributes): string
|
||||
{
|
||||
if (count($attributes) === 0) {
|
||||
return Attributes::class . '::empty()';
|
||||
}
|
||||
|
||||
$attributesListCode = $this->compileAttributes($attributes);
|
||||
|
||||
return <<<PHP
|
||||
new \CuyZ\Valinor\Definition\Attributes($attributesListCode)
|
||||
PHP;
|
||||
}
|
||||
|
||||
private function compileAttributes(Attributes $attributes): string
|
||||
{
|
||||
$attributesListCode = [];
|
||||
|
||||
foreach ($attributes as $attribute) {
|
||||
$class = $this->classDefinitionCompiler->compile($attribute->class);
|
||||
$argumentsCode = $this->compileAttributeArguments($attribute->arguments);
|
||||
$reflectionParts = var_export($attribute->reflectionParts, true);
|
||||
$attributeIndex = var_export($attribute->attributeIndex, true);
|
||||
|
||||
$attributesListCode[] = <<<PHP
|
||||
new \CuyZ\Valinor\Definition\AttributeDefinition(
|
||||
$class,
|
||||
$argumentsCode,
|
||||
$reflectionParts,
|
||||
$attributeIndex,
|
||||
)
|
||||
PHP;
|
||||
}
|
||||
|
||||
return implode(', ', $attributesListCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|list<array<scalar>|scalar> $arguments
|
||||
*/
|
||||
private function compileAttributeArguments(?array $arguments): string
|
||||
{
|
||||
if ($arguments === null) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
$code = array_map(static fn ($value) => var_export($value, true), $arguments);
|
||||
|
||||
return '[' . implode(', ', $code) . ']';
|
||||
}
|
||||
}
|
||||
71
vendor/cuyz/valinor/src/Definition/Repository/Cache/Compiler/ClassDefinitionCompiler.php
vendored
Normal file
71
vendor/cuyz/valinor/src/Definition/Repository/Cache/Compiler/ClassDefinitionCompiler.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Cache\Compiler;
|
||||
|
||||
use CuyZ\Valinor\Definition\ClassDefinition;
|
||||
use CuyZ\Valinor\Definition\MethodDefinition;
|
||||
use CuyZ\Valinor\Definition\PropertyDefinition;
|
||||
|
||||
use function array_map;
|
||||
use function implode;
|
||||
use function iterator_to_array;
|
||||
use function var_export;
|
||||
|
||||
/** @internal */
|
||||
final class ClassDefinitionCompiler
|
||||
{
|
||||
private TypeCompiler $typeCompiler;
|
||||
|
||||
private AttributesCompiler $attributesCompiler;
|
||||
|
||||
private MethodDefinitionCompiler $methodCompiler;
|
||||
|
||||
private PropertyDefinitionCompiler $propertyCompiler;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->attributesCompiler = new AttributesCompiler($this);
|
||||
$this->typeCompiler = new TypeCompiler($this->attributesCompiler);
|
||||
|
||||
$this->methodCompiler = new MethodDefinitionCompiler($this->typeCompiler, $this->attributesCompiler);
|
||||
$this->propertyCompiler = new PropertyDefinitionCompiler($this->typeCompiler, $this->attributesCompiler);
|
||||
}
|
||||
|
||||
public function compile(ClassDefinition $value): string
|
||||
{
|
||||
$name = var_export($value->name, true);
|
||||
$type = $this->typeCompiler->compile($value->type);
|
||||
|
||||
$properties = array_map(
|
||||
fn (PropertyDefinition $property) => $this->propertyCompiler->compile($property),
|
||||
iterator_to_array($value->properties)
|
||||
);
|
||||
|
||||
$properties = implode(', ', $properties);
|
||||
|
||||
$methods = array_map(
|
||||
fn (MethodDefinition $method) => $this->methodCompiler->compile($method),
|
||||
iterator_to_array($value->methods)
|
||||
);
|
||||
|
||||
$methods = implode(', ', $methods);
|
||||
$attributes = $this->attributesCompiler->compile($value->attributes);
|
||||
|
||||
$isFinal = var_export($value->isFinal, true);
|
||||
$isAbstract = var_export($value->isAbstract, true);
|
||||
|
||||
return <<<PHP
|
||||
new \CuyZ\Valinor\Definition\ClassDefinition(
|
||||
$name,
|
||||
$type,
|
||||
$attributes,
|
||||
new \CuyZ\Valinor\Definition\Properties($properties),
|
||||
new \CuyZ\Valinor\Definition\Methods($methods),
|
||||
$isFinal,
|
||||
$isAbstract,
|
||||
)
|
||||
PHP;
|
||||
}
|
||||
}
|
||||
19
vendor/cuyz/valinor/src/Definition/Repository/Cache/Compiler/Exception/TypeCannotBeCompiled.php
vendored
Normal file
19
vendor/cuyz/valinor/src/Definition/Repository/Cache/Compiler/Exception/TypeCannotBeCompiled.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Cache\Compiler\Exception;
|
||||
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use LogicException;
|
||||
|
||||
/** @internal */
|
||||
final class TypeCannotBeCompiled extends LogicException
|
||||
{
|
||||
public function __construct(Type $type)
|
||||
{
|
||||
$class = $type::class;
|
||||
|
||||
parent::__construct("The type `$class` cannot be compiled.");
|
||||
}
|
||||
}
|
||||
58
vendor/cuyz/valinor/src/Definition/Repository/Cache/Compiler/FunctionDefinitionCompiler.php
vendored
Normal file
58
vendor/cuyz/valinor/src/Definition/Repository/Cache/Compiler/FunctionDefinitionCompiler.php
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Cache\Compiler;
|
||||
|
||||
use CuyZ\Valinor\Definition\FunctionDefinition;
|
||||
use CuyZ\Valinor\Definition\ParameterDefinition;
|
||||
|
||||
use function var_export;
|
||||
|
||||
/** @internal */
|
||||
final class FunctionDefinitionCompiler
|
||||
{
|
||||
private TypeCompiler $typeCompiler;
|
||||
|
||||
private AttributesCompiler $attributesCompiler;
|
||||
|
||||
private ParameterDefinitionCompiler $parameterCompiler;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->attributesCompiler = new AttributesCompiler(new ClassDefinitionCompiler());
|
||||
$this->typeCompiler = new TypeCompiler($this->attributesCompiler);
|
||||
|
||||
$this->parameterCompiler = new ParameterDefinitionCompiler($this->typeCompiler, $this->attributesCompiler);
|
||||
}
|
||||
|
||||
public function compile(FunctionDefinition $value): string
|
||||
{
|
||||
$parameters = array_map(
|
||||
fn (ParameterDefinition $parameter) => $this->parameterCompiler->compile($parameter),
|
||||
iterator_to_array($value->parameters)
|
||||
);
|
||||
|
||||
$attributes = $this->attributesCompiler->compile($value->attributes);
|
||||
$fileName = var_export($value->fileName, true);
|
||||
$class = var_export($value->class, true);
|
||||
$isStatic = var_export($value->isStatic, true);
|
||||
$isClosure = var_export($value->isClosure, true);
|
||||
$parameters = implode(', ', $parameters);
|
||||
$returnType = $this->typeCompiler->compile($value->returnType);
|
||||
|
||||
return <<<PHP
|
||||
new \CuyZ\Valinor\Definition\FunctionDefinition(
|
||||
'{$value->name}',
|
||||
'{$value->signature}',
|
||||
$attributes,
|
||||
$fileName,
|
||||
$class,
|
||||
$isStatic,
|
||||
$isClosure,
|
||||
new \CuyZ\Valinor\Definition\Parameters($parameters),
|
||||
$returnType
|
||||
)
|
||||
PHP;
|
||||
}
|
||||
}
|
||||
54
vendor/cuyz/valinor/src/Definition/Repository/Cache/Compiler/MethodDefinitionCompiler.php
vendored
Normal file
54
vendor/cuyz/valinor/src/Definition/Repository/Cache/Compiler/MethodDefinitionCompiler.php
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Cache\Compiler;
|
||||
|
||||
use CuyZ\Valinor\Definition\MethodDefinition;
|
||||
use CuyZ\Valinor\Definition\ParameterDefinition;
|
||||
|
||||
use function var_export;
|
||||
|
||||
/** @internal */
|
||||
final class MethodDefinitionCompiler
|
||||
{
|
||||
private TypeCompiler $typeCompiler;
|
||||
|
||||
private AttributesCompiler $attributesCompiler;
|
||||
|
||||
private ParameterDefinitionCompiler $parameterCompiler;
|
||||
|
||||
public function __construct(TypeCompiler $typeCompiler, AttributesCompiler $attributesCompiler)
|
||||
{
|
||||
$this->typeCompiler = $typeCompiler;
|
||||
$this->attributesCompiler = $attributesCompiler;
|
||||
$this->parameterCompiler = new ParameterDefinitionCompiler($typeCompiler, $attributesCompiler);
|
||||
}
|
||||
|
||||
public function compile(MethodDefinition $method): string
|
||||
{
|
||||
$attributes = $this->attributesCompiler->compile($method->attributes);
|
||||
|
||||
$parameters = array_map(
|
||||
fn (ParameterDefinition $parameter) => $this->parameterCompiler->compile($parameter),
|
||||
iterator_to_array($method->parameters)
|
||||
);
|
||||
|
||||
$parameters = implode(', ', $parameters);
|
||||
$isStatic = var_export($method->isStatic, true);
|
||||
$isPublic = var_export($method->isPublic, true);
|
||||
$returnType = $this->typeCompiler->compile($method->returnType);
|
||||
|
||||
return <<<PHP
|
||||
new \CuyZ\Valinor\Definition\MethodDefinition(
|
||||
'{$method->name}',
|
||||
'{$method->signature}',
|
||||
$attributes,
|
||||
new \CuyZ\Valinor\Definition\Parameters($parameters),
|
||||
$isStatic,
|
||||
$isPublic,
|
||||
$returnType
|
||||
)
|
||||
PHP;
|
||||
}
|
||||
}
|
||||
48
vendor/cuyz/valinor/src/Definition/Repository/Cache/Compiler/ParameterDefinitionCompiler.php
vendored
Normal file
48
vendor/cuyz/valinor/src/Definition/Repository/Cache/Compiler/ParameterDefinitionCompiler.php
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Cache\Compiler;
|
||||
|
||||
use CuyZ\Valinor\Definition\ParameterDefinition;
|
||||
|
||||
/** @internal */
|
||||
final class ParameterDefinitionCompiler
|
||||
{
|
||||
public function __construct(
|
||||
private TypeCompiler $typeCompiler,
|
||||
private AttributesCompiler $attributesCompiler
|
||||
) {}
|
||||
|
||||
public function compile(ParameterDefinition $parameter): string
|
||||
{
|
||||
$isOptional = var_export($parameter->isOptional, true);
|
||||
$isVariadic = var_export($parameter->isVariadic, true);
|
||||
$defaultValue = $this->defaultValue($parameter);
|
||||
$type = $this->typeCompiler->compile($parameter->type);
|
||||
$nativeType = $this->typeCompiler->compile($parameter->nativeType);
|
||||
$attributes = $this->attributesCompiler->compile($parameter->attributes);
|
||||
|
||||
return <<<PHP
|
||||
new \CuyZ\Valinor\Definition\ParameterDefinition(
|
||||
'{$parameter->name}',
|
||||
'{$parameter->signature}',
|
||||
$type,
|
||||
$nativeType,
|
||||
$isOptional,
|
||||
$isVariadic,
|
||||
$defaultValue,
|
||||
$attributes
|
||||
)
|
||||
PHP;
|
||||
}
|
||||
|
||||
private function defaultValue(ParameterDefinition $parameter): string
|
||||
{
|
||||
$defaultValue = $parameter->defaultValue;
|
||||
|
||||
return is_object($defaultValue)
|
||||
? 'unserialize(' . var_export(serialize($defaultValue), true) . ')'
|
||||
: var_export($defaultValue, true);
|
||||
}
|
||||
}
|
||||
39
vendor/cuyz/valinor/src/Definition/Repository/Cache/Compiler/PropertyDefinitionCompiler.php
vendored
Normal file
39
vendor/cuyz/valinor/src/Definition/Repository/Cache/Compiler/PropertyDefinitionCompiler.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Cache\Compiler;
|
||||
|
||||
use CuyZ\Valinor\Definition\PropertyDefinition;
|
||||
|
||||
/** @internal */
|
||||
final class PropertyDefinitionCompiler
|
||||
{
|
||||
public function __construct(
|
||||
private TypeCompiler $typeCompiler,
|
||||
private AttributesCompiler $attributesCompiler
|
||||
) {}
|
||||
|
||||
public function compile(PropertyDefinition $property): string
|
||||
{
|
||||
$type = $this->typeCompiler->compile($property->type);
|
||||
$nativeType = $this->typeCompiler->compile($property->nativeType);
|
||||
$hasDefaultValue = var_export($property->hasDefaultValue, true);
|
||||
$defaultValue = var_export($property->defaultValue, true);
|
||||
$isPublic = var_export($property->isPublic, true);
|
||||
$attributes = $this->attributesCompiler->compile($property->attributes);
|
||||
|
||||
return <<<PHP
|
||||
new \CuyZ\Valinor\Definition\PropertyDefinition(
|
||||
'{$property->name}',
|
||||
'{$property->signature}',
|
||||
$type,
|
||||
$nativeType,
|
||||
$hasDefaultValue,
|
||||
$defaultValue,
|
||||
$isPublic,
|
||||
$attributes
|
||||
)
|
||||
PHP;
|
||||
}
|
||||
}
|
||||
214
vendor/cuyz/valinor/src/Definition/Repository/Cache/Compiler/TypeCompiler.php
vendored
Normal file
214
vendor/cuyz/valinor/src/Definition/Repository/Cache/Compiler/TypeCompiler.php
vendored
Normal file
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Cache\Compiler;
|
||||
|
||||
use CuyZ\Valinor\Definition\Repository\Cache\Compiler\Exception\TypeCannotBeCompiled;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use CuyZ\Valinor\Type\Types\ArrayKeyType;
|
||||
use CuyZ\Valinor\Type\Types\ArrayType;
|
||||
use CuyZ\Valinor\Type\Types\BooleanValueType;
|
||||
use CuyZ\Valinor\Type\Types\CallableType;
|
||||
use CuyZ\Valinor\Type\Types\ClassStringType;
|
||||
use CuyZ\Valinor\Type\Types\EnumType;
|
||||
use CuyZ\Valinor\Type\Types\FloatValueType;
|
||||
use CuyZ\Valinor\Type\Types\GenericType;
|
||||
use CuyZ\Valinor\Type\Types\IntegerRangeType;
|
||||
use CuyZ\Valinor\Type\Types\IntegerValueType;
|
||||
use CuyZ\Valinor\Type\Types\InterfaceType;
|
||||
use CuyZ\Valinor\Type\Types\IntersectionType;
|
||||
use CuyZ\Valinor\Type\Types\IterableType;
|
||||
use CuyZ\Valinor\Type\Types\ListType;
|
||||
use CuyZ\Valinor\Type\Types\MixedType;
|
||||
use CuyZ\Valinor\Type\Types\NativeBooleanType;
|
||||
use CuyZ\Valinor\Type\Types\NativeClassType;
|
||||
use CuyZ\Valinor\Type\Types\NativeFloatType;
|
||||
use CuyZ\Valinor\Type\Types\NativeIntegerType;
|
||||
use CuyZ\Valinor\Type\Types\NativeStringType;
|
||||
use CuyZ\Valinor\Type\Types\NegativeIntegerType;
|
||||
use CuyZ\Valinor\Type\Types\NonEmptyArrayType;
|
||||
use CuyZ\Valinor\Type\Types\NonEmptyListType;
|
||||
use CuyZ\Valinor\Type\Types\NonEmptyStringType;
|
||||
use CuyZ\Valinor\Type\Types\NonNegativeIntegerType;
|
||||
use CuyZ\Valinor\Type\Types\NonPositiveIntegerType;
|
||||
use CuyZ\Valinor\Type\Types\NullType;
|
||||
use CuyZ\Valinor\Type\Types\NumericStringType;
|
||||
use CuyZ\Valinor\Type\Types\PositiveIntegerType;
|
||||
use CuyZ\Valinor\Type\Types\ScalarConcreteType;
|
||||
use CuyZ\Valinor\Type\Types\ShapedArrayElement;
|
||||
use CuyZ\Valinor\Type\Types\ShapedArrayType;
|
||||
use CuyZ\Valinor\Type\Types\StringValueType;
|
||||
use CuyZ\Valinor\Type\Types\UndefinedObjectType;
|
||||
use CuyZ\Valinor\Type\Types\UnionType;
|
||||
use CuyZ\Valinor\Type\Types\UnresolvableType;
|
||||
use UnitEnum;
|
||||
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function implode;
|
||||
use function var_export;
|
||||
|
||||
/** @internal */
|
||||
final class TypeCompiler
|
||||
{
|
||||
public function __construct(
|
||||
private AttributesCompiler $attributesCompiler,
|
||||
) {}
|
||||
|
||||
public function compile(Type $type): string
|
||||
{
|
||||
$class = $type::class;
|
||||
|
||||
switch (true) {
|
||||
case $type instanceof NullType:
|
||||
case $type instanceof NativeBooleanType:
|
||||
case $type instanceof NativeFloatType:
|
||||
case $type instanceof NativeIntegerType:
|
||||
case $type instanceof PositiveIntegerType:
|
||||
case $type instanceof NegativeIntegerType:
|
||||
case $type instanceof NonPositiveIntegerType:
|
||||
case $type instanceof NonNegativeIntegerType:
|
||||
case $type instanceof NativeStringType:
|
||||
case $type instanceof NonEmptyStringType:
|
||||
case $type instanceof NumericStringType:
|
||||
case $type instanceof UndefinedObjectType:
|
||||
case $type instanceof MixedType:
|
||||
case $type instanceof ScalarConcreteType:
|
||||
return "$class::get()";
|
||||
case $type instanceof BooleanValueType:
|
||||
return $type->value() === true
|
||||
? "$class::true()"
|
||||
: "$class::false()";
|
||||
case $type instanceof IntegerRangeType:
|
||||
return "new $class({$type->min()}, {$type->max()})";
|
||||
case $type instanceof StringValueType:
|
||||
$value = var_export($type->toString(), true);
|
||||
|
||||
return "$class::from($value)";
|
||||
case $type instanceof IntegerValueType:
|
||||
case $type instanceof FloatValueType:
|
||||
$value = var_export($type->value(), true);
|
||||
|
||||
return "new $class($value)";
|
||||
case $type instanceof IntersectionType:
|
||||
case $type instanceof UnionType:
|
||||
$subTypes = array_map(
|
||||
fn (Type $subType) => $this->compile($subType),
|
||||
$type->types()
|
||||
);
|
||||
|
||||
return "new $class(" . implode(', ', $subTypes) . ')';
|
||||
case $type instanceof ArrayKeyType:
|
||||
return match ($type->toString()) {
|
||||
'array-key' => "$class::default()",
|
||||
'string' => "$class::string()",
|
||||
'int' => "$class::integer()",
|
||||
default => (function () use ($type, $class) {
|
||||
$types = array_map(
|
||||
fn (Type $subType) => $this->compile($subType),
|
||||
$type->types,
|
||||
);
|
||||
|
||||
return "new $class([" . implode(', ', $types) . '])';
|
||||
})(),
|
||||
};
|
||||
case $type instanceof ShapedArrayType:
|
||||
$elements = [];
|
||||
|
||||
foreach ($type->elements as $key => $element) {
|
||||
$subkey = $this->compile($element->key());
|
||||
$subtype = $this->compile($element->type());
|
||||
$optional = var_export($element->isOptional(), true);
|
||||
$attributes = $this->attributesCompiler->compile($element->attributes());
|
||||
|
||||
$elements[] = var_export($key, true) . ' => new ' . ShapedArrayElement::class . "($subkey, $subtype, $optional, $attributes)";
|
||||
}
|
||||
|
||||
$elements = implode(', ', $elements);
|
||||
$isUnsealed = var_export($type->isUnsealed, true);
|
||||
$unsealedType = $type->hasUnsealedType() ? $this->compile($type->unsealedType()) : 'null';
|
||||
|
||||
return "new $class([$elements], $isUnsealed, $unsealedType)";
|
||||
case $type instanceof ArrayType:
|
||||
case $type instanceof NonEmptyArrayType:
|
||||
if ($type->toString() === 'array' || $type->toString() === 'non-empty-array') {
|
||||
return "$class::native()";
|
||||
}
|
||||
|
||||
$subType = $this->compile($type->subType());
|
||||
|
||||
if (str_ends_with($type->toString(), '[]')) {
|
||||
return "$class::simple($subType)";
|
||||
}
|
||||
|
||||
$keyType = $this->compile($type->keyType());
|
||||
|
||||
return "new $class($keyType, $subType)";
|
||||
case $type instanceof ListType:
|
||||
case $type instanceof NonEmptyListType:
|
||||
if ($type->toString() === 'list' || $type->toString() === 'non-empty-list') {
|
||||
return "$class::native()";
|
||||
}
|
||||
|
||||
$subType = $this->compile($type->subType());
|
||||
|
||||
return "new $class($subType)";
|
||||
case $type instanceof IterableType:
|
||||
$keyType = $this->compile($type->keyType());
|
||||
$subType = $this->compile($type->subType());
|
||||
|
||||
return "new $class($keyType, $subType)";
|
||||
case $type instanceof NativeClassType:
|
||||
case $type instanceof InterfaceType:
|
||||
$generics = [];
|
||||
|
||||
foreach ($type->generics() as $key => $generic) {
|
||||
$generics[] = var_export($key, true) . ' => ' . $this->compile($generic);
|
||||
}
|
||||
|
||||
$generics = implode(', ', $generics);
|
||||
|
||||
return "new $class('{$type->className()}', [$generics])";
|
||||
case $type instanceof ClassStringType:
|
||||
$subTypes = implode(', ', array_map(
|
||||
fn (Type $subType) => $this->compile($subType),
|
||||
$type->subTypes(),
|
||||
));
|
||||
|
||||
return "new $class([$subTypes])";
|
||||
case $type instanceof EnumType:
|
||||
$enumName = var_export($type->className(), true);
|
||||
$pattern = var_export($type->pattern(), true);
|
||||
|
||||
$cases = array_map(
|
||||
fn (string|int $key, UnitEnum $case) => var_export($key, true) . ' => ' . var_export($case, true),
|
||||
array_keys($type->cases()),
|
||||
$type->cases()
|
||||
);
|
||||
$cases = implode(', ', $cases);
|
||||
|
||||
return "new $class($enumName, $pattern, [$cases])";
|
||||
case $type instanceof CallableType:
|
||||
$returnType = $this->compile($type->returnType);
|
||||
$parameters = implode(', ', array_map(
|
||||
fn (Type $subType) => $this->compile($subType),
|
||||
$type->parameters,
|
||||
));
|
||||
|
||||
return "new $class([$parameters], $returnType)";
|
||||
case $type instanceof GenericType:
|
||||
$symbol = var_export($type->symbol, true);
|
||||
$innerType = $this->compile($type->innerType);
|
||||
|
||||
return "new $class($symbol, $innerType)";
|
||||
case $type instanceof UnresolvableType:
|
||||
$raw = var_export($type->toString(), true);
|
||||
$message = var_export($type->message(), true);
|
||||
|
||||
return "new $class($raw, $message)";
|
||||
default:
|
||||
throw new TypeCannotBeCompiled($type);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
vendor/cuyz/valinor/src/Definition/Repository/Cache/InMemoryClassDefinitionRepository.php
vendored
Normal file
25
vendor/cuyz/valinor/src/Definition/Repository/Cache/InMemoryClassDefinitionRepository.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Cache;
|
||||
|
||||
use CuyZ\Valinor\Definition\ClassDefinition;
|
||||
use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
|
||||
use CuyZ\Valinor\Type\ObjectType;
|
||||
|
||||
/** @internal */
|
||||
final class InMemoryClassDefinitionRepository implements ClassDefinitionRepository
|
||||
{
|
||||
/** @var array<string, ClassDefinition> */
|
||||
private array $classDefinitions = [];
|
||||
|
||||
public function __construct(
|
||||
private ClassDefinitionRepository $delegate,
|
||||
) {}
|
||||
|
||||
public function for(ObjectType $type): ClassDefinition
|
||||
{
|
||||
return $this->classDefinitions[$type->toString()] ??= $this->delegate->for($type);
|
||||
}
|
||||
}
|
||||
30
vendor/cuyz/valinor/src/Definition/Repository/Cache/InMemoryFunctionDefinitionRepository.php
vendored
Normal file
30
vendor/cuyz/valinor/src/Definition/Repository/Cache/InMemoryFunctionDefinitionRepository.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Cache;
|
||||
|
||||
use CuyZ\Valinor\Definition\FunctionDefinition;
|
||||
use CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository;
|
||||
use CuyZ\Valinor\Utility\Reflection\Reflection;
|
||||
|
||||
/** @internal */
|
||||
final class InMemoryFunctionDefinitionRepository implements FunctionDefinitionRepository
|
||||
{
|
||||
/** @var array<string, FunctionDefinition> */
|
||||
private array $functionDefinitions = [];
|
||||
|
||||
public function __construct(
|
||||
private FunctionDefinitionRepository $delegate,
|
||||
) {}
|
||||
|
||||
public function for(callable $function): FunctionDefinition
|
||||
{
|
||||
$reflection = Reflection::function($function);
|
||||
|
||||
// @infection-ignore-all
|
||||
$key = $reflection->getFileName() . ':' . $reflection->getStartLine() . '-' . $reflection->getEndLine();
|
||||
|
||||
return ($this->functionDefinitions[$key] ??= $this->delegate->for($function))->forCallable($function);
|
||||
}
|
||||
}
|
||||
14
vendor/cuyz/valinor/src/Definition/Repository/ClassDefinitionRepository.php
vendored
Normal file
14
vendor/cuyz/valinor/src/Definition/Repository/ClassDefinitionRepository.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository;
|
||||
|
||||
use CuyZ\Valinor\Definition\ClassDefinition;
|
||||
use CuyZ\Valinor\Type\ObjectType;
|
||||
|
||||
/** @internal */
|
||||
interface ClassDefinitionRepository
|
||||
{
|
||||
public function for(ObjectType $type): ClassDefinition;
|
||||
}
|
||||
13
vendor/cuyz/valinor/src/Definition/Repository/FunctionDefinitionRepository.php
vendored
Normal file
13
vendor/cuyz/valinor/src/Definition/Repository/FunctionDefinitionRepository.php
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository;
|
||||
|
||||
use CuyZ\Valinor\Definition\FunctionDefinition;
|
||||
|
||||
/** @internal */
|
||||
interface FunctionDefinitionRepository
|
||||
{
|
||||
public function for(callable $function): FunctionDefinition;
|
||||
}
|
||||
124
vendor/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionAttributesRepository.php
vendored
Normal file
124
vendor/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionAttributesRepository.php
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Reflection;
|
||||
|
||||
use CuyZ\Valinor\Definition\AttributeDefinition;
|
||||
use CuyZ\Valinor\Definition\Repository\AttributesRepository;
|
||||
use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
|
||||
use CuyZ\Valinor\Mapper\AsConverter;
|
||||
use CuyZ\Valinor\Normalizer\AsTransformer;
|
||||
use CuyZ\Valinor\Type\Types\NativeClassType;
|
||||
use CuyZ\Valinor\Utility\Reflection\Reflection;
|
||||
use Error;
|
||||
use ReflectionAttribute;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionParameter;
|
||||
use ReflectionProperty;
|
||||
use Reflector;
|
||||
|
||||
use function is_a;
|
||||
use function is_array;
|
||||
use function is_scalar;
|
||||
|
||||
/** @internal */
|
||||
final class ReflectionAttributesRepository implements AttributesRepository
|
||||
{
|
||||
public function __construct(
|
||||
private ClassDefinitionRepository $classDefinitionRepository,
|
||||
/** @var list<class-string> */
|
||||
private array $allowedAttributes,
|
||||
) {}
|
||||
|
||||
public function for(Reflector $reflection): array
|
||||
{
|
||||
$attributes = [];
|
||||
|
||||
foreach ($reflection->getAttributes() as $key => $attribute) {
|
||||
if (! $this->attributeIsAllowed($attribute) || ! $this->attributeCanBeInstantiated($attribute)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$arguments = $attribute->getArguments();
|
||||
|
||||
if (! self::containOnlyScalar($arguments)) {
|
||||
$arguments = null;
|
||||
}
|
||||
|
||||
/** @var null|list<array<scalar>|scalar> $arguments */
|
||||
$attributes[] = new AttributeDefinition(
|
||||
$this->classDefinitionRepository->for(new NativeClassType($attribute->getName())),
|
||||
$arguments,
|
||||
match ($reflection::class) {
|
||||
ReflectionClass::class => ['class', $reflection->name],
|
||||
ReflectionProperty::class => ['property', $reflection->getDeclaringClass()->name, $reflection->name],
|
||||
ReflectionMethod::class => ['method', $reflection->getDeclaringClass()->name, $reflection->name],
|
||||
ReflectionParameter::class => $reflection->getDeclaringFunction()->isClosure()
|
||||
? ['closureParameter', $reflection->getPosition()]
|
||||
// @phpstan-ignore property.nonObject ($reflection->getDeclaringClass() is not null)
|
||||
: ['methodParameter', $reflection->getDeclaringClass()->name, $reflection->getDeclaringFunction()->name, $reflection->getPosition()],
|
||||
default => ['closure'],
|
||||
},
|
||||
$key,
|
||||
);
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ReflectionAttribute<object> $attribute
|
||||
*/
|
||||
private function attributeIsAllowed(ReflectionAttribute $attribute): bool
|
||||
{
|
||||
foreach ($this->allowedAttributes as $allowedAttribute) {
|
||||
if (is_a($attribute->getName(), $allowedAttribute, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return Reflection::class($attribute->getName())->getAttributes(AsConverter::class) !== []
|
||||
|| Reflection::class($attribute->getName())->getAttributes(AsTransformer::class) !== [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ReflectionAttribute<object> $attribute
|
||||
*/
|
||||
private function attributeCanBeInstantiated(ReflectionAttribute $attribute): bool
|
||||
{
|
||||
try {
|
||||
$attribute->newInstance();
|
||||
|
||||
return true;
|
||||
} catch (Error) {
|
||||
// Race condition when the attribute is affected to a property/parameter
|
||||
// that was PROMOTED, in this case the attribute will be applied to both
|
||||
// ParameterReflection AND PropertyReflection, BUT the target arg inside the attribute
|
||||
// class is configured to support only ONE of them (parameter OR property)
|
||||
// https://wiki.php.net/rfc/constructor_promotion#attributes for more details.
|
||||
// Ignore attribute if the instantiation failed.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static function containOnlyScalar(mixed $value): bool
|
||||
{
|
||||
if (is_scalar($value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $subValue) {
|
||||
if (! self::containOnlyScalar($subValue)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
206
vendor/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionClassDefinitionRepository.php
vendored
Normal file
206
vendor/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionClassDefinitionRepository.php
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Reflection;
|
||||
|
||||
use CuyZ\Valinor\Definition\Attributes;
|
||||
use CuyZ\Valinor\Definition\ClassDefinition;
|
||||
use CuyZ\Valinor\Definition\MethodDefinition;
|
||||
use CuyZ\Valinor\Definition\Methods;
|
||||
use CuyZ\Valinor\Definition\Properties;
|
||||
use CuyZ\Valinor\Definition\PropertyDefinition;
|
||||
use CuyZ\Valinor\Definition\Repository\AttributesRepository;
|
||||
use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ClassImportedTypeAliasResolver;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ClassLocalTypeAliasResolver;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ClassParentTypeResolver;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ClassGenericResolver;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ReflectionTypeResolver;
|
||||
use CuyZ\Valinor\Mapper\Object\Constructor;
|
||||
use CuyZ\Valinor\Type\ObjectType;
|
||||
use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
|
||||
use CuyZ\Valinor\Type\Parser\UnresolvableTypeFinderParser;
|
||||
use CuyZ\Valinor\Type\Parser\VacantTypeAssignerParser;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use CuyZ\Valinor\Type\Types\InterfaceType;
|
||||
use CuyZ\Valinor\Type\Types\NativeClassType;
|
||||
use CuyZ\Valinor\Type\Types\UnresolvableType;
|
||||
use CuyZ\Valinor\Utility\Reflection\Reflection;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
|
||||
use function array_filter;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
|
||||
/** @internal */
|
||||
final class ReflectionClassDefinitionRepository implements ClassDefinitionRepository
|
||||
{
|
||||
private TypeParserFactory $typeParserFactory;
|
||||
|
||||
private AttributesRepository $attributesRepository;
|
||||
|
||||
private ReflectionPropertyDefinitionBuilder $propertyBuilder;
|
||||
|
||||
private ReflectionMethodDefinitionBuilder $methodBuilder;
|
||||
|
||||
private ClassParentTypeResolver $parentTypeResolver;
|
||||
|
||||
private ClassGenericResolver $genericResolver;
|
||||
|
||||
private ClassLocalTypeAliasResolver $localTypeAliasResolver;
|
||||
|
||||
private ClassImportedTypeAliasResolver $importedTypeAliasResolver;
|
||||
|
||||
/**
|
||||
* @param list<class-string> $allowedAttributes
|
||||
*/
|
||||
public function __construct(
|
||||
TypeParserFactory $typeParserFactory,
|
||||
array $allowedAttributes,
|
||||
) {
|
||||
$this->typeParserFactory = $typeParserFactory;
|
||||
$this->attributesRepository = new ReflectionAttributesRepository($this, $allowedAttributes);
|
||||
$this->propertyBuilder = new ReflectionPropertyDefinitionBuilder($this->attributesRepository);
|
||||
$this->methodBuilder = new ReflectionMethodDefinitionBuilder($this->attributesRepository);
|
||||
$this->parentTypeResolver = new ClassParentTypeResolver($this->typeParserFactory);
|
||||
$this->genericResolver = new ClassGenericResolver($this->typeParserFactory);
|
||||
$this->localTypeAliasResolver = new ClassLocalTypeAliasResolver($this->typeParserFactory);
|
||||
$this->importedTypeAliasResolver = new ClassImportedTypeAliasResolver($this->typeParserFactory);
|
||||
}
|
||||
|
||||
public function for(ObjectType $type): ClassDefinition
|
||||
{
|
||||
$reflection = Reflection::class($type->className());
|
||||
|
||||
$vacantTypes = $this->vacantTypes($type);
|
||||
|
||||
$nativeTypeParser = $this->typeParserFactory->buildNativeTypeParserForClass($type->className());
|
||||
|
||||
$advancedTypeParser = $this->typeParserFactory->buildAdvancedTypeParserForClass($type->className());
|
||||
$advancedTypeParser = new VacantTypeAssignerParser($advancedTypeParser, $vacantTypes);
|
||||
$advancedTypeParser = new UnresolvableTypeFinderParser($advancedTypeParser);
|
||||
|
||||
$typeResolver = new ReflectionTypeResolver($nativeTypeParser, $advancedTypeParser);
|
||||
|
||||
return new ClassDefinition(
|
||||
$reflection->name,
|
||||
$type,
|
||||
new Attributes(...$this->attributesRepository->for($reflection)),
|
||||
new Properties(...$this->properties($type, $typeResolver)),
|
||||
new Methods(...$this->methods($type, $typeResolver)),
|
||||
$reflection->isFinal(),
|
||||
$reflection->isAbstract(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<non-empty-string, Type>
|
||||
*/
|
||||
private function vacantTypes(ObjectType $type): array
|
||||
{
|
||||
$generics = [];
|
||||
|
||||
if ($type instanceof NativeClassType || $type instanceof InterfaceType) {
|
||||
$generics = $this->genericResolver->resolveGenerics($type);
|
||||
}
|
||||
|
||||
$localTypes = $this->localTypeAliasResolver->resolveLocalTypeAliases($type);
|
||||
$importedTypes = $this->importedTypeAliasResolver->resolveImportedTypeAliases($type);
|
||||
|
||||
$vacantTypes = [...$generics, ...$localTypes, ...$importedTypes];
|
||||
|
||||
$keys = [...array_keys($generics), ...array_keys($localTypes), ...array_keys($importedTypes)];
|
||||
|
||||
// PHP8.5 use pipes
|
||||
$aliasCollision = array_filter(
|
||||
array_count_values($keys),
|
||||
fn (int $count) => $count > 1
|
||||
);
|
||||
|
||||
foreach ($aliasCollision as $alias => $numberOfCollisions) {
|
||||
/** @var non-empty-string $alias */
|
||||
$vacantTypes[$alias] = UnresolvableType::forClassTypeAliasesCollision($alias, $numberOfCollisions);
|
||||
}
|
||||
|
||||
return $vacantTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<PropertyDefinition>
|
||||
*/
|
||||
private function properties(ObjectType $type, ReflectionTypeResolver $typeResolver): array
|
||||
{
|
||||
$reflection = Reflection::class($type->className());
|
||||
|
||||
$properties = [];
|
||||
|
||||
foreach ($reflection->getProperties() as $property) {
|
||||
$declaringClass = $property->getDeclaringClass();
|
||||
|
||||
if ($declaringClass->name === $type->className()) {
|
||||
$properties[$property->name] = $this->propertyBuilder->for($property, $typeResolver);
|
||||
} else {
|
||||
$parentClass = $this->parentTypeResolver->resolveParentTypeFor($type);
|
||||
|
||||
$properties[$property->name] = $this->for($parentClass)->properties->get($property->name);
|
||||
}
|
||||
}
|
||||
|
||||
// Properties will be sorted by inheritance order, from parent to child.
|
||||
$sortedProperties = [];
|
||||
|
||||
while ($reflection) {
|
||||
$currentProperties = array_map(
|
||||
fn (ReflectionProperty $property) => $properties[$property->name],
|
||||
array_filter(
|
||||
$reflection->getProperties(),
|
||||
fn (ReflectionProperty $property) => isset($properties[$property->name]),
|
||||
),
|
||||
);
|
||||
|
||||
$sortedProperties = [...$currentProperties, ...$sortedProperties];
|
||||
|
||||
$reflection = $reflection->getParentClass();
|
||||
}
|
||||
|
||||
return $sortedProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<MethodDefinition>
|
||||
*/
|
||||
private function methods(ObjectType $type, ReflectionTypeResolver $typeResolver): array
|
||||
{
|
||||
$reflection = Reflection::class($type->className());
|
||||
$methods = array_filter($reflection->getMethods(), $this->shouldMethodBeIncluded(...));
|
||||
|
||||
// Because `ReflectionMethod::getMethods()` wont list the constructor if
|
||||
// it comes from a parent class AND is not public, we need to manually
|
||||
// fetch it and add it to the list.
|
||||
if ($reflection->hasMethod('__construct')) {
|
||||
$methods[] = $reflection->getMethod('__construct');
|
||||
}
|
||||
|
||||
return array_map(function (ReflectionMethod $method) use ($type, $typeResolver) {
|
||||
$declaringClass = $method->getDeclaringClass();
|
||||
|
||||
if ($declaringClass->name === $type->className()) {
|
||||
return $this->methodBuilder->for($method, $typeResolver);
|
||||
}
|
||||
|
||||
$parentClass = $this->parentTypeResolver->resolveParentTypeFor($type);
|
||||
|
||||
return $this->for($parentClass)->methods->get($method->name);
|
||||
}, $methods);
|
||||
}
|
||||
|
||||
private function shouldMethodBeIncluded(ReflectionMethod $method): bool
|
||||
{
|
||||
return $method->name === 'map'
|
||||
|| $method->name === 'normalize'
|
||||
|| $method->name === 'normalizeKey'
|
||||
|| $method->getAttributes(Constructor::class) !== [];
|
||||
}
|
||||
}
|
||||
112
vendor/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepository.php
vendored
Normal file
112
vendor/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepository.php
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Reflection;
|
||||
|
||||
use CuyZ\Valinor\Definition\Attributes;
|
||||
use CuyZ\Valinor\Definition\FunctionDefinition;
|
||||
use CuyZ\Valinor\Definition\Parameters;
|
||||
use CuyZ\Valinor\Definition\Repository\AttributesRepository;
|
||||
use CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\FunctionReturnTypeResolver;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ReflectionTypeResolver;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\TemplateResolver;
|
||||
use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
|
||||
use CuyZ\Valinor\Type\Parser\UnresolvableTypeFinderParser;
|
||||
use CuyZ\Valinor\Type\Parser\VacantTypeAssignerParser;
|
||||
use CuyZ\Valinor\Type\Types\UnresolvableType;
|
||||
use CuyZ\Valinor\Utility\Reflection\Reflection;
|
||||
use ReflectionFunction;
|
||||
use ReflectionParameter;
|
||||
|
||||
use function array_map;
|
||||
use function str_ends_with;
|
||||
use function str_starts_with;
|
||||
|
||||
/** @internal */
|
||||
final class ReflectionFunctionDefinitionRepository implements FunctionDefinitionRepository
|
||||
{
|
||||
private TypeParserFactory $typeParserFactory;
|
||||
|
||||
private AttributesRepository $attributesRepository;
|
||||
|
||||
private ReflectionParameterDefinitionBuilder $parameterBuilder;
|
||||
|
||||
private TemplateResolver $templateResolver;
|
||||
|
||||
public function __construct(TypeParserFactory $typeParserFactory, AttributesRepository $attributesRepository)
|
||||
{
|
||||
$this->typeParserFactory = $typeParserFactory;
|
||||
$this->attributesRepository = $attributesRepository;
|
||||
$this->parameterBuilder = new ReflectionParameterDefinitionBuilder($attributesRepository);
|
||||
$this->templateResolver = new TemplateResolver();
|
||||
}
|
||||
|
||||
public function for(callable $function): FunctionDefinition
|
||||
{
|
||||
$reflection = Reflection::function($function);
|
||||
$signature = $this->signature($reflection);
|
||||
|
||||
$nativeParser = $this->typeParserFactory->buildNativeTypeParserForFunction($function);
|
||||
$advancedParser = $this->typeParserFactory->buildAdvancedTypeParserForFunction($function);
|
||||
|
||||
$templates = $this->templateResolver->templatesFromDocBlock($reflection, $signature, $advancedParser);
|
||||
$advancedParser = new VacantTypeAssignerParser($advancedParser, $templates);
|
||||
$advancedParser = new UnresolvableTypeFinderParser($advancedParser);
|
||||
|
||||
$typeResolver = new ReflectionTypeResolver($nativeParser, $advancedParser);
|
||||
|
||||
$returnTypeResolver = new FunctionReturnTypeResolver($typeResolver);
|
||||
|
||||
$parameters = array_map(
|
||||
fn (ReflectionParameter $parameter) => $this->parameterBuilder->for($parameter, $typeResolver),
|
||||
$reflection->getParameters(),
|
||||
);
|
||||
|
||||
$name = $reflection->getName();
|
||||
$class = $reflection->getClosureScopeClass();
|
||||
$returnType = $returnTypeResolver->resolveReturnTypeFor($reflection);
|
||||
$nativeReturnType = $returnTypeResolver->resolveNativeReturnTypeFor($reflection);
|
||||
// PHP8.2 use `ReflectionFunction::isAnonymous()`
|
||||
$isClosure = $name === '{closure}' || str_ends_with($name, '\\{closure}') || str_starts_with($name, '{closure:');
|
||||
|
||||
if ($returnType instanceof UnresolvableType) {
|
||||
$returnType = $returnType->forFunctionReturnType($signature);
|
||||
} elseif (! $returnType->matches($nativeReturnType)) {
|
||||
$returnType = UnresolvableType::forNonMatchingTypes($nativeReturnType, $returnType)->forFunctionReturnType($signature);
|
||||
}
|
||||
|
||||
return (new FunctionDefinition(
|
||||
$name,
|
||||
$signature,
|
||||
new Attributes(...$this->attributesRepository->for($reflection)),
|
||||
$reflection->getFileName() ?: null,
|
||||
$class?->name,
|
||||
$reflection->getClosureThis() === null,
|
||||
$isClosure,
|
||||
new Parameters(...$parameters),
|
||||
$returnType,
|
||||
))->forCallable($function);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
private function signature(ReflectionFunction $reflection): string
|
||||
{
|
||||
// PHP8.2 use `ReflectionFunction::isAnonymous()`
|
||||
if ($reflection->name === '{closure}' || str_ends_with($reflection->name, '\\{closure}') || str_starts_with($reflection->name, '{closure:')) {
|
||||
$startLine = $reflection->getStartLine();
|
||||
$endLine = $reflection->getEndLine();
|
||||
|
||||
return $startLine === $endLine
|
||||
? "Closure (line $startLine of {$reflection->getFileName()})"
|
||||
: "Closure (lines $startLine to $endLine of {$reflection->getFileName()})";
|
||||
}
|
||||
|
||||
return $reflection->getClosureScopeClass()
|
||||
? $reflection->getClosureScopeClass()->name . '::' . $reflection->name . '()'
|
||||
: $reflection->name . '()';
|
||||
}
|
||||
}
|
||||
63
vendor/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionMethodDefinitionBuilder.php
vendored
Normal file
63
vendor/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionMethodDefinitionBuilder.php
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Reflection;
|
||||
|
||||
use CuyZ\Valinor\Definition\Attributes;
|
||||
use CuyZ\Valinor\Definition\MethodDefinition;
|
||||
use CuyZ\Valinor\Definition\Parameters;
|
||||
use CuyZ\Valinor\Definition\Repository\AttributesRepository;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\FunctionReturnTypeResolver;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ReflectionTypeResolver;
|
||||
use CuyZ\Valinor\Type\Types\UnresolvableType;
|
||||
use ReflectionMethod;
|
||||
use ReflectionParameter;
|
||||
|
||||
use function array_map;
|
||||
|
||||
/** @internal */
|
||||
final class ReflectionMethodDefinitionBuilder
|
||||
{
|
||||
private AttributesRepository $attributesRepository;
|
||||
|
||||
private ReflectionParameterDefinitionBuilder $parameterBuilder;
|
||||
|
||||
public function __construct(AttributesRepository $attributesRepository)
|
||||
{
|
||||
$this->attributesRepository = $attributesRepository;
|
||||
$this->parameterBuilder = new ReflectionParameterDefinitionBuilder($attributesRepository);
|
||||
}
|
||||
|
||||
public function for(ReflectionMethod $reflection, ReflectionTypeResolver $typeResolver): MethodDefinition
|
||||
{
|
||||
$name = $reflection->name;
|
||||
$signature = $reflection->getDeclaringClass()->name . '::' . $reflection->name . '()';
|
||||
|
||||
$parameters = array_map(
|
||||
fn (ReflectionParameter $parameter) => $this->parameterBuilder->for($parameter, $typeResolver),
|
||||
$reflection->getParameters()
|
||||
);
|
||||
|
||||
$returnTypeResolver = new FunctionReturnTypeResolver($typeResolver);
|
||||
|
||||
$returnType = $returnTypeResolver->resolveReturnTypeFor($reflection);
|
||||
$nativeReturnType = $returnTypeResolver->resolveNativeReturnTypeFor($reflection);
|
||||
|
||||
if ($returnType instanceof UnresolvableType) {
|
||||
$returnType = $returnType->forMethodReturnType($signature);
|
||||
} elseif (! $returnType->matches($nativeReturnType)) {
|
||||
$returnType = UnresolvableType::forNonMatchingTypes($nativeReturnType, $returnType)->forMethodReturnType($signature);
|
||||
}
|
||||
|
||||
return new MethodDefinition(
|
||||
$name,
|
||||
$signature,
|
||||
new Attributes(...$this->attributesRepository->for($reflection)),
|
||||
new Parameters(...$parameters),
|
||||
$reflection->isStatic(),
|
||||
$reflection->isPublic(),
|
||||
$returnType
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Reflection;
|
||||
|
||||
use CuyZ\Valinor\Definition\Attributes;
|
||||
use CuyZ\Valinor\Definition\ParameterDefinition;
|
||||
use CuyZ\Valinor\Definition\Repository\AttributesRepository;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ParameterTypeResolver;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ReflectionTypeResolver;
|
||||
use CuyZ\Valinor\Type\Types\UnresolvableType;
|
||||
use ReflectionParameter;
|
||||
|
||||
/** @internal */
|
||||
final class ReflectionParameterDefinitionBuilder
|
||||
{
|
||||
public function __construct(private AttributesRepository $attributesRepository) {}
|
||||
|
||||
public function for(ReflectionParameter $reflection, ReflectionTypeResolver $typeResolver): ParameterDefinition
|
||||
{
|
||||
$parameterTypeResolver = new ParameterTypeResolver($typeResolver);
|
||||
|
||||
/** @var non-empty-string $name */
|
||||
$name = $reflection->name;
|
||||
$signature = $this->signature($reflection);
|
||||
$type = $parameterTypeResolver->resolveTypeFor($reflection);
|
||||
$nativeType = $parameterTypeResolver->resolveNativeTypeFor($reflection);
|
||||
$isOptional = $reflection->isOptional();
|
||||
$isVariadic = $reflection->isVariadic();
|
||||
|
||||
if ($reflection->isDefaultValueAvailable()) {
|
||||
$defaultValue = $reflection->getDefaultValue();
|
||||
} elseif ($reflection->isVariadic()) {
|
||||
$defaultValue = [];
|
||||
} else {
|
||||
$defaultValue = null;
|
||||
}
|
||||
|
||||
if ($type instanceof UnresolvableType) {
|
||||
$type = $type->forParameter($signature);
|
||||
} elseif (! $type->matches($nativeType)) {
|
||||
$type = UnresolvableType::forNonMatchingTypes($nativeType, $type)->forParameter($signature);
|
||||
} elseif ($isOptional && ! $type->accepts($defaultValue)) {
|
||||
$type = UnresolvableType::forInvalidDefaultValue($type, $defaultValue)->forParameter($signature);
|
||||
}
|
||||
|
||||
return new ParameterDefinition(
|
||||
$name,
|
||||
$signature,
|
||||
$type,
|
||||
$nativeType,
|
||||
$isOptional,
|
||||
$isVariadic,
|
||||
$defaultValue,
|
||||
new Attributes(...$this->attributesRepository->for($reflection)),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
private function signature(ReflectionParameter $reflection): string
|
||||
{
|
||||
$signature = $reflection->getDeclaringFunction()->name . "(\$$reflection->name)";
|
||||
$class = $reflection->getDeclaringClass();
|
||||
|
||||
if ($class) {
|
||||
$signature = $class->name . '::' . $signature;
|
||||
}
|
||||
|
||||
return $signature;
|
||||
}
|
||||
}
|
||||
64
vendor/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionPropertyDefinitionBuilder.php
vendored
Normal file
64
vendor/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionPropertyDefinitionBuilder.php
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Reflection;
|
||||
|
||||
use CuyZ\Valinor\Definition\Attributes;
|
||||
use CuyZ\Valinor\Definition\PropertyDefinition;
|
||||
use CuyZ\Valinor\Definition\Repository\AttributesRepository;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\PropertyTypeResolver;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ReflectionTypeResolver;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use CuyZ\Valinor\Type\Types\NullType;
|
||||
use CuyZ\Valinor\Type\Types\UnresolvableType;
|
||||
use ReflectionProperty;
|
||||
|
||||
/** @internal */
|
||||
final class ReflectionPropertyDefinitionBuilder
|
||||
{
|
||||
public function __construct(private AttributesRepository $attributesRepository) {}
|
||||
|
||||
public function for(ReflectionProperty $reflection, ReflectionTypeResolver $typeResolver): PropertyDefinition
|
||||
{
|
||||
$propertyTypeResolver = new PropertyTypeResolver($typeResolver);
|
||||
|
||||
/** @var non-empty-string $name */
|
||||
$name = $reflection->name;
|
||||
$signature = $reflection->getDeclaringClass()->name . '::$' . $reflection->name;
|
||||
$type = $propertyTypeResolver->resolveTypeFor($reflection);
|
||||
$nativeType = $propertyTypeResolver->resolveNativeTypeFor($reflection);
|
||||
$hasDefaultValue = $this->hasDefaultValue($reflection, $type);
|
||||
$defaultValue = $hasDefaultValue ? $reflection->getDefaultValue() : null;
|
||||
$isPublic = $reflection->isPublic();
|
||||
|
||||
if ($type instanceof UnresolvableType) {
|
||||
$type = $type->forProperty($signature);
|
||||
} elseif (! $type->matches($nativeType)) {
|
||||
$type = UnresolvableType::forNonMatchingTypes($nativeType, $type)->forProperty($signature);
|
||||
} elseif ($hasDefaultValue && ! $type->accepts($defaultValue)) {
|
||||
$type = UnresolvableType::forInvalidDefaultValue($type, $defaultValue)->forProperty($signature);
|
||||
}
|
||||
|
||||
return new PropertyDefinition(
|
||||
$name,
|
||||
$signature,
|
||||
$type,
|
||||
$nativeType,
|
||||
$hasDefaultValue,
|
||||
$defaultValue,
|
||||
$isPublic,
|
||||
new Attributes(...$this->attributesRepository->for($reflection)),
|
||||
);
|
||||
}
|
||||
|
||||
private function hasDefaultValue(ReflectionProperty $reflection, Type $type): bool
|
||||
{
|
||||
if ($reflection->hasType()) {
|
||||
return $reflection->hasDefaultValue();
|
||||
}
|
||||
|
||||
return $reflection->getDeclaringClass()->getDefaultProperties()[$reflection->name] !== null
|
||||
|| NullType::get()->matches($type);
|
||||
}
|
||||
}
|
||||
50
vendor/cuyz/valinor/src/Definition/Repository/Reflection/TypeResolver/ClassGenericResolver.php
vendored
Normal file
50
vendor/cuyz/valinor/src/Definition/Repository/Reflection/TypeResolver/ClassGenericResolver.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver;
|
||||
|
||||
use CuyZ\Valinor\Type\ObjectWithGenericType;
|
||||
use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use CuyZ\Valinor\Type\Types\UnresolvableType;
|
||||
use CuyZ\Valinor\Utility\Reflection\Reflection;
|
||||
|
||||
/** @internal */
|
||||
final class ClassGenericResolver
|
||||
{
|
||||
private TemplateResolver $templateResolver;
|
||||
|
||||
public function __construct(
|
||||
private TypeParserFactory $typeParserFactory,
|
||||
) {
|
||||
$this->templateResolver = new TemplateResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<non-empty-string, Type>
|
||||
*/
|
||||
public function resolveGenerics(ObjectWithGenericType $type): array
|
||||
{
|
||||
$typeParser = $this->typeParserFactory->buildAdvancedTypeParserForClass($type->className());
|
||||
|
||||
$templates = $this->templateResolver->templatesFromDocBlock(Reflection::class($type->className()), $type->className(), $typeParser);
|
||||
$generics = $type->generics();
|
||||
|
||||
$assignedGenerics = [];
|
||||
|
||||
foreach ($templates as $template => $templateType) {
|
||||
$generic = $generics === [] ? $templateType : array_shift($generics);
|
||||
|
||||
if ($templateType->innerType instanceof UnresolvableType) {
|
||||
$generic = $templateType->innerType;
|
||||
} elseif (! $generic instanceof UnresolvableType && ! $generic->matches($templateType->innerType)) {
|
||||
$generic = UnresolvableType::forInvalidAssignedGeneric($generic, $templateType->innerType, $template, $type->className());
|
||||
}
|
||||
|
||||
$assignedGenerics[$template] = $generic;
|
||||
}
|
||||
|
||||
return $assignedGenerics;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver;
|
||||
|
||||
use CuyZ\Valinor\Type\ObjectType;
|
||||
use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use CuyZ\Valinor\Type\Types\UnresolvableType;
|
||||
use CuyZ\Valinor\Utility\Reflection\Annotations;
|
||||
|
||||
use function current;
|
||||
use function key;
|
||||
use function next;
|
||||
|
||||
/** @internal */
|
||||
final class ClassImportedTypeAliasResolver
|
||||
{
|
||||
private ClassLocalTypeAliasResolver $localTypeAliasResolver;
|
||||
|
||||
public function __construct(private TypeParserFactory $typeParserFactory)
|
||||
{
|
||||
$this->localTypeAliasResolver = new ClassLocalTypeAliasResolver($this->typeParserFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<non-empty-string, Type>
|
||||
*/
|
||||
public function resolveImportedTypeAliases(ObjectType $type): array
|
||||
{
|
||||
$importedTypesRaw = $this->extractImportedAliasesFromDocBlock($type->className());
|
||||
|
||||
if ($importedTypesRaw === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$typeParser = $this->typeParserFactory->buildAdvancedTypeParserForClass($type->className());
|
||||
|
||||
$importedTypes = [];
|
||||
|
||||
foreach ($importedTypesRaw as $class => $types) {
|
||||
$classType = $typeParser->parse($class);
|
||||
|
||||
if (! $classType instanceof ObjectType) {
|
||||
foreach ($types as $importedType) {
|
||||
$importedTypes[$importedType] = UnresolvableType::forInvalidAliasImportClassType($type->className(), $types[0], $class);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$localTypes = $this->localTypeAliasResolver->resolveLocalTypeAliases($classType);
|
||||
|
||||
foreach ($types as $importedType) {
|
||||
if (isset($localTypes[$importedType])) {
|
||||
$importedTypes[$importedType] = $localTypes[$importedType];
|
||||
} else {
|
||||
$importedTypes[$importedType] = UnresolvableType::forUnknownTypeAliasImport($type, $classType->className(), $importedType);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $importedTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string $className
|
||||
* @return array<non-empty-string, non-empty-list<non-empty-string>>
|
||||
*/
|
||||
private function extractImportedAliasesFromDocBlock(string $className): array
|
||||
{
|
||||
$importedAliases = [];
|
||||
|
||||
$annotations = Annotations::forImportTypes($className);
|
||||
|
||||
foreach ($annotations as $annotation) {
|
||||
$tokens = $annotation->filtered();
|
||||
|
||||
$name = current($tokens);
|
||||
$from = next($tokens);
|
||||
|
||||
if ($from !== 'from') {
|
||||
continue;
|
||||
}
|
||||
|
||||
next($tokens);
|
||||
|
||||
$key = key($tokens);
|
||||
|
||||
// @phpstan-ignore identical.alwaysFalse (Somehow PHPStan does not properly infer the key)
|
||||
if ($key === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$class = $annotation->allAfter($key);
|
||||
|
||||
$importedAliases[$class][] = $name;
|
||||
}
|
||||
|
||||
return $importedAliases;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver;
|
||||
|
||||
use CuyZ\Valinor\Type\ObjectWithGenericType;
|
||||
use CuyZ\Valinor\Type\ObjectType;
|
||||
use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
|
||||
use CuyZ\Valinor\Type\Parser\VacantTypeAssignerParser;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use CuyZ\Valinor\Type\Types\UnresolvableType;
|
||||
use CuyZ\Valinor\Utility\Reflection\Annotations;
|
||||
|
||||
use function current;
|
||||
use function key;
|
||||
use function next;
|
||||
|
||||
/** @internal */
|
||||
final class ClassLocalTypeAliasResolver
|
||||
{
|
||||
private ClassGenericResolver $genericResolver;
|
||||
|
||||
public function __construct(
|
||||
private TypeParserFactory $typeParserFactory,
|
||||
) {
|
||||
$this->genericResolver = new ClassGenericResolver($this->typeParserFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<non-empty-string, Type>
|
||||
*/
|
||||
public function resolveLocalTypeAliases(ObjectType $type): array
|
||||
{
|
||||
$localAliases = $this->extractLocalAliasesFromDocBlock($type->className());
|
||||
|
||||
if ($localAliases === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$vacantTypes = [];
|
||||
|
||||
if ($type instanceof ObjectWithGenericType) {
|
||||
$vacantTypes = $this->genericResolver->resolveGenerics($type);
|
||||
}
|
||||
|
||||
$typeParser = $this->typeParserFactory->buildAdvancedTypeParserForClass($type->className());
|
||||
$typeParser = new VacantTypeAssignerParser($typeParser, $vacantTypes);
|
||||
|
||||
$types = [];
|
||||
|
||||
foreach ($localAliases as $name => $raw) {
|
||||
$types[$name] = $typeParser->parse($raw);
|
||||
|
||||
if ($types[$name] instanceof UnresolvableType) {
|
||||
$types[$name] = $types[$name]->forLocalAlias($raw, $name, $type);
|
||||
}
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string $className
|
||||
* @return array<non-empty-string, non-empty-string>
|
||||
*/
|
||||
private function extractLocalAliasesFromDocBlock(string $className): array
|
||||
{
|
||||
$aliases = [];
|
||||
|
||||
$annotations = Annotations::forLocalAliases($className);
|
||||
|
||||
foreach ($annotations as $annotation) {
|
||||
$tokens = $annotation->filtered();
|
||||
|
||||
$name = current($tokens);
|
||||
$next = next($tokens);
|
||||
|
||||
if ($next === '=') {
|
||||
next($tokens);
|
||||
}
|
||||
|
||||
$key = key($tokens);
|
||||
|
||||
// @phpstan-ignore notIdentical.alwaysTrue (Somehow PHPStan does not properly infer the key)
|
||||
if ($key !== null) {
|
||||
$aliases[$name] = $annotation->allAfter($key);
|
||||
}
|
||||
}
|
||||
|
||||
return $aliases;
|
||||
}
|
||||
}
|
||||
100
vendor/cuyz/valinor/src/Definition/Repository/Reflection/TypeResolver/ClassParentTypeResolver.php
vendored
Normal file
100
vendor/cuyz/valinor/src/Definition/Repository/Reflection/TypeResolver/ClassParentTypeResolver.php
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver;
|
||||
|
||||
use CuyZ\Valinor\Type\ObjectType;
|
||||
use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
|
||||
use CuyZ\Valinor\Type\Parser\Lexer\TokenizedAnnotation;
|
||||
use CuyZ\Valinor\Type\Parser\VacantTypeAssignerParser;
|
||||
use CuyZ\Valinor\Type\Types\GenericType;
|
||||
use CuyZ\Valinor\Type\Types\NativeClassType;
|
||||
use CuyZ\Valinor\Type\Types\UnresolvableType;
|
||||
use CuyZ\Valinor\Utility\Reflection\Annotations;
|
||||
use CuyZ\Valinor\Utility\Reflection\Reflection;
|
||||
use ReflectionClass;
|
||||
|
||||
use function array_map;
|
||||
use function array_values;
|
||||
|
||||
/** @internal */
|
||||
final class ClassParentTypeResolver
|
||||
{
|
||||
private ClassGenericResolver $genericResolver;
|
||||
|
||||
private TemplateResolver $templateResolver;
|
||||
|
||||
public function __construct(private TypeParserFactory $typeParserFactory)
|
||||
{
|
||||
$this->genericResolver = new ClassGenericResolver($this->typeParserFactory);
|
||||
$this->templateResolver = new TemplateResolver();
|
||||
}
|
||||
|
||||
public function resolveParentTypeFor(ObjectType $child): NativeClassType
|
||||
{
|
||||
assert($child instanceof NativeClassType);
|
||||
|
||||
$reflection = Reflection::class($child->className());
|
||||
|
||||
/** @var ReflectionClass<covariant object> $parentReflection */
|
||||
$parentReflection = $reflection->getParentClass();
|
||||
|
||||
$extendedClass = $this->extractParentTypeFromDocBlock($reflection);
|
||||
|
||||
if (count($extendedClass) > 1) {
|
||||
return $this->fillParentGenericsWithUnresolvableTypes($parentReflection, UnresolvableType::forSeveralExtendTagsFound($reflection->name));
|
||||
} elseif (count($extendedClass) === 0) {
|
||||
$extendedClass = $parentReflection->name;
|
||||
} else {
|
||||
$extendedClass = $extendedClass[0];
|
||||
}
|
||||
|
||||
$generics = $this->genericResolver->resolveGenerics($child);
|
||||
|
||||
$typeParser = $this->typeParserFactory->buildAdvancedTypeParserForClass($child->className());
|
||||
$typeParser = new VacantTypeAssignerParser($typeParser, $generics);
|
||||
|
||||
$parentType = $typeParser->parse($extendedClass);
|
||||
|
||||
if ($parentType instanceof UnresolvableType) {
|
||||
return $this->fillParentGenericsWithUnresolvableTypes($parentReflection, $parentType->forExtendTagTypeError($reflection->name));
|
||||
}
|
||||
|
||||
if (! $parentType instanceof NativeClassType || $parentType->className() !== $parentReflection->name) {
|
||||
return $this->fillParentGenericsWithUnresolvableTypes($parentReflection, UnresolvableType::forInvalidExtendTagType($reflection->name, $parentReflection->name, $parentType));
|
||||
}
|
||||
|
||||
return $parentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ReflectionClass<covariant object> $reflection
|
||||
* @return list<non-empty-string>
|
||||
*/
|
||||
private function extractParentTypeFromDocBlock(ReflectionClass $reflection): array
|
||||
{
|
||||
$annotations = Annotations::forParents($reflection->name);
|
||||
|
||||
return array_map(
|
||||
fn (TokenizedAnnotation $annotation) => $annotation->raw(),
|
||||
$annotations,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ReflectionClass<covariant object> $class
|
||||
*/
|
||||
private function fillParentGenericsWithUnresolvableTypes(ReflectionClass $class, UnresolvableType $unresolvableType): NativeClassType
|
||||
{
|
||||
$typeParser = $this->typeParserFactory->buildAdvancedTypeParserForClass($class->name);
|
||||
|
||||
$templates = $this->templateResolver->templatesFromDocBlock($class, $class->name, $typeParser);
|
||||
$generics = array_values(array_map(
|
||||
static fn (GenericType $type) => new UnresolvableType($type->symbol, $unresolvableType->message()),
|
||||
$templates,
|
||||
));
|
||||
|
||||
return new NativeClassType($class->name, $generics);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver;
|
||||
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use CuyZ\Valinor\Utility\Reflection\Annotations;
|
||||
use ReflectionFunctionAbstract;
|
||||
|
||||
/** @internal */
|
||||
final class FunctionReturnTypeResolver
|
||||
{
|
||||
public function __construct(private ReflectionTypeResolver $typeResolver) {}
|
||||
|
||||
public function resolveReturnTypeFor(ReflectionFunctionAbstract $reflection): Type
|
||||
{
|
||||
$docBlockType = Annotations::forFunctionReturnType($reflection);
|
||||
|
||||
return $this->typeResolver->resolveType($reflection->getReturnType(), $docBlockType);
|
||||
}
|
||||
|
||||
public function resolveNativeReturnTypeFor(ReflectionFunctionAbstract $reflection): Type
|
||||
{
|
||||
return $this->typeResolver->resolveNativeType($reflection->getReturnType());
|
||||
}
|
||||
}
|
||||
76
vendor/cuyz/valinor/src/Definition/Repository/Reflection/TypeResolver/ParameterTypeResolver.php
vendored
Normal file
76
vendor/cuyz/valinor/src/Definition/Repository/Reflection/TypeResolver/ParameterTypeResolver.php
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver;
|
||||
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use CuyZ\Valinor\Type\Types\ArrayKeyType;
|
||||
use CuyZ\Valinor\Type\Types\ArrayType;
|
||||
use CuyZ\Valinor\Type\Types\UnresolvableType;
|
||||
use CuyZ\Valinor\Utility\Reflection\Annotations;
|
||||
use ReflectionParameter;
|
||||
|
||||
/** @internal */
|
||||
final class ParameterTypeResolver
|
||||
{
|
||||
public function __construct(private ReflectionTypeResolver $typeResolver) {}
|
||||
|
||||
public function resolveTypeFor(ReflectionParameter $reflection): Type
|
||||
{
|
||||
$docBlockType = null;
|
||||
|
||||
if ($reflection->isPromoted()) {
|
||||
// @phpstan-ignore-next-line / parameter is promoted so class exists for sure
|
||||
$property = $reflection->getDeclaringClass()->getProperty($reflection->name);
|
||||
|
||||
$docBlockType = Annotations::forProperty($property);
|
||||
}
|
||||
|
||||
if ($docBlockType === null) {
|
||||
$docBlockType = $this->extractTypeFromDocBlock($reflection);
|
||||
}
|
||||
|
||||
$type = $this->typeResolver->resolveType($reflection->getType(), $docBlockType);
|
||||
|
||||
if ($reflection->isVariadic() && ! $type instanceof UnresolvableType) {
|
||||
return new ArrayType(ArrayKeyType::default(), $type);
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
public function resolveNativeTypeFor(ReflectionParameter $reflection): Type
|
||||
{
|
||||
$type = $this->typeResolver->resolveNativeType($reflection->getType());
|
||||
|
||||
if ($reflection->isVariadic()) {
|
||||
return new ArrayType(ArrayKeyType::default(), $type);
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
private function extractTypeFromDocBlock(ReflectionParameter $reflection): ?string
|
||||
{
|
||||
$annotations = Annotations::forParameters($reflection->getDeclaringFunction());
|
||||
|
||||
foreach ($annotations as $annotation) {
|
||||
$tokens = $annotation->filtered();
|
||||
|
||||
$dollarSignKey = array_search('$', $tokens, true);
|
||||
|
||||
if ($dollarSignKey === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parameterName = $tokens[$dollarSignKey + 1] ?? null;
|
||||
|
||||
if ($parameterName === $reflection->name) {
|
||||
return $annotation->raw();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
27
vendor/cuyz/valinor/src/Definition/Repository/Reflection/TypeResolver/PropertyTypeResolver.php
vendored
Normal file
27
vendor/cuyz/valinor/src/Definition/Repository/Reflection/TypeResolver/PropertyTypeResolver.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver;
|
||||
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use CuyZ\Valinor\Utility\Reflection\Annotations;
|
||||
use ReflectionProperty;
|
||||
|
||||
/** @internal */
|
||||
final class PropertyTypeResolver
|
||||
{
|
||||
public function __construct(private ReflectionTypeResolver $typeResolver) {}
|
||||
|
||||
public function resolveTypeFor(ReflectionProperty $reflection): Type
|
||||
{
|
||||
$docBlockType = Annotations::forProperty($reflection);
|
||||
|
||||
return $this->typeResolver->resolveType($reflection->getType(), $docBlockType);
|
||||
}
|
||||
|
||||
public function resolveNativeTypeFor(ReflectionProperty $reflection): Type
|
||||
{
|
||||
return $this->typeResolver->resolveNativeType($reflection->getType());
|
||||
}
|
||||
}
|
||||
75
vendor/cuyz/valinor/src/Definition/Repository/Reflection/TypeResolver/ReflectionTypeResolver.php
vendored
Normal file
75
vendor/cuyz/valinor/src/Definition/Repository/Reflection/TypeResolver/ReflectionTypeResolver.php
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver;
|
||||
|
||||
use CuyZ\Valinor\Type\Parser\TypeParser;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use CuyZ\Valinor\Type\Types\MixedType;
|
||||
use ReflectionIntersectionType;
|
||||
use ReflectionNamedType;
|
||||
use ReflectionType;
|
||||
use ReflectionUnionType;
|
||||
|
||||
use function trim;
|
||||
|
||||
/** @internal */
|
||||
final class ReflectionTypeResolver
|
||||
{
|
||||
public function __construct(
|
||||
private TypeParser $nativeParser,
|
||||
private TypeParser $advancedParser,
|
||||
) {}
|
||||
|
||||
public function resolveType(?ReflectionType $native, ?string $docBlock): Type
|
||||
{
|
||||
if ($docBlock !== null) {
|
||||
$docBlock = trim($docBlock);
|
||||
|
||||
return $this->advancedParser->parse($docBlock);
|
||||
}
|
||||
|
||||
if ($native === null) {
|
||||
return MixedType::get();
|
||||
}
|
||||
|
||||
$type = $this->exportNativeType($native);
|
||||
|
||||
// When the type is a class, it may declare templates that must be
|
||||
// filled with generics. PHP does not handle generics natively, so we
|
||||
// need to make sure that no generics are left unassigned by parsing the
|
||||
// type using the advanced parser.
|
||||
return $this->advancedParser->parse($type);
|
||||
}
|
||||
|
||||
public function resolveNativeType(?ReflectionType $reflection): Type
|
||||
{
|
||||
if ($reflection === null) {
|
||||
return MixedType::get();
|
||||
}
|
||||
|
||||
$type = $this->exportNativeType($reflection);
|
||||
|
||||
return $this->nativeParser->parse($type);
|
||||
}
|
||||
|
||||
private function exportNativeType(ReflectionType $type): string
|
||||
{
|
||||
if ($type instanceof ReflectionUnionType) {
|
||||
return implode('|', $type->getTypes());
|
||||
}
|
||||
if ($type instanceof ReflectionIntersectionType) {
|
||||
return implode('&', $type->getTypes());
|
||||
}
|
||||
|
||||
/** @var ReflectionNamedType $type */
|
||||
$name = $type->getName();
|
||||
|
||||
if ($name !== 'null' && $type->allowsNull() && $name !== 'mixed') {
|
||||
return $name . '|null';
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
67
vendor/cuyz/valinor/src/Definition/Repository/Reflection/TypeResolver/TemplateResolver.php
vendored
Normal file
67
vendor/cuyz/valinor/src/Definition/Repository/Reflection/TypeResolver/TemplateResolver.php
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver;
|
||||
|
||||
use CuyZ\Valinor\Type\Parser\TypeParser;
|
||||
use CuyZ\Valinor\Type\Types\GenericType;
|
||||
use CuyZ\Valinor\Type\Types\MixedType;
|
||||
use CuyZ\Valinor\Type\Types\UnresolvableType;
|
||||
use CuyZ\Valinor\Utility\Reflection\Annotations;
|
||||
use ReflectionClass;
|
||||
use ReflectionFunctionAbstract;
|
||||
|
||||
/** @internal */
|
||||
final class TemplateResolver
|
||||
{
|
||||
/**
|
||||
* @param ReflectionClass<covariant object>|ReflectionFunctionAbstract $reflection
|
||||
* @return array<non-empty-string, GenericType>
|
||||
*/
|
||||
public function templatesFromDocBlock(ReflectionClass|ReflectionFunctionAbstract $reflection, string $signature, TypeParser $typeParser): array
|
||||
{
|
||||
$annotations = Annotations::forTemplates($reflection);
|
||||
|
||||
$templates = [];
|
||||
|
||||
foreach ($annotations as $annotation) {
|
||||
$tokens = $annotation->filtered();
|
||||
|
||||
$name = current($tokens);
|
||||
|
||||
if (array_key_exists($name, $templates)) {
|
||||
$templateType = UnresolvableType::forDuplicatedTemplateName($signature, $name);
|
||||
} else {
|
||||
$of = next($tokens);
|
||||
|
||||
if ($of !== 'of') {
|
||||
// The keyword `of` was not found, the following tokens are
|
||||
// considered as comments, and we ignore them.
|
||||
$templateType = MixedType::get();
|
||||
} else {
|
||||
// The keyword `of` was found, the following tokens represent
|
||||
// the template type.
|
||||
next($tokens);
|
||||
|
||||
/** @var array<int, non-empty-string> $tokens */
|
||||
$key = key($tokens);
|
||||
|
||||
if ($key === null) {
|
||||
$templateType = MixedType::get();
|
||||
} else {
|
||||
$templateType = $typeParser->parse($annotation->allAfter($key));
|
||||
|
||||
if ($templateType instanceof UnresolvableType) {
|
||||
$templateType = $templateType->forInvalidTemplateType($signature, $name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$templates[$name] = new GenericType($name, $templateType);
|
||||
}
|
||||
|
||||
return $templates;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user