Completed
Push — master ( 64b389...43ae8c )
by Andreas
06:46 queued 02:51
created

manager::resolve_targetclass()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 5.005

Importance

Changes 0
Metric Value
cc 5
eloc 13
nc 6
nop 1
dl 0
loc 25
ccs 16
cts 17
cp 0.9412
crap 5.005
rs 8.439
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 = array();
38
39
    /**
40
     *
41
     * @var array
42
     */
43
    private $child_classes = array();
44
45 14
    public function __construct(array $schemadirs, $namespace)
46
    {
47 14
        $this->schemadirs = $schemadirs;
48 14
        $this->namespace = $namespace;
49 14
    }
50
51 15
    public function get_types()
52
    {
53 15
        $this->initialize();
54 15
        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 11
    public function get_child_classes($typename)
64
    {
65 11
        $this->initialize();
66 11
        if (array_key_exists($typename, $this->child_classes))
67 11
        {
68 8
            return $this->child_classes[$typename];
69
        }
70 8
        return array();
71
    }
72
73 13
    public function resolve_targetclass(property $property)
74
    {
75 13
        $this->initialize();
76
77 13
        $fqcn = $property->link['target'];
78 13
        if (!empty($this->namespace))
79 13
        {
80 13
            $fqcn = $this->namespace . '\\' . $fqcn;
81 13
        }
82
83 13
        if (   array_key_exists($fqcn, $this->types)
84 5
            || $property->link['target'] === $property->get_parent()->name)
85 13
        {
86 13
            $target_class = $property->link['target'];
87 13
        }
88
        else
89
        {
90 5
            if (!array_key_exists($property->link['target'], $this->merged_types))
91 5
            {
92
                throw new \Exception('Link to unknown class ' . $property->link['target']);
93
            }
94 5
            $target_class = $this->merged_types[$property->link['target']];
95
        }
96 13
        return $target_class;
97
    }
98
99 16
    private function initialize()
100
    {
101 16
        if ($this->types !== null)
102 16
        {
103 15
            return;
104
        }
105 14
        $reader = new xmlreader;
106 14
        $types = $reader->parse(dirname(dirname(__DIR__)) . '/xml/core.xml');
107
108 14
        foreach ($this->schemadirs as $schemadir)
109
        {
110 14
            foreach (glob($schemadir . '*.xml', GLOB_NOSORT) as $filename)
111
            {
112 14
                if (!file_exists($filename))
113 14
                {
114
                    connection::log()->warning('File exists check for ' . $filename . ' returned false, skipping');
115
                    continue;
116
                }
117 14
                $types = array_merge($types, $reader->parse($filename));
118 14
            }
119 14
        }
120
121 14
        $tablemap = array();
122 14
        foreach ($types as $name => $type)
123
        {
124 14
            if ($type->parent)
125 14
            {
126 14
                $this->register_child_class($type);
127 14
            }
128
129 14
            if (!array_key_exists($type->table, $tablemap))
130 14
            {
131 14
                $tablemap[$type->table] = array();
132 14
            }
133 14
            $tablemap[$type->table][] = $type;
134 14
        }
135
136 14
        foreach ($tablemap as $name => $types)
137
        {
138 14
            if (count($types) == 1)
139 14
            {
140 14
                $this->add_type($types[0]);
141 14
                unset($tablemap[$name]);
142 14
            }
143 14
        }
144
145
        // We need to process those separately, to be sure the targets for rewriting links are present
146 14
        while ($types = array_pop($tablemap))
147
        {
148 5
            if (!$this->create_merged_types($types))
149 5
            {
150
                array_push($types, $tablemap);
151
            }
152 5
        }
153 14
    }
154
155 14
    private function register_child_class(type $type)
156
    {
157 14
        if (!array_key_exists($type->parent, $this->child_classes))
158 14
        {
159 14
            $this->child_classes[$type->parent] = array();
160 14
        }
161 14
        $this->child_classes[$type->parent][$type->name] = $type->parentfield;
162 14
    }
163
164
    /**
165
     * This sort of provides a workaround for situations where two tables use the same name
166
     */
167 5
    private function create_merged_types(array $types)
168
    {
169 5
        $root_type = null;
170 5
        foreach ($types as $i => $type)
171
        {
172
            // TODO: We should have a second pass here that prefers classnames starting with midgard_
173 5
            if ($type->extends === 'midgard_object')
174 5
            {
175 5
                $root_type = $type;
176 5
                unset($types[$i]);
177 5
                break;
178
            }
179 5
        }
180 5
        if (empty($root_type))
181 5
        {
182
            throw new \Exception('could not determine root type of merged group');
183
        }
184
185 5
        foreach ($types as $type)
186
        {
187 5
            foreach ($type->get_properties() as $property)
188
            {
189 5
                if ($root_type->has_property($property->name))
190 5
                {
191 5
                    $root_property = $root_type->get_property($property->name);
192 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...
193 5
                    {
194
                        connection::log()->error('Naming collision in ' . $root_type->name . ': Field ' . $type->name . '.' . $property->name . ' cannot use column ' . $property->field);
195
                    }
196 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...
197 5
                    {
198
                        connection::log()->warn('Naming collision in ' . $root_type->name . ': Field ' . $type->name . '.' . $property->name . ' cannot use type ' . $property->type);
199
                    }
200 5
                    continue;
201
                }
202 5
                $root_type->add_property($property);
203 5
            }
204
205 5
            if (array_key_exists($type->name, $this->child_classes))
206 5
            {
207 5
                foreach ($this->child_classes[$type->name] as $childname => $parentfield)
208
                {
209 5
                    $child_type = $this->get_type_by_shortname($childname);
210 5
                    if ($child_type === null)
211 5
                    {
212
                        return false;
213
                    }
214
215 5
                    $child_type->parent = $root_type->name;
216 5
                    $this->register_child_class($child_type);
217 5
                }
218 5
                unset($this->child_classes[$type->name]);
219 5
            }
220 5
            $this->merged_types[$type->name] = $root_type->name;
221 5
        }
222 5
        $this->add_type($root_type);
223 5
        return true;
224
    }
225
226 5
    private function get_type_by_shortname($classname)
227
    {
228 5
        $fqcn = $classname;
229 5
        if (!empty($this->namespace))
230 5
        {
231 5
            $fqcn = $this->namespace . '\\' . $classname;
232 5
        }
233 5
        if (array_key_exists($fqcn, $this->types))
234 5
        {
235 5
            return $this->types[$fqcn];
236
        }
237 5
        else if (array_key_exists($classname, $this->merged_types))
238 5
        {
239 5
            $fqcn = $this->merged_types[$classname];
240 5
            if (!empty($this->namespace))
241 5
            {
242 5
                $fqcn = $this->namespace . '\\' . $fqcn;
243 5
            }
244 5
            return $this->types[$fqcn];
245
        }
246
        return null;
247
    }
248
249 14
    private function add_type(type $type)
250
    {
251 14
        $classname = $type->name;
252
        // TODO: This should probably be in classgenerator
253 14
        if ($classname === 'midgard_user')
254 14
        {
255 14
            $type->extends = 'base_user';
256 14
        }
257 14
        if ($classname === 'midgard_person')
258 14
        {
259 14
            $type->extends = 'base_person';
260 14
        }
261 14
        if ($classname === 'midgard_parameter')
262 14
        {
263 14
            $type->extends = 'base_parameter';
264 14
        }
265 14
        if ($classname === 'midgard_repligard')
266 14
        {
267 14
            $type->extends = 'base_repligard';
268 14
        }
269 14
        if ($classname === 'midgard_attachment')
270 14
        {
271 14
            $type->extends = 'base_attachment';
272 14
        }
273 14
        if (!empty($this->namespace))
274 14
        {
275 14
            $classname = $this->namespace . '\\' . $classname;
276 14
        }
277 14
        $this->types[$classname] = $type;
278
    }
279
}