Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 42 |
|
0.00% |
0 / 6 |
CRAP | |
0.00% |
0 / 1 |
APCUBagOStuff | |
0.00% |
0 / 41 |
|
0.00% |
0 / 6 |
306 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
doGet | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
doSet | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
doAdd | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
doDelete | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
doIncrWithInit | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
56 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | */ |
20 | namespace Wikimedia\ObjectCache; |
21 | |
22 | /** |
23 | * Store data in the local server memory via APCu (php-apcu) |
24 | * |
25 | * Past issues of note: |
26 | * - Memory corruption when `apc.serializer=default` in INI: |
27 | * https://phabricator.wikimedia.org/T120267 |
28 | * - We used to recommend `apc.serializer=php` as non-default setting, and if not set, |
29 | * applied serialize() manually to workaround bugs and to create values we can use |
30 | * as CAS tokens. Upstream defaults to serializer=php since php-apcu 5.1.15 (2018). |
31 | * https://gerrit.wikimedia.org/r/671634 |
32 | * |
33 | * @see https://www.php.net/apcu |
34 | * @ingroup Cache |
35 | */ |
36 | class APCUBagOStuff extends MediumSpecificBagOStuff { |
37 | /** |
38 | * @var string String to append to each APC key. This may be changed |
39 | * whenever the handling of values is changed, to prevent existing code |
40 | * from encountering older values which it cannot handle. |
41 | */ |
42 | private const KEY_SUFFIX = ':5'; |
43 | |
44 | /** @var int Max attempts for implicit CAS operations */ |
45 | private static $CAS_MAX_ATTEMPTS = 100; |
46 | |
47 | public function __construct( array $params = [] ) { |
48 | // No use in segmenting values |
49 | $params['segmentationSize'] = INF; |
50 | parent::__construct( $params ); |
51 | // Versions of apcu < 5.1.19 use apc.use_request_time=1 by default, causing new keys |
52 | // to be assigned timestamps based on the start of the PHP request/script. The longer |
53 | // the request has been running, the more likely that newly stored keys will instantly |
54 | // be seen as expired by other requests. Disable apc.use_request_time. |
55 | ini_set( 'apc.use_request_time', '0' ); |
56 | |
57 | if ( PHP_SAPI === 'cli' ) { |
58 | $this->attrMap[self::ATTR_DURABILITY] = ini_get( 'apc.enable_cli' ) |
59 | ? self::QOS_DURABILITY_SCRIPT |
60 | : self::QOS_DURABILITY_NONE; |
61 | } else { |
62 | $this->attrMap[self::ATTR_DURABILITY] = self::QOS_DURABILITY_SERVICE; |
63 | } |
64 | } |
65 | |
66 | protected function doGet( $key, $flags = 0, &$casToken = null ) { |
67 | $getToken = ( $casToken === self::PASS_BY_REF ); |
68 | $casToken = null; |
69 | |
70 | $value = apcu_fetch( $key . self::KEY_SUFFIX ); |
71 | if ( $getToken && $value !== false ) { |
72 | // Note that if the driver handles serialization then this uses the PHP value |
73 | // as the token. This might require inspection or re-serialization in doCas(). |
74 | $casToken = $value; |
75 | } |
76 | |
77 | return $value; |
78 | } |
79 | |
80 | protected function doSet( $key, $value, $exptime = 0, $flags = 0 ) { |
81 | $ttl = $this->getExpirationAsTTL( $exptime ); |
82 | |
83 | return apcu_store( $key . self::KEY_SUFFIX, $value, $ttl ); |
84 | } |
85 | |
86 | protected function doAdd( $key, $value, $exptime = 0, $flags = 0 ) { |
87 | if ( apcu_exists( $key . self::KEY_SUFFIX ) ) { |
88 | // Avoid global write locks for high contention keys |
89 | return false; |
90 | } |
91 | |
92 | $ttl = $this->getExpirationAsTTL( $exptime ); |
93 | |
94 | return apcu_add( $key . self::KEY_SUFFIX, $value, $ttl ); |
95 | } |
96 | |
97 | protected function doDelete( $key, $flags = 0 ) { |
98 | apcu_delete( $key . self::KEY_SUFFIX ); |
99 | |
100 | return true; |
101 | } |
102 | |
103 | protected function doIncrWithInit( $key, $exptime, $step, $init, $flags ) { |
104 | // Use apcu 5.1.12 $ttl argument if apcu_inc() will initialize to $init: |
105 | // https://www.php.net/manual/en/function.apcu-inc.php |
106 | if ( $step === $init ) { |
107 | /** @noinspection PhpMethodParametersCountMismatchInspection */ |
108 | $ttl = $this->getExpirationAsTTL( $exptime ); |
109 | $result = apcu_inc( $key . self::KEY_SUFFIX, $step, $success, $ttl ); |
110 | } else { |
111 | $result = false; |
112 | for ( $i = 0; $i < self::$CAS_MAX_ATTEMPTS; ++$i ) { |
113 | $oldCount = apcu_fetch( $key . self::KEY_SUFFIX ); |
114 | if ( $oldCount === false ) { |
115 | $count = $init; |
116 | $ttl = $this->getExpirationAsTTL( $exptime ); |
117 | if ( apcu_add( $key . self::KEY_SUFFIX, $count, $ttl ) ) { |
118 | $result = $count; |
119 | break; |
120 | } |
121 | } elseif ( is_int( $oldCount ) ) { |
122 | $count = $oldCount + $step; |
123 | if ( apcu_cas( $key . self::KEY_SUFFIX, $oldCount, $count ) ) { |
124 | $result = $count; |
125 | break; |
126 | } |
127 | } else { |
128 | break; |
129 | } |
130 | } |
131 | } |
132 | |
133 | return $result; |
134 | } |
135 | } |
136 | |
137 | /** @deprecated class alias since 1.43 */ |
138 | class_alias( APCUBagOStuff::class, 'APCUBagOStuff' ); |