Test Failed
Pull Request — master (#37)
by Divine Niiquaye
03:24
created

Injectable   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 119
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 43
c 0
b 0
f 0
dl 0
loc 119
rs 10
wmc 22

6 Methods

Rating   Name   Duplication   Size   Complexity  
A build() 0 13 3
A resolve() 0 11 3
A __construct() 0 4 1
A getMethods() 0 3 1
A getProperties() 0 3 1
C getResolved() 0 50 13
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 (!empty($pValue = $propertyAttributes[0]->getArguments()[0] ?? null)) {
117
                $properties[0][$property->getName()] = $resolver->resolveReference($pValue);
118
                continue;
119
            }
120
121
            foreach (Resolver::getTypes($property) as $pType) {
122
                if (Reflection::isBuiltinType($pType)) {
123
                    continue;
124
                }
125
126
                if (!empty($pValue = $resolver->resolveReference('?' . $pType))) {
127
                    $properties[0][$property->getName()] = $pValue;
128
                }
129
            }
130
        }
131
132
        foreach ($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
133
            if (empty($methodAttributes = $method->getAttributes(Inject::class))) {
134
                continue;
135
            }
136
137
            if (!empty($methodAttributes[0]->getArguments())) {
138
                throw new ContainerResolutionException(\sprintf('Method with Inject attributes does not support having arguments.'));
139
            }
140
141
            $properties[1][$method->getName()] = $resolver->autowireArguments($method);
142
        }
143
144
        if (!empty($properties)) {
145
            $service = new self($service, $properties);
146
147
            if (null === $resolver->getBuilder()) {
148
                $service = $service->resolve();
149
            }
150
        }
151
152
        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...
153
    }
154
}
155