Add PHPCS & PHPStan

This commit is contained in:
Florian Brinker 2021-05-09 00:50:26 +02:00
parent 3982e7aa95
commit 7f2305ec04
17 changed files with 313 additions and 82 deletions

4
.gitignore vendored
View File

@ -1,2 +1,4 @@
/tmp/
/vendor/ /vendor/
*.phar *.phar
cs-check.json

View File

@ -1,18 +1,21 @@
<?php <?php
use Fbrinker\ExtensionCheck\Command\CheckCommand; use Fbrinker\ExtensionCheck\Command\CheckCommand;
use Fbrinker\ExtensionCheck\Command\CheckCommandFactory; use Fbrinker\ExtensionCheck\Command\CheckCommandFactory;
use Fbrinker\ExtensionCheck\Extension\ExtensionCheck; use Fbrinker\ExtensionCheck\Extension\ExtensionCheck;
use Fbrinker\ExtensionCheck\Extension\ExtensionCheckFactory; use Fbrinker\ExtensionCheck\Extension\ExtensionCheckFactory;
use Fbrinker\ExtensionCheck\Extension\ExtensionDetails; use Fbrinker\ExtensionCheck\Extension\ExtensionDetails;
use Fbrinker\ExtensionCheck\Output\SymfonyStyleFactory;
use Fbrinker\ExtensionCheck\Parser\FileParser; use Fbrinker\ExtensionCheck\Parser\FileParser;
use Fbrinker\ExtensionCheck\Parser\FileParserFactory; use Fbrinker\ExtensionCheck\Parser\FileParserFactory;
use Laminas\ServiceManager\Factory\InvokableFactory; use Laminas\ServiceManager\Factory\InvokableFactory;
return [ return [
'factories' => [ 'factories' => [
CheckCommand::class =>CheckCommandFactory::class, CheckCommand::class => CheckCommandFactory::class,
ExtensionCheck::class => ExtensionCheckFactory::class, ExtensionCheck::class => ExtensionCheckFactory::class,
ExtensionDetails::class => InvokableFactory::class, ExtensionDetails::class => InvokableFactory::class,
FileParser::class => FileParserFactory::class, FileParser::class => FileParserFactory::class,
SymfonyStyleFactory::class => InvokableFactory::class,
], ],
]; ];

24
phpcs.xml Normal file
View File

@ -0,0 +1,24 @@
<?xml version="1.0"?>
<ruleset name="Skeleton coding standard">
<description>Skeleton coding standard</description>
<arg value="p"/>
<arg name="colors"/>
<arg name="cache" value="cs-check.json"/>
<arg name="parallel" value="8" />
<arg name="ignore" value="tests/assets"/>
<rule ref="PSR12">
<exclude name="Generic.Files.LineLength"/>
</rule>
<rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace">
<properties>
<property name="ignoreBlankLines" value="false"/>
</properties>
</rule>
<file>src</file>
<file>tests</file>
<file>config</file>
</ruleset>

9
phpstan.neon Normal file
View File

@ -0,0 +1,9 @@
parameters:
level: max
inferPrivatePropertyTypeFromConstructor: true
paths:
- src/
- tests/
excludes_analyse:
- %currentWorkingDirectory%/src/TEST
tmpDir: tmp/phpstan

View File

@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace Fbrinker\ExtensionCheck\Command; namespace Fbrinker\ExtensionCheck\Command;
@ -6,24 +7,27 @@ namespace Fbrinker\ExtensionCheck\Command;
use Closure; use Closure;
use Fbrinker\ExtensionCheck\Extension\ExtensionCheck; use Fbrinker\ExtensionCheck\Extension\ExtensionCheck;
use Fbrinker\ExtensionCheck\Extension\ExtensionDetails; use Fbrinker\ExtensionCheck\Extension\ExtensionDetails;
use Fbrinker\ExtensionCheck\Output\SymfonyStyleFactory;
use Fbrinker\ExtensionCheck\Parser\FileParser; use Fbrinker\ExtensionCheck\Parser\FileParser;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
class CheckCommand extends Command class CheckCommand extends Command
{ {
private const ARG_DIRECTORY = 'directory';
protected static $defaultName = 'check'; protected static $defaultName = 'check';
private $symfonyStyleFactory;
private $fileParser; private $fileParser;
private $extensionDetails; private $extensionDetails;
private $extensionCheck; private $extensionCheck;
public function __construct( public function __construct(
$symfonyStyleFactory, SymfonyStyleFactory $symfonyStyleFactory,
FileParser $fileParser, FileParser $fileParser,
ExtensionDetails $extensionDetails, ExtensionDetails $extensionDetails,
ExtensionCheck $extensionCheck ExtensionCheck $extensionCheck
@ -42,7 +46,7 @@ class CheckCommand extends Command
->setHelp('This command allows you to check your extensions...') ->setHelp('This command allows you to check your extensions...')
->setDefinition( ->setDefinition(
new InputDefinition([ 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 = ($this->symfonyStyleFactory)($input, $output);
$io->section("Analyzing Files"); $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(); $fileCount = $this->fileParser->getFileCount();
$io->text(sprintf('Files: %d', $fileCount)); $io->text(sprintf('Files: %d', $fileCount));
$io->newLine(); $io->newLine();
@ -66,7 +76,7 @@ class CheckCommand extends Command
$progressBar->finish(); $progressBar->finish();
$io->newLine(2); $io->newLine(2);
$io->text(sprintf("Calls to check:", count($classes))); $io->text("Calls to check:");
$io->text(sprintf( $io->text(sprintf(
"%d Classes, %d Functions, %d Constants", "%d Classes, %d Functions, %d Constants",
count($classes), count($classes),
@ -95,16 +105,17 @@ class CheckCommand extends Command
$io->text(sprintf( $io->text(sprintf(
"<fg=green>%d</> Used, <fg=red>%d</> Unused", "<fg=green>%d</> Used, <fg=red>%d</> Unused",
count($usedExtensions), count($usedExtensions),
count($unusedExtensions), count($unusedExtensions)
)); ));
$io->section("Result"); $io->section("Result");
$io->text("Used Extensions:"); $io->text("Used Extensions:");
$tmp = array_keys($usedExtensions); $tmp = array_keys($usedExtensions);
natcasesort($tmp); natcasesort($tmp);
foreach($tmp as $usedExtension) { foreach ($tmp as $usedExtension) {
$reason = $usedExtensions[$usedExtension]; $reason = $usedExtensions[$usedExtension];
$io->text(sprintf(' <fg=green>%s</> %s <comment>[Usage: %s]</>', $io->text(sprintf(
' <fg=green>%s</> %s <comment>[Usage: %s]</>',
"\u{2713}", "\u{2713}",
$usedExtension, $usedExtension,
implode(", ", array_keys($reason)) implode(", ", array_keys($reason))
@ -114,8 +125,9 @@ class CheckCommand extends Command
$io->newLine(); $io->newLine();
$io->text("Unused Extensions:"); $io->text("Unused Extensions:");
foreach($unusedExtensions as $unusedExtension) { foreach ($unusedExtensions as $unusedExtension) {
$io->text(sprintf(' <fg=red>%s</> %s', $io->text(sprintf(
' <fg=red>%s</> %s',
"\u{2717}", "\u{2717}",
$unusedExtension $unusedExtension
)); ));
@ -124,13 +136,15 @@ class CheckCommand extends Command
return Command::SUCCESS; return Command::SUCCESS;
} }
private function getProgressBarClosure(&$progressBar): Closure { private function getProgressBarClosure(ProgressBar $progressBar): Closure
return function() use ($progressBar) { {
return function () use ($progressBar) {
$progressBar->advance(); $progressBar->advance();
}; };
} }
private function getStyledProgressBar(OutputInterface $output, int $maxValue): ProgressBar { private function getStyledProgressBar(OutputInterface $output, int $maxValue): ProgressBar
{
$progressBar = new ProgressBar($output, $maxValue); $progressBar = new ProgressBar($output, $maxValue);
$progressBar->setBarCharacter('<info>=</info>'); $progressBar->setBarCharacter('<info>=</info>');
$progressBar->setEmptyBarCharacter(' '); $progressBar->setEmptyBarCharacter(' ');
@ -138,4 +152,4 @@ class CheckCommand extends Command
return $progressBar; return $progressBar;
} }
} }

View File

@ -1,23 +1,28 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace Fbrinker\ExtensionCheck\Command; namespace Fbrinker\ExtensionCheck\Command;
use Fbrinker\ExtensionCheck\Extension\ExtensionCheck; use Fbrinker\ExtensionCheck\Extension\ExtensionCheck;
use Fbrinker\ExtensionCheck\Extension\ExtensionDetails; use Fbrinker\ExtensionCheck\Extension\ExtensionDetails;
use Fbrinker\ExtensionCheck\Output\SymfonyStyleFactory;
use Fbrinker\ExtensionCheck\Parser\FileParser; use Fbrinker\ExtensionCheck\Parser\FileParser;
use Interop\Container\ContainerInterface; use Interop\Container\ContainerInterface;
use Laminas\ServiceManager\Factory\FactoryInterface; use Laminas\ServiceManager\Factory\FactoryInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class CheckCommandFactory implements FactoryInterface class CheckCommandFactory implements FactoryInterface
{ {
/**
* @param array<mixed> $options
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null) public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{ {
return new CheckCommand( 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(FileParser::class),
$container->get(ExtensionDetails::class), $container->get(ExtensionDetails::class),
$container->get(ExtensionCheck::class), $container->get(ExtensionCheck::class)
); );
} }
} }

View File

@ -1,10 +1,13 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace Fbrinker\ExtensionCheck\Extension; namespace Fbrinker\ExtensionCheck\Extension;
use Closure; use Closure;
class ExtensionCheck { class ExtensionCheck
{
private $extensionDetails; private $extensionDetails;
@ -13,17 +16,29 @@ class ExtensionCheck {
$this->extensionDetails = $extensionDetails; $this->extensionDetails = $extensionDetails;
} }
public function getUnused(array $usedExtensions) { /**
* @param string[] $usedExtensions
* @return string[]
*/
public function getUnused(array $usedExtensions): array
{
return array_diff( return array_diff(
$this->extensionDetails->getLoadedExtensions(), $this->extensionDetails->getLoadedExtensions(),
$usedExtensions $usedExtensions
); );
} }
public function checkUsages(array $classes, array $functions, array $constants, Closure $callback) { /**
* @param string[] $classes
* @param string[] $functions
* @param string[] $constants
* @return array<string,array<string,bool>>
*/
public function checkUsages(array $classes, array $functions, array $constants, Closure $callback)
{
$usedExtensions = []; $usedExtensions = [];
foreach($classes as $class) { foreach ($classes as $class) {
$extension = $this->checkClass($class); $extension = $this->checkClass($class);
if (!empty($extension)) { if (!empty($extension)) {
@ -33,7 +48,7 @@ class ExtensionCheck {
$callback(); $callback();
} }
foreach($functions as $function) { foreach ($functions as $function) {
$extension = $this->checkFunction($function); $extension = $this->checkFunction($function);
if (!empty($extension)) { if (!empty($extension)) {
@ -43,7 +58,7 @@ class ExtensionCheck {
$callback(); $callback();
} }
foreach($constants as $constant) { foreach ($constants as $constant) {
$extension = $this->checkConstant($constant); $extension = $this->checkConstant($constant);
if (!empty($extension)) { if (!empty($extension)) {
@ -54,11 +69,11 @@ class ExtensionCheck {
} }
$tmp = array_keys($usedExtensions); $tmp = array_keys($usedExtensions);
foreach($tmp as $usedExtension) { foreach ($tmp as $usedExtension) {
$requiredExtensions = $this->checkRequiredDependency($usedExtension); $requiredExtensions = $this->checkRequiredDependency($usedExtension);
if (!empty($requiredExtensions)) { if (!empty($requiredExtensions)) {
foreach($requiredExtensions as $requiredExtension) { foreach ($requiredExtensions as $requiredExtension) {
$usedExtensions[$requiredExtension]['dependency'] = true; $usedExtensions[$requiredExtension]['dependency'] = true;
} }
} }
@ -69,7 +84,8 @@ class ExtensionCheck {
return $usedExtensions; return $usedExtensions;
} }
private function checkClass($class): ?string { private function checkClass(string $class): ?string
{
$classMap = $this->extensionDetails->getExtensionClassMap(); $classMap = $this->extensionDetails->getExtensionClassMap();
if (!isset($classMap[$class])) { if (!isset($classMap[$class])) {
@ -79,7 +95,8 @@ class ExtensionCheck {
return $classMap[$class]; return $classMap[$class];
} }
private function checkFunction($function): ?string { private function checkFunction(string $function): ?string
{
$functionMap = $this->extensionDetails->getExtensionFunctionMap(); $functionMap = $this->extensionDetails->getExtensionFunctionMap();
if (!isset($functionMap[$function])) { if (!isset($functionMap[$function])) {
@ -89,7 +106,8 @@ class ExtensionCheck {
return $functionMap[$function]; return $functionMap[$function];
} }
private function checkConstant($function): ?string { private function checkConstant(string $function): ?string
{
$constantMap = $this->extensionDetails->getExtensionConstantMap(); $constantMap = $this->extensionDetails->getExtensionConstantMap();
if (!isset($constantMap[$function])) { if (!isset($constantMap[$function])) {
@ -99,7 +117,11 @@ class ExtensionCheck {
return $constantMap[$function]; return $constantMap[$function];
} }
private function checkRequiredDependency($extension): array { /**
* @return string[]
*/
private function checkRequiredDependency(string $extension): array
{
$dependencyMap = $this->extensionDetails->getExtensionDependencyMap(); $dependencyMap = $this->extensionDetails->getExtensionDependencyMap();
if (!isset($dependencyMap[$extension]['required'])) { if (!isset($dependencyMap[$extension]['required'])) {
@ -108,5 +130,4 @@ class ExtensionCheck {
return $dependencyMap[$extension]['required']; return $dependencyMap[$extension]['required'];
} }
}
}

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace Fbrinker\ExtensionCheck\Extension; namespace Fbrinker\ExtensionCheck\Extension;
use Interop\Container\ContainerInterface; use Interop\Container\ContainerInterface;
@ -7,10 +9,13 @@ use Laminas\ServiceManager\Factory\FactoryInterface;
class ExtensionCheckFactory implements FactoryInterface class ExtensionCheckFactory implements FactoryInterface
{ {
/**
* @param array<mixed> $options
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null) public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{ {
return new ExtensionCheck( return new ExtensionCheck(
$container->get(ExtensionDetails::class) $container->get(ExtensionDetails::class)
); );
} }
} }

View File

@ -1,60 +1,90 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace Fbrinker\ExtensionCheck\Extension; namespace Fbrinker\ExtensionCheck\Extension;
use ReflectionExtension; use ReflectionExtension;
class ExtensionDetails { class ExtensionDetails
{
private $loadedExtensions; private $loadedExtensions;
private $extensionMap = [];
/** @var ExtensionMap|null */
private $extensionMap;
public function __construct() public function __construct()
{ {
$this->loadedExtensions = array_map('strtolower', get_loaded_extensions()); $this->loadedExtensions = array_map('strtolower', get_loaded_extensions());
} }
public function getLoadedExtensions(): array { /**
* @return string[]
*/
public function getLoadedExtensions(): array
{
natcasesort($this->loadedExtensions); natcasesort($this->loadedExtensions);
return $this->loadedExtensions; return $this->loadedExtensions;
} }
public function getLoadedExtensionsCount(): int { public function getLoadedExtensionsCount(): int
{
return count($this->loadedExtensions); return count($this->loadedExtensions);
} }
public function getExtensionClassMap(): array { /**
* @return array<string,string>
*/
public function getExtensionClassMap(): array
{
if (empty($this->extensionMap)) { if (empty($this->extensionMap)) {
$this->buildExtensionMaps(); $this->buildExtensionMaps();
} }
return $this->extensionMap['classes'] ?? []; return $this->extensionMap->classes ?? [];
} }
public function getExtensionFunctionMap(): array { /**
* @return array<string,string>
*/
public function getExtensionFunctionMap(): array
{
if (empty($this->extensionMap)) { if (empty($this->extensionMap)) {
$this->buildExtensionMaps(); $this->buildExtensionMaps();
} }
return $this->extensionMap['functions'] ?? []; return $this->extensionMap->functions ?? [];
} }
public function getExtensionConstantMap(): array { /**
* @return array<string,string>
*/
public function getExtensionConstantMap(): array
{
if (empty($this->extensionMap)) { if (empty($this->extensionMap)) {
$this->buildExtensionMaps(); $this->buildExtensionMaps();
} }
return $this->extensionMap['constants'] ?? []; return $this->extensionMap->constants ?? [];
} }
public function getExtensionDependencyMap(): array { /**
* @return array<string,array<string,string[]>>
*/
public function getExtensionDependencyMap(): array
{
if (empty($this->extensionMap)) { if (empty($this->extensionMap)) {
$this->buildExtensionMaps(); $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 = []; $extensionClasses = $extensionFunctions = $extensionConstants = $extensionDependencies = $uncheckedExtensions = [];
foreach ($this->loadedExtensions as $loadedExtension) { foreach ($this->loadedExtensions as $loadedExtension) {
if (in_array(strtolower($loadedExtension), array_map('strtolower', $extensionsToExclude))) { if (in_array(strtolower($loadedExtension), array_map('strtolower', $extensionsToExclude))) {
@ -106,13 +136,12 @@ class ExtensionDetails {
} }
} }
$this->extensionMap = [ $this->extensionMap = new ExtensionMap(
'classes' => $extensionClasses, $extensionClasses,
'functions' => $extensionFunctions, $extensionFunctions,
'constants' => $extensionConstants, $extensionConstants,
'dependencies' => $extensionDependencies, $extensionDependencies,
'unchecked' => $uncheckedExtensions $uncheckedExtensions
]; );
} }
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace Fbrinker\ExtensionCheck\Extension;
class ExtensionMap
{
/** @var array<string,string> */
public $classes;
/** @var array<string,string> */
public $functions;
/** @var array<string,string> */
public $constants;
/** @var array<string,array<string,string[]>> */
public $dependencies;
/** @var string[] */
public $unchecked;
/**
* @param array<string,string> $classes
* @param array<string,string> $functions
* @param array<string,string> $constants
* @param array<string,array<string,string[]>> $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;
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace Fbrinker\ExtensionCheck\Output;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class SymfonyStyleFactory
{
public function __invoke(InputInterface $input, OutputInterface $output): SymfonyStyle
{
return new SymfonyStyle($input, $output);
}
}

View File

@ -1,16 +1,21 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace Fbrinker\ExtensionCheck\Parser; namespace Fbrinker\ExtensionCheck\Parser;
use Closure; use Closure;
use Error;
use Fbrinker\ExtensionCheck\Parser\Visitors\ClassCollector; use Fbrinker\ExtensionCheck\Parser\Visitors\ClassCollector;
use Fbrinker\ExtensionCheck\Parser\Visitors\ConstantCollector; use Fbrinker\ExtensionCheck\Parser\Visitors\ConstantCollector;
use Fbrinker\ExtensionCheck\Parser\Visitors\FunctionCollector; use Fbrinker\ExtensionCheck\Parser\Visitors\FunctionCollector;
use PhpParser\NodeTraverser; use PhpParser\NodeTraverser;
use PhpParser\ParserFactory; use PhpParser\ParserFactory;
use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\Finder;
use Throwable;
class FileParser { class FileParser
{
private $finder; private $finder;
public function __construct(Finder $finder) public function __construct(Finder $finder)
@ -18,7 +23,8 @@ class FileParser {
$this->finder = $finder; $this->finder = $finder;
} }
public function scanForFiles(string $directory): void { public function scanForFiles(string $directory): void
{
$this->finder->files()->name('*.php')->in($directory); $this->finder->files()->name('*.php')->in($directory);
$this->finder->sortByName(); $this->finder->sortByName();
} }
@ -28,8 +34,12 @@ class FileParser {
return $this->finder->count(); return $this->finder->count();
} }
public function parseFiles(Closure $callback) { /**
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); * @return array<int,string[]>
*/
public function parseFiles(Closure $callback): array
{
$parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
$traverser = new NodeTraverser(); $traverser = new NodeTraverser();
@ -42,9 +52,19 @@ class FileParser {
$constantCollector = new ConstantCollector(); $constantCollector = new ConstantCollector();
$traverser->addVisitor($constantCollector); $traverser->addVisitor($constantCollector);
foreach($this->finder as $file) { foreach ($this->finder as $file) {
$content = $file->getContents(); $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); $traverser->traverse($stmts);
$callback(); $callback();
@ -56,4 +76,4 @@ class FileParser {
$constantCollector->getCollected(), $constantCollector->getCollected(),
]; ];
} }
} }

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace Fbrinker\ExtensionCheck\Parser; namespace Fbrinker\ExtensionCheck\Parser;
use Interop\Container\ContainerInterface; use Interop\Container\ContainerInterface;
@ -8,10 +10,13 @@ use Symfony\Component\Finder\Finder;
class FileParserFactory implements FactoryInterface class FileParserFactory implements FactoryInterface
{ {
/**
* @param array<mixed> $options
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null) public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{ {
return new FileParser( return new FileParser(
new Finder() new Finder()
); );
} }
} }

View File

@ -1,28 +1,37 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace Fbrinker\ExtensionCheck\Parser\Visitors; namespace Fbrinker\ExtensionCheck\Parser\Visitors;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\NodeVisitorAbstract; use PhpParser\NodeVisitorAbstract;
class ClassCollector extends NodeVisitorAbstract implements CollectorInferface { class ClassCollector extends NodeVisitorAbstract implements CollectorInferface
{
/**
* @var array<string,bool> $classes
*/
private $classes = []; private $classes = [];
public function getCollected(): array { public function getCollected(): array
{
$list = array_keys($this->classes); $list = array_keys($this->classes);
natcasesort($list); natcasesort($list);
return array_values($list); return array_values($list);
} }
public function enterNode(Node $node) { public function enterNode(Node $node)
{
if ($node instanceof Node\Stmt\Class_) { if ($node instanceof Node\Stmt\Class_) {
if (!empty($node->extends)) { if (!empty($node->extends)) {
$this->classes[$node->extends->toString()] = true; $this->classes[$node->extends->toString()] = true;
} }
if (!empty($node->implements)) { if (!empty($node->implements)) {
foreach($node->implements as $implement) { foreach ($node->implements as $implement) {
$this->classes[$implement->toString()] = true; $this->classes[$implement->toString()] = true;
} }
} }
@ -35,7 +44,7 @@ class ClassCollector extends NodeVisitorAbstract implements CollectorInferface {
} }
if ($node instanceof Node\Expr\New_) { 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; $this->classes[$node->class->toString()] = true;
} }
} }
@ -45,5 +54,7 @@ class ClassCollector extends NodeVisitorAbstract implements CollectorInferface {
$this->classes[$node->class->toString()] = true; $this->classes[$node->class->toString()] = true;
} }
} }
return null;
} }
} }

View File

@ -1,7 +1,13 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace Fbrinker\ExtensionCheck\Parser\Visitors; namespace Fbrinker\ExtensionCheck\Parser\Visitors;
interface CollectorInferface { interface CollectorInferface
{
/**
* @return string[]
*/
public function getCollected(): array; public function getCollected(): array;
} }

View File

@ -1,25 +1,35 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace Fbrinker\ExtensionCheck\Parser\Visitors; namespace Fbrinker\ExtensionCheck\Parser\Visitors;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\NodeVisitorAbstract; use PhpParser\NodeVisitorAbstract;
class ConstantCollector extends NodeVisitorAbstract implements CollectorInferface { class ConstantCollector extends NodeVisitorAbstract implements CollectorInferface
{
/**
* @var array<string,bool> $constants
*/
private $constants = []; private $constants = [];
public function getCollected(): array { public function getCollected(): array
{
$list = array_keys($this->constants); $list = array_keys($this->constants);
natcasesort($list); natcasesort($list);
return array_values($list); return array_values($list);
} }
public function enterNode(Node $node) { public function enterNode(Node $node)
{
if ($node instanceof Node\Expr\ConstFetch) { if ($node instanceof Node\Expr\ConstFetch) {
if (!empty($node->name) && $node->name instanceof Node\Name) { if (!empty($node->name) && $node->name instanceof Node\Name) {
$this->constants[$node->name->toString()] = true; $this->constants[$node->name->toString()] = true;
} }
} }
return null;
} }
} }

View File

@ -1,25 +1,35 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace Fbrinker\ExtensionCheck\Parser\Visitors; namespace Fbrinker\ExtensionCheck\Parser\Visitors;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\NodeVisitorAbstract; use PhpParser\NodeVisitorAbstract;
class FunctionCollector extends NodeVisitorAbstract implements CollectorInferface { class FunctionCollector extends NodeVisitorAbstract implements CollectorInferface
{
/**
* @var array<string,bool> $functions
*/
private $functions = []; private $functions = [];
public function getCollected(): array { public function getCollected(): array
{
$list = array_keys($this->functions); $list = array_keys($this->functions);
natcasesort($list); natcasesort($list);
return array_values($list); return array_values($list);
} }
public function enterNode(Node $node) { public function enterNode(Node $node)
{
if ($node instanceof Node\Expr\FuncCall) { if ($node instanceof Node\Expr\FuncCall) {
if (!empty($node->name) && $node->name instanceof Node\Name) { if (!empty($node->name) && $node->name instanceof Node\Name) {
$this->functions[$node->name->toString()] = true; $this->functions[$node->name->toString()] = true;
} }
} }
return null;
} }
} }