Passed
Push — main ( e04f1b...aea2b4 )
by Michael
03:28
created

HelpsProxies   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 158
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 20
Bugs 1 Features 0
Metric Value
eloc 37
c 20
b 1
f 0
dl 0
loc 158
ccs 42
cts 42
cp 1
rs 10
wmc 19

9 Methods

Rating   Name   Duplication   Size   Complexity  
A getContextualConcrete() 0 3 1
A convertToNamespace() 0 5 2
A throwPropertyNotFoundException() 0 6 1
A getClassForResolution() 0 5 3
A makeContainerParameters() 0 13 2
A getDependencies() 0 15 4
A getPassedParameters() 0 11 2
A resolvePassedClass() 0 6 1
A isOrderable() 0 3 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace MichaelRubel\EnhancedContainer\Traits;
6
7
use Illuminate\Support\Arr;
8
use JetBrains\PhpStorm\Pure;
9
use MichaelRubel\EnhancedContainer\Exceptions\PropertyNotFoundException;
10
11
trait HelpsProxies
12
{
13
    /**
14
     * @param string      $class
15
     * @param array       $dependencies
16
     * @param string|null $context
17
     *
18
     * @return object
19
     */
20 53
    public function resolvePassedClass(string $class, array $dependencies = [], ?string $context = null): object
21
    {
22 53
        $class        = $this->getClassForResolution($class, $context);
23 53
        $dependencies = $this->getDependencies($class, $dependencies);
24
25 53
        return resolve($class, $dependencies);
26
    }
27
28
    /**
29
     * Get the class for resolution.
30
     *
31
     * @param string      $class
32
     * @param string|null $context
33
     *
34
     * @return string
35
     */
36 53
    public function getClassForResolution(string $class, ?string $context = null): string
37
    {
38 53
        return ! is_null($context) && isset(app()->contextual[$context])
39 1
            ? $this->getContextualConcrete($class, $context)
40 53
            : $class;
41
    }
42
43
    /**
44
     * Try to get the contextual concrete.
45
     *
46
     * @param string      $class
47
     * @param string|null $context
48
     *
49
     * @return string
50
     */
51 1
    public function getContextualConcrete(string $class, ?string $context = null): string
52
    {
53 1
        return app()->contextual[$context][$class] ?? $class;
54
    }
55
56
    /**
57
     * Resolve class dependencies.
58
     *
59
     * @param string $class
60
     * @param array  $dependencies
61
     *
62
     * @return array
63
     * @throws \ReflectionException
64
     */
65 53
    public function getDependencies(string $class, array $dependencies = []): array
66
    {
67 53
        if (! empty($dependencies) && ! Arr::isAssoc($dependencies)) {
68
            /** @var class-string $class */
69 2
            $constructor = (new \ReflectionClass($class))->getConstructor();
70
71 2
            if ($constructor) {
72 2
                $dependencies = $this->makeContainerParameters(
73 2
                    $constructor->getParameters(),
74
                    $dependencies
75
                );
76
            }
77
        }
78
79 53
        return $dependencies;
80
    }
81
82
    /**
83
     * @param object $class
84
     * @param string $method
85
     * @param array  $parameters
86
     *
87
     * @return array
88
     * @throws \ReflectionException
89
     */
90 50
    public function getPassedParameters(object $class, string $method, array $parameters): array
91
    {
92 50
        if (empty($parameters)) {
93 25
            return $parameters;
94
        }
95
96 28
        $reflectionMethod = new \ReflectionMethod($class, $method);
97
98 26
        return $this->makeContainerParameters(
99 26
            $reflectionMethod->getParameters(),
100
            $parameters
101
        );
102
    }
103
104
    /**
105
     * Combine parameters to make it container-readable.
106
     *
107
     * @param array $reflectionParameters
108
     * @param array $methodParameters
109
     *
110
     * @return array
111
     */
112 27
    public function makeContainerParameters(array $reflectionParameters, array $methodParameters): array
113
    {
114 27
        $base = current($methodParameters);
115
116 27
        if ($this->isOrderable($base, $reflectionParameters, $methodParameters)) {
117 1
            return $base;
118
        }
119
120 27
        return collect($reflectionParameters)
121 27
            ->slice(0, count($methodParameters))
122 27
            ->map(fn ($param) => $param->getName())
123 27
            ->combine(array_slice($methodParameters, 0, count($reflectionParameters)))
124 27
            ->all();
125
    }
126
127
    /**
128
     * Determine if the container can handle parameter order.
129
     *
130
     * @param array $base
131
     * @param array $reflectionParameters
132
     * @param array $methodParameters
133
     *
134
     * @return bool
135
     */
136 27
    public function isOrderable(mixed $base, array $reflectionParameters, array $methodParameters): bool
137
    {
138 27
        return is_array($base) && Arr::isAssoc($base) && single($methodParameters) <=> single($reflectionParameters);
139
    }
140
141
    /**
142
     * Convert the object to its namespace.
143
     *
144
     * @param object|string $object
145
     *
146
     * @return string
147
     */
148 49
    public function convertToNamespace(object|string $object): string
149
    {
150 49
        return is_string($object)
151 42
            ? $object
152 49
            : $object::class;
153
    }
154
155
    /**
156
     * Handle property error.
157
     *
158
     * @param string $name
159
     * @param object $instance
160
     *
161
     * @throws PropertyNotFoundException
162
     */
163 3
    public function throwPropertyNotFoundException(string $name, object $instance): self
164
    {
165 3
        throw new PropertyNotFoundException(sprintf(
166 3
            'Call to undefined property %s::%s()',
167
            $name,
168 3
            $instance::class
169
        ));
170
    }
171
}
172