Passed
Push — parser-refactoring ( e758c6 )
by Luis
04:05
created

StructureBuilder::buildFromDefinitions()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 6
nc 4
nop 1
dl 0
loc 10
ccs 7
cts 7
cp 1
crap 6
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 * PHP version 7.1
4
 *
5
 * This source file is subject to the license that is bundled with this package in the file LICENSE.
6
 */
7
namespace PhUml\Parser;
8
9
use PhUml\Code\Attribute;
10
use PhUml\Code\ClassDefinition;
11
use PhUml\Code\Definition;
12
use PhUml\Code\InterfaceDefinition;
13
use PhUml\Code\Method;
14
use PhUml\Code\Structure;
15
use PhUml\Code\Variable;
16
17
class StructureBuilder
18
{
19
    /** @var Structure */
20
    private $structure;
21
22 39
    public function __construct(Structure $structure = null)
23
    {
24 39
        $this->structure = $structure ?? new Structure();
25 39
    }
26
27 39
    public function buildFromDefinitions(Definitions $definitions): Structure
28
    {
29 39
        foreach ($definitions->all() as $definition) {
30 39
            if ($definitions->isClass($definition) && !$this->structure->has($definition['class'])) {
31 30
                $this->structure->addClass($this->buildClass($definitions, $definition));
32 21
            } elseif ($definitions->isInterface($definition) && !$this->structure->has($definition['interface'])) {
33 39
                $this->structure->addInterface($this->buildInterface($definitions, $definition));
34
            }
35
        }
36 39
        return $this->structure;
37
    }
38
39 21
    private function buildInterface(Definitions $definitions, array $interface): InterfaceDefinition
40
    {
41 21
        return new InterfaceDefinition(
42 21
            $interface['interface'],
43 21
            $this->buildMethods($interface),
44 21
            $this->resolveRelatedInterface($definitions, $interface['extends'])
45
        );
46
    }
47
48 30
    private function buildClass(Definitions $definitions, array $class): ClassDefinition
49
    {
50 30
        return new ClassDefinition(
51 30
            $class['class'],
52 30
            $this->buildAttributes($class),
53 30
            $this->buildMethods($class),
54 30
            $this->buildInterfaces($definitions, $class['implements']),
55 30
            $this->resolveParentClass($definitions, $class['extends'])
56
        );
57
    }
58
59
    /** @return Method[] */
60 39
    private function buildMethods(array $definition): array
61
    {
62 39
        $methods = [];
63 39
        foreach ($definition['functions'] as $method) {
64 18
            [$name, $modifier, $parameters] = $method;
65 18
            $methods[] = new Method($name, $modifier, $this->buildParameters($parameters));
66
        }
67 39
        return $methods;
68
    }
69
70
    /** @return Variable[] */
71 18
    private function buildParameters(array $parameters): array
72
    {
73 18
        $params = [];
74 18
        foreach ($parameters as $param) {
75 18
            $params[] = new Variable($param[1], $param[0]);
76
        }
77 18
        return $params;
78
    }
79
80
    /** @return Attribute[] */
81 30
    private function buildAttributes(array $class): array
82
    {
83 30
        $attributes = [];
84 30
        foreach ($class['attributes'] as $attribute) {
85 12
            [$name, $modifier, $comment] = $attribute;
86 12
            $attributes[] = new Attribute($name, $modifier, $this->extractType($comment));
87
        }
88 30
        return $attributes;
89
    }
90
91 12
    private function extractType(?string $comment): ?string
92
    {
93 12
        $type = null;
94 12
        if ($comment === null) {
95 9
            return $type;
96
        }
97
98 9
        $matches = null;
99 9
        $arrayExpression = '/^[\s*]*@var\s+array\(\s*(\w+\s*=>\s*)?(\w+)\s*\).*$/m';
100 9
        if (preg_match($arrayExpression, $comment, $matches)) {
101 9
            $type = $matches[2];
102
        } else {
103 9
            $typeExpression = '/^[\s*]*@var\s+(\S+).*$/m';
104 9
            if (preg_match($typeExpression, $comment, $matches)) {
105 9
                $type = trim($matches[1]);
106
            }
107
        }
108 9
        return $type;
109
    }
110
111
    /**
112
     * @param string[] $implements
113
     * @return Definition[]
114
     */
115 30
    private function buildInterfaces(Definitions $definitions, array $implements): array
116
    {
117 30
        $interfaces = [];
118 30
        foreach ($implements as $interface) {
119 12
            $interfaces[] = $this->resolveRelatedInterface($definitions, $interface);
120
        }
121 30
        return $interfaces;
122
    }
123
124 21
    private function resolveRelatedInterface(Definitions $definitions, ?string $interface): ?Definition
125
    {
126 21
        if ($interface === null) {
127 21
            return null;
128
        }
129 18
        if (!$this->structure->has($interface)) {
130 3
            $this->structure->addInterface($this->buildInterface(
131 3
                $definitions,
132 3
                $definitions->get($interface)
133
            ));
134
        }
135 18
        return $this->structure->get($interface);
136
    }
137
138 30
    private function resolveParentClass(Definitions $definitions, ?string $parent): ?Definition
139
    {
140 30
        if ($parent === null) {
141 30
            return null;
142
        }
143 12
        if (!$this->structure->has($parent)) {
144 3
            $this->structure->addClass($this->buildClass($definitions, $definitions->get($parent)));
145
        }
146 12
        return $this->structure->get($parent);
147
    }
148
}
149