From 552dfdf16d2a5b0716566bfd2e9c6474e038a837 Mon Sep 17 00:00:00 2001 From: Iman Ghafoori Date: Sun, 19 May 2024 16:44:38 +0330 Subject: [PATCH] refactor --- composer.json | 2 +- src/Analyzers/Fixer.php | 58 ++++----- src/Checks/CheckEarlyReturn.php | 6 +- src/Checks/CheckIsQuery.php | 6 +- src/Checks/CheckRouteCalls.php | 6 +- src/Checks/CheckRubySyntax.php | 7 +- src/Checks/CheckStringy.php | 97 -------------- src/Checks/PSR12/CurlyBraces.php | 12 +- src/Commands/ClassifyStrings.php | 33 ----- src/Commands/PatternApply.php | 1 - src/Commands/PrettyPrintRoutes.php | 4 +- .../ConsolePrinterInstaller.php | 3 - .../ActionComments/ActionsComments.php | 42 +++--- src/Features/ActionComments/CommentMaker.php | 16 ++- .../CheckClassyStrings/CheckStringy.php | 104 +++++++++++++++ .../CheckClassyStrings/CheckStringyMsg.php | 34 +++++ .../CheckClassyStrings/ClassifyStrings.php | 46 +++++++ src/Features/CheckDD/CheckDD.php | 6 +- src/Features/CheckDD/CheckDDCommand.php | 11 +- .../CheckDeadControllers.php | 15 ++- .../RoutelessControllerActions.php} | 43 ++---- .../CheckFacadeDocblocks/FacadeDocblocks.php | 10 +- .../CheckGenericDocBlocksCommand.php | 15 ++- .../GenericDocblocks.php | 47 ++++--- .../CheckImports/CheckImportsCommand.php | 9 +- .../Checks/CheckClassAtMethod.php | 6 +- .../Checks/CheckClassReferencesAreValid.php | 6 +- .../CheckImports/Reporters/AutoloadFiles.php | 5 +- .../Reporters/CheckImportReporter.php | 4 +- .../CheckImports/Reporters/Psr4Report.php | 36 ++++-- .../CheckImports/Reporters/Reporting.php | 13 +- src/Features/CheckView/BladeFile.php | 10 -- src/Features/CheckView/Check/CheckView.php | 26 ++-- .../Check/CheckViewFilesExistence.php | 13 +- src/Features/CheckView/CheckViewsCommand.php | 14 +- src/Features/CheckView/ViewsInstaller.php | 22 ---- .../ExtractBladePartial.php | 6 +- .../FacadeAlias/CheckAliasesCommand.php | 5 +- .../FacadeAlias/FacadeAliasesCheck.php | 6 +- src/Features/ListModels/SubclassFinder.php | 2 +- src/Features/Psr4/CheckPsr4ArtisanCommand.php | 2 +- src/ForPsr4LoadedClasses.php | 14 +- src/Foundations/Path.php | 66 ++++++++++ src/Foundations/PhpFileDescriptor.php | 122 ++++++++++++++++++ src/Iterators/BaseIterator.php | 16 +-- src/Iterators/BladeFiles/CheckBladePaths.php | 16 ++- src/Iterators/Check.php | 4 +- src/Iterators/ChecksOnPsr4Classes.php | 20 +-- src/LaravelMicroscopeServiceProvider.php | 22 +++- src/SearchReplace/PatternRefactorings.php | 6 +- src/SpyClasses/RoutePaths.php | 3 +- .../CheckClassReferencesAreValidTest.php | 12 +- .../{wongImport.stub => wrongImport.stub} | 0 53 files changed, 706 insertions(+), 404 deletions(-) delete mode 100644 src/Checks/CheckStringy.php delete mode 100644 src/Commands/ClassifyStrings.php create mode 100644 src/Features/CheckClassyStrings/CheckStringy.php create mode 100644 src/Features/CheckClassyStrings/CheckStringyMsg.php create mode 100644 src/Features/CheckClassyStrings/ClassifyStrings.php rename src/{Commands => Features/CheckDeadControllers}/CheckDeadControllers.php (58%) rename src/{Checks/RoutelessActions.php => Features/CheckDeadControllers/RoutelessControllerActions.php} (64%) delete mode 100644 src/Features/CheckView/BladeFile.php delete mode 100644 src/Features/CheckView/ViewsInstaller.php create mode 100644 src/Foundations/Path.php create mode 100644 src/Foundations/PhpFileDescriptor.php rename tests/CheckImports/{wongImport.stub => wrongImport.stub} (100%) diff --git a/composer.json b/composer.json index d3c74a1e..5a53b3eb 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": "^7.2|8.0.*|8.1.*|8.2.*|8.3.*", - "imanghafoori/composer-json": "^1.0.14", + "imanghafoori/composer-json": "^1.0.15", "composer/class-map-generator": "^1.0.0", "imanghafoori/php-abstract-filesystem": "^0.1.5", "imanghafoori/php-search-replace": "^1.1.12", diff --git a/src/Analyzers/Fixer.php b/src/Analyzers/Fixer.php index 2bcb1227..0eb77774 100644 --- a/src/Analyzers/Fixer.php +++ b/src/Analyzers/Fixer.php @@ -4,9 +4,8 @@ use ImanGhafoori\ComposerJson\NamespaceCalculator; use Imanghafoori\Filesystem\FileManipulator; -use Imanghafoori\Filesystem\Filesystem; use Imanghafoori\LaravelMicroscope\ClassListProvider; -use Imanghafoori\SearchReplace\Searcher; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\TokenAnalyzer\ParseUseStatement; class Fixer @@ -62,10 +61,11 @@ public static function fixReference($absPath, $inlinedClassRef, $lineNum) } $fullClassPath = $correct[0]; - $contextClassNamespace = ComposerJson::make()->getNamespacedClassFromPath($absPath); + $file = PhpFileDescriptor::make($absPath); + $contextClassNamespace = $file->getNamespace(); if (NamespaceCalculator::haveSameNamespace($contextClassNamespace, $fullClassPath)) { - return [self::doReplacement($absPath, $inlinedClassRef, class_basename($fullClassPath), $lineNum), $correct]; + return [self::doReplacement($file, $inlinedClassRef, class_basename($fullClassPath), $lineNum), $correct]; } $uses = ParseUseStatement::parseUseStatements(token_get_all(file_get_contents($absPath)))[1]; @@ -80,11 +80,11 @@ public static function fixReference($absPath, $inlinedClassRef, $lineNum) $fullClassPath = $classBaseName; } - return [self::doReplacement($absPath, $inlinedClassRef, $fullClassPath, $lineNum), $correct]; + return [self::doReplacement($file, $inlinedClassRef, $fullClassPath, $lineNum), $correct]; } // Replace in the class reference - self::doReplacement($absPath, $inlinedClassRef, $classBaseName, $lineNum); + self::doReplacement($file, $inlinedClassRef, $classBaseName, $lineNum); // Insert a new import at the top $lineNum = array_values($uses)[0][1]; // first use statement @@ -105,49 +105,35 @@ public static function fixImport($absPath, $import, $lineNum, $isAliased) { $correct = self::guessCorrect(class_basename($import)); - if (\count($correct) !== 1) { + if (count($correct) !== 1) { return [false, $correct]; } - $tokens = token_get_all(file_get_contents($absPath)); - $hostNamespacedClass = ComposerJson::make()->getNamespacedClassFromPath($absPath); + $file = PhpFileDescriptor::make($absPath); + $hostNamespacedClass = $file->getNamespace(); // We just remove the wrong import if it is not needed. if (! $isAliased && NamespaceCalculator::haveSameNamespace($hostNamespacedClass, $correct[0])) { - return [self::replaceSave("use $import;", '', $tokens, $absPath), [' Deleted!']]; - } - - return [self::replaceSave("use $import;", 'use '.$correct[0].';'.PHP_EOL, $tokens, $absPath), $correct]; - } + $lines = $file->searchReplacePatterns("use $import;", ''); - private static function replaceSave($old, $new, array $tokens, $absPath) - { - [$newVersion, $lines] = Searcher::searchReplace([ - 'fix' => [ - 'search' => $old, - 'replace' => $new, - ], - ], $tokens); + return [$lines, [' Deleted!']]; + } - Filesystem::$fileSystem::file_put_contents($absPath, $newVersion); + $lines = $file->searchReplacePatterns("use $import;", 'use '.$correct[0].';'.PHP_EOL); - return $lines; + return [$lines, $correct]; } - private static function doReplacement($absPath, $wrongRef, $correctRef, $lineNum) + private static function doReplacement(PhpFileDescriptor $file, $wrongRef, $correctRef, $lineNum) { - if (version_compare(PHP_VERSION, '8.0.0') === 1) { - return FileManipulator::replaceFirst($absPath, $wrongRef, $correctRef, $lineNum); + if (self::phpVersionIsMoreOrEqualTo('8.0.0')) { + return $file->replaceAtLine($wrongRef, $correctRef, $lineNum); } - $tokens = token_get_all(file_get_contents($absPath)); - [$newVersion, $lines] = Searcher::searchReplace([ - 'fix' => [ - 'search' => $wrongRef, - 'replace' => $correctRef, - ], - ], $tokens); - Filesystem::$fileSystem::file_put_contents($absPath, $newVersion); + return (bool) $file->searchReplacePatterns($wrongRef, $correctRef); + } - return (bool) $lines; + private static function phpVersionIsMoreOrEqualTo($version): bool + { + return version_compare(PHP_VERSION, $version) !== -1; } } diff --git a/src/Checks/CheckEarlyReturn.php b/src/Checks/CheckEarlyReturn.php index 3dbb8abe..d2108bfb 100644 --- a/src/Checks/CheckEarlyReturn.php +++ b/src/Checks/CheckEarlyReturn.php @@ -6,12 +6,16 @@ use Imanghafoori\LaravelMicroscope\Check; use Imanghafoori\LaravelMicroscope\ErrorReporters\ErrorPrinter; use Imanghafoori\LaravelMicroscope\FileReaders\FilePath; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\TokenAnalyzer\Refactor; class CheckEarlyReturn implements Check { - public static function check(array $tokens, $absFilePath, $params) + public static function check(PhpFileDescriptor $file, $params) { + $tokens = $file->getTokens(); + $absFilePath = $file->getAbsolutePath(); + $nofix = $params['nofix']; $nofixCallback = $params['nofixCallback']; $fixCallback = $params['fixCallback']; diff --git a/src/Checks/CheckIsQuery.php b/src/Checks/CheckIsQuery.php index 04b8d94a..460dccee 100644 --- a/src/Checks/CheckIsQuery.php +++ b/src/Checks/CheckIsQuery.php @@ -6,12 +6,16 @@ use Illuminate\Support\Facades\DB; use Imanghafoori\LaravelMicroscope\Check; use Imanghafoori\LaravelMicroscope\ErrorReporters\ErrorPrinter; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\TokenAnalyzer\ParseUseStatement; class CheckIsQuery implements Check { - public static function check($tokens, $absPath) + public static function check(PhpFileDescriptor $file) { + $tokens = $file->getTokens(); + $absPath = $file->getAbsolutePath(); + [$classes] = ParseUseStatement::findClassReferences($tokens); foreach ($classes as $class) { diff --git a/src/Checks/CheckRouteCalls.php b/src/Checks/CheckRouteCalls.php index 8fa82378..67c35956 100644 --- a/src/Checks/CheckRouteCalls.php +++ b/src/Checks/CheckRouteCalls.php @@ -4,6 +4,7 @@ use Imanghafoori\LaravelMicroscope\Check; use Imanghafoori\LaravelMicroscope\ErrorReporters\ErrorPrinter; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\TokenAnalyzer\FunctionCall; class CheckRouteCalls implements Check @@ -12,11 +13,14 @@ class CheckRouteCalls implements Check public static $skippedRouteCallsNum = 0; - public static function check($tokens, $absFilePath) + public static function check(PhpFileDescriptor $file) { // we skip the very first tokens: 'getTokens(); + $absFilePath = $file->getAbsolutePath(); + $total = \count($tokens) - 3; while ($i < $total) { $index = FunctionCall::isGlobalCall('route', $tokens, $i); diff --git a/src/Checks/CheckRubySyntax.php b/src/Checks/CheckRubySyntax.php index 9188cc0c..3ded7546 100644 --- a/src/Checks/CheckRubySyntax.php +++ b/src/Checks/CheckRubySyntax.php @@ -6,17 +6,20 @@ use Imanghafoori\LaravelMicroscope\Check; use Imanghafoori\LaravelMicroscope\ErrorReporters\ErrorPrinter; use Imanghafoori\LaravelMicroscope\FileReaders\FilePath; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\TokenAnalyzer\Refactor; use Imanghafoori\TokenAnalyzer\SyntaxNormalizer; class CheckRubySyntax implements Check { - public static function check($tokens, $absFilePath) + public static function check(PhpFileDescriptor $file) { if (empty($tokens) || $tokens[0][0] !== T_OPEN_TAG) { return false; } + $absFilePath = $file->getAbsolutePath(); + try { $tokens = SyntaxNormalizer::normalizeSyntax($tokens, true); } catch (Exception $e) { @@ -41,7 +44,7 @@ private static function getConfirm($filePath) return ErrorPrinter::singleton()->printer->confirm('Replacing endif in: '.$filePath); } - private static function requestIssue(string $path) + private static function requestIssue($path) { dump('(O_o) Well, It seems we had some problem parsing the contents of: (o_O)'); dump('Submit an issue on github: https://github.com/imanghafoori1/microscope'); diff --git a/src/Checks/CheckStringy.php b/src/Checks/CheckStringy.php deleted file mode 100644 index 866b877a..00000000 --- a/src/Checks/CheckStringy.php +++ /dev/null @@ -1,97 +0,0 @@ -checkStringy($tokens, $absFilePath); - } - - public function checkStringy($tokens, $absFilePath) - { - $errorPrinter = resolve(ErrorPrinter::class); - foreach (ComposerJson::readPsr4() as $psr4) { - $namespaces = array_keys($psr4); - foreach ($tokens as $token) { - if ($token[0] !== T_CONSTANT_ENCAPSED_STRING) { - continue; - } - - $classPath = \trim($token[1], '\'\"'); - - if (! $this->isPossiblyClassyString($namespaces, $classPath)) { - continue; - } - - if (! \class_exists(\str_replace('\\\\', '\\', $classPath))) { - if (self::refersToDir($classPath)) { - continue; - } - $errorPrinter->wrongUsedClassError($absFilePath, $token[1], $token[2]); - continue; - } - - $errorPrinter->printLink($absFilePath, $token[2]); - $command = app('current.command'); - - if (! self::ask($errorPrinter->printer, $token, $absFilePath)) { - continue; - } - - $classPath = $this->getClassyPath($classPath); - $command->info('✔ Replaced with: '.$classPath.''); - - $contextClass = ComposerJson::make()->getNamespacedClassFromPath($absFilePath); - - if (NamespaceCalculator::haveSameNamespace($contextClass, $classPath)) { - $classPath = trim(class_basename($classPath), '\\'); - } - - FileManipulator::replaceFirst($absFilePath, $token[1], $classPath); - $width = (new Terminal)->getWidth() - 4; - $command->info(' '.str_repeat('_', $width).''); - } - } - } - - protected function getClassyPath($string) - { - ($string[0] !== '\\') && ($string = '\\'.$string); - $string .= '::class'; - - return str_replace('\\\\', '\\', $string); - } - - private function isPossiblyClassyString($namespaces, $classPath) - { - $chars = ['@', ' ', ',', ':', '/', '.', '-']; - - return Str::contains($classPath, $namespaces) && - ! in_array($classPath, $namespaces) && - ! Str::contains($classPath, $chars) && - ! Str::endsWith($classPath, '\\'); - } - - private static function ask($printer, $token, $absFilePath) - { - $printer->text($token[2].' |'.file($absFilePath)[$token[2] - 1]); - $text = 'Replace: '.$token[1].' with ::class version of it?'; - - return $printer->confirm($text, true); - } - - private static function refersToDir(string $classPath) - { - return is_dir(base_path(ComposerJson::make()->getRelativePathFromNamespace($classPath))); - } -} diff --git a/src/Checks/PSR12/CurlyBraces.php b/src/Checks/PSR12/CurlyBraces.php index 31f46934..41fccf5f 100644 --- a/src/Checks/PSR12/CurlyBraces.php +++ b/src/Checks/PSR12/CurlyBraces.php @@ -3,14 +3,18 @@ namespace Imanghafoori\LaravelMicroscope\Checks\PSR12; use Imanghafoori\LaravelMicroscope\Check; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\TokenAnalyzer\Refactor; class CurlyBraces implements Check { public static $command; - public static function check($tokens, $absFilePath) + public static function check(PhpFileDescriptor $file) { + $tokens = $file->getTokens(); + $absFilePath = $file->getAbsolutePath(); + self::addPublicKeyword($tokens, $absFilePath); } @@ -37,20 +41,20 @@ private static function addPublicKeyword($tokens, $absolutePath) } } - private static function openCurly($token, $level, $tokens, $i, $classFilePath) + private static function openCurly($token, $level, $tokens, $i, $absFilePath) { if ($token == '{' && ! in_array($tokens[$i - 1][0], [T_DOUBLE_COLON, T_OBJECT_OPERATOR])) { $sp = str_repeat(' ', $level); if ($tokens[$i + 1][0] === T_WHITESPACE) { if ($tokens[$i + 1][1] !== PHP_EOL.$sp && $tokens[$i + 1][1] !== "\n".$sp) { $tokens[$i + 1][1] = PHP_EOL.$sp; - Refactor::saveTokens($classFilePath, $tokens); + Refactor::saveTokens($absFilePath, $tokens); } else { // } } else { array_splice($tokens, $i + 1, 0, [[T_WHITESPACE, PHP_EOL.$sp]]); - Refactor::saveTokens($classFilePath, $tokens); + Refactor::saveTokens($absFilePath, $tokens); } } } diff --git a/src/Commands/ClassifyStrings.php b/src/Commands/ClassifyStrings.php deleted file mode 100644 index cd67ef2b..00000000 --- a/src/Commands/ClassifyStrings.php +++ /dev/null @@ -1,33 +0,0 @@ -info('Checking stringy classes...'); - app()->singleton('current.command', function () { - return $this; - }); - $errorPrinter->printer = $this->output; - - $fileName = ltrim($this->option('file'), '='); - $folder = ltrim($this->option('folder'), '='); - - ForPsr4LoadedClasses::checkNow([CheckStringy::class], [], $fileName, $folder); - - $this->getOutput()->writeln(' ✔ - Finished looking for stringy classes.'); - - return $errorPrinter->hasErrors() ? 1 : 0; - } -} diff --git a/src/Commands/PatternApply.php b/src/Commands/PatternApply.php index f1dacc3d..f53feecc 100644 --- a/src/Commands/PatternApply.php +++ b/src/Commands/PatternApply.php @@ -5,7 +5,6 @@ use Imanghafoori\LaravelMicroscope\ErrorReporters\ErrorPrinter; use Imanghafoori\LaravelMicroscope\Features\CheckImports\Reporters; use Imanghafoori\LaravelMicroscope\ForPsr4LoadedClasses; -use Imanghafoori\LaravelMicroscope\Iterators\BladeFiles; use Imanghafoori\LaravelMicroscope\Iterators\ClassMapIterator; use Imanghafoori\LaravelMicroscope\SearchReplace\PatternRefactorings; use Imanghafoori\SearchReplace\PatternParser; diff --git a/src/Commands/PrettyPrintRoutes.php b/src/Commands/PrettyPrintRoutes.php index e0f552a6..d5e694c9 100644 --- a/src/Commands/PrettyPrintRoutes.php +++ b/src/Commands/PrettyPrintRoutes.php @@ -45,7 +45,7 @@ private function writeIt($route, $filename) $action = $this->getAction($route->getActionName()); - if (\count($methods) == 1) { + if (count($methods) == 1) { $definition = PHP_EOL.$this->getMovableRoute($route, $methods, $action, $middlewares); file_put_contents($filename, $definition, FILE_APPEND); @@ -112,7 +112,7 @@ private function getAction($action) private function getMiddlewares($route) { $middlewares = $route->gatherMiddleware(); - $middlewares && $middlewares = "'".\implode("', '", $route->gatherMiddleware())."'"; + $middlewares && $middlewares = "'".implode("', '", $route->gatherMiddleware())."'"; return $middlewares ? '->middleware(['.$middlewares.'])' : ''; } diff --git a/src/ErrorReporters/ConsolePrinterInstaller.php b/src/ErrorReporters/ConsolePrinterInstaller.php index 58c53737..0c6b0cfc 100644 --- a/src/ErrorReporters/ConsolePrinterInstaller.php +++ b/src/ErrorReporters/ConsolePrinterInstaller.php @@ -5,7 +5,6 @@ use Illuminate\Support\Facades\Event; use Illuminate\Support\Str; use Imanghafoori\LaravelMicroscope\Features\CheckEnvCalls\EnvFound; -use Imanghafoori\LaravelMicroscope\Features\CheckView\ViewsInstaller; use Imanghafoori\LaravelMicroscope\Features\RouteOverride\Installer as RouteOverrideInstaller; class ConsolePrinterInstaller @@ -47,8 +46,6 @@ protected static function getKey($commandType) public static function boot() { - ViewsInstaller::boot(); - RouteOverrideInstaller::install(); EnvFound::listen(); diff --git a/src/Features/ActionComments/ActionsComments.php b/src/Features/ActionComments/ActionsComments.php index b460ede4..6acee33d 100644 --- a/src/Features/ActionComments/ActionsComments.php +++ b/src/Features/ActionComments/ActionsComments.php @@ -3,7 +3,8 @@ namespace Imanghafoori\LaravelMicroscope\Features\ActionComments; use Imanghafoori\LaravelMicroscope\Check; -use Imanghafoori\LaravelMicroscope\Checks\RoutelessActions; +use Imanghafoori\LaravelMicroscope\Features\CheckDeadControllers\RoutelessControllerActions; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\TokenAnalyzer\ClassMethods; use Imanghafoori\TokenAnalyzer\Refactor; @@ -13,39 +14,42 @@ class ActionsComments implements Check public static $controllers = []; - public static function check($tokens, $absFilePath, $params, $classFilePath, $psr4Path, $psr4Namespace) + public static function check(PhpFileDescriptor $file, $params, $psr4Path, $psr4Namespace) { - $fullNamespace = RoutelessActions::getFullNamespace($classFilePath, $psr4Path, $psr4Namespace); + $tokens = $file->getTokens(); + $absFilePath = $file->getAbsolutePath(); + + $fullNamespace = $file->getNamespace(); if (isset(static::$controllers[trim($fullNamespace, '\\')])) { - self::checkActions($tokens, $fullNamespace, $classFilePath); + self::checkActions($tokens, $fullNamespace, $absFilePath); } } - private static function checkActions($tokens, $fullNamespace, $path) + private static function checkActions($tokens, $fullNamespace, $absFilePath) { $methods = ClassMethods::read($tokens)['methods']; - $methods = RoutelessActions::getControllerActions($methods); - $routelessActions = []; + $methods = RoutelessControllerActions::getControllerActions($methods); $shouldSave = false; $allRoutes = app('router')->getRoutes()->getRoutes(); foreach ($methods as $method) { - $classAtMethod = RoutelessActions::classAtMethod($fullNamespace, $method['name'][1]); - $actions = self::getActions($allRoutes, $classAtMethod); + $classAtMethod = RoutelessControllerActions::classAtMethod($fullNamespace, $method['name'][1]); + $routes = self::getActionRoutes($allRoutes, $classAtMethod); - if (! $actions) { + if (! $routes) { continue; } /** * @var $route \Illuminate\Routing\Route */ - $msg = CommentMaker::getComment($actions); + $msg = CommentMaker::getComment($routes); $commentIndex = $method['startBodyIndex'][0] + 1; if (T_DOC_COMMENT !== $tokens[$commentIndex + 1][0]) { + // in case there is no doc-block $shouldSave = true; $tokens[$commentIndex][1] = "\n ".$msg.$tokens[$commentIndex][1]; } elseif ($msg !== $tokens[$commentIndex + 1][1]) { @@ -53,17 +57,12 @@ private static function checkActions($tokens, $fullNamespace, $path) $shouldSave = true; $tokens[$commentIndex + 1][1] = $msg; } - - $line = $method['name'][2]; - $routelessActions[] = [$line, $classAtMethod]; } $question = 'Add route definition into the: '.$fullNamespace.''; if ($shouldSave && self::$command->confirm($question, true)) { - Refactor::saveTokens($path->getRealpath(), $tokens); + Refactor::saveTokens($absFilePath, $tokens); } - - return $routelessActions; } public static function getCallsiteInfo($methods, $route) @@ -77,14 +76,13 @@ public static function getCallsiteInfo($methods, $route) return [$file, $line]; } - private static function getActions($allRoutes, $classAtMethod) + private static function getActionRoutes($allRoutes, $method) { - $actions = []; + $routes = []; foreach ($allRoutes as $route) { - $action = $route->getAction('uses'); - $classAtMethod === $action && $actions[] = $route; + $method === $route->getAction('uses') && ($routes[] = $route); } - return $actions; + return $routes; } } diff --git a/src/Features/ActionComments/CommentMaker.php b/src/Features/ActionComments/CommentMaker.php index edfa6a1c..87080b32 100644 --- a/src/Features/ActionComments/CommentMaker.php +++ b/src/Features/ActionComments/CommentMaker.php @@ -4,14 +4,15 @@ class CommentMaker { - public static function getComment(array $actions) + private const separator = "\n *"; + + public static function getComment(array $routes) { $msg = '/**'; - $separator = "\n *"; - foreach ($actions as $i => $action) { - $i === count($actions) - 1 && $separator = ''; - $msg .= "\n ".rtrim(self::getMsg($action)).$separator; + foreach ($routes as $i => $route) { + $sep = self::getSeparator(isset($routes[$i + 1])); + $msg .= "\n ".rtrim(self::getMsg($route)).$sep; } $msg .= "\n */"; @@ -78,4 +79,9 @@ private static function getUrl($route) return $url; } + + private static function getSeparator($isFinal) + { + return $isFinal ? '' : self::separator; + } } diff --git a/src/Features/CheckClassyStrings/CheckStringy.php b/src/Features/CheckClassyStrings/CheckStringy.php new file mode 100644 index 00000000..19cc4fbf --- /dev/null +++ b/src/Features/CheckClassyStrings/CheckStringy.php @@ -0,0 +1,104 @@ +getTokens() as $token) { + if ($token[0] !== T_CONSTANT_ENCAPSED_STRING) { + continue; + } + + $classPath = trim($token[1], '\'\"'); + + if (! self::isPossiblyClassyString($namespaces, $classPath)) { + continue; + } + + $lineNum = $token[2]; + $absFilePath = $file->getAbsolutePath(); + if (! class_exists(str_replace('\\\\', '\\', $classPath))) { + if (self::refersToDir($classPath)) { + continue; + } + $errorPrinter->wrongUsedClassError($absFilePath, $token[1], $lineNum); + continue; + } + + $errorPrinter->printLink($absFilePath, $lineNum); + $command = app('current.command'); + + if (! self::ask($errorPrinter->printer, $lineNum, $token[1], $file)) { + continue; + } + $replacement = self::getClassPath($classPath, $file); + self::performReplacementProcess($token[1], $replacement, $command, $file); + } + } + } + + private static function isPossiblyClassyString($namespaces, $classPath) + { + $chars = ['@', ' ', ',', ':', '/', '.', '-']; + + return Str::contains($classPath, $namespaces) && + ! in_array($classPath, $namespaces) && + ! Str::contains($classPath, $chars) && + ! Str::endsWith($classPath, '\\'); + } + + private static function ask($printer, $lineNumber, $classPath, PhpFileDescriptor $file) + { + $printer->text(CheckStringyMsg::getLineContents($lineNumber, $file)); + + return $printer->confirm(CheckStringyMsg::question($classPath), true); + } + + private static function refersToDir($classPath) + { + return is_dir(base_path(ComposerJson::make()->getRelativePathFromNamespace($classPath))); + } + + private static function performReplacementProcess($classyString, $classPath, $command, PhpFileDescriptor $file) + { + $command->info(CheckStringyMsg::successfulReplacementMsg($classPath)); + + // todo: should replace tokens not the file contents. + FileManipulator::replaceFirst($file->getAbsolutePath(), $classyString, $classPath); + + $command->info(CheckStringyMsg::lineSeparator()); + } + + public static function getClassPath(string $classPath, PhpFileDescriptor $file) + { + // Put back-slash at the beginning. + ($classPath[0] !== '\\') && ($classPath = '\\'.$classPath); + + $classPath .= '::class'; + + // Remove possible double back-slash: + $classPath = str_replace('\\\\', '\\', $classPath); + + // Remove unnecessary qualifier if possible. + $contextClass = $file->getNamespace(); + + if (NamespaceCalculator::haveSameNamespace($contextClass, $classPath)) { + $classPath = trim(class_basename($classPath), '\\'); + } + + return $classPath; + } +} diff --git a/src/Features/CheckClassyStrings/CheckStringyMsg.php b/src/Features/CheckClassyStrings/CheckStringyMsg.php new file mode 100644 index 00000000..53dd944d --- /dev/null +++ b/src/Features/CheckClassyStrings/CheckStringyMsg.php @@ -0,0 +1,34 @@ +✔ Replaced with: '.$classPath.''; + } + + public static function lineSeparator(): string + { + return ' '.str_repeat('_', (new Terminal)->getWidth() - 4).''; + } + + public static function question($class) + { + return 'Replace: '.$class.' with ::class version of it?'; + } + + public static function getLineContents($lineNumber, PhpFileDescriptor $file) + { + return $lineNumber.' |'.$file->getLine($lineNumber); + } + + public static function finished() + { + return ' ✔ - Finished looking for stringy classes.'; + } +} diff --git a/src/Features/CheckClassyStrings/ClassifyStrings.php b/src/Features/CheckClassyStrings/ClassifyStrings.php new file mode 100644 index 00000000..7ffa3d5d --- /dev/null +++ b/src/Features/CheckClassyStrings/ClassifyStrings.php @@ -0,0 +1,46 @@ +info('Checking stringy classes...'); + app()->singleton('current.command', function () { + return $this; + }); + $errorPrinter->printer = $this->output; + + $fileName = ltrim($this->option('file'), '='); + $folder = ltrim($this->option('folder'), '='); + + [$psr4Stats, $classMapStats] = self::classifyString($fileName, $folder); + + $this->getOutput()->writeln(implode(PHP_EOL, [ + Psr4Report::printAutoload($psr4Stats, $classMapStats), + ])); + + $this->getOutput()->writeln(CheckStringyMsg::finished()); + + return $errorPrinter->hasErrors() ? 1 : 0; + } + + public static function classifyString(string $fileName, string $folder): array + { + $psr4Stats = ForPsr4LoadedClasses::check([CheckStringy::class], [], $fileName, $folder); + $classMapStats = ClassMapIterator::iterate(base_path(), [CheckStringy::class], [], $fileName, $folder); + + return [$psr4Stats, $classMapStats]; + } +} diff --git a/src/Features/CheckDD/CheckDD.php b/src/Features/CheckDD/CheckDD.php index 4fc33fa6..437eac7f 100644 --- a/src/Features/CheckDD/CheckDD.php +++ b/src/Features/CheckDD/CheckDD.php @@ -3,12 +3,16 @@ namespace Imanghafoori\LaravelMicroscope\Features\CheckDD; use Imanghafoori\LaravelMicroscope\Check; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\TokenAnalyzer\FunctionCall; class CheckDD implements Check { - public static function check($tokens, $absPath, $params) + public static function check(PhpFileDescriptor $file, $params) { + $tokens = $file->getTokens(); + $absPath = $file->getAbsolutePath(); + $callback = $params[0]; foreach ($tokens as $i => $token) { if ( diff --git a/src/Features/CheckDD/CheckDDCommand.php b/src/Features/CheckDD/CheckDDCommand.php index f9e1a359..6dcc098d 100644 --- a/src/Features/CheckDD/CheckDDCommand.php +++ b/src/Features/CheckDD/CheckDDCommand.php @@ -7,6 +7,7 @@ use Imanghafoori\LaravelMicroscope\Features\CheckImports\Reporters\LaravelFoldersReport; use Imanghafoori\LaravelMicroscope\Features\CheckImports\Reporters\Psr4Report; use Imanghafoori\LaravelMicroscope\ForPsr4LoadedClasses; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\LaravelMicroscope\Iterators\ClassMapIterator; use Imanghafoori\LaravelMicroscope\Iterators\FileIterators; use Imanghafoori\LaravelMicroscope\LaravelPaths\LaravelPaths; @@ -27,17 +28,17 @@ public function handle() $fileName = ltrim($this->option('file'), '='); $folder = ltrim($this->option('folder'), '='); - $callback = function ($tokens, $absPath, $token) { + $paramProvider = function (PhpFileDescriptor $file, $token) { ErrorPrinter::singleton()->simplePendError( - $token[1], $absPath, $token[2], 'ddFound', 'Debug function found: ' + $token[1], $file->getTokens(), $token[2], 'ddFound', 'Debug function found: ' ); }; - $psr4Stats = ForPsr4LoadedClasses::check([CheckDD::class], [$callback], $fileName, $folder); - $classMapStats = ClassMapIterator::iterate(base_path(), [CheckDD::class], [$callback], $fileName, $folder); + $psr4Stats = ForPsr4LoadedClasses::check([CheckDD::class], [$paramProvider], $fileName, $folder); + $classMapStats = ClassMapIterator::iterate(base_path(), [CheckDD::class], [$paramProvider], $fileName, $folder); $foldersStats = FileIterators::checkFolders( - [CheckDD::class], $this->getLaravelFolders(), [$callback], $fileName, $folder + [CheckDD::class], $this->getLaravelFolders(), [$paramProvider], $fileName, $folder ); $this->getOutput()->writeln(implode(PHP_EOL, [ diff --git a/src/Commands/CheckDeadControllers.php b/src/Features/CheckDeadControllers/CheckDeadControllers.php similarity index 58% rename from src/Commands/CheckDeadControllers.php rename to src/Features/CheckDeadControllers/CheckDeadControllers.php index 07328a1a..0ad9d2c2 100644 --- a/src/Commands/CheckDeadControllers.php +++ b/src/Features/CheckDeadControllers/CheckDeadControllers.php @@ -1,10 +1,10 @@ printer = $this->output; - ForPsr4LoadedClasses::checkNow([RoutelessActions::class]); + $fileName = ltrim($this->option('file'), '='); + $folder = ltrim($this->option('folder'), '='); + + $psr4Stats = ForPsr4LoadedClasses::check([RoutelessControllerActions::class], [], $fileName, $folder); + + $this->getOutput()->writeln(implode(PHP_EOL, [ + Psr4Report::printAutoload($psr4Stats, []), + ])); $this->finishCommand($errorPrinter); $errorPrinter->printTime(); diff --git a/src/Checks/RoutelessActions.php b/src/Features/CheckDeadControllers/RoutelessControllerActions.php similarity index 64% rename from src/Checks/RoutelessActions.php rename to src/Features/CheckDeadControllers/RoutelessControllerActions.php index 636ef9f5..c8cbcefe 100644 --- a/src/Checks/RoutelessActions.php +++ b/src/Features/CheckDeadControllers/RoutelessControllerActions.php @@ -1,21 +1,20 @@ getNamespace(); if (! self::isLaravelController($fullNamespace)) { return; @@ -26,9 +25,9 @@ public static function check($tokens, $absFilePath, $params, $classFilePath, $ps return; } - $actions = self::findOrphanActions($tokens, $fullNamespace); + $actions = self::findOrphanActions($file->getTokens(), $fullNamespace); - self::printErrors($actions, $absFilePath); + self::printErrors($actions, $file->getAbsolutePath()); } public static function getControllerActions($methods) @@ -57,16 +56,6 @@ public static function getControllerActions($methods) return $orphanMethods; } - public static function getNamespacedClassName($classFilePath, $psr4Path, $psr4Namespace) - { - $absFilePath = $classFilePath->getRealPath(); - $className = $classFilePath->getFilename(); - $relativePath = \str_replace(base_path(), '', $absFilePath); - $namespace = NamespaceCalculator::calculateCorrectNamespace($relativePath, $psr4Path, $psr4Namespace); - - return $namespace.'\\'.$className; - } - public static function isLaravelController($fullNamespace) { try { @@ -77,13 +66,6 @@ public static function isLaravelController($fullNamespace) } } - public static function getFullNamespace($classFilePath, $psr4Path, $psr4Namespace) - { - $fullNamespace = self::getNamespacedClassName($classFilePath, $psr4Path, $psr4Namespace); - - return Str::replaceFirst('.php', '', $fullNamespace); - } - protected static function findOrphanActions($tokens, $fullNamespace) { $class = ClassMethods::read($tokens); @@ -107,9 +89,9 @@ protected static function findOrphanActions($tokens, $fullNamespace) public static function classAtMethod($fullNamespace, $methodName) { - ($methodName == '__invoke') ? ($methodName = '') : ($methodName = '@'.$methodName); + $methodName = $methodName === '__invoke' ? '' : '@'.$methodName; - return \trim($fullNamespace, '\\').$methodName; + return trim($fullNamespace, '\\').$methodName; } protected static function getByAction($classAtMethod) @@ -117,7 +99,7 @@ protected static function getByAction($classAtMethod) return app('router')->getRoutes()->getByAction($classAtMethod); } - private static function printErrors(array $actions, $absFilePath): void + private static function printErrors(array $actions, $absFilePath) { $errorPrinter = ErrorPrinter::singleton(); @@ -125,9 +107,4 @@ private static function printErrors(array $actions, $absFilePath): void $errorPrinter->simplePendError($action[1], $absFilePath, $action[0], 'routelessCtrl', 'No route is defined for controller action:'); } } - - private static function invoke($method, string $classAtMethod): bool - { - return $method === '__invoke' && ! self::getByAction($classAtMethod) && ! self::getByAction($classAtMethod.'@__invoke'); - } } diff --git a/src/Features/CheckFacadeDocblocks/FacadeDocblocks.php b/src/Features/CheckFacadeDocblocks/FacadeDocblocks.php index 1b8ccc82..bc9d462c 100644 --- a/src/Features/CheckFacadeDocblocks/FacadeDocblocks.php +++ b/src/Features/CheckFacadeDocblocks/FacadeDocblocks.php @@ -6,7 +6,7 @@ use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Facade; use Imanghafoori\Filesystem\Filesystem; -use Imanghafoori\LaravelMicroscope\Analyzers\ComposerJson; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\RealtimeFacades\SmartRealTimeFacadesProvider; use Imanghafoori\SearchReplace\Searcher; use ReflectionClass; @@ -16,9 +16,11 @@ class FacadeDocblocks { public static $command; - public static function check($tokens, $absFilePath) + public static function check(PhpFileDescriptor $file) { - $facade = ComposerJson::make()->getNamespacedClassFromPath($absFilePath); + $absFilePath = $file->getAbsolutePath(); + + $facade = $file->getNamespace(); if (! self::isFacade($facade)) { return null; @@ -41,7 +43,7 @@ public static function check($tokens, $absFilePath) } } - self::addDocBlocks($accessor, $facade, $tokens, $absFilePath); + self::addDocBlocks($accessor, $facade, $file->getTokens(), $absFilePath); } private static function addDocBlocks(string $accessor, $facade, $tokens, $absFilePath) diff --git a/src/Features/CheckGenericDocBlocks/CheckGenericDocBlocksCommand.php b/src/Features/CheckGenericDocBlocks/CheckGenericDocBlocksCommand.php index 52cad077..b231d5ef 100644 --- a/src/Features/CheckGenericDocBlocks/CheckGenericDocBlocksCommand.php +++ b/src/Features/CheckGenericDocBlocks/CheckGenericDocBlocksCommand.php @@ -3,6 +3,7 @@ namespace Imanghafoori\LaravelMicroscope\Features\CheckGenericDocBlocks; use Illuminate\Console\Command; +use Imanghafoori\LaravelMicroscope\Features\CheckImports\Reporters\Psr4Report; use Imanghafoori\LaravelMicroscope\ForPsr4LoadedClasses; class CheckGenericDocBlocksCommand extends Command @@ -15,10 +16,18 @@ public function handle() { $this->info('Removing generic doc-blocks...'); - GenericDocblocks::$confirmer = $this->getConformer(); + GenericDocblocks::$conformer = $this->getConformer(); - $results = ForPsr4LoadedClasses::check([GenericDocblocks::class], [], ltrim($this->option('file'), '='), ltrim($this->option('folder'), '=')); - iterator_to_array($results); + $psr4Stats = ForPsr4LoadedClasses::check( + [GenericDocblocks::class], + [], + ltrim($this->option('file'), '='), + ltrim($this->option('folder'), '=') + ); + + $this->getOutput()->writeln(implode(PHP_EOL, [ + Psr4Report::printAutoload($psr4Stats, []), + ])); $this->info(GenericDocblocks::$foundCount.' generic doc-blocks were found.'); $this->info(GenericDocblocks::$removedCount.' of them were removed.'); diff --git a/src/Features/CheckGenericDocBlocks/GenericDocblocks.php b/src/Features/CheckGenericDocBlocks/GenericDocblocks.php index 97a0e6d1..bc022b52 100644 --- a/src/Features/CheckGenericDocBlocks/GenericDocblocks.php +++ b/src/Features/CheckGenericDocBlocks/GenericDocblocks.php @@ -3,7 +3,8 @@ namespace Imanghafoori\LaravelMicroscope\Features\CheckGenericDocBlocks; use Imanghafoori\LaravelMicroscope\Check; -use Imanghafoori\LaravelMicroscope\Checks\RoutelessActions; +use Imanghafoori\LaravelMicroscope\Features\CheckDeadControllers\RoutelessControllerActions; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\TokenAnalyzer\Refactor; use Imanghafoori\TokenAnalyzer\Str; @@ -20,7 +21,7 @@ class GenericDocblocks implements Check '* Handle the incoming request.', ]; - public static $confirmer; + public static $conformer; public static $foundCount = 0; @@ -28,28 +29,20 @@ class GenericDocblocks implements Check public static $controllers = []; - public static function check($tokens, $absFilePath, $params, $classFilePath, $psr4Path, $psr4Namespace) + public static function check(PhpFileDescriptor $file) { - $fullNamespace = RoutelessActions::getFullNamespace($classFilePath, $psr4Path, $psr4Namespace); + $tokens = $file->getTokens(); - if (! RoutelessActions::isLaravelController($fullNamespace)) { + $fullNamespace = $file->getNamespace(); + + if (! RoutelessControllerActions::isLaravelController($fullNamespace)) { return null; } - $hasReplacement = false; - foreach ($tokens as $i => $token) { - if ($token[0] !== T_DOC_COMMENT) { - continue; - } + [$hasReplacement, $tokens] = self::removeDocBlocks($tokens); - if (self::shouldBeRemoved($token[1])) { - self::$foundCount++; - $hasReplacement = true; - $tokens = self::removeDocblock($tokens, $i); - } - } - - if ($hasReplacement && (self::$confirmer)($absFilePath)) { + $absFilePath = $file->getAbsolutePath(); + if ($hasReplacement && (self::$conformer)($absFilePath)) { Refactor::saveTokens($absFilePath, $tokens); } } @@ -73,4 +66,22 @@ private static function shouldBeRemoved($docblock) { return Str::contains($docblock, self::statements); } + + private static function removeDocBlocks(array $tokens): array + { + $hasReplacement = false; + foreach ($tokens as $i => $token) { + if ($token[0] !== T_DOC_COMMENT) { + continue; + } + + if (self::shouldBeRemoved($token[1])) { + self::$foundCount++; + $hasReplacement = true; + $tokens = self::removeDocblock($tokens, $i); + } + } + + return [$hasReplacement, $tokens]; + } } diff --git a/src/Features/CheckImports/CheckImportsCommand.php b/src/Features/CheckImports/CheckImportsCommand.php index c681dbeb..d3bb992f 100644 --- a/src/Features/CheckImports/CheckImportsCommand.php +++ b/src/Features/CheckImports/CheckImportsCommand.php @@ -17,6 +17,7 @@ use Imanghafoori\LaravelMicroscope\Features\Thanks; use Imanghafoori\LaravelMicroscope\FileReaders\FilePath; use Imanghafoori\LaravelMicroscope\ForPsr4LoadedClasses; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\LaravelMicroscope\Iterators\BladeFiles; use Imanghafoori\LaravelMicroscope\Iterators\ChecksOnPsr4Classes; use Imanghafoori\LaravelMicroscope\Iterators\ClassMapIterator; @@ -133,8 +134,8 @@ public function handle() $messages[3] = $this->getFilesStats(); $messages[4] = Reporters\BladeReport::getBladeStats($bladeStats); $messages[5] = Reporters\LaravelFoldersReport::foldersStats($foldersStats); - $messages[6] = CheckImportReporter::getRouteStats(base_path(), $routeFiles); - $messages[7] = AutoloadFiles::getLines(base_path(), $autoloadedFilesGen); + $messages[6] = CheckImportReporter::getRouteStats($routeFiles); + $messages[7] = AutoloadFiles::getLines($autoloadedFilesGen); $messages[8] = Reporters\SummeryReport::summery($errorPrinter->errorsCounts); if (! ImportsAnalyzer::$checkedRefCount) { @@ -168,8 +169,8 @@ private function printThanks($command) */ private function getParamProvider() { - return function ($tokens) { - $imports = ParseUseStatement::parseUseStatements($tokens); + return function (PhpFileDescriptor $file) { + $imports = ParseUseStatement::parseUseStatements($file->getTokens()); return $imports[0] ?: [$imports[1]]; }; diff --git a/src/Features/CheckImports/Checks/CheckClassAtMethod.php b/src/Features/CheckImports/Checks/CheckClassAtMethod.php index 08f2c2e3..de41faf1 100644 --- a/src/Features/CheckImports/Checks/CheckClassAtMethod.php +++ b/src/Features/CheckImports/Checks/CheckClassAtMethod.php @@ -4,13 +4,17 @@ use Imanghafoori\LaravelMicroscope\Check; use Imanghafoori\LaravelMicroscope\Features\CheckImports\Handlers\ClassAtMethodHandler; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; class CheckClassAtMethod implements Check { public static $handler = ClassAtMethodHandler::class; - public static function check($tokens, $absFilePath) + public static function check(PhpFileDescriptor $file) { + $tokens = $file->getTokens(); + $absFilePath = $file->getAbsolutePath(); + $replaced = self::$handler::handle( $absFilePath, self::getAtSignTokens($tokens, $absFilePath) diff --git a/src/Features/CheckImports/Checks/CheckClassReferencesAreValid.php b/src/Features/CheckImports/Checks/CheckClassReferencesAreValid.php index 135c67ab..2e9bd4de 100644 --- a/src/Features/CheckImports/Checks/CheckClassReferencesAreValid.php +++ b/src/Features/CheckImports/Checks/CheckClassReferencesAreValid.php @@ -4,6 +4,7 @@ use Imanghafoori\LaravelMicroscope\Check; use Imanghafoori\LaravelMicroscope\Features\CheckImports\Handlers; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\TokenAnalyzer\ImportsAnalyzer; class CheckClassReferencesAreValid implements Check @@ -18,8 +19,11 @@ class CheckClassReferencesAreValid implements Check public static $wrongClassRefsHandler = Handlers\FixWrongClassRefs::class; - public static function check($tokens, $absFilePath, $imports = []) + public static function check(PhpFileDescriptor $file, $imports = []) { + $tokens = $file->getTokens(); + $absFilePath = $file->getAbsolutePath(); + loopStart: [ $hostNamespace, diff --git a/src/Features/CheckImports/Reporters/AutoloadFiles.php b/src/Features/CheckImports/Reporters/AutoloadFiles.php index 2fd2fb5e..ca0e5ccb 100644 --- a/src/Features/CheckImports/Reporters/AutoloadFiles.php +++ b/src/Features/CheckImports/Reporters/AutoloadFiles.php @@ -7,16 +7,15 @@ class AutoloadFiles use Reporting; /** - * @param string $basePath * @param \Generator $filesListGen * @return string */ - public static function getLines($basePath, $filesListGen) + public static function getLines($filesListGen) { $lines = ''; $total = 0; foreach ($filesListGen as $files) { - $linesArr = self::formatFiles($files, $basePath); + $linesArr = self::formatFiles($files); $total += count($linesArr); $lines .= implode('', $linesArr); } diff --git a/src/Features/CheckImports/Reporters/CheckImportReporter.php b/src/Features/CheckImports/Reporters/CheckImportReporter.php index fc64e3b9..527e6041 100644 --- a/src/Features/CheckImports/Reporters/CheckImportReporter.php +++ b/src/Features/CheckImports/Reporters/CheckImportReporter.php @@ -13,9 +13,9 @@ public static function totalImportsMsg() return 'Imports were checked under:'; } - public static function getRouteStats($basePath, $routeFiles) + public static function getRouteStats($routeFiles) { - $linesArr = self::formatFiles($routeFiles, $basePath); + $linesArr = self::formatFiles($routeFiles); $count = count($linesArr); $lines = implode('', $linesArr); diff --git a/src/Features/CheckImports/Reporters/Psr4Report.php b/src/Features/CheckImports/Reporters/Psr4Report.php index 3739d93c..64a6f513 100644 --- a/src/Features/CheckImports/Reporters/Psr4Report.php +++ b/src/Features/CheckImports/Reporters/Psr4Report.php @@ -2,6 +2,7 @@ namespace Imanghafoori\LaravelMicroscope\Features\CheckImports\Reporters; +use Generator; use JetBrains\PhpStorm\Pure; class Psr4Report @@ -16,20 +17,17 @@ class Psr4Report */ public static function printAutoload($psr4Stats, $classMapStats) { - $output = ''; + $callback = function ($composerPath, $psr4, $classMapStats) { + return self::present($composerPath, $psr4, $classMapStats); + }; + + $outputAll = ''; foreach ($psr4Stats as $composerPath => $psr4) { - $output .= PHP_EOL; - $output .= self::formatComposerPath($composerPath); - $output .= PHP_EOL; - $output .= self::hyphen('PSR-4 '); - $output .= self::formatPsr4Stats($psr4); - if (isset($classMapStats[$composerPath])) { - $lines = ClassMapStats::getMessage($classMapStats[$composerPath], self::$callback); - $lines && ($output .= PHP_EOL.$lines); - } + $output = $callback($composerPath, $psr4, $classMapStats); + $outputAll .= $output; } - return trim($output); + return trim($outputAll); } public static function formatComposerPath($composerPath) @@ -131,4 +129,20 @@ private static function concatinate($longest, array $lines) return implode('', $lines); } + + private static function present(string $composerPath, Generator $psr4, $classMapStats) + { + $output = ''; + $output .= PHP_EOL; + $output .= self::formatComposerPath($composerPath); + $output .= PHP_EOL; + $output .= self::hyphen('PSR-4 '); + $output .= self::formatPsr4Stats($psr4); + if (isset($classMapStats[$composerPath])) { + $lines = ClassMapStats::getMessage($classMapStats[$composerPath], self::$callback); + $lines && ($output .= PHP_EOL.$lines); + } + + return $output; + } } diff --git a/src/Features/CheckImports/Reporters/Reporting.php b/src/Features/CheckImports/Reporters/Reporting.php index 2c78d555..5e30c966 100644 --- a/src/Features/CheckImports/Reporters/Reporting.php +++ b/src/Features/CheckImports/Reporters/Reporting.php @@ -3,6 +3,7 @@ namespace Imanghafoori\LaravelMicroscope\Features\CheckImports\Reporters; use Imanghafoori\LaravelMicroscope\FileReaders\FilePath; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use JetBrains\PhpStorm\Pure; trait Reporting @@ -48,21 +49,19 @@ public static function normalize($dirPath) return str_replace(DIRECTORY_SEPARATOR, '/', $path).'/'; } - private static function formatLine($basePath, $absFilePath): string + private static function formatLine(PhpFileDescriptor $file): string { - $relPath = str_replace($basePath, '', $absFilePath); - $relPath = ltrim($relPath, DIRECTORY_SEPARATOR); - $relPath = str_replace(DIRECTORY_SEPARATOR, '/', $relPath); + $relPath = $file->path()->relativePath()->getWithUnixDirectorySeprator(); return PHP_EOL.' '.self::hyphen(''.$relPath.''); } #[Pure] - private static function formatFiles($files, string $basePath): array + private static function formatFiles($files) { $lines = []; - foreach ($files as $absFilePath) { - $lines[] = self::formatLine($basePath, $absFilePath); + foreach ($files as $file) { + $lines[] = self::formatLine($file); } return $lines; diff --git a/src/Features/CheckView/BladeFile.php b/src/Features/CheckView/BladeFile.php deleted file mode 100644 index 7e39b9f9..00000000 --- a/src/Features/CheckView/BladeFile.php +++ /dev/null @@ -1,10 +0,0 @@ -getTokens(); + $absPath = $file->getAbsolutePath(); + $staticCalls = [ 'View' => ['make', 0], 'Route' => ['view', 1], @@ -33,10 +36,10 @@ private static function checkViewParams($absPath, &$tokens, $i, $index) if (FunctionCall::isSolidString($paramTokens)) { self::$checkedCallsCount++; - $viewName = \trim($paramTokens[0][1], '\'\"'); - - $viewName = str_replace('.', '/', $viewName); - $viewName && ! View::exists($viewName) && BladeFile::warn($absPath, $paramTokens[0][2], $viewName); + $viewName = self::getViewName($paramTokens[0][1]); + if ($viewName && ! View::exists($viewName)) { + CheckView::viewError($absPath, $paramTokens[0][2], $viewName); + } } else { self::$skippedCallsCount++; } @@ -60,15 +63,22 @@ public static function checkViewCalls($tokens, $absPath, array $staticCalls) return $tokens; } - public static function viewError($absPath, $message, $lineNumber, $fileName) + public static function viewError($absPath, $lineNumber, $fileName) { ErrorPrinter::singleton()->simplePendError( $fileName.'.blade.php', $absPath, $lineNumber, 'missing_view', - \trim($message), + 'The blade file is missing:', ' does not exist' ); } + + private static function getViewName($string) + { + $viewName = trim($string, '\'\"'); + + return str_replace('.', '/', $viewName); + } } diff --git a/src/Features/CheckView/Check/CheckViewFilesExistence.php b/src/Features/CheckView/Check/CheckViewFilesExistence.php index 062ec3e3..ad1da52a 100644 --- a/src/Features/CheckView/Check/CheckViewFilesExistence.php +++ b/src/Features/CheckView/Check/CheckViewFilesExistence.php @@ -4,19 +4,22 @@ use Illuminate\Support\Facades\View; use Imanghafoori\LaravelMicroscope\Check; -use Imanghafoori\LaravelMicroscope\Features\CheckView\BladeFile; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; class CheckViewFilesExistence implements Check { - public static function check($tokens, $absPath) + public static function check(PhpFileDescriptor $file) { - $tCount = \count($tokens); + $tokens = $file->getTokens(); + $absPath = $file->getAbsolutePath(); + + $tCount = count($tokens); for ($i = 0; $i < $tCount; $i++) { if (! self::isEnvMake($tokens, $i)) { continue; } - $viewName = \trim($tokens[$i + 4][1], '\'\"'); + $viewName = trim($tokens[$i + 4][1], '\'\"'); CheckView::$checkedCallsCount++; if (! View::exists($viewName)) { self::error($tokens, $absPath, $i); @@ -44,7 +47,7 @@ private static function error($tokens, $absPath, $i) { $viewName = $tokens[$i + 4][1]; $viewName = str_replace('.', '/', trim($viewName, '\'\"')); - BladeFile::warn($absPath, $tokens[$i + 4][2], $viewName); + CheckView::viewError($absPath, $tokens[$i + 4][2], $viewName); } private static function isVariable($token, string $varName) diff --git a/src/Features/CheckView/CheckViewsCommand.php b/src/Features/CheckView/CheckViewsCommand.php index dcbcba19..2dadb865 100644 --- a/src/Features/CheckView/CheckViewsCommand.php +++ b/src/Features/CheckView/CheckViewsCommand.php @@ -4,6 +4,7 @@ use Illuminate\Console\Command; use Imanghafoori\LaravelMicroscope\ErrorReporters\ErrorPrinter; +use Imanghafoori\LaravelMicroscope\Features\CheckImports\Reporters\Psr4Report; use Imanghafoori\LaravelMicroscope\Features\CheckView\Check\CheckView; use Imanghafoori\LaravelMicroscope\Features\CheckView\Check\CheckViewFilesExistence; use Imanghafoori\LaravelMicroscope\FileReaders\FilePath; @@ -29,7 +30,13 @@ public function handle(ErrorPrinter $errorPrinter) $this->checkRoutePaths( FilePath::removeExtraPaths(RoutePaths::get(), $folder, $fileName) ); - $this->checkPsr4($fileName, $folder); + + $psr4Stats = ForPsr4LoadedClasses::check([CheckView::class], [], $fileName, $folder); + + $this->getOutput()->writeln(implode(PHP_EOL, [ + Psr4Report::printAutoload($psr4Stats, []), + ])); + $this->checkBladeFiles($fileName, $folder); $this->logErrors($errorPrinter); @@ -78,9 +85,4 @@ private function logErrors(ErrorPrinter $errorPrinter) $errorPrinter->printTime(); } - - private function checkPsr4(string $fileName, string $folder) - { - ForPsr4LoadedClasses::checkNow([CheckView::class], [], $fileName, $folder); - } } diff --git a/src/Features/CheckView/ViewsInstaller.php b/src/Features/CheckView/ViewsInstaller.php deleted file mode 100644 index 49711893..00000000 --- a/src/Features/CheckView/ViewsInstaller.php +++ /dev/null @@ -1,22 +0,0 @@ -data; - CheckView::viewError( - $data['absPath'], - 'The blade file is missing:', - $data['lineNumber'], - $data['name'] - ); - }); - } -} diff --git a/src/Features/ExtractsBladePartials/ExtractBladePartial.php b/src/Features/ExtractsBladePartials/ExtractBladePartial.php index d98feacd..97de1bb1 100644 --- a/src/Features/ExtractsBladePartials/ExtractBladePartial.php +++ b/src/Features/ExtractsBladePartials/ExtractBladePartial.php @@ -6,13 +6,17 @@ use Illuminate\Support\Facades\View; use Illuminate\Support\Str; use Imanghafoori\LaravelMicroscope\Check; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\TokenAnalyzer\FunctionCall; use InvalidArgumentException; class ExtractBladePartial implements Check { - public static function check($tokens, $absPath) + public static function check(PhpFileDescriptor $file) { + $tokens = $file->getTokens(); + $absPath = $file->getAbsolutePath(); + // we skip the very first tokens: 'getTokens()); return $imports[0] ?: [$imports[1]]; }; diff --git a/src/Features/FacadeAlias/FacadeAliasesCheck.php b/src/Features/FacadeAlias/FacadeAliasesCheck.php index f14fe14f..35a33f34 100644 --- a/src/Features/FacadeAlias/FacadeAliasesCheck.php +++ b/src/Features/FacadeAlias/FacadeAliasesCheck.php @@ -4,6 +4,7 @@ use Illuminate\Foundation\AliasLoader; use Imanghafoori\LaravelMicroscope\Check; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; class FacadeAliasesCheck implements Check { @@ -17,8 +18,11 @@ class FacadeAliasesCheck implements Check */ public static $command; - public static function check($tokens, $absFilePath, $imports) + public static function check(PhpFileDescriptor $file, $imports) { + $tokens = $file->getTokens(); + $absFilePath = $file->getAbsolutePath(); + $aliases = AliasLoader::getInstance()->getAliases(); self::$handler::$command = self::$command; diff --git a/src/Features/ListModels/SubclassFinder.php b/src/Features/ListModels/SubclassFinder.php index 0c12b787..436ab4ad 100644 --- a/src/Features/ListModels/SubclassFinder.php +++ b/src/Features/ListModels/SubclassFinder.php @@ -25,7 +25,7 @@ public function getList($folder, $parentClass) return ComposerJson::make()->getClasslists($filter, $pathFilter); } - protected function getPathFilter(string $folder) + protected function getPathFilter($folder) { return function ($absFilePath, $fileName) use ($folder) { return strpos(str_replace(base_path(), '', $absFilePath), $folder); diff --git a/src/Features/Psr4/CheckPsr4ArtisanCommand.php b/src/Features/Psr4/CheckPsr4ArtisanCommand.php index c5b08dbf..f5515c7f 100644 --- a/src/Features/Psr4/CheckPsr4ArtisanCommand.php +++ b/src/Features/Psr4/CheckPsr4ArtisanCommand.php @@ -108,7 +108,7 @@ private function countClasses($classLists) return [$stats, $typesStats]; } - private function getPathFilter(string $folder) + private function getPathFilter($folder) { return function ($absFilePath, $fileName) use ($folder) { return strpos($absFilePath, $folder); diff --git a/src/ForPsr4LoadedClasses.php b/src/ForPsr4LoadedClasses.php index ba69c7a4..1af62b09 100644 --- a/src/ForPsr4LoadedClasses.php +++ b/src/ForPsr4LoadedClasses.php @@ -23,10 +23,18 @@ public static function check($checks, $params = [], $includeFile = '', $includeF public static function checkNow($checks, $params = [], $includeFile = '', $includeFolder = '', $callback = null) { - foreach (self::check($checks, $params, $includeFile, $includeFolder) as $results) { - foreach (iterator_to_array($results) as $result) { + self::applyOnStats( + self::check($checks, $params, $includeFile, $includeFolder), + $callback + ); + } + + public static function applyOnStats(array $allStats, $callback = null) + { + foreach ($allStats as $path => $results) { + foreach (iterator_to_array($results) as $namespace => $result) { foreach ($result as $folder => $count) { - $callback && $callback($folder, $count); + $callback && $callback($folder, $count, $path, $namespace); } } } diff --git a/src/Foundations/Path.php b/src/Foundations/Path.php new file mode 100644 index 00000000..99b089fc --- /dev/null +++ b/src/Foundations/Path.php @@ -0,0 +1,66 @@ +path = $path; + } + + private static function normalizeDirectorySeparator($absolutePath): string + { + return str_replace('/\\', DIRECTORY_SEPARATOR, $absolutePath); + } + + public function relativePath() + { + $relPath = str_replace(self::$basePath, '', $this->path); + + return self::make(trim($relPath, DIRECTORY_SEPARATOR)); + } + + private static function removeTrailingSlash($path): string + { + return rtrim($path, DIRECTORY_SEPARATOR); + } + + public function __toString() + { + return $this->path; + } + + public function getWithUnixDirectorySeprator() + { + return str_replace('\\', '/', $this->path); + } + + public function getWithWindowsDirectorySeprator() + { + return str_replace('/', '\\', $this->path); + } +} diff --git a/src/Foundations/PhpFileDescriptor.php b/src/Foundations/PhpFileDescriptor.php new file mode 100644 index 00000000..22f70f76 --- /dev/null +++ b/src/Foundations/PhpFileDescriptor.php @@ -0,0 +1,122 @@ +path = Path::make($absolutePath); + + return $obj; + } + + public function setTokenizer($tokenizer) + { + $this->tokenizer = $tokenizer; + } + + public function getTokens($reload = false) + { + if (! $this->tokens || $reload) { + $this->tokens = $this->tokenizer ? ($this->tokenizer)($this->path) : $this->tokenize(); + } + + return $this->tokens; + } + + public function getAbsolutePath() + { + return $this->path->__toString(); + } + + public function relativePath() + { + return $this->path->relativePath(); + } + + public function getLine(int $lineNumber) + { + return file($this->path)[$lineNumber - 1] ?? ''; + } + + public function getNamespace() + { + return ComposerJson::make()->getNamespacedClassFromPath($this->path); + } + + private function tokenize() + { + return token_get_all(file_get_contents($this->path)); + } + + public function setTokens($tokens) + { + $this->tokens = $tokens; + } + + public function path() + { + return $this->path; + } + + public function putContents($newVersion) + { + $this->tokens = []; + + return Filesystem::$fileSystem::file_put_contents($this->path, $newVersion); + } + + public function replaceAtLine($search, $replace, $lineNum) + { + $this->tokens = []; + + return FileManipulator::replaceFirst($this->path, $search, $replace, $lineNum); + } + + public function searchReplacePatterns($search, $replace) + { + [$newVersion, $lines] = self::searchReplace($search, $replace, $this->tokens); + + $this->putContents($newVersion); + + return $lines; + } + + private static function pattern($search, $replace): array + { + return [ + 'fix' => [ + 'search' => $search, + 'replace' => $replace, + ], + ]; + } + + public static function searchReplace($search, $replace, $tokens): array + { + return Searcher::searchReplace(self::pattern($search, $replace), $tokens); + } +} diff --git a/src/Iterators/BaseIterator.php b/src/Iterators/BaseIterator.php index 7191a12e..1c7750ad 100644 --- a/src/Iterators/BaseIterator.php +++ b/src/Iterators/BaseIterator.php @@ -2,25 +2,23 @@ namespace Imanghafoori\LaravelMicroscope\Iterators; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; + abstract class BaseIterator { protected static function applyChecks($absFilePaths, $checks, $params) { foreach ($absFilePaths as $absFilePath) { - $tokens = token_get_all(file_get_contents($absFilePath)); + $file = PhpFileDescriptor::make($absFilePath); foreach ($checks as $check) { - $check::check( - $tokens, - $absFilePath, - self::processParams($tokens, $absFilePaths, $params) - ); + $check::check($file, self::processParams($file, $params)); } - yield $absFilePath; + yield $file; } } - private static function processParams($tokens, $absFilePaths, $params) + private static function processParams(PhpFileDescriptor $file, $params) { - return (! is_array($params) && is_callable($params)) ? $params($tokens, $absFilePaths) : $params; + return (! is_array($params) && is_callable($params)) ? $params($file) : $params; } } diff --git a/src/Iterators/BladeFiles/CheckBladePaths.php b/src/Iterators/BladeFiles/CheckBladePaths.php index fabbbd61..c967f3a9 100644 --- a/src/Iterators/BladeFiles/CheckBladePaths.php +++ b/src/Iterators/BladeFiles/CheckBladePaths.php @@ -3,6 +3,7 @@ namespace Imanghafoori\LaravelMicroscope\Iterators\BladeFiles; use Imanghafoori\LaravelMicroscope\Features\CheckUnusedBladeVars\ViewsData; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\LaravelMicroscope\Iterators\FiltersFiles; use Symfony\Component\Finder\Finder; @@ -48,7 +49,7 @@ public static function findFiles($path, $fileName = null): Finder * @param string $path * @return bool */ - private static function shouldSkip(string $path) + private static function shouldSkip($path) { if (! is_dir($path)) { return true; @@ -82,12 +83,17 @@ private static function applyChecks($files, $paramsProvider, $checkers): int /** * @var \Symfony\Component\Finder\SplFileInfo $blade */ - $absPath = $blade->getPathname(); - $tokens = ViewsData::getBladeTokens($absPath); - $params1 = (! is_array($paramsProvider) && is_callable($paramsProvider)) ? $paramsProvider($tokens, $absPath) : $paramsProvider; + $absFilePath = $blade->getPathname(); + + $file = PhpFileDescriptor::make($absFilePath); + $file->setTokenizer(function ($absPath) { + return ViewsData::getBladeTokens($absPath); + }); + + $params1 = (! is_array($paramsProvider) && is_callable($paramsProvider)) ? $paramsProvider($file) : $paramsProvider; foreach ($checkers as $check) { - $check::check($tokens, $absPath, $params1); + $check::check($file, $params1); } } diff --git a/src/Iterators/Check.php b/src/Iterators/Check.php index 45b1115e..2c68d4ef 100644 --- a/src/Iterators/Check.php +++ b/src/Iterators/Check.php @@ -2,7 +2,9 @@ namespace Imanghafoori\LaravelMicroscope\Iterators; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; + abstract class Check { - abstract public static function check($tokens, $absFilePath, $processedParams, $phpFilePath, $psr4Path, $psr4Namespace); + abstract public static function check(PhpFileDescriptor $file, $processedParams, $psr4Path, $psr4Namespace); } diff --git a/src/Iterators/ChecksOnPsr4Classes.php b/src/Iterators/ChecksOnPsr4Classes.php index 2e0f20dc..38849a97 100644 --- a/src/Iterators/ChecksOnPsr4Classes.php +++ b/src/Iterators/ChecksOnPsr4Classes.php @@ -4,6 +4,7 @@ use Imanghafoori\LaravelMicroscope\Analyzers\ComposerJson; use Imanghafoori\LaravelMicroscope\FileReaders\PhpFinder; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Throwable; class ChecksOnPsr4Classes @@ -43,9 +44,9 @@ public static function apply($checks, $params, $includeFile, $includeFolder) return $stats; } - private static function getParams($params, array $tokens, $absFilePath, $psr4Path, $psr4Namespace) + private static function getParams($params, PhpFileDescriptor $file, $psr4Path, $psr4Namespace) { - return (! is_array($params) && is_callable($params)) ? $params($tokens, $absFilePath, $psr4Path, $psr4Namespace) : $params; + return (! is_array($params) && is_callable($params)) ? $params($file, $psr4Path, $psr4Namespace) : $params; } /** @@ -69,21 +70,22 @@ private static function applyChecksInPath($psr4Namespace, $psr4Path, $checks, $p return $filesCount; } - private static function applyChecks($phpFilePath, $params, $psr4Path, $psr4Namespace, $checks) + private static function applyChecks($phpFileObj, $params, $psr4Path, $psr4Namespace, $checks) { - $absFilePath = $phpFilePath->getRealPath(); - $tokens = token_get_all(file_get_contents($absFilePath)); + $absFilePath = $phpFileObj->getRealPath(); - $processedParams = self::getParams($params, $tokens, $absFilePath, $psr4Path, $psr4Namespace); + $file = PhpFileDescriptor::make($absFilePath); + + $processedParams = self::getParams($params, $file, $psr4Path, $psr4Namespace); foreach ($checks as $check) { try { /** * @var $check \Imanghafoori\LaravelMicroscope\Iterators\Check */ - $newTokens = $check::check($tokens, $absFilePath, $processedParams, $phpFilePath, $psr4Path, $psr4Namespace); + $newTokens = $check::check($file, $processedParams, $psr4Path, $psr4Namespace); if ($newTokens) { - $tokens = $newTokens; - $processedParams = self::getParams($params, $tokens, $absFilePath, $psr4Path, $psr4Namespace); + $file->setTokens($newTokens); + $processedParams = self::getParams($params, $file, $psr4Path, $psr4Namespace); } } catch (Throwable $exception) { self::$exceptions[] = $exception; diff --git a/src/LaravelMicroscopeServiceProvider.php b/src/LaravelMicroscopeServiceProvider.php index 008a64d1..eead8029 100644 --- a/src/LaravelMicroscopeServiceProvider.php +++ b/src/LaravelMicroscopeServiceProvider.php @@ -14,6 +14,8 @@ use Imanghafoori\LaravelMicroscope\Features\CheckView\Check\CheckView; use Imanghafoori\LaravelMicroscope\Features\ListModels\ListModelsArtisanCommand; use Imanghafoori\LaravelMicroscope\FileReaders\PhpFinder; +use Imanghafoori\LaravelMicroscope\Foundations\Path; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\LaravelMicroscope\SpyClasses\SpyBladeCompiler; use Imanghafoori\LaravelMicroscope\SpyClasses\SpyGate; use Imanghafoori\TokenAnalyzer\ImportsAnalyzer; @@ -30,7 +32,7 @@ class LaravelMicroscopeServiceProvider extends ServiceProvider Features\CheckImports\CheckImportsCommand::class, Features\FacadeAlias\CheckAliasesCommand::class, Commands\CheckAll::class, - Commands\ClassifyStrings::class, + Features\CheckClassyStrings\ClassifyStrings::class, Features\CheckDD\CheckDDCommand::class, Commands\CheckEarlyReturns::class, Commands\CheckCompact::class, @@ -40,7 +42,7 @@ class LaravelMicroscopeServiceProvider extends ServiceProvider Features\ExtractsBladePartials\CheckExtractBladeIncludesCommand::class, Commands\PrettyPrintRoutes::class, Features\ServiceProviderGenerator\CheckCodeGeneration::class, - Commands\CheckDeadControllers::class, + Features\CheckDeadControllers\CheckDeadControllers::class, Features\CheckGenericDocBlocks\CheckGenericDocBlocksCommand::class, Commands\CheckPsr12::class, Commands\CheckEndIf::class, @@ -99,12 +101,9 @@ public function register() }; PhpFinder::$basePath = base_path(); + Path::setBasePath(base_path()); - [$major] = explode('.', app()->version()); - - $color = (int) $major >= 8 ? 'gray' : 'blue'; - - config()->set('microscope.colors.line_separator', $color); + $this->setLineSeparatorColor(); $this->registerCompiler(); @@ -159,4 +158,13 @@ private function resetCountersOnFinish() Iterators\ChecksOnPsr4Classes::$checkedFilesCount = 0; }); } + + private function setLineSeparatorColor() + { + [$major] = explode('.', app()->version()); + + $color = (int) $major >= 9 ? 'gray' : 'blue'; + + config()->set('microscope.colors.line_separator', $color); + } } diff --git a/src/SearchReplace/PatternRefactorings.php b/src/SearchReplace/PatternRefactorings.php index 804fc594..8df20b33 100644 --- a/src/SearchReplace/PatternRefactorings.php +++ b/src/SearchReplace/PatternRefactorings.php @@ -6,6 +6,7 @@ use Imanghafoori\Filesystem\Filesystem; use Imanghafoori\LaravelMicroscope\Check; use Imanghafoori\LaravelMicroscope\ErrorReporters\ErrorPrinter; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\SearchReplace\Finder; use Imanghafoori\SearchReplace\Replacer; use Imanghafoori\SearchReplace\Stringify; @@ -15,8 +16,11 @@ class PatternRefactorings implements Check { public static $patternFound = false; - public static function check($tokens, $absFilePath, $patterns) + public static function check(PhpFileDescriptor $file, $patterns) { + $tokens = $file->getTokens(); + $absFilePath = $file->getAbsolutePath(); + foreach ($patterns[0] as $pattern) { if (isset($pattern['file']) && ! Str::endsWith($absFilePath, $pattern['file'])) { continue; diff --git a/src/SpyClasses/RoutePaths.php b/src/SpyClasses/RoutePaths.php index 1fa930c9..07aa23da 100644 --- a/src/SpyClasses/RoutePaths.php +++ b/src/SpyClasses/RoutePaths.php @@ -5,6 +5,7 @@ use Illuminate\Support\Str; use Imanghafoori\LaravelMicroscope\Analyzers\ComposerJson; use Imanghafoori\LaravelMicroscope\FileReaders\FilePath; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\TokenAnalyzer\FunctionCall; use Throwable; @@ -72,7 +73,7 @@ private static function fullPath($calls, $providerClass, $path) private static function readLoadedRouteFiles($path) { - $tokens = token_get_all(file_get_contents(base_path($path).'.php')); + $tokens = PhpFileDescriptor::make(base_path($path).'.php')->getTokens(); foreach ($tokens as $i => $routeFileToken) { if (FunctionCall::isMethodCallOnThis('loadRoutesFrom', $tokens, $i)) { diff --git a/tests/CheckImports/CheckClassReferencesAreValidTest.php b/tests/CheckImports/CheckClassReferencesAreValidTest.php index ca012653..8c7449c9 100644 --- a/tests/CheckImports/CheckClassReferencesAreValidTest.php +++ b/tests/CheckImports/CheckClassReferencesAreValidTest.php @@ -3,6 +3,7 @@ namespace Imanghafoori\LaravelMicroscope\Tests\CheckImports; use Imanghafoori\LaravelMicroscope\Features\CheckImports\Checks\CheckClassReferencesAreValid; +use Imanghafoori\LaravelMicroscope\Foundations\PhpFileDescriptor; use Imanghafoori\LaravelMicroscope\Tests\CheckImports\MockExistenceChecker\AlwaysExistsMock; use Imanghafoori\TokenAnalyzer\ImportsAnalyzer; use Imanghafoori\TokenAnalyzer\ParseUseStatement; @@ -13,7 +14,8 @@ class CheckClassReferencesAreValidTest extends TestCase /** @test */ public function check() { - $absPath = __DIR__.'/wongImport.stub'; + $absPath = __DIR__.'/wrongImport.stub'; + $file = PhpFileDescriptor::make($absPath); $tokens = token_get_all(file_get_contents($absPath)); CheckClassReferencesAreValid::$extraCorrectImportsHandler = MockHandlers\MockExtraImportsHandler::class; CheckClassReferencesAreValid::$extraWrongImportsHandler = MockHandlers\MockerUnusedWrongImportsHandler::class; @@ -21,7 +23,7 @@ public function check() ImportsAnalyzer::$existenceChecker = AlwaysExistsMock::class; - CheckClassReferencesAreValid::check($tokens, $absPath, (function ($tokens) { + CheckClassReferencesAreValid::check($file, (function ($tokens) { $imports = ParseUseStatement::parseUseStatements($tokens); return $imports[0] ?: [$imports[1]]; @@ -37,18 +39,18 @@ public function check() 'doo' => ['doo', 5], 'Foooo' => ['Foooo', 6], ], - __DIR__.'/wongImport.stub', + __DIR__.'/wrongImport.stub', ], ], $extraImportHandler); $this->assertEquals([[ 0 => [], - 1 => __DIR__.'/wongImport.stub', + 1 => __DIR__.'/wrongImport.stub', ]], $unusedWrongImportsHandler); $this->assertEquals([[ 0 => [], - 1 => __DIR__.'/wongImport.stub', + 1 => __DIR__.'/wrongImport.stub', ]], $wrongClassRefsHandler); } } diff --git a/tests/CheckImports/wongImport.stub b/tests/CheckImports/wrongImport.stub similarity index 100% rename from tests/CheckImports/wongImport.stub rename to tests/CheckImports/wrongImport.stub