Completed
Push — master ( 05d77e...ee005d )
by Pavel
06:28
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 5
    public function generate($route, array $sources, array $fillers = [])
14
    {
15 5
        $itemsCollection = $this->cartesian($sources);
16
17 5
        $this->validateFillerCircularity($fillers);
18
19 4
        $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
    public function count(array $sources)
31
    {
32
        if (count($sources) === 0) {
33
            return 0;
34
        }
35
36
        $count = 1;
37
        foreach ($sources as $source) {
38
            $count *= $source->count();
39
        }
40
41
        return $count;
42
    }
43
44
    /**
45
     * Returns cartesian product of sources
46
     *
47
     * @param SourceInterface[] $sources
48
     *
49
     * @return array
50
     */
51 5
    private function cartesian($sources)
52
    {
53 5
        $result = [[]];
54
55 5
        foreach ($sources as $key => $source) {
56 5
            $append = [];
57
58 5
            foreach ($result as $product) {
59 5
                foreach ($source as $item) {
60 5
                    $product[$key] = $item;
61 5
                    $append[]      = $product;
62 5
                }
63 5
            }
64
65 5
            $result = $append;
66 5
        }
67
68 5
        return $result;
69
    }
70
71
    /**
72
     * @param SourceFiller[] $fillers
73
     *
74
     * @throws DestinationException
75
     */
76 5
    private function validateFillerCircularity(array $fillers)
77
    {
78 5
        foreach ($fillers as $code => $filler) {
79 3
            $visited = [$code];
80 3
            $this->visitFiller($filler, $fillers, $visited);
81 4
        }
82 4
    }
83
84
    /**
85
     * @param SourceFiller   $filler
86
     * @param SourceFiller[] $fillers
87
     * @param string[]       $visited
88
     *
89
     * @throws DestinationException
90
     */
91 3
    private function visitFiller(SourceFiller $filler, array $fillers, array &$visited)
92
    {
93 3
        foreach ($filler->getDependencies() as $code) {
94 3
            if (!array_key_exists($code, $fillers)) {
95 2
                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 2
        }
105 2
    }
106
107
    /**
108
     * @param array[]        $items
109
     * @param SourceFiller[] $fillers
110
     *
111
     * @return array
112
     * @throws DestinationException
113
     */
114 4
    private function infer(array $items, array $fillers)
115
    {
116 4
        foreach ($items as &$item) {
117 4
            foreach ($fillers as $code => $filler) {
118 2
                $item = $this->inferCode($item, $code, $fillers);
119 4
            }
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 2
    private function inferCode(array $item, $code, array $fillers)
135
    {
136 2
        if (!array_key_exists($code, $fillers)) {
137
            DestinationException::inferenceFailed($code, $item, $fillers);
138
        }
139
140 2
        $filler = $fillers[$code];
141 2
        foreach ($filler->getDependencies() as $key) {
142 2
            if (!array_key_exists($key, $item)) {
143
                $item[$key] = $this->inferCode($item, $key, $fillers);
144
            }
145 2
        }
146 2
        $item[$code] = $filler->infer($item);
147
148 2
        return $item;
149
    }
150
}
151