Completed
Push — symfony-console-application ( 3187e2...c3ee2a )
by Luis
10:39
created

StructureBuilder::buildFromDefinitions()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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