1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace GoetasWebservices\XML\XSDReader; |
4
|
|
|
|
5
|
|
|
use Closure; |
6
|
|
|
use DOMDocument; |
7
|
|
|
use DOMElement; |
8
|
|
|
use DOMNode; |
9
|
|
|
use GoetasWebservices\XML\XSDReader\Exception\IOException; |
10
|
|
|
use GoetasWebservices\XML\XSDReader\Exception\TypeException; |
11
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Attribute; |
12
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeContainer; |
13
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeDef; |
14
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem; |
15
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Group as AttributeGroup; |
16
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Element\Element; |
17
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementContainer; |
18
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementDef; |
19
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementItem; |
20
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementRef; |
21
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Element\Group; |
22
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Element\InterfaceSetMinMax; |
23
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Element\GroupRef; |
24
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Exception\TypeNotFoundException; |
25
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Base; |
26
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Extension; |
27
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Restriction; |
28
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Item; |
29
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Schema; |
30
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\SchemaItem; |
31
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Type\BaseComplexType; |
32
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType; |
33
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexTypeSimpleContent; |
34
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Type\SimpleType; |
35
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Type\Type; |
36
|
|
|
use GoetasWebservices\XML\XSDReader\Utils\UrlUtils; |
37
|
|
|
use RuntimeException; |
38
|
|
|
|
39
|
|
|
class SchemaReader |
40
|
|
|
{ |
41
|
|
|
/** |
42
|
|
|
* @param string $typeName |
43
|
|
|
* |
44
|
|
|
* @return mixed[] |
45
|
|
|
*/ |
46
|
45 |
|
private static function splitParts(DOMElement $node, $typeName) |
47
|
|
|
{ |
48
|
45 |
|
$prefix = null; |
49
|
45 |
|
$name = $typeName; |
50
|
45 |
|
if (strpos($typeName, ':') !== false) { |
51
|
45 |
|
list($prefix, $name) = explode(':', $typeName); |
52
|
45 |
|
} |
53
|
|
|
|
54
|
45 |
|
$namespace = $node->lookupNamespaceUri($prefix ?: ''); |
55
|
|
|
|
56
|
|
|
return array( |
57
|
45 |
|
$name, |
58
|
45 |
|
$namespace, |
59
|
45 |
|
$prefix, |
60
|
45 |
|
); |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @param bool $attributeDef |
65
|
|
|
* |
66
|
|
|
* @return Closure |
67
|
|
|
*/ |
68
|
45 |
|
private function loadAttributeOrElementDef( |
69
|
|
|
Schema $schema, |
70
|
|
|
DOMElement $node, |
71
|
|
|
$attributeDef |
72
|
|
|
) { |
73
|
45 |
|
$name = $node->getAttribute('name'); |
74
|
45 |
|
if ($attributeDef) { |
75
|
45 |
|
$attribute = new AttributeDef($schema, $name); |
76
|
45 |
|
$schema->addAttribute($attribute); |
77
|
45 |
|
} else { |
78
|
45 |
|
$attribute = new ElementDef($schema, $name); |
79
|
45 |
|
$schema->addElement($attribute); |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
return function () use ($attribute, $node) { |
83
|
45 |
|
$this->fillItem($attribute, $node); |
84
|
45 |
|
}; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* @return Closure |
89
|
|
|
*/ |
90
|
45 |
|
private function loadAttributeDef(Schema $schema, DOMElement $node) |
91
|
|
|
{ |
92
|
45 |
|
return $this->loadAttributeOrElementDef($schema, $node, true); |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* @param int|null $max |
97
|
|
|
* |
98
|
|
|
* @return int|null |
99
|
|
|
*/ |
100
|
45 |
|
private static function loadSequenceNormaliseMax(DOMElement $node, $max) |
101
|
|
|
{ |
102
|
|
|
return |
103
|
|
|
( |
104
|
45 |
|
(is_int($max) && (bool) $max) || |
105
|
45 |
|
$node->getAttribute('maxOccurs') == 'unbounded' || |
106
|
45 |
|
$node->getAttribute('maxOccurs') > 1 |
107
|
45 |
|
) |
108
|
45 |
|
? 2 |
109
|
45 |
|
: null; |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* @param int|null $max |
114
|
|
|
*/ |
115
|
45 |
|
private function loadSequence(ElementContainer $elementContainer, DOMElement $node, $max = null) |
116
|
|
|
{ |
117
|
45 |
|
$max = static::loadSequenceNormaliseMax($node, $max); |
118
|
|
|
|
119
|
45 |
|
static::againstDOMNodeList( |
120
|
45 |
|
$node, |
121
|
|
|
function ( |
122
|
|
|
DOMElement $node, |
123
|
|
|
DOMElement $childNode |
124
|
|
|
) use ( |
125
|
45 |
|
$elementContainer, |
126
|
45 |
|
$max |
127
|
|
|
) { |
128
|
45 |
|
$this->loadSequenceChildNode( |
129
|
45 |
|
$elementContainer, |
130
|
45 |
|
$node, |
131
|
45 |
|
$childNode, |
132
|
|
|
$max |
133
|
45 |
|
); |
134
|
45 |
|
} |
135
|
45 |
|
); |
136
|
45 |
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* @param int|null $max |
140
|
|
|
*/ |
141
|
45 |
View Code Duplication |
private function loadSequenceChildNode( |
|
|
|
|
142
|
|
|
ElementContainer $elementContainer, |
143
|
|
|
DOMElement $node, |
144
|
|
|
DOMElement $childNode, |
145
|
|
|
$max |
146
|
|
|
) { |
147
|
45 |
|
switch ($childNode->localName) { |
148
|
45 |
|
case 'sequence': |
149
|
45 |
|
case 'choice': |
150
|
45 |
|
case 'all': |
151
|
45 |
|
$this->loadSequence( |
152
|
45 |
|
$elementContainer, |
153
|
45 |
|
$childNode, |
154
|
|
|
$max |
155
|
45 |
|
); |
156
|
45 |
|
break; |
157
|
45 |
|
case 'element': |
158
|
45 |
|
$this->loadSequenceChildNodeLoadElement( |
159
|
45 |
|
$elementContainer, |
160
|
45 |
|
$node, |
161
|
45 |
|
$childNode, |
162
|
|
|
$max |
163
|
45 |
|
); |
164
|
45 |
|
break; |
165
|
45 |
|
case 'group': |
166
|
45 |
|
$this->addGroupAsElement( |
167
|
45 |
|
$elementContainer->getSchema(), |
168
|
45 |
|
$node, |
169
|
45 |
|
$childNode, |
170
|
|
|
$elementContainer |
171
|
45 |
|
); |
172
|
45 |
|
break; |
173
|
45 |
|
} |
174
|
45 |
|
} |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* @param int|null $max |
178
|
|
|
*/ |
179
|
45 |
|
private function loadSequenceChildNodeLoadElement( |
180
|
|
|
ElementContainer $elementContainer, |
181
|
|
|
DOMElement $node, |
182
|
|
|
DOMElement $childNode, |
183
|
|
|
$max |
184
|
|
|
) { |
185
|
45 |
|
if ($childNode->hasAttribute('ref')) { |
186
|
|
|
/** |
187
|
|
|
* @var ElementDef $referencedElement |
188
|
|
|
*/ |
189
|
45 |
|
$referencedElement = $this->findSomething('findElement', $elementContainer->getSchema(), $node, $childNode->getAttribute('ref')); |
190
|
45 |
|
$element = static::loadElementRef( |
191
|
45 |
|
$referencedElement, |
192
|
|
|
$childNode |
193
|
45 |
|
); |
194
|
45 |
|
} else { |
195
|
45 |
|
$element = $this->loadElement( |
196
|
45 |
|
$elementContainer->getSchema(), |
197
|
|
|
$childNode |
198
|
45 |
|
); |
199
|
|
|
} |
200
|
45 |
|
if ($max > 1) { |
201
|
|
|
/* |
202
|
|
|
* although one might think the typecast is not needed with $max being `? int $max` after passing > 1, |
203
|
|
|
* phpstan@a4f89fa still thinks it's possibly null. |
204
|
|
|
* see https://github.com/phpstan/phpstan/issues/577 for related issue |
205
|
|
|
*/ |
206
|
45 |
|
$element->setMax((int) $max); |
207
|
45 |
|
} |
208
|
45 |
|
$elementContainer->addElement($element); |
209
|
45 |
|
} |
210
|
|
|
|
211
|
45 |
|
private function addGroupAsElement( |
212
|
|
|
Schema $schema, |
213
|
|
|
DOMElement $node, |
214
|
|
|
DOMElement $childNode, |
215
|
|
|
ElementContainer $elementContainer |
216
|
|
|
) { |
217
|
|
|
/** |
218
|
|
|
* @var Group |
219
|
|
|
*/ |
220
|
45 |
|
$referencedGroup = $this->findSomething( |
221
|
45 |
|
'findGroup', |
222
|
45 |
|
$schema, |
223
|
45 |
|
$node, |
224
|
45 |
|
$childNode->getAttribute('ref') |
225
|
45 |
|
); |
226
|
|
|
|
227
|
45 |
|
$group = $this->loadGroupRef($referencedGroup, $childNode); |
228
|
45 |
|
$elementContainer->addElement($group); |
229
|
45 |
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* @return Closure |
233
|
|
|
*/ |
234
|
45 |
|
private function loadGroup(Schema $schema, DOMElement $node) |
235
|
|
|
{ |
236
|
45 |
|
$group = static::loadGroupBeforeCheckingChildNodes( |
237
|
45 |
|
$schema, |
238
|
|
|
$node |
239
|
45 |
|
); |
240
|
|
|
static $methods = [ |
241
|
|
|
'sequence' => 'loadSequence', |
242
|
|
|
'choice' => 'loadSequence', |
243
|
|
|
'all' => 'loadSequence', |
244
|
45 |
|
]; |
245
|
|
|
|
246
|
|
|
return function () use ($group, $node, $methods) { |
247
|
|
|
/** |
248
|
|
|
* @var string[] |
249
|
|
|
*/ |
250
|
45 |
|
$methods = $methods; |
251
|
45 |
|
$this->maybeCallMethodAgainstDOMNodeList( |
252
|
45 |
|
$node, |
253
|
45 |
|
$group, |
254
|
|
|
$methods |
255
|
45 |
|
); |
256
|
45 |
|
}; |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
/** |
260
|
|
|
* @return Group|GroupRef |
261
|
|
|
*/ |
262
|
45 |
|
private static function loadGroupBeforeCheckingChildNodes( |
263
|
|
|
Schema $schema, |
264
|
|
|
DOMElement $node |
265
|
|
|
) { |
266
|
45 |
|
$group = new Group($schema, $node->getAttribute('name')); |
267
|
45 |
|
$group->setDoc(self::getDocumentation($node)); |
268
|
|
|
|
269
|
45 |
|
if ($node->hasAttribute('maxOccurs')) { |
270
|
|
|
/** |
271
|
|
|
* @var GroupRef |
272
|
|
|
*/ |
273
|
|
|
$group = self::maybeSetMax(new GroupRef($group), $node); |
274
|
|
|
} |
275
|
45 |
|
if ($node->hasAttribute('minOccurs')) { |
276
|
|
|
/** |
277
|
|
|
* @var GroupRef |
278
|
|
|
*/ |
279
|
|
|
$group = self::maybeSetMin( |
280
|
|
|
$group instanceof GroupRef ? $group : new GroupRef($group), |
281
|
|
|
$node |
282
|
|
|
); |
283
|
|
|
} |
284
|
|
|
|
285
|
45 |
|
$schema->addGroup($group); |
286
|
|
|
|
287
|
45 |
|
return $group; |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
/** |
291
|
|
|
* @return GroupRef |
292
|
|
|
*/ |
293
|
45 |
|
private function loadGroupRef(Group $referenced, DOMElement $node) |
294
|
|
|
{ |
295
|
45 |
|
$ref = new GroupRef($referenced); |
296
|
45 |
|
$ref->setDoc(self::getDocumentation($node)); |
297
|
|
|
|
298
|
45 |
|
self::maybeSetMax($ref, $node); |
299
|
45 |
|
self::maybeSetMin($ref, $node); |
300
|
|
|
|
301
|
45 |
|
return $ref; |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
/** |
305
|
|
|
* @return BaseComplexType |
306
|
|
|
*/ |
307
|
45 |
|
private function loadComplexTypeBeforeCallbackCallback( |
308
|
|
|
Schema $schema, |
309
|
|
|
DOMElement $node |
310
|
|
|
) { |
311
|
|
|
/** |
312
|
|
|
* @var bool |
313
|
|
|
*/ |
314
|
45 |
|
$isSimple = false; |
315
|
|
|
|
316
|
45 |
|
static::againstDOMNodeList( |
317
|
45 |
|
$node, |
318
|
|
|
function ( |
319
|
|
|
DOMElement $node, |
320
|
|
|
DOMElement $childNode |
321
|
|
|
) use ( |
322
|
|
|
&$isSimple |
323
|
|
|
) { |
324
|
45 |
|
if ($isSimple) { |
325
|
1 |
|
return; |
326
|
|
|
} |
327
|
45 |
|
if ($childNode->localName === 'simpleContent') { |
328
|
2 |
|
$isSimple = true; |
329
|
2 |
|
} |
330
|
45 |
|
} |
331
|
45 |
|
); |
332
|
|
|
|
333
|
45 |
|
$type = $isSimple ? new ComplexTypeSimpleContent($schema, $node->getAttribute('name')) : new ComplexType($schema, $node->getAttribute('name')); |
334
|
|
|
|
335
|
45 |
|
$type->setDoc(static::getDocumentation($node)); |
336
|
45 |
|
if ($node->getAttribute('name')) { |
337
|
45 |
|
$schema->addType($type); |
338
|
45 |
|
} |
339
|
|
|
|
340
|
45 |
|
return $type; |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
/** |
344
|
|
|
* @param Closure|null $callback |
345
|
|
|
* |
346
|
|
|
* @return Closure |
347
|
|
|
*/ |
348
|
45 |
|
private function loadComplexType(Schema $schema, DOMElement $node, $callback = null) |
349
|
|
|
{ |
350
|
45 |
|
$type = $this->loadComplexTypeBeforeCallbackCallback($schema, $node); |
351
|
|
|
|
352
|
45 |
|
return $this->makeCallbackCallback( |
353
|
45 |
|
$type, |
354
|
45 |
|
$node, |
355
|
|
|
function ( |
356
|
|
|
DOMElement $node, |
357
|
|
|
DOMElement $childNode |
358
|
|
|
) use ( |
359
|
45 |
|
$schema, |
360
|
45 |
|
$type |
361
|
|
|
) { |
362
|
45 |
|
$this->loadComplexTypeFromChildNode( |
363
|
45 |
|
$type, |
364
|
45 |
|
$node, |
365
|
45 |
|
$childNode, |
366
|
|
|
$schema |
367
|
45 |
|
); |
368
|
45 |
|
}, |
369
|
|
|
$callback |
370
|
45 |
|
); |
371
|
|
|
} |
372
|
|
|
|
373
|
45 |
|
private function loadComplexTypeFromChildNode( |
374
|
|
|
BaseComplexType $type, |
375
|
|
|
DOMElement $node, |
376
|
|
|
DOMElement $childNode, |
377
|
|
|
Schema $schema |
378
|
|
|
) { |
379
|
45 |
|
switch ($childNode->localName) { |
380
|
45 |
|
case 'sequence': |
381
|
45 |
|
case 'choice': |
382
|
45 |
|
case 'all': |
383
|
45 |
|
if ($type instanceof ElementContainer) { |
384
|
45 |
|
$this->loadSequence( |
385
|
45 |
|
$type, |
386
|
|
|
$childNode |
387
|
45 |
|
); |
388
|
45 |
|
} |
389
|
45 |
|
break; |
390
|
45 |
|
case 'attribute': |
391
|
45 |
|
$this->addAttributeFromAttributeOrRef( |
392
|
45 |
|
$type, |
393
|
45 |
|
$childNode, |
394
|
45 |
|
$schema, |
395
|
|
|
$node |
396
|
45 |
|
); |
397
|
45 |
|
break; |
398
|
45 |
|
case 'attributeGroup': |
399
|
2 |
|
$this->findSomethingLikeAttributeGroup( |
400
|
2 |
|
$schema, |
401
|
2 |
|
$node, |
402
|
2 |
|
$childNode, |
403
|
|
|
$type |
404
|
2 |
|
); |
405
|
2 |
|
break; |
406
|
45 |
|
case 'group': |
407
|
|
|
if ( |
408
|
|
|
$type instanceof ComplexType |
409
|
1 |
|
) { |
410
|
1 |
|
$this->addGroupAsElement( |
411
|
1 |
|
$schema, |
412
|
1 |
|
$node, |
413
|
1 |
|
$childNode, |
414
|
|
|
$type |
415
|
1 |
|
); |
416
|
1 |
|
} |
417
|
1 |
|
break; |
418
|
45 |
|
} |
419
|
45 |
|
} |
420
|
|
|
|
421
|
|
|
/** |
422
|
|
|
* @param Closure|null $callback |
423
|
|
|
* |
424
|
|
|
* @return Closure |
425
|
|
|
*/ |
426
|
45 |
|
private function loadSimpleType(Schema $schema, DOMElement $node, $callback = null) |
427
|
|
|
{ |
428
|
45 |
|
$type = new SimpleType($schema, $node->getAttribute('name')); |
429
|
45 |
|
$type->setDoc(static::getDocumentation($node)); |
430
|
45 |
|
if ($node->getAttribute('name')) { |
431
|
45 |
|
$schema->addType($type); |
432
|
45 |
|
} |
433
|
|
|
|
434
|
45 |
|
return $this->makeCallbackCallback( |
435
|
45 |
|
$type, |
436
|
45 |
|
$node, |
437
|
45 |
|
$this->CallbackGeneratorMaybeCallMethodAgainstDOMNodeList( |
438
|
45 |
|
$type, |
439
|
|
|
[ |
440
|
45 |
|
'union' => 'loadUnion', |
441
|
45 |
|
'list' => 'loadList', |
442
|
|
|
] |
443
|
45 |
|
), |
444
|
|
|
$callback |
445
|
45 |
|
); |
446
|
|
|
} |
447
|
|
|
|
448
|
45 |
|
private function loadList(SimpleType $type, DOMElement $node) |
449
|
|
|
{ |
450
|
45 |
|
if ($node->hasAttribute('itemType')) { |
451
|
|
|
/** |
452
|
|
|
* @var SimpleType |
453
|
|
|
*/ |
454
|
45 |
|
$listType = $this->findSomeType($type, $node, 'itemType'); |
455
|
45 |
|
$type->setList($listType); |
456
|
45 |
|
} else { |
457
|
|
|
$addCallback = function (SimpleType $list) use ($type) { |
458
|
45 |
|
$type->setList($list); |
459
|
45 |
|
}; |
460
|
|
|
|
461
|
45 |
|
$this->loadTypeWithCallbackOnChildNodes( |
462
|
45 |
|
$type->getSchema(), |
463
|
45 |
|
$node, |
464
|
|
|
$addCallback |
465
|
45 |
|
); |
466
|
|
|
} |
467
|
45 |
|
} |
468
|
|
|
|
469
|
45 |
|
private function loadUnion(SimpleType $type, DOMElement $node) |
470
|
|
|
{ |
471
|
45 |
|
if ($node->hasAttribute('memberTypes')) { |
472
|
45 |
|
$types = preg_split('/\s+/', $node->getAttribute('memberTypes')); |
473
|
45 |
|
foreach ($types as $typeName) { |
474
|
|
|
/** |
475
|
|
|
* @var SimpleType |
476
|
|
|
*/ |
477
|
45 |
|
$unionType = $this->findSomeTypeFromAttribute( |
478
|
45 |
|
$type, |
479
|
45 |
|
$node, |
480
|
|
|
$typeName |
481
|
45 |
|
); |
482
|
45 |
|
$type->addUnion($unionType); |
483
|
45 |
|
} |
484
|
45 |
|
} |
485
|
|
|
$addCallback = function (SimpleType $unType) use ($type) { |
486
|
45 |
|
$type->addUnion($unType); |
487
|
45 |
|
}; |
488
|
|
|
|
489
|
45 |
|
$this->loadTypeWithCallbackOnChildNodes( |
490
|
45 |
|
$type->getSchema(), |
491
|
45 |
|
$node, |
492
|
|
|
$addCallback |
493
|
45 |
|
); |
494
|
45 |
|
} |
495
|
|
|
|
496
|
45 |
|
private function loadExtensionChildNodes( |
497
|
|
|
BaseComplexType $type, |
498
|
|
|
DOMElement $node |
499
|
|
|
) { |
500
|
45 |
|
static::againstDOMNodeList( |
501
|
45 |
|
$node, |
502
|
|
View Code Duplication |
function ( |
|
|
|
|
503
|
|
|
DOMElement $node, |
504
|
|
|
DOMElement $childNode |
505
|
|
|
) use ( |
506
|
45 |
|
$type |
507
|
|
|
) { |
508
|
45 |
|
switch ($childNode->localName) { |
509
|
45 |
|
case 'sequence': |
510
|
45 |
|
case 'choice': |
511
|
45 |
|
case 'all': |
512
|
45 |
|
if ($type instanceof ElementContainer) { |
513
|
45 |
|
$this->loadSequence( |
514
|
45 |
|
$type, |
515
|
|
|
$childNode |
516
|
45 |
|
); |
517
|
45 |
|
} |
518
|
45 |
|
break; |
519
|
45 |
|
case 'attribute': |
520
|
45 |
|
$this->addAttributeFromAttributeOrRef( |
521
|
45 |
|
$type, |
522
|
45 |
|
$childNode, |
523
|
45 |
|
$type->getSchema(), |
524
|
|
|
$node |
525
|
45 |
|
); |
526
|
45 |
|
break; |
527
|
45 |
|
case 'attributeGroup': |
528
|
45 |
|
$this->findSomethingLikeAttributeGroup( |
529
|
45 |
|
$type->getSchema(), |
530
|
45 |
|
$node, |
531
|
45 |
|
$childNode, |
532
|
|
|
$type |
533
|
45 |
|
); |
534
|
45 |
|
break; |
535
|
45 |
|
} |
536
|
45 |
|
} |
537
|
45 |
|
); |
538
|
45 |
|
} |
539
|
|
|
|
540
|
45 |
|
private function loadExtension(BaseComplexType $type, DOMElement $node) |
541
|
|
|
{ |
542
|
45 |
|
$extension = new Extension(); |
543
|
45 |
|
$type->setExtension($extension); |
544
|
|
|
|
545
|
45 |
|
if ($node->hasAttribute('base')) { |
546
|
45 |
|
$this->findAndSetSomeBase( |
547
|
45 |
|
$type, |
548
|
45 |
|
$extension, |
549
|
|
|
$node |
550
|
45 |
|
); |
551
|
45 |
|
} |
552
|
45 |
|
$this->loadExtensionChildNodes($type, $node); |
553
|
45 |
|
} |
554
|
|
|
|
555
|
45 |
|
private function loadRestriction(Type $type, DOMElement $node) |
556
|
|
|
{ |
557
|
45 |
|
$restriction = new Restriction(); |
558
|
45 |
|
$type->setRestriction($restriction); |
559
|
45 |
|
if ($node->hasAttribute('base')) { |
560
|
45 |
|
$this->findAndSetSomeBase($type, $restriction, $node); |
561
|
45 |
|
} else { |
562
|
|
|
$addCallback = function (Type $restType) use ($restriction) { |
563
|
45 |
|
$restriction->setBase($restType); |
564
|
45 |
|
}; |
565
|
|
|
|
566
|
45 |
|
$this->loadTypeWithCallbackOnChildNodes( |
567
|
45 |
|
$type->getSchema(), |
568
|
45 |
|
$node, |
569
|
|
|
$addCallback |
570
|
45 |
|
); |
571
|
|
|
} |
572
|
45 |
|
self::againstDOMNodeList( |
573
|
45 |
|
$node, |
574
|
|
|
function ( |
575
|
|
|
DOMElement $node, |
576
|
|
|
DOMElement $childNode |
577
|
|
|
) use ( |
578
|
45 |
|
$restriction |
579
|
|
|
) { |
580
|
|
|
if ( |
581
|
45 |
|
in_array( |
582
|
45 |
|
$childNode->localName, |
583
|
|
|
[ |
584
|
45 |
|
'enumeration', |
585
|
45 |
|
'pattern', |
586
|
45 |
|
'length', |
587
|
45 |
|
'minLength', |
588
|
45 |
|
'maxLength', |
589
|
45 |
|
'minInclusive', |
590
|
45 |
|
'maxInclusive', |
591
|
45 |
|
'minExclusive', |
592
|
45 |
|
'maxExclusive', |
593
|
45 |
|
'fractionDigits', |
594
|
45 |
|
'totalDigits', |
595
|
45 |
|
'whiteSpace', |
596
|
45 |
|
], |
597
|
|
|
true |
598
|
45 |
|
) |
599
|
45 |
|
) { |
600
|
45 |
|
$restriction->addCheck( |
601
|
45 |
|
$childNode->localName, |
602
|
|
|
[ |
603
|
45 |
|
'value' => $childNode->getAttribute('value'), |
604
|
45 |
|
'doc' => self::getDocumentation($childNode), |
605
|
|
|
] |
606
|
45 |
|
); |
607
|
45 |
|
} |
608
|
45 |
|
} |
609
|
45 |
|
); |
610
|
45 |
|
} |
611
|
|
|
|
612
|
|
|
/** |
613
|
|
|
* @return Closure |
614
|
|
|
*/ |
615
|
45 |
|
private function loadElementDef(Schema $schema, DOMElement $node) |
616
|
|
|
{ |
617
|
45 |
|
return $this->loadAttributeOrElementDef($schema, $node, false); |
618
|
|
|
} |
619
|
|
|
|
620
|
|
|
/** |
621
|
|
|
* @param bool $checkAbstract |
622
|
|
|
*/ |
623
|
45 |
|
private function fillTypeNode(Type $type, DOMElement $node, $checkAbstract = false) |
624
|
|
|
{ |
625
|
45 |
|
if ($checkAbstract) { |
626
|
45 |
|
$type->setAbstract($node->getAttribute('abstract') === 'true' || $node->getAttribute('abstract') === '1'); |
627
|
45 |
|
} |
628
|
|
|
static $methods = [ |
629
|
|
|
'restriction' => 'loadRestriction', |
630
|
|
|
'extension' => 'maybeLoadExtensionFromBaseComplexType', |
631
|
|
|
'simpleContent' => 'fillTypeNode', |
632
|
|
|
'complexContent' => 'fillTypeNode', |
633
|
45 |
|
]; |
634
|
|
|
|
635
|
|
|
/** |
636
|
|
|
* @var string[] |
637
|
|
|
*/ |
638
|
45 |
|
$methods = $methods; |
639
|
|
|
|
640
|
45 |
|
$this->maybeCallMethodAgainstDOMNodeList($node, $type, $methods); |
641
|
45 |
|
} |
642
|
|
|
|
643
|
45 |
|
private function fillItemNonLocalType(Item $element, DOMElement $node) |
644
|
|
|
{ |
645
|
45 |
|
if ($node->getAttribute('type')) { |
646
|
|
|
/** |
647
|
|
|
* @var Type |
648
|
|
|
*/ |
649
|
45 |
|
$type = $this->findSomeType($element, $node, 'type'); |
650
|
45 |
|
} else { |
651
|
|
|
/** |
652
|
|
|
* @var Type |
653
|
|
|
*/ |
654
|
45 |
|
$type = $this->findSomeTypeFromAttribute( |
655
|
45 |
|
$element, |
656
|
45 |
|
$node, |
657
|
45 |
|
($node->lookupPrefix(self::XSD_NS).':anyType') |
658
|
45 |
|
); |
659
|
|
|
} |
660
|
|
|
|
661
|
45 |
|
$element->setType($type); |
662
|
45 |
|
} |
663
|
|
|
|
664
|
|
|
/** |
665
|
|
|
* @param string $attributeName |
666
|
|
|
* |
667
|
|
|
* @return SchemaItem |
668
|
|
|
*/ |
669
|
45 |
|
private function findSomeType( |
670
|
|
|
SchemaItem $fromThis, |
671
|
|
|
DOMElement $node, |
672
|
|
|
$attributeName |
673
|
|
|
) { |
674
|
45 |
|
return $this->findSomeTypeFromAttribute( |
675
|
45 |
|
$fromThis, |
676
|
45 |
|
$node, |
677
|
45 |
|
$node->getAttribute($attributeName) |
678
|
45 |
|
); |
679
|
|
|
} |
680
|
|
|
|
681
|
|
|
/** |
682
|
|
|
* @param string $attributeName |
683
|
|
|
* |
684
|
|
|
* @return SchemaItem |
685
|
|
|
*/ |
686
|
45 |
|
private function findSomeTypeFromAttribute( |
687
|
|
|
SchemaItem $fromThis, |
688
|
|
|
DOMElement $node, |
689
|
|
|
$attributeName |
690
|
|
|
) { |
691
|
|
|
/** |
692
|
|
|
* @var SchemaItem |
693
|
|
|
*/ |
694
|
45 |
|
$out = $this->findSomething( |
695
|
45 |
|
'findType', |
696
|
45 |
|
$fromThis->getSchema(), |
697
|
45 |
|
$node, |
698
|
|
|
$attributeName |
699
|
45 |
|
); |
700
|
|
|
|
701
|
45 |
|
return $out; |
702
|
|
|
} |
703
|
|
|
|
704
|
|
|
/** |
705
|
|
|
* @param Closure|null $callback |
706
|
|
|
* |
707
|
|
|
* @return Closure |
708
|
|
|
*/ |
709
|
45 |
|
private function makeCallbackCallback( |
710
|
|
|
Type $type, |
711
|
|
|
DOMElement $node, |
712
|
|
|
Closure $callbackCallback, |
713
|
|
|
$callback = null |
714
|
|
|
) { |
715
|
|
|
return function ( |
716
|
|
|
) use ( |
717
|
45 |
|
$type, |
718
|
45 |
|
$node, |
719
|
45 |
|
$callbackCallback, |
720
|
45 |
|
$callback |
721
|
|
|
) { |
722
|
45 |
|
$this->runCallbackAgainstDOMNodeList( |
723
|
45 |
|
$type, |
724
|
45 |
|
$node, |
725
|
45 |
|
$callbackCallback, |
726
|
|
|
$callback |
727
|
45 |
|
); |
728
|
45 |
|
}; |
729
|
|
|
} |
730
|
|
|
|
731
|
|
|
/** |
732
|
|
|
* @param Closure|null $callback |
733
|
|
|
*/ |
734
|
45 |
|
private function runCallbackAgainstDOMNodeList( |
735
|
|
|
Type $type, |
736
|
|
|
DOMElement $node, |
737
|
|
|
Closure $againstNodeList, |
738
|
|
|
$callback = null |
739
|
|
|
) { |
740
|
45 |
|
$this->fillTypeNode($type, $node, true); |
741
|
|
|
|
742
|
45 |
|
static::againstDOMNodeList($node, $againstNodeList); |
743
|
|
|
|
744
|
45 |
|
if ($callback) { |
745
|
45 |
|
call_user_func($callback, $type); |
746
|
45 |
|
} |
747
|
45 |
|
} |
748
|
|
|
|
749
|
45 |
|
private function maybeLoadExtensionFromBaseComplexType( |
750
|
|
|
Type $type, |
751
|
|
|
DOMElement $childNode |
752
|
|
|
) { |
753
|
45 |
|
if ($type instanceof BaseComplexType) { |
754
|
45 |
|
$this->loadExtension($type, $childNode); |
755
|
45 |
|
} |
756
|
45 |
|
} |
757
|
|
|
|
758
|
|
|
const XSD_NS = 'http://www.w3.org/2001/XMLSchema'; |
759
|
|
|
|
760
|
|
|
const XML_NS = 'http://www.w3.org/XML/1998/namespace'; |
761
|
|
|
|
762
|
|
|
/** |
763
|
|
|
* @var string[] |
764
|
|
|
*/ |
765
|
|
|
protected $knownLocationSchemas = [ |
766
|
|
|
'http://www.w3.org/2001/xml.xsd' => ( |
767
|
|
|
__DIR__.'/Resources/xml.xsd' |
768
|
|
|
), |
769
|
|
|
'http://www.w3.org/2001/XMLSchema.xsd' => ( |
770
|
|
|
__DIR__.'/Resources/XMLSchema.xsd' |
771
|
|
|
), |
772
|
|
|
'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' => ( |
773
|
|
|
__DIR__.'/Resources/oasis-200401-wss-wssecurity-secext-1.0.xsd' |
774
|
|
|
), |
775
|
|
|
'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' => ( |
776
|
|
|
__DIR__.'/Resources/oasis-200401-wss-wssecurity-utility-1.0.xsd' |
777
|
|
|
), |
778
|
|
|
'https://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd' => ( |
779
|
|
|
__DIR__.'/Resources/xmldsig-core-schema.xsd' |
780
|
|
|
), |
781
|
|
|
'http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd' => ( |
782
|
|
|
__DIR__.'/Resources/xmldsig-core-schema.xsd' |
783
|
|
|
), |
784
|
|
|
]; |
785
|
|
|
|
786
|
|
|
/** |
787
|
|
|
* @var string[] |
788
|
|
|
*/ |
789
|
|
|
protected static $globalSchemaInfo = array( |
790
|
|
|
self::XML_NS => 'http://www.w3.org/2001/xml.xsd', |
791
|
|
|
self::XSD_NS => 'http://www.w3.org/2001/XMLSchema.xsd', |
792
|
|
|
); |
793
|
|
|
|
794
|
|
|
/** |
795
|
|
|
* @param string $remote |
796
|
|
|
* @param string $local |
797
|
|
|
*/ |
798
|
|
|
public function addKnownSchemaLocation($remote, $local) |
799
|
|
|
{ |
800
|
|
|
$this->knownLocationSchemas[$remote] = $local; |
801
|
|
|
} |
802
|
|
|
|
803
|
|
|
/** |
804
|
|
|
* @param string $remote |
805
|
|
|
* |
806
|
|
|
* @return bool |
807
|
|
|
*/ |
808
|
1 |
|
private function hasKnownSchemaLocation($remote) |
809
|
|
|
{ |
810
|
1 |
|
return isset($this->knownLocationSchemas[$remote]); |
811
|
|
|
} |
812
|
|
|
|
813
|
|
|
/** |
814
|
|
|
* @param string $remote |
815
|
|
|
* |
816
|
|
|
* @return string |
817
|
|
|
*/ |
818
|
|
|
private function getKnownSchemaLocation($remote) |
819
|
|
|
{ |
820
|
|
|
return $this->knownLocationSchemas[$remote]; |
821
|
|
|
} |
822
|
|
|
|
823
|
|
|
/** |
824
|
|
|
* @param DOMElement $node |
825
|
|
|
* |
826
|
|
|
* @return string |
827
|
|
|
*/ |
828
|
45 |
|
private static function getDocumentation(DOMElement $node) |
829
|
|
|
{ |
830
|
45 |
|
$doc = ''; |
831
|
45 |
|
static::againstDOMNodeList( |
832
|
45 |
|
$node, |
833
|
|
|
function ( |
834
|
|
|
DOMElement $node, |
835
|
|
|
DOMElement $childNode |
836
|
|
|
) use ( |
837
|
|
|
&$doc |
838
|
|
|
) { |
839
|
45 |
|
if ($childNode->localName == 'annotation') { |
840
|
45 |
|
$doc .= static::getDocumentation($childNode); |
841
|
45 |
|
} elseif ($childNode->localName == 'documentation') { |
842
|
45 |
|
$doc .= $childNode->nodeValue; |
843
|
45 |
|
} |
844
|
45 |
|
} |
845
|
45 |
|
); |
846
|
45 |
|
$doc = preg_replace('/[\t ]+/', ' ', $doc); |
847
|
|
|
|
848
|
45 |
|
return trim($doc); |
849
|
|
|
} |
850
|
|
|
|
851
|
|
|
/** |
852
|
|
|
* @param string[] $methods |
853
|
|
|
* @param string $key |
854
|
|
|
* |
855
|
|
|
* @return Closure|null |
856
|
|
|
*/ |
857
|
45 |
|
private function maybeCallMethod( |
858
|
|
|
array $methods, |
859
|
|
|
$key, |
860
|
|
|
DOMNode $childNode, |
861
|
|
|
...$args |
862
|
|
|
) { |
863
|
45 |
|
if ($childNode instanceof DOMElement && isset($methods[$key])) { |
864
|
45 |
|
$method = $methods[$key]; |
865
|
|
|
|
866
|
|
|
/** |
867
|
|
|
* @var Closure|null |
868
|
|
|
*/ |
869
|
45 |
|
$append = $this->$method(...$args); |
870
|
|
|
|
871
|
45 |
|
if ($append instanceof Closure) { |
872
|
45 |
|
return $append; |
873
|
|
|
} |
874
|
45 |
|
} |
875
|
45 |
|
} |
876
|
|
|
|
877
|
|
|
/** |
878
|
|
|
* @param Schema $schema |
879
|
|
|
* @param DOMElement $node |
880
|
|
|
* @param Schema $parent |
881
|
|
|
* |
882
|
|
|
* @return Closure[] |
883
|
|
|
*/ |
884
|
45 |
|
private function schemaNode(Schema $schema, DOMElement $node, Schema $parent = null) |
885
|
|
|
{ |
886
|
45 |
|
$this->setSchemaThingsFromNode($schema, $node, $parent); |
887
|
45 |
|
$functions = array(); |
888
|
|
|
|
889
|
45 |
|
static::againstDOMNodeList( |
890
|
45 |
|
$node, |
891
|
|
|
function ( |
892
|
|
|
DOMElement $node, |
893
|
|
|
DOMElement $childNode |
894
|
|
|
) use ( |
895
|
45 |
|
$schema, |
896
|
|
|
&$functions |
897
|
|
|
) { |
898
|
45 |
|
$callback = null; |
899
|
|
|
|
900
|
45 |
|
switch ($childNode->localName) { |
901
|
45 |
|
case 'attributeGroup': |
902
|
45 |
|
$callback = $this->loadAttributeGroup($schema, $childNode); |
903
|
45 |
|
break; |
904
|
45 |
|
case 'include': |
905
|
45 |
|
case 'import': |
906
|
45 |
|
$callback = $this->loadImport($schema, $childNode); |
907
|
45 |
|
break; |
908
|
45 |
|
case 'element': |
909
|
45 |
|
$callback = $this->loadElementDef($schema, $childNode); |
910
|
45 |
|
break; |
911
|
45 |
|
case 'attribute': |
912
|
45 |
|
$callback = $this->loadAttributeDef($schema, $childNode); |
913
|
45 |
|
break; |
914
|
45 |
|
case 'group': |
915
|
45 |
|
$callback = $this->loadGroup($schema, $childNode); |
916
|
45 |
|
break; |
917
|
45 |
|
case 'complexType': |
918
|
45 |
|
$callback = $this->loadComplexType($schema, $childNode); |
919
|
45 |
|
break; |
920
|
45 |
|
case 'simpleType': |
921
|
45 |
|
$callback = $this->loadSimpleType($schema, $childNode); |
922
|
45 |
|
break; |
923
|
45 |
|
} |
924
|
|
|
|
925
|
45 |
|
if ($callback instanceof Closure) { |
926
|
45 |
|
$functions[] = $callback; |
927
|
45 |
|
} |
928
|
45 |
|
} |
929
|
45 |
|
); |
930
|
|
|
|
931
|
45 |
|
return $functions; |
932
|
|
|
} |
933
|
|
|
|
934
|
|
|
/** |
935
|
|
|
* @return InterfaceSetMinMax |
936
|
|
|
*/ |
937
|
45 |
|
private static function maybeSetMax(InterfaceSetMinMax $ref, DOMElement $node) |
938
|
|
|
{ |
939
|
|
|
if ( |
940
|
45 |
|
$node->hasAttribute('maxOccurs') |
941
|
45 |
|
) { |
942
|
45 |
|
$ref->setMax($node->getAttribute('maxOccurs') == 'unbounded' ? -1 : (int) $node->getAttribute('maxOccurs')); |
943
|
45 |
|
} |
944
|
|
|
|
945
|
45 |
|
return $ref; |
946
|
|
|
} |
947
|
|
|
|
948
|
|
|
/** |
949
|
|
|
* @return InterfaceSetMinMax |
950
|
|
|
*/ |
951
|
45 |
|
private static function maybeSetMin(InterfaceSetMinMax $ref, DOMElement $node) |
952
|
|
|
{ |
953
|
45 |
|
if ($node->hasAttribute('minOccurs')) { |
954
|
45 |
|
$ref->setMin((int) $node->getAttribute('minOccurs')); |
955
|
45 |
|
} |
956
|
|
|
|
957
|
45 |
|
return $ref; |
958
|
|
|
} |
959
|
|
|
|
960
|
45 |
|
private function findAndSetSomeBase( |
961
|
|
|
Type $type, |
962
|
|
|
Base $setBaseOnThis, |
963
|
|
|
DOMElement $node |
964
|
|
|
) { |
965
|
|
|
/** |
966
|
|
|
* @var Type |
967
|
|
|
*/ |
968
|
45 |
|
$parent = $this->findSomeType($type, $node, 'base'); |
969
|
45 |
|
$setBaseOnThis->setBase($parent); |
970
|
45 |
|
} |
971
|
|
|
|
972
|
|
|
/** |
973
|
|
|
* @param string $finder |
974
|
|
|
* @param Schema $schema |
975
|
|
|
* @param DOMElement $node |
976
|
|
|
* @param string $typeName |
977
|
|
|
* |
978
|
|
|
* @throws TypeException |
979
|
|
|
* |
980
|
|
|
* @return ElementItem|Group|AttributeItem|AttributeGroup|Type |
981
|
|
|
*/ |
982
|
45 |
|
private function findSomething($finder, Schema $schema, DOMElement $node, $typeName) |
983
|
|
|
{ |
984
|
45 |
|
list($name, $namespace) = static::splitParts($node, $typeName); |
985
|
|
|
|
986
|
|
|
/** |
987
|
|
|
* @var string|null |
988
|
|
|
*/ |
989
|
45 |
|
$namespace = $namespace ?: $schema->getTargetNamespace(); |
990
|
|
|
|
991
|
|
|
try { |
992
|
|
|
/** |
993
|
|
|
* @var ElementItem|Group|AttributeItem|AttributeGroup|Type |
994
|
|
|
*/ |
995
|
45 |
|
$out = $schema->$finder($name, $namespace); |
996
|
|
|
|
997
|
45 |
|
return $out; |
998
|
|
|
} catch (TypeNotFoundException $e) { |
999
|
|
|
throw new TypeException(sprintf("Can't find %s named {%s}#%s, at line %d in %s ", strtolower(substr($finder, 4)), $namespace, $name, $node->getLineNo(), $node->ownerDocument->documentURI), 0, $e); |
1000
|
|
|
} |
1001
|
|
|
} |
1002
|
|
|
|
1003
|
45 |
|
private function fillItem(Item $element, DOMElement $node) |
1004
|
|
|
{ |
1005
|
|
|
/** |
1006
|
|
|
* @var bool |
1007
|
|
|
*/ |
1008
|
45 |
|
$skip = false; |
1009
|
45 |
|
static::againstDOMNodeList( |
1010
|
45 |
|
$node, |
1011
|
|
|
function ( |
1012
|
|
|
DOMElement $node, |
1013
|
|
|
DOMElement $childNode |
1014
|
|
|
) use ( |
1015
|
45 |
|
$element, |
1016
|
|
|
&$skip |
1017
|
|
|
) { |
1018
|
|
|
if ( |
1019
|
45 |
|
!$skip && |
1020
|
45 |
|
in_array( |
1021
|
45 |
|
$childNode->localName, |
1022
|
|
|
[ |
1023
|
45 |
|
'complexType', |
1024
|
45 |
|
'simpleType', |
1025
|
|
|
] |
1026
|
45 |
|
) |
1027
|
45 |
|
) { |
1028
|
45 |
|
$this->loadTypeWithCallback( |
1029
|
45 |
|
$element->getSchema(), |
1030
|
45 |
|
$childNode, |
1031
|
|
|
function (Type $type) use ($element) { |
1032
|
45 |
|
$element->setType($type); |
1033
|
45 |
|
} |
1034
|
45 |
|
); |
1035
|
45 |
|
$skip = true; |
1036
|
45 |
|
} |
1037
|
45 |
|
} |
1038
|
45 |
|
); |
1039
|
45 |
|
if ($skip) { |
1040
|
45 |
|
return; |
1041
|
|
|
} |
1042
|
45 |
|
$this->fillItemNonLocalType($element, $node); |
1043
|
45 |
|
} |
1044
|
|
|
|
1045
|
|
|
/** |
1046
|
|
|
* @var Schema|null |
1047
|
|
|
*/ |
1048
|
|
|
protected $globalSchema; |
1049
|
|
|
|
1050
|
|
|
/** |
1051
|
|
|
* @return Schema[] |
1052
|
|
|
*/ |
1053
|
45 |
|
private function setupGlobalSchemas(array &$callbacks) |
1054
|
|
|
{ |
1055
|
45 |
|
$globalSchemas = array(); |
1056
|
45 |
|
foreach (self::$globalSchemaInfo as $namespace => $uri) { |
1057
|
45 |
|
self::setLoadedFile( |
1058
|
45 |
|
$uri, |
1059
|
45 |
|
$globalSchemas[$namespace] = $schema = new Schema() |
1060
|
45 |
|
); |
1061
|
45 |
|
if ($namespace === self::XSD_NS) { |
1062
|
45 |
|
$this->globalSchema = $schema; |
1063
|
45 |
|
} |
1064
|
45 |
|
$xml = $this->getDOM($this->knownLocationSchemas[$uri]); |
1065
|
45 |
|
$callbacks = array_merge($callbacks, $this->schemaNode($schema, $xml->documentElement)); |
1066
|
45 |
|
} |
1067
|
|
|
|
1068
|
45 |
|
return $globalSchemas; |
1069
|
|
|
} |
1070
|
|
|
|
1071
|
|
|
/** |
1072
|
|
|
* @return string[] |
1073
|
|
|
*/ |
1074
|
45 |
|
public function getGlobalSchemaInfo() |
1075
|
|
|
{ |
1076
|
45 |
|
return self::$globalSchemaInfo; |
1077
|
|
|
} |
1078
|
|
|
|
1079
|
|
|
/** |
1080
|
|
|
* @return Schema |
1081
|
|
|
*/ |
1082
|
45 |
|
public function getGlobalSchema() |
1083
|
|
|
{ |
1084
|
45 |
|
if (!$this->globalSchema) { |
1085
|
45 |
|
$callbacks = array(); |
1086
|
45 |
|
$globalSchemas = $this->setupGlobalSchemas($callbacks); |
1087
|
|
|
|
1088
|
45 |
|
$globalSchemas[static::XSD_NS]->addType(new SimpleType($globalSchemas[static::XSD_NS], 'anySimpleType')); |
1089
|
45 |
|
$globalSchemas[static::XSD_NS]->addType(new SimpleType($globalSchemas[static::XSD_NS], 'anyType')); |
1090
|
|
|
|
1091
|
45 |
|
$globalSchemas[static::XML_NS]->addSchema( |
1092
|
45 |
|
$globalSchemas[static::XSD_NS], |
1093
|
|
|
(string) static::XSD_NS |
1094
|
45 |
|
); |
1095
|
45 |
|
$globalSchemas[static::XSD_NS]->addSchema( |
1096
|
45 |
|
$globalSchemas[static::XML_NS], |
1097
|
|
|
(string) static::XML_NS |
1098
|
45 |
|
); |
1099
|
|
|
|
1100
|
|
|
/** |
1101
|
|
|
* @var Closure |
1102
|
|
|
*/ |
1103
|
45 |
|
foreach ($callbacks as $callback) { |
1104
|
45 |
|
$callback(); |
1105
|
45 |
|
} |
1106
|
45 |
|
} |
1107
|
|
|
|
1108
|
|
|
/** |
1109
|
|
|
* @var Schema |
1110
|
|
|
*/ |
1111
|
45 |
|
$out = $this->globalSchema; |
1112
|
|
|
|
1113
|
45 |
|
return $out; |
1114
|
|
|
} |
1115
|
|
|
|
1116
|
|
|
/** |
1117
|
|
|
* @param DOMElement $node |
1118
|
|
|
* @param string $file |
1119
|
|
|
* |
1120
|
|
|
* @return Schema |
1121
|
|
|
*/ |
1122
|
45 |
|
private function readNode(DOMElement $node, $file = 'schema.xsd') |
1123
|
|
|
{ |
1124
|
45 |
|
$fileKey = $node->hasAttribute('targetNamespace') ? $this->getNamespaceSpecificFileIndex($file, $node->getAttribute('targetNamespace')) : $file; |
1125
|
45 |
|
self::setLoadedFile($fileKey, $rootSchema = new Schema()); |
1126
|
|
|
|
1127
|
45 |
|
$rootSchema->addSchema($this->getGlobalSchema()); |
1128
|
45 |
|
$callbacks = $this->schemaNode($rootSchema, $node); |
1129
|
|
|
|
1130
|
45 |
|
foreach ($callbacks as $callback) { |
1131
|
39 |
|
call_user_func($callback); |
1132
|
45 |
|
} |
1133
|
|
|
|
1134
|
45 |
|
return $rootSchema; |
1135
|
|
|
} |
1136
|
|
|
|
1137
|
|
|
/** |
1138
|
|
|
* It is possible that a single file contains multiple <xsd:schema/> nodes, for instance in a WSDL file. |
1139
|
|
|
* |
1140
|
|
|
* Each of these <xsd:schema/> nodes typically target a specific namespace. Append the target namespace to the |
1141
|
|
|
* file to distinguish between multiple schemas in a single file. |
1142
|
|
|
* |
1143
|
|
|
* @param string $file |
1144
|
|
|
* @param string $targetNamespace |
1145
|
|
|
* |
1146
|
|
|
* @return string |
1147
|
|
|
*/ |
1148
|
45 |
|
private function getNamespaceSpecificFileIndex($file, $targetNamespace) |
1149
|
|
|
{ |
1150
|
45 |
|
return $file.'#'.$targetNamespace; |
1151
|
|
|
} |
1152
|
|
|
|
1153
|
|
|
/** |
1154
|
|
|
* @param string $content |
1155
|
|
|
* @param string $file |
1156
|
|
|
* |
1157
|
|
|
* @return Schema |
1158
|
|
|
* |
1159
|
|
|
* @throws IOException |
1160
|
|
|
*/ |
1161
|
44 |
|
public function readString($content, $file = 'schema.xsd') |
1162
|
|
|
{ |
1163
|
44 |
|
$xml = new DOMDocument('1.0', 'UTF-8'); |
1164
|
44 |
|
if (!$xml->loadXML($content)) { |
1165
|
|
|
throw new IOException("Can't load the schema"); |
1166
|
|
|
} |
1167
|
44 |
|
$xml->documentURI = $file; |
1168
|
|
|
|
1169
|
44 |
|
return $this->readNode($xml->documentElement, $file); |
1170
|
|
|
} |
1171
|
|
|
|
1172
|
|
|
/** |
1173
|
|
|
* @param string $file |
1174
|
|
|
* |
1175
|
|
|
* @return Schema |
1176
|
|
|
*/ |
1177
|
1 |
|
public function readFile($file) |
1178
|
|
|
{ |
1179
|
1 |
|
$xml = $this->getDOM($file); |
1180
|
|
|
|
1181
|
1 |
|
return $this->readNode($xml->documentElement, $file); |
1182
|
|
|
} |
1183
|
|
|
|
1184
|
|
|
/** |
1185
|
|
|
* @param string $file |
1186
|
|
|
* |
1187
|
|
|
* @return DOMDocument |
1188
|
|
|
* |
1189
|
|
|
* @throws IOException |
1190
|
|
|
*/ |
1191
|
45 |
|
private function getDOM($file) |
1192
|
|
|
{ |
1193
|
45 |
|
$xml = new DOMDocument('1.0', 'UTF-8'); |
1194
|
45 |
|
if (!$xml->load($file)) { |
1195
|
|
|
throw new IOException("Can't load the file $file"); |
1196
|
|
|
} |
1197
|
|
|
|
1198
|
45 |
|
return $xml; |
1199
|
|
|
} |
1200
|
|
|
|
1201
|
45 |
|
private static function againstDOMNodeList( |
1202
|
|
|
DOMElement $node, |
1203
|
|
|
Closure $againstNodeList |
1204
|
|
|
) { |
1205
|
45 |
|
$limit = $node->childNodes->length; |
1206
|
45 |
|
for ($i = 0; $i < $limit; $i += 1) { |
1207
|
|
|
/** |
1208
|
|
|
* @var DOMNode |
1209
|
|
|
*/ |
1210
|
45 |
|
$childNode = $node->childNodes->item($i); |
1211
|
|
|
|
1212
|
45 |
|
if ($childNode instanceof DOMElement) { |
1213
|
45 |
|
$againstNodeList( |
1214
|
45 |
|
$node, |
1215
|
|
|
$childNode |
1216
|
45 |
|
); |
1217
|
45 |
|
} |
1218
|
45 |
|
} |
1219
|
45 |
|
} |
1220
|
|
|
|
1221
|
45 |
|
private function maybeCallMethodAgainstDOMNodeList( |
1222
|
|
|
DOMElement $node, |
1223
|
|
|
SchemaItem $type, |
1224
|
|
|
array $methods |
1225
|
|
|
) { |
1226
|
45 |
|
static::againstDOMNodeList( |
1227
|
45 |
|
$node, |
1228
|
45 |
|
$this->CallbackGeneratorMaybeCallMethodAgainstDOMNodeList( |
1229
|
45 |
|
$type, |
1230
|
|
|
$methods |
1231
|
45 |
|
) |
1232
|
45 |
|
); |
1233
|
45 |
|
} |
1234
|
|
|
|
1235
|
|
|
/** |
1236
|
|
|
* @return Closure |
1237
|
|
|
*/ |
1238
|
45 |
|
private function CallbackGeneratorMaybeCallMethodAgainstDOMNodeList( |
1239
|
|
|
SchemaItem $type, |
1240
|
|
|
array $methods |
1241
|
|
|
) { |
1242
|
|
|
return function ( |
1243
|
|
|
DOMElement $node, |
1244
|
|
|
DOMElement $childNode |
1245
|
|
|
) use ( |
1246
|
45 |
|
$methods, |
1247
|
45 |
|
$type |
1248
|
|
|
) { |
1249
|
|
|
/** |
1250
|
|
|
* @var string[] |
1251
|
|
|
*/ |
1252
|
45 |
|
$methods = $methods; |
1253
|
|
|
|
1254
|
45 |
|
$this->maybeCallMethod( |
1255
|
45 |
|
$methods, |
1256
|
45 |
|
$childNode->localName, |
1257
|
45 |
|
$childNode, |
1258
|
45 |
|
$type, |
1259
|
|
|
$childNode |
1260
|
45 |
|
); |
1261
|
45 |
|
}; |
1262
|
|
|
} |
1263
|
|
|
|
1264
|
45 |
|
private function loadTypeWithCallbackOnChildNodes( |
1265
|
|
|
Schema $schema, |
1266
|
|
|
DOMElement $node, |
1267
|
|
|
Closure $callback |
1268
|
|
|
) { |
1269
|
45 |
|
self::againstDOMNodeList( |
1270
|
45 |
|
$node, |
1271
|
|
|
function ( |
1272
|
|
|
DOMElement $node, |
1273
|
|
|
DOMElement $childNode |
1274
|
|
|
) use ( |
1275
|
45 |
|
$schema, |
1276
|
45 |
|
$callback |
1277
|
|
|
) { |
1278
|
45 |
|
$this->loadTypeWithCallback( |
1279
|
45 |
|
$schema, |
1280
|
45 |
|
$childNode, |
1281
|
|
|
$callback |
1282
|
45 |
|
); |
1283
|
45 |
|
} |
1284
|
45 |
|
); |
1285
|
45 |
|
} |
1286
|
|
|
|
1287
|
45 |
|
private function loadTypeWithCallback( |
1288
|
|
|
Schema $schema, |
1289
|
|
|
DOMElement $childNode, |
1290
|
|
|
Closure $callback |
1291
|
|
|
) { |
1292
|
|
|
$methods = [ |
1293
|
45 |
|
'complexType' => 'loadComplexType', |
1294
|
45 |
|
'simpleType' => 'loadSimpleType', |
1295
|
45 |
|
]; |
1296
|
|
|
|
1297
|
|
|
/** |
1298
|
|
|
* @var Closure|null |
1299
|
|
|
*/ |
1300
|
45 |
|
$func = $this->maybeCallMethod( |
1301
|
45 |
|
$methods, |
1302
|
45 |
|
$childNode->localName, |
1303
|
45 |
|
$childNode, |
1304
|
45 |
|
$schema, |
1305
|
45 |
|
$childNode, |
1306
|
|
|
$callback |
1307
|
45 |
|
); |
1308
|
|
|
|
1309
|
45 |
|
if ($func instanceof Closure) { |
1310
|
45 |
|
call_user_func($func); |
1311
|
45 |
|
} |
1312
|
45 |
|
} |
1313
|
|
|
|
1314
|
|
|
/** |
1315
|
|
|
* @param string $file |
1316
|
|
|
* @param string $namespace |
1317
|
|
|
* |
1318
|
|
|
* @return Closure |
1319
|
|
|
*/ |
1320
|
45 |
|
private function loadImport( |
1321
|
|
|
Schema $schema, |
1322
|
|
|
DOMElement $node |
1323
|
|
|
) { |
1324
|
45 |
|
$base = urldecode($node->ownerDocument->documentURI); |
1325
|
45 |
|
$file = UrlUtils::resolveRelativeUrl($base, $node->getAttribute('schemaLocation')); |
1326
|
|
|
|
1327
|
45 |
|
$namespace = $node->getAttribute('namespace'); |
1328
|
|
|
|
1329
|
45 |
|
$keys = $this->loadImportFreshKeys($namespace, $file); |
1330
|
|
|
|
1331
|
|
|
if ( |
1332
|
45 |
|
self::hasLoadedFile(...$keys) |
1333
|
45 |
|
) { |
1334
|
45 |
|
$schema->addSchema(self::getLoadedFile(...$keys)); |
1335
|
|
|
|
1336
|
|
|
return function () { |
1337
|
45 |
|
}; |
1338
|
|
|
} |
1339
|
|
|
|
1340
|
1 |
|
return $this->loadImportFresh($namespace, $schema, $file); |
1341
|
|
|
} |
1342
|
|
|
|
1343
|
|
|
/** |
1344
|
|
|
* @param string $namespace |
1345
|
|
|
* @param string $file |
1346
|
|
|
* |
1347
|
|
|
* @return mixed[] |
1348
|
|
|
*/ |
1349
|
45 |
|
private function loadImportFreshKeys( |
1350
|
|
|
$namespace, |
1351
|
|
|
$file |
1352
|
|
|
) { |
1353
|
45 |
|
$globalSchemaInfo = $this->getGlobalSchemaInfo(); |
1354
|
|
|
|
1355
|
45 |
|
$keys = []; |
1356
|
|
|
|
1357
|
45 |
|
if (isset($globalSchemaInfo[$namespace])) { |
1358
|
45 |
|
$keys[] = $globalSchemaInfo[$namespace]; |
1359
|
45 |
|
} |
1360
|
|
|
|
1361
|
45 |
|
$keys[] = $this->getNamespaceSpecificFileIndex( |
1362
|
45 |
|
$file, |
1363
|
|
|
$namespace |
1364
|
45 |
|
); |
1365
|
|
|
|
1366
|
45 |
|
$keys[] = $file; |
1367
|
|
|
|
1368
|
45 |
|
return $keys; |
1369
|
|
|
} |
1370
|
|
|
|
1371
|
|
|
/** |
1372
|
|
|
* @param string $namespace |
1373
|
|
|
* @param string $file |
1374
|
|
|
* |
1375
|
|
|
* @return Schema |
1376
|
|
|
*/ |
1377
|
1 |
|
private function loadImportFreshCallbacksNewSchema( |
1378
|
|
|
$namespace, |
1379
|
|
|
Schema $schema, |
1380
|
|
|
$file |
1381
|
|
|
) { |
1382
|
|
|
/** |
1383
|
|
|
* @var Schema $newSchema |
1384
|
|
|
*/ |
1385
|
1 |
|
$newSchema = self::setLoadedFile( |
1386
|
1 |
|
$file, |
1387
|
1 |
|
($namespace ? new Schema() : $schema) |
1388
|
1 |
|
); |
1389
|
|
|
|
1390
|
1 |
|
if ($namespace) { |
1391
|
|
|
$newSchema->addSchema($this->getGlobalSchema()); |
1392
|
|
|
$schema->addSchema($newSchema); |
1393
|
|
|
} |
1394
|
|
|
|
1395
|
1 |
|
return $newSchema; |
1396
|
|
|
} |
1397
|
|
|
|
1398
|
|
|
/** |
1399
|
|
|
* @param string $namespace |
1400
|
|
|
* @param string $file |
1401
|
|
|
* |
1402
|
|
|
* @return Closure[] |
1403
|
|
|
*/ |
1404
|
1 |
|
private function loadImportFreshCallbacks( |
1405
|
|
|
$namespace, |
1406
|
|
|
Schema $schema, |
1407
|
|
|
$file |
1408
|
|
|
) { |
1409
|
|
|
/** |
1410
|
|
|
* @var string |
1411
|
|
|
*/ |
1412
|
1 |
|
$file = $file; |
1413
|
|
|
|
1414
|
1 |
|
return $this->schemaNode( |
1415
|
1 |
|
$this->loadImportFreshCallbacksNewSchema( |
1416
|
1 |
|
$namespace, |
1417
|
1 |
|
$schema, |
1418
|
|
|
$file |
1419
|
1 |
|
), |
1420
|
1 |
|
$this->getDOM( |
1421
|
1 |
|
$this->hasKnownSchemaLocation($file) |
1422
|
1 |
|
? $this->getKnownSchemaLocation($file) |
1423
|
|
|
: $file |
1424
|
1 |
|
)->documentElement, |
1425
|
|
|
$schema |
1426
|
1 |
|
); |
1427
|
|
|
} |
1428
|
|
|
|
1429
|
|
|
/** |
1430
|
|
|
* @param string $namespace |
1431
|
|
|
* @param string $file |
1432
|
|
|
* |
1433
|
|
|
* @return Closure |
1434
|
|
|
*/ |
1435
|
1 |
|
private function loadImportFresh( |
1436
|
|
|
$namespace, |
1437
|
|
|
Schema $schema, |
1438
|
|
|
$file |
1439
|
|
|
) { |
1440
|
|
|
return function () use ($namespace, $schema, $file) { |
1441
|
|
|
foreach ( |
1442
|
1 |
|
$this->loadImportFreshCallbacks( |
1443
|
1 |
|
$namespace, |
1444
|
1 |
|
$schema, |
1445
|
|
|
$file |
1446
|
1 |
|
) as $callback |
1447
|
1 |
|
) { |
1448
|
1 |
|
$callback(); |
1449
|
1 |
|
} |
1450
|
1 |
|
}; |
1451
|
|
|
} |
1452
|
|
|
|
1453
|
|
|
/** |
1454
|
|
|
* @return Element |
1455
|
|
|
*/ |
1456
|
45 |
|
private function loadElement( |
1457
|
|
|
Schema $schema, |
1458
|
|
|
DOMElement $node |
1459
|
|
|
) { |
1460
|
45 |
|
$element = new Element($schema, $node->getAttribute('name')); |
1461
|
45 |
|
$element->setDoc(self::getDocumentation($node)); |
1462
|
|
|
|
1463
|
45 |
|
$this->fillItem($element, $node); |
1464
|
|
|
|
1465
|
45 |
|
self::maybeSetMax($element, $node); |
1466
|
45 |
|
self::maybeSetMin($element, $node); |
1467
|
|
|
|
1468
|
45 |
|
$xp = new \DOMXPath($node->ownerDocument); |
1469
|
45 |
|
$xp->registerNamespace('xs', 'http://www.w3.org/2001/XMLSchema'); |
1470
|
|
|
|
1471
|
45 |
|
if ($xp->query('ancestor::xs:choice', $node)->length) { |
1472
|
45 |
|
$element->setMin(0); |
1473
|
45 |
|
} |
1474
|
|
|
|
1475
|
45 |
|
if ($node->hasAttribute('nillable')) { |
1476
|
3 |
|
$element->setNil($node->getAttribute('nillable') == 'true'); |
1477
|
3 |
|
} |
1478
|
45 |
|
if ($node->hasAttribute('form')) { |
1479
|
3 |
|
$element->setQualified($node->getAttribute('form') == 'qualified'); |
1480
|
3 |
|
} |
1481
|
|
|
|
1482
|
45 |
|
return $element; |
1483
|
|
|
} |
1484
|
|
|
|
1485
|
|
|
/** |
1486
|
|
|
* @return ElementRef |
1487
|
|
|
*/ |
1488
|
45 |
|
private static function loadElementRef( |
1489
|
|
|
ElementDef $referenced, |
1490
|
|
|
DOMElement $node |
1491
|
|
|
) { |
1492
|
45 |
|
$ref = new ElementRef($referenced); |
1493
|
45 |
|
$ref->setDoc(self::getDocumentation($node)); |
1494
|
|
|
|
1495
|
45 |
|
self::maybeSetMax($ref, $node); |
1496
|
45 |
|
self::maybeSetMin($ref, $node); |
1497
|
45 |
|
if ($node->hasAttribute('nillable')) { |
1498
|
|
|
$ref->setNil($node->getAttribute('nillable') == 'true'); |
1499
|
|
|
} |
1500
|
45 |
|
if ($node->hasAttribute('form')) { |
1501
|
|
|
$ref->setQualified($node->getAttribute('form') == 'qualified'); |
1502
|
|
|
} |
1503
|
|
|
|
1504
|
45 |
|
return $ref; |
1505
|
|
|
} |
1506
|
|
|
|
1507
|
|
|
/** |
1508
|
|
|
* @return \Closure |
1509
|
|
|
*/ |
1510
|
45 |
|
private function loadAttributeGroup( |
1511
|
|
|
Schema $schema, |
1512
|
|
|
DOMElement $node |
1513
|
|
|
) { |
1514
|
45 |
|
$attGroup = new AttributeGroup($schema, $node->getAttribute('name')); |
1515
|
45 |
|
$attGroup->setDoc(self::getDocumentation($node)); |
1516
|
45 |
|
$schema->addAttributeGroup($attGroup); |
1517
|
|
|
|
1518
|
|
|
return function () use ($schema, $node, $attGroup) { |
1519
|
45 |
|
SchemaReader::againstDOMNodeList( |
1520
|
45 |
|
$node, |
1521
|
45 |
|
function ( |
1522
|
|
|
DOMElement $node, |
1523
|
|
|
DOMElement $childNode |
1524
|
|
|
) use ( |
1525
|
45 |
|
$schema, |
1526
|
45 |
|
$attGroup |
1527
|
|
|
) { |
1528
|
45 |
|
switch ($childNode->localName) { |
1529
|
45 |
|
case 'attribute': |
1530
|
45 |
|
$attribute = $this->getAttributeFromAttributeOrRef( |
1531
|
45 |
|
$childNode, |
1532
|
45 |
|
$schema, |
1533
|
|
|
$node |
1534
|
45 |
|
); |
1535
|
45 |
|
$attGroup->addAttribute($attribute); |
1536
|
45 |
|
break; |
1537
|
45 |
|
case 'attributeGroup': |
1538
|
1 |
|
$this->findSomethingLikeAttributeGroup( |
1539
|
1 |
|
$schema, |
1540
|
1 |
|
$node, |
1541
|
1 |
|
$childNode, |
1542
|
|
|
$attGroup |
1543
|
1 |
|
); |
1544
|
1 |
|
break; |
1545
|
45 |
|
} |
1546
|
45 |
|
} |
1547
|
45 |
|
); |
1548
|
45 |
|
}; |
1549
|
|
|
} |
1550
|
|
|
|
1551
|
|
|
/** |
1552
|
|
|
* @return AttributeItem |
1553
|
|
|
*/ |
1554
|
45 |
|
private function getAttributeFromAttributeOrRef( |
1555
|
|
|
DOMElement $childNode, |
1556
|
|
|
Schema $schema, |
1557
|
|
|
DOMElement $node |
1558
|
|
|
) { |
1559
|
45 |
|
if ($childNode->hasAttribute('ref')) { |
1560
|
|
|
/** |
1561
|
|
|
* @var AttributeItem |
1562
|
|
|
*/ |
1563
|
45 |
|
$attribute = $this->findSomething('findAttribute', $schema, $node, $childNode->getAttribute('ref')); |
1564
|
45 |
|
} else { |
1565
|
|
|
/** |
1566
|
|
|
* @var Attribute |
1567
|
|
|
*/ |
1568
|
45 |
|
$attribute = $this->loadAttribute($schema, $childNode); |
1569
|
|
|
} |
1570
|
|
|
|
1571
|
45 |
|
return $attribute; |
1572
|
|
|
} |
1573
|
|
|
|
1574
|
|
|
/** |
1575
|
|
|
* @return Attribute |
1576
|
|
|
*/ |
1577
|
45 |
|
private function loadAttribute( |
1578
|
|
|
Schema $schema, |
1579
|
|
|
DOMElement $node |
1580
|
|
|
) { |
1581
|
45 |
|
$attribute = new Attribute($schema, $node->getAttribute('name')); |
1582
|
45 |
|
$attribute->setDoc(self::getDocumentation($node)); |
1583
|
45 |
|
$this->fillItem($attribute, $node); |
1584
|
|
|
|
1585
|
45 |
|
if ($node->hasAttribute('nillable')) { |
1586
|
1 |
|
$attribute->setNil($node->getAttribute('nillable') == 'true'); |
1587
|
1 |
|
} |
1588
|
45 |
|
if ($node->hasAttribute('form')) { |
1589
|
1 |
|
$attribute->setQualified($node->getAttribute('form') == 'qualified'); |
1590
|
1 |
|
} |
1591
|
45 |
|
if ($node->hasAttribute('use')) { |
1592
|
45 |
|
$attribute->setUse($node->getAttribute('use')); |
1593
|
45 |
|
} |
1594
|
|
|
|
1595
|
45 |
|
return $attribute; |
1596
|
|
|
} |
1597
|
|
|
|
1598
|
45 |
|
private function addAttributeFromAttributeOrRef( |
1599
|
|
|
BaseComplexType $type, |
1600
|
|
|
DOMElement $childNode, |
1601
|
|
|
Schema $schema, |
1602
|
|
|
DOMElement $node |
1603
|
|
|
) { |
1604
|
45 |
|
$attribute = $this->getAttributeFromAttributeOrRef( |
1605
|
45 |
|
$childNode, |
1606
|
45 |
|
$schema, |
1607
|
|
|
$node |
1608
|
45 |
|
); |
1609
|
|
|
|
1610
|
45 |
|
$type->addAttribute($attribute); |
1611
|
45 |
|
} |
1612
|
|
|
|
1613
|
45 |
|
private function findSomethingLikeAttributeGroup( |
1614
|
|
|
Schema $schema, |
1615
|
|
|
DOMElement $node, |
1616
|
|
|
DOMElement $childNode, |
1617
|
|
|
AttributeContainer $addToThis |
1618
|
|
|
) { |
1619
|
|
|
/** |
1620
|
|
|
* @var AttributeItem |
1621
|
|
|
*/ |
1622
|
45 |
|
$attribute = $this->findSomething('findAttributeGroup', $schema, $node, $childNode->getAttribute('ref')); |
1623
|
45 |
|
$addToThis->addAttribute($attribute); |
1624
|
45 |
|
} |
1625
|
|
|
|
1626
|
|
|
/** |
1627
|
|
|
* @var Schema[] |
1628
|
|
|
*/ |
1629
|
|
|
protected static $loadedFiles = array(); |
1630
|
|
|
|
1631
|
|
|
/** |
1632
|
|
|
* @param string ...$keys |
1633
|
|
|
* |
1634
|
|
|
* @return bool |
1635
|
|
|
*/ |
1636
|
45 |
|
private static function hasLoadedFile(...$keys) |
1637
|
|
|
{ |
1638
|
45 |
|
foreach ($keys as $key) { |
1639
|
45 |
|
if (isset(self::$loadedFiles[$key])) { |
1640
|
45 |
|
return true; |
1641
|
|
|
} |
1642
|
1 |
|
} |
1643
|
|
|
|
1644
|
1 |
|
return false; |
1645
|
|
|
} |
1646
|
|
|
|
1647
|
|
|
/** |
1648
|
|
|
* @param string ...$keys |
1649
|
|
|
* |
1650
|
|
|
* @return Schema |
1651
|
|
|
* |
1652
|
|
|
* @throws RuntimeException if loaded file not found |
1653
|
|
|
*/ |
1654
|
45 |
|
public static function getLoadedFile(...$keys) |
1655
|
|
|
{ |
1656
|
45 |
|
foreach ($keys as $key) { |
1657
|
45 |
|
if (isset(self::$loadedFiles[$key])) { |
1658
|
45 |
|
return self::$loadedFiles[$key]; |
1659
|
|
|
} |
1660
|
|
|
} |
1661
|
|
|
|
1662
|
|
|
throw new RuntimeException('Loaded file was not found!'); |
1663
|
|
|
} |
1664
|
|
|
|
1665
|
|
|
/** |
1666
|
|
|
* @param string $key |
1667
|
|
|
* |
1668
|
|
|
* @return Schema |
1669
|
|
|
*/ |
1670
|
45 |
|
private static function setLoadedFile($key, Schema $schema) |
1671
|
|
|
{ |
1672
|
45 |
|
self::$loadedFiles[$key] = $schema; |
1673
|
|
|
|
1674
|
45 |
|
return $schema; |
1675
|
|
|
} |
1676
|
|
|
|
1677
|
45 |
|
private function setSchemaThingsFromNode( |
1678
|
|
|
Schema $schema, |
1679
|
|
|
DOMElement $node, |
1680
|
|
|
Schema $parent = null |
1681
|
|
|
) { |
1682
|
45 |
|
$schema->setDoc(self::getDocumentation($node)); |
1683
|
|
|
|
1684
|
45 |
|
if ($node->hasAttribute('targetNamespace')) { |
1685
|
45 |
|
$schema->setTargetNamespace($node->getAttribute('targetNamespace')); |
1686
|
45 |
|
} elseif ($parent) { |
1687
|
|
|
$schema->setTargetNamespace($parent->getTargetNamespace()); |
1688
|
|
|
} |
1689
|
45 |
|
$schema->setElementsQualification($node->getAttribute('elementFormDefault') == 'qualified'); |
1690
|
45 |
|
$schema->setAttributesQualification($node->getAttribute('attributeFormDefault') == 'qualified'); |
1691
|
45 |
|
$schema->setDoc(self::getDocumentation($node)); |
1692
|
45 |
|
} |
1693
|
|
|
} |
1694
|
|
|
|
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.