Completed
Pull Request — master (#14)
by
unknown
08:12
created

StructureResolver::resolveConstructorToRender()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 32
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 32
rs 8.439
c 0
b 0
f 0
cc 5
eloc 19
nc 6
nop 3
1
<?php
2
3
namespace SimpleEntityGeneratorBundle\Lib;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
use Exception;
7
use SimpleEntityGeneratorBundle\Lib\Exceptions\StructureResolverException;
8
use SimpleEntityGeneratorBundle\Lib\Interfaces\DumpableInterface;
9
use SimpleEntityGeneratorBundle\Lib\Interfaces\MethodInterface;
10
use SimpleEntityGeneratorBundle\Lib\Interfaces\StructureWithMethodsInterface;
11
use SimpleEntityGeneratorBundle\Lib\Items\ClassConstructorManager;
12
use SimpleEntityGeneratorBundle\Lib\Items\ClassManager;
13
use SimpleEntityGeneratorBundle\Lib\Items\InterfaceManager;
14
use SimpleEntityGeneratorBundle\Lib\Items\TestClassManager;
15
use ReflectionClass;
16
use SimpleEntityGeneratorBundle\Lib\Exceptions\UnrecognizedItemToDumpException;
17
use ReflectionMethod;
18
19
/**
20
 * Resolve Classes content elements
21
 *
22
 * @author Sławomir Kania <[email protected]>
23
 */
24
class StructureResolver
25
{
26
    /**
27
     * @var Renderer
28
     */
29
    private $renderer;
30
31
    /**
32
     * CONSTR
33
     *
34
     * @param Renderer $renderer
35
     */
36
    public function __construct(Renderer $renderer)
37
    {
38
        $this->renderer = $renderer;
39
    }
40
41
    /**
42
     * @param string $content
43
     * @param DumpableInterface $item
44
     * @param ReflectionClass $reflectionClass
45
     * @return string
46
     * @throws UnrecognizedItemToDumpException
47
     */
48
    public function getUpdatedItemSourceContent($content, DumpableInterface $item, ReflectionClass $reflectionClass)
49
    {
50
        switch (true) {
51
            case $item instanceof ClassManager:
52
                return $this->getUpdatedClassContent($content, $item, $reflectionClass);
53
            case $item instanceof InterfaceManager:
54
                return $this->getUpdatedInterfaceContent($content, $item, $reflectionClass);
55
            case $item instanceof TestClassManager:
56
                return $this->getUpdatedTestClassContent($content, $item, $reflectionClass);
57
            default:
58
                throw $this->getExceptionUnrecognizedItem($item);
59
        }
60
    }
61
62
    /**
63
     * @param string $content
64
     * @param TestClassManager $item
65
     * @param ReflectionClass $reflectionClass
66
     * @return string
67
     */
68
    protected function getUpdatedTestClassContent($content, TestClassManager $item, ReflectionClass $reflectionClass)
69
    {
70
        $item->setMethods($this->resolveMethodsToRender($item, $reflectionClass));
71
        return $this->getRenderer()->renderAndPutItemsToContent($content, $item->getMethods(), $this->getNewMethodPostion($reflectionClass));
72
    }
73
74
    /**
75
     * @param string $content
76
     * @param ClassManager $item
77
     * @param ReflectionClass $reflectionClass
78
     * @return string
79
     */
80
    protected function getUpdatedClassContent($content, ClassManager $item, ReflectionClass $reflectionClass)
81
    {
82
        $this->resolveClassContent($content, $item, $reflectionClass);
83
        $updatedContent = $this->getRenderer()->renderAndPutItemsToContent($content, $item->getMethods(), $this->getNewMethodPostion($reflectionClass));
84
        $updatedContent = $this->getRenderer()->renderAndPutConstructorBodyToContent($updatedContent, $item->getConstructor(), $this->getNewInitPropertyPosition($reflectionClass));
85
        $updatedContent = $this->getRenderer()->renderAndPutItemsToContent($updatedContent, $item->getProperties(), $this->getNewPropertyPosition($reflectionClass));
86
        return $updatedContent;
87
    }
88
89
    /**
90
     * @param string $content
91
     * @param InterfaceManager $item
92
     * @param ReflectionClass $reflectionClass
93
     * @return string
94
     */
95
    protected function getUpdatedInterfaceContent($content, InterfaceManager $item, ReflectionClass $reflectionClass)
96
    {
97
        $this->resolveInterfaceContent($item, $reflectionClass);
98
        $updatedContent = $this->getRenderer()->renderAndPutItemsToContent($content, $item->getMethods(), $this->getNewMethodPostion($reflectionClass));
99
        return $updatedContent;
100
    }
101
102
    /**
103
     * @param string $content
104
     * @param ClassManager $item
105
     * @param ReflectionClass $reflectionClass
106
     * @return ClassManager
107
     */
108
    protected function resolveClassContent($content, ClassManager $item, ReflectionClass $reflectionClass)
109
    {
110
        $item->setConstructor($this->resolveConstructorToRender($content, $item, $reflectionClass));
111
        $item->setProperties($this->resolvePropertiesToRender($item, $reflectionClass));
112
        $item->setMethods($this->resolveMethodsToRender($item, $reflectionClass));
113
        return $item;
114
    }
115
116
    /**
117
     * @param InterfaceManager $item
118
     * @param ReflectionClass $reflectionClass
119
     * @return InterfaceManager
120
     */
121
    protected function resolveInterfaceContent(InterfaceManager $item, ReflectionClass $reflectionClass)
122
    {
123
        $item->setMethods($this->resolveMethodsToRender($item, $reflectionClass));
124
        return $item;
125
    }
126
127
    /**
128
     * Get properties to render in item file
129
     *
130
     * @param ClassManager $item
131
     * @param ReflectionClass $reflectionClass
132
     * @return ArrayCollection
133
     */
134
    protected function resolvePropertiesToRender(ClassManager $item, ReflectionClass $reflectionClass)
135
    {
136
        $propertiesToRender = new ArrayCollection();
137
        foreach ($item->getProperties() as $itemProperty) {
138
            $found = false;
139
            foreach ($reflectionClass->getProperties() as $reflectionProperty) {
140
                if ($itemProperty->getPreparedName() == $reflectionProperty->name) {
141
                    $found = true;
142
                    break;
143
                }
144
            }
145
            if (false === $found) {
146
                $propertiesToRender->add($itemProperty);
147
            }
148
        }
149
150
        return $propertiesToRender;
151
    }
152
153
    /**
154
     * Get methods to render in item file
155
     *
156
     * @param StructureWithMethodsInterface $item
157
     * @param ReflectionClass $reflectionClass
158
     * @return ArrayCollection
159
     * @throws Exception
160
     */
161
    protected function resolveMethodsToRender(StructureWithMethodsInterface $item, ReflectionClass $reflectionClass)
162
    {
163
        $methodsToRender = new ArrayCollection();
164
        foreach ($item->getMethods() as $itemMethod) {
165
            if (false === ($itemMethod instanceof MethodInterface)) {
166
                throw new StructureResolverException(sprintf("Item %s does not implement Method Interface", get_class($itemMethod)));
167
            }
168
169
            $found = false;
170
            foreach ($reflectionClass->getMethods() as $reflectionMethod) {
171
                if ($itemMethod->getPreparedName() == $reflectionMethod->name) {
172
                    $found = true;
173
                    break;
174
                }
175
            }
176
            if (false === $found) {
177
                $methodsToRender->add($itemMethod);
178
            }
179
        }
180
181
        return $methodsToRender;
182
    }
183
184
    /**
185
     * @param string $content
186
     * @param ClassManager $item
187
     * @param ReflectionClass $reflectionClass
188
     * @return ClassConstructorManager
189
     */
190
    protected function resolveConstructorToRender($content, ClassManager $item, ReflectionClass $reflectionClass)
191
    {
192
        $constructor = $item->getConstructor();
193
        $constructorFromFile = $reflectionClass->getConstructor();
194
        $source = Tools::explodeTemplateStringToArray($content);
195
196
        $constructorBody = implode("", array_slice($source, $constructorFromFile->getStartLine(), $constructorFromFile->getEndLine() - $constructorFromFile->getStartLine()));
197
198
        $matches = [];
199
        preg_match_all("/this->[\S]+/", $constructorBody, $matches);
200
201
        $initPropertiesFromConstructor = [];
202
        if (count($matches) > 0) {
203
            foreach ($matches[0] as $match) {
204
                $initPropertiesFromConstructor[] = str_replace("this->", "", trim($match));
205
            }
206
        }
207
208
        $newInitProperties = new ArrayCollection();
209
        foreach ($constructor->getInitProperties() as $initProperty) {
210
            if (in_array($initProperty->getProperty()->getPreparedName(), $initPropertiesFromConstructor)) {
211
                continue;
212
            }
213
214
            $newInitProperties->add($initProperty);
215
        }
216
217
        $constructor->setInitProperties($newInitProperties);
218
        $item->setConstructor($constructor);
219
220
        return $constructor;
221
    }
222
223
    /**
224
     * @param ReflectionClass $reflectionClass
225
     * @return integer
226
     */
227
    protected function getNewMethodPostion(ReflectionClass $reflectionClass)
228
    {
229
        return $reflectionClass->getEndLine() - 1;
230
    }
231
232
    /**
233
     * @param ReflectionClass $reflectionClass
234
     * @return int
235
     */
236
    protected function getNewInitPropertyPosition(ReflectionClass $reflectionClass)
237
    {
238
        $constructor = $reflectionClass->getConstructor();
239
240
        if ($constructor instanceof ReflectionMethod) {
241
            return $constructor->getEndLine() - 1;
242
        }
243
244
        return $reflectionClass->getEndLine() - 1;
245
    }
246
247
    /**
248
     * @param ReflectionClass $reflectionClass
249
     * @return int
250
     */
251
    protected function getNewPropertyPosition(ReflectionClass $reflectionClass)
252
    {
253
        $constructor = $reflectionClass->getConstructor();
254
        if ($constructor instanceof ReflectionMethod) {
255
            $commentParts = Tools::explodeTemplateStringToArray($constructor->getDocComment());
256
            return ($constructor->getStartLine() - count($commentParts)) - 1;
257
        }
258
259
        $methods = $reflectionClass->getMethods();
260
261
        if (count($methods) > 0) {
262
            $firstMethod = reset($methods);
263
            if ($firstMethod instanceof ReflectionMethod) {
264
                return $firstMethod->getStartLine() - 1;
265
            }
266
        }
267
268
        return $reflectionClass->getEndLine() - 1;
269
    }
270
271
    /**
272
     * @return Renderer
273
     */
274
    protected function getRenderer()
275
    {
276
        return $this->renderer;
277
    }
278
279
    /**
280
     * @param mixed $item
281
     * @return UnrecognizedItemToDumpException
282
     */
283
    protected function getExceptionUnrecognizedItem($item)
284
    {
285
        return new UnrecognizedItemToDumpException(sprintf("Unrecognized item class: %s", get_class($item)));
286
    }
287
}
288