FilesystemCommonTrait.php 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  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\InvalidArgumentException;
  12. /**
  13. * @author Nicolas Grekas <p@tchwork.com>
  14. *
  15. * @internal
  16. */
  17. trait FilesystemCommonTrait
  18. {
  19. private $directory;
  20. private $tmp;
  21. private function init($namespace, $directory)
  22. {
  23. if (!isset($directory[0])) {
  24. $directory = sys_get_temp_dir().'/symfony-cache';
  25. } else {
  26. $directory = realpath($directory) ?: $directory;
  27. }
  28. if (isset($namespace[0])) {
  29. if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) {
  30. throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0]));
  31. }
  32. $directory .= \DIRECTORY_SEPARATOR.$namespace;
  33. }
  34. if (!file_exists($directory)) {
  35. @mkdir($directory, 0777, true);
  36. }
  37. $directory .= \DIRECTORY_SEPARATOR;
  38. // On Windows the whole path is limited to 258 chars
  39. if ('\\' === \DIRECTORY_SEPARATOR && \strlen($directory) > 234) {
  40. throw new InvalidArgumentException(sprintf('Cache directory too long (%s)', $directory));
  41. }
  42. $this->directory = $directory;
  43. }
  44. /**
  45. * {@inheritdoc}
  46. */
  47. protected function doClear($namespace)
  48. {
  49. $ok = true;
  50. foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS)) as $file) {
  51. $ok = ($file->isDir() || @unlink($file) || !file_exists($file)) && $ok;
  52. }
  53. return $ok;
  54. }
  55. /**
  56. * {@inheritdoc}
  57. */
  58. protected function doDelete(array $ids)
  59. {
  60. $ok = true;
  61. foreach ($ids as $id) {
  62. $file = $this->getFile($id);
  63. $ok = (!file_exists($file) || @unlink($file) || !file_exists($file)) && $ok;
  64. }
  65. return $ok;
  66. }
  67. private function write($file, $data, $expiresAt = null)
  68. {
  69. set_error_handler(__CLASS__.'::throwError');
  70. try {
  71. if (null === $this->tmp) {
  72. $this->tmp = $this->directory.uniqid('', true);
  73. }
  74. file_put_contents($this->tmp, $data);
  75. if (null !== $expiresAt) {
  76. touch($this->tmp, $expiresAt);
  77. }
  78. return rename($this->tmp, $file);
  79. } finally {
  80. restore_error_handler();
  81. }
  82. }
  83. private function getFile($id, $mkdir = false)
  84. {
  85. $hash = str_replace('/', '-', base64_encode(hash('sha256', static::class.$id, true)));
  86. $dir = $this->directory.strtoupper($hash[0].\DIRECTORY_SEPARATOR.$hash[1].\DIRECTORY_SEPARATOR);
  87. if ($mkdir && !file_exists($dir)) {
  88. @mkdir($dir, 0777, true);
  89. }
  90. return $dir.substr($hash, 2, 20);
  91. }
  92. /**
  93. * @internal
  94. */
  95. public static function throwError($type, $message, $file, $line)
  96. {
  97. throw new \ErrorException($message, 0, $type, $file, $line);
  98. }
  99. public function __sleep()
  100. {
  101. throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
  102. }
  103. public function __wakeup()
  104. {
  105. throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
  106. }
  107. public function __destruct()
  108. {
  109. if (method_exists(parent::class, '__destruct')) {
  110. parent::__destruct();
  111. }
  112. if (null !== $this->tmp && file_exists($this->tmp)) {
  113. unlink($this->tmp);
  114. }
  115. }
  116. }