ApcuAdapter.php 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  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. foreach (apcu_fetch($ids, $ok) ?: [] as $k => $v) {
  54. if (null !== $v || $ok) {
  55. $values[$k] = null !== $this->marshaller ? $this->marshaller->unmarshall($v) : $v;
  56. }
  57. }
  58. return $values;
  59. } catch (\Error $e) {
  60. throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine());
  61. } finally {
  62. ini_set('unserialize_callback_func', $unserializeCallbackHandler);
  63. }
  64. }
  65. /**
  66. * {@inheritdoc}
  67. */
  68. protected function doHave(string $id)
  69. {
  70. return apcu_exists($id);
  71. }
  72. /**
  73. * {@inheritdoc}
  74. */
  75. protected function doClear(string $namespace)
  76. {
  77. return isset($namespace[0]) && class_exists(\APCuIterator::class, false) && ('cli' !== \PHP_SAPI || filter_var(ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN))
  78. ? apcu_delete(new \APCuIterator(sprintf('/^%s/', preg_quote($namespace, '/')), \APC_ITER_KEY))
  79. : apcu_clear_cache();
  80. }
  81. /**
  82. * {@inheritdoc}
  83. */
  84. protected function doDelete(array $ids)
  85. {
  86. foreach ($ids as $id) {
  87. apcu_delete($id);
  88. }
  89. return true;
  90. }
  91. /**
  92. * {@inheritdoc}
  93. */
  94. protected function doSave(array $values, int $lifetime)
  95. {
  96. if (null !== $this->marshaller && (!$values = $this->marshaller->marshall($values, $failed))) {
  97. return $failed;
  98. }
  99. try {
  100. if (false === $failures = apcu_store($values, null, $lifetime)) {
  101. $failures = $values;
  102. }
  103. return array_keys($failures);
  104. } catch (\Throwable $e) {
  105. if (1 === \count($values)) {
  106. // Workaround https://github.com/krakjoe/apcu/issues/170
  107. apcu_delete(array_key_first($values));
  108. }
  109. throw $e;
  110. }
  111. }
  112. }