Completed
Push — refactor ( 1ac315 )
by Akihito
05:56
created

DependencyCode   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 154
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 17

Importance

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

10 Methods

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