ApcuAdapter.php 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Cache\Adapter;
  11. use Symfony\Component\Cache\CacheItem;
  12. use Symfony\Component\Cache\Exception\CacheException;
  13. use Symfony\Component\Cache\Marshaller\MarshallerInterface;
  14. /**
  15. * @author Nicolas Grekas <p@tchwork.com>
  16. */
  17. class ApcuAdapter extends AbstractAdapter
  18. {
  19. /**
  20. * @throws CacheException if APCu is not enabled
  21. */
  22. public function __construct(
  23. string $namespace = '',
  24. int $defaultLifetime = 0,
  25. ?string $version = null,
  26. private ?MarshallerInterface $marshaller = null,
  27. ) {
  28. if (!static::isSupported()) {
  29. throw new CacheException('APCu is not enabled.');
  30. }
  31. if ('cli' === \PHP_SAPI) {
  32. ini_set('apc.use_request_time', 0);
  33. }
  34. parent::__construct($namespace, $defaultLifetime);
  35. if (null !== $version) {
  36. CacheItem::validateKey($version);
  37. if (!apcu_exists($version.'@'.$namespace)) {
  38. $this->doClear($namespace);
  39. apcu_add($version.'@'.$namespace, null);
  40. }
  41. }
  42. }
  43. public static function isSupported(): bool
  44. {
  45. return \function_exists('apcu_fetch') && filter_var(\ini_get('apc.enabled'), \FILTER_VALIDATE_BOOL);
  46. }
  47. protected function doFetch(array $ids): iterable
  48. {
  49. $unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback');
  50. try {
  51. $values = [];
  52. foreach (apcu_fetch($ids, $ok) ?: [] as $k => $v) {
  53. if (null !== $v || $ok) {
  54. $values[$k] = null !== $this->marshaller ? $this->marshaller->unmarshall($v) : $v;
  55. }
  56. }
  57. return $values;
  58. } catch (\Error $e) {
  59. throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine());
  60. } finally {
  61. ini_set('unserialize_callback_func', $unserializeCallbackHandler);
  62. }
  63. }
  64. protected function doHave(string $id): bool
  65. {
  66. return apcu_exists($id);
  67. }
  68. protected function doClear(string $namespace): bool
  69. {
  70. return isset($namespace[0]) && class_exists(\APCUIterator::class, false) && ('cli' !== \PHP_SAPI || filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOL))
  71. ? apcu_delete(new \APCUIterator(\sprintf('/^%s/', preg_quote($namespace, '/')), \APC_ITER_KEY))
  72. : apcu_clear_cache();
  73. }
  74. protected function doDelete(array $ids): bool
  75. {
  76. foreach ($ids as $id) {
  77. apcu_delete($id);
  78. }
  79. return true;
  80. }
  81. protected function doSave(array $values, int $lifetime): array|bool
  82. {
  83. if (null !== $this->marshaller && (!$values = $this->marshaller->marshall($values, $failed))) {
  84. return $failed;
  85. }
  86. if (false === $failures = apcu_store($values, null, $lifetime)) {
  87. $failures = $values;
  88. }
  89. return array_keys($failures);
  90. }
  91. }