Test Failed
Push — static-analysis ( a7d7d4...0e82cb )
by SignpostMarv
03:17
created

Schema::loadImportFreshCallbacks()   B

Complexity

Conditions 4
Paths 2

Size

Total Lines 26
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 4.0629

Importance

Changes 0
Metric Value
cc 4
eloc 15
nc 2
nop 4
dl 0
loc 26
ccs 16
cts 19
cp 0.8421
crap 4.0629
rs 8.5806
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
        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
        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
        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
        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
        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
        } elseif (
326 135
            $this->getTargetNamespace() === $namespace &&
327 135
            (($item = $this->$getter($name)) instanceof SchemaItem)
328 45
        ) {
329 135
            return $this->typeCache[$cid] = $item;
330
        }
331
332 135
        return $this->findSomethingNoThrowSchemas(
333 135
            $this->getSchemas(),
334 135
            $cid,
335 135
            $getter,
336 135
            $name,
337 135
            $namespace,
338 90
            $calling
339 45
        );
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 135
    protected function findSomethingNoThrowSchemas(
355
        array $schemas,
356
        $cid,
357
        $getter,
358
        $name,
359
        $namespace = null,
360
        array & $calling = array()
361
    ) {
362 135
        foreach ($this->getSchemas() as $childSchema) {
363 135
            if (!isset($calling[spl_object_hash($childSchema)])) {
364 135
                $in = $childSchema->findSomethingNoThrow($getter, $name, $namespace, $calling);
365
366 135
                if ($in instanceof SchemaItem) {
367 135
                    return $this->typeCache[$cid] = $in;
368
                }
369 7
            }
370 7
        }
371 21
    }
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 135
    protected function findSomething($getter, $name, $namespace = null, &$calling = array())
385
    {
386 135
        $in = $this->findSomethingNoThrow(
387 135
            $getter,
388 135
            $name,
389 135
            $namespace,
390 90
            $calling
391 45
        );
392
393 135
        if ($in instanceof SchemaItem) {
394 135
            return $in;
395
        }
396
397 15
        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 135
    public function findType($name, $namespace = null)
407
    {
408
        /**
409
        * @var Type $out
410
        */
411 135
        $out = $this->findSomething('getType', $name, $namespace);
412
413 135
        return $out;
414
    }
415
416
    /**
417
     *
418
     * @param string $name
419
     * @param string $namespace
420
     * @return Group
421
     */
422 135
    public function findGroup($name, $namespace = null)
423
    {
424
        /**
425
        * @var Group $out
426
        */
427 135
        $out = $this->findSomething('getGroup', $name, $namespace);
428
429 135
        return $out;
430
    }
431
432
    /**
433
     *
434
     * @param string $name
435
     * @param string $namespace
436
     * @return ElementDef
437
     */
438 135
    public function findElement($name, $namespace = null)
439
    {
440
        /**
441
        * @var ElementDef $out
442
        */
443 135
        $out = $this->findSomething('getElement', $name, $namespace);
444
445 135
        return $out;
446
    }
447
448
    /**
449
     *
450
     * @param string $name
451
     * @param string $namespace
452
     * @return AttributeItem
453
     */
454 135
    public function findAttribute($name, $namespace = null)
455
    {
456
        /**
457
        * @var AttributeItem $out
458
        */
459 135
        $out = $this->findSomething('getAttribute', $name, $namespace);
460
461 135
        return $out;
462
    }
463
464
    /**
465
     *
466
     * @param string $name
467
     * @param string $namespace
468
     * @return AttributeGroup
469
     */
470 135
    public function findAttributeGroup($name, $namespace = null)
471
    {
472
        /**
473
        * @var AttributeGroup
474
        */
475 135
        $out = $this->findSomething('getAttributeGroup', $name, $namespace);
476
477 135
        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 135
    public static function hasLoadedFile(...$keys)
491
    {
492 135
        foreach ($keys as $key) {
493 135
            if (isset(self::$loadedFiles[$key])) {
494 135
                return true;
495
            }
496 1
        }
497
498 3
        return false;
499
    }
500
501
    /**
502
    * @param string ...$keys
503
    *
504
    * @return Schema
505
    *
506
    * @throws RuntimeException if loaded file not found
507
    */
508 135
    public static function getLoadedFile(...$keys)
509
    {
510 135
        foreach ($keys as $key) {
511 135
            if (isset(self::$loadedFiles[$key])) {
512 135
        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 135
    public static function setLoadedFile($key, Schema $schema)
525
    {
526 135
        self::$loadedFiles[$key] = $schema;
527
528 135
        return $schema;
529
    }
530
531 135
    public function setSchemaThingsFromNode(
532
        DOMElement $node,
533
        Schema $parent = null
534
    ) {
535 135
        $this->setDoc(AbstractSchemaReader::getDocumentation($node));
536
537 135
        if ($node->hasAttribute("targetNamespace")) {
538 135
            $this->setTargetNamespace($node->getAttribute("targetNamespace"));
539 45
        } elseif ($parent) {
540
            $this->setTargetNamespace($parent->getTargetNamespace());
541
        }
542 135
        $this->setElementsQualification($node->getAttribute("elementFormDefault") == "qualified");
543 135
        $this->setAttributesQualification($node->getAttribute("attributeFormDefault") == "qualified");
544 135
        $this->setDoc(AbstractSchemaReader::getDocumentation($node));
545 135
    }
546
547
    /**
548
    * @param string $file
549
    * @param string $namespace
550
    *
551
    * @return Closure
552
    */
553 135
    public static function loadImport(
554
        SchemaReaderLoadAbstraction $reader,
555
        Schema $schema,
556
        DOMElement $node
557
    ) {
558 135
        $base = urldecode($node->ownerDocument->documentURI);
559 135
        $file = UrlUtils::resolveRelativeUrl($base, $node->getAttribute("schemaLocation"));
560
561 135
        $namespace = $node->getAttribute("namespace");
562
563 135
        $globalSchemaInfo = $reader->getGlobalSchemaInfo();
564
565 135
        $keys = [];
566
567 135
        if (isset($globalSchemaInfo[$namespace])) {
568 135
            $keys[] = $globalSchemaInfo[$namespace];
569 45
        }
570
571 135
        $keys[] = $reader->getNamespaceSpecificFileIndex(
572 135
            $file,
573 90
            $namespace
574 45
        );
575
576 135
        $keys[] = $file;
577
578
        if (
579 135
            Schema::hasLoadedFile(...$keys)
580 45
        ) {
581 135
            $schema->addSchema(Schema::getLoadedFile(...$keys));
582
583
            return function() {
584 135
            };
585
        }
586
587 3
        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 3
            $newSchema = Schema::setLoadedFile(
603 3
                $file,
604 3
                ($namespace ? new Schema() : $schema)
605 3
            );
606 1
607
            if ($namespace) {
608 3
                $newSchema->addSchema($reader->getGlobalSchema());
609
                $schema->addSchema($newSchema);
610
            }
611
            $callbacks = $reader->schemaNode(
612 3
                $newSchema,
613 3
                $reader->getDOM(
614 3
                    $reader->hasKnownSchemaLocation($file)
615 3
                        ? $reader->getKnownSchemaLocation($file)
616 1
                        : $file
617 2
                )->documentElement,
618 3
                $schema
619 2
            );
620 1
621 3
        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 3
    }
623 1
624 3
    /**
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