Dependency::weaveAspects()   A
last analyzed

Complexity

Conditions 5
Paths 4

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 15
c 1
b 0
f 0
dl 0
loc 25
rs 9.4555
cc 5
nc 4
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ray\Di;
6
7
use Ray\Aop\Bind as AopBind;
8
use Ray\Aop\CompilerInterface;
9
use Ray\Aop\MethodInterceptor;
10
use Ray\Aop\Pointcut;
11
use Ray\Aop\WeavedInterface;
12
use ReflectionClass;
13
use ReflectionMethod;
14
15
use function assert;
16
use function class_exists;
17
use function method_exists;
18
use function sprintf;
19
20
final class Dependency implements DependencyInterface, AcceptInterface
21
{
22
    /** @var NewInstance */
23
    private $newInstance;
24
25
    /** @var ?string */
26
    private $postConstruct;
27
28
    /** @var bool */
29
    private $isSingleton = false;
30
31
    /** @var ?mixed */
32
    private $instance;
33
34
    public function __construct(NewInstance $newInstance, ?ReflectionMethod $postConstruct = null)
35
    {
36
        $this->newInstance = $newInstance;
37
        $this->postConstruct = $postConstruct->name ?? null;
38
    }
39
40
    /**
41
     * @return array<string>
42
     */
43
    public function __sleep()
44
    {
45
        return ['newInstance', 'postConstruct', 'isSingleton'];
46
    }
47
48
    public function __toString(): string
49
    {
50
        return sprintf(
51
            '(dependency) %s',
52
            (string) $this->newInstance
53
        );
54
    }
55
56
    /**
57
     * {@inheritdoc}
58
     */
59
    public function register(array &$container, Bind $bind): void
60
    {
61
        $container[(string) $bind] = $bind->getBound();
62
    }
63
64
    /**
65
     * {@inheritdoc}
66
     */
67
    public function inject(Container $container)
68
    {
69
        // singleton ?
70
        if ($this->isSingleton === true && $this->instance) {
71
            return $this->instance;
72
        }
73
74
        // create dependency injected instance
75
        $this->instance = ($this->newInstance)($container);
76
77
        // @PostConstruct
78
        if ($this->postConstruct) {
79
            assert(method_exists($this->instance, $this->postConstruct));
80
            $this->instance->{$this->postConstruct}();
81
        }
82
83
        return $this->instance;
84
    }
85
86
    /**
87
     * @param array<int, mixed> $params
88
     *
89
     * @return mixed
90
     */
91
    public function injectWithArgs(Container $container, array $params)
92
    {
93
        // singleton ?
94
        if ($this->isSingleton === true && $this->instance) {
95
            return $this->instance;
96
        }
97
98
        // create dependency injected instance
99
        $this->instance = $this->newInstance->newInstanceArgs($container, $params);
100
101
        // @PostConstruct
102
        if ($this->postConstruct) {
103
            assert(method_exists($this->instance, $this->postConstruct));
104
            $this->instance->{$this->postConstruct}();
105
        }
106
107
        return $this->instance;
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113
    public function setScope($scope): void
114
    {
115
        if ($scope === Scope::SINGLETON) {
116
            $this->isSingleton = true;
117
        }
118
    }
119
120
    /**
121
     * @param array<int,Pointcut> $pointcuts
122
     */
123
    public function weaveAspects(CompilerInterface $compiler, array $pointcuts): void
124
    {
125
        $class = (string) $this->newInstance;
126
        /**  @psalm-suppress RedundantConditionGivenDocblockType */
127
        assert(class_exists($class));
128
        if ((new ReflectionClass($class))->isFinal()) {
129
            return;
130
        }
131
132
        $isInterceptor = (new ReflectionClass($class))->implementsInterface(MethodInterceptor::class);
133
        $isWeaved = (new ReflectionClass($class))->implementsInterface(WeavedInterface::class);
134
        if ($isInterceptor || $isWeaved) {
135
            return;
136
        }
137
138
        $bind = new AopBind();
139
        $className = (string) $this->newInstance;
140
        $bind->bind($className, $pointcuts);
141
        if (! $bind->getBindings()) {
142
            return;
143
        }
144
145
        $class = $compiler->compile($className, $bind);
146
        /** @psalm-suppress ArgumentTypeCoercion */
147
        $this->newInstance->weaveAspects($class, $bind); // @phpstan-ignore-line
148
    }
149
150
    /** @inheritDoc */
151
    public function accept(VisitorInterface $visitor)
152
    {
153
        return $visitor->visitDependency(
154
            $this->newInstance,
155
            $this->postConstruct,
156
            $this->isSingleton
157
        );
158
    }
159
160
    public function isSingleton(): bool
161
    {
162
        return $this->isSingleton;
163
    }
164
}
165