|
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
|
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 array(); |
|
70
|
|
|
} |
|
71
|
|
|
|
|
72
|
15 |
|
public function resolve_targetclass(property $property) |
|
73
|
|
|
{ |
|
74
|
15 |
|
$this->initialize(); |
|
75
|
|
|
|
|
76
|
15 |
|
$fqcn = $property->link['target']; |
|
77
|
15 |
|
if (!empty($this->namespace)) { |
|
78
|
15 |
|
$fqcn = $this->namespace . '\\' . $fqcn; |
|
79
|
15 |
|
} |
|
80
|
|
|
|
|
81
|
15 |
|
if ( array_key_exists($fqcn, $this->types) |
|
82
|
15 |
|
|| $property->link['target'] === $property->get_parent()->name) { |
|
83
|
15 |
|
$target_class = $property->link['target']; |
|
84
|
15 |
|
} else { |
|
85
|
5 |
|
if (!array_key_exists($property->link['target'], $this->merged_types)) { |
|
86
|
|
|
throw new \Exception('Link to unknown class ' . $property->link['target']); |
|
87
|
|
|
} |
|
88
|
5 |
|
$target_class = $this->merged_types[$property->link['target']]; |
|
89
|
|
|
} |
|
90
|
15 |
|
return $target_class; |
|
91
|
|
|
} |
|
92
|
|
|
|
|
93
|
19 |
|
private function initialize() |
|
94
|
|
|
{ |
|
95
|
19 |
|
if ($this->types !== null) { |
|
96
|
18 |
|
return; |
|
97
|
|
|
} |
|
98
|
14 |
|
$reader = new xmlreader; |
|
99
|
14 |
|
$types = $reader->parse(dirname(dirname(__DIR__)) . '/xml/core.xml'); |
|
100
|
|
|
|
|
101
|
14 |
|
foreach ($this->schemadirs as $schemadir) { |
|
102
|
14 |
|
foreach (glob($schemadir . '*.xml', GLOB_NOSORT) as $filename) { |
|
103
|
14 |
|
if (!file_exists($filename)) { |
|
104
|
|
|
connection::log()->warning('File exists check for ' . $filename . ' returned false, skipping'); |
|
105
|
|
|
continue; |
|
106
|
|
|
} |
|
107
|
14 |
|
$types = array_merge($types, $reader->parse($filename)); |
|
108
|
14 |
|
} |
|
109
|
14 |
|
} |
|
110
|
|
|
|
|
111
|
14 |
|
$tablemap = array(); |
|
112
|
14 |
|
foreach ($types as $name => $type) { |
|
113
|
14 |
|
if ($type->parent) { |
|
114
|
14 |
|
$this->register_child_class($type); |
|
115
|
14 |
|
} |
|
116
|
|
|
|
|
117
|
14 |
|
if (!array_key_exists($type->table, $tablemap)) { |
|
118
|
14 |
|
$tablemap[$type->table] = array(); |
|
119
|
14 |
|
} |
|
120
|
14 |
|
$tablemap[$type->table][] = $type; |
|
121
|
14 |
|
} |
|
122
|
|
|
|
|
123
|
14 |
|
foreach ($tablemap as $name => $types) { |
|
124
|
14 |
|
if (count($types) == 1) { |
|
125
|
14 |
|
$this->add_type($types[0]); |
|
126
|
14 |
|
unset($tablemap[$name]); |
|
127
|
14 |
|
} |
|
128
|
14 |
|
} |
|
129
|
|
|
|
|
130
|
|
|
// We need to process those separately, to be sure the targets for rewriting links are present |
|
131
|
14 |
|
while ($types = array_pop($tablemap)) { |
|
132
|
5 |
|
if (!$this->create_merged_types($types)) { |
|
133
|
|
|
array_push($types, $tablemap); |
|
134
|
|
|
} |
|
135
|
5 |
|
} |
|
136
|
14 |
|
} |
|
137
|
|
|
|
|
138
|
14 |
|
private function register_child_class(type $type) |
|
139
|
|
|
{ |
|
140
|
14 |
|
if (!array_key_exists($type->parent, $this->child_classes)) { |
|
141
|
14 |
|
$this->child_classes[$type->parent] = array(); |
|
142
|
14 |
|
} |
|
143
|
14 |
|
$this->child_classes[$type->parent][$type->name] = $type->parentfield; |
|
144
|
14 |
|
} |
|
145
|
|
|
|
|
146
|
|
|
/** |
|
147
|
|
|
* This sort of provides a workaround for situations where two tables use the same name |
|
148
|
|
|
*/ |
|
149
|
5 |
|
private function create_merged_types(array $types) |
|
150
|
|
|
{ |
|
151
|
5 |
|
$root_type = null; |
|
152
|
5 |
|
foreach ($types as $i => $type) { |
|
153
|
|
|
// TODO: We should have a second pass here that prefers classnames starting with midgard_ |
|
154
|
5 |
|
if ($type->extends === '\\midgard\\portable\\api\\object') { |
|
155
|
5 |
|
$root_type = $type; |
|
156
|
5 |
|
unset($types[$i]); |
|
157
|
5 |
|
break; |
|
158
|
|
|
} |
|
159
|
5 |
|
} |
|
160
|
5 |
|
if (empty($root_type)) { |
|
161
|
|
|
throw new \Exception('could not determine root type of merged group'); |
|
162
|
|
|
} |
|
163
|
|
|
|
|
164
|
5 |
|
foreach ($types as $type) { |
|
165
|
5 |
|
foreach ($type->get_properties() as $property) { |
|
166
|
5 |
|
if ($root_type->has_property($property->name)) { |
|
167
|
5 |
|
$root_property = $root_type->get_property($property->name); |
|
168
|
5 |
View Code Duplication |
if ($root_property->field !== $property->field) { |
|
|
|
|
|
|
169
|
|
|
connection::log()->error('Naming collision in ' . $root_type->name . ': Field ' . $type->name . '.' . $property->name . ' cannot use column ' . $property->field); |
|
170
|
|
|
} |
|
171
|
5 |
View Code Duplication |
if ($root_property->type !== $property->type) { |
|
|
|
|
|
|
172
|
|
|
connection::log()->warn('Naming collision in ' . $root_type->name . ': Field ' . $type->name . '.' . $property->name . ' cannot use type ' . $property->type); |
|
173
|
|
|
} |
|
174
|
5 |
|
continue; |
|
175
|
|
|
} |
|
176
|
5 |
|
$root_type->add_property($property); |
|
177
|
5 |
|
} |
|
178
|
|
|
|
|
179
|
5 |
|
if (array_key_exists($type->name, $this->child_classes)) { |
|
180
|
5 |
|
foreach ($this->child_classes[$type->name] as $childname => $parentfield) { |
|
181
|
5 |
|
$child_type = $this->get_type_by_shortname($childname); |
|
182
|
5 |
|
if ($child_type === null) { |
|
183
|
|
|
return false; |
|
184
|
|
|
} |
|
185
|
|
|
|
|
186
|
5 |
|
$child_type->parent = $root_type->name; |
|
187
|
5 |
|
$this->register_child_class($child_type); |
|
188
|
5 |
|
} |
|
189
|
5 |
|
unset($this->child_classes[$type->name]); |
|
190
|
5 |
|
} |
|
191
|
5 |
|
$this->merged_types[$type->name] = $root_type->name; |
|
192
|
5 |
|
} |
|
193
|
5 |
|
$this->add_type($root_type); |
|
194
|
5 |
|
return true; |
|
195
|
|
|
} |
|
196
|
|
|
|
|
197
|
5 |
|
private function get_type_by_shortname($classname) |
|
198
|
|
|
{ |
|
199
|
5 |
|
$fqcn = $classname; |
|
200
|
5 |
|
if (!empty($this->namespace)) { |
|
201
|
5 |
|
$fqcn = $this->namespace . '\\' . $classname; |
|
202
|
5 |
|
} |
|
203
|
5 |
|
if (array_key_exists($fqcn, $this->types)) { |
|
204
|
5 |
|
return $this->types[$fqcn]; |
|
205
|
5 |
|
} elseif (array_key_exists($classname, $this->merged_types)) { |
|
206
|
5 |
|
$fqcn = $this->merged_types[$classname]; |
|
207
|
5 |
|
if (!empty($this->namespace)) { |
|
208
|
5 |
|
$fqcn = $this->namespace . '\\' . $fqcn; |
|
209
|
5 |
|
} |
|
210
|
5 |
|
return $this->types[$fqcn]; |
|
211
|
|
|
} |
|
212
|
|
|
return null; |
|
213
|
|
|
} |
|
214
|
|
|
|
|
215
|
14 |
|
private function add_type(type $type) |
|
216
|
|
|
{ |
|
217
|
14 |
|
$classname = $type->name; |
|
218
|
|
|
// TODO: This should probably be in classgenerator |
|
219
|
14 |
|
if ($classname === 'midgard_user') { |
|
220
|
14 |
|
$type->extends = 'base_user'; |
|
221
|
14 |
|
} |
|
222
|
14 |
|
if ($classname === 'midgard_person') { |
|
223
|
14 |
|
$type->extends = 'base_person'; |
|
224
|
14 |
|
} |
|
225
|
14 |
|
if ($classname === 'midgard_parameter') { |
|
226
|
14 |
|
$type->extends = 'base_parameter'; |
|
227
|
14 |
|
} |
|
228
|
14 |
|
if ($classname === 'midgard_repligard') { |
|
229
|
14 |
|
$type->extends = 'base_repligard'; |
|
230
|
14 |
|
} |
|
231
|
14 |
|
if ($classname === 'midgard_attachment') { |
|
232
|
14 |
|
$type->extends = 'base_attachment'; |
|
233
|
14 |
|
} |
|
234
|
14 |
|
if (!empty($this->namespace)) { |
|
235
|
14 |
|
$classname = $this->namespace . '\\' . $classname; |
|
236
|
14 |
|
} |
|
237
|
14 |
|
$this->types[$classname] = $type; |
|
238
|
14 |
|
} |
|
239
|
|
|
} |
|
240
|
|
|
|
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.