Last active
December 10, 2023 08:40
-
-
Save staabm/ec764d9476bbefe55fcda7744951ef62 to your computer and use it in GitHub Desktop.
Revisions
-
staabm renamed this gist
Dec 10, 2023 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
staabm created this gist
Dec 10, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,223 @@ <?php declare(strict_types=1); /* * This file is part of the overtrue/phplint package * * (c) overtrue * * This source file is subject to the MIT license that is bundled * with this source code in the file LICENSE. */ namespace Overtrue\PHPLint; use Fiber; use LogicException; use Overtrue\PHPLint\Configuration\OptionDefinition; use Overtrue\PHPLint\Configuration\Resolver; use Overtrue\PHPLint\Event\AfterCheckingEvent; use Overtrue\PHPLint\Event\AfterLintFileEvent; use Overtrue\PHPLint\Event\BeforeCheckingEvent; use Overtrue\PHPLint\Event\BeforeLintFileEvent; use Overtrue\PHPLint\Output\LinterOutput; use Overtrue\PHPLint\Process\LintProcess; use Symfony\Component\Cache\Adapter\FilesystemAdapter; use Symfony\Component\Cache\Adapter\NullAdapter; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\SplFileInfo; use Throwable; use function array_push; use function array_slice; use function count; use function md5_file; use function microtime; use function phpversion; use function version_compare; /** * @author Overtrue * @author Laurent Laville (code-rewrites since v9.0) */ final class Linter { private Resolver $configResolver; private EventDispatcherInterface $dispatcher; private Cache $cache; private array $results; private int $processLimit; private string $memoryLimit; private bool $warning; private array $options; private string $appLongVersion; public function __construct(Resolver $configResolver, EventDispatcherInterface $dispatcher, string $appVersion = '9.1.x-dev') { $this->configResolver = $configResolver; $this->dispatcher = $dispatcher; $this->appLongVersion = $appVersion; $this->options = $configResolver->getOptions(); $this->processLimit = $configResolver->getOption(OptionDefinition::JOBS); $this->memoryLimit = (string) $configResolver->getOption(OptionDefinition::OPTION_MEMORY_LIMIT); $this->warning = $configResolver->getOption(OptionDefinition::WARNING); if ($configResolver->getOption(OptionDefinition::NO_CACHE)) { $adapter = new NullAdapter(); } else { $adapter = new FilesystemAdapter('paths', 0, $configResolver->getOption(OptionDefinition::CACHE)); } $this->cache = new Cache($adapter); $this->results = [ 'errors' => [], 'warnings' => [], 'hits' => [], 'misses' => [], ]; } /** * @throws Throwable */ public function lintFiles(Finder $finder, ?float $startTime = null): LinterOutput { if (null === $startTime) { $startTime = microtime(true); } try { $fileCount = count($finder); } catch (LogicException) { $fileCount = 0; } $this->dispatcher->dispatch( new BeforeCheckingEvent( $this, ['fileCount' => $fileCount, 'appVersion' => $this->appLongVersion, 'options' => $this->options] ) ); $processCount = 0; if ($fileCount > 0) { $iterator = $finder->getIterator(); while ($iterator->valid()) { $fileInfo = $iterator->current(); $this->dispatcher->dispatch(new BeforeLintFileEvent($this, ['file' => $fileInfo])); $filename = $fileInfo->getRealPath(); if ($this->cache->isHit($filename)) { $this->results['hits'][] = $fileInfo; } else { $this->results['misses'][] = $fileInfo; } $iterator->next(); } if (version_compare(phpversion(), '8.3', 'ge')) { $chunkSize = 10; } else { $chunkSize = 1; } $chunks = array_chunk($this->results['misses'], $chunkSize); $processRunning = []; $pid = 0; foreach($chunks as $chunk) { $lintProcess = $this->createLintProcess($chunk); $lintProcess->start(); ++$pid; $processRunning[$pid] = [ 'process' => $lintProcess, 'files' => $chunk, ]; while (count($processRunning) >= $this->processLimit) { foreach ($processRunning as $pid => $item) { /** @var LintProcess $lintProcess */ $lintProcess = $item['process']; if ($lintProcess->isRunning()) { continue; } unset($processRunning[$pid]); // checks status of all files linked at end of the php lint process foreach ($item['files'] as $fileInfo) { $status = $this->processFile($fileInfo, $lintProcess); $this->dispatcher->dispatch( new AfterLintFileEvent($this, ['file' => $fileInfo, 'status' => $status]) ); } } } } $results = $this->results; } else { $results = []; } $finalResults = new LinterOutput($results, $finder); $finalResults->setContext($this->configResolver, $startTime, $processCount); $this->dispatcher->dispatch(new AfterCheckingEvent($this, ['results' => $finalResults])); return $finalResults; } private function processFile(SplFileInfo $fileInfo, LintProcess $lintProcess): string { $filename = $fileInfo->getRealPath(); $item = $lintProcess->getItem($fileInfo); if ($item->hasSyntaxError()) { $status = 'error'; } elseif ($this->warning && $item->hasSyntaxWarning()) { $status = 'warning'; } else { $status = 'ok'; $item = $this->cache->getItem($filename); $item->set(md5_file($filename)); $this->cache->saveItem($item); } if ($status !== 'ok') { $this->results[$status . 's'][$filename] = [ 'absolute_file' => $filename, 'relative_file' => $item->getFileInfo()->getRelativePathname(), 'error' => $item->getMessage(), 'line' => $item->getLine(), ]; } return $status; } private function createLintProcess(array $files): LintProcess { $command = [ PHP_SAPI == 'cli' ? PHP_BINARY : PHP_BINDIR . '/php', '-d error_reporting=E_ALL', '-d display_errors=On', ]; if (!empty($this->memoryLimit)) { $command[] = '-d memory_limit=' . $this->memoryLimit; } $command[] = '-l'; array_push($command, ...$files); return (new LintProcess($command))->setFiles($files); } }