Test Failed
Pull Request — master (#18)
by SignpostMarv
03:33
created

Schema::findType()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
ccs 0
cts 0
cp 0
crap 2
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 45
    protected $types = array();
46
47 45
    /**
48 45
    * @var ElementDef[]
49
    */
50
    protected $elements = array();
51
52
    /**
53
    * @var Group[]
54
    */
55 45
    protected $groups = array();
56
57 45
    /**
58 45
    * @var AttributeGroup[]
59
    */
60 45
    protected $attributeGroups = array();
61
62 45
    /**
63
    * @var AttributeDef[]
64
    */
65 45
    protected $attributes = array();
66
67 45
    /**
68 45
    * @var string|null
69
    */
70 5
    protected $doc;
71
72 5
    /**
73
    * @var \GoetasWebservices\XML\XSDReader\Schema\SchemaItem[]
74
    */
75 3
    private $typeCache = array();
76
77 3
    /**
78
    * @return bool
79
    */
80 45
    public function getElementsQualification()
81
    {
82 45
        return $this->elementsQualification;
83
    }
84
85 1
    /**
86
    * @param bool $elementsQualification
87 1
    */
88
    public function setElementsQualification($elementsQualification)
89
    {
90 2
        $this->elementsQualification = $elementsQualification;
91
    }
92 2
93
    /**
94
    * @return bool
95
    */
96
    public function getAttributesQualification()
97
    {
98
        return $this->attributesQualification;
99
    }
100 45
101
    /**
102 45
    * @param bool $attributesQualification
103 45
    */
104
    public function setAttributesQualification($attributesQualification)
105 45
    {
106
        $this->attributesQualification = $attributesQualification;
107 45
    }
108 45
109
    /**
110 45
    * @return string|null
111
    */
112 45
    public function getTargetNamespace()
113 45
    {
114
        return $this->targetNamespace;
115 45
    }
116
117 45
    /**
118
    * @param string|null $targetNamespace
119
    */
120
    public function setTargetNamespace($targetNamespace)
121 45
    {
122 45
        $this->targetNamespace = $targetNamespace;
123 45
    }
124 45
125
    /**
126 45
    * @return Type[]
127
    */
128 45
    public function getTypes()
129
    {
130 45
        return $this->types;
131 45
    }
132
133 45
    /**
134
    * @return ElementDef[]
135 45
    */
136 45
    public function getElements()
137
    {
138 45
        return $this->elements;
139
    }
140 45
141 45
    /**
142
    * @return Schema[]
143 1
    */
144
    public function getSchemas()
145 1
    {
146
        return $this->schemas;
147
    }
148
149
    /**
150
    * @return AttributeDef[]
151
    */
152
    public function getAttributes()
153 45
    {
154
        return $this->attributes;
155 45
    }
156 45
157
    /**
158 1
    * @return Group[]
159
    */
160
    public function getGroups()
161
    {
162
        return $this->groups;
163
    }
164
165
    /**
166 45
    * @return string|null
167
    */
168 45
    public function getDoc()
169 45
    {
170
        return $this->doc;
171 1
    }
172
173
    /**
174
    * @param string $doc
175
    */
176
    public function setDoc($doc)
177
    {
178
        $this->doc = $doc;
179 45
    }
180
181 45
    public function addType(Type $type)
182 45
    {
183
        $this->types[$type->getName()] = $type;
184 1
    }
185
186
    public function addElement(ElementDef $element)
187
    {
188
        $this->elements[$element->getName()] = $element;
189
    }
190
191
    /**
192 45
    * @param string|null $namespace
193
    */
194 45
    public function addSchema(Schema $schema, $namespace = null)
195 45
    {
196
        if ($namespace !== null) {
197 1
            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 45
            }
206
            $this->schemas[$namespace] = $schema;
207 45
        } else {
208 45
            $this->schemas[] = $schema;
209
        }
210 1
    }
211
212
    public function addAttribute(AttributeDef $attribute)
213
    {
214
        $this->attributes[$attribute->getName()] = $attribute;
215
    }
216
217
    public function addGroup(Group $group)
218
    {
219
        $this->groups[$group->getName()] = $group;
220
    }
221
222
    public function addAttributeGroup(AttributeGroup $group)
223
    {
224
        $this->attributeGroups[$group->getName()] = $group;
225
    }
226 45
227
    /**
228 45
    * @return AttributeGroup[]
229 45
    */
230
    public function getAttributeGroups()
231 45
    {
232 45
        return $this->attributeGroups;
233
    }
234
235 45
    /**
236 45
     *
237 45
     * @param string $name
238
     * @return Group|false
239 5
     */
240 45
    public function getGroup($name)
241 45
    {
242
        if (isset($this->groups[$name])) {
243 45
            return $this->groups[$name];
244 7
        }
245
        return false;
246 7
    }
247 7
248 7
    /**
249
     *
250
     * @param string $name
251
     * @return ElementItem|false
252
     */
253
    public function getElement($name)
254
    {
255
        if (isset($this->elements[$name])) {
256
            return $this->elements[$name];
257 45
        }
258
        return false;
259 45
    }
260
261
    /**
262
     *
263
     * @param string $name
264
     * @return Type|false
265
     */
266
    public function getType($name)
267
    {
268 45
        if (isset($this->types[$name])) {
269
            return $this->types[$name];
270 45
        }
271
        return false;
272
    }
273
274
    /**
275
     *
276
     * @param string $name
277
     * @return AttributeItem|false
278
     */
279 45
    public function getAttribute($name)
280
    {
281 45
        if (isset($this->attributes[$name])) {
282
            return $this->attributes[$name];
283
        }
284
        return false;
285
    }
286
287
    /**
288
     *
289
     * @param string $name
290 45
     * @return AttributeGroup|false
291
     */
292 45
    public function getAttributeGroup($name)
293
    {
294
        if (isset($this->attributeGroups[$name])) {
295
            return $this->attributeGroups[$name];
296
        }
297
        return false;
298
    }
299
300
    public function __toString()
301 45
    {
302
        return sprintf("Target namespace %s", $this->getTargetNamespace());
303 45
    }
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
    protected function findSomethingNoThrow(
315
        $getter,
316
        $name,
317
        $namespace = null,
318
        array & $calling = array()
319
    ) {
320
        $calling[spl_object_hash($this)] = true;
321
        $cid = "$getter, $name, $namespace";
322
323
        if (isset($this->typeCache[$cid])) {
324
            return $this->typeCache[$cid];
325
        } elseif (
326
            $this->getTargetNamespace() === $namespace &&
327
            (($item = $this->$getter($name)) instanceof SchemaItem)
328
        ) {
329
            return $this->typeCache[$cid] = $item;
330
        }
331
332
        return $this->findSomethingNoThrowSchemas(
333
            $this->getSchemas(),
334
            $cid,
335
            $getter,
336
            $name,
337
            $namespace,
338
            $calling
339
        );
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
    protected function findSomethingNoThrowSchemas(
355
        array $schemas,
356
        $cid,
357
        $getter,
358
        $name,
359
        $namespace = null,
360
        array & $calling = array()
361
    ) {
362
        foreach ($this->getSchemas() as $childSchema) {
363
            if (!isset($calling[spl_object_hash($childSchema)])) {
364
                $in = $childSchema->findSomethingNoThrow($getter, $name, $namespace, $calling);
365
366
                if ($in instanceof SchemaItem) {
367
                    return $this->typeCache[$cid] = $in;
368
                }
369
            }
370
        }
371
    }
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
    protected function findSomething($getter, $name, $namespace = null, &$calling = array())
385
    {
386
        $in = $this->findSomethingNoThrow(
387
            $getter,
388
            $name,
389
            $namespace,
390
            $calling
391
        );
392
393
        if ($in instanceof SchemaItem) {
394
            return $in;
395
        }
396
397
        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
    public function findType($name, $namespace = null)
407
    {
408
        /**
409
        * @var Type $out
410
        */
411
        $out = $this->findSomething('getType', $name, $namespace);
412
413
        return $out;
414
    }
415
416
    /**
417
     *
418
     * @param string $name
419
     * @param string $namespace
420
     * @return Group
421
     */
422
    public function findGroup($name, $namespace = null)
423
    {
424
        /**
425
        * @var Group $out
426
        */
427
        $out = $this->findSomething('getGroup', $name, $namespace);
428
429
        return $out;
430
    }
431
432
    /**
433
     *
434
     * @param string $name
435
     * @param string $namespace
436
     * @return ElementDef
437
     */
438
    public function findElement($name, $namespace = null)
439
    {
440
        /**
441
        * @var ElementDef $out
442
        */
443
        $out = $this->findSomething('getElement', $name, $namespace);
444
445
        return $out;
446
    }
447
448
    /**
449
     *
450
     * @param string $name
451
     * @param string $namespace
452
     * @return AttributeItem
453
     */
454
    public function findAttribute($name, $namespace = null)
455
    {
456
        /**
457
        * @var AttributeItem $out
458
        */
459
        $out = $this->findSomething('getAttribute', $name, $namespace);
460
461
        return $out;
462
    }
463
464
    /**
465
     *
466
     * @param string $name
467
     * @param string $namespace
468
     * @return AttributeGroup
469
     */
470
    public function findAttributeGroup($name, $namespace = null)
471
    {
472
        /**
473
        * @var AttributeGroup
474
        */
475
        $out = $this->findSomething('getAttributeGroup', $name, $namespace);
476
477
        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
    public static function hasLoadedFile(...$keys)
491
    {
492
        foreach ($keys as $key) {
493
            if (isset(self::$loadedFiles[$key])) {
494
                return true;
495
            }
496
        }
497
498
        return false;
499
    }
500
501
    /**
502
    * @param string ...$keys
503
    *
504
    * @return Schema
505
    *
506
    * @throws RuntimeException if loaded file not found
507
    */
508
    public static function getLoadedFile(...$keys)
509
    {
510
        foreach ($keys as $key) {
511
            if (isset(self::$loadedFiles[$key])) {
512
        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
    public static function setLoadedFile($key, Schema $schema)
525
    {
526
        self::$loadedFiles[$key] = $schema;
527
528
        return $schema;
529
    }
530
531
    public function setSchemaThingsFromNode(
532
        DOMElement $node,
533
        Schema $parent = null
534
    ) {
535
        $this->setDoc(AbstractSchemaReader::getDocumentation($node));
536
537
        if ($node->hasAttribute("targetNamespace")) {
538
            $this->setTargetNamespace($node->getAttribute("targetNamespace"));
539
        } elseif ($parent) {
540
            $this->setTargetNamespace($parent->getTargetNamespace());
541
        }
542
        $this->setElementsQualification($node->getAttribute("elementFormDefault") == "qualified");
543
        $this->setAttributesQualification($node->getAttribute("attributeFormDefault") == "qualified");
544
        $this->setDoc(AbstractSchemaReader::getDocumentation($node));
545
    }
546
547
    /**
548
    * @param string $file
549
    * @param string $namespace
550
    *
551
    * @return Closure
552
    */
553
    public static function loadImport(
554
        SchemaReaderLoadAbstraction $reader,
555
        Schema $schema,
556
        DOMElement $node
557
    ) {
558
        $base = urldecode($node->ownerDocument->documentURI);
559
        $file = UrlUtils::resolveRelativeUrl($base, $node->getAttribute("schemaLocation"));
560
561
        $namespace = $node->getAttribute("namespace");
562
563
        $globalSchemaInfo = $reader->getGlobalSchemaInfo();
564
565
        $keys = [];
566
567
        if (isset($globalSchemaInfo[$namespace])) {
568
            $keys[] = $globalSchemaInfo[$namespace];
569
        }
570
571
        $keys[] = $reader->getNamespaceSpecificFileIndex(
572
            $file,
573
            $namespace
574
        );
575
576
        $keys[] = $file;
577
578
        if (
579
            Schema::hasLoadedFile(...$keys)
580
        ) {
581
            $schema->addSchema(Schema::getLoadedFile(...$keys));
582
583
            return function() {
584
            };
585
        }
586
587
        return static::loadImportFresh($namespace, $reader, $schema, $file);
588
    }
589
590
    /**
591
    * @param string $namespace
592
    * @param string $file
593
    *
594
    * @return Closure
595
    */
596
    protected static function loadImportFreshCallbacks(
597
        $namespace,
598
        SchemaReaderLoadAbstraction $reader,
599
        Schema $schema,
600
        $file
601
    ) {
602
            $newSchema = Schema::setLoadedFile(
603
                $file,
604
                ($namespace ? new Schema() : $schema)
605
            );
606
607
            if ($namespace) {
608
                $newSchema->addSchema($reader->getGlobalSchema());
609
                $schema->addSchema($newSchema);
610
            }
611
            $callbacks = $reader->schemaNode(
612
                $newSchema,
613
                $reader->getDOM(
614
                    $reader->hasKnownSchemaLocation($file)
615
                        ? $reader->getKnownSchemaLocation($file)
616
                        : $file
617
                )->documentElement,
618
                $schema
619
            );
620
621
        return $callbacks;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $callbacks returns the type Closure[] which is incompatible with the documented return type Closure.
Loading history...
622
    }
623
624
    /**
625
    * @param string $namespace
626
    * @param string $file
627
    *
628
    * @return Closure
629
    */
630
    protected static function loadImportFresh(
631
        $namespace,
632
        SchemaReaderLoadAbstraction $reader,
633
        Schema $schema,
634
        $file
635
    ) {
636
        return function () use ($namespace, $reader, $schema, $file) {
0 ignored issues
show
Unused Code introduced by
The import $reader is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
Unused Code introduced by
The import $namespace is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
Unused Code introduced by
The import $schema is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
Unused Code introduced by
The import $file is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
637
            foreach (static::loadImportFreshCallbacks() as $callback) {
0 ignored issues
show
Bug introduced by
The call to GoetasWebservices\XML\XS...dImportFreshCallbacks() has too few arguments starting with namespace. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

637
            foreach (static::/** @scrutinizer ignore-call */ loadImportFreshCallbacks() as $callback) {

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
638
                $callback();
639
            }
640
        };
641
    }
642
}
643