Completed
Push — master ( 5d85cb...99533f )
by Pavel
03:42
created

CartesianDestinationGenerator::cartesian()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 13
cts 13
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 10
nc 4
nop 1
crap 4
1
<?php
2
3
namespace Bankiru\Seo\Destination;
4
5
use Bankiru\Seo\Destination;
6
use Bankiru\Seo\Exception\DestinationException;
7
use Bankiru\Seo\SourceFiller;
8
use Bankiru\Seo\SourceInterface;
9
10
final class CartesianDestinationGenerator implements DestinationGenerator
11
{
12
    /** {@inheritdoc} */
13 6
    public function generate($route, array $sources, array $fillers = [])
14
    {
15 6
        $itemsCollection = $this->cartesian($sources);
16
17 6
        $this->validateFillerCircularity($fillers);
18
19 5
        $itemsCollection = $this->infer($itemsCollection, $fillers);
20
21 4
        return array_map(
22 4
            function ($items) use ($route) {
23 4
                return new Destination($route, $items);
24 4
            },
25
            $itemsCollection
26 4
        );
27
    }
28
29
    /** {@inheritdoc} */
30 1
    public function count(array $sources)
31
    {
32 1
        if (count($sources) === 0) {
33
            return 0;
34
        }
35
36 1
        $count = 1;
37 1
        foreach ($sources as $source) {
38 1
            $count *= $source->count();
39 1
        }
40
41 1
        return $count;
42
    }
43
44
    /**
45
     * Returns cartesian product of sources
46
     *
47
     * @param SourceInterface[] $sources
48
     *
49
     * @return array
50
     */
51 6
    private function cartesian($sources)
52
    {
53 6
        $result = [[]];
54
55 6
        foreach ($sources as $key => $source) {
56 6
            $append = [];
57
58 6
            foreach ($result as $product) {
59 6
                foreach ($source as $item) {
60 6
                    $product[$key] = $item;
61 6
                    $append[]      = $product;
62 6
                }
63 6
            }
64
65 6
            $result = $append;
66 6
        }
67
68 6
        return $result;
69
    }
70
71
    /**
72
     * @param SourceFiller[] $fillers
73
     *
74
     * @throws DestinationException
75
     */
76 6
    private function validateFillerCircularity(array $fillers)
77
    {
78 6
        foreach ($fillers as $code => $filler) {
79 4
            $visited = [$code];
80 4
            $this->visitFiller($filler, $fillers, $visited);
81 5
        }
82 5
    }
83
84
    /**
85
     * @param SourceFiller   $filler
86
     * @param SourceFiller[] $fillers
87
     * @param string[]       $visited
88
     *
89
     * @throws DestinationException
90
     */
91 4
    private function visitFiller(SourceFiller $filler, array $fillers, array &$visited)
92
    {
93 4
        foreach ($filler->getDependencies() as $code) {
94 4
            if (!array_key_exists($code, $fillers)) {
95 3
                continue;
96
            }
97
98 1
            if (in_array($code, $visited, true)) {
99 1
                throw DestinationException::circularInference($code, $visited);
100
            }
101
102 1
            $visited[] = $code;
103 1
            $this->visitFiller($fillers[$code], $fillers, $visited);
104 3
        }
105 3
    }
106
107
    /**
108
     * @param array[]        $items
109
     * @param SourceFiller[] $fillers
110
     *
111
     * @return array
112
     * @throws DestinationException
113
     */
114 5
    private function infer(array $items, array $fillers)
115
    {
116 5
        foreach ($items as &$item) {
117 5
            foreach ($fillers as $code => $filler) {
118 3
                $item = $this->inferCode($item, $code, $fillers);
119 5
            }
120 4
        }
121
122 4
        return $items;
123
    }
124
125
    /**
126
     * @param array          $item
127
     * @param string         $code
128
     * @param SourceFiller[] $fillers
129
     *
130
     * @return array
131
     *
132
     * @throws DestinationException
133
     */
134 3
    private function inferCode(array $item, $code, array $fillers)
135
    {
136 3
        if (!array_key_exists($code, $fillers)) {
137 1
            throw DestinationException::inferenceFailed($code, $item, $fillers);
138
        }
139
140 3
        $filler = $fillers[$code];
141 3
        foreach ($filler->getDependencies() as $key) {
142 3
            if (!array_key_exists($key, $item)) {
143 1
                $item[$key] = $this->inferCode($item, $key, $fillers);
144
            }
145 3
        }
146 3
        $item[$code] = $filler->infer($item);
147
148 3
        return $item;
149
    }
150
}
151