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

Injectable   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 110
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 23
eloc 46
dl 0
loc 110
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A resolve() 0 11 3
A __construct() 0 4 1
A build() 0 13 3
B doProperties() 0 27 8
B getProperties() 0 32 8
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, Type};
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\Definitions\Reference;
26
use Rade\DI\Exceptions\ContainerResolutionException;
27
use Rade\DI\Resolver;
28
29
/**
30
 * An injectable class used by service definitions.
31
 *
32
 * @internal This is almost a final class
33
 * @author Divine Niiquaye Ibok <[email protected]>
34
 */
35
class Injectable
36
{
37
    private object $service;
38
39
    /** @var array<int,array<string,mixed>> */
40
    private array $properties;
41
42
    /**
43
     * @param array<string,array<string,mixed[]>> $properties
44
     */
45
    public function __construct(object $service, array $properties)
46
    {
47
        $this->service = $service;
48
        $this->properties = $properties;
49
    }
50
51
    public function resolve(): object
52
    {
53
        foreach ($this->properties[0] ?? [] as $property => $propertyValue) {
54
            $this->service->{$property} = $propertyValue;
55
        }
56
57
        foreach ($this->properties[1] ?? [] as $method => $methodValue) {
58
            \call_user_func_array([$this->service, $method], (array) $methodValue);
59
        }
60
61
        return $this->service;
62
    }
63
64
    public function build(Method $definition, Variable $service, BuilderFactory $builder): Assign
65
    {
66
        $definition->addStmt($createdService = new Assign($service, $this->service));
67
68
        foreach ($this->properties[0] ?? [] as $property => $propertyValue) {
69
            $definition->addStmt(new Assign($builder->propertyFetch($service, $property), $builder->val($propertyValue)));
70
        }
71
72
        foreach ($this->properties[1] ?? [] as $method => $methodValue) {
73
            $definition->addStmt($builder->methodCall($service, $method, $methodValue));
74
        }
75
76
        return $createdService;
77
    }
78
79
    /**
80
     * Generates list of properties with #[Inject] attributes.
81
     *
82
     * @return array<string,array<string,mixed[]>>
83
     */
84
    public static function getProperties(Resolver $resolver, object $service, \ReflectionClass $reflection): object
85
    {
86
        if (\PHP_VERSION_ID < 80000) {
87
            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...
88
        }
89
90
        $properties = [];
91
        self::doProperties($reflection, $resolver, $properties);
92
93
        foreach ($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
94
            if ([] === $methodAttributes = $method->getAttributes(Inject::class)) {
95
                continue;
96
            }
97
98
            if (null === $methodAttribute = $methodAttributes[0] ?? null) {
99
                continue;
100
            }
101
102
            if ([] !== $methodAttribute->getArguments()) {
103
                throw new ContainerResolutionException(\sprintf('Method with Inject attributes does not support having arguments.'));
104
            }
105
106
            $properties[1][$method->getName()] = $resolver->autowireArguments($method);
107
        }
108
109
        if ([] === $properties) {
110
            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...
111
        }
112
113
        $injected = new self($service, $properties);
114
115
        return null === $resolver->getBuilder() ? $injected->resolve() : $injected;
0 ignored issues
show
Bug Best Practice introduced by
The expression return null === $resolve...->resolve() : $injected 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...
116
    }
117
118
    private static function doProperties(\ReflectionClass $reflection, Resolver $resolver, array &$properties): void
119
    {
120
        foreach ($reflection->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
121
            if ([] === $propertyAttributes = $property->getAttributes(Inject::class)) {
122
                continue;
123
            }
124
125
            if (null === $propertyAttribute = $propertyAttributes[0] ?? null) {
126
                continue;
127
            }
128
129
            if ([] !== $pValue = $propertyAttribute->getArguments()) {
130
                $properties[0][$property->getName()] = $resolver->resolve(new Reference($pValue[0]));
131
132
                continue;
133
            }
134
135
            if (null === $pTypes = Type::fromReflection($property)) {
136
                continue;
137
            }
138
139
            foreach ($pTypes->getNames() as $pType) {
140
                if (Reflection::isBuiltinType($pType)) {
141
                    continue;
142
                }
143
144
                $properties[0][$property->getName()] = $resolver->resolve(new Reference($pType));
145
            }
146
        }
147
    }
148
}
149