manager::initialize()   C
last analyzed

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