Issues (9)

DIReflector.php (6 issues)

1
<?php declare(strict_types=1);
2
3
/*
4
 * This file is part of the Koded package.
5
 *
6
 * (c) Mihail Binev <[email protected]>
7
 *
8
 * Please view the LICENSE distributed with this source code
9
 * for the full copyright and license information.
10
 *
11
 */
12
13
namespace Koded;
14
15
use Closure;
16
use Error;
17
use ReflectionClass;
18
use ReflectionException;
19
use ReflectionFunction;
20
use ReflectionFunctionAbstract;
21
use ReflectionMethod;
22
use ReflectionParameter;
23
use function array_intersect;
24
use function array_keys;
25
use function gettype;
26
27
class DIReflector
28
{
29 33
    public function newInstance(
30
        IContainer $container,
0 ignored issues
show
The type Koded\IContainer was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
31
        string     $class,
32
        array      $arguments): object
33
    {
34
        try {
35 33
            $dependency = new ReflectionClass($class);
36 1
        } catch (ReflectionException $e) {
37 1
            throw DIException::forReflectionError($e);
38
        }
39 32
        $constructor = $dependency->getConstructor();
40 32
        if ($dependency->isInstantiable()) {
41 27
            return $constructor
42 24
                ? new $class(...$this->processMethodArguments($container, $constructor, $arguments))
43 25
                : new $class;
44
        }
45 6
        if (false === $constructor?->isPublic()) {
46 1
            throw DIException::forNonPublicMethod(
47 1
                $constructor->getDeclaringClass()->name,
48 1
                $constructor->name);
49
        }
50 5
        throw DIException::cannotInstantiate($dependency);
51
    }
52
53
    /**
54
     * @param IContainer $container
55
     * @param ReflectionFunctionAbstract $method
56
     * @param array $arguments
57
     * @return array
58
     */
59 26
    public function processMethodArguments(
60
        IContainer                 $container,
61
        ReflectionFunctionAbstract $method,
62
        array                      $arguments): array
63
    {
64 26
        $args = $method->getParameters();
65 26
        foreach ($args as $i => $param) {
66 21
            $args[$i] = $this->getFromParameterType($container, $param, $arguments[$i] ?? null);
67
        }
68 23
        return $args;
69
    }
70
71
    /**
72
     * @param callable $callable
73
     * @return ReflectionFunctionAbstract
74
     * @throws ReflectionException
75
     */
76 7
    public function newMethodFromCallable(callable $callable): ReflectionFunctionAbstract
77
    {
78 7
        return match (gettype($callable)) {
79 6
            'array' => new ReflectionMethod(...$callable),
0 ignored issues
show
$callable is expanded, but the parameter $objectOrMethod of ReflectionMethod::__construct() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

79
            'array' => new ReflectionMethod(/** @scrutinizer ignore-type */ ...$callable),
Loading history...
80 2
            'object' => $callable instanceof Closure
81 2
                ? new ReflectionFunction($callable)
82 2
                : (new ReflectionClass($callable))->getMethod('__invoke'),
0 ignored issues
show
$callable of type callable is incompatible with the type object|string expected by parameter $objectOrClass of ReflectionClass::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

82
                : (new ReflectionClass(/** @scrutinizer ignore-type */ $callable))->getMethod('__invoke'),
Loading history...
83 7
            default => new ReflectionFunction($callable)
84
        };
85
    }
86
87 21
    protected function getFromParameterType(
88
        IContainer          $container,
89
        ReflectionParameter $parameter,
90
        mixed               $value): mixed
91
    {
92 21
        if (!$class = $parameter->getType()) {
93 2
            return $arguments[$parameter->getPosition()]
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $arguments seems to never exist and therefore isset should always be false.
Loading history...
94 2
                ?? $this->getFromParameter($container, $parameter, $value);
95
        }
96
        // Global parameter overriding / singleton instance?
97 21
        if (null !== $param = $this->getFromParameter($container, $parameter, $value)) {
98 11
            return $param;
99
        }
100 15
        if ($parameter->isDefaultValueAvailable()) {
101 3
            return $parameter->getDefaultValue();
102
        }
103 14
        return $container->new($class->getName());
0 ignored issues
show
The method getName() does not exist on ReflectionType. It seems like you code against a sub-type of ReflectionType such as ReflectionNamedType. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

103
        return $container->new($class->/** @scrutinizer ignore-call */ getName());
Loading history...
104
    }
105
106 21
    protected function getFromParameter(
107
        IContainer          $container,
108
        ReflectionParameter $parameter,
109
        mixed               $value): mixed
110
    {
111
        try {
112 21
            $type = ($parameter->getType() ?? $parameter)->getName();
113 1
        } catch (Error) {
114
            // i.e. for ReflectionUnionType, continue with processing
115 1
            return $value;
116
        }
117
118 21
        $type = $container->getBinding($type);
119 21
        if ($_ = $container->getFromStorage(DIStorage::EXCLUDE, $type)) {
0 ignored issues
show
The type Koded\DIStorage was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
120 1
            if (array_intersect($_, array_keys($container->getFromStorage(DIStorage::SINGLETONS)))) {
121 1
                return (clone $container)->new($type);
122
            }
123
        }
124 21
        if ($_ = $container->getFromStorage(DIStorage::SINGLETONS, $type)) {
125 2
            return $_;
126
        }
127 21
        if ($_ = $container->getFromStorage(DIStorage::NAMED, $parameter->name)) {
128 4
            return $_;
129
        }
130
131
        try {
132 19
            return $value ?? $parameter->getDefaultValue();
133 16
        } catch (ReflectionException $e) {
134 16
            if ($parameter->getType()?->isBuiltin()) {
135 1
                throw DIException::forMissingArgument($type, $parameter, $e);
136
            }
137
        }
138 15
        return null;
139
    }
140
}
141