Prototype::process()   B
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 14
nc 5
nop 1
dl 0
loc 23
ccs 14
cts 14
cp 1
crap 5
rs 8.5906
c 0
b 0
f 0
1
<?php
2
declare(strict_types = 1);
3
4
namespace Innmind\Config\Structure;
5
6
use Innmind\Config\{
7
    Structure,
8
    Structures,
9
    Properties,
10
    Property,
11
    Exception\OnlyOnePrototypeDefinitionAllowed,
12
    Exception\SchemaNotParseable,
13
    Exception\InvalidArgumentException,
14
};
15
use Innmind\Immutable\{
16
    MapInterface,
17
    Map,
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Innmind\Config\Structure\Map. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
18
    Str,
19
};
20
21
final class Prototype implements Structure
22
{
23
    private const PATTERN = '~^prototype<(?<type>(int|string|scalar))>\+?$~';
24
    private $prototype;
25
    private $prototypeKey;
26
    private $structure;
27
    private $structureKeys;
28
    private $requiresValue;
29
30
    /**
31
     * @param Property\Structure $prototype
0 ignored issues
show
Bug introduced by
The type Innmind\Config\Property\Structure was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
32
     */
33 9
    private function __construct(
34
        $prototype,
35
        Property $prototypeKey,
36
        Structure $structure,
37
        array $structureKeys,
38
        bool $requiresValue
39
    ) {
40 9
        $this->prototype = $prototype;
41 9
        $this->prototypeKey = $prototypeKey;
42 9
        $this->structure = $structure;
43 9
        $this->structureKeys = array_flip($structureKeys);
44 9
        $this->requiresValue = $requiresValue;
45 9
    }
46
47 22
    public static function build(
48
        array $schema,
49
        Structures $structures,
50
        Properties $properties
51
    ): Structure {
52 22
        $prototype = $prototypeKey = null;
53 22
        $keyToRemove = null;
54
55 22
        foreach ($schema as $key => $value) {
56 19
            if (!is_string($key) || !Str::of($key)->matches(self::PATTERN)) {
57 11
                continue;
58
            }
59
60 12
            if (!is_null($prototype)) {
61 1
                throw new OnlyOnePrototypeDefinitionAllowed;
62
            }
63
64 12
            $prototypeKey = $properties->build(
65 12
                Str::of($key)->capture(self::PATTERN)->get('type')
66
            );
67 12
            $keyToRemove = $key;
68
69 12
            if (is_string($value)) {
70
                try {
71 8
                    $prototype = $properties->build(Str::of($value));
72
                } catch (SchemaNotParseable $e) {
73
                    throw new SchemaNotParseable($key, 0, $e);
74
                }
75
76 8
                continue;
77
            }
78
79 4
            if (is_array($value)) {
80
                try {
81 3
                    $prototype = $structures->build($value, $properties);
82 1
                } catch (SchemaNotParseable $e) {
83 1
                    throw new SchemaNotParseable($key, 0, $e);
84
                }
85
86 2
                continue;
87
            }
88
89 1
            throw new SchemaNotParseable($key);
90
        }
91
92 20
        if (is_null($prototype)) {
93 20
            throw new SchemaNotParseable;
94
        }
95
96 9
        unset($schema[$keyToRemove]);
97
98 9
        return new self(
99 9
            $prototype,
100 9
            $prototypeKey,
101 9
            $structures->build($schema, $properties),
102 9
            array_keys($schema),
103 9
            (string) Str::of($keyToRemove)->substring(-1) === '+'
0 ignored issues
show
Bug introduced by
It seems like $keyToRemove can also be of type null; however, parameter $value of Innmind\Immutable\Str::of() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

103
            (string) Str::of(/** @scrutinizer ignore-type */ $keyToRemove)->substring(-1) === '+'
Loading history...
104
        );
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110 6
    public function process(array $data): MapInterface
111
    {
112 6
        $prototypes = array_diff_key($data, $this->structureKeys);
0 ignored issues
show
Bug introduced by
It seems like $this->structureKeys can also be of type null; however, parameter $array2 of array_diff_key() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

112
        $prototypes = array_diff_key($data, /** @scrutinizer ignore-type */ $this->structureKeys);
Loading history...
113 6
        $map = new Map('scalar', 'mixed');
114
115 6
        foreach ($prototypes as $key => $value) {
116
            try {
117 5
                $map = $map->put(
118 5
                    $this->prototypeKey->process($key),
119 5
                    $this->prototype->process($value)
120
                );
121 2
            } catch (InvalidArgumentException $e) {
122 5
                throw new InvalidArgumentException((string) $key, 0, $e);
123
            }
124
        }
125
126 4
        if ($this->requiresValue && $map->size() === 0) {
127 1
            throw new InvalidArgumentException;
128
        }
129
130 3
        return $map->merge(
131 3
            $this->structure->process(
132 3
                array_intersect_key($data, $this->structureKeys)
0 ignored issues
show
Bug introduced by
It seems like $this->structureKeys can also be of type null; however, parameter $array2 of array_intersect_key() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

132
                array_intersect_key($data, /** @scrutinizer ignore-type */ $this->structureKeys)
Loading history...
133
            )
134
        );
135
    }
136
}
137