FileCacheManager.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. <?php
  2. /*
  3. * This file is part of the PHP CS utility.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * This source file is subject to the MIT license that is bundled
  8. * with this source code in the file LICENSE.
  9. */
  10. namespace Symfony\CS;
  11. use Symfony\Component\Filesystem\Exception\IOException;
  12. /**
  13. * Class supports caching information about state of fixing files.
  14. *
  15. * Cache is supported only for phar version and version installed via composer.
  16. *
  17. * File will be processed by PHP CS Fixer only if any of the following conditions is fulfilled:
  18. * - cache is not available,
  19. * - fixer version changed,
  20. * - fixers list is changed,
  21. * - file is new,
  22. * - file changed.
  23. *
  24. * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
  25. */
  26. class FileCacheManager
  27. {
  28. const CACHE_FILE = '.php_cs.cache';
  29. private $dir;
  30. private $isEnabled;
  31. private $fixers;
  32. private $newHashes = array();
  33. private $oldHashes = array();
  34. public function __construct($isEnabled, $dir, array $fixers)
  35. {
  36. $this->isEnabled = $isEnabled;
  37. $this->dir = null !== $dir ? $dir.DIRECTORY_SEPARATOR : '';
  38. $this->fixers = array_map(function (FixerInterface $f) {
  39. return $f->getName();
  40. }, $fixers);
  41. sort($this->fixers);
  42. $this->readFromFile();
  43. }
  44. public function __destruct()
  45. {
  46. $this->saveToFile();
  47. }
  48. public function needFixing($file, $fileContent)
  49. {
  50. if (!$this->isCacheAvailable()) {
  51. return true;
  52. }
  53. if (!isset($this->oldHashes[$file])) {
  54. return true;
  55. }
  56. if ($this->oldHashes[$file] !== $this->calcHash($fileContent)) {
  57. return true;
  58. }
  59. // file do not change - keep hash in new collection
  60. $this->newHashes[$file] = $this->oldHashes[$file];
  61. return false;
  62. }
  63. public function setFile($file, $fileContent)
  64. {
  65. if (!$this->isCacheAvailable()) {
  66. return;
  67. }
  68. $this->newHashes[$file] = $this->calcHash($fileContent);
  69. }
  70. private function calcHash($content)
  71. {
  72. return crc32($content);
  73. }
  74. private function isCacheAvailable()
  75. {
  76. static $result;
  77. if (null === $result) {
  78. $result = $this->isEnabled && (ToolInfo::isInstalledAsPhar() || ToolInfo::isInstalledByComposer());
  79. }
  80. return $result;
  81. }
  82. private function isCacheStale($cacheVersion, $fixers)
  83. {
  84. if (!$this->isCacheAvailable()) {
  85. return true;
  86. }
  87. return ToolInfo::getVersion() !== $cacheVersion || $this->fixers !== $fixers;
  88. }
  89. private function readFromFile()
  90. {
  91. if (!$this->isCacheAvailable()) {
  92. return;
  93. }
  94. if (!file_exists($this->dir.self::CACHE_FILE)) {
  95. return;
  96. }
  97. $content = file_get_contents($this->dir.self::CACHE_FILE);
  98. $data = unserialize($content);
  99. // BC for old cache without fixers list
  100. if (!isset($data['fixers'])) {
  101. $data['fixers'] = null;
  102. }
  103. // Set hashes only if the cache is fresh, otherwise we need to parse all files
  104. if (!$this->isCacheStale($data['version'], $data['fixers'])) {
  105. $this->oldHashes = $data['hashes'];
  106. }
  107. }
  108. private function saveToFile()
  109. {
  110. if (!$this->isCacheAvailable()) {
  111. return;
  112. }
  113. $data = serialize(
  114. array(
  115. 'version' => ToolInfo::getVersion(),
  116. 'fixers' => $this->fixers,
  117. 'hashes' => $this->newHashes,
  118. )
  119. );
  120. if (false === @file_put_contents($this->dir.self::CACHE_FILE, $data, LOCK_EX)) {
  121. throw new IOException(sprintf('Failed to write file "%s".', $this->cacheFile), 0, null, $this->cacheFile);
  122. }
  123. }
  124. }