Passed
Push — static-analysis ( e35b6b...759744 )
by SignpostMarv
03:18
created

Schema::findSomethingNoThrowSchemas()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 6
cts 6
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 5
nc 4
nop 6
crap 4
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 3
        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 3
        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 3
        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 3
        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 3
        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
        }
326
327 135
        if (null === $namespace || $this->getTargetNamespace() === $namespace) {
328
            /**
329
            * @var \GoetasWebservices\XML\XSDReader\Schema\SchemaItem|null $item
330
            */
331 135
            $item = $this->$getter($name);
332 135
            if ($item instanceof SchemaItem) {
333 135
                return $this->typeCache[$cid] = $item;
334
            }
335 5
        }
336
337 135
        return $this->findSomethingNoThrowSchemas(
338 135
            $this->getSchemas(),
339 135
            $cid,
340 135
            $getter,
341 135
            $name,
342 135
            $namespace,
343 90
            $calling
344 45
        );
345
    }
346
347
348
    /**
349
    * @param Schema[] $schemas
350
    * @param string $cid
351
    * @param string $getter
352
    * @param string $name
353
    * @param string $namespace
354
    * @param bool[] $calling
355
    * @param bool $throw
356
    *
357
    * @return SchemaItem|null
358
    */
359 135
    protected function findSomethingNoThrowSchemas(
360
        array $schemas,
361
        $cid,
362
        $getter,
363
        $name,
364
        $namespace = null,
365
        array & $calling = array()
366
    ) {
367 135
        foreach ($this->getSchemas() as $childSchema) {
368 135
            if (!isset($calling[spl_object_hash($childSchema)])) {
369 135
                $in = $childSchema->findSomethingNoThrow($getter, $name, $namespace, $calling);
370
371 135
                if ($in instanceof SchemaItem) {
372 135
                    return $this->typeCache[$cid] = $in;
373
                }
374 7
            }
375 7
        }
376 21
    }
377
378
    /**
379
     *
380
     * @param string $getter
381
     * @param string $name
382
     * @param string $namespace
383
     * @param bool[] $calling
384
     * @param bool $throw
385
     *
386
     * @throws TypeNotFoundException
387
     * @return SchemaItem
388
     */
389 135
    protected function findSomething($getter, $name, $namespace = null, &$calling = array())
390
    {
391 135
        $in = $this->findSomethingNoThrow(
392 135
            $getter,
393 135
            $name,
394 135
            $namespace,
395 90
            $calling
396 45
        );
397
398 135
        if ($in instanceof SchemaItem) {
399 135
            return $in;
400
        }
401
402 15
        throw new TypeNotFoundException(sprintf("Can't find the %s named {%s}#%s.", substr($getter, 3), $namespace, $name));
403
    }
404
405
    /**
406
     *
407
     * @param string $name
408
     * @param string $namespace
409
     * @return Type
410
     */
411 135
    public function findType($name, $namespace = null)
412
    {
413
        /**
414
        * @var Type $out
415
        */
416 135
        $out = $this->findSomething('getType', $name, $namespace);
417
418 135
        return $out;
419
    }
420
421
    /**
422
     *
423
     * @param string $name
424
     * @param string $namespace
425
     * @return Group
426
     */
427 135
    public function findGroup($name, $namespace = null)
428
    {
429
        /**
430
        * @var Group $out
431
        */
432 135
        $out = $this->findSomething('getGroup', $name, $namespace);
433
434 135
        return $out;
435
    }
436
437
    /**
438
     *
439
     * @param string $name
440
     * @param string $namespace
441
     * @return ElementDef
442
     */
443 135
    public function findElement($name, $namespace = null)
444
    {
445
        /**
446
        * @var ElementDef $out
447
        */
448 135
        $out = $this->findSomething('getElement', $name, $namespace);
449
450 135
        return $out;
451
    }
452
453
    /**
454
     *
455
     * @param string $name
456
     * @param string $namespace
457
     * @return AttributeItem
458
     */
459 135
    public function findAttribute($name, $namespace = null)
460
    {
461
        /**
462
        * @var AttributeItem $out
463
        */
464 135
        $out = $this->findSomething('getAttribute', $name, $namespace);
465
466 135
        return $out;
467
    }
468
469
    /**
470
     *
471
     * @param string $name
472
     * @param string $namespace
473
     * @return AttributeGroup
474
     */
475 135
    public function findAttributeGroup($name, $namespace = null)
476
    {
477
        /**
478
        * @var AttributeGroup
479
        */
480 135
        $out = $this->findSomething('getAttributeGroup', $name, $namespace);
481
482 135
        return $out;
483
    }
484
485
    /**
486
    * @var Schema[]
487
    */
488
    private static $loadedFiles = array();
489
490
    /**
491
    * @param string ...$keys
492
    *
493
    * @return bool
494
    */
495 135
    public static function hasLoadedFile(...$keys)
496
    {
497 135
        foreach ($keys as $key) {
498 135
            if (isset(self::$loadedFiles[$key])) {
499 135
                return true;
500
            }
501 1
        }
502
503 3
        return false;
504
    }
505
506
    /**
507
    * @param string ...$keys
508
    *
509
    * @return Schema
510
    *
511
    * @throws RuntimeException if loaded file not found
512
    */
513 135
    public static function getLoadedFile(...$keys)
514
    {
515 135
        foreach ($keys as $key) {
516 135
            if (isset(self::$loadedFiles[$key])) {
517 135
        return self::$loadedFiles[$key];
518
            }
519
        }
520
521
        throw new RuntimeException('Loaded file was not found!');
522
    }
523
524
    /**
525
    * @param string $key
526
    *
527
    * @return Schema
528
    */
529 135
    public static function setLoadedFile($key, Schema $schema)
530
    {
531 135
        self::$loadedFiles[$key] = $schema;
532
533 135
        return $schema;
534
    }
535
536 135
    public function setSchemaThingsFromNode(
537
        DOMElement $node,
538
        Schema $parent = null
539
    ) {
540 135
        $this->setDoc(AbstractSchemaReader::getDocumentation($node));
541
542 135
        if ($node->hasAttribute("targetNamespace")) {
543 135
            $this->setTargetNamespace($node->getAttribute("targetNamespace"));
544 45
        } elseif ($parent) {
545
            $this->setTargetNamespace($parent->getTargetNamespace());
546
        }
547 135
        $this->setElementsQualification($node->getAttribute("elementFormDefault") == "qualified");
548 135
        $this->setAttributesQualification($node->getAttribute("attributeFormDefault") == "qualified");
549 135
        $this->setDoc(AbstractSchemaReader::getDocumentation($node));
550 135
    }
551
552
    /**
553
    * @param string $file
554
    * @param string $namespace
555
    *
556
    * @return Closure
557
    */
558 135
    public static function loadImport(
559
        SchemaReaderLoadAbstraction $reader,
560
        Schema $schema,
561
        DOMElement $node
562
    ) {
563 135
        $base = urldecode($node->ownerDocument->documentURI);
564 135
        $file = UrlUtils::resolveRelativeUrl($base, $node->getAttribute("schemaLocation"));
565
566 135
        $namespace = $node->getAttribute("namespace");
567
568 135
        $globalSchemaInfo = $reader->getGlobalSchemaInfo();
569
570 135
        $keys = [];
571
572 135
        if (isset($globalSchemaInfo[$namespace])) {
573 135
            $keys[] = $globalSchemaInfo[$namespace];
574 45
        }
575
576 135
        $keys[] = $reader->getNamespaceSpecificFileIndex(
577 135
            $file,
578 90
            $namespace
579 45
        );
580
581 135
        $keys[] = $file;
582
583
        if (
584 135
            Schema::hasLoadedFile(...$keys)
585 45
        ) {
586 135
            $schema->addSchema(Schema::getLoadedFile(...$keys));
587
588
            return function() {
589 135
            };
590
        }
591
592 3
        return function () use ($namespace, $reader, $schema, $file) {
593 3
        $newSchema = Schema::setLoadedFile(
594 3
            $file,
595 3
            ($namespace ? new Schema() : $schema)
596 1
        );
597
598 3
        if ($namespace) {
599
            $newSchema->addSchema($reader->getGlobalSchema());
600
            $schema->addSchema($newSchema);
601
        }
602 3
            $callbacks = $reader->schemaNode(
603 3
                $newSchema,
604 3
                $reader->getDOM(
605 3
                    $reader->hasKnownSchemaLocation($file)
606 1
                        ? $reader->getKnownSchemaLocation($file)
607 2
                        : $file
608 3
                )->documentElement,
609 2
                $schema
610 1
            );
611 3
            foreach ($callbacks as $callback) {
612 3
                $callback();
613 1
            }
614 3
        };
615
    }
616
}
617