Missing dependancies
This commit is contained in:
+84
@@ -0,0 +1,84 @@
|
||||
<?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\Linter\LinterInterface;
|
||||
use PhpCsFixer\Linter\LintingResultInterface;
|
||||
|
||||
/**
|
||||
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @extends \CachingIterator<mixed, \SplFileInfo, \Iterator<mixed, \SplFileInfo>>
|
||||
*/
|
||||
final class FileCachingLintingIterator extends \CachingIterator
|
||||
{
|
||||
private LinterInterface $linter;
|
||||
|
||||
/**
|
||||
* @var LintingResultInterface
|
||||
*/
|
||||
private $currentResult;
|
||||
|
||||
/**
|
||||
* @var LintingResultInterface
|
||||
*/
|
||||
private $nextResult;
|
||||
|
||||
/**
|
||||
* @param \Iterator<mixed, \SplFileInfo> $iterator
|
||||
*/
|
||||
public function __construct(\Iterator $iterator, LinterInterface $linter)
|
||||
{
|
||||
parent::__construct($iterator);
|
||||
|
||||
$this->linter = $linter;
|
||||
}
|
||||
|
||||
public function currentLintingResult(): LintingResultInterface
|
||||
{
|
||||
return $this->currentResult;
|
||||
}
|
||||
|
||||
public function next(): void
|
||||
{
|
||||
parent::next();
|
||||
|
||||
$this->currentResult = $this->nextResult;
|
||||
|
||||
if ($this->hasNext()) {
|
||||
$this->nextResult = $this->handleItem($this->getInnerIterator()->current());
|
||||
}
|
||||
}
|
||||
|
||||
public function rewind(): void
|
||||
{
|
||||
parent::rewind();
|
||||
|
||||
if ($this->valid()) {
|
||||
$this->currentResult = $this->handleItem($this->current());
|
||||
}
|
||||
|
||||
if ($this->hasNext()) {
|
||||
$this->nextResult = $this->handleItem($this->getInnerIterator()->current());
|
||||
}
|
||||
}
|
||||
|
||||
private function handleItem(\SplFileInfo $file): LintingResultInterface
|
||||
{
|
||||
return $this->linter->lintFile($file->getRealPath());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?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\Cache\CacheManagerInterface;
|
||||
use PhpCsFixer\FileReader;
|
||||
use PhpCsFixer\FixerFileProcessedEvent;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @extends \FilterIterator<mixed, \SplFileInfo, \Iterator<mixed, \SplFileInfo>>
|
||||
*/
|
||||
final class FileFilterIterator extends \FilterIterator
|
||||
{
|
||||
private ?EventDispatcherInterface $eventDispatcher;
|
||||
|
||||
private CacheManagerInterface $cacheManager;
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
private array $visitedElements = [];
|
||||
|
||||
/**
|
||||
* @param \Traversable<\SplFileInfo> $iterator
|
||||
*/
|
||||
public function __construct(
|
||||
\Traversable $iterator,
|
||||
?EventDispatcherInterface $eventDispatcher,
|
||||
CacheManagerInterface $cacheManager
|
||||
) {
|
||||
if (!$iterator instanceof \Iterator) {
|
||||
$iterator = new \IteratorIterator($iterator);
|
||||
}
|
||||
|
||||
parent::__construct($iterator);
|
||||
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->cacheManager = $cacheManager;
|
||||
}
|
||||
|
||||
public function accept(): bool
|
||||
{
|
||||
$file = $this->current();
|
||||
if (!$file instanceof \SplFileInfo) {
|
||||
throw new \RuntimeException(
|
||||
sprintf(
|
||||
'Expected instance of "\SplFileInfo", got "%s".',
|
||||
get_debug_type($file)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$path = $file->isLink() ? $file->getPathname() : $file->getRealPath();
|
||||
|
||||
if (isset($this->visitedElements[$path])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->visitedElements[$path] = true;
|
||||
|
||||
if (!$file->isFile() || $file->isLink()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$content = FileReader::createSingleton()->read($path);
|
||||
|
||||
// mark as skipped:
|
||||
if (
|
||||
// empty file
|
||||
'' === $content
|
||||
// file that does not need fixing due to cache
|
||||
|| !$this->cacheManager->needFixing($file->getPathname(), $content)
|
||||
) {
|
||||
$this->dispatchEvent(
|
||||
FixerFileProcessedEvent::NAME,
|
||||
new FixerFileProcessedEvent(FixerFileProcessedEvent::STATUS_SKIPPED)
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function dispatchEvent(string $name, Event $event): void
|
||||
{
|
||||
if (null === $this->eventDispatcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->eventDispatcher->dispatch($event, $name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?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\Linter\LinterInterface;
|
||||
use PhpCsFixer\Linter\LintingResultInterface;
|
||||
|
||||
/**
|
||||
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @extends \IteratorIterator<mixed, \SplFileInfo, \Traversable<\SplFileInfo>>
|
||||
*/
|
||||
final class FileLintingIterator extends \IteratorIterator
|
||||
{
|
||||
/**
|
||||
* @var LintingResultInterface
|
||||
*/
|
||||
private $currentResult;
|
||||
|
||||
private LinterInterface $linter;
|
||||
|
||||
/**
|
||||
* @param \Iterator<mixed, \SplFileInfo> $iterator
|
||||
*/
|
||||
public function __construct(\Iterator $iterator, LinterInterface $linter)
|
||||
{
|
||||
parent::__construct($iterator);
|
||||
|
||||
$this->linter = $linter;
|
||||
}
|
||||
|
||||
public function currentLintingResult(): ?LintingResultInterface
|
||||
{
|
||||
return $this->currentResult;
|
||||
}
|
||||
|
||||
public function next(): void
|
||||
{
|
||||
parent::next();
|
||||
|
||||
$this->currentResult = $this->valid() ? $this->handleItem($this->current()) : null;
|
||||
}
|
||||
|
||||
public function rewind(): void
|
||||
{
|
||||
parent::rewind();
|
||||
|
||||
$this->currentResult = $this->valid() ? $this->handleItem($this->current()) : null;
|
||||
}
|
||||
|
||||
private function handleItem(\SplFileInfo $file): LintingResultInterface
|
||||
{
|
||||
return $this->linter->lintFile($file->getRealPath());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
<?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
|
||||
{
|
||||
private DifferInterface $differ;
|
||||
|
||||
private ?DirectoryInterface $directory;
|
||||
|
||||
private ?EventDispatcherInterface $eventDispatcher;
|
||||
|
||||
private ErrorsManager $errorsManager;
|
||||
|
||||
private CacheManagerInterface $cacheManager;
|
||||
|
||||
private bool $isDryRun;
|
||||
|
||||
private LinterInterface $linter;
|
||||
|
||||
/**
|
||||
* @var \Traversable<\SplFileInfo>
|
||||
*/
|
||||
private $finder;
|
||||
|
||||
/**
|
||||
* @var list<FixerInterface>
|
||||
*/
|
||||
private array $fixers;
|
||||
|
||||
private bool $stopOnViolation;
|
||||
|
||||
/**
|
||||
* @param \Traversable<\SplFileInfo> $finder
|
||||
* @param list<FixerInterface> $fixers
|
||||
*/
|
||||
public function __construct(
|
||||
\Traversable $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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array{appliedFixers: list<string>, diff: string}>
|
||||
*/
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|array{appliedFixers: list<string>, diff: string}
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user