Passed
Push — static-analysis ( 48443d...1a3120 )
by SignpostMarv
03:27
created

Schema::loadImport()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 22
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2

Importance

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