ArrayTrait.php 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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\Traits;
  11. use Psr\Log\LoggerAwareTrait;
  12. use Symfony\Component\Cache\CacheItem;
  13. /**
  14. * @author Nicolas Grekas <p@tchwork.com>
  15. *
  16. * @internal
  17. */
  18. trait ArrayTrait
  19. {
  20. use LoggerAwareTrait;
  21. private $storeSerialized;
  22. private $values = [];
  23. private $expiries = [];
  24. /**
  25. * Returns all cached values, with cache miss as null.
  26. *
  27. * @return array
  28. */
  29. public function getValues()
  30. {
  31. if (!$this->storeSerialized) {
  32. return $this->values;
  33. }
  34. $values = $this->values;
  35. foreach ($values as $k => $v) {
  36. if (null === $v || 'N;' === $v) {
  37. continue;
  38. }
  39. if (!\is_string($v) || !isset($v[2]) || ':' !== $v[1]) {
  40. $values[$k] = serialize($v);
  41. }
  42. }
  43. return $values;
  44. }
  45. /**
  46. * {@inheritdoc}
  47. */
  48. public function hasItem($key)
  49. {
  50. if (\is_string($key) && isset($this->expiries[$key]) && $this->expiries[$key] > microtime(true)) {
  51. return true;
  52. }
  53. CacheItem::validateKey($key);
  54. return isset($this->expiries[$key]) && !$this->deleteItem($key);
  55. }
  56. /**
  57. * {@inheritdoc}
  58. */
  59. public function clear()
  60. {
  61. $this->values = $this->expiries = [];
  62. return true;
  63. }
  64. /**
  65. * {@inheritdoc}
  66. */
  67. public function deleteItem($key)
  68. {
  69. if (!\is_string($key) || !isset($this->expiries[$key])) {
  70. CacheItem::validateKey($key);
  71. }
  72. unset($this->values[$key], $this->expiries[$key]);
  73. return true;
  74. }
  75. /**
  76. * {@inheritdoc}
  77. */
  78. public function reset()
  79. {
  80. $this->clear();
  81. }
  82. private function generateItems(array $keys, $now, $f)
  83. {
  84. foreach ($keys as $i => $key) {
  85. if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] > $now || !$this->deleteItem($key))) {
  86. $this->values[$key] = $value = null;
  87. } else {
  88. $value = $this->storeSerialized ? $this->unfreeze($key, $isHit) : $this->values[$key];
  89. }
  90. unset($keys[$i]);
  91. yield $key => $f($key, $value, $isHit);
  92. }
  93. foreach ($keys as $key) {
  94. yield $key => $f($key, null, false);
  95. }
  96. }
  97. private function freeze($value, $key)
  98. {
  99. if (null === $value) {
  100. return 'N;';
  101. }
  102. if (\is_string($value)) {
  103. // Serialize strings if they could be confused with serialized objects or arrays
  104. if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) {
  105. return serialize($value);
  106. }
  107. } elseif (!is_scalar($value)) {
  108. try {
  109. $serialized = serialize($value);
  110. } catch (\Exception $e) {
  111. $type = \is_object($value) ? \get_class($value) : \gettype($value);
  112. $message = sprintf('Failed to save key "{key}" of type %s: %s', $type, $e->getMessage());
  113. CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e]);
  114. return null;
  115. }
  116. // Keep value serialized if it contains any objects or any internal references
  117. if ('C' === $serialized[0] || 'O' === $serialized[0] || preg_match('/;[OCRr]:[1-9]/', $serialized)) {
  118. return $serialized;
  119. }
  120. }
  121. return $value;
  122. }
  123. private function unfreeze(string $key, bool &$isHit)
  124. {
  125. if ('N;' === $value = $this->values[$key]) {
  126. return null;
  127. }
  128. if (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
  129. try {
  130. $value = unserialize($value);
  131. } catch (\Exception $e) {
  132. CacheItem::log($this->logger, 'Failed to unserialize key "{key}": '.$e->getMessage(), ['key' => $key, 'exception' => $e]);
  133. $value = false;
  134. }
  135. if (false === $value) {
  136. $this->values[$key] = $value = null;
  137. $isHit = false;
  138. }
  139. }
  140. return $value;
  141. }
  142. }