Passed
Push — static-analysis ( 7ddd7f...172fb7 )
by SignpostMarv
03:57
created

AbstractSchemaReader::fillItem()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 14
nc 3
nop 2
dl 0
loc 25
ccs 19
cts 19
cp 1
crap 3
rs 8.8571
c 0
b 0
f 0
1
<?php
2
namespace GoetasWebservices\XML\XSDReader;
3
4
use Closure;
5
use DOMDocument;
6
use DOMElement;
7
use DOMNode;
8
use DOMNodeList;
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\AttributeDef;
13
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem;
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
abstract class AbstractSchemaReader
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 string[]
47
    */
48
    protected $knownLocationSchemas = [
49
        'http://www.w3.org/2001/xml.xsd' => (
50
            __DIR__ . '/Resources/xml.xsd'
51
        ),
52
        'http://www.w3.org/2001/XMLSchema.xsd' => (
53
            __DIR__ . '/Resources/XMLSchema.xsd'
54
        ),
55
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' => (
56
            __DIR__ . '/Resources/oasis-200401-wss-wssecurity-secext-1.0.xsd'
57
        ),
58
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' => (
59
            __DIR__ . '/Resources/oasis-200401-wss-wssecurity-utility-1.0.xsd'
60
        ),
61
        'https://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd' => (
62
            __DIR__ . '/Resources/xmldsig-core-schema.xsd'
63
        ),
64
        'http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd' => (
65
            __DIR__ . '/Resources/xmldsig-core-schema.xsd'
66
        ),
67
    ];
68
69
    /**
70
    * @var string[]
71
    */
72
    protected static $globalSchemaInfo = array(
73
        self::XML_NS => 'http://www.w3.org/2001/xml.xsd',
74
        self::XSD_NS => 'http://www.w3.org/2001/XMLSchema.xsd'
75
    );
76
77
    /**
78
    * @param string $remote
79
    * @param string $local
80
    */
81
    public function addKnownSchemaLocation($remote, $local)
82
    {
83
        $this->knownLocationSchemas[$remote] = $local;
84
    }
85
86
    /**
87
    * @return Closure
88
    */
89
    abstract protected function loadAttributeGroup(Schema $schema, DOMElement $node);
90
91
    /**
92
    * @param bool $attributeDef
93
    *
94
    * @return Closure
95
    */
96
    abstract protected function loadAttributeOrElementDef(
97
        Schema $schema,
98
        DOMElement $node,
99
        $attributeDef
100
    );
101
102
    /**
103
    * @return Closure
104
    */
105
    abstract protected function loadAttributeDef(Schema $schema, DOMElement $node);
106
107
    /**
108
     * @param DOMElement $node
109
     * @return string
110
     */
111 135
    public static function getDocumentation(DOMElement $node)
112
    {
113 135
        $doc = '';
114 135
        foreach ($node->childNodes as $childNode) {
115 135
            if ($childNode->localName == "annotation") {
116 135
                $doc .= static::getDocumentation($childNode);
117 135
            } elseif ($childNode->localName == 'documentation') {
118 135
                $doc .= ($childNode->nodeValue);
119 45
            }
120 45
        }
121 135
        $doc = preg_replace('/[\t ]+/', ' ', $doc);
122 135
        return trim($doc);
123
    }
124
125
    abstract protected function setSchemaThingsFromNode(
126
        Schema $schema,
127
        DOMElement $node,
128
        Schema $parent = null
129
    );
130
131
    /**
132
    * @param string $key
133
    *
134
    * @return Closure|null
135
    */
136 135
    public function maybeCallMethod(
137
        array $methods,
138
        $key,
139
        DOMNode $childNode,
140
        ...$args
141
    ) {
142 135
        if ($childNode instanceof DOMElement && isset($methods[$key])) {
143 135
            $method = $methods[$key];
144
145 135
            $append = $this->$method(...$args);
146
147 135
            if ($append instanceof Closure) {
148 135
                return $append;
149
            }
150 45
        }
151 135
    }
152
153
    /**
154
     *
155
     * @param Schema $schema
156
     * @param DOMElement $node
157
     * @param Schema $parent
158
     * @return Closure[]
159
     */
160
    abstract protected function schemaNode(Schema $schema, DOMElement $node, Schema $parent = null);
161
162
    /**
163
    * @return InterfaceSetMinMax
164
    */
165 135
    public static function maybeSetMax(InterfaceSetMinMax $ref, DOMElement $node)
166
    {
167
        if (
168 135
            $node->hasAttribute("maxOccurs")
169 45
        ) {
170 135
            $ref->setMax($node->getAttribute("maxOccurs") == "unbounded" ? -1 : (int)$node->getAttribute("maxOccurs"));
171 45
        }
172
173 135
        return $ref;
174
    }
175
176
    /**
177
    * @return InterfaceSetMinMax
178
    */
179 135
    public static function maybeSetMin(InterfaceSetMinMax $ref, DOMElement $node)
180
    {
181 135
        if ($node->hasAttribute("minOccurs")) {
182 135
            $ref->setMin((int) $node->getAttribute("minOccurs"));
183 45
        }
184
185 135
        return $ref;
186
    }
187
188
    /**
189
    * @param int|null $max
190
    *
191
    * @return int|null
192
    */
193
    abstract protected static function loadSequenceNormaliseMax(DOMElement $node, $max);
194
195
    /**
196
    * @param int|null $max
197
    */
198
    abstract protected function loadSequence(ElementContainer $elementContainer, DOMElement $node, $max = null);
199
200
    /**
201
    * @param int|null $max
202
    */
203
    abstract protected function loadSequenceChildNode(
204
        ElementContainer $elementContainer,
205
        DOMElement $node,
206
        DOMElement $childNode,
207
        $max
208
    );
209
210
    /**
211
    * @param mixed[][] $methods
212
    *
213
    * @return mixed
214
    */
215
    abstract protected function maybeCallCallableWithArgs(
216
        DOMElement $childNode,
217
        array $commonMethods = [],
218
        array $methods = []
219
    );
220
221
    /**
222
    * @param int|null $max
223
    */
224
    abstract protected function loadSequenceChildNodeLoadSequence(
225
        ElementContainer $elementContainer,
226
        DOMElement $childNode,
227
        $max
228
    );
229
230
    /**
231
    * @param int|null $max
232
    */
233
    abstract protected function loadSequenceChildNodeLoadElement(
234
        ElementContainer $elementContainer,
235
        DOMElement $node,
236
        DOMElement $childNode,
237
        $max
238
    );
239
240
    abstract protected function loadSequenceChildNodeLoadGroup(
241
        ElementContainer $elementContainer,
242
        DOMElement $node,
243
        DOMElement $childNode
244
    );
245
246
    abstract protected function addGroupAsElement(
247
        Schema $schema,
248
        DOMElement $node,
249
        DOMElement $childNode,
250
        ElementContainer $elementContainer
251
    );
252
253
    abstract protected function maybeLoadSequenceFromElementContainer(
254
        BaseComplexType $type,
255
        DOMElement $childNode
256
    );
257
258
    /**
259
    * @return Closure
260
    */
261
    abstract protected function loadGroup(Schema $schema, DOMElement $node);
262
263
    /**
264
    * @return BaseComplexType
265
    */
266
    abstract protected function loadComplexTypeBeforeCallbackCallback(
267
        Schema $schema,
268
        DOMElement $node
269
    );
270
271
    /**
272
    * @param Closure|null $callback
273
    *
274
    * @return Closure
275
    */
276
    abstract protected function loadComplexType(Schema $schema, DOMElement $node, $callback = null);
277
278
    /**
279
    * @param Closure|null $callback
280
    *
281
    * @return Closure
282
    */
283
    abstract protected function makeCallbackCallback(
284
        Type $type,
285
        DOMElement $node,
286
        Closure $callbackCallback,
287
        $callback = null
288
    );
289
290
    /**
291
    * @param Closure|null $callback
292
    */
293
    abstract protected function runCallbackAgainstDOMNodeList(
294
        Type $type,
295
        DOMElement $node,
296
        Closure $againstNodeList,
297
        $callback = null
298
    );
299
300
    abstract protected function loadComplexTypeFromChildNode(
301
        BaseComplexType $type,
302
        DOMElement $node,
303
        DOMElement $childNode,
304
        Schema $schema
305
    );
306
307
    /**
308
    * @param Closure|null $callback
309
    *
310
    * @return Closure
311
    */
312
    abstract protected function loadSimpleType(Schema $schema, DOMElement $node, $callback = null);
313
314
    abstract protected function loadList(SimpleType $type, DOMElement $node);
315
316
    /**
317
    * @param string $attributeName
318
    *
319
    * @return SchemaItem
320
    */
321
    abstract protected function findSomeType(
322
        SchemaItem $fromThis,
323
        DOMElement $node,
324
        $attributeName
325
    );
326
327
    /**
328
    * @param string $attributeName
329
    *
330
    * @return SchemaItem
331
    */
332
    abstract protected function findSomeTypeFromAttribute(
333
        SchemaItem $fromThis,
334
        DOMElement $node,
335
        $attributeName
336
    );
337
338
    abstract protected function loadUnion(SimpleType $type, DOMElement $node);
339
340
    /**
341
    * @param bool $checkAbstract
342
    */
343
    abstract protected function fillTypeNode(Type $type, DOMElement $node, $checkAbstract = false);
344
345
    abstract protected function loadExtensionChildNode(
346
        BaseComplexType $type,
347
        DOMElement $node,
348
        DOMElement $childNode
349
    );
350
351
    abstract protected function loadExtension(BaseComplexType $type, DOMElement $node);
352
353
    abstract protected function loadExtensionChildNodes(
354
        BaseComplexType $type,
355
        DOMNodeList $childNodes,
356
        DOMElement $node
357
    );
358
359 135
    public function findAndSetSomeBase(
360
        Type $type,
361
        Base $setBaseOnThis,
362
        DOMElement $node
363
    ) {
364
        /**
365
        * @var Type $parent
366
        */
367 135
        $parent = $this->findSomeType($type, $node, 'base');
368 135
        $setBaseOnThis->setBase($parent);
369 135
    }
370
371
    abstract protected function maybeLoadExtensionFromBaseComplexType(
372
        Type $type,
373
        DOMElement $childNode
374
    );
375
376
    abstract protected function loadRestriction(Type $type, DOMElement $node);
377
378
    /**
379
    * @param string $typeName
380
    *
381
    * @return mixed[]
382
    */
383
    abstract protected static function splitParts(DOMElement $node, $typeName);
384
385
    /**
386
     *
387
     * @param string $finder
388
     * @param Schema $schema
389
     * @param DOMElement $node
390
     * @param string $typeName
391
     * @throws TypeException
392
     * @return ElementItem|Group|AttributeItem|AttributeGroup|Type
393
     */
394 135
    public function findSomething($finder, Schema $schema, DOMElement $node, $typeName)
395
    {
396 135
        list ($name, $namespace) = static::splitParts($node, $typeName);
397
398 135
        $namespace = $namespace ?: $schema->getTargetNamespace();
399
400
        try {
401 135
            return $schema->$finder($name, $namespace);
402
        } catch (TypeNotFoundException $e) {
403
            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);
404
        }
405
    }
406
407
    /**
408
    * @return Closure
409
    */
410
    abstract protected function loadElementDef(Schema $schema, DOMElement $node);
411
412 135
    public function fillItem(Item $element, DOMElement $node)
413
    {
414 135
        foreach ($node->childNodes as $childNode) {
415
            if (
416 135
                in_array(
417 135
                    $childNode->localName,
418
                    [
419 135
                        'complexType',
420 45
                        'simpleType',
421
                    ]
422 45
                )
423 45
            ) {
424 135
                Type::loadTypeWithCallback(
425 135
                    $this,
426 135
                    $element->getSchema(),
427 135
                    $childNode,
428 135
                    function (Type $type) use ($element) {
429 135
                        $element->setType($type);
430 135
                    }
431 45
                );
432 135
                return;
433
            }
434 45
        }
435
436 135
        $this->fillItemNonLocalType($element, $node);
437 135
    }
438
439
    abstract protected function fillItemNonLocalType(Item $element, DOMElement $node);
440
441
    /**
442
    * @return Closure
443
    */
444
    abstract protected function loadImport(Schema $schema, DOMElement $node);
445
446
    /**
447
    * @param string $file
448
    * @param string $namespace
449
    *
450
    * @return Closure
451
    */
452
    abstract protected function loadImportFresh(
453
        Schema $schema,
454
        DOMElement $node,
455
        $file,
456
        $namespace
457
    );
458
459
    /**
460
    * @var Schema|null
461
    */
462
    protected $globalSchema;
463
464
    /**
465
    * @return Schema[]
466
    */
467
    abstract protected function setupGlobalSchemas(array & $callbacks);
468
469
    /**
470
     *
471
     * @return Schema
472
     */
473 135
    public function getGlobalSchema()
474
    {
475 135
        if (!$this->globalSchema) {
476 135
            $callbacks = array();
477 135
            $globalSchemas = $this->setupGlobalSchemas($callbacks);
478
479 135
            $globalSchemas[static::XSD_NS]->addType(new SimpleType($globalSchemas[static::XSD_NS], "anySimpleType"));
480 135
            $globalSchemas[static::XSD_NS]->addType(new SimpleType($globalSchemas[static::XSD_NS], "anyType"));
481
482 135
            $globalSchemas[static::XML_NS]->addSchema($globalSchemas[static::XSD_NS], static::XSD_NS);
483 135
            $globalSchemas[static::XSD_NS]->addSchema($globalSchemas[static::XML_NS], static::XML_NS);
484
485 135
            foreach ($callbacks as $callback) {
486 135
                $callback();
487 45
            }
488 45
        }
489
490
        /**
491
        * @var Schema $out
492
        */
493 135
        $out = $this->globalSchema;
494
495 135
        return $out;
496
    }
497
498
    /**
499
     * @param DOMElement $node
500
     * @param string  $file
501
     *
502
     * @return Schema
503
     */
504 135
    public function readNode(DOMElement $node, $file = 'schema.xsd')
505
    {
506 135
        $fileKey = $node->hasAttribute('targetNamespace') ? $this->getNamespaceSpecificFileIndex($file, $node->getAttribute('targetNamespace')) : $file;
507 135
        Schema::setLoadedFile($fileKey, $rootSchema = new Schema());
508
509 135
        $rootSchema->addSchema($this->getGlobalSchema());
510 135
        $callbacks = $this->schemaNode($rootSchema, $node);
511
512 135
        foreach ($callbacks as $callback) {
513 117
            call_user_func($callback);
514 45
        }
515
516 135
        return $rootSchema;
517
    }
518
519
    /**
520
     * It is possible that a single file contains multiple <xsd:schema/> nodes, for instance in a WSDL file.
521
     *
522
     * Each of these  <xsd:schema/> nodes typically target a specific namespace. Append the target namespace to the
523
     * file to distinguish between multiple schemas in a single file.
524
     *
525
     * @param string $file
526
     * @param string $targetNamespace
527
     *
528
     * @return string
529
     */
530
    abstract protected function getNamespaceSpecificFileIndex($file, $targetNamespace);
531
532
    /**
533
     * @param string $content
534
     * @param string $file
535
     *
536
     * @return Schema
537
     *
538
     * @throws IOException
539
     */
540 132
    public function readString($content, $file = 'schema.xsd')
541
    {
542 132
        $xml = new DOMDocument('1.0', 'UTF-8');
543 132
        if (!$xml->loadXML($content)) {
544
            throw new IOException("Can't load the schema");
545
        }
546 132
        $xml->documentURI = $file;
547
548 132
        return $this->readNode($xml->documentElement, $file);
549
    }
550
551
    /**
552
     * @param string $file
553
     *
554
     * @return Schema
555
     */
556 3
    public function readFile($file)
557
    {
558 3
        $xml = $this->getDOM($file);
559 3
        return $this->readNode($xml->documentElement, $file);
560
    }
561
562
    /**
563
     * @param string $file
564
     *
565
     * @return DOMDocument
566
     *
567
     * @throws IOException
568
     */
569
    abstract protected function getDOM($file);
570
}
571