ApcuAdapter.php 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  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. private $marshaller;
  20. /**
  21. * @throws CacheException if APCu is not enabled
  22. */
  23. public function __construct(string $namespace = '', int $defaultLifetime = 0, ?string $version = null, ?MarshallerInterface $marshaller = null)
  24. {
  25. if (!static::isSupported()) {
  26. throw new CacheException('APCu is not enabled.');
  27. }
  28. if ('cli' === \PHP_SAPI) {
  29. ini_set('apc.use_request_time', 0);
  30. }
  31. parent::__construct($namespace, $defaultLifetime);
  32. if (null !== $version) {
  33. CacheItem::validateKey($version);
  34. if (!apcu_exists($version.'@'.$namespace)) {
  35. $this->doClear($namespace);
  36. apcu_add($version.'@'.$namespace, null);
  37. }
  38. }
  39. $this->marshaller = $marshaller;
  40. }
  41. public static function isSupported()
  42. {
  43. return \function_exists('apcu_fetch') && filter_var(\ini_get('apc.enabled'), \FILTER_VALIDATE_BOOLEAN);
  44. }
  45. /**
  46. * {@inheritdoc}
  47. */
  48. protected function doFetch(array $ids)
  49. {
  50. $unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback');
  51. try {
  52. $values = [];
  53. $ids = array_flip($ids);
  54. foreach (apcu_fetch(array_keys($ids), $ok) ?: [] as $k => $v) {
  55. if (!isset($ids[$k])) {
  56. // work around https://github.com/krakjoe/apcu/issues/247
  57. $k = key($ids);
  58. }
  59. unset($ids[$k]);
  60. if (null !== $v || $ok) {
  61. $values[$k] = null !== $this->marshaller ? $this->marshaller->unmarshall($v) : $v;
  62. }
  63. }
  64. return $values;
  65. } catch (\Error $e) {
  66. throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine());
  67. } finally {
  68. ini_set('unserialize_callback_func', $unserializeCallbackHandler);
  69. }
  70. }
  71. /**
  72. * {@inheritdoc}
  73. */
  74. protected function doHave(string $id)
  75. {
  76. return apcu_exists($id);
  77. }
  78. /**
  79. * {@inheritdoc}
  80. */
  81. protected function doClear(string $namespace)
  82. {
  83. return isset($namespace[0]) && class_exists(\APCUIterator::class, false) && ('cli' !== \PHP_SAPI || filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN))
  84. ? apcu_delete(new \APCUIterator(sprintf('/^%s/', preg_quote($namespace, '/')), \APC_ITER_KEY))
  85. : apcu_clear_cache();
  86. }
  87. /**
  88. * {@inheritdoc}
  89. */
  90. protected function doDelete(array $ids)
  91. {
  92. foreach ($ids as $id) {
  93. apcu_delete($id);
  94. }
  95. return true;
  96. }
  97. /**
  98. * {@inheritdoc}
  99. */
  100. protected function doSave(array $values, int $lifetime)
  101. {
  102. if (null !== $this->marshaller && (!$values = $this->marshaller->marshall($values, $failed))) {
  103. return $failed;
  104. }
  105. try {
  106. if (false === $failures = apcu_store($values, null, $lifetime)) {
  107. $failures = $values;
  108. }
  109. return array_keys($failures);
  110. } catch (\Throwable $e) {
  111. if (1 === \count($values)) {
  112. // Workaround https://github.com/krakjoe/apcu/issues/170
  113. apcu_delete(array_key_first($values));
  114. }
  115. throw $e;
  116. }
  117. }
  118. }