Passed
Push — static-analysis ( c8fdad...7d02a1 )
by SignpostMarv
03:21
created

Schema::setSchemaThingsFromNode()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3.072

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 2
dl 0
loc 14
ccs 8
cts 10
cp 0.8
crap 3.072
rs 9.4285
c 0
b 0
f 0
1
<?php
2
namespace GoetasWebservices\XML\XSDReader\Schema;
3
4
use Closure;
5
use DOMElement;
6
use GoetasWebservices\XML\XSDReader\AbstractSchemaReader;
7
use GoetasWebservices\XML\XSDReader\SchemaReaderLoadAbstraction;
8
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
9
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Group as AttributeGroup;
10
use GoetasWebservices\XML\XSDReader\Schema\Element\Group;
11
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementDef;
12
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementItem;
13
use GoetasWebservices\XML\XSDReader\Schema\Exception\TypeNotFoundException;
14
use GoetasWebservices\XML\XSDReader\Schema\Exception\SchemaException;
15
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem;
16
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeDef;
17
use GoetasWebservices\XML\XSDReader\Utils\UrlUtils;
18
19
class Schema
20
{
21
    /**
22
    * @var bool
23
    */
24
    protected $elementsQualification = false;
25
26
    /**
27
    * @var bool
28
    */
29
    protected $attributesQualification = false;
30
31
    /**
32
    * @var string|null
33
    */
34
    protected $targetNamespace;
35
36
    /**
37
    * @var Schema[]
38
    */
39
    protected $schemas = array();
40
41
    /**
42
    * @var Type[]
43
    */
44
    protected $types = array();
45
46
    /**
47
    * @var ElementDef[]
48
    */
49
    protected $elements = array();
50
51
    /**
52
    * @var Group[]
53
    */
54
    protected $groups = array();
55
56
    /**
57
    * @var AttributeGroup[]
58
    */
59
    protected $attributeGroups = array();
60
61
    /**
62
    * @var AttributeDef[]
63
    */
64
    protected $attributes = array();
65
66
    /**
67
    * @var string|null
68
    */
69
    protected $doc;
70
71
    /**
72
    * @var \GoetasWebservices\XML\XSDReader\Schema\SchemaItem[]
73
    */
74
    private $typeCache = array();
75
76
    /**
77
    * @return bool
78
    */
79
    public function getElementsQualification()
80
    {
81
        return $this->elementsQualification;
82
    }
83
84
    /**
85
    * @param bool $elementsQualification
86
    */
87 135
    public function setElementsQualification($elementsQualification)
88
    {
89 135
        $this->elementsQualification = $elementsQualification;
90 135
    }
91
92
    /**
93
    * @return bool
94
    */
95
    public function getAttributesQualification()
96
    {
97
        return $this->attributesQualification;
98
    }
99
100
    /**
101
    * @param bool $attributesQualification
102
    */
103 135
    public function setAttributesQualification($attributesQualification)
104
    {
105 135
        $this->attributesQualification = $attributesQualification;
106 135
    }
107
108
    /**
109
    * @return string|null
110
    */
111 135
    public function getTargetNamespace()
112
    {
113 135
        return $this->targetNamespace;
114
    }
115
116
    /**
117
    * @param string|null $targetNamespace
118
    */
119 135
    public function setTargetNamespace($targetNamespace)
120
    {
121 135
        $this->targetNamespace = $targetNamespace;
122 135
    }
123
124
    /**
125
    * @return Type[]
126
    */
127 15
    public function getTypes()
128
    {
129 15
        return $this->types;
130
    }
131
132
    /**
133
    * @return ElementDef[]
134
    */
135 9
    public function getElements()
136
    {
137 9
        return $this->elements;
138
    }
139
140
    /**
141
    * @return Schema[]
142
    */
143 135
    public function getSchemas()
144
    {
145 135
        return $this->schemas;
146
    }
147
148
    /**
149
    * @return AttributeDef[]
150
    */
151 3
    public function getAttributes()
152
    {
153 3
        return $this->attributes;
154
    }
155
156
    /**
157
    * @return Group[]
158
    */
159 6
    public function getGroups()
160
    {
161 6
        return $this->groups;
162
    }
163
164
    /**
165
    * @return string|null
166
    */
167
    public function getDoc()
168
    {
169
        return $this->doc;
170
    }
171
172
    /**
173
    * @param string $doc
174
    */
175 135
    public function setDoc($doc)
176
    {
177 135
        $this->doc = $doc;
178 135
    }
179
180 135
    public function addType(Type $type)
181
    {
182 135
        $this->types[$type->getName()] = $type;
183 135
    }
184
185 135
    public function addElement(ElementDef $element)
186
    {
187 135
        $this->elements[$element->getName()] = $element;
188 135
    }
189
190
    /**
191
    * @param string|null $namespace
192
    */
193 135
    public function addSchema(Schema $schema, $namespace = null)
194
    {
195 135
        if ($namespace !== null && $schema->getTargetNamespace() !== $namespace) {
196
            throw new SchemaException(sprintf("The target namespace ('%s') for schema, does not match the declared namespace '%s'", $schema->getTargetNamespace(), $namespace));
197
        }
198
199 135
        if ($namespace !== null) {
200 135
            $this->schemas[$namespace] = $schema;
201 45
        } else {
202 135
            $this->schemas[] = $schema;
203
        }
204 135
    }
205
206 135
    public function addAttribute(AttributeDef $attribute)
207
    {
208 135
        $this->attributes[$attribute->getName()] = $attribute;
209 135
    }
210
211 135
    public function addGroup(Group $group)
212
    {
213 135
        $this->groups[$group->getName()] = $group;
214 135
    }
215
216 135
    public function addAttributeGroup(AttributeGroup $group)
217
    {
218 135
        $this->attributeGroups[$group->getName()] = $group;
219 135
    }
220
221
    /**
222
    * @return AttributeGroup[]
223
    */
224 3
    public function getAttributeGroups()
225
    {
226 3
        return $this->attributeGroups;
227
    }
228
229
    /**
230
     *
231
     * @param string $name
232
     * @return Group|false
233
     */
234 135
    public function getGroup($name)
235
    {
236 135
        if (isset($this->groups[$name])) {
237 135
            return $this->groups[$name];
238
        }
239 3
        return false;
240
    }
241
242
    /**
243
     *
244
     * @param string $name
245
     * @return ElementItem|false
246
     */
247 135
    public function getElement($name)
248
    {
249 135
        if (isset($this->elements[$name])) {
250 135
            return $this->elements[$name];
251
        }
252 3
        return false;
253
    }
254
255
    /**
256
     *
257
     * @param string $name
258
     * @return Type|false
259
     */
260 135
    public function getType($name)
261
    {
262 135
        if (isset($this->types[$name])) {
263 135
            return $this->types[$name];
264
        }
265 3
        return false;
266
    }
267
268
    /**
269
     *
270
     * @param string $name
271
     * @return AttributeItem|false
272
     */
273 135
    public function getAttribute($name)
274
    {
275 135
        if (isset($this->attributes[$name])) {
276 135
            return $this->attributes[$name];
277
        }
278 3
        return false;
279
    }
280
281
    /**
282
     *
283
     * @param string $name
284
     * @return AttributeGroup|false
285
     */
286 135
    public function getAttributeGroup($name)
287
    {
288 135
        if (isset($this->attributeGroups[$name])) {
289 135
            return $this->attributeGroups[$name];
290
        }
291 3
        return false;
292
    }
293
294
    public function __toString()
295
    {
296
        return sprintf("Target namespace %s", $this->getTargetNamespace());
297
    }
298
299
    /**
300
    * @param string $getter
301
    * @param string $name
302
    * @param string $namespace
303
    * @param bool[] $calling
304
    * @param bool $throw
305
    *
306
    * @return SchemaItem|null
307
    */
308 135
    protected function findSomethingNoThrow(
309
        $getter,
310
        $name,
311
        $namespace = null,
312
        array & $calling = array()
313
    ) {
314 135
        $calling[spl_object_hash($this)] = true;
315 135
        $cid = "$getter, $name, $namespace";
316
317 135
        if (isset($this->typeCache[$cid])) {
318 135
            return $this->typeCache[$cid];
319
        }
320
321 135
        if (null === $namespace || $this->getTargetNamespace() === $namespace) {
322
            /**
323
            * @var \GoetasWebservices\XML\XSDReader\Schema\SchemaItem|null $item
324
            */
325 135
            $item = $this->$getter($name);
326 135
            if ($item instanceof SchemaItem) {
327 135
                return $this->typeCache[$cid] = $item;
328
            }
329 5
        }
330
331 135
        return $this->findSomethingNoThrowSchemas(
332 135
            $this->getSchemas(),
333 135
            $cid,
334 135
            $getter,
335 135
            $name,
336 135
            $namespace,
337 90
            $calling
338 45
        );
339
    }
340
341
342
    /**
343
    * @param Schema[] $schemas
344
    * @param string $cid
345
    * @param string $getter
346
    * @param string $name
347
    * @param string $namespace
348
    * @param bool[] $calling
349
    * @param bool $throw
350
    *
351
    * @return SchemaItem|null
352
    */
353 135
    protected function findSomethingNoThrowSchemas(
354
        array $schemas,
355
        $cid,
356
        $getter,
357
        $name,
358
        $namespace = null,
359
        array & $calling = array()
360
    ) {
361 135
        foreach ($this->getSchemas() as $childSchema) {
362 135
            if (!isset($calling[spl_object_hash($childSchema)])) {
363 135
                $in = $childSchema->findSomethingNoThrow($getter, $name, $namespace, $calling);
364
365 135
                if ($in instanceof SchemaItem) {
366 135
                    return $this->typeCache[$cid] = $in;
367
                }
368 7
            }
369 7
        }
370 21
    }
371
372
    /**
373
     *
374
     * @param string $getter
375
     * @param string $name
376
     * @param string $namespace
377
     * @param bool[] $calling
378
     * @param bool $throw
379
     *
380
     * @throws TypeNotFoundException
381
     * @return SchemaItem
382
     */
383 135
    protected function findSomething($getter, $name, $namespace = null, &$calling = array())
384
    {
385 135
        $in = $this->findSomethingNoThrow(
386 135
            $getter,
387 135
            $name,
388 135
            $namespace,
389 90
            $calling
390 45
        );
391
392 135
        if ($in instanceof SchemaItem) {
393 135
            return $in;
394
        }
395
396 15
        throw new TypeNotFoundException(sprintf("Can't find the %s named {%s}#%s.", substr($getter, 3), $namespace, $name));
397
    }
398
399
    /**
400
     *
401
     * @param string $name
402
     * @param string $namespace
403
     * @return Type
404
     */
405 135
    public function findType($name, $namespace = null)
406
    {
407
        /**
408
        * @var Type $out
409
        */
410 135
        $out = $this->findSomething('getType', $name, $namespace);
411
412 135
        return $out;
413
    }
414
415
    /**
416
     *
417
     * @param string $name
418
     * @param string $namespace
419
     * @return Group
420
     */
421 135
    public function findGroup($name, $namespace = null)
422
    {
423
        /**
424
        * @var Group $out
425
        */
426 135
        $out = $this->findSomething('getGroup', $name, $namespace);
427
428 135
        return $out;
429
    }
430
431
    /**
432
     *
433
     * @param string $name
434
     * @param string $namespace
435
     * @return ElementDef
436
     */
437 135
    public function findElement($name, $namespace = null)
438
    {
439
        /**
440
        * @var ElementDef $out
441
        */
442 135
        $out = $this->findSomething('getElement', $name, $namespace);
443
444 135
        return $out;
445
    }
446
447
    /**
448
     *
449
     * @param string $name
450
     * @param string $namespace
451
     * @return AttributeItem
452
     */
453 135
    public function findAttribute($name, $namespace = null)
454
    {
455
        /**
456
        * @var AttributeItem $out
457
        */
458 135
        $out = $this->findSomething('getAttribute', $name, $namespace);
459
460 135
        return $out;
461
    }
462
463
    /**
464
     *
465
     * @param string $name
466
     * @param string $namespace
467
     * @return AttributeGroup
468
     */
469 135
    public function findAttributeGroup($name, $namespace = null)
470
    {
471
        /**
472
        * @var AttributeGroup
473
        */
474 135
        $out = $this->findSomething('getAttributeGroup', $name, $namespace);
475
476 135
        return $out;
477
    }
478
479
    /**
480
    * @var Schema[]
481
    */
482
    private static $loadedFiles = array();
483
484
    /**
485
    * @param string $key
486
    *
487
    * @return bool
488
    */
489 135
    public static function hasLoadedFile($key)
490
    {
491 135
        return isset(self::$loadedFiles[$key]);
492
    }
493
494
    /**
495
    * @param string $key
496
    *
497
    * @return Schema
498
    */
499 135
    public static function getLoadedFile($key)
500
    {
501 135
        return self::$loadedFiles[$key];
502
    }
503
504
    /**
505
    * @param string $key
506
    *
507
    * @return Schema
508
    */
509 135
    public static function setLoadedFile($key, Schema $schema)
510
    {
511 135
        self::$loadedFiles[$key] = $schema;
512
513 135
        return $schema;
514
    }
515
516 135
    public function setSchemaThingsFromNode(
517
        DOMElement $node,
518
        Schema $parent = null
519
    ) {
520 135
        $this->setDoc(AbstractSchemaReader::getDocumentation($node));
521
522 135
        if ($node->hasAttribute("targetNamespace")) {
523 135
            $this->setTargetNamespace($node->getAttribute("targetNamespace"));
524 45
        } elseif ($parent) {
525
            $this->setTargetNamespace($parent->getTargetNamespace());
526
        }
527 135
        $this->setElementsQualification($node->getAttribute("elementFormDefault") == "qualified");
528 135
        $this->setAttributesQualification($node->getAttribute("attributeFormDefault") == "qualified");
529 135
        $this->setDoc(AbstractSchemaReader::getDocumentation($node));
530 135
    }
531
532
    /**
533
    * @param string $file
534
    * @param string $namespace
535
    *
536
    * @return Closure
537
    */
538 135
    public static function loadImport(
539
        SchemaReaderLoadAbstraction $reader,
540
        Schema $schema,
541
        DOMElement $node
542
    ) {
543 135
        $base = urldecode($node->ownerDocument->documentURI);
544 135
        $file = UrlUtils::resolveRelativeUrl($base, $node->getAttribute("schemaLocation"));
545
546 135
        $namespace = $node->getAttribute("namespace");
547
548 135
        $globalSchemaInfo = $reader->getGlobalSchemaInfo();
549
550
        if (
551
            (
552 135
                isset($globalSchemaInfo[$namespace]) &&
553 135
                Schema::hasLoadedFile(
554 135
                    $loadedFilesKey = $globalSchemaInfo[$namespace]
555 45
                )
556 45
            ) ||
557 9
            Schema::hasLoadedFile(
558 9
                $loadedFilesKey = $reader->getNamespaceSpecificFileIndex(
559 9
                    $file,
560 6
                    $namespace
561 3
                )
562 3
            ) ||
563 47
            Schema::hasLoadedFile($loadedFilesKey = $file)
564 45
        ) {
565 135
            $schema->addSchema(Schema::getLoadedFile($loadedFilesKey));
566
567
            return function() {
568 135
            };
569
        }
570
571 3
        return static::loadImportFresh(
572 3
            $reader,
573 3
            $schema,
574 3
            $node,
575 3
            $file,
576 2
            $namespace
577 1
        );
578
    }
579
580
    /**
581
    * @param string $file
582
    * @param string $namespace
583
    *
584
    * @return Closure
585
    */
586 3
    protected static function loadImportFresh(
587
        SchemaReaderLoadAbstraction $reader,
588
        Schema $schema,
589
        DOMElement $node,
590
        $file,
591
        $namespace
592
    ) {
593 3
        if (! $namespace) {
594 3
            $newSchema = Schema::setLoadedFile($file, $schema);
595 1
        } else {
596
            $newSchema = Schema::setLoadedFile($file, new Schema());
597
            $newSchema->addSchema($reader->getGlobalSchema());
598
        }
599
600 3
        $xml = $reader->getDOM(
601 3
            $reader->hasKnownSchemaLocation($file)
602 1
                ? $reader->getKnownSchemaLocation($file)
603 2
                : $file
604 1
        );
605
606 3
        $callbacks = $reader->schemaNode($newSchema, $xml->documentElement, $schema);
607
608 3
        if ($namespace) {
609
            $schema->addSchema($newSchema);
610
        }
611
612
613 3
        return function () use ($callbacks) {
614 3
            foreach ($callbacks as $callback) {
615 3
                $callback();
616 1
            }
617 3
        };
618
    }
619
}
620