Completed
Pull Request — 2.x (#216)
by Akihito
09:22
created

DependencyCode   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 175
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 17

Importance

Changes 0
Metric Value
wmc 15
lcom 1
cbo 17
dl 0
loc 175
c 0
b 0
f 0
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A getCode() 0 14 4
A setContext() 0 4 1
A setQaulifier() 0 4 1
A getIsSingletonCode() 0 6 2
A getInstanceCode() 0 6 1
A getDependencyCode() 0 15 1
A getProviderCode() 0 18 2
A getSetContextCode() 0 6 1
A getFactoryNode() 0 12 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ray\Compiler;
6
7
use PhpParser\BuilderFactory;
8
use PhpParser\Node;
9
use PhpParser\Node\Expr;
10
use PhpParser\Node\Expr\MethodCall;
11
use PhpParser\Node\Stmt;
12
use Ray\Di\Container;
13
use Ray\Di\Dependency;
14
use Ray\Di\DependencyInterface;
15
use Ray\Di\DependencyProvider;
16
use Ray\Di\Instance;
17
use Ray\Di\SetContextInterface;
18
19
final class DependencyCode implements SetContextInterface
20
{
21
    /**
22
     * @var \PhpParser\BuilderFactory
23
     */
24
    private $factory;
25
26
    /**
27
     * @var Container
28
     */
29
    private $container;
30
31
    /**
32
     * @var null|ScriptInjector
33
     */
34
    private $injector;
35
36
    /**
37
     * @var Normalizer
38
     */
39
    private $normalizer;
40
41
    /**
42
     * @var FactoryCode
43
     */
44
    private $factoryCompiler;
45
46
    /**
47
     * @var PrivateProperty
48
     */
49
    private $privateProperty;
50
51
    /**
52
     * @var null|IpQualifier
53
     */
54
    private $qualifier;
55
56
    /**
57
     * @var string
58
     * @psalm-suppress PropertyNotSetInConstructor
59
     */
60
    private $context;
61
62
    /**
63
     * @var AopCode
64
     */
65
    private $aopCode;
66
67
    public function __construct(Container $container, ScriptInjector $injector = null)
68
    {
69
        $this->factory = new BuilderFactory;
70
        $this->container = $container;
71
        $this->normalizer = new Normalizer;
72
        $this->injector = $injector;
73
        $this->factoryCompiler = new FactoryCode($container, new Normalizer, $this, $injector);
74
        $this->privateProperty = new PrivateProperty;
75
        $this->aopCode = new AopCode($this->privateProperty);
76
    }
77
78
    /**
79
     * Return compiled dependency code
80
     */
81
    public function getCode(DependencyInterface $dependency) : Code
82
    {
83
        if ($dependency instanceof Dependency) {
84
            return $this->getDependencyCode($dependency);
85
        }
86
        if ($dependency instanceof Instance) {
87
            return $this->getInstanceCode($dependency);
88
        }
89
        if ($dependency instanceof DependencyProvider) {
90
            return $this->getProviderCode($dependency);
91
        }
92
93
        throw new \DomainException(\get_class($dependency));
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99
    public function setContext($context)
100
    {
101
        $this->context = $context;
102
    }
103
104
    public function setQaulifier(IpQualifier $qualifer) : void
105
    {
106
        $this->qualifier = $qualifer;
107
    }
108
109
    public function getIsSingletonCode(bool $isSingleton) : Expr\Assign
110
    {
111
        $bool = new Expr\ConstFetch(new Node\Name([$isSingleton ? 'true' : 'false']));
112
113
        return new Expr\Assign(new Expr\Variable('is_singleton'), $bool);
114
    }
115
116
    /**
117
     * Compile DependencyInstance
118
     */
119
    private function getInstanceCode(Instance $instance) : Code
120
    {
121
        $node = ($this->normalizer)($instance->value);
122
123
        return new Code(new Node\Stmt\Return_($node), false);
124
    }
125
126
    /**
127
     * Compile generic object dependency
128
     */
129
    private function getDependencyCode(Dependency $dependency) : Code
130
    {
131
        $prop = $this->privateProperty;
132
        $node = $this->getFactoryNode($dependency);
133
        ($this->aopCode)($dependency, $node);
134
        $isSingleton = $prop($dependency, 'isSingleton');
135
        $node[] = $this->getIsSingletonCode($isSingleton);
136
        $node[] = new Node\Stmt\Return_(new Node\Expr\Variable('instance'));
137
        /** @var Stmt\Namespace_ $namespace */
138
        $namespace = $this->factory->namespace('Ray\Di\Compiler')->addStmts($node)->getNode();
139
        $qualifer = $this->qualifier;
140
        $this->qualifier = null;
141
142
        return new Code($namespace, $isSingleton, $qualifer);
143
    }
144
145
    /**
146
     * Compile dependency provider
147
     */
148
    private function getProviderCode(DependencyProvider $provider) : Code
149
    {
150
        $prop = $this->privateProperty;
151
        $dependency = $prop($provider, 'dependency');
152
        $node = $this->getFactoryNode($dependency);
153
        $provider->setContext($this);
154
        if ($this->context) {
155
            $node[] = $this->getSetContextCode($this->context); // $instance->setContext($this->context);
156
        }
157
        $isSingleton = $prop($provider, 'isSingleton');
158
        $node[] = $this->getIsSingletonCode($isSingleton);
159
        $node[] = new Stmt\Return_(new MethodCall(new Expr\Variable('instance'), 'get'));
160
        $node = $this->factory->namespace('Ray\Di\Compiler')->addStmts($node)->getNode();
161
        $qualifer = $this->qualifier;
162
        $this->qualifier = null;
163
164
        return new Code($node, $isSingleton, $qualifer);
165
    }
166
167
    private function getSetContextCode(string $context) : MethodCall
168
    {
169
        $arg = new Node\Arg(new Node\Scalar\String_($context));
170
171
        return new MethodCall(new Expr\Variable('instance'), 'setContext', [$arg]);
172
    }
173
174
    /**
175
     * Return generic factory code
176
     *
177
     * This code is used by Dependency and DependencyProvider
178
     *
179
     * @return array<Expr>
180
     */
181
    private function getFactoryNode(DependencyInterface $dependency) : array
182
    {
183
        $prop = $this->privateProperty;
184
        $newInstance = $prop($dependency, 'newInstance');
185
        // class name
186
        $class = $prop($newInstance, 'class');
187
        $setterMethods = (array) $prop($prop($newInstance, 'setterMethods'), 'setterMethods');
188
        $arguments = (array) $prop($prop($newInstance, 'arguments'), 'arguments');
189
        $postConstruct = (string) $prop($dependency, 'postConstruct');
190
191
        return $this->factoryCompiler->getFactoryCode($class, $arguments, $setterMethods, $postConstruct);
192
    }
193
}
194