Completed
Push — master ( d10f54...00ced4 )
by
unknown
05:32
created

DefinitionCompiler::getClassDependencies()   B

Complexity

Conditions 7
Paths 8

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 7.551
c 0
b 0
f 0
ccs 10
cts 10
cp 1
cc 7
eloc 10
nc 8
nop 1
crap 7
1
<?php declare(strict_types = 1);
2
/**
3
 * Created by PhpStorm.
4
 * User: root
5
 * Date: 02.08.16
6
 * Time: 0:46.
7
 */
8
namespace samsonframework\container\definition\builder;
9
10
use samsonframework\container\ContainerInterface;
11
use samsonframework\container\definition\analyzer\DefinitionAnalyzer;
12
use samsonframework\container\definition\analyzer\exception\ParameterNotFoundException;
13
use samsonframework\container\definition\analyzer\exception\WrongAnalyzerTypeException;
14
use samsonframework\container\definition\exception\ClassDefinitionAlreadyExistsException;
15
use samsonframework\container\definition\builder\exception\ReferenceNotImplementsException;
16
use samsonframework\container\definition\reference\ClassReference;
17
use samsonframework\container\definition\reference\ReferenceInterface;
18
use samsonframework\di\Container;
19
20
/**
21
 * Class DefinitionCompiler
22
 *
23
 * @author Ruslan Molodyko <[email protected]>
24
 */
25
class DefinitionCompiler
26
{
27
    /** @var DefinitionGenerator */
28
    protected $generator;
29
30
    /** @var DefinitionAnalyzer */
31
    protected $analyzer;
32
33
    /** @var array All registered dependencies*/
34
    protected $dependencies = [];
35
36
    /**
37
     * DefinitionCompiler constructor.
38
     *
39
     * @param DefinitionGenerator $generator
40
     * @param DefinitionAnalyzer $analyzer
41
     */
42 4
    public function __construct(DefinitionGenerator $generator, DefinitionAnalyzer $analyzer)
43
    {
44 4
        $this->generator = $generator;
45 4
        $this->analyzer = $analyzer;
46 4
    }
47
48
    /**
49
     * Compile and get container
50
     *
51
     * @param DefinitionBuilder $definitionBuilder
52
     * @param $containerName
53
     * @param $namespace
54
     * @param $containerDir
55
     * @return ContainerInterface
56
     * @throws WrongAnalyzerTypeException
57
     * @throws ParameterNotFoundException
58
     * @throws ClassDefinitionAlreadyExistsException
59
     * @throws ReferenceNotImplementsException
60
     * @throws \InvalidArgumentException
61
     */
62 2
    public function compile(DefinitionBuilder $definitionBuilder, $containerName, $namespace, $containerDir)
63
    {
64 2
        $this->generator->getClassGenerator()
65 2
            ->defName($containerName)
66 2
            ->defExtends('BaseContainer')
67 2
            ->defNamespace($namespace)
68 2
            ->defUse(Container::class, 'BaseContainer');
69
70
        // Max count analyzer iterations
71 2
        $count = 10;
72
73
        /**
74
         * Analyze builder metadata
75
         *
76
         * 1. Analyze definitions
77
         * 2. Add dependencies by analyzers
78
         * 3. Append missing definitions for dependencies
79
         * ... Analyze again
80
         */
81 2
        while ($this->analyzer->analyze($definitionBuilder)) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
82
83
            // Get dependencies
84 2
            $dependencies = $this->getClassDependencies($definitionBuilder);
85
            // Generate metadata
86 2
            $this->generateDefinitions($definitionBuilder, $dependencies);
87
88
            // Wrong behavior
89 2
            if ($count === 0) {
90
                throw new \InvalidArgumentException('Wrong analyze');
91
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
92
            }
93 2
            $count--;
94
        }
95
96
        // Get container code
97 2
        $code = $this->generator->generateClass($definitionBuilder);
98
        // Get file path
99 2
        $containerFilePath = rtrim($containerDir, '/') . '/' . $containerName . '.php';
100
        // Save file
101 2
        file_put_contents($containerFilePath, $code);
102
        // Get container class name
103
        $className = $namespace . '\\' . $containerName;
104
        // Require container
105
        require_once($containerFilePath);
106
        // Instantiate container
107
        return new $className();
108
    }
109
110
    /**
111
     * Get class dependencies form definition builder
112
     *
113
     * @param DefinitionBuilder $definitionBuilder
114
     * @return array
115
     */
116 4
    protected function getClassDependencies(DefinitionBuilder $definitionBuilder): array
117
    {
118 4
        $dependencyList = [];
119
        // Get dependencies which will be used for generation definitions
120 4
        foreach ($definitionBuilder->getDefinitionCollection() as $classDefinition) {
121
            // When this class definition did not analyzed
122 4
            if (true || !$classDefinition->isAnalyzed()) {
123
                // Iterate properties and get their dependencies
124 4
                foreach ($classDefinition->getPropertiesCollection() as $propertyDefinition) {
125
                    // Add dependency to list if valid
126 2
                    $this->addDependency($dependencyList, $propertyDefinition->getDependency());
127
                }
128 4
                foreach ($classDefinition->getMethodsCollection() as $methodDefinition) {
129 4
                    foreach ($methodDefinition->getParametersCollection() as $parameterDefinition) {
130 4
                        $this->addDependency($dependencyList, $parameterDefinition->getDependency());
131
                    }
132
                }
133
            }
134
        }
135 4
        return $dependencyList;
136
    }
137
138
    /**
139
     * Generate definitions from dependencies for builder
140
     *
141
     * @param DefinitionBuilder $definitionBuilder
142
     * @param array $dependencyList
143
     * @throws ClassDefinitionAlreadyExistsException
144
     */
145 3
    protected function generateDefinitions(
146
        DefinitionBuilder $definitionBuilder,
147
        array $dependencyList
148
    ) {
149
        // Iterate all classes and auto generate definition for missing
150 3
        foreach ($dependencyList as $className => $classReference) {
151 3
            if (!$definitionBuilder->hasDefinition($className)) {
152 3
                $definitionBuilder->addDefinition($className);
153
            }
154
        }
155 3
    }
156
157
    /**
158
     * Add dependencies which then will be use for automatic creation the definitions
159
     *
160
     * @param array $dependencyList
161
     * @param ReferenceInterface $reference
162
     * @return array
163
     */
164 4
    protected function addDependency(array &$dependencyList, ReferenceInterface $reference)
165
    {
166
        // Add class dependency to list
167 4
        if ($reference instanceof ClassReference) {
168 4
            $dependencyList[$reference->getClassName()] = $reference;
169
        }
170 4
    }
171
}
172