ResourceCheckerConfigCache.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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\Config;
  11. use Symfony\Component\Config\Resource\ResourceInterface;
  12. use Symfony\Component\Filesystem\Exception\IOException;
  13. use Symfony\Component\Filesystem\Filesystem;
  14. /**
  15. * ResourceCheckerConfigCache uses instances of ResourceCheckerInterface
  16. * to check whether cached data is still fresh.
  17. *
  18. * @author Matthias Pigulla <mp@webfactory.de>
  19. */
  20. class ResourceCheckerConfigCache implements ConfigCacheInterface
  21. {
  22. /**
  23. * @var string
  24. */
  25. private $file;
  26. /**
  27. * @var ResourceCheckerInterface[]
  28. */
  29. private $resourceCheckers;
  30. /**
  31. * @param string $file The absolute cache path
  32. * @param ResourceCheckerInterface[] $resourceCheckers The ResourceCheckers to use for the freshness check
  33. */
  34. public function __construct($file, array $resourceCheckers = array())
  35. {
  36. $this->file = $file;
  37. $this->resourceCheckers = $resourceCheckers;
  38. }
  39. /**
  40. * {@inheritdoc}
  41. */
  42. public function getPath()
  43. {
  44. return $this->file;
  45. }
  46. /**
  47. * Checks if the cache is still fresh.
  48. *
  49. * This implementation will make a decision solely based on the ResourceCheckers
  50. * passed in the constructor.
  51. *
  52. * The first ResourceChecker that supports a given resource is considered authoritative.
  53. * Resources with no matching ResourceChecker will silently be ignored and considered fresh.
  54. *
  55. * @return bool true if the cache is fresh, false otherwise
  56. */
  57. public function isFresh()
  58. {
  59. if (!is_file($this->file)) {
  60. return false;
  61. }
  62. if (!$this->resourceCheckers) {
  63. return true; // shortcut - if we don't have any checkers we don't need to bother with the meta file at all
  64. }
  65. $metadata = $this->getMetaFile();
  66. if (!is_file($metadata)) {
  67. return false;
  68. }
  69. $e = null;
  70. $meta = false;
  71. $time = filemtime($this->file);
  72. $signalingException = new \UnexpectedValueException();
  73. $prevUnserializeHandler = ini_set('unserialize_callback_func', '');
  74. $prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context) use (&$prevErrorHandler, $signalingException) {
  75. if (E_WARNING === $type && 'Class __PHP_Incomplete_Class has no unserializer' === $msg) {
  76. throw $signalingException;
  77. }
  78. return $prevErrorHandler ? $prevErrorHandler($type, $msg, $file, $line, $context) : false;
  79. });
  80. try {
  81. $meta = unserialize(file_get_contents($metadata));
  82. } catch (\Error $e) {
  83. } catch (\Exception $e) {
  84. }
  85. restore_error_handler();
  86. ini_set('unserialize_callback_func', $prevUnserializeHandler);
  87. if (null !== $e && $e !== $signalingException) {
  88. throw $e;
  89. }
  90. if (false === $meta) {
  91. return false;
  92. }
  93. foreach ($meta as $resource) {
  94. /* @var ResourceInterface $resource */
  95. foreach ($this->resourceCheckers as $checker) {
  96. if (!$checker->supports($resource)) {
  97. continue; // next checker
  98. }
  99. if ($checker->isFresh($resource, $time)) {
  100. break; // no need to further check this resource
  101. }
  102. return false; // cache is stale
  103. }
  104. // no suitable checker found, ignore this resource
  105. }
  106. return true;
  107. }
  108. /**
  109. * Writes cache.
  110. *
  111. * @param string $content The content to write in the cache
  112. * @param ResourceInterface[] $metadata An array of metadata
  113. *
  114. * @throws \RuntimeException When cache file can't be written
  115. */
  116. public function write($content, array $metadata = null)
  117. {
  118. $mode = 0666;
  119. $umask = umask();
  120. $filesystem = new Filesystem();
  121. $filesystem->dumpFile($this->file, $content, null);
  122. try {
  123. $filesystem->chmod($this->file, $mode, $umask);
  124. } catch (IOException $e) {
  125. // discard chmod failure (some filesystem may not support it)
  126. }
  127. if (null !== $metadata) {
  128. $filesystem->dumpFile($this->getMetaFile(), serialize($metadata), null);
  129. try {
  130. $filesystem->chmod($this->getMetaFile(), $mode, $umask);
  131. } catch (IOException $e) {
  132. // discard chmod failure (some filesystem may not support it)
  133. }
  134. }
  135. if (\function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN)) {
  136. @opcache_invalidate($this->file, true);
  137. }
  138. }
  139. /**
  140. * Gets the meta file path.
  141. *
  142. * @return string The meta file path
  143. */
  144. private function getMetaFile()
  145. {
  146. return $this->file.'.meta';
  147. }
  148. }