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