1
|
|
|
<?php |
2
|
|
|
namespace GoetasWebservices\XML\XSDReader; |
3
|
|
|
|
4
|
|
|
use Closure; |
5
|
|
|
use DOMDocument; |
6
|
|
|
use DOMElement; |
7
|
|
|
use DOMNode; |
8
|
|
|
use GoetasWebservices\XML\XSDReader\Exception\IOException; |
9
|
|
|
use GoetasWebservices\XML\XSDReader\Exception\TypeException; |
10
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Attribute; |
11
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeDef; |
12
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem; |
13
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeRef; |
14
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Group as AttributeGroup; |
15
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Element\Element; |
16
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementContainer; |
17
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementDef; |
18
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementItem; |
19
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementRef; |
20
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Element\Group; |
21
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Element\GroupRef; |
22
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Element\InterfaceSetMinMax; |
23
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Exception\TypeNotFoundException; |
24
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Base; |
25
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Extension; |
26
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Restriction; |
27
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Item; |
28
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Schema; |
29
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\SchemaItem; |
30
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Type\BaseComplexType; |
31
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType; |
32
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexTypeSimpleContent; |
33
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Type\SimpleType; |
34
|
|
|
use GoetasWebservices\XML\XSDReader\Schema\Type\Type; |
35
|
|
|
use GoetasWebservices\XML\XSDReader\Utils\UrlUtils; |
36
|
|
|
use RuntimeException; |
37
|
|
|
|
38
|
|
|
class SchemaReader |
39
|
|
|
{ |
40
|
|
|
|
41
|
|
|
const XSD_NS = "http://www.w3.org/2001/XMLSchema"; |
42
|
|
|
|
43
|
|
|
const XML_NS = "http://www.w3.org/XML/1998/namespace"; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* @var Schema[] |
47
|
|
|
*/ |
48
|
|
|
private $loadedFiles = array(); |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @var string[] |
52
|
|
|
*/ |
53
|
|
|
private $knownLocationSchemas = array(); |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* @var string[] |
57
|
|
|
*/ |
58
|
|
|
private static $globalSchemaInfo = array( |
59
|
|
|
self::XML_NS => 'http://www.w3.org/2001/xml.xsd', |
60
|
|
|
self::XSD_NS => 'http://www.w3.org/2001/XMLSchema.xsd' |
61
|
|
|
); |
62
|
|
|
|
63
|
|
|
public function __construct() |
64
|
|
|
{ |
65
|
|
|
$this->addKnownSchemaLocation('http://www.w3.org/2001/xml.xsd', __DIR__ . '/Resources/xml.xsd'); |
66
|
|
|
$this->addKnownSchemaLocation('http://www.w3.org/2001/XMLSchema.xsd', __DIR__ . '/Resources/XMLSchema.xsd'); |
67
|
|
|
$this->addKnownSchemaLocation('http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd', __DIR__ . '/Resources/oasis-200401-wss-wssecurity-secext-1.0.xsd'); |
68
|
|
|
$this->addKnownSchemaLocation('http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd', __DIR__ . '/Resources/oasis-200401-wss-wssecurity-utility-1.0.xsd'); |
69
|
|
|
$this->addKnownSchemaLocation('https://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd', __DIR__ . '/Resources/xmldsig-core-schema.xsd'); |
70
|
|
|
$this->addKnownSchemaLocation('http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd', __DIR__ . '/Resources/xmldsig-core-schema.xsd'); |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* @param string $remote |
75
|
|
|
* @param string $local |
76
|
|
|
*/ |
77
|
|
|
public function addKnownSchemaLocation($remote, $local) |
78
|
|
|
{ |
79
|
|
|
$this->knownLocationSchemas[$remote] = $local; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* @return Closure |
84
|
|
|
*/ |
85
|
|
|
private function loadAttributeGroup(Schema $schema, DOMElement $node) |
86
|
|
|
{ |
87
|
|
|
$attGroup = new AttributeGroup($schema, $node->getAttribute("name")); |
88
|
|
|
$attGroup->setDoc($this->getDocumentation($node)); |
89
|
|
|
$schema->addAttributeGroup($attGroup); |
90
|
|
|
|
91
|
|
|
return function () use ($schema, $node, $attGroup) { |
92
|
|
|
foreach ($node->childNodes as $childNode) { |
93
|
|
|
switch ($childNode->localName) { |
94
|
|
|
case 'attribute': |
95
|
|
|
$attribute = $this->getAttributeFromAttributeOrRef( |
96
|
|
|
$childNode, |
97
|
|
|
$schema, |
98
|
|
|
$node |
99
|
|
|
); |
100
|
|
|
$attGroup->addAttribute($attribute); |
101
|
|
|
break; |
102
|
|
|
case 'attributeGroup': |
103
|
|
|
AttributeGroup::findSomethingLikeThis( |
104
|
|
|
$this, |
105
|
|
|
$schema, |
106
|
|
|
$node, |
107
|
|
|
$childNode, |
108
|
|
|
$attGroup |
109
|
|
|
); |
110
|
|
|
break; |
111
|
|
|
} |
112
|
|
|
} |
113
|
|
|
}; |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* @return AttributeItem |
118
|
|
|
*/ |
119
|
|
|
private function getAttributeFromAttributeOrRef( |
120
|
|
|
DOMElement $childNode, |
121
|
|
|
Schema $schema, |
122
|
|
|
DOMElement $node |
123
|
|
|
) { |
124
|
|
|
if ($childNode->hasAttribute("ref")) { |
125
|
|
|
/** |
126
|
|
|
* @var AttributeItem $attribute |
127
|
|
|
*/ |
128
|
|
|
$attribute = $this->findSomething('findAttribute', $schema, $node, $childNode->getAttribute("ref")); |
129
|
|
|
} else { |
130
|
|
|
/** |
131
|
|
|
* @var Attribute $attribute |
132
|
|
|
*/ |
133
|
|
|
$attribute = $this->loadAttribute($schema, $childNode); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
return $attribute; |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* @return Attribute |
141
|
|
|
*/ |
142
|
|
|
private function loadAttribute(Schema $schema, DOMElement $node) |
143
|
|
|
{ |
144
|
|
|
$attribute = new Attribute($schema, $node->getAttribute("name")); |
145
|
|
|
$attribute->setDoc($this->getDocumentation($node)); |
146
|
|
|
$this->fillItem($attribute, $node); |
147
|
|
|
|
148
|
|
|
if ($node->hasAttribute("nillable")) { |
149
|
|
|
$attribute->setNil($node->getAttribute("nillable") == "true"); |
150
|
|
|
} |
151
|
|
|
if ($node->hasAttribute("form")) { |
152
|
|
|
$attribute->setQualified($node->getAttribute("form") == "qualified"); |
153
|
|
|
} |
154
|
|
|
if ($node->hasAttribute("use")) { |
155
|
|
|
$attribute->setUse($node->getAttribute("use")); |
156
|
|
|
} |
157
|
|
|
return $attribute; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* @return Closure |
162
|
|
|
*/ |
163
|
|
|
private function loadAttributeOrElementDef( |
164
|
|
|
Schema $schema, |
165
|
|
|
DOMElement $node, |
166
|
|
|
bool $attributeDef |
167
|
|
|
) { |
168
|
|
|
$name = $node->getAttribute('name'); |
169
|
|
|
if ($attributeDef) { |
170
|
|
|
$attribute = new AttributeDef($schema, $name); |
171
|
|
|
$schema->addAttribute($attribute); |
172
|
|
|
} else { |
173
|
|
|
$attribute = new ElementDef($schema, $name); |
174
|
|
|
$schema->addElement($attribute); |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
|
178
|
|
|
return function () use ($attribute, $node) { |
179
|
|
|
$this->fillItem($attribute, $node); |
180
|
|
|
}; |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* @return Closure |
185
|
|
|
*/ |
186
|
|
|
private function loadAttributeDef(Schema $schema, DOMElement $node) |
187
|
|
|
{ |
188
|
|
|
return $this->loadAttributeOrElementDef($schema, $node, true); |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* @param DOMElement $node |
193
|
|
|
* @return string |
194
|
|
|
*/ |
195
|
|
|
private function getDocumentation(DOMElement $node) |
196
|
|
|
{ |
197
|
|
|
$doc = ''; |
198
|
|
|
foreach ($node->childNodes as $childNode) { |
199
|
|
|
if ($childNode->localName == "annotation") { |
200
|
|
|
foreach ($childNode->childNodes as $subChildNode) { |
201
|
|
|
if ($subChildNode->localName == "documentation") { |
202
|
|
|
$doc .= ($subChildNode->nodeValue); |
203
|
|
|
} |
204
|
|
|
} |
205
|
|
|
} |
206
|
|
|
} |
207
|
|
|
$doc = preg_replace('/[\t ]+/', ' ', $doc); |
208
|
|
|
return trim($doc); |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
private function setSchemaThingsFromNode( |
212
|
|
|
Schema $schema, |
213
|
|
|
DOMElement $node, |
214
|
|
|
Schema $parent = null |
215
|
|
|
) { |
216
|
|
|
$schema->setDoc($this->getDocumentation($node)); |
217
|
|
|
|
218
|
|
|
if ($node->hasAttribute("targetNamespace")) { |
219
|
|
|
$schema->setTargetNamespace($node->getAttribute("targetNamespace")); |
220
|
|
|
} elseif ($parent) { |
221
|
|
|
$schema->setTargetNamespace($parent->getTargetNamespace()); |
222
|
|
|
} |
223
|
|
|
$schema->setElementsQualification($node->getAttribute("elementFormDefault") == "qualified"); |
224
|
|
|
$schema->setAttributesQualification($node->getAttribute("attributeFormDefault") == "qualified"); |
225
|
|
|
$schema->setDoc($this->getDocumentation($node)); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* @param mixed $schema |
230
|
|
|
* |
231
|
|
|
* @return Closure|null |
232
|
|
|
*/ |
233
|
|
|
private function maybeCallMethod( |
234
|
|
|
array $methods, |
235
|
|
|
string $key, |
236
|
|
|
DOMNode $childNode, |
237
|
|
|
...$args |
238
|
|
|
) { |
239
|
|
|
if ($childNode instanceof DOMElement && isset($methods[$key])) { |
240
|
|
|
$method = $methods[$key]; |
241
|
|
|
|
242
|
|
|
$append = $this->$method(...$args); |
243
|
|
|
|
244
|
|
|
if ($append instanceof Closure) { |
245
|
|
|
return $append; |
246
|
|
|
} |
247
|
|
|
} |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
/** |
251
|
|
|
* |
252
|
|
|
* @param Schema $schema |
253
|
|
|
* @param DOMElement $node |
254
|
|
|
* @param Schema $parent |
255
|
|
|
* @return Closure[] |
256
|
|
|
*/ |
257
|
|
|
private function schemaNode(Schema $schema, DOMElement $node, Schema $parent = null) |
258
|
|
|
{ |
259
|
|
|
$this->setSchemaThingsFromNode($schema, $node, $parent); |
260
|
|
|
$functions = array(); |
261
|
|
|
|
262
|
|
|
static $methods = [ |
263
|
|
|
'include' => 'loadImport', |
264
|
|
|
'import' => 'loadImport', |
265
|
|
|
'element' => 'loadElementDef', |
266
|
|
|
'attribute' => 'loadAttributeDef', |
267
|
|
|
'attributeGroup' => 'loadAttributeGroup', |
268
|
|
|
'group' => 'loadGroup', |
269
|
|
|
'complexType' => 'loadComplexType', |
270
|
|
|
'simpleType' => 'loadSimpleType', |
271
|
|
|
]; |
272
|
|
|
|
273
|
|
|
foreach ($node->childNodes as $childNode) { |
274
|
|
|
$callback = $this->maybeCallMethod( |
275
|
|
|
$methods, |
276
|
|
|
(string) $childNode->localName, |
277
|
|
|
$childNode, |
278
|
|
|
$schema, |
279
|
|
|
$childNode |
280
|
|
|
); |
281
|
|
|
|
282
|
|
|
if ($callback instanceof Closure) { |
283
|
|
|
$functions[] = $callback; |
284
|
|
|
} |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
return $functions; |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
/** |
291
|
|
|
* @return Element |
292
|
|
|
*/ |
293
|
|
|
private function loadElement(Schema $schema, DOMElement $node) |
294
|
|
|
{ |
295
|
|
|
$element = new Element($schema, $node->getAttribute("name")); |
296
|
|
|
$element->setDoc($this->getDocumentation($node)); |
297
|
|
|
|
298
|
|
|
$this->fillItem($element, $node); |
299
|
|
|
|
300
|
|
|
static::maybeSetMax($element, $node); |
301
|
|
|
static::maybeSetMin($element, $node); |
302
|
|
|
|
303
|
|
|
$xp = new \DOMXPath($node->ownerDocument); |
304
|
|
|
$xp->registerNamespace('xs', 'http://www.w3.org/2001/XMLSchema'); |
305
|
|
|
|
306
|
|
|
if ($xp->query('ancestor::xs:choice', $node)->length) { |
307
|
|
|
$element->setMin(0); |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
if ($node->hasAttribute("nillable")) { |
311
|
|
|
$element->setNil($node->getAttribute("nillable") == "true"); |
312
|
|
|
} |
313
|
|
|
if ($node->hasAttribute("form")) { |
314
|
|
|
$element->setQualified($node->getAttribute("form") == "qualified"); |
315
|
|
|
} |
316
|
|
|
return $element; |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* @return GroupRef |
321
|
|
|
*/ |
322
|
|
|
private function loadGroupRef(Group $referenced, DOMElement $node) |
323
|
|
|
{ |
324
|
|
|
$ref = new GroupRef($referenced); |
325
|
|
|
$ref->setDoc($this->getDocumentation($node)); |
326
|
|
|
|
327
|
|
|
static::maybeSetMax($ref, $node); |
328
|
|
|
static::maybeSetMin($ref, $node); |
329
|
|
|
|
330
|
|
|
return $ref; |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
/** |
334
|
|
|
* @return ElementRef |
335
|
|
|
*/ |
336
|
|
|
private function loadElementRef(ElementDef $referenced, DOMElement $node) |
337
|
|
|
{ |
338
|
|
|
$ref = new ElementRef($referenced); |
339
|
|
|
$this->setDoc($ref, $node); |
340
|
|
|
|
341
|
|
|
static::maybeSetMax($ref, $node); |
342
|
|
|
static::maybeSetMin($ref, $node); |
343
|
|
|
if ($node->hasAttribute("nillable")) { |
344
|
|
|
$ref->setNil($node->getAttribute("nillable") == "true"); |
345
|
|
|
} |
346
|
|
|
if ($node->hasAttribute("form")) { |
347
|
|
|
$ref->setQualified($node->getAttribute("form") == "qualified"); |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
return $ref; |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
private function setDoc(Item $ref, DOMElement $node) |
354
|
|
|
{ |
355
|
|
|
$ref->setDoc($this->getDocumentation($node)); |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
/** |
359
|
|
|
* @return InterfaceSetMinMax |
360
|
|
|
*/ |
361
|
|
|
private static function maybeSetMax(InterfaceSetMinMax $ref, DOMElement $node) |
362
|
|
|
{ |
363
|
|
|
if ( |
364
|
|
|
$node->hasAttribute("maxOccurs") |
365
|
|
|
) { |
366
|
|
|
$ref->setMax($node->getAttribute("maxOccurs") == "unbounded" ? -1 : (int)$node->getAttribute("maxOccurs")); |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
return $ref; |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
/** |
373
|
|
|
* @return InterfaceSetMinMax |
374
|
|
|
*/ |
375
|
|
|
private static function maybeSetMin(InterfaceSetMinMax $ref, DOMElement $node) |
376
|
|
|
{ |
377
|
|
|
if ($node->hasAttribute("minOccurs")) { |
378
|
|
|
$ref->setMin((int) $node->getAttribute("minOccurs")); |
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
return $ref; |
382
|
|
|
} |
383
|
|
|
|
384
|
|
|
/** |
385
|
|
|
* @return AttributeRef |
386
|
|
|
*/ |
387
|
|
|
private function loadAttributeRef(AttributeDef $referencedAttribiute, DOMElement $node) |
388
|
|
|
{ |
389
|
|
|
$attribute = new AttributeRef($referencedAttribiute); |
390
|
|
|
$this->setDoc($attribute, $node); |
391
|
|
|
|
392
|
|
|
if ($node->hasAttribute("nillable")) { |
393
|
|
|
$attribute->setNil($node->getAttribute("nillable") == "true"); |
394
|
|
|
} |
395
|
|
|
if ($node->hasAttribute("form")) { |
396
|
|
|
$attribute->setQualified($node->getAttribute("form") == "qualified"); |
397
|
|
|
} |
398
|
|
|
if ($node->hasAttribute("use")) { |
399
|
|
|
$attribute->setUse($node->getAttribute("use")); |
400
|
|
|
} |
401
|
|
|
return $attribute; |
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
/** |
405
|
|
|
* @param int|null $max |
406
|
|
|
*/ |
407
|
|
|
private function loadSequence(ElementContainer $elementContainer, DOMElement $node, $max = null) |
408
|
|
|
{ |
409
|
|
|
$max = ( |
410
|
|
|
(is_int($max) && (bool) $max) || |
411
|
|
|
$node->getAttribute("maxOccurs") == "unbounded" || |
412
|
|
|
$node->getAttribute("maxOccurs") > 1 |
413
|
|
|
) |
414
|
|
|
? 2 |
415
|
|
|
: null; |
416
|
|
|
|
417
|
|
|
foreach ($node->childNodes as $childNode) { |
418
|
|
|
if ($childNode instanceof DOMElement) { |
419
|
|
|
$this->loadSequenceChildNode( |
420
|
|
|
$elementContainer, |
421
|
|
|
$node, |
422
|
|
|
$childNode, |
423
|
|
|
$max |
424
|
|
|
); |
425
|
|
|
} |
426
|
|
|
} |
427
|
|
|
} |
428
|
|
|
|
429
|
|
|
/** |
430
|
|
|
* @param int|null $max |
431
|
|
|
*/ |
432
|
|
|
private function loadSequenceChildNode( |
433
|
|
|
ElementContainer $elementContainer, |
434
|
|
|
DOMElement $node, |
435
|
|
|
DOMElement $childNode, |
436
|
|
|
$max |
437
|
|
|
) { |
438
|
|
|
if ( |
439
|
|
|
in_array( |
440
|
|
|
$childNode->localName, |
441
|
|
|
[ |
442
|
|
|
'choice', |
443
|
|
|
'sequence', |
444
|
|
|
'all', |
445
|
|
|
] |
446
|
|
|
) |
447
|
|
|
) { |
448
|
|
|
$this->loadSequence($elementContainer, $childNode, $max); |
449
|
|
|
} elseif ($childNode->localName === 'element') { |
450
|
|
|
if ($childNode->hasAttribute("ref")) { |
451
|
|
|
/** |
452
|
|
|
* @var ElementDef $referencedElement |
453
|
|
|
*/ |
454
|
|
|
$referencedElement = $this->findSomething('findElement', $elementContainer->getSchema(), $node, $childNode->getAttribute("ref")); |
455
|
|
|
$element = $this->loadElementRef($referencedElement, $childNode); |
456
|
|
|
} else { |
457
|
|
|
$element = $this->loadElement($elementContainer->getSchema(), $childNode); |
458
|
|
|
} |
459
|
|
|
if (is_int($max) && (bool) $max) { |
460
|
|
|
$element->setMax($max); |
461
|
|
|
} |
462
|
|
|
$elementContainer->addElement($element); |
463
|
|
|
} elseif ($childNode->localName === 'group') { |
464
|
|
|
$this->addGroupAsElement( |
465
|
|
|
$elementContainer->getSchema(), |
466
|
|
|
$node, |
467
|
|
|
$childNode, |
468
|
|
|
$elementContainer |
469
|
|
|
); |
470
|
|
|
} |
471
|
|
|
} |
472
|
|
|
|
473
|
|
|
private function addGroupAsElement( |
474
|
|
|
Schema $schema, |
475
|
|
|
DOMElement $node, |
476
|
|
|
DOMElement $childNode, |
477
|
|
|
ElementContainer $elementContainer |
478
|
|
|
) { |
479
|
|
|
/** |
480
|
|
|
* @var Group $referencedGroup |
481
|
|
|
*/ |
482
|
|
|
$referencedGroup = $this->findSomething( |
483
|
|
|
'findGroup', |
484
|
|
|
$schema, |
485
|
|
|
$node, |
486
|
|
|
$childNode->getAttribute("ref") |
487
|
|
|
); |
488
|
|
|
|
489
|
|
|
$group = $this->loadGroupRef($referencedGroup, $childNode); |
490
|
|
|
$elementContainer->addElement($group); |
491
|
|
|
} |
492
|
|
|
|
493
|
|
|
private function maybeLoadSequenceFromElementContainer( |
494
|
|
|
BaseComplexType $type, |
495
|
|
|
DOMElement $childNode |
496
|
|
|
) { |
497
|
|
|
if (! ($type instanceof ElementContainer)) { |
498
|
|
|
throw new RuntimeException( |
499
|
|
|
'$type passed to ' . |
500
|
|
|
__FUNCTION__ . |
501
|
|
|
'expected to be an instance of ' . |
502
|
|
|
ElementContainer::class . |
503
|
|
|
' when child node localName is "group", ' . |
504
|
|
|
get_class($type) . |
505
|
|
|
' given.' |
506
|
|
|
); |
507
|
|
|
} |
508
|
|
|
$this->loadSequence($type, $childNode); |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
/** |
512
|
|
|
* @return Closure |
513
|
|
|
*/ |
514
|
|
|
private function loadGroup(Schema $schema, DOMElement $node) |
515
|
|
|
{ |
516
|
|
|
$group = new Group($schema, $node->getAttribute("name")); |
517
|
|
|
$group->setDoc($this->getDocumentation($node)); |
518
|
|
|
|
519
|
|
|
if ($node->hasAttribute("maxOccurs")) { |
520
|
|
|
/** |
521
|
|
|
* @var GroupRef $group |
522
|
|
|
*/ |
523
|
|
|
$group = static::maybeSetMax(new GroupRef($group), $node); |
524
|
|
|
} |
525
|
|
|
if ($node->hasAttribute("minOccurs")) { |
526
|
|
|
/** |
527
|
|
|
* @var GroupRef $group |
528
|
|
|
*/ |
529
|
|
|
$group = static::maybeSetMin( |
530
|
|
|
$group instanceof GroupRef ? $group : new GroupRef($group), |
531
|
|
|
$node |
532
|
|
|
); |
533
|
|
|
} |
534
|
|
|
|
535
|
|
|
$schema->addGroup($group); |
536
|
|
|
|
537
|
|
|
static $methods = [ |
538
|
|
|
'sequence' => 'loadSequence', |
539
|
|
|
'choice' => 'loadSequence', |
540
|
|
|
'all' => 'loadSequence', |
541
|
|
|
]; |
542
|
|
|
|
543
|
|
|
return function () use ($group, $node, $methods) { |
544
|
|
|
foreach ($node->childNodes as $childNode) { |
545
|
|
|
$this->maybeCallMethod( |
546
|
|
|
$methods, |
547
|
|
|
(string) $childNode->localName, |
548
|
|
|
$childNode, |
549
|
|
|
$group, |
550
|
|
|
$childNode |
551
|
|
|
); |
552
|
|
|
} |
553
|
|
|
}; |
554
|
|
|
} |
555
|
|
|
|
556
|
|
|
/** |
557
|
|
|
* @param Closure|null $callback |
558
|
|
|
* |
559
|
|
|
* @return Closure |
560
|
|
|
*/ |
561
|
|
|
private function loadComplexType(Schema $schema, DOMElement $node, $callback = null) |
562
|
|
|
{ |
563
|
|
|
$isSimple = false; |
564
|
|
|
|
565
|
|
|
foreach ($node->childNodes as $childNode) { |
566
|
|
|
if ($childNode->localName === "simpleContent") { |
567
|
|
|
$isSimple = true; |
568
|
|
|
break; |
569
|
|
|
} |
570
|
|
|
} |
571
|
|
|
|
572
|
|
|
$type = $isSimple ? new ComplexTypeSimpleContent($schema, $node->getAttribute("name")) : new ComplexType($schema, $node->getAttribute("name")); |
573
|
|
|
|
574
|
|
|
$type->setDoc($this->getDocumentation($node)); |
575
|
|
|
if ($node->getAttribute("name")) { |
576
|
|
|
$schema->addType($type); |
577
|
|
|
} |
578
|
|
|
|
579
|
|
View Code Duplication |
return function () use ($type, $node, $schema, $callback) { |
|
|
|
|
580
|
|
|
$this->runCallbackAgainstDOMNodeList( |
581
|
|
|
$type, |
582
|
|
|
$node, |
583
|
|
|
function ( |
584
|
|
|
DOMElement $node, |
585
|
|
|
DOMElement $childNode |
586
|
|
|
) use( |
587
|
|
|
$schema, |
588
|
|
|
$type |
589
|
|
|
) { |
590
|
|
|
$this->loadComplexTypeFromChildNode( |
591
|
|
|
$type, |
592
|
|
|
$node, |
593
|
|
|
$childNode, |
594
|
|
|
$schema |
595
|
|
|
); |
596
|
|
|
}, |
597
|
|
|
$callback |
598
|
|
|
); |
599
|
|
|
}; |
600
|
|
|
} |
601
|
|
|
|
602
|
|
|
/** |
603
|
|
|
* @param Closure|null $callback |
604
|
|
|
*/ |
605
|
|
|
private function runCallbackAgainstDOMNodeList( |
606
|
|
|
Type $type, |
607
|
|
|
DOMElement $node, |
608
|
|
|
Closure $againstNodeList, |
609
|
|
|
$callback = null |
610
|
|
|
) { |
611
|
|
|
$this->fillTypeNode($type, $node, true); |
612
|
|
|
|
613
|
|
|
foreach ($node->childNodes as $childNode) { |
614
|
|
|
if ($childNode instanceof DOMElement) { |
615
|
|
|
$againstNodeList( |
616
|
|
|
$node, |
617
|
|
|
$childNode |
618
|
|
|
); |
619
|
|
|
} |
620
|
|
|
} |
621
|
|
|
|
622
|
|
|
if ($callback) { |
623
|
|
|
call_user_func($callback, $type); |
624
|
|
|
} |
625
|
|
|
} |
626
|
|
|
|
627
|
|
|
private function loadComplexTypeFromChildNode( |
628
|
|
|
BaseComplexType $type, |
629
|
|
|
DOMElement $node, |
630
|
|
|
DOMElement $childNode, |
631
|
|
|
Schema $schema |
632
|
|
|
) { |
633
|
|
|
if ( |
634
|
|
|
in_array( |
635
|
|
|
$childNode->localName, |
636
|
|
|
[ |
637
|
|
|
'sequence', |
638
|
|
|
'choice', |
639
|
|
|
'all', |
640
|
|
|
] |
641
|
|
|
) |
642
|
|
|
) { |
643
|
|
|
$this->maybeLoadSequenceFromElementContainer( |
644
|
|
|
$type, |
645
|
|
|
$childNode |
646
|
|
|
); |
647
|
|
|
} elseif ($childNode->localName === 'attribute') { |
648
|
|
|
$attribute = $this->getAttributeFromAttributeOrRef( |
649
|
|
|
$childNode, |
650
|
|
|
$schema, |
651
|
|
|
$node |
652
|
|
|
); |
653
|
|
|
|
654
|
|
|
$type->addAttribute($attribute); |
655
|
|
|
} elseif ( |
656
|
|
|
$childNode->localName === 'group' && |
657
|
|
|
$type instanceof ComplexType |
658
|
|
|
) { |
659
|
|
|
$this->addGroupAsElement( |
660
|
|
|
$schema, |
661
|
|
|
$node, |
662
|
|
|
$childNode, |
663
|
|
|
$type |
664
|
|
|
); |
665
|
|
|
} elseif ($childNode->localName === 'attributeGroup') { |
666
|
|
|
AttributeGroup::findSomethingLikeThis( |
667
|
|
|
$this, |
668
|
|
|
$schema, |
669
|
|
|
$node, |
670
|
|
|
$childNode, |
671
|
|
|
$type |
672
|
|
|
); |
673
|
|
|
} |
674
|
|
|
} |
675
|
|
|
|
676
|
|
|
/** |
677
|
|
|
* @param Closure|null $callback |
678
|
|
|
* |
679
|
|
|
* @return Closure |
680
|
|
|
*/ |
681
|
|
|
private function loadSimpleType(Schema $schema, DOMElement $node, $callback = null) |
682
|
|
|
{ |
683
|
|
|
$type = new SimpleType($schema, $node->getAttribute("name")); |
684
|
|
|
$type->setDoc($this->getDocumentation($node)); |
685
|
|
|
if ($node->getAttribute("name")) { |
686
|
|
|
$schema->addType($type); |
687
|
|
|
} |
688
|
|
|
|
689
|
|
|
static $methods = [ |
690
|
|
|
'union' => 'loadUnion', |
691
|
|
|
'list' => 'loadList', |
692
|
|
|
]; |
693
|
|
|
|
694
|
|
View Code Duplication |
return function () use ($type, $node, $callback, $methods) { |
|
|
|
|
695
|
|
|
$this->runCallbackAgainstDOMNodeList( |
696
|
|
|
$type, |
697
|
|
|
$node, |
698
|
|
|
function ( |
699
|
|
|
DOMElement $node, |
700
|
|
|
DOMElement $childNode |
701
|
|
|
) use ( |
702
|
|
|
$methods, |
703
|
|
|
$type |
704
|
|
|
) { |
705
|
|
|
$this->maybeCallMethod( |
706
|
|
|
$methods, |
707
|
|
|
$childNode->localName, |
708
|
|
|
$childNode, |
709
|
|
|
$type, |
710
|
|
|
$childNode |
711
|
|
|
); |
712
|
|
|
}, |
713
|
|
|
$callback |
714
|
|
|
); |
715
|
|
|
}; |
716
|
|
|
} |
717
|
|
|
|
718
|
|
|
private function loadList(SimpleType $type, DOMElement $node) |
719
|
|
|
{ |
720
|
|
|
if ($node->hasAttribute("itemType")) { |
721
|
|
|
/** |
722
|
|
|
* @var SimpleType $listType |
723
|
|
|
*/ |
724
|
|
|
$listType = $this->findSomeType($type, $node, 'itemType'); |
725
|
|
|
$type->setList($listType); |
726
|
|
|
} else { |
727
|
|
|
$addCallback = function (SimpleType $list) use ($type) { |
728
|
|
|
$type->setList($list); |
729
|
|
|
}; |
730
|
|
|
|
731
|
|
|
$this->loadTypeWithCallbackOnChildNodes( |
732
|
|
|
$type->getSchema(), |
733
|
|
|
$node, |
734
|
|
|
$addCallback |
735
|
|
|
); |
736
|
|
|
} |
737
|
|
|
} |
738
|
|
|
|
739
|
|
|
private function loadTypeWithCallbackOnChildNodes( |
740
|
|
|
Schema $schema, |
741
|
|
|
DOMNode $node, |
742
|
|
|
Closure $callback |
743
|
|
|
) { |
744
|
|
|
foreach ($node->childNodes as $childNode) { |
745
|
|
|
$this->loadTypeWithCallback($schema, $childNode, $callback); |
746
|
|
|
} |
747
|
|
|
} |
748
|
|
|
|
749
|
|
|
private function loadTypeWithCallback( |
750
|
|
|
Schema $schema, |
751
|
|
|
DOMNode $childNode, |
752
|
|
|
Closure $callback |
753
|
|
|
) { |
754
|
|
|
if (! ($childNode instanceof DOMElement)) { |
755
|
|
|
return; |
756
|
|
|
} |
757
|
|
|
$methods = [ |
758
|
|
|
'complexType' => 'loadComplexType', |
759
|
|
|
'simpleType' => 'loadSimpleType', |
760
|
|
|
]; |
761
|
|
|
|
762
|
|
|
$func = $this->maybeCallMethod( |
763
|
|
|
$methods, |
764
|
|
|
$childNode->localName, |
765
|
|
|
$childNode, |
766
|
|
|
$schema, |
767
|
|
|
$childNode, |
768
|
|
|
$callback |
769
|
|
|
); |
770
|
|
|
|
771
|
|
|
if ($func instanceof Closure) { |
772
|
|
|
call_user_func($func); |
773
|
|
|
} |
774
|
|
|
} |
775
|
|
|
|
776
|
|
|
/** |
777
|
|
|
* @return SchemaItem |
778
|
|
|
*/ |
779
|
|
|
private function findSomeType( |
780
|
|
|
SchemaItem $fromThis, |
781
|
|
|
DOMElement $node, |
782
|
|
|
string $attributeName |
783
|
|
|
) { |
784
|
|
|
return $this->findSomeTypeFromAttribute( |
785
|
|
|
$fromThis, |
786
|
|
|
$node, |
787
|
|
|
$node->getAttribute($attributeName) |
788
|
|
|
); |
789
|
|
|
} |
790
|
|
|
|
791
|
|
|
/** |
792
|
|
|
* @return SchemaItem |
793
|
|
|
*/ |
794
|
|
|
private function findSomeTypeFromAttribute( |
795
|
|
|
SchemaItem $fromThis, |
796
|
|
|
DOMElement $node, |
797
|
|
|
string $attributeName |
798
|
|
|
) { |
799
|
|
|
/** |
800
|
|
|
* @var SchemaItem $out |
801
|
|
|
*/ |
802
|
|
|
$out = $this->findSomething( |
803
|
|
|
'findType', |
804
|
|
|
$fromThis->getSchema(), |
805
|
|
|
$node, |
806
|
|
|
$attributeName |
807
|
|
|
); |
808
|
|
|
|
809
|
|
|
return $out; |
810
|
|
|
} |
811
|
|
|
|
812
|
|
|
private function loadUnion(SimpleType $type, DOMElement $node) |
813
|
|
|
{ |
814
|
|
|
if ($node->hasAttribute("memberTypes")) { |
815
|
|
|
$types = preg_split('/\s+/', $node->getAttribute("memberTypes")); |
816
|
|
|
foreach ($types as $typeName) { |
817
|
|
|
/** |
818
|
|
|
* @var SimpleType $unionType |
819
|
|
|
*/ |
820
|
|
|
$unionType = $this->findSomeTypeFromAttribute( |
821
|
|
|
$type, |
822
|
|
|
$node, |
823
|
|
|
$typeName |
824
|
|
|
); |
825
|
|
|
$type->addUnion($unionType); |
826
|
|
|
} |
827
|
|
|
} |
828
|
|
|
$addCallback = function (SimpleType $unType) use ($type) { |
829
|
|
|
$type->addUnion($unType); |
830
|
|
|
}; |
831
|
|
|
|
832
|
|
|
$this->loadTypeWithCallbackOnChildNodes( |
833
|
|
|
$type->getSchema(), |
834
|
|
|
$node, |
835
|
|
|
$addCallback |
836
|
|
|
); |
837
|
|
|
} |
838
|
|
|
|
839
|
|
|
/** |
840
|
|
|
* @param bool $checkAbstract |
841
|
|
|
*/ |
842
|
|
|
private function fillTypeNode(Type $type, DOMElement $node, $checkAbstract = false) |
843
|
|
|
{ |
844
|
|
|
|
845
|
|
|
if ($checkAbstract) { |
846
|
|
|
$type->setAbstract($node->getAttribute("abstract") === "true" || $node->getAttribute("abstract") === "1"); |
847
|
|
|
} |
848
|
|
|
|
849
|
|
|
static $methods = [ |
850
|
|
|
'restriction' => 'loadRestriction', |
851
|
|
|
'extension' => 'maybeLoadExtensionFromBaseComplexType', |
852
|
|
|
'simpleContent' => 'fillTypeNode', |
853
|
|
|
'complexContent' => 'fillTypeNode', |
854
|
|
|
]; |
855
|
|
|
|
856
|
|
|
foreach ($node->childNodes as $childNode) { |
857
|
|
|
$this->maybeCallMethod( |
858
|
|
|
$methods, |
859
|
|
|
(string) $childNode->localName, |
860
|
|
|
$childNode, |
861
|
|
|
$type, |
862
|
|
|
$childNode |
863
|
|
|
); |
864
|
|
|
} |
865
|
|
|
} |
866
|
|
|
|
867
|
|
|
private function loadExtension(BaseComplexType $type, DOMElement $node) |
868
|
|
|
{ |
869
|
|
|
$extension = new Extension(); |
870
|
|
|
$type->setExtension($extension); |
871
|
|
|
|
872
|
|
|
if ($node->hasAttribute("base")) { |
873
|
|
|
$this->findAndSetSomeBase( |
874
|
|
|
$type, |
875
|
|
|
$extension, |
876
|
|
|
$node |
877
|
|
|
); |
878
|
|
|
} |
879
|
|
|
|
880
|
|
|
foreach ($node->childNodes as $childNode) { |
881
|
|
|
switch ($childNode->localName) { |
882
|
|
|
case 'sequence': |
883
|
|
|
case 'choice': |
884
|
|
|
case 'all': |
885
|
|
|
$this->maybeLoadSequenceFromElementContainer( |
886
|
|
|
$type, |
887
|
|
|
$childNode |
888
|
|
|
); |
889
|
|
|
break; |
890
|
|
|
case 'attribute': |
891
|
|
|
$attribute = $this->getAttributeFromAttributeOrRef( |
892
|
|
|
$childNode, |
893
|
|
|
$type->getSchema(), |
894
|
|
|
$node |
895
|
|
|
); |
896
|
|
|
$type->addAttribute($attribute); |
897
|
|
|
break; |
898
|
|
|
case 'attributeGroup': |
899
|
|
|
AttributeGroup::findSomethingLikeThis( |
900
|
|
|
$this, |
901
|
|
|
$type->getSchema(), |
902
|
|
|
$node, |
903
|
|
|
$childNode, |
904
|
|
|
$type |
905
|
|
|
); |
906
|
|
|
break; |
907
|
|
|
} |
908
|
|
|
} |
909
|
|
|
} |
910
|
|
|
|
911
|
|
|
private function findAndSetSomeBase( |
912
|
|
|
Type $type, |
913
|
|
|
Base $setBaseOnThis, |
914
|
|
|
DOMElement $node |
915
|
|
|
) { |
916
|
|
|
/** |
917
|
|
|
* @var Type $parent |
918
|
|
|
*/ |
919
|
|
|
$parent = $this->findSomeType($type, $node, 'base'); |
920
|
|
|
$setBaseOnThis->setBase($parent); |
921
|
|
|
} |
922
|
|
|
|
923
|
|
|
private function maybeLoadExtensionFromBaseComplexType( |
924
|
|
|
Type $type, |
925
|
|
|
DOMElement $childNode |
926
|
|
|
) { |
927
|
|
|
if (! ($type instanceof BaseComplexType)) { |
928
|
|
|
throw new RuntimeException( |
929
|
|
|
'Argument 1 passed to ' . |
930
|
|
|
__METHOD__ . |
931
|
|
|
' needs to be an instance of ' . |
932
|
|
|
BaseComplexType::class . |
933
|
|
|
' when passed onto ' . |
934
|
|
|
static::class . |
935
|
|
|
'::loadExtension(), ' . |
936
|
|
|
get_class($type) . |
937
|
|
|
' given.' |
938
|
|
|
); |
939
|
|
|
} |
940
|
|
|
$this->loadExtension($type, $childNode); |
941
|
|
|
} |
942
|
|
|
|
943
|
|
|
private function loadRestriction(Type $type, DOMElement $node) |
944
|
|
|
{ |
945
|
|
|
$restriction = new Restriction(); |
946
|
|
|
$type->setRestriction($restriction); |
947
|
|
|
if ($node->hasAttribute("base")) { |
948
|
|
|
$this->findAndSetSomeBase($type, $restriction, $node); |
949
|
|
|
} else { |
950
|
|
|
$addCallback = function (Type $restType) use ($restriction) { |
951
|
|
|
$restriction->setBase($restType); |
952
|
|
|
}; |
953
|
|
|
|
954
|
|
|
$this->loadTypeWithCallbackOnChildNodes( |
955
|
|
|
$type->getSchema(), |
956
|
|
|
$node, |
957
|
|
|
$addCallback |
958
|
|
|
); |
959
|
|
|
} |
960
|
|
|
foreach ($node->childNodes as $childNode) { |
961
|
|
|
if (in_array($childNode->localName, |
962
|
|
|
[ |
963
|
|
|
'enumeration', |
964
|
|
|
'pattern', |
965
|
|
|
'length', |
966
|
|
|
'minLength', |
967
|
|
|
'maxLength', |
968
|
|
|
'minInclusive', |
969
|
|
|
'maxInclusive', |
970
|
|
|
'minExclusive', |
971
|
|
|
'maxExclusive', |
972
|
|
|
'fractionDigits', |
973
|
|
|
'totalDigits', |
974
|
|
|
'whiteSpace' |
975
|
|
|
], true)) { |
976
|
|
|
$restriction->addCheck($childNode->localName, |
977
|
|
|
[ |
978
|
|
|
'value' => $childNode->getAttribute("value"), |
979
|
|
|
'doc' => $this->getDocumentation($childNode) |
980
|
|
|
]); |
981
|
|
|
} |
982
|
|
|
} |
983
|
|
|
} |
984
|
|
|
|
985
|
|
|
/** |
986
|
|
|
* @param string $typeName |
987
|
|
|
* |
988
|
|
|
* @return mixed[] |
989
|
|
|
*/ |
990
|
|
|
private static function splitParts(DOMElement $node, $typeName) |
991
|
|
|
{ |
992
|
|
|
$prefix = null; |
993
|
|
|
$name = $typeName; |
994
|
|
|
if (strpos($typeName, ':') !== false) { |
995
|
|
|
list ($prefix, $name) = explode(':', $typeName); |
996
|
|
|
} |
997
|
|
|
|
998
|
|
|
$namespace = $node->lookupNamespaceUri($prefix ?: ''); |
999
|
|
|
return array( |
1000
|
|
|
$name, |
1001
|
|
|
$namespace, |
1002
|
|
|
$prefix |
1003
|
|
|
); |
1004
|
|
|
} |
1005
|
|
|
|
1006
|
|
|
/** |
1007
|
|
|
* |
1008
|
|
|
* @param string $finder |
1009
|
|
|
* @param Schema $schema |
1010
|
|
|
* @param DOMElement $node |
1011
|
|
|
* @param string $typeName |
1012
|
|
|
* @throws TypeException |
1013
|
|
|
* @return ElementItem|Group|AttributeItem|AttributeGroup|Type |
1014
|
|
|
*/ |
1015
|
|
|
public function findSomething($finder, Schema $schema, DOMElement $node, $typeName) |
1016
|
|
|
{ |
1017
|
|
|
list ($name, $namespace) = self::splitParts($node, $typeName); |
1018
|
|
|
|
1019
|
|
|
$namespace = $namespace ?: $schema->getTargetNamespace(); |
1020
|
|
|
|
1021
|
|
|
try { |
1022
|
|
|
return $schema->$finder($name, $namespace); |
1023
|
|
|
} catch (TypeNotFoundException $e) { |
1024
|
|
|
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); |
1025
|
|
|
} |
1026
|
|
|
} |
1027
|
|
|
|
1028
|
|
|
/** |
1029
|
|
|
* @return Closure |
1030
|
|
|
*/ |
1031
|
|
|
private function loadElementDef(Schema $schema, DOMElement $node) |
1032
|
|
|
{ |
1033
|
|
|
return $this->loadAttributeOrElementDef($schema, $node, false); |
1034
|
|
|
} |
1035
|
|
|
|
1036
|
|
|
private function fillItem(Item $element, DOMElement $node) |
1037
|
|
|
{ |
1038
|
|
|
$localType = null; |
1039
|
|
|
foreach ($node->childNodes as $childNode) { |
1040
|
|
|
switch ($childNode->localName) { |
1041
|
|
|
case 'complexType': |
1042
|
|
|
case 'simpleType': |
1043
|
|
|
$localType = $childNode; |
1044
|
|
|
break 2; |
1045
|
|
|
} |
1046
|
|
|
} |
1047
|
|
|
|
1048
|
|
|
if ($localType) { |
1049
|
|
|
$addCallback = function (Type $type) use ($element) { |
1050
|
|
|
$element->setType($type); |
1051
|
|
|
}; |
1052
|
|
|
$this->loadTypeWithCallback( |
1053
|
|
|
$element->getSchema(), |
1054
|
|
|
$localType, |
1055
|
|
|
$addCallback |
1056
|
|
|
); |
1057
|
|
|
} else { |
1058
|
|
|
|
1059
|
|
|
if ($node->getAttribute("type")) { |
1060
|
|
|
/** |
1061
|
|
|
* @var Type $type |
1062
|
|
|
*/ |
1063
|
|
|
$type = $this->findSomeType($element, $node, 'type'); |
1064
|
|
|
} else { |
1065
|
|
|
/** |
1066
|
|
|
* @var Type $type |
1067
|
|
|
*/ |
1068
|
|
|
$type = $this->findSomeTypeFromAttribute( |
1069
|
|
|
$element, |
1070
|
|
|
$node, |
1071
|
|
|
($node->lookupPrefix(self::XSD_NS) . ':anyType') |
1072
|
|
|
); |
1073
|
|
|
} |
1074
|
|
|
|
1075
|
|
|
$element->setType($type); |
1076
|
|
|
} |
1077
|
|
|
} |
1078
|
|
|
|
1079
|
|
|
/** |
1080
|
|
|
* @return Closure |
1081
|
|
|
*/ |
1082
|
|
|
private function loadImport(Schema $schema, DOMElement $node) |
1083
|
|
|
{ |
1084
|
|
|
$base = urldecode($node->ownerDocument->documentURI); |
1085
|
|
|
$file = UrlUtils::resolveRelativeUrl($base, $node->getAttribute("schemaLocation")); |
1086
|
|
|
if ($node->hasAttribute("namespace") |
1087
|
|
|
&& isset(self::$globalSchemaInfo[$node->getAttribute("namespace")]) |
1088
|
|
|
&& isset($this->loadedFiles[self::$globalSchemaInfo[$node->getAttribute("namespace")]]) |
1089
|
|
|
) { |
1090
|
|
|
|
1091
|
|
|
$schema->addSchema($this->loadedFiles[self::$globalSchemaInfo[$node->getAttribute("namespace")]]); |
1092
|
|
|
|
1093
|
|
|
return function () { |
1094
|
|
|
}; |
1095
|
|
|
} elseif ($node->hasAttribute("namespace") |
1096
|
|
|
&& isset($this->loadedFiles[$this->getNamespaceSpecificFileIndex($file, $node->getAttribute("namespace"))])) { |
1097
|
|
|
$schema->addSchema($this->loadedFiles[$this->getNamespaceSpecificFileIndex($file, $node->getAttribute("namespace"))]); |
1098
|
|
|
return function () { |
1099
|
|
|
}; |
1100
|
|
|
} elseif (isset($this->loadedFiles[$file])) { |
1101
|
|
|
$schema->addSchema($this->loadedFiles[$file]); |
1102
|
|
|
return function () { |
1103
|
|
|
}; |
1104
|
|
|
} |
1105
|
|
|
|
1106
|
|
|
if (!$node->getAttribute("namespace")) { |
1107
|
|
|
$this->loadedFiles[$file] = $newSchema = $schema; |
1108
|
|
|
} else { |
1109
|
|
|
$this->loadedFiles[$file] = $newSchema = new Schema(); |
1110
|
|
|
$newSchema->addSchema($this->getGlobalSchema()); |
1111
|
|
|
} |
1112
|
|
|
|
1113
|
|
|
$xml = $this->getDOM(isset($this->knownLocationSchemas[$file]) ? $this->knownLocationSchemas[$file] : $file); |
1114
|
|
|
|
1115
|
|
|
$callbacks = $this->schemaNode($newSchema, $xml->documentElement, $schema); |
1116
|
|
|
|
1117
|
|
|
if ($node->getAttribute("namespace")) { |
1118
|
|
|
$schema->addSchema($newSchema); |
1119
|
|
|
} |
1120
|
|
|
|
1121
|
|
|
|
1122
|
|
|
return function () use ($callbacks) { |
1123
|
|
|
foreach ($callbacks as $callback) { |
1124
|
|
|
call_user_func($callback); |
1125
|
|
|
} |
1126
|
|
|
}; |
1127
|
|
|
} |
1128
|
|
|
|
1129
|
|
|
/** |
1130
|
|
|
* @var Schema|null |
1131
|
|
|
*/ |
1132
|
|
|
private $globalSchema; |
1133
|
|
|
|
1134
|
|
|
/** |
1135
|
|
|
* |
1136
|
|
|
* @return Schema |
1137
|
|
|
*/ |
1138
|
|
|
public function getGlobalSchema() |
1139
|
|
|
{ |
1140
|
|
|
if (!$this->globalSchema) { |
1141
|
|
|
$callbacks = array(); |
1142
|
|
|
$globalSchemas = array(); |
1143
|
|
|
foreach (self::$globalSchemaInfo as $namespace => $uri) { |
1144
|
|
|
$this->loadedFiles[$uri] = $globalSchemas [$namespace] = $schema = new Schema(); |
1145
|
|
|
if ($namespace === self::XSD_NS) { |
1146
|
|
|
$this->globalSchema = $schema; |
1147
|
|
|
} |
1148
|
|
|
$xml = $this->getDOM($this->knownLocationSchemas[$uri]); |
1149
|
|
|
$callbacks = array_merge($callbacks, $this->schemaNode($schema, $xml->documentElement)); |
1150
|
|
|
} |
1151
|
|
|
|
1152
|
|
|
$globalSchemas[self::XSD_NS]->addType(new SimpleType($globalSchemas[self::XSD_NS], "anySimpleType")); |
1153
|
|
|
$globalSchemas[self::XSD_NS]->addType(new SimpleType($globalSchemas[self::XSD_NS], "anyType")); |
1154
|
|
|
|
1155
|
|
|
$globalSchemas[self::XML_NS]->addSchema($globalSchemas[self::XSD_NS], self::XSD_NS); |
1156
|
|
|
$globalSchemas[self::XSD_NS]->addSchema($globalSchemas[self::XML_NS], self::XML_NS); |
1157
|
|
|
|
1158
|
|
|
foreach ($callbacks as $callback) { |
1159
|
|
|
$callback(); |
1160
|
|
|
} |
1161
|
|
|
} |
1162
|
|
|
|
1163
|
|
|
/** |
1164
|
|
|
* @var Schema $out |
1165
|
|
|
*/ |
1166
|
|
|
$out = $this->globalSchema; |
1167
|
|
|
|
1168
|
|
|
return $out; |
1169
|
|
|
} |
1170
|
|
|
|
1171
|
|
|
/** |
1172
|
|
|
* @param DOMElement $node |
1173
|
|
|
* @param string $file |
1174
|
|
|
* |
1175
|
|
|
* @return Schema |
1176
|
|
|
*/ |
1177
|
|
|
public function readNode(DOMElement $node, $file = 'schema.xsd') |
1178
|
|
|
{ |
1179
|
|
|
$fileKey = $node->hasAttribute('targetNamespace') ? $this->getNamespaceSpecificFileIndex($file, $node->getAttribute('targetNamespace')) : $file; |
1180
|
|
|
$this->loadedFiles[$fileKey] = $rootSchema = new Schema(); |
1181
|
|
|
|
1182
|
|
|
$rootSchema->addSchema($this->getGlobalSchema()); |
1183
|
|
|
$callbacks = $this->schemaNode($rootSchema, $node); |
1184
|
|
|
|
1185
|
|
|
foreach ($callbacks as $callback) { |
1186
|
|
|
call_user_func($callback); |
1187
|
|
|
} |
1188
|
|
|
|
1189
|
|
|
return $rootSchema; |
1190
|
|
|
} |
1191
|
|
|
|
1192
|
|
|
/** |
1193
|
|
|
* It is possible that a single file contains multiple <xsd:schema/> nodes, for instance in a WSDL file. |
1194
|
|
|
* |
1195
|
|
|
* Each of these <xsd:schema/> nodes typically target a specific namespace. Append the target namespace to the |
1196
|
|
|
* file to distinguish between multiple schemas in a single file. |
1197
|
|
|
* |
1198
|
|
|
* @param string $file |
1199
|
|
|
* @param string $targetNamespace |
1200
|
|
|
* |
1201
|
|
|
* @return string |
1202
|
|
|
*/ |
1203
|
|
|
private function getNamespaceSpecificFileIndex($file, $targetNamespace) |
1204
|
|
|
{ |
1205
|
|
|
return $file . '#' . $targetNamespace; |
1206
|
|
|
} |
1207
|
|
|
|
1208
|
|
|
/** |
1209
|
|
|
* @param string $content |
1210
|
|
|
* @param string $file |
1211
|
|
|
* |
1212
|
|
|
* @return Schema |
1213
|
|
|
* |
1214
|
|
|
* @throws IOException |
1215
|
|
|
*/ |
1216
|
|
|
public function readString($content, $file = 'schema.xsd') |
1217
|
|
|
{ |
1218
|
|
|
$xml = new DOMDocument('1.0', 'UTF-8'); |
1219
|
|
|
if (!$xml->loadXML($content)) { |
1220
|
|
|
throw new IOException("Can't load the schema"); |
1221
|
|
|
} |
1222
|
|
|
$xml->documentURI = $file; |
1223
|
|
|
|
1224
|
|
|
return $this->readNode($xml->documentElement, $file); |
1225
|
|
|
} |
1226
|
|
|
|
1227
|
|
|
/** |
1228
|
|
|
* @param string $file |
1229
|
|
|
* |
1230
|
|
|
* @return Schema |
1231
|
|
|
*/ |
1232
|
|
|
public function readFile($file) |
1233
|
|
|
{ |
1234
|
|
|
$xml = $this->getDOM($file); |
1235
|
|
|
return $this->readNode($xml->documentElement, $file); |
1236
|
|
|
} |
1237
|
|
|
|
1238
|
|
|
/** |
1239
|
|
|
* @param string $file |
1240
|
|
|
* |
1241
|
|
|
* @return DOMDocument |
1242
|
|
|
* |
1243
|
|
|
* @throws IOException |
1244
|
|
|
*/ |
1245
|
|
|
private function getDOM($file) |
1246
|
|
|
{ |
1247
|
|
|
$xml = new DOMDocument('1.0', 'UTF-8'); |
1248
|
|
|
if (!$xml->load($file)) { |
1249
|
|
|
throw new IOException("Can't load the file $file"); |
1250
|
|
|
} |
1251
|
|
|
return $xml; |
1252
|
|
|
} |
1253
|
|
|
} |
1254
|
|
|
|
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.