Passed
Push — master ( f2c4fa...0238e7 )
by Andreas
03:03
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 1
Bugs 0 Features 0
Metric Value
cc 12
eloc 24
c 1
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 14
    public function __construct(array $schemadirs, $namespace)
42
    {
43 14
        $this->schemadirs = $schemadirs;
44 14
        $this->namespace = $namespace;
45 14
    }
46
47
    /**
48
     * @return type[]
49
     */
50 17
    public function get_types() : array
51
    {
52 17
        $this->initialize();
53 17
        return $this->types;
54
    }
55
56 11
    public function get_inherited_mapping() : array
57
    {
58 11
        $this->initialize();
59 11
        return $this->merged_types;
60
    }
61
62 15
    public function get_child_classes($typename) : array
63
    {
64 15
        $this->initialize();
65 15
        return $this->child_classes[$typename] ?? [];
66
    }
67
68 15
    public function resolve_targetclass(property $property)
69
    {
70 15
        $this->initialize();
71
72 15
        $fqcn = $this->get_fcqn($property->link['target']);
73
74 15
        if (   isset($this->types[$fqcn])
75 15
            || $property->link['target'] === $property->get_parent()->name) {
76 15
            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 20
    private function initialize()
85
    {
86 20
        if ($this->types !== null) {
87 19
            return;
88
        }
89 14
        $reader = new xmlreader;
90 14
        $types = $reader->parse(dirname(__DIR__, 2) . '/xml/core.xml');
91
92 14
        foreach ($this->schemadirs as $schemadir) {
93 14
            foreach (glob($schemadir . '*.xml', GLOB_NOSORT) as $filename) {
94 14
                if (!file_exists($filename)) {
95
                    connection::log()->warning('File exists check for ' . $filename . ' returned false, skipping');
96
                    continue;
97
                }
98 14
                $types = array_merge($types, $reader->parse($filename));
99
            }
100
        }
101
102 14
        $tablemap = [];
103 14
        foreach ($types as $name => $type) {
104 14
            if ($type->parent) {
105 14
                $this->register_child_class($type);
106
            }
107
108 14
            if (!isset($tablemap[$type->table])) {
109 14
                $tablemap[$type->table] = [];
110
            }
111 14
            $tablemap[$type->table][] = $type;
112
        }
113
114 14
        foreach ($tablemap as $name => $types) {
115 14
            if (count($types) == 1) {
116 14
                $this->add_type($types[0]);
117 14
                unset($tablemap[$name]);
118
            }
119
        }
120
121
        // We need to process those separately, to be sure the targets for rewriting links are present
122 14
        while ($types = array_pop($tablemap)) {
123 5
            if (!$this->create_merged_types($types)) {
124
                array_push($types, $tablemap);
125
            }
126
        }
127 14
    }
128
129 14
    private function register_child_class(type $type)
130
    {
131 14
        if (!isset($this->child_classes[$type->parent])) {
132 14
            $this->child_classes[$type->parent] = [];
133
        }
134 14
        $this->child_classes[$type->parent][$type->name] = $type->parentfield;
135 14
    }
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
        usort($types, function(type $a, type $b) {
145 5
            return strcasecmp($a->name, $b->name);
146 5
        });
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()->warn('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($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 14
    private function add_type(type $type)
207
    {
208 14
        $classname = $type->name;
209
        // TODO: This should probably be in classgenerator
210 14
        if ($classname === 'midgard_user') {
211 14
            $type->extends = 'base_user';
212
        }
213 14
        if ($classname === 'midgard_person') {
214 14
            $type->extends = 'base_person';
215
        }
216 14
        if ($classname === 'midgard_parameter') {
217 14
            $type->extends = 'base_parameter';
218
        }
219 14
        if ($classname === 'midgard_repligard') {
220 14
            $type->extends = 'base_repligard';
221
        }
222 14
        if ($classname === 'midgard_attachment') {
223 14
            $type->extends = 'base_attachment';
224
        }
225 14
        $this->types[$this->get_fcqn($classname)] = $type;
226 14
    }
227
228 17
    private function get_fcqn(string $classname) : string
229
    {
230 17
        if (!empty($this->namespace)) {
231 17
            return $this->namespace . '\\' . $classname;
232
        }
233
        return $classname;
234
    }
235
}
236