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