Completed
Push — master ( c8af52...234fe1 )
by
unknown
11s
created

ConcatenationAdapter::refreshAdaptersNbResults()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 5
nc 4
nop 0
crap 3
1
<?php
2
3
namespace Pagerfanta\Adapter;
4
5
use Pagerfanta\Exception\InvalidArgumentException;
6
7
/**
8
 * Adapter that concatenates the results of other adapters.
9
 *
10
 * @author Surgie Finesse <[email protected]>
11
 */
12
class ConcatenationAdapter implements AdapterInterface
13
{
14
    /**
15
     * @var AdapterInterface[] List of adapters
16
     */
17
    protected $adapters;
18
19
    /**
20
     * @var int[]|null Cache of the numbers of results of the adapters. The indexes correspond the indexes of the
21
     * `adapters` property.
22
     */
23
    protected $adaptersNbResultsCache;
24
25
    /**
26
     * @param AdapterInterface[] $adapters
27
     * @throws InvalidArgumentException
28
     */
29 4
    public function __construct(array $adapters)
30
    {
31 4
        foreach ($adapters as $index => $adapter) {
32 4
            if (!($adapter instanceof AdapterInterface)) {
33 1
                throw new InvalidArgumentException(sprintf(
34 1
                    'Argument $adapters[%s] expected to be a \Pagerfanta\Adapter\AdapterInterface instance, a %s given',
35 1
                    $index,
36 1
                    is_object($adapter) ? sprintf('%s instance', get_class($adapter)) : gettype($adapter)
37 1
                ));
38
            }
39 4
        }
40
41 4
        $this->adapters = $adapters;
42 4
    }
43
44
    /**
45
     * {@inheritdoc}
46
     */
47 1
    public function getNbResults()
48
    {
49 1
        if (!isset($this->adaptersNbResultsCache)) {
50 1
            $this->refreshAdaptersNbResults();
51 1
        }
52
53 1
        return array_sum($this->adaptersNbResultsCache);
54
    }
55
56
    /**
57
     * {@inheritdoc}
58
     * @return array
59
     */
60 2
    public function getSlice($offset, $length)
61
    {
62 2
        if (!isset($this->adaptersNbResultsCache)) {
63 2
            $this->refreshAdaptersNbResults();
64 2
        }
65
66 2
        $slice = array();
67 2
        $previousAdaptersNbResultsSum = 0;
68 2
        $requestFirstIndex = $offset;
69 2
        $requestLastIndex  = $offset + $length - 1;
70
71 2
        foreach ($this->adapters as $index => $adapter) {
72 2
            $adapterNbResults  = $this->adaptersNbResultsCache[$index];
73 2
            $adapterFirstIndex = $previousAdaptersNbResultsSum;
74 2
            $adapterLastIndex  = $adapterFirstIndex + $adapterNbResults - 1;
75
76 2
            $previousAdaptersNbResultsSum += $adapterNbResults;
77
78
            // The adapter is fully below the requested slice range — skip it
79 2
            if ($adapterLastIndex < $requestFirstIndex) {
80 1
                continue;
81
            }
82
83
            // The adapter is fully above the requested slice range — finish the gathering
84 2
            if ($adapterFirstIndex > $requestLastIndex) {
85 2
                break;
86
            }
87
88
            // Else the adapter range definitely intersects with the requested range
89 2
            $fetchOffset = $requestFirstIndex - $adapterFirstIndex;
90 2
            $fetchLength = $length;
91
92
            // The requested range start is below the adapter range start
93 2
            if ($fetchOffset < 0) {
94 2
                $fetchLength += $fetchOffset;
95 2
                $fetchOffset = 0;
96 2
            }
97
98
            // The requested range end is above the adapter range end
99 2
            if ($fetchOffset + $fetchLength > $adapterNbResults) {
100 2
                $fetchLength = $adapterNbResults - $fetchOffset;
101 2
            }
102
103
            // Getting the subslice from the adapter and adding it to the result slice
104 2
            $fetchSlice = $adapter->getSlice($fetchOffset, $fetchLength);
105 2
            foreach ($fetchSlice as $item) {
106 2
                $slice[] = $item;
107 2
            }
108 2
        }
109
110 2
        return $slice;
111
    }
112
113
    /**
114
     * Refreshes the cache of the numbers of results of the adapters.
115
     */
116 3
    protected function refreshAdaptersNbResults()
117
    {
118 3
        if (!isset($this->adaptersNbResultsCache)) {
119 3
            $this->adaptersNbResultsCache = array();
120 3
        }
121
122 3
        foreach ($this->adapters as $index => $adapter) {
123 3
            $this->adaptersNbResultsCache[$index] = $adapter->getNbResults();
124 3
        }
125 3
    }
126
}
127