Passed
Push — master ( 2d423f...904272 )
by Andreas
03:29
created

manager::get_child_classes()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
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 16
    public function get_types()
52
    {
53 16
        $this->initialize();
54 16
        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 14
    public function get_child_classes($typename)
64
    {
65 14
        $this->initialize();
66 14
        if (array_key_exists($typename, $this->child_classes)) {
67 9
            return $this->child_classes[$typename];
68
        }
69 11
        return [];
70
    }
71
72 15
    public function resolve_targetclass(property $property)
73
    {
74 15
        $this->initialize();
75
76 15
        $fqcn = $this->get_fcqn($property->link['target']);
77
78 15
        if (   array_key_exists($fqcn, $this->types)
79 15
            || $property->link['target'] === $property->get_parent()->name) {
80 15
            $target_class = $property->link['target'];
81 15
        } else {
82
            if (!array_key_exists($property->link['target'], $this->merged_types)) {
83
                throw new \Exception('Link to unknown class ' . $property->link['target']);
84
            }
85
            $target_class = $this->merged_types[$property->link['target']];
86
        }
87 15
        return $target_class;
88
    }
89
90 19
    private function initialize()
91
    {
92 19
        if ($this->types !== null) {
93 18
            return;
94
        }
95 14
        $reader = new xmlreader;
96 14
        $types = $reader->parse(dirname(dirname(__DIR__)) . '/xml/core.xml');
97
98 14
        foreach ($this->schemadirs as $schemadir) {
99 14
            foreach (glob($schemadir . '*.xml', GLOB_NOSORT) as $filename) {
100 14
                if (!file_exists($filename)) {
101
                    connection::log()->warning('File exists check for ' . $filename . ' returned false, skipping');
102
                    continue;
103
                }
104 14
                $types = array_merge($types, $reader->parse($filename));
105 14
            }
106 14
        }
107
108 14
        $tablemap = [];
109 14
        foreach ($types as $name => $type) {
110 14
            if ($type->parent) {
111 14
                $this->register_child_class($type);
112 14
            }
113
114 14
            if (!array_key_exists($type->table, $tablemap)) {
115 14
                $tablemap[$type->table] = [];
116 14
            }
117 14
            $tablemap[$type->table][] = $type;
118 14
        }
119
120 14
        foreach ($tablemap as $name => $types) {
121 14
            if (count($types) == 1) {
122 14
                $this->add_type($types[0]);
123 14
                unset($tablemap[$name]);
124 14
            }
125 14
        }
126
127
        // We need to process those separately, to be sure the targets for rewriting links are present
128 14
        while ($types = array_pop($tablemap)) {
129 5
            if (!$this->create_merged_types($types)) {
130
                array_push($types, $tablemap);
131
            }
132 5
        }
133 14
    }
134
135 14
    private function register_child_class(type $type)
136
    {
137 14
        if (!array_key_exists($type->parent, $this->child_classes)) {
138 14
            $this->child_classes[$type->parent] = [];
139 14
        }
140 14
        $this->child_classes[$type->parent][$type->name] = $type->parentfield;
141 14
    }
142
143
    /**
144
     * This sort of provides a workaround for situations where two tables use the same name
145
     */
146 5
    private function create_merged_types(array $types)
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 5
        }
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 View Code Duplication
                    if ($root_property->field !== $property->field) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
166
                        connection::log()->error('Naming collision in ' . $root_type->name . ': Field ' . $type->name . '.' . $property->name . ' cannot use column ' . $property->field);
167
                    }
168 5 View Code Duplication
                    if ($root_property->type !== $property->type) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 5
            }
175
176 5
            if (array_key_exists($type->name, $this->child_classes)) {
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 5
                }
186 5
                unset($this->child_classes[$type->name]);
187 5
            }
188 5
            $this->merged_types[$type->name] = $root_type->name;
189 5
        }
190 5
        $this->add_type($root_type);
191 5
        return true;
192
    }
193
194 5
    private function get_type_by_shortname($classname)
195
    {
196 5
        $fqcn = $this->get_fcqn($classname);
197 5
        if (array_key_exists($fqcn, $this->types)) {
198
            return $this->types[$fqcn];
199 5
        } elseif (array_key_exists($classname, $this->merged_types)) {
200 5
            $fqcn = $this->get_fcqn($this->merged_types[$classname]);
201 5
            return $this->types[$fqcn];
202
        }
203
        return null;
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 14
        }
213 14
        if ($classname === 'midgard_person') {
214 14
            $type->extends = 'base_person';
215 14
        }
216 14
        if ($classname === 'midgard_parameter') {
217 14
            $type->extends = 'base_parameter';
218 14
        }
219 14
        if ($classname === 'midgard_repligard') {
220 14
            $type->extends = 'base_repligard';
221 14
        }
222 14
        if ($classname === 'midgard_attachment') {
223 14
            $type->extends = 'base_attachment';
224 14
        }
225 14
        $this->types[$this->get_fcqn($classname)] = $type;
226 14
    }
227
228 17
    private function get_fcqn($classname)
229
    {
230 17
        if (!empty($this->namespace)) {
231 17
            return $this->namespace . '\\' . $classname;
232
        }
233
        return $classname;
234
    }
235
}
236