Apc::apcu_fetch()   A
last analyzed

Complexity

Conditions 6
Paths 8

Size

Total Lines 30
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 6
eloc 15
c 3
b 0
f 0
nc 8
nop 2
dl 0
loc 30
rs 9.2222
1
<?php
2
3
namespace MatthiasMullie\Scrapbook\Adapters;
4
5
use MatthiasMullie\Scrapbook\Adapters\Collections\Apc as Collection;
6
use MatthiasMullie\Scrapbook\Exception\Exception;
7
use MatthiasMullie\Scrapbook\KeyValueStore;
8
9
/**
10
 * APC adapter. Basically just a wrapper over apc_* functions, but in an
11
 * exchangeable (KeyValueStore) interface.
12
 *
13
 * @author Matthias Mullie <[email protected]>
14
 * @copyright Copyright (c) 2014, Matthias Mullie. All rights reserved
15
 * @license LICENSE MIT
16
 */
17
class Apc implements KeyValueStore
18
{
19
    /**
20
     * APC does this crazy thing of only deleting expired data on every new
21
     * (page) request, not checking it when you actually retrieve the value
22
     * (which you may just have set in the same request)
23
     * Since it's totally possible to store values that expire in the same
24
     * request, we'll keep track of those expiration times here & filter them
25
     * out in case they did expire.
26
     *
27
     * @see http://stackoverflow.com/questions/11750223/apc-user-cache-entries-not-expiring
28
     *
29
     * @var array
30
     */
31
    protected $expires = array();
32
33
    public function __construct()
34
    {
35
        if (!function_exists('apcu_fetch') && !function_exists('apc_fetch')) {
36
            throw new Exception('ext-apc(u) is not installed.');
37
        }
38
    }
39
40
    /**
41
     * {@inheritdoc}
42
     */
43
    public function get($key, &$token = null)
44
    {
45
        // check for values that were just stored in this request but have
46
        // actually expired by now
47
        if (isset($this->expires[$key]) && $this->expires[$key] < time()) {
48
            return false;
49
        }
50
51
        $value = $this->apcu_fetch($key, $success);
52
        if (false === $success) {
53
            $token = null;
54
55
            return false;
56
        }
57
58
        $token = serialize($value);
59
60
        return $value;
61
    }
62
63
    /**
64
     * {@inheritdoc}
65
     */
66
    public function getMulti(array $keys, array &$tokens = null)
67
    {
68
        $tokens = array();
69
        if (empty($keys)) {
70
            return array();
71
        }
72
73
        // check for values that were just stored in this request but have
74
        // actually expired by now
75
        foreach ($keys as $i => $key) {
76
            if (isset($this->expires[$key]) && $this->expires[$key] < time()) {
77
                unset($keys[$i]);
78
            }
79
        }
80
81
        $values = $this->apcu_fetch($keys);
82
        if (false === $values) {
83
            return array();
84
        }
85
86
        $tokens = array();
87
        foreach ($values as $key => $value) {
88
            $tokens[$key] = serialize($value);
89
        }
90
91
        return $values;
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97
    public function set($key, $value, $expire = 0)
98
    {
99
        $ttl = $this->ttl($expire);
100
101
        // negative TTLs don't always seem to properly treat the key as deleted
102
        if ($ttl < 0) {
103
            $this->delete($key);
104
105
            return true;
106
        }
107
108
        // lock required for CAS
109
        if (!$this->lock($key)) {
110
            return false;
111
        }
112
113
        $success = $this->apcu_store($key, $value, $ttl);
114
        $this->expire($key, $ttl);
115
        $this->unlock($key);
116
117
        return $success;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $success also could return the type boolean[] which is incompatible with the return type mandated by MatthiasMullie\Scrapbook\KeyValueStore::set() of boolean.
Loading history...
118
    }
119
120
    /**
121
     * {@inheritdoc}
122
     */
123
    public function setMulti(array $items, $expire = 0)
124
    {
125
        if (empty($items)) {
126
            return array();
127
        }
128
129
        $ttl = $this->ttl($expire);
130
131
        // negative TTLs don't always seem to properly treat the key as deleted
132
        if ($ttl < 0) {
133
            $this->deleteMulti(array_keys($items));
134
135
            return array_fill_keys(array_keys($items), true);
136
        }
137
138
        // attempt to get locks for all items
139
        $locked = $this->lock(array_keys($items));
140
        $locked = array_fill_keys($locked, null);
141
        $failed = array_diff_key($items, $locked);
142
        $items = array_intersect_key($items, $locked);
143
144
        if ($items) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $items of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
145
            // only write to those where lock was acquired
146
            $this->apcu_store($items, null, $ttl);
147
            $this->expire(array_keys($items), $ttl);
148
            $this->unlock(array_keys($items));
149
        }
150
151
        $return = array();
152
        foreach ($items as $key => $value) {
153
            $return[$key] = !array_key_exists($key, $failed);
154
        }
155
156
        return $return;
157
    }
158
159
    /**
160
     * {@inheritdoc}
161
     */
162
    public function delete($key)
163
    {
164
        // lock required for CAS
165
        if (!$this->lock($key)) {
166
            return false;
167
        }
168
169
        $success = $this->apcu_delete($key);
170
        unset($this->expires[$key]);
171
        $this->unlock($key);
172
173
        return $success;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $success also could return the type string[] which is incompatible with the return type mandated by MatthiasMullie\Scrapbook\KeyValueStore::delete() of boolean.
Loading history...
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179
    public function deleteMulti(array $keys)
180
    {
181
        if (empty($keys)) {
182
            return array();
183
        }
184
185
        // attempt to get locks for all items
186
        $locked = $this->lock($keys);
187
        $failed = array_diff($keys, $locked);
188
        $keys = array_intersect($keys, $locked);
189
190
        // only delete those where lock was acquired
191
        if ($keys) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $keys of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
192
            /**
193
             * Contrary to the docs, apc_delete also accepts an array of
194
             * multiple keys to be deleted. Docs for apcu_delete are ok in this
195
             * regard.
196
             * But both are flawed in terms of return value in this case: an
197
             * array with failed keys is returned.
198
             *
199
             * @see http://php.net/manual/en/function.apc-delete.php
200
             *
201
             * @var string[]
202
             */
203
            $result = $this->apcu_delete($keys);
204
            $failed = array_merge($failed, $result);
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type true; however, parameter $arrays of array_merge() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

204
            $failed = array_merge($failed, /** @scrutinizer ignore-type */ $result);
Loading history...
205
            $this->unlock($keys);
206
        }
207
208
        $return = array();
209
        foreach ($keys as $key) {
210
            $return[$key] = !in_array($key, $failed);
211
            unset($this->expires[$key]);
212
        }
213
214
        return $return;
215
    }
216
217
    /**
218
     * {@inheritdoc}
219
     */
220
    public function add($key, $value, $expire = 0)
221
    {
222
        $ttl = $this->ttl($expire);
223
224
        // negative TTLs don't always seem to properly treat the key as deleted
225
        if ($ttl < 0) {
226
            // don't add - it's expired already; just check if it already
227
            // existed to return true/false as expected from add()
228
            return false === $this->get($key);
229
        }
230
231
        // lock required for CAS
232
        if (!$this->lock($key)) {
233
            return false;
234
        }
235
236
        $success = $this->apcu_add($key, $value, $ttl);
237
        $this->expire($key, $ttl);
238
        $this->unlock($key);
239
240
        return $success;
241
    }
242
243
    /**
244
     * {@inheritdoc}
245
     */
246
    public function replace($key, $value, $expire = 0)
247
    {
248
        $ttl = $this->ttl($expire);
249
250
        // APC doesn't support replace; I'll use get to check key existence,
251
        // then safely replace with cas
252
        $current = $this->get($key, $token);
253
        if (false === $current) {
254
            return false;
255
        }
256
257
        // negative TTLs don't always seem to properly treat the key as deleted
258
        if ($ttl < 0) {
259
            $this->delete($key);
260
261
            return true;
262
        }
263
264
        // no need for locking - cas will do that
265
        return $this->cas($token, $key, $value, $ttl);
266
    }
267
268
    /**
269
     * {@inheritdoc}
270
     */
271
    public function cas($token, $key, $value, $expire = 0)
272
    {
273
        $ttl = $this->ttl($expire);
274
275
        // lock required because we can't perform an atomic CAS
276
        if (!$this->lock($key)) {
277
            return false;
278
        }
279
280
        // check for values that were just stored in this request but have
281
        // actually expired by now
282
        if (isset($this->expires[$key]) && $this->expires[$key] < time()) {
283
            return false;
284
        }
285
286
        // get current value, to compare with token
287
        $compare = $this->apcu_fetch($key);
288
289
        if (false === $compare) {
290
            $this->unlock($key);
291
292
            return false;
293
        }
294
295
        if ($token !== serialize($compare)) {
296
            $this->unlock($key);
297
298
            return false;
299
        }
300
301
        // negative TTLs don't always seem to properly treat the key as deleted
302
        if ($ttl < 0) {
303
            $this->apcu_delete($key);
304
            unset($this->expires[$key]);
305
            $this->unlock($key);
306
307
            return true;
308
        }
309
310
        $success = $this->apcu_store($key, $value, $ttl);
311
        $this->expire($key, $ttl);
312
        $this->unlock($key);
313
314
        return $success;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $success also could return the type boolean[] which is incompatible with the return type mandated by MatthiasMullie\Scrapbook\KeyValueStore::cas() of boolean.
Loading history...
315
    }
316
317
    /**
318
     * {@inheritdoc}
319
     */
320
    public function increment($key, $offset = 1, $initial = 0, $expire = 0)
321
    {
322
        if ($offset <= 0 || $initial < 0) {
323
            return false;
324
        }
325
326
        // not doing apc_inc because that one it doesn't let us set an initial
327
        // value or TTL
328
        return $this->doIncrement($key, $offset, $initial, $expire);
329
    }
330
331
    /**
332
     * {@inheritdoc}
333
     */
334
    public function decrement($key, $offset = 1, $initial = 0, $expire = 0)
335
    {
336
        if ($offset <= 0 || $initial < 0) {
337
            return false;
338
        }
339
340
        // not doing apc_dec because that one it doesn't let us set an initial
341
        // value or TTL
342
        return $this->doIncrement($key, -$offset, $initial, $expire);
343
    }
344
345
    /**
346
     * {@inheritdoc}
347
     */
348
    public function touch($key, $expire)
349
    {
350
        $ttl = $this->ttl($expire);
351
352
        // shortcut - expiring is similar to deleting, but the former has no
353
        // 1-operation equivalent
354
        if ($ttl < 0) {
355
            return $this->delete($key);
356
        }
357
358
        // get existing TTL & quit early if it's that one already
359
        $iterator = $this->APCUIterator('/^'.preg_quote($key, '/').'$/', \APC_ITER_VALUE | \APC_ITER_TTL, 1, \APC_LIST_ACTIVE);
360
        if (!$iterator->valid()) {
361
            return false;
362
        }
363
        $current = $iterator->current();
364
        if (!$current) {
365
            // doesn't exist
366
            return false;
367
        }
368
        if ($current['ttl'] === $ttl) {
369
            // that's the TTL already, no need to reset it
370
            return true;
371
        }
372
373
        // generate CAS token to safely CAS existing value with new TTL
374
        $value = $current['value'];
375
        $token = serialize($value);
376
377
        return $this->cas($token, $key, $value, $ttl);
378
    }
379
380
    /**
381
     * {@inheritdoc}
382
     */
383
    public function flush()
384
    {
385
        $this->expires = array();
386
387
        return $this->apcu_clear_cache();
388
    }
389
390
    /**
391
     * {@inheritdoc}
392
     */
393
    public function getCollection($name)
394
    {
395
        return new Collection($this, $name);
396
    }
397
398
    /**
399
     * Shared between increment/decrement: both have mostly the same logic
400
     * (decrement just increments a negative value), but need their validation
401
     * split up (increment won't accept negative values).
402
     *
403
     * @param string $key
404
     * @param int    $offset
405
     * @param int    $initial
406
     * @param int    $expire
407
     *
408
     * @return int|bool
409
     */
410
    protected function doIncrement($key, $offset, $initial, $expire)
411
    {
412
        $ttl = $this->ttl($expire);
413
414
        /*
415
         * APC has apc_inc & apc_dec, which work great. However, they don't
416
         * allow for a TTL to be set.
417
         * I could use apc_inc & apc_dec & then do a touch, but touch also
418
         * doesn't have an APC implementation & needs a get & cas. That would
419
         * be 2 operations + CAS.
420
         * Instead, I'll just do a get, implement the increase or decrease in
421
         * PHP, then CAS the new value = 1 operation + CAS.
422
         */
423
        $value = $this->get($key, $token);
424
        if (false === $value) {
425
            // don't even set initial value, it's already expired...
426
            if ($ttl < 0) {
427
                return $initial;
428
            }
429
430
            // no need for locking - set will do that
431
            $success = $this->add($key, $initial, $ttl);
432
433
            return $success ? $initial : false;
434
        }
435
436
        if (!is_numeric($value) || $value < 0) {
437
            return false;
438
        }
439
440
        $value += $offset;
441
        // value can never be lower than 0
442
        $value = max(0, $value);
443
444
        // negative TTLs don't always seem to properly treat the key as deleted
445
        if ($ttl < 0) {
446
            $success = $this->delete($key);
447
448
            return $success ? $value : false;
449
        }
450
451
        // no need for locking - cas will do that
452
        $success = $this->cas($token, $key, $value, $ttl);
453
454
        return $success ? $value : false;
455
    }
456
457
    /**
458
     * APC expects true TTL, not expiration timestamp.
459
     *
460
     * @param int $expire
461
     *
462
     * @return int TTL in seconds
463
     */
464
    protected function ttl($expire)
465
    {
466
        // relative time in seconds, <30 days
467
        if ($expire < 30 * 24 * 60 * 60) {
468
            $expire += time();
469
        }
470
471
        return $expire ? $expire - time() : 0;
472
    }
473
474
    /**
475
     * Acquire a lock. If we failed to acquire a lock, it'll automatically try
476
     * again in 1ms, for a maximum of 10 times.
477
     *
478
     * APC provides nothing that would allow us to do CAS. To "emulate" CAS,
479
     * we'll work with locks: all cache writes also briefly create a lock
480
     * cache entry (yup: #writes * 3, for lock & unlock - luckily, they're
481
     * not over the network)
482
     * Writes are disallows when a lock can't be obtained (= locked by
483
     * another write), which makes it possible for us to first retrieve,
484
     * compare & then set in a nob-atomic way.
485
     * However, there's a possibility for interference with direct APC
486
     * access touching the same keys - e.g. other scripts, not using this
487
     * class. If CAS is of importance, make sure the only things touching
488
     * APC on your server is using these classes!
489
     *
490
     * @param string|string[] $keys
491
     *
492
     * @return array Array of successfully locked keys
493
     */
494
    protected function lock($keys)
495
    {
496
        // both string (single key) and array (multiple) are accepted
497
        $keys = (array) $keys;
498
499
        $locked = array();
500
        for ($i = 0; $i < 10; ++$i) {
501
            $locked += $this->acquire($keys);
502
            $keys = array_diff($keys, $locked);
503
504
            if (empty($keys)) {
505
                break;
506
            }
507
508
            usleep(1);
509
        }
510
511
        return $locked;
512
    }
513
514
    /**
515
     * Acquire a lock - required to provide CAS functionality.
516
     *
517
     * @param string|string[] $keys
518
     *
519
     * @return string[] Array of successfully locked keys
520
     */
521
    protected function acquire($keys)
522
    {
523
        $keys = (array) $keys;
524
525
        $values = array();
526
        foreach ($keys as $key) {
527
            $values["scrapbook.lock.$key"] = null;
528
        }
529
530
        // there's no point in locking longer than max allowed execution time
531
        // for this script
532
        $ttl = ini_get('max_execution_time');
533
534
        // lock these keys, then compile a list of successfully locked keys
535
        // (using the returned failure array)
536
        $result = (array) $this->apcu_add($values, null, $ttl);
0 ignored issues
show
Bug introduced by
$ttl of type string is incompatible with the type integer expected by parameter $ttl of MatthiasMullie\Scrapbook\Adapters\Apc::apcu_add(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

536
        $result = (array) $this->apcu_add($values, null, /** @scrutinizer ignore-type */ $ttl);
Loading history...
537
        $failed = array();
538
        foreach ($result as $key => $err) {
539
            $failed[] = substr($key, strlen('scrapbook.lock.'));
540
        }
541
542
        return array_diff($keys, $failed);
543
    }
544
545
    /**
546
     * Release a lock.
547
     *
548
     * @param string|string[] $keys
549
     *
550
     * @return bool
551
     */
552
    protected function unlock($keys)
553
    {
554
        $keys = (array) $keys;
555
        foreach ($keys as $i => $key) {
556
            $keys[$i] = "scrapbook.lock.$key";
557
        }
558
559
        $this->apcu_delete($keys);
560
561
        return true;
562
    }
563
564
    /**
565
     * Store the expiration time for items we're setting in this request, to
566
     * work around APC's behavior of only clearing expires per page request.
567
     *
568
     * @see static::$expires
569
     *
570
     * @param array|string $key
571
     * @param int          $ttl
572
     */
573
    protected function expire($key = array(), $ttl = 0)
574
    {
575
        if (0 === $ttl) {
576
            // when storing indefinitely, there's no point in keeping it around,
577
            // it won't just expire
578
            return;
579
        }
580
581
        // $key can be both string (1 key) or array (multiple)
582
        $keys = (array) $key;
583
584
        $time = time() + $ttl;
585
        foreach ($keys as $key) {
586
            $this->expires[$key] = $time;
587
        }
588
    }
589
590
    /**
591
     * @param string|string[] $key
592
     * @param bool            $success
593
     *
594
     * @return mixed|false
595
     */
596
    protected function apcu_fetch($key, &$success = null)
597
    {
598
        /*
599
         * $key can also be numeric, in which case APC is able to retrieve it,
600
         * but will have an invalid $key in the results array, and trying to
601
         * locate it by its $key in that array will fail with `undefined index`.
602
         * I'll work around this by requesting those values 1 by 1.
603
         */
604
        if (is_array($key)) {
605
            $nums = array_filter($key, 'is_numeric');
606
            if ($nums) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $nums of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
607
                $values = array();
608
                foreach ($nums as $k) {
609
                    $values[$k] = $this->apcu_fetch((string) $k, $success);
610
                }
611
612
                $remaining = array_diff($key, $nums);
613
                if ($remaining) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $remaining of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
614
                    $values += $this->apcu_fetch($remaining, $success2);
615
                    $success &= $success2;
616
                }
617
618
                return $values;
619
            }
620
        }
621
622
        if (function_exists('apcu_fetch')) {
623
            return apcu_fetch($key, $success);
624
        } else {
625
            return apc_fetch($key, $success);
626
        }
627
    }
628
629
    /**
630
     * @param string|string[] $key
631
     * @param mixed           $var
632
     * @param int             $ttl
633
     *
634
     * @return bool|bool[]
635
     */
636
    protected function apcu_store($key, $var, $ttl = 0)
637
    {
638
        /*
639
         * $key can also be a [$key => $value] array, where key is numeric,
640
         * but got cast to int by PHP. APC doesn't seem to store such numerical
641
         * key, so we'll have to take care of those one by one.
642
         */
643
        if (is_array($key)) {
644
            $nums = array_filter(array_keys($key), 'is_numeric');
645
            if ($nums) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $nums of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
646
                $success = array();
647
                $nums = array_intersect_key($key, array_fill_keys($nums, null));
648
                foreach ($nums as $k => $v) {
649
                    $success[$k] = $this->apcu_store((string) $k, $v, $ttl);
650
                }
651
652
                $remaining = array_diff_key($key, $nums);
653
                if ($remaining) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $remaining of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
654
                    $success += $this->apcu_store($remaining, $var, $ttl);
655
                }
656
657
                return $success;
658
            }
659
        }
660
661
        if (function_exists('apcu_store')) {
662
            return apcu_store($key, $var, $ttl);
663
        } else {
664
            return apc_store($key, $var, $ttl);
665
        }
666
    }
667
668
    /**
669
     * @param string|string[]|\APCIterator|\APCUIterator $key
670
     *
671
     * @return bool|string[]
672
     */
673
    protected function apcu_delete($key)
674
    {
675
        if (function_exists('apcu_delete')) {
676
            return apcu_delete($key);
0 ignored issues
show
Bug introduced by
It seems like $key can also be of type APCIterator; however, parameter $key of apcu_delete() does only seem to accept APCuIterator|string|string[], maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

676
            return apcu_delete(/** @scrutinizer ignore-type */ $key);
Loading history...
677
        } else {
678
            return apc_delete($key);
0 ignored issues
show
Bug introduced by
It seems like $key can also be of type APCUIterator; however, parameter $key of apc_delete() does only seem to accept APCIterator|string|string[], maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

678
            return apc_delete(/** @scrutinizer ignore-type */ $key);
Loading history...
679
        }
680
    }
681
682
    /**
683
     * @param string|string[] $key
684
     * @param mixed           $var
685
     * @param int             $ttl
686
     *
687
     * @return bool|bool[]
688
     */
689
    protected function apcu_add($key, $var, $ttl = 0)
690
    {
691
        if (function_exists('apcu_add')) {
692
            return apcu_add($key, $var, $ttl);
693
        } else {
694
            return apc_add($key, $var, $ttl);
0 ignored issues
show
Bug introduced by
It seems like $key can also be of type string[]; however, parameter $key of apc_add() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

694
            return apc_add(/** @scrutinizer ignore-type */ $key, $var, $ttl);
Loading history...
695
        }
696
    }
697
698
    /**
699
     * @return bool
700
     */
701
    protected function apcu_clear_cache()
702
    {
703
        if (function_exists('apcu_clear_cache')) {
704
            return apcu_clear_cache();
705
        } else {
706
            return apc_clear_cache('user');
707
        }
708
    }
709
710
    /**
711
     * @param string|string[]|null $search
712
     * @param int                  $format
713
     * @param int                  $chunk_size
714
     * @param int                  $list
715
     *
716
     * @return \APCIterator|\APCUIterator
717
     */
718
    protected function APCUIterator($search = null, $format = null, $chunk_size = null, $list = null)
719
    {
720
        $arguments = func_get_args();
721
722
        if (class_exists('APCUIterator', false)) {
723
            // I can't set the defaults parameter values because the APC_ or
724
            // APCU_ constants may not exist, so I'll just initialize from
725
            // func_get_args, not passing those params that haven't been set
726
            $reflect = new \ReflectionClass('APCUIterator');
727
728
            return $reflect->newInstanceArgs($arguments);
729
        } else {
730
            array_unshift($arguments, 'user');
731
            $reflect = new \ReflectionClass('APCIterator');
732
733
            return $reflect->newInstanceArgs($arguments);
734
        }
735
    }
736
}
737