annotate vendor/squizlabs/php_codesniffer/src/Util/Cache.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@17 1 <?php
Chris@17 2 /**
Chris@17 3 * Function for caching between runs.
Chris@17 4 *
Chris@17 5 * @author Greg Sherwood <gsherwood@squiz.net>
Chris@17 6 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
Chris@17 7 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
Chris@17 8 */
Chris@17 9
Chris@17 10 namespace PHP_CodeSniffer\Util;
Chris@17 11
Chris@17 12 use PHP_CodeSniffer\Autoload;
Chris@17 13 use PHP_CodeSniffer\Config;
Chris@17 14 use PHP_CodeSniffer\Ruleset;
Chris@17 15
Chris@17 16 class Cache
Chris@17 17 {
Chris@17 18
Chris@17 19 /**
Chris@17 20 * The filesystem location of the cache file.
Chris@17 21 *
Chris@17 22 * @var void
Chris@17 23 */
Chris@17 24 private static $path = '';
Chris@17 25
Chris@17 26 /**
Chris@17 27 * The cached data.
Chris@17 28 *
Chris@17 29 * @var array<string, mixed>
Chris@17 30 */
Chris@17 31 private static $cache = [];
Chris@17 32
Chris@17 33
Chris@17 34 /**
Chris@17 35 * Loads existing cache data for the run, if any.
Chris@17 36 *
Chris@17 37 * @param \PHP_CodeSniffer\Ruleset $ruleset The ruleset used for the run.
Chris@17 38 * @param \PHP_CodeSniffer\Config $config The config data for the run.
Chris@17 39 *
Chris@17 40 * @return void
Chris@17 41 */
Chris@17 42 public static function load(Ruleset $ruleset, Config $config)
Chris@17 43 {
Chris@17 44 // Look at every loaded sniff class so far and use their file contents
Chris@17 45 // to generate a hash for the code used during the run.
Chris@17 46 // At this point, the loaded class list contains the core PHPCS code
Chris@17 47 // and all sniffs that have been loaded as part of the run.
Chris@17 48 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 49 echo PHP_EOL."\tGenerating loaded file list for code hash".PHP_EOL;
Chris@17 50 }
Chris@17 51
Chris@17 52 $codeHashFiles = [];
Chris@17 53
Chris@17 54 $classes = array_keys(Autoload::getLoadedClasses());
Chris@17 55 sort($classes);
Chris@17 56
Chris@17 57 $installDir = dirname(__DIR__);
Chris@17 58 $installDirLen = strlen($installDir);
Chris@17 59 $standardDir = $installDir.DIRECTORY_SEPARATOR.'Standards';
Chris@17 60 $standardDirLen = strlen($standardDir);
Chris@17 61 foreach ($classes as $file) {
Chris@17 62 if (substr($file, 0, $standardDirLen) !== $standardDir) {
Chris@17 63 if (substr($file, 0, $installDirLen) === $installDir) {
Chris@17 64 // We are only interested in sniffs here.
Chris@17 65 continue;
Chris@17 66 }
Chris@17 67
Chris@17 68 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 69 echo "\t\t=> external file: $file".PHP_EOL;
Chris@17 70 }
Chris@17 71 } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 72 echo "\t\t=> internal sniff: $file".PHP_EOL;
Chris@17 73 }
Chris@17 74
Chris@17 75 $codeHashFiles[] = $file;
Chris@17 76 }
Chris@17 77
Chris@17 78 // Add the content of the used rulesets to the hash so that sniff setting
Chris@17 79 // changes in the ruleset invalidate the cache.
Chris@17 80 $rulesets = $ruleset->paths;
Chris@17 81 sort($rulesets);
Chris@17 82 foreach ($rulesets as $file) {
Chris@17 83 if (substr($file, 0, $standardDirLen) !== $standardDir) {
Chris@17 84 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 85 echo "\t\t=> external ruleset: $file".PHP_EOL;
Chris@17 86 }
Chris@17 87 } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 88 echo "\t\t=> internal ruleset: $file".PHP_EOL;
Chris@17 89 }
Chris@17 90
Chris@17 91 $codeHashFiles[] = $file;
Chris@17 92 }
Chris@17 93
Chris@17 94 // Go through the core PHPCS code and add those files to the file
Chris@17 95 // hash. This ensures that core PHPCS changes will also invalidate the cache.
Chris@17 96 // Note that we ignore sniffs here, and any files that don't affect
Chris@17 97 // the outcome of the run.
Chris@17 98 $di = new \RecursiveDirectoryIterator($installDir);
Chris@17 99 $filter = new \RecursiveCallbackFilterIterator(
Chris@17 100 $di,
Chris@17 101 function ($file, $key, $iterator) {
Chris@17 102 // Skip hidden files.
Chris@17 103 $filename = $file->getFilename();
Chris@17 104 if (substr($filename, 0, 1) === '.') {
Chris@17 105 return false;
Chris@17 106 }
Chris@17 107
Chris@17 108 $filePath = Common::realpath($file->getPathname());
Chris@17 109 if ($filePath === false) {
Chris@17 110 return false;
Chris@17 111 }
Chris@17 112
Chris@17 113 if (is_dir($filePath) === true
Chris@17 114 && ($filename === 'Standards'
Chris@17 115 || $filename === 'Exceptions'
Chris@17 116 || $filename === 'Reports'
Chris@17 117 || $filename === 'Generators')
Chris@17 118 ) {
Chris@17 119 return false;
Chris@17 120 }
Chris@17 121
Chris@17 122 return true;
Chris@17 123 }
Chris@17 124 );
Chris@17 125
Chris@17 126 $iterator = new \RecursiveIteratorIterator($filter);
Chris@17 127 foreach ($iterator as $file) {
Chris@17 128 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 129 echo "\t\t=> core file: $file".PHP_EOL;
Chris@17 130 }
Chris@17 131
Chris@17 132 $codeHashFiles[] = $file->getPathname();
Chris@17 133 }
Chris@17 134
Chris@17 135 $codeHash = '';
Chris@17 136 sort($codeHashFiles);
Chris@17 137 foreach ($codeHashFiles as $file) {
Chris@17 138 $codeHash .= md5_file($file);
Chris@17 139 }
Chris@17 140
Chris@17 141 $codeHash = md5($codeHash);
Chris@17 142
Chris@17 143 // Along with the code hash, use various settings that can affect
Chris@17 144 // the results of a run to create a new hash. This hash will be used
Chris@17 145 // in the cache file name.
Chris@17 146 $rulesetHash = md5(var_export($ruleset->ignorePatterns, true).var_export($ruleset->includePatterns, true));
Chris@17 147 $configData = [
Chris@17 148 'phpVersion' => PHP_VERSION_ID,
Chris@17 149 'tabWidth' => $config->tabWidth,
Chris@17 150 'encoding' => $config->encoding,
Chris@17 151 'recordErrors' => $config->recordErrors,
Chris@17 152 'annotations' => $config->annotations,
Chris@17 153 'configData' => Config::getAllConfigData(),
Chris@17 154 'codeHash' => $codeHash,
Chris@17 155 'rulesetHash' => $rulesetHash,
Chris@17 156 ];
Chris@17 157
Chris@17 158 $configString = var_export($configData, true);
Chris@17 159 $cacheHash = substr(sha1($configString), 0, 12);
Chris@17 160
Chris@17 161 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 162 echo "\tGenerating cache key data".PHP_EOL;
Chris@17 163 foreach ($configData as $key => $value) {
Chris@17 164 if (is_array($value) === true) {
Chris@17 165 echo "\t\t=> $key:".PHP_EOL;
Chris@17 166 foreach ($value as $subKey => $subValue) {
Chris@17 167 echo "\t\t\t=> $subKey: $subValue".PHP_EOL;
Chris@17 168 }
Chris@17 169
Chris@17 170 continue;
Chris@17 171 }
Chris@17 172
Chris@17 173 if ($value === true || $value === false) {
Chris@17 174 $value = (int) $value;
Chris@17 175 }
Chris@17 176
Chris@17 177 echo "\t\t=> $key: $value".PHP_EOL;
Chris@17 178 }
Chris@17 179
Chris@17 180 echo "\t\t=> cacheHash: $cacheHash".PHP_EOL;
Chris@17 181 }//end if
Chris@17 182
Chris@17 183 if ($config->cacheFile !== null) {
Chris@17 184 $cacheFile = $config->cacheFile;
Chris@17 185 } else {
Chris@17 186 // Determine the common paths for all files being checked.
Chris@17 187 // We can use this to locate an existing cache file, or to
Chris@17 188 // determine where to create a new one.
Chris@17 189 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 190 echo "\tChecking possible cache file paths".PHP_EOL;
Chris@17 191 }
Chris@17 192
Chris@17 193 $paths = [];
Chris@17 194 foreach ($config->files as $file) {
Chris@17 195 $file = Common::realpath($file);
Chris@17 196 while ($file !== DIRECTORY_SEPARATOR) {
Chris@17 197 if (isset($paths[$file]) === false) {
Chris@17 198 $paths[$file] = 1;
Chris@17 199 } else {
Chris@17 200 $paths[$file]++;
Chris@17 201 }
Chris@17 202
Chris@17 203 $lastFile = $file;
Chris@17 204 $file = dirname($file);
Chris@17 205 if ($file === $lastFile) {
Chris@17 206 // Just in case something went wrong,
Chris@17 207 // we don't want to end up in an infinite loop.
Chris@17 208 break;
Chris@17 209 }
Chris@17 210 }
Chris@17 211 }
Chris@17 212
Chris@17 213 ksort($paths);
Chris@17 214 $paths = array_reverse($paths);
Chris@17 215
Chris@17 216 $numFiles = count($config->files);
Chris@17 217
Chris@17 218 $cacheFile = null;
Chris@17 219 $cacheDir = getenv('XDG_CACHE_HOME');
Chris@17 220 if ($cacheDir === false || is_dir($cacheDir) === false) {
Chris@17 221 $cacheDir = sys_get_temp_dir();
Chris@17 222 }
Chris@17 223
Chris@17 224 foreach ($paths as $file => $count) {
Chris@17 225 if ($count !== $numFiles) {
Chris@17 226 unset($paths[$file]);
Chris@17 227 continue;
Chris@17 228 }
Chris@17 229
Chris@17 230 $fileHash = substr(sha1($file), 0, 12);
Chris@17 231 $testFile = $cacheDir.DIRECTORY_SEPARATOR."phpcs.$fileHash.$cacheHash.cache";
Chris@17 232 if ($cacheFile === null) {
Chris@17 233 // This will be our default location if we can't find
Chris@17 234 // an existing file.
Chris@17 235 $cacheFile = $testFile;
Chris@17 236 }
Chris@17 237
Chris@17 238 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 239 echo "\t\t=> $testFile".PHP_EOL;
Chris@17 240 echo "\t\t\t * based on shared location: $file *".PHP_EOL;
Chris@17 241 }
Chris@17 242
Chris@17 243 if (file_exists($testFile) === true) {
Chris@17 244 $cacheFile = $testFile;
Chris@17 245 break;
Chris@17 246 }
Chris@17 247 }//end foreach
Chris@17 248
Chris@17 249 if ($cacheFile === null) {
Chris@17 250 // Unlikely, but just in case $paths is empty for some reason.
Chris@17 251 $cacheFile = $cacheDir.DIRECTORY_SEPARATOR."phpcs.$cacheHash.cache";
Chris@17 252 }
Chris@17 253 }//end if
Chris@17 254
Chris@17 255 self::$path = $cacheFile;
Chris@17 256 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 257 echo "\t=> Using cache file: ".self::$path.PHP_EOL;
Chris@17 258 }
Chris@17 259
Chris@17 260 if (file_exists(self::$path) === true) {
Chris@17 261 self::$cache = json_decode(file_get_contents(self::$path), true);
Chris@17 262
Chris@17 263 // Verify the contents of the cache file.
Chris@17 264 if (self::$cache['config'] !== $configData) {
Chris@17 265 self::$cache = [];
Chris@17 266 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 267 echo "\t* cache was invalid and has been cleared *".PHP_EOL;
Chris@17 268 }
Chris@17 269 }
Chris@17 270 } else if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@17 271 echo "\t* cache file does not exist *".PHP_EOL;
Chris@17 272 }
Chris@17 273
Chris@17 274 self::$cache['config'] = $configData;
Chris@17 275
Chris@17 276 }//end load()
Chris@17 277
Chris@17 278
Chris@17 279 /**
Chris@17 280 * Saves the current cache to the filesystem.
Chris@17 281 *
Chris@17 282 * @return void
Chris@17 283 */
Chris@17 284 public static function save()
Chris@17 285 {
Chris@17 286 file_put_contents(self::$path, json_encode(self::$cache));
Chris@17 287
Chris@17 288 }//end save()
Chris@17 289
Chris@17 290
Chris@17 291 /**
Chris@17 292 * Retrieves a single entry from the cache.
Chris@17 293 *
Chris@17 294 * @param string $key The key of the data to get. If NULL,
Chris@17 295 * everything in the cache is returned.
Chris@17 296 *
Chris@17 297 * @return mixed
Chris@17 298 */
Chris@17 299 public static function get($key=null)
Chris@17 300 {
Chris@17 301 if ($key === null) {
Chris@17 302 return self::$cache;
Chris@17 303 }
Chris@17 304
Chris@17 305 if (isset(self::$cache[$key]) === true) {
Chris@17 306 return self::$cache[$key];
Chris@17 307 }
Chris@17 308
Chris@17 309 return false;
Chris@17 310
Chris@17 311 }//end get()
Chris@17 312
Chris@17 313
Chris@17 314 /**
Chris@17 315 * Retrieves a single entry from the cache.
Chris@17 316 *
Chris@17 317 * @param string $key The key of the data to set. If NULL,
Chris@17 318 * sets the entire cache.
Chris@17 319 * @param mixed $value The value to set.
Chris@17 320 *
Chris@17 321 * @return void
Chris@17 322 */
Chris@17 323 public static function set($key, $value)
Chris@17 324 {
Chris@17 325 if ($key === null) {
Chris@17 326 self::$cache = $value;
Chris@17 327 } else {
Chris@17 328 self::$cache[$key] = $value;
Chris@17 329 }
Chris@17 330
Chris@17 331 }//end set()
Chris@17 332
Chris@17 333
Chris@17 334 /**
Chris@17 335 * Retrieves the number of cache entries.
Chris@17 336 *
Chris@17 337 * @return int
Chris@17 338 */
Chris@17 339 public static function getSize()
Chris@17 340 {
Chris@17 341 return (count(self::$cache) - 1);
Chris@17 342
Chris@17 343 }//end getSize()
Chris@17 344
Chris@17 345
Chris@17 346 }//end class