| Total Complexity | 97 |
| Total Lines | 717 |
| Duplicated Lines | 0 % |
| Changes | 8 | ||
| Bugs | 0 | Features | 0 |
Complex classes like Apc often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Apc, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 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; |
||
|
|
|||
| 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) { |
||
| 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; |
||
| 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) { |
||
| 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); |
||
| 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; |
||
| 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); |
||
| 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) { |
||
| 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) { |
||
| 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) { |
||
| 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) { |
||
| 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); |
||
| 677 | } else { |
||
| 678 | return apc_delete($key); |
||
| 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); |
||
| 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) |
||
| 734 | } |
||
| 735 | } |
||
| 736 | } |
||
| 737 |