Passed
Branch master (117216)
by Pavel
08:06
created

DestinationGenerator::visitFiller()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 15
rs 9.2
c 0
b 0
f 0
cc 4
eloc 8
nc 4
nop 3
1
<?php
2
3
namespace Bankiru\Seo\Generator;
4
5
use Bankiru\Seo\BankiruSeoBridge\SeoFilterFactoryInterface;
6
use Bankiru\Seo\Destination;
7
use Bankiru\Seo\DestinationInterface;
8
use Bankiru\Seo\Exception\DestinationException;
9
use Bankiru\Seo\SourceInterface;
10
use Bankiru\Seo\SourceFiller;
11
12
final class DestinationGenerator
13
{
14
    /**
15
     * @param string            $route
16
     * @param SourceInterface[] $sources
17
     * @param SourceFiller[]    $fillers
18
     *
19
     * @return DestinationInterface[]
20
     * @throws DestinationException
21
     */
22
    public function generate($route, array $sources, array $fillers = [])
23
    {
24
        $itemsCollection = $this->cartesian($sources);
25
26
        $this->validateFillerCircularity($fillers);
27
28
        $itemsCollection = $this->infer($itemsCollection, $fillers);
0 ignored issues
show
Documentation introduced by
$fillers is of type array<integer,object<Bankiru\Seo\SourceFiller>>, but the function expects a array<integer,object<Ban...ilterFactoryInterface>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
29
30
        return array_map(
31
            function ($items) use ($route) {
32
                return new Destination($route, $items);
33
            },
34
            $itemsCollection
35
        );
36
    }
37
38
    /**
39
     * @param \Countable[] $sources
40
     *
41
     * @return int|null
42
     */
43
    public function count(array $sources)
44
    {
45
        if (count($sources) === 0) {
46
            return 0;
47
        }
48
49
        $count = 1;
50
        foreach ($sources as $source) {
51
            $count *= $source->count();
52
        }
53
54
        return $count;
55
    }
56
57
    /**
58
     * Returns cartesian product of sources
59
     *
60
     * @param SourceInterface[] $sources
61
     *
62
     * @return array
63
     */
64
    private function cartesian($sources)
65
    {
66
        $result = [[]];
67
68
        foreach ($sources as $key => $source) {
69
            $append = [];
70
71
            foreach ($result as $product) {
72
                foreach ($source as $item) {
73
                    $product[$key] = $item;
74
                    $append[]      = $product;
75
                }
76
            }
77
78
            $result = $append;
79
        }
80
81
        return $result;
82
    }
83
84
    /**
85
     * @param SourceFiller[] $fillers
86
     *
87
     * @throws \RuntimeException
88
     */
89
    private function validateFillerCircularity(array $fillers)
90
    {
91
        foreach ($fillers as $code => $filler) {
92
            $visited = [$code];
93
            $this->visitFiller($filler, $fillers, $visited);
0 ignored issues
show
Documentation introduced by
$fillers is of type array<integer,object<Bankiru\Seo\SourceFiller>>, but the function expects a array<integer,object<Ban...ilterFactoryInterface>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
94
        }
95
    }
96
97
    /**
98
     * @param SourceFiller                $filler
99
     * @param SeoFilterFactoryInterface[] $fillers
100
     * @param string[]                    $visited
101
     *
102
     * @throws DestinationException
103
     */
104
    private function visitFiller(SourceFiller $filler, array $fillers, array &$visited)
105
    {
106
        foreach ($filler->getDependencies() as $code) {
107
            if (!array_key_exists($code, $fillers)) {
108
                continue;
109
            }
110
111
            if (in_array($code, $visited, true)) {
112
                throw DestinationException::circularInference($code, $visited);
113
            }
114
115
            $visited[] = $code;
116
            $this->visitFiller($fillers[$code], $fillers, $visited);
117
        }
118
    }
119
120
    /**
121
     * @param array[]                     $items
122
     * @param SeoFilterFactoryInterface[] $fillers
123
     *
124
     * @return array
125
     * @throws DestinationException
126
     */
127
    private function infer(array $items, array $fillers)
128
    {
129
        foreach ($items as &$item) {
130
            foreach ($fillers as $code => $filler) {
131
                $item = $this->inferCode($item, $code, $fillers);
132
            }
133
        }
134
135
        return $items;
136
    }
137
138
    /**
139
     * @param array          $item
140
     * @param string         $code
141
     * @param SourceFiller[] $fillers
142
     *
143
     * @return array
144
     *
145
     * @throws DestinationException
146
     */
147
    private function inferCode(array $item, $code, array $fillers)
148
    {
149
        if (!array_key_exists($code, $fillers)) {
150
            DestinationException::inferenceFailed($code, $item, $fillers);
151
        }
152
153
        $filler = $fillers[$code];
154
        foreach ($filler->getDependencies() as $key) {
155
            if (!array_key_exists($key, $item)) {
156
                $item[$key] = $this->inferCode($item, $key, $fillers);
157
            }
158
        }
159
        $item[$code] = $filler->infer($item);
160
161
        return $item;
162
    }
163
}
164