Passed
Push — php-7.1 ( 4609c1...d00c2a )
by SignpostMarv
06:02
created

Schema::getGroups()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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