Issues (55)

src/ScalarParam.php (2 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\Resource;
6
7
use Ray\Di\Di\Named;
8
use Ray\Di\Di\Qualifier;
9
use Ray\Di\InjectorInterface;
10
use ReflectionClass;
11
use ReflectionMethod;
12
use ReflectionNamedType;
13
14
use function array_shift;
15
use function array_unshift;
16
use function assert;
17
use function class_exists;
18
19
/**
20
 * @psalm-type DependencyMeta = array{0:class-string|'', 1:string}
21
 * @psalm-type DependencyMetas = list<DependencyMeta>
22
 */
23
final class ScalarParam implements ParamInterface
24
{
25
    /** @var DependencyMetas */
26
    private array $dependenciesMetas;
27
    private QueryProp $queryProp;
28
29
    /** @param class-string $typeName */
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
30
    public function __construct(
31
        private string $typeName,
32
    ) {
33
        // Retrieve the dependency metadata to construct the object later
34
        $this->dependenciesMetas = $this->getDependenciesMetas();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getDependenciesMetas() of type array is incompatible with the declared type BEAR\Resource\DependencyMetas of property $dependenciesMetas.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
35
        $this->queryProp = new QueryProp();
36
    }
37
38
    /**
39
     * {@inheritDoc}
40
     *
41
     * @param array<string, mixed> $query
42
     *
43
     * @return object
44
     */
45
    public function __invoke(string $varName, array $query, InjectorInterface $injector): object
46
    {
47
        /** @psalm-suppress MixedAssignment */
48
        $arg1 = $this->queryProp->getProp($varName, $query, $injector);
49
        $args = [];
50
        foreach ($this->dependenciesMetas as $meta) {
51
            /** @psalm-suppress MixedAssignment */
52
            [$className, $qualifier] = $meta;
53
            /** @psalm-suppress MixedAssignment, ArgumentTypeCoercion, MixedArgument */
54
            $args[] = $injector->getInstance($className, $qualifier);
55
        }
56
57
        array_unshift($args, $arg1);
58
59
        /** @psalm-suppress InvalidStringClass, MixedMethodCall */
60
        return new $this->typeName(...$args);
61
    }
62
63
    /**
64
     * @return DependencyMetas
65
     *
66
     * @psalm-suppress MoreSpecificReturnType, LessSpecificReturnStatement
67
     */
68
    public function getDependenciesMetas(): array
69
    {
70
        $class = new ReflectionClass($this->typeName);
71
        $const = $class->getConstructor();
72
        assert($const instanceof ReflectionMethod, 'Expected class has a constructor');
73
74
        $args = $const->getParameters();
75
        $params = [];
76
77
        // Skip the first parameter (scalar value)
78
        array_shift($args);
79
80
        foreach ($args as $arg) {
81
            $type = $arg->getType();
82
            assert($type instanceof ReflectionNamedType || $type === null, 'Expected a named type or null');
83
            $typeName = $type ? $type->getName() : '';
84
            $attributes = $arg->getAttributes();
85
            foreach ($attributes as $attribute) {
86
                if ($attribute->getName() === Named::class) {
87
                    $named = $attribute->newInstance();
88
                    /** @var DependencyMeta $dependencyMeta */
89
                    $dependencyMeta = [$typeName, $named->value];
90
                    $params[] = $dependencyMeta;
91
                    continue;
92
                }
93
94
                $class = $attribute->getName();
95
                assert(class_exists($class));
96
                $attributeReflection = new ReflectionClass($class);
97
                // Check if this attribute has the Qualifier attribute
98
                $qualifierAttributes = $attributeReflection->getAttributes(Qualifier::class);
99
                if (! $qualifierAttributes) {
100
                    continue;
101
                }
102
103
                /** @var DependencyMeta $dependencyMeta */
104
                $dependencyMeta = [$typeName, $attribute->getName()];
105
106
                $params[] = $dependencyMeta;
107
            }
108
109
            $params[] = [$typeName, ''];
110
        }
111
112
        /** @var DependencyMetas $params */
113
        return $params;
114
    }
115
}
116