Passed
Push — visitor ( 2d5566 )
by Akihito
11:02
created

Dependency::inject()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 17
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 17
c 0
b 0
f 0
rs 10
cc 4
nc 3
nop 1
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
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
            return;
117
        }
118
119
        $this->isSingleton = true;
120
    }
121
122
    /**
123
     * @param array<int,Pointcut> $pointcuts
124
     */
125
    public function weaveAspects(CompilerInterface $compiler, array $pointcuts): void
126
    {
127
        $class = (string) $this->newInstance;
128
        /**  @psalm-suppress RedundantConditionGivenDocblockType */
129
        assert(class_exists($class));
130
        if ((new ReflectionClass($class))->isFinal()) {
131
            return;
132
        }
133
134
        $isInterceptor = (new ReflectionClass($class))->implementsInterface(MethodInterceptor::class);
135
        $isWeaved = (new ReflectionClass($class))->implementsInterface(WeavedInterface::class);
136
        if ($isInterceptor || $isWeaved) {
137
            return;
138
        }
139
140
        $bind = new AopBind();
141
        $className = (string) $this->newInstance;
142
        $bind->bind($className, $pointcuts);
143
        if (! $bind->getBindings()) {
144
            return;
145
        }
146
147
        $class = $compiler->compile($className, $bind);
148
        /** @psalm-suppress ArgumentTypeCoercion */
149
        $this->newInstance->weaveAspects($class, $bind); // @phpstan-ignore-line
150
    }
151
152
    public function accept(VisitorInterface $visitor): string
153
    {
154
        return $visitor->visitDependency(
0 ignored issues
show
Bug introduced by
The method visitDependency() does not exist on Ray\Di\VisitorInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

154
        return $visitor->/** @scrutinizer ignore-call */ visitDependency(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
155
            $this->newInstance,
156
            $this->postConstruct,
157
            $this->isSingleton
158
        );
159
    }
160
161
    public function isSingleton(): bool
162
    {
163
        return $this->isSingleton;
164
    }
165
}
166