Passed
Push — static-analysis ( b7ede5...e35b6b )
by SignpostMarv
03:33
created

Schema::findSomethingNoThrow()   C

Complexity

Conditions 8
Paths 10

Size

Total Lines 29
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 8

Importance

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