PhpFilesTrait.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  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\Exception\CacheException;
  12. use Symfony\Component\Cache\Exception\InvalidArgumentException;
  13. /**
  14. * @author Piotr Stankowski <git@trakos.pl>
  15. * @author Nicolas Grekas <p@tchwork.com>
  16. * @author Rob Frawley 2nd <rmf@src.run>
  17. *
  18. * @internal
  19. */
  20. trait PhpFilesTrait
  21. {
  22. use FilesystemCommonTrait;
  23. private $includeHandler;
  24. private $zendDetectUnicode;
  25. public static function isSupported()
  26. {
  27. return \function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN);
  28. }
  29. /**
  30. * @return bool
  31. */
  32. public function prune()
  33. {
  34. $time = time();
  35. $pruned = true;
  36. $allowCompile = 'cli' !== \PHP_SAPI || filter_var(ini_get('opcache.enable_cli'), FILTER_VALIDATE_BOOLEAN);
  37. set_error_handler($this->includeHandler);
  38. try {
  39. foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
  40. list($expiresAt) = include $file;
  41. if ($time >= $expiresAt) {
  42. $pruned = @unlink($file) && !file_exists($file) && $pruned;
  43. if ($allowCompile) {
  44. @opcache_invalidate($file, true);
  45. }
  46. }
  47. }
  48. } finally {
  49. restore_error_handler();
  50. }
  51. return $pruned;
  52. }
  53. /**
  54. * {@inheritdoc}
  55. */
  56. protected function doFetch(array $ids)
  57. {
  58. $values = [];
  59. $now = time();
  60. if ($this->zendDetectUnicode) {
  61. $zmb = ini_set('zend.detect_unicode', 0);
  62. }
  63. set_error_handler($this->includeHandler);
  64. try {
  65. foreach ($ids as $id) {
  66. try {
  67. $file = $this->getFile($id);
  68. list($expiresAt, $values[$id]) = include $file;
  69. if ($now >= $expiresAt) {
  70. unset($values[$id]);
  71. }
  72. } catch (\Exception $e) {
  73. continue;
  74. }
  75. }
  76. } finally {
  77. restore_error_handler();
  78. if ($this->zendDetectUnicode) {
  79. ini_set('zend.detect_unicode', $zmb);
  80. }
  81. }
  82. foreach ($values as $id => $value) {
  83. if ('N;' === $value) {
  84. $values[$id] = null;
  85. } elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
  86. $values[$id] = parent::unserialize($value);
  87. }
  88. }
  89. return $values;
  90. }
  91. /**
  92. * {@inheritdoc}
  93. */
  94. protected function doHave($id)
  95. {
  96. return (bool) $this->doFetch([$id]);
  97. }
  98. /**
  99. * {@inheritdoc}
  100. */
  101. protected function doSave(array $values, $lifetime)
  102. {
  103. $ok = true;
  104. $data = [$lifetime ? time() + $lifetime : PHP_INT_MAX, ''];
  105. $allowCompile = 'cli' !== \PHP_SAPI || filter_var(ini_get('opcache.enable_cli'), FILTER_VALIDATE_BOOLEAN);
  106. foreach ($values as $key => $value) {
  107. if (null === $value || \is_object($value)) {
  108. $value = serialize($value);
  109. } elseif (\is_array($value)) {
  110. $serialized = serialize($value);
  111. $unserialized = parent::unserialize($serialized);
  112. // Store arrays serialized if they contain any objects or references
  113. if ($unserialized !== $value || (false !== strpos($serialized, ';R:') && preg_match('/;R:[1-9]/', $serialized))) {
  114. $value = $serialized;
  115. }
  116. } elseif (\is_string($value)) {
  117. // Serialize strings if they could be confused with serialized objects or arrays
  118. if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) {
  119. $value = serialize($value);
  120. }
  121. } elseif (!\is_scalar($value)) {
  122. throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \gettype($value)));
  123. }
  124. $data[1] = $value;
  125. $file = $this->getFile($key, true);
  126. $ok = $this->write($file, '<?php return '.var_export($data, true).';') && $ok;
  127. if ($allowCompile) {
  128. @opcache_invalidate($file, true);
  129. }
  130. }
  131. if (!$ok && !is_writable($this->directory)) {
  132. throw new CacheException(sprintf('Cache directory is not writable (%s)', $this->directory));
  133. }
  134. return $ok;
  135. }
  136. }