Passed
Branch master (b2f909)
by Andreas
04:01
created

manager::initialize()   C

Complexity

Conditions 12
Paths 181

Size

Total Lines 41
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 12.2487

Importance

Changes 0
Metric Value
cc 12
eloc 24
c 0
b 0
f 0
nc 181
nop 0
dl 0
loc 41
ccs 22
cts 25
cp 0.88
crap 12.2487
rs 6.2916

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @author CONTENT CONTROL http://www.contentcontrol-berlin.de/
4
 * @copyright CONTENT CONTROL http://www.contentcontrol-berlin.de/
5
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License
6
 */
7
8
namespace midgard\portable\mgdschema;
9
10
use midgard\portable\xmlreader;
11
use midgard\portable\storage\connection;
12
13
class manager
14
{
15
    /**
16
     * @var type[]
17
     */
18
    private $types;
19
20
    /**
21
     * @var string[]
22
     */
23
    private $schemadirs;
24
25
    /**
26
     * @var string
27
     */
28
    private $namespace;
29
30
    /**
31
     * @var array
32
     */
33
    private $merged_types = [];
34
35
    /**
36
     *
37
     * @var array
38
     */
39
    private $child_classes = [];
40
41 13
    public function __construct(array $schemadirs, string $namespace)
42
    {
43 13
        $this->schemadirs = $schemadirs;
44 13
        $this->namespace = $namespace;
45
    }
46
47
    /**
48
     * @return type[]
49
     */
50 16
    public function get_types() : array
51
    {
52 16
        $this->initialize();
53 16
        return $this->types;
54
    }
55
56 10
    public function get_inherited_mapping() : array
57
    {
58 10
        $this->initialize();
59 10
        return $this->merged_types;
60
    }
61
62 15
    public function get_child_classes(string $typename) : array
63
    {
64 15
        $this->initialize();
65 15
        return $this->child_classes[$typename] ?? [];
66
    }
67
68 14
    public function resolve_targetclass(property $property) : string
69
    {
70 14
        $this->initialize();
71
72 14
        $fqcn = $this->get_fcqn($property->link['target']);
73
74 14
        if (   isset($this->types[$fqcn])
75 14
            || $property->link['target'] === $property->get_parent()->name) {
76 14
            return $property->link['target'];
77
        }
78
        if (!isset($this->merged_types[$property->link['target']])) {
79
            throw new \Exception('Link to unknown class ' . $property->link['target']);
80
        }
81
        return $this->merged_types[$property->link['target']];
82
    }
83
84 19
    private function initialize()
85
    {
86 19
        if ($this->types !== null) {
87 18
            return;
88
        }
89 13
        $reader = new xmlreader;
90 13
        $types = $reader->parse(dirname(__DIR__, 2) . '/xml/core.xml');
91
92 13
        foreach ($this->schemadirs as $schemadir) {
93 13
            foreach (glob($schemadir . '*.xml') as $filename) {
94 13
                if (!file_exists($filename)) {
95
                    connection::log()->warning('File exists check for ' . $filename . ' returned false, skipping');
96
                    continue;
97
                }
98 13
                $types = array_merge($types, $reader->parse($filename));
99
            }
100
        }
101
102 13
        $tablemap = [];
103 13
        foreach ($types as $name => $type) {
104 13
            if ($type->parent) {
105 13
                $this->register_child_class($type);
106
            }
107
108 13
            if (!isset($tablemap[$type->table])) {
109 13
                $tablemap[$type->table] = [];
110
            }
111 13
            $tablemap[$type->table][] = $type;
112
        }
113
114 13
        foreach ($tablemap as $name => $types) {
115 13
            if (count($types) == 1) {
116 13
                $this->add_type($types[0]);
117 13
                unset($tablemap[$name]);
118
            }
119
        }
120
121
        // We need to process those separately, to be sure the targets for rewriting links are present
122 13
        while ($types = array_pop($tablemap)) {
123 5
            if (!$this->create_merged_types($types)) {
124
                array_push($types, $tablemap);
125
            }
126
        }
127
    }
128
129 13
    private function register_child_class(type $type)
130
    {
131 13
        if (!isset($this->child_classes[$type->parent])) {
132 13
            $this->child_classes[$type->parent] = [];
133
        }
134 13
        $this->child_classes[$type->parent][$type->name] = $type->parentfield;
135
    }
136
137
    /**
138
     * This sort of provides a workaround for situations where two tables use the same name
139
     *
140
     * @param type[] $types
141
     */
142 5
    private function create_merged_types(array $types) : bool
143
    {
144 5
        usort($types, function(type $a, type $b) {
145 5
            return strcasecmp($a->name, $b->name);
146
        });
147
148 5
        $root_type = null;
149 5
        foreach ($types as $i => $type) {
150
            // TODO: We should have a second pass here that prefers classnames starting with midgard_
151 5
            if ($type->extends === '\\midgard\\portable\\api\\mgdobject') {
152 5
                $root_type = $type;
153 5
                unset($types[$i]);
154 5
                break;
155
            }
156
        }
157 5
        if (empty($root_type)) {
158
            throw new \Exception('could not determine root type of merged group');
159
        }
160
161 5
        foreach ($types as $type) {
162 5
            foreach ($type->get_properties() as $property) {
163 5
                if ($root_type->has_property($property->name)) {
164 5
                    $root_property = $root_type->get_property($property->name);
165 5
                    if ($root_property->field !== $property->field) {
166
                        connection::log()->error('Naming collision in ' . $root_type->name . ': Field ' . $type->name . '.' . $property->name . ' cannot use column ' . $property->field);
167
                    }
168 5
                    if ($root_property->type !== $property->type) {
169
                        connection::log()->warning('Naming collision in ' . $root_type->name . ': Field ' . $type->name . '.' . $property->name . ' cannot use type ' . $property->type);
170
                    }
171 5
                    continue;
172
                }
173 5
                $root_type->add_property($property);
174
            }
175
176 5
            if (isset($this->child_classes[$type->name])) {
177 5
                foreach ($this->child_classes[$type->name] as $childname => $parentfield) {
178 5
                    $child_type = $this->get_type_by_shortname($childname);
179 5
                    if ($child_type === null) {
180
                        return false;
181
                    }
182
183 5
                    $child_type->parent = $root_type->name;
184 5
                    $this->register_child_class($child_type);
185
                }
186 5
                unset($this->child_classes[$type->name]);
187
            }
188 5
            $this->merged_types[$type->name] = $root_type->name;
189
        }
190 5
        $this->add_type($root_type);
191 5
        return true;
192
    }
193
194 5
    private function get_type_by_shortname(string $classname) : type
195
    {
196 5
        $fqcn = $this->get_fcqn($classname);
197 5
        if (!isset($this->types[$fqcn])) {
198 5
            if (!isset($this->merged_types[$classname])) {
199
                return null;
200
            }
201 5
            $fqcn = $this->get_fcqn($this->merged_types[$classname]);
202
        }
203 5
        return $this->types[$fqcn];
204
    }
205
206 13
    private function add_type(type $type)
207
    {
208 13
        $classname = $type->name;
209
        // TODO: This should probably be in classgenerator
210 13
        if ($classname === 'midgard_user') {
211 13
            $type->extends = 'base_user';
212
        }
213 13
        if ($classname === 'midgard_person') {
214 13
            $type->extends = 'base_person';
215
        }
216 13
        if ($classname === 'midgard_parameter') {
217 13
            $type->extends = 'base_parameter';
218
        }
219 13
        if ($classname === 'midgard_repligard') {
220 13
            $type->extends = 'base_repligard';
221
        }
222 13
        if ($classname === 'midgard_attachment') {
223 13
            $type->extends = 'base_attachment';
224
        }
225 13
        $this->types[$this->get_fcqn($classname)] = $type;
226
    }
227
228 16
    private function get_fcqn(string $classname) : string
229
    {
230 16
        if (!empty($this->namespace)) {
231 16
            return $this->namespace . '\\' . $classname;
232
        }
233
        return $classname;
234
    }
235
}
236