Completed
Push — master ( e86a88...9f06ff )
by Boudry
04:59 queued 02:04
created

ArrayManager::offsetSet()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 26
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 5.3906

Importance

Changes 0
Metric Value
dl 0
loc 26
ccs 12
cts 16
cp 0.75
rs 8.439
c 0
b 0
f 0
cc 5
eloc 19
nc 5
nop 2
crap 5.3906
1
<?php
2
/*
3
    Condorcet PHP - Election manager and results calculator.
4
    Designed for the Condorcet method. Integrating a large number of algorithms extending Condorcet. Expandable for all types of voting systems.
5
6
    By Julien Boudry and contributors - MIT LICENSE (Please read LICENSE.txt)
7
    https://github.com/julien-boudry/Condorcet
8
*/
9
declare(strict_types=1);
10
11
12
namespace Condorcet\DataManager;
13
14
use Condorcet\DataManager\DataContextInterface;
15
use Condorcet\DataManager\DataHandlerDrivers\DataHandlerDriverInterface;
16
use Condorcet\CondorcetException;
17
use Condorcet\CondorcetVersion;
18
19
abstract class ArrayManager implements \ArrayAccess, \Countable, \Iterator
20
{
21
    use CondorcetVersion;
22
23
        //////
24
25
    public static $CacheSize = 2000;
26
    public static $MaxContainerLength = 2000;
27
28
    protected $_Container = [];
29
    protected $_DataHandler = null;
30
    protected $_link = [];
31
32
    protected $_Cache = [];
33
    protected $_CacheMaxKey = 0;
34
    protected $_CacheMinKey = 0;
35
36
    protected $_cursor = null;
37
    protected $_counter = 0;
38
    protected $_maxKey = -1;
39
40
    public function __construct () {}
41
42 1
    public function __destruct ()
43
    {
44 1
        $this->regularize();
45 1
    }
46
47 1
    public function __clone ()
48
    {
49 1
        $this->_link = [];
50 1
    }
51
52 2
    public function __sleep () : array
53
    {
54 2
        $this->regularize();
55 2
        $this->clearCache();
56 2
        $this->rewind();
57
58 2
        return ['_Container','_DataHandler','_link'];
59
    }
60
61 1
    public function __wakeup ()
62
    {
63 1
        $this->resetMaxKey();
64 1
        $this->resetCounter();
65 1
    }
66
67
68
/////////// Implement ArrayAccess ///////////
69
70 103
    public function offsetSet($offset, $value) : void
71
    {
72 103
        if ($offset === null) :
73 103
            $this->_Container[++$this->_maxKey] = $value;
74 103
            ++$this->_counter;
75
        else :
76 1
            $state = $this->keyExist($offset);
77 1
            $this->_Container[$offset] = $value;
78
79 1
            if (!$state) :
80
                ++$this->_counter;
81
82
                if ($offset > $this->_maxKey) :
83
                    $this->_maxKey = $offset;
84
                endif;
85
86
                ksort($this->_Container,SORT_NUMERIC);
87 1
            elseif ($this->_DataHandler !== null) :
88 1
                $this->_DataHandler->deleteOneEntity($offset, true);
89
            endif;
90
91 1
            $this->clearCache();
92
        endif;
93
94 103
        $this->checkRegularize();
95 103
    }
96
97
    // Use by isset() function, must return false if offset value is null.
98
    public function offsetExists($offset) : bool
99
    {
100
        return ( isset($this->_Container[$offset]) || ($this->_DataHandler !== null && $this->_DataHandler->selectOneEntity($offset) !== false) ) ? true : false ;
101
    }
102
103 7
    public function offsetUnset($offset) : void
104
    {
105 7
        if ($this->keyExist($offset)) :
106 7
            if (array_key_exists($offset, $this->_Container)) :
107 7
                $this->preDeletedTask($this->_Container[$offset]);
108 7
                unset($this->_Container[$offset]);
109
            else :
110 1
                if (array_key_exists($offset, $this->_Cache)) :
111 1
                    $this->preDeletedTask($this->_Cache[$offset]);
112 1
                    unset($this->_Cache[$offset]);
113
                endif;
114
115 1
                $this->_DataHandler->deleteOneEntity($offset, false);
116
            endif;
117
118 7
            --$this->_counter;
119
        endif;
120 7
    }
121
122 87
    public function offsetGet($offset)
123
    {
124 87
        if (isset($this->_Container[$offset])) :
125 85
            return $this->_Container[$offset];
126 4
        elseif ($this->_DataHandler !== null) :
127 4
            if (array_key_exists($offset, $this->_Cache)) :
128 4
                return $this->_Cache[$offset];
129
            else :
130 4
                $oneEntity = $this->_DataHandler->selectOneEntity($offset);
131 4
                if ($oneEntity === false) :
132
                    return null;
133
                else : 
134 4
                    $this->_Cache[$offset] = $oneEntity;
135 4
                    return $oneEntity;
136
                endif;
137
            endif;
138
        else :
139
            return null;
140
        endif;
141
    }
142
143
144
/////////// Implement Iterator ///////////
145
146
    protected $valid = true;
147
148 86
    public function rewind() : void {
149 86
        $this->_cursor = null;
150 86
        $this->valid = true;
151
152 86
        reset($this->_Cache);
153 86
        reset($this->_Container);
154 86
    }
155
156 86
    public function current() {
157 86
        return $this->offsetGet($this->key());
158
    }
159
160 86
    public function key() : ?int
161
    {
162 86
        if ($this->_counter === 0) :
163
            return null;
164
        else :
165 86
            return ($this->_cursor === null) ? $this->getFirstKey() : $this->_cursor;
166
        endif;
167
    }
168
169 86
    public function next() : void
170
    {
171 86
        $oldCursor = $this->_cursor;
172
173 86
        if ($this->_cursor >= $this->_maxKey) :
174
            // Do nothing
175 74
        elseif (!$this->isUsingHandler()) :
176 71
            $this->setCursorOnNextKeyInArray($this->_Container);
177
        else :
178 4
            $this->populateCache();
179 4
            $this->setCursorOnNextKeyInArray($this->_Cache);
180
        endif;
181
182 86
        if ($this->_cursor === $oldCursor) :
183 86
            $this->valid = false;
184
        endif;
185 86
    }
186
187 74
        protected function setCursorOnNextKeyInArray (array &$array)
188
        {
189 74
            next($array);
190 74
            $arrayKey = key($array);
191
192 74
            if ($arrayKey > $this->key()) :
193 74
                return $this->_cursor = $arrayKey;
194
            endif;
195 2
        }
196
197 86
    public function valid() : bool {
198 86
        return ($this->_counter !== 0) ? $this->valid : false;
199
    }
200
201
202
/////////// Implement Countable ///////////
203
204 12
    public function count () : int {
205 12
        return $this->_counter;
206
    }
207
208
/////////// Array Methods ///////////
209
210 12
    public function getFullDataSet () : array
211
    {
212 12
        if ($this->isUsingHandler()) :
213 1
            $this->regularize();
214 1
            $this->clearCache();
215
216 1
            return $this->_Cache = $this->_DataHandler->selectRangeEntitys(0,$this->_maxKey + 1);
217
        else :
218 11
            return $this->_Container;
219
        endif;
220
    }
221
222 8
    public function keyExist ($offset) : bool
223
    {
224 8
        if ( array_key_exists($offset, $this->_Container) || ($this->_DataHandler !== null && $this->_DataHandler->selectOneEntity($offset) !== false) ) :
225 8
            return true;
226
        else  :
227 1
            return false;
228
        endif;
229
    }
230
231 86
    public function getFirstKey () : int
232
    {
233 86
        $r = array_keys($this->_Container);
234
235 86
        if ($this->_DataHandler !== null) :
236 4
            $r[] = $this->_DataHandler->selectMinKey();
237
        endif;
238
239 86
        return (int) min($r);
240
    }
241
242 6
    public function getContainerSize () : int
243
    {
244 6
        return count($this->_Container);
245
    }
246
247 1
    public function getCacheSize () : int
248
    {
249 1
        return count($this->_Cache);
250
    }
251
252 1
    public function debugGetCache () : array
253
    {
254 1
        return $this->_Cache;
255
    }
256
257
258
/////////// HANDLER API ///////////
259
260
    abstract protected function preDeletedTask ($object) : void;
261
262 7
    public function regularize () : bool
263
    {
264 7
        if (!$this->isUsingHandler() || empty($this->_Container)) :
265 7
            return false;
266
        else :
267 5
            $this->_DataHandler->insertEntitys($this->_Container);
268 5
            $this->_Container = [];
269 5
            return true;
270
        endif;
271
    }
272
273 103
    public function checkRegularize () : bool
274
    {
275 103
        if ( $this->_DataHandler !== null && self::$MaxContainerLength <= $this->getContainerSize() ) :
276 3
            $this->regularize();
277 3
            return true;
278
        else :
279 103
            return false;
280
        endif;
281
    }
282
283 4
    protected function populateCache () : void
284
    {
285 4
        $this->regularize();
286
287 4
        $currentKey = $this->key();
288
289 4
        if ( empty($this->_Cache) || $currentKey >= $this->_CacheMaxKey || $currentKey < $this->_CacheMinKey ) :
290 4
            $this->clearCache();
291 4
            $this->_Cache = $this->_DataHandler->selectRangeEntitys($currentKey, self::$CacheSize);
292
293 4
            $keys = array_keys($this->_Cache);
294 4
            $this->_CacheMaxKey = max($keys);
295 4
            $this->_CacheMinKey = min($keys);
296
        endif;
297 4
    }
298
299 7
    public function clearCache () : void
300
    {
301 7
        foreach ($this->_Cache as $e) :
302 5
            $this->preDeletedTask($e);
303
        endforeach;
304
305 7
        $this->_Cache = [];
306 7
        $this->_CacheMaxKey = 0;
307 7
        $this->_CacheMinKey = 0;
308 7
    }
309
310 83
    public function isUsingHandler ()
311
    {
312 83
        return $this->_DataHandler !== null;
313
    }
314
315
/////////// HANDLER INTERRACTION ///////////
316
317 6
    public function resetCounter () : int
318
    {
319 6
        return $this->_counter = $this->getContainerSize() + ( ($this->isUsingHandler()) ? $this->_DataHandler->countEntitys() : 0 );
320
    }
321
322 6
    public function resetMaxKey () : ?int
323
    {
324 6
        $this->resetCounter();
325
326 6
        if ($this->count() < 1) :
327 5
            $this->_maxKey = -1;
328 5
            return null;
329
        else :
330 2
            $maxContainerKey = (empty($this->_Container)) ? null : max(array_keys($this->_Container));
331 2
            $maxHandlerKey = ($this->_DataHandler !== null) ? $this->_DataHandler->selectMaxKey() : null;
332
333 2
            return $this->_maxKey = max( $maxContainerKey,$maxHandlerKey );
334
        endif;
335
    }
336
337 5
    public function importHandler (DataHandlerDriverInterface $handler) : bool
338
    {
339 5
        if ($handler->countEntitys() === 0) :
340 5
            $this->_DataHandler = $handler;
341 5
            $this->_DataHandler->_dataContextObject = $this->getDataContextObject();
342
343
            try {
344 5
                $this->regularize();
345
            } catch (\Exception $e) {
346
                $this->_DataHandler = null;
347
                $this->resetCounter();
348
                $this->resetMaxKey();
349
                throw $e;
350
            }
351
352 5
            $this->resetCounter();
353 5
            $this->resetMaxKey();
354
355 5
            return true;
356
        else :
357
            throw new CondorcetException;
358
        endif;
359
    }
360
361 1
    public function closeHandler () : void
362
    {
363 1
        if ($this->_DataHandler !== null) :
364 1
            $this->regularize();
365 1
            $this->clearCache();
366
367 1
            $this->_Container = $this->_DataHandler->selectRangeEntitys(0,$this->_maxKey + 1);
368
369 1
            $this->_DataHandler = null;
370
371 1
            $this->resetCounter();
372 1
            $this->resetMaxKey();
373
        endif;
374 1
    }
375
376
    abstract public function getDataContextObject () : DataContextInterface;
377
}
378