Current File : //var/www/prestashop/modules/psxdesign/vendor/friendsofphp/php-cs-fixer/src/Runner/Runner.php |
<?php
declare(strict_types=1);
/*
* This file is part of PHP CS Fixer.
*
* (c) Fabien Potencier <fabien@symfony.com>
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Runner;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Cache\CacheManagerInterface;
use PhpCsFixer\Cache\Directory;
use PhpCsFixer\Cache\DirectoryInterface;
use PhpCsFixer\Differ\DifferInterface;
use PhpCsFixer\Error\Error;
use PhpCsFixer\Error\ErrorsManager;
use PhpCsFixer\FileReader;
use PhpCsFixer\Fixer\FixerInterface;
use PhpCsFixer\FixerFileProcessedEvent;
use PhpCsFixer\Linter\LinterInterface;
use PhpCsFixer\Linter\LintingException;
use PhpCsFixer\Linter\LintingResultInterface;
use PhpCsFixer\Tokenizer\Tokens;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Contracts\EventDispatcher\Event;
/**
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
*/
final class Runner
{
/**
* @var DifferInterface
*/
private $differ;
/**
* @var DirectoryInterface
*/
private $directory;
/**
* @var null|EventDispatcherInterface
*/
private $eventDispatcher;
/**
* @var ErrorsManager
*/
private $errorsManager;
/**
* @var CacheManagerInterface
*/
private $cacheManager;
/**
* @var bool
*/
private $isDryRun;
/**
* @var LinterInterface
*/
private $linter;
/**
* @var \Traversable
*/
private $finder;
/**
* @var FixerInterface[]
*/
private $fixers;
/**
* @var bool
*/
private $stopOnViolation;
public function __construct(
$finder,
array $fixers,
DifferInterface $differ,
?EventDispatcherInterface $eventDispatcher,
ErrorsManager $errorsManager,
LinterInterface $linter,
bool $isDryRun,
CacheManagerInterface $cacheManager,
?DirectoryInterface $directory = null,
bool $stopOnViolation = false
) {
$this->finder = $finder;
$this->fixers = $fixers;
$this->differ = $differ;
$this->eventDispatcher = $eventDispatcher;
$this->errorsManager = $errorsManager;
$this->linter = $linter;
$this->isDryRun = $isDryRun;
$this->cacheManager = $cacheManager;
$this->directory = $directory ?: new Directory('');
$this->stopOnViolation = $stopOnViolation;
}
public function fix(): array
{
$changed = [];
$finder = $this->finder;
$finderIterator = $finder instanceof \IteratorAggregate ? $finder->getIterator() : $finder;
$fileFilteredFileIterator = new FileFilterIterator(
$finderIterator,
$this->eventDispatcher,
$this->cacheManager
);
$collection = $this->linter->isAsync()
? new FileCachingLintingIterator($fileFilteredFileIterator, $this->linter)
: new FileLintingIterator($fileFilteredFileIterator, $this->linter);
/** @var \SplFileInfo $file */
foreach ($collection as $file) {
$fixInfo = $this->fixFile($file, $collection->currentLintingResult());
// we do not need Tokens to still caching just fixed file - so clear the cache
Tokens::clearCache();
if (null !== $fixInfo) {
$name = $this->directory->getRelativePathTo($file->__toString());
$changed[$name] = $fixInfo;
if ($this->stopOnViolation) {
break;
}
}
}
return $changed;
}
private function fixFile(\SplFileInfo $file, LintingResultInterface $lintingResult): ?array
{
$name = $file->getPathname();
try {
$lintingResult->check();
} catch (LintingException $e) {
$this->dispatchEvent(
FixerFileProcessedEvent::NAME,
new FixerFileProcessedEvent(FixerFileProcessedEvent::STATUS_INVALID)
);
$this->errorsManager->report(new Error(Error::TYPE_INVALID, $name, $e));
return null;
}
$old = FileReader::createSingleton()->read($file->getRealPath());
$tokens = Tokens::fromCode($old);
$oldHash = $tokens->getCodeHash();
$newHash = $oldHash;
$new = $old;
$appliedFixers = [];
try {
foreach ($this->fixers as $fixer) {
// for custom fixers we don't know is it safe to run `->fix()` without checking `->supports()` and `->isCandidate()`,
// thus we need to check it and conditionally skip fixing
if (
!$fixer instanceof AbstractFixer
&& (!$fixer->supports($file) || !$fixer->isCandidate($tokens))
) {
continue;
}
$fixer->fix($file, $tokens);
if ($tokens->isChanged()) {
$tokens->clearEmptyTokens();
$tokens->clearChanged();
$appliedFixers[] = $fixer->getName();
}
}
} catch (\ParseError $e) {
$this->dispatchEvent(
FixerFileProcessedEvent::NAME,
new FixerFileProcessedEvent(FixerFileProcessedEvent::STATUS_LINT)
);
$this->errorsManager->report(new Error(Error::TYPE_LINT, $name, $e));
return null;
} catch (\Throwable $e) {
$this->processException($name, $e);
return null;
}
$fixInfo = null;
if (!empty($appliedFixers)) {
$new = $tokens->generateCode();
$newHash = $tokens->getCodeHash();
}
// We need to check if content was changed and then applied changes.
// But we can't simply check $appliedFixers, because one fixer may revert
// work of other and both of them will mark collection as changed.
// Therefore we need to check if code hashes changed.
if ($oldHash !== $newHash) {
$fixInfo = [
'appliedFixers' => $appliedFixers,
'diff' => $this->differ->diff($old, $new, $file),
];
try {
$this->linter->lintSource($new)->check();
} catch (LintingException $e) {
$this->dispatchEvent(
FixerFileProcessedEvent::NAME,
new FixerFileProcessedEvent(FixerFileProcessedEvent::STATUS_LINT)
);
$this->errorsManager->report(new Error(Error::TYPE_LINT, $name, $e, $fixInfo['appliedFixers'], $fixInfo['diff']));
return null;
}
if (!$this->isDryRun) {
$fileName = $file->getRealPath();
if (!file_exists($fileName)) {
throw new IOException(
sprintf('Failed to write file "%s" (no longer) exists.', $file->getPathname()),
0,
null,
$file->getPathname()
);
}
if (is_dir($fileName)) {
throw new IOException(
sprintf('Cannot write file "%s" as the location exists as directory.', $fileName),
0,
null,
$fileName
);
}
if (!is_writable($fileName)) {
throw new IOException(
sprintf('Cannot write to file "%s" as it is not writable.', $fileName),
0,
null,
$fileName
);
}
if (false === @file_put_contents($fileName, $new)) {
$error = error_get_last();
throw new IOException(
sprintf('Failed to write file "%s", "%s".', $fileName, $error ? $error['message'] : 'no reason available'),
0,
null,
$fileName
);
}
}
}
$this->cacheManager->setFile($name, $new);
$this->dispatchEvent(
FixerFileProcessedEvent::NAME,
new FixerFileProcessedEvent($fixInfo ? FixerFileProcessedEvent::STATUS_FIXED : FixerFileProcessedEvent::STATUS_NO_CHANGES)
);
return $fixInfo;
}
/**
* Process an exception that occurred.
*/
private function processException(string $name, \Throwable $e): void
{
$this->dispatchEvent(
FixerFileProcessedEvent::NAME,
new FixerFileProcessedEvent(FixerFileProcessedEvent::STATUS_EXCEPTION)
);
$this->errorsManager->report(new Error(Error::TYPE_EXCEPTION, $name, $e));
}
private function dispatchEvent(string $name, Event $event): void
{
if (null === $this->eventDispatcher) {
return;
}
$this->eventDispatcher->dispatch($event, $name);
}
}