diff --git a/.gitignore b/.gitignore index 6ee0aa6..37f4810 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ +/tmp/ /vendor/ -*.phar \ No newline at end of file +*.phar +cs-check.json \ No newline at end of file diff --git a/config/serviceManager.php b/config/serviceManager.php index 6c6dea7..ef6afed 100644 --- a/config/serviceManager.php +++ b/config/serviceManager.php @@ -1,18 +1,21 @@ [ - CheckCommand::class =>CheckCommandFactory::class, + CheckCommand::class => CheckCommandFactory::class, ExtensionCheck::class => ExtensionCheckFactory::class, ExtensionDetails::class => InvokableFactory::class, FileParser::class => FileParserFactory::class, + SymfonyStyleFactory::class => InvokableFactory::class, ], -]; \ No newline at end of file +]; diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..7749ee0 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,24 @@ + + + Skeleton coding standard + + + + + + + + + + + + + + + + + + src + tests + config + \ No newline at end of file diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..e51c96f --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,9 @@ +parameters: + level: max + inferPrivatePropertyTypeFromConstructor: true + paths: + - src/ + - tests/ + excludes_analyse: + - %currentWorkingDirectory%/src/TEST + tmpDir: tmp/phpstan \ No newline at end of file diff --git a/src/Command/CheckCommand.php b/src/Command/CheckCommand.php index 34ffa47..c312993 100644 --- a/src/Command/CheckCommand.php +++ b/src/Command/CheckCommand.php @@ -1,4 +1,5 @@ setHelp('This command allows you to check your extensions...') ->setDefinition( new InputDefinition([ - new InputArgument('directory', InputArgument::OPTIONAL, "The directory to scan", "./"), + new InputArgument(self::ARG_DIRECTORY, InputArgument::OPTIONAL, "The directory to scan", "./"), ]) ); } @@ -52,7 +56,13 @@ class CheckCommand extends Command $io = ($this->symfonyStyleFactory)($input, $output); $io->section("Analyzing Files"); - $this->fileParser->scanForFiles($input->getArgument('directory')); + + $scanDir = $input->getArgument(self::ARG_DIRECTORY); + if (empty($scanDir) || !\is_string($scanDir)) { + $scanDir = './'; + } + $this->fileParser->scanForFiles($scanDir); + $fileCount = $this->fileParser->getFileCount(); $io->text(sprintf('Files: %d', $fileCount)); $io->newLine(); @@ -66,7 +76,7 @@ class CheckCommand extends Command $progressBar->finish(); $io->newLine(2); - $io->text(sprintf("Calls to check:", count($classes))); + $io->text("Calls to check:"); $io->text(sprintf( "%d Classes, %d Functions, %d Constants", count($classes), @@ -95,16 +105,17 @@ class CheckCommand extends Command $io->text(sprintf( "%d Used, %d Unused", count($usedExtensions), - count($unusedExtensions), + count($unusedExtensions) )); $io->section("Result"); $io->text("Used Extensions:"); $tmp = array_keys($usedExtensions); natcasesort($tmp); - foreach($tmp as $usedExtension) { + foreach ($tmp as $usedExtension) { $reason = $usedExtensions[$usedExtension]; - $io->text(sprintf(' %s %s [Usage: %s]', + $io->text(sprintf( + ' %s %s [Usage: %s]', "\u{2713}", $usedExtension, implode(", ", array_keys($reason)) @@ -114,8 +125,9 @@ class CheckCommand extends Command $io->newLine(); $io->text("Unused Extensions:"); - foreach($unusedExtensions as $unusedExtension) { - $io->text(sprintf(' %s %s', + foreach ($unusedExtensions as $unusedExtension) { + $io->text(sprintf( + ' %s %s', "\u{2717}", $unusedExtension )); @@ -124,13 +136,15 @@ class CheckCommand extends Command return Command::SUCCESS; } - private function getProgressBarClosure(&$progressBar): Closure { - return function() use ($progressBar) { + private function getProgressBarClosure(ProgressBar $progressBar): Closure + { + return function () use ($progressBar) { $progressBar->advance(); }; } - private function getStyledProgressBar(OutputInterface $output, int $maxValue): ProgressBar { + private function getStyledProgressBar(OutputInterface $output, int $maxValue): ProgressBar + { $progressBar = new ProgressBar($output, $maxValue); $progressBar->setBarCharacter('='); $progressBar->setEmptyBarCharacter(' '); @@ -138,4 +152,4 @@ class CheckCommand extends Command return $progressBar; } -} \ No newline at end of file +} diff --git a/src/Command/CheckCommandFactory.php b/src/Command/CheckCommandFactory.php index e28e623..c072f79 100644 --- a/src/Command/CheckCommandFactory.php +++ b/src/Command/CheckCommandFactory.php @@ -1,23 +1,28 @@ $options + */ public function __invoke(ContainerInterface $container, $requestedName, array $options = null) { return new CheckCommand( - new class { public function __invoke($input, $output) { return new SymfonyStyle($input, $output); } }, + $container->get(SymfonyStyleFactory::class), $container->get(FileParser::class), $container->get(ExtensionDetails::class), - $container->get(ExtensionCheck::class), + $container->get(ExtensionCheck::class) ); } -} \ No newline at end of file +} diff --git a/src/Extension/ExtensionCheck.php b/src/Extension/ExtensionCheck.php index 60eb61d..b13a877 100644 --- a/src/Extension/ExtensionCheck.php +++ b/src/Extension/ExtensionCheck.php @@ -1,10 +1,13 @@ extensionDetails = $extensionDetails; } - public function getUnused(array $usedExtensions) { + /** + * @param string[] $usedExtensions + * @return string[] + */ + public function getUnused(array $usedExtensions): array + { return array_diff( $this->extensionDetails->getLoadedExtensions(), $usedExtensions ); } - public function checkUsages(array $classes, array $functions, array $constants, Closure $callback) { + /** + * @param string[] $classes + * @param string[] $functions + * @param string[] $constants + * @return array> + */ + public function checkUsages(array $classes, array $functions, array $constants, Closure $callback) + { $usedExtensions = []; - foreach($classes as $class) { + foreach ($classes as $class) { $extension = $this->checkClass($class); if (!empty($extension)) { @@ -33,7 +48,7 @@ class ExtensionCheck { $callback(); } - foreach($functions as $function) { + foreach ($functions as $function) { $extension = $this->checkFunction($function); if (!empty($extension)) { @@ -43,7 +58,7 @@ class ExtensionCheck { $callback(); } - foreach($constants as $constant) { + foreach ($constants as $constant) { $extension = $this->checkConstant($constant); if (!empty($extension)) { @@ -54,11 +69,11 @@ class ExtensionCheck { } $tmp = array_keys($usedExtensions); - foreach($tmp as $usedExtension) { + foreach ($tmp as $usedExtension) { $requiredExtensions = $this->checkRequiredDependency($usedExtension); if (!empty($requiredExtensions)) { - foreach($requiredExtensions as $requiredExtension) { + foreach ($requiredExtensions as $requiredExtension) { $usedExtensions[$requiredExtension]['dependency'] = true; } } @@ -69,7 +84,8 @@ class ExtensionCheck { return $usedExtensions; } - private function checkClass($class): ?string { + private function checkClass(string $class): ?string + { $classMap = $this->extensionDetails->getExtensionClassMap(); if (!isset($classMap[$class])) { @@ -79,7 +95,8 @@ class ExtensionCheck { return $classMap[$class]; } - private function checkFunction($function): ?string { + private function checkFunction(string $function): ?string + { $functionMap = $this->extensionDetails->getExtensionFunctionMap(); if (!isset($functionMap[$function])) { @@ -89,7 +106,8 @@ class ExtensionCheck { return $functionMap[$function]; } - private function checkConstant($function): ?string { + private function checkConstant(string $function): ?string + { $constantMap = $this->extensionDetails->getExtensionConstantMap(); if (!isset($constantMap[$function])) { @@ -99,7 +117,11 @@ class ExtensionCheck { return $constantMap[$function]; } - private function checkRequiredDependency($extension): array { + /** + * @return string[] + */ + private function checkRequiredDependency(string $extension): array + { $dependencyMap = $this->extensionDetails->getExtensionDependencyMap(); if (!isset($dependencyMap[$extension]['required'])) { @@ -108,5 +130,4 @@ class ExtensionCheck { return $dependencyMap[$extension]['required']; } - -} \ No newline at end of file +} diff --git a/src/Extension/ExtensionCheckFactory.php b/src/Extension/ExtensionCheckFactory.php index 33a6e89..20e7f8b 100644 --- a/src/Extension/ExtensionCheckFactory.php +++ b/src/Extension/ExtensionCheckFactory.php @@ -1,5 +1,7 @@ $options + */ public function __invoke(ContainerInterface $container, $requestedName, array $options = null) { return new ExtensionCheck( $container->get(ExtensionDetails::class) ); } -} \ No newline at end of file +} diff --git a/src/Extension/ExtensionDetails.php b/src/Extension/ExtensionDetails.php index cc056b1..b15e25f 100644 --- a/src/Extension/ExtensionDetails.php +++ b/src/Extension/ExtensionDetails.php @@ -1,60 +1,90 @@ loadedExtensions = array_map('strtolower', get_loaded_extensions()); } - public function getLoadedExtensions(): array { + /** + * @return string[] + */ + public function getLoadedExtensions(): array + { natcasesort($this->loadedExtensions); return $this->loadedExtensions; } - public function getLoadedExtensionsCount(): int { + public function getLoadedExtensionsCount(): int + { return count($this->loadedExtensions); } - public function getExtensionClassMap(): array { + /** + * @return array + */ + public function getExtensionClassMap(): array + { if (empty($this->extensionMap)) { $this->buildExtensionMaps(); } - return $this->extensionMap['classes'] ?? []; + return $this->extensionMap->classes ?? []; } - public function getExtensionFunctionMap(): array { + /** + * @return array + */ + public function getExtensionFunctionMap(): array + { if (empty($this->extensionMap)) { $this->buildExtensionMaps(); } - return $this->extensionMap['functions'] ?? []; + return $this->extensionMap->functions ?? []; } - public function getExtensionConstantMap(): array { + /** + * @return array + */ + public function getExtensionConstantMap(): array + { if (empty($this->extensionMap)) { $this->buildExtensionMaps(); } - return $this->extensionMap['constants'] ?? []; + return $this->extensionMap->constants ?? []; } - public function getExtensionDependencyMap(): array { + /** + * @return array> + */ + public function getExtensionDependencyMap(): array + { if (empty($this->extensionMap)) { $this->buildExtensionMaps(); } - return $this->extensionMap['dependencies'] ?? []; + return $this->extensionMap->dependencies ?? []; } - private function buildExtensionMaps(array $extensionsToExclude = []) { + /** + * @param string[] $extensionsToExclude + */ + private function buildExtensionMaps(array $extensionsToExclude = []): void + { $extensionClasses = $extensionFunctions = $extensionConstants = $extensionDependencies = $uncheckedExtensions = []; foreach ($this->loadedExtensions as $loadedExtension) { if (in_array(strtolower($loadedExtension), array_map('strtolower', $extensionsToExclude))) { @@ -106,13 +136,12 @@ class ExtensionDetails { } } - $this->extensionMap = [ - 'classes' => $extensionClasses, - 'functions' => $extensionFunctions, - 'constants' => $extensionConstants, - 'dependencies' => $extensionDependencies, - 'unchecked' => $uncheckedExtensions - ]; + $this->extensionMap = new ExtensionMap( + $extensionClasses, + $extensionFunctions, + $extensionConstants, + $extensionDependencies, + $uncheckedExtensions + ); } - -} \ No newline at end of file +} diff --git a/src/Extension/ExtensionMap.php b/src/Extension/ExtensionMap.php new file mode 100644 index 0000000..e7859d8 --- /dev/null +++ b/src/Extension/ExtensionMap.php @@ -0,0 +1,42 @@ + */ + public $classes; + + /** @var array */ + public $functions; + + /** @var array */ + public $constants; + + /** @var array> */ + public $dependencies; + + /** @var string[] */ + public $unchecked; + + /** + * @param array $classes + * @param array $functions + * @param array $constants + * @param array> $dependencies + * @param string[] $unchecked + */ + public function __construct( + array $classes = [], + array $functions = [], + array $constants = [], + array $dependencies = [], + array $unchecked = [] + ) { + $this->classes = $classes; + $this->functions = $functions; + $this->constants = $constants; + $this->dependencies = $dependencies; + $this->unchecked = $unchecked; + } +} diff --git a/src/Output/SymfonyStyleFactory.php b/src/Output/SymfonyStyleFactory.php new file mode 100644 index 0000000..a3c1650 --- /dev/null +++ b/src/Output/SymfonyStyleFactory.php @@ -0,0 +1,15 @@ +finder = $finder; } - public function scanForFiles(string $directory): void { + public function scanForFiles(string $directory): void + { $this->finder->files()->name('*.php')->in($directory); $this->finder->sortByName(); } @@ -28,8 +34,12 @@ class FileParser { return $this->finder->count(); } - public function parseFiles(Closure $callback) { - $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); + /** + * @return array + */ + public function parseFiles(Closure $callback): array + { + $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7); $traverser = new NodeTraverser(); @@ -42,9 +52,19 @@ class FileParser { $constantCollector = new ConstantCollector(); $traverser->addVisitor($constantCollector); - foreach($this->finder as $file) { + foreach ($this->finder as $file) { $content = $file->getContents(); - $stmts = $parser->parse($content); + $stmts = []; + + try { + $stmts = $parser->parse($content); + } catch (Throwable $e) { + echo $e->getMessage(); + } + + if (empty($stmts)) { + continue; + } $traverser->traverse($stmts); $callback(); @@ -56,4 +76,4 @@ class FileParser { $constantCollector->getCollected(), ]; } -} \ No newline at end of file +} diff --git a/src/Parser/FileParserFactory.php b/src/Parser/FileParserFactory.php index 21113e5..769ba32 100644 --- a/src/Parser/FileParserFactory.php +++ b/src/Parser/FileParserFactory.php @@ -1,5 +1,7 @@ $options + */ public function __invoke(ContainerInterface $container, $requestedName, array $options = null) { return new FileParser( new Finder() ); } -} \ No newline at end of file +} diff --git a/src/Parser/Visitors/ClassCollector.php b/src/Parser/Visitors/ClassCollector.php index 7890574..bd55242 100644 --- a/src/Parser/Visitors/ClassCollector.php +++ b/src/Parser/Visitors/ClassCollector.php @@ -1,28 +1,37 @@ $classes + */ private $classes = []; - public function getCollected(): array { + public function getCollected(): array + { $list = array_keys($this->classes); natcasesort($list); return array_values($list); } - public function enterNode(Node $node) { + public function enterNode(Node $node) + { if ($node instanceof Node\Stmt\Class_) { if (!empty($node->extends)) { $this->classes[$node->extends->toString()] = true; } if (!empty($node->implements)) { - foreach($node->implements as $implement) { + foreach ($node->implements as $implement) { $this->classes[$implement->toString()] = true; } } @@ -35,7 +44,7 @@ class ClassCollector extends NodeVisitorAbstract implements CollectorInferface { } if ($node instanceof Node\Expr\New_) { - if (!empty($node->class) && $node->class->name instanceof Node\Name) { + if (!empty($node->class) && $node->class instanceof Node\Name) { $this->classes[$node->class->toString()] = true; } } @@ -45,5 +54,7 @@ class ClassCollector extends NodeVisitorAbstract implements CollectorInferface { $this->classes[$node->class->toString()] = true; } } + + return null; } -} \ No newline at end of file +} diff --git a/src/Parser/Visitors/CollectorInferface.php b/src/Parser/Visitors/CollectorInferface.php index fb903b8..2d991e5 100644 --- a/src/Parser/Visitors/CollectorInferface.php +++ b/src/Parser/Visitors/CollectorInferface.php @@ -1,7 +1,13 @@ $constants + */ private $constants = []; - public function getCollected(): array { + public function getCollected(): array + { $list = array_keys($this->constants); natcasesort($list); return array_values($list); } - public function enterNode(Node $node) { + public function enterNode(Node $node) + { if ($node instanceof Node\Expr\ConstFetch) { if (!empty($node->name) && $node->name instanceof Node\Name) { $this->constants[$node->name->toString()] = true; } } + + return null; } -} \ No newline at end of file +} diff --git a/src/Parser/Visitors/FunctionCollector.php b/src/Parser/Visitors/FunctionCollector.php index 8d0c268..af0b75d 100644 --- a/src/Parser/Visitors/FunctionCollector.php +++ b/src/Parser/Visitors/FunctionCollector.php @@ -1,25 +1,35 @@ $functions + */ private $functions = []; - public function getCollected(): array { + public function getCollected(): array + { $list = array_keys($this->functions); natcasesort($list); return array_values($list); } - public function enterNode(Node $node) { + public function enterNode(Node $node) + { if ($node instanceof Node\Expr\FuncCall) { if (!empty($node->name) && $node->name instanceof Node\Name) { $this->functions[$node->name->toString()] = true; } } + + return null; } -} \ No newline at end of file +}