Test Failed
Pull Request — master (#37)
by Divine Niiquaye
13:18
created

Injectable::getResolved()   C

Complexity

Conditions 15
Paths 46

Size

Total Lines 62
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
eloc 31
c 0
b 0
f 0
nc 46
nop 3
dl 0
loc 62
rs 5.9166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of DivineNii opensource projects.
7
 *
8
 * PHP version 7.4 and above required
9
 *
10
 * @author    Divine Niiquaye Ibok <[email protected]>
11
 * @copyright 2021 DivineNii (https://divinenii.com/)
12
 * @license   https://opensource.org/licenses/BSD-3-Clause License
13
 *
14
 * For the full copyright and license information, please view the LICENSE
15
 * file that was distributed with this source code.
16
 */
17
18
namespace Rade\DI\Injector;
19
20
use Nette\Utils\Reflection;
21
use PhpParser\Builder\Method;
22
use PhpParser\BuilderFactory;
23
use PhpParser\Node\Expr\{Assign, Variable};
24
use Rade\DI\Attribute\Inject;
25
use Rade\DI\Exceptions\ContainerResolutionException;
26
use Rade\DI\Resolver;
27
28
/**
29
 * An injectable class used by service definitions.
30
 *
31
 * @internal This is almost a final class
32
 * @author Divine Niiquaye Ibok <[email protected]>
33
 */
34
class Injectable
35
{
36
    private object $service;
37
38
    /** @var array<int,array<string,mixed>> */
39
    private array $properties;
40
41
    /**
42
     * @param array<string,array<string,mixed[]>> $properties
43
     */
44
    public function __construct(object $service, array $properties)
45
    {
46
        $this->service = $service;
47
        $this->properties = $properties;
48
    }
49
50
    public function resolve(): object
51
    {
52
        foreach ($this->properties[0] ?? [] as $property => $propertyValue) {
53
            $this->service->{$property} = $propertyValue;
54
        }
55
56
        foreach ($this->properties[1] ?? [] as $method => $methodValue) {
57
            \call_user_func_array([$this->service, $method], (array) $methodValue);
58
        }
59
60
        return $this->service;
61
    }
62
63
    public function build(Method $definition, Variable $service, BuilderFactory $builder): Assign
64
    {
65
        $definition->addStmt($createdService = new Assign($service, $this->service));
66
67
        foreach ($this->properties[0] ?? [] as $property => $propertyValue) {
68
            $definition->addStmt(new Assign($builder->propertyFetch($service, $property), $builder->val($propertyValue)));
69
        }
70
71
        foreach ($this->properties[1] ?? [] as $method => $methodValue) {
72
            $definition->addStmt($builder->methodCall($service, $method, $methodValue));
73
        }
74
75
        return $createdService;
76
    }
77
78
    /**
79
     * Return the properties with the #[Inject] attribute.
80
     *
81
     * @return array<string,mixed>
82
     */
83
    public function getProperties(): array
84
    {
85
        return $this->properties[0] ?? [];
86
    }
87
88
    /**
89
     * Return the methods with the #[Inject] attribute.
90
     *
91
     * @return array<string,mixed>
92
     */
93
    public function getMethods(): array
94
    {
95
        return $this->properties[1] ?? [];
96
    }
97
98
    /**
99
     * Generates list of properties with #[Inject] attributes.
100
     *
101
     * @return array<string,array<string,mixed[]>>
102
     */
103
    public static function getResolved(Resolver $resolver, object $service, \ReflectionClass $reflection): object
104
    {
105
        if (\PHP_VERSION_ID < 80000) {
106
            return $service;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $service returns the type object which is incompatible with the documented return type array<string,array<string,array<mixed,mixed>>>.
Loading history...
107
        }
108
109
        $properties = [];
110
111
        foreach ($reflection->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
112
            if (empty($propertyAttributes = $property->getAttributes(Inject::class))) {
113
                continue;
114
            }
115
116
            if (\count($propertyAttributes) > 1) {
117
                throw new ContainerResolutionException(
118
                    \sprintf('Property "%s" has more than one #[Inject] attribute.', $property->getName())
119
                );
120
            }
121
122
            if (!empty($pValue = $propertyAttributes[0]->getArguments()[0] ?? null)) {
0 ignored issues
show
Unused Code introduced by
The assignment to $pValue is dead and can be removed.
Loading history...
123
                $properties[0][$property->getName()] = $propertyAttributes[0]->newInstance()->resolve($resolver);
124
                continue;
125
            }
126
127
            foreach (Resolver::getTypes($property) as $pType) {
128
                if (Reflection::isBuiltinType($pType)) {
129
                    continue;
130
                }
131
132
                if (!empty($pValue = $resolver->resolveReference('?' . $pType))) {
133
                    $properties[0][$property->getName()] = $pValue;
134
                }
135
            }
136
        }
137
138
        foreach ($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
139
            if (empty($methodAttributes = $method->getAttributes(Inject::class))) {
140
                continue;
141
            }
142
143
            if (\count($methodAttributes) > 1) {
144
                throw new ContainerResolutionException(
145
                    \sprintf('Method %s::%s has more than one #[Inject] attribute.', $reflection->getName(), $method->getName())
146
                );
147
            }
148
149
            if (!empty($methodAttributes[0]->getArguments())) {
150
                throw new ContainerResolutionException(\sprintf('Method with #[Inject] attribute does not support having arguments.'));
151
            }
152
153
            $properties[1][$method->getName()] = $resolver->autowireArguments($method);
154
        }
155
156
        if (!empty($properties)) {
157
            $service = new self($service, $properties);
158
159
            if (null === $resolver->getBuilder()) {
160
                $service = $service->resolve();
161
            }
162
        }
163
164
        return $service;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $service returns the type Rade\DI\Injector\Injectable|object which is incompatible with the documented return type array<string,array<string,array<mixed,mixed>>>.
Loading history...
165
    }
166
}
167