PhpArrayTrait.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  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 Symfony\Component\Cache\CacheItem;
  12. use Symfony\Component\Cache\Exception\InvalidArgumentException;
  13. /**
  14. * @author Titouan Galopin <galopintitouan@gmail.com>
  15. * @author Nicolas Grekas <p@tchwork.com>
  16. *
  17. * @internal
  18. */
  19. trait PhpArrayTrait
  20. {
  21. use ProxyTrait;
  22. private $file;
  23. private $values;
  24. private $zendDetectUnicode;
  25. private static $valuesCache = [];
  26. /**
  27. * Store an array of cached values.
  28. *
  29. * @param array $values The cached values
  30. */
  31. public function warmUp(array $values)
  32. {
  33. if (file_exists($this->file)) {
  34. if (!is_file($this->file)) {
  35. throw new InvalidArgumentException(sprintf('Cache path exists and is not a file: "%s".', $this->file));
  36. }
  37. if (!is_writable($this->file)) {
  38. throw new InvalidArgumentException(sprintf('Cache file is not writable: "%s".', $this->file));
  39. }
  40. } else {
  41. $directory = \dirname($this->file);
  42. if (!is_dir($directory) && !@mkdir($directory, 0777, true)) {
  43. throw new InvalidArgumentException(sprintf('Cache directory does not exist and cannot be created: "%s".', $directory));
  44. }
  45. if (!is_writable($directory)) {
  46. throw new InvalidArgumentException(sprintf('Cache directory is not writable: "%s".', $directory));
  47. }
  48. }
  49. $dump = <<<'EOF'
  50. <?php
  51. // This file has been auto-generated by the Symfony Cache Component.
  52. return [
  53. EOF;
  54. foreach ($values as $key => $value) {
  55. CacheItem::validateKey(\is_int($key) ? (string) $key : $key);
  56. if (null === $value || \is_object($value)) {
  57. try {
  58. $value = serialize($value);
  59. } catch (\Exception $e) {
  60. throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable "%s" value.', $key, \get_class($value)), 0, $e);
  61. }
  62. } elseif (\is_array($value)) {
  63. try {
  64. $serialized = serialize($value);
  65. $unserialized = unserialize($serialized);
  66. } catch (\Exception $e) {
  67. throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable array value.', $key), 0, $e);
  68. }
  69. // Store arrays serialized if they contain any objects or references
  70. if ($unserialized !== $value || (false !== strpos($serialized, ';R:') && preg_match('/;R:[1-9]/', $serialized))) {
  71. $value = $serialized;
  72. }
  73. } elseif (\is_string($value)) {
  74. // Serialize strings if they could be confused with serialized objects or arrays
  75. if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) {
  76. $value = serialize($value);
  77. }
  78. } elseif (!is_scalar($value)) {
  79. throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable "%s" value.', $key, \gettype($value)));
  80. }
  81. $dump .= var_export($key, true).' => '.var_export($value, true).",\n";
  82. }
  83. $dump .= "\n];\n";
  84. $dump = str_replace("' . \"\\0\" . '", "\0", $dump);
  85. $tmpFile = uniqid($this->file, true);
  86. file_put_contents($tmpFile, $dump);
  87. @chmod($tmpFile, 0666 & ~umask());
  88. unset($serialized, $unserialized, $value, $dump);
  89. @rename($tmpFile, $this->file);
  90. unset(self::$valuesCache[$this->file]);
  91. $this->initialize();
  92. }
  93. /**
  94. * {@inheritdoc}
  95. */
  96. public function clear()
  97. {
  98. $this->values = [];
  99. $cleared = @unlink($this->file) || !file_exists($this->file);
  100. unset(self::$valuesCache[$this->file]);
  101. return $this->pool->clear() && $cleared;
  102. }
  103. /**
  104. * Load the cache file.
  105. */
  106. private function initialize()
  107. {
  108. if (isset(self::$valuesCache[$this->file])) {
  109. $this->values = self::$valuesCache[$this->file];
  110. return;
  111. }
  112. if ($this->zendDetectUnicode) {
  113. $zmb = ini_set('zend.detect_unicode', 0);
  114. }
  115. try {
  116. $this->values = self::$valuesCache[$this->file] = file_exists($this->file) ? (include $this->file ?: []) : [];
  117. } finally {
  118. if ($this->zendDetectUnicode) {
  119. ini_set('zend.detect_unicode', $zmb);
  120. }
  121. }
  122. }
  123. }