Passed
Push — static-analysis ( be3dd0...1958c5 )
by SignpostMarv
03:00
created

Schema::findAttributeGroup()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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