Base Application
This commit is contained in:
parent
0f646bba37
commit
9ac0fe07dd
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/vendor/
|
||||||
|
*.phar
|
17
.vscode/launch.json
vendored
Normal file
17
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Listen for Xdebug",
|
||||||
|
"type": "php",
|
||||||
|
"request": "launch",
|
||||||
|
"port": 9000,
|
||||||
|
"pathMappings": {
|
||||||
|
"/app": "${workspaceFolder}/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
21
bin/extension-check
Executable file
21
bin/extension-check
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
require __DIR__.'/../vendor/autoload.php';
|
||||||
|
|
||||||
|
use Fbrinker\ExtensionCheck\Command\CheckCommand;
|
||||||
|
use Laminas\ServiceManager\ServiceManager;
|
||||||
|
use Symfony\Component\Console\Application;
|
||||||
|
use Symfony\Component\Console\Input\ArgvInput;
|
||||||
|
|
||||||
|
$serviceManager = new ServiceManager(require("config/serviceManager.php"));
|
||||||
|
$application = new Application();
|
||||||
|
|
||||||
|
$command = $serviceManager->get(CheckCommand::class);
|
||||||
|
$application->add($command);
|
||||||
|
|
||||||
|
// Prepend the command as default command to accept arguments and options if necessary
|
||||||
|
// Workaround, since the Symfony $application->setDefaultCommand() can't accept arguments or options
|
||||||
|
if (!isset($argv[1]) || $argv[1] !== CheckCommand::getDefaultName()) {
|
||||||
|
$argv = array_merge([$argv[0], CheckCommand::getDefaultName()], array_slice($argv, 1));
|
||||||
|
}
|
||||||
|
$application->run(new ArgvInput($argv));
|
20
composer.json
Normal file
20
composer.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "fbrinker/extension-check",
|
||||||
|
"description": "Checks your code for usages of loaded PHP Extensions",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": [
|
||||||
|
"bin/extension-check"
|
||||||
|
],
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Fbrinker\\ExtensionCheck\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.2",
|
||||||
|
"nikic/php-parser": "^4.10",
|
||||||
|
"symfony/console": "^5.2",
|
||||||
|
"laminas/laminas-servicemanager": "^3.6",
|
||||||
|
"symfony/finder": "^5.2"
|
||||||
|
}
|
||||||
|
}
|
1174
composer.lock
generated
Normal file
1174
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
18
config/serviceManager.php
Normal file
18
config/serviceManager.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
use Fbrinker\ExtensionCheck\Command\CheckCommand;
|
||||||
|
use Fbrinker\ExtensionCheck\Command\CheckCommandFactory;
|
||||||
|
use Fbrinker\ExtensionCheck\Extension\ExtensionCheck;
|
||||||
|
use Fbrinker\ExtensionCheck\Extension\ExtensionCheckFactory;
|
||||||
|
use Fbrinker\ExtensionCheck\Extension\ExtensionDetails;
|
||||||
|
use Fbrinker\ExtensionCheck\Parser\FileParser;
|
||||||
|
use Fbrinker\ExtensionCheck\Parser\FileParserFactory;
|
||||||
|
use Laminas\ServiceManager\Factory\InvokableFactory;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'factories' => [
|
||||||
|
CheckCommand::class =>CheckCommandFactory::class,
|
||||||
|
ExtensionCheck::class => ExtensionCheckFactory::class,
|
||||||
|
ExtensionDetails::class => InvokableFactory::class,
|
||||||
|
FileParser::class => FileParserFactory::class,
|
||||||
|
],
|
||||||
|
];
|
11
docker-compose.yaml
Normal file
11
docker-compose.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
version: "3.7"
|
||||||
|
|
||||||
|
services:
|
||||||
|
php7.4:
|
||||||
|
build: docker/php7.4
|
||||||
|
container_name: extension-check-7.4
|
||||||
|
volumes:
|
||||||
|
- .:/app:rw
|
||||||
|
tty: true
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
18
docker/php7.4/Dockerfile
Normal file
18
docker/php7.4/Dockerfile
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
FROM php:7.4-alpine
|
||||||
|
|
||||||
|
COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/bin/
|
||||||
|
RUN install-php-extensions xdebug
|
||||||
|
|
||||||
|
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||||
|
ENV COMPOSER_ALLOW_SUPERUSER 1
|
||||||
|
|
||||||
|
RUN echo "xdebug.start_with_request=yes" >> /usr/local/etc/php/conf.d/xdebug.ini \
|
||||||
|
&& echo "xdebug.mode=debug" >> /usr/local/etc/php/conf.d/xdebug.ini \
|
||||||
|
&& echo "xdebug.log=/tmp/xdebug.log" >> /usr/local/etc/php/conf.d/xdebug.ini \
|
||||||
|
&& echo "xdebug.discover_client_host=1" >> /usr/local/etc/php/conf.d/xdebug.ini \
|
||||||
|
&& echo "xdebug.client_port=9000" >> /usr/local/etc/php/conf.d/xdebug.ini \
|
||||||
|
&& echo "xdebug.client_host=host.docker.internal" >> /usr/local/etc/php/conf.d/xdebug.ini
|
||||||
|
|
||||||
|
WORKDIR /docker
|
||||||
|
# Workaround to keep container running
|
||||||
|
CMD ["tail", "-f", "/dev/null"]
|
141
src/Command/CheckCommand.php
Normal file
141
src/Command/CheckCommand.php
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Fbrinker\ExtensionCheck\Command;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Fbrinker\ExtensionCheck\Extension\ExtensionCheck;
|
||||||
|
use Fbrinker\ExtensionCheck\Extension\ExtensionDetails;
|
||||||
|
use Fbrinker\ExtensionCheck\Parser\FileParser;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Helper\ProgressBar;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputDefinition;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
class CheckCommand extends Command
|
||||||
|
{
|
||||||
|
protected static $defaultName = 'check';
|
||||||
|
private $fileParser;
|
||||||
|
private $extensionDetails;
|
||||||
|
private $extensionCheck;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
$symfonyStyleFactory,
|
||||||
|
FileParser $fileParser,
|
||||||
|
ExtensionDetails $extensionDetails,
|
||||||
|
ExtensionCheck $extensionCheck
|
||||||
|
) {
|
||||||
|
parent::__construct();
|
||||||
|
$this->symfonyStyleFactory = $symfonyStyleFactory;
|
||||||
|
$this->fileParser = $fileParser;
|
||||||
|
$this->extensionDetails = $extensionDetails;
|
||||||
|
$this->extensionCheck = $extensionCheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure(): void
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->setDescription('Checks extensions...')
|
||||||
|
->setHelp('This command allows you to check your extensions...')
|
||||||
|
->setDefinition(
|
||||||
|
new InputDefinition([
|
||||||
|
new InputArgument('directory', InputArgument::OPTIONAL, "The directory to scan", "./"),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$io = ($this->symfonyStyleFactory)($input, $output);
|
||||||
|
|
||||||
|
$io->section("Analyzing Files");
|
||||||
|
$this->fileParser->scanForFiles($input->getArgument('directory'));
|
||||||
|
$fileCount = $this->fileParser->getFileCount();
|
||||||
|
$io->text(sprintf('Files: %d', $fileCount));
|
||||||
|
$io->newLine();
|
||||||
|
|
||||||
|
$progressBar = $this->getStyledProgressBar($output, $fileCount);
|
||||||
|
$progressBar->start();
|
||||||
|
|
||||||
|
[$classes, $functions, $constants] = $this->fileParser->parseFiles(
|
||||||
|
$this->getProgressBarClosure($progressBar)
|
||||||
|
);
|
||||||
|
|
||||||
|
$progressBar->finish();
|
||||||
|
$io->newLine(2);
|
||||||
|
$io->text(sprintf("Calls to check:", count($classes)));
|
||||||
|
$io->text(sprintf(
|
||||||
|
"%d Classes, %d Functions, %d Constants",
|
||||||
|
count($classes),
|
||||||
|
count($functions),
|
||||||
|
count($constants)
|
||||||
|
));
|
||||||
|
|
||||||
|
$io->section("Checking Extension Usages");
|
||||||
|
$io->text(sprintf('Loaded Extensions: %d', $this->extensionDetails->getLoadedExtensionsCount()));
|
||||||
|
$io->newLine();
|
||||||
|
$totalUsagesToCheck = count($classes) + count($functions);
|
||||||
|
$progressBar = $this->getStyledProgressBar($output, $totalUsagesToCheck);
|
||||||
|
$progressBar->start();
|
||||||
|
|
||||||
|
$usedExtensions = $this->extensionCheck->checkUsages(
|
||||||
|
$classes,
|
||||||
|
$functions,
|
||||||
|
$constants,
|
||||||
|
$this->getProgressBarClosure($progressBar)
|
||||||
|
);
|
||||||
|
|
||||||
|
$progressBar->finish();
|
||||||
|
$io->newLine(2);
|
||||||
|
|
||||||
|
$unusedExtensions = $this->extensionCheck->getUnused(array_keys($usedExtensions));
|
||||||
|
$io->text(sprintf(
|
||||||
|
"<fg=green>%d</> Used, <fg=red>%d</> Unused",
|
||||||
|
count($usedExtensions),
|
||||||
|
count($unusedExtensions),
|
||||||
|
));
|
||||||
|
|
||||||
|
$io->section("Result");
|
||||||
|
$io->text("Used Extensions:");
|
||||||
|
$tmp = array_keys($usedExtensions);
|
||||||
|
natcasesort($tmp);
|
||||||
|
foreach($tmp as $usedExtension) {
|
||||||
|
$reason = $usedExtensions[$usedExtension];
|
||||||
|
$io->text(sprintf(' <fg=green>%s</> %s <comment>[Usage: %s]</>',
|
||||||
|
"\u{2713}",
|
||||||
|
$usedExtension,
|
||||||
|
implode(", ", array_keys($reason))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$io->newLine();
|
||||||
|
|
||||||
|
$io->text("Unused Extensions:");
|
||||||
|
foreach($unusedExtensions as $unusedExtension) {
|
||||||
|
$io->text(sprintf(' <fg=red>%s</> %s',
|
||||||
|
"\u{2717}",
|
||||||
|
$unusedExtension
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getProgressBarClosure(&$progressBar): Closure {
|
||||||
|
return function() use ($progressBar) {
|
||||||
|
$progressBar->advance();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getStyledProgressBar(OutputInterface $output, int $maxValue): ProgressBar {
|
||||||
|
$progressBar = new ProgressBar($output, $maxValue);
|
||||||
|
$progressBar->setBarCharacter('<info>=</info>');
|
||||||
|
$progressBar->setEmptyBarCharacter(' ');
|
||||||
|
$progressBar->setProgressCharacter('>');
|
||||||
|
|
||||||
|
return $progressBar;
|
||||||
|
}
|
||||||
|
}
|
23
src/Command/CheckCommandFactory.php
Normal file
23
src/Command/CheckCommandFactory.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace Fbrinker\ExtensionCheck\Command;
|
||||||
|
|
||||||
|
use Fbrinker\ExtensionCheck\Extension\ExtensionCheck;
|
||||||
|
use Fbrinker\ExtensionCheck\Extension\ExtensionDetails;
|
||||||
|
use Fbrinker\ExtensionCheck\Parser\FileParser;
|
||||||
|
use Interop\Container\ContainerInterface;
|
||||||
|
use Laminas\ServiceManager\Factory\FactoryInterface;
|
||||||
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
|
||||||
|
class CheckCommandFactory implements FactoryInterface
|
||||||
|
{
|
||||||
|
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(FileParser::class),
|
||||||
|
$container->get(ExtensionDetails::class),
|
||||||
|
$container->get(ExtensionCheck::class),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
112
src/Extension/ExtensionCheck.php
Normal file
112
src/Extension/ExtensionCheck.php
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace Fbrinker\ExtensionCheck\Extension;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
|
||||||
|
class ExtensionCheck {
|
||||||
|
|
||||||
|
private $extensionDetails;
|
||||||
|
|
||||||
|
public function __construct(ExtensionDetails $extensionDetails)
|
||||||
|
{
|
||||||
|
$this->extensionDetails = $extensionDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUnused(array $usedExtensions) {
|
||||||
|
return array_diff(
|
||||||
|
$this->extensionDetails->getLoadedExtensions(),
|
||||||
|
$usedExtensions
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkUsages(array $classes, array $functions, array $constants, Closure $callback) {
|
||||||
|
$usedExtensions = [];
|
||||||
|
|
||||||
|
foreach($classes as $class) {
|
||||||
|
$extension = $this->checkClass($class);
|
||||||
|
|
||||||
|
if (!empty($extension)) {
|
||||||
|
$usedExtensions[$extension]['class'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($functions as $function) {
|
||||||
|
$extension = $this->checkFunction($function);
|
||||||
|
|
||||||
|
if (!empty($extension)) {
|
||||||
|
$usedExtensions[$extension]['function'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($constants as $constant) {
|
||||||
|
$extension = $this->checkConstant($constant);
|
||||||
|
|
||||||
|
if (!empty($extension)) {
|
||||||
|
$usedExtensions[$extension]['constant'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
$tmp = array_keys($usedExtensions);
|
||||||
|
foreach($tmp as $usedExtension) {
|
||||||
|
$requiredExtensions = $this->checkRequiredDependency($usedExtension);
|
||||||
|
|
||||||
|
if (!empty($requiredExtensions)) {
|
||||||
|
foreach($requiredExtensions as $requiredExtension) {
|
||||||
|
$usedExtensions[$requiredExtension]['dependency'] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $usedExtensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkClass($class): ?string {
|
||||||
|
$classMap = $this->extensionDetails->getExtensionClassMap();
|
||||||
|
|
||||||
|
if (!isset($classMap[$class])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $classMap[$class];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkFunction($function): ?string {
|
||||||
|
$functionMap = $this->extensionDetails->getExtensionFunctionMap();
|
||||||
|
|
||||||
|
if (!isset($functionMap[$function])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $functionMap[$function];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkConstant($function): ?string {
|
||||||
|
$constantMap = $this->extensionDetails->getExtensionConstantMap();
|
||||||
|
|
||||||
|
if (!isset($constantMap[$function])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $constantMap[$function];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkRequiredDependency($extension): array {
|
||||||
|
$dependencyMap = $this->extensionDetails->getExtensionDependencyMap();
|
||||||
|
|
||||||
|
if (!isset($dependencyMap[$extension]['required'])) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dependencyMap[$extension]['required'];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
16
src/Extension/ExtensionCheckFactory.php
Normal file
16
src/Extension/ExtensionCheckFactory.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace Fbrinker\ExtensionCheck\Extension;
|
||||||
|
|
||||||
|
use Interop\Container\ContainerInterface;
|
||||||
|
use Laminas\ServiceManager\Factory\FactoryInterface;
|
||||||
|
|
||||||
|
class ExtensionCheckFactory implements FactoryInterface
|
||||||
|
{
|
||||||
|
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
|
||||||
|
{
|
||||||
|
return new ExtensionCheck(
|
||||||
|
$container->get(ExtensionDetails::class)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
118
src/Extension/ExtensionDetails.php
Normal file
118
src/Extension/ExtensionDetails.php
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace Fbrinker\ExtensionCheck\Extension;
|
||||||
|
|
||||||
|
use ReflectionExtension;
|
||||||
|
|
||||||
|
class ExtensionDetails {
|
||||||
|
private $loadedExtensions;
|
||||||
|
private $extensionMap = [];
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->loadedExtensions = array_map('strtolower', get_loaded_extensions());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLoadedExtensions(): array {
|
||||||
|
natcasesort($this->loadedExtensions);
|
||||||
|
return $this->loadedExtensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLoadedExtensionsCount(): int {
|
||||||
|
return count($this->loadedExtensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExtensionClassMap(): array {
|
||||||
|
if (empty($this->extensionMap)) {
|
||||||
|
$this->buildExtensionMaps();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->extensionMap['classes'] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExtensionFunctionMap(): array {
|
||||||
|
if (empty($this->extensionMap)) {
|
||||||
|
$this->buildExtensionMaps();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->extensionMap['functions'] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExtensionConstantMap(): array {
|
||||||
|
if (empty($this->extensionMap)) {
|
||||||
|
$this->buildExtensionMaps();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->extensionMap['constants'] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExtensionDependencyMap(): array {
|
||||||
|
if (empty($this->extensionMap)) {
|
||||||
|
$this->buildExtensionMaps();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->extensionMap['dependencies'] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildExtensionMaps(array $extensionsToExclude = []) {
|
||||||
|
$extensionClasses = $extensionFunctions = $extensionConstants = $extensionDependencies = $uncheckedExtensions = [];
|
||||||
|
foreach ($this->loadedExtensions as $loadedExtension) {
|
||||||
|
if (in_array(strtolower($loadedExtension), array_map('strtolower', $extensionsToExclude))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$extension = new ReflectionExtension($loadedExtension);
|
||||||
|
|
||||||
|
$classes = $extension->getClasses();
|
||||||
|
if (!empty($classes)) {
|
||||||
|
natcasesort($classes);
|
||||||
|
foreach ($classes as $class) {
|
||||||
|
$extensionClasses[$class->getName()] = $loadedExtension;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$functions = $extension->getFunctions();
|
||||||
|
if (!empty($functions)) {
|
||||||
|
natcasesort($functions);
|
||||||
|
foreach ($functions as $function) {
|
||||||
|
$extensionFunctions[$function->getName()] = $loadedExtension;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$constants = $extension->getConstants();
|
||||||
|
if (!empty($constants)) {
|
||||||
|
natcasesort($constants);
|
||||||
|
foreach ($constants as $constant => $_) {
|
||||||
|
$extensionConstants[$constant] = $loadedExtension;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$dependencies = $extension->getDependencies();
|
||||||
|
if (!empty($dependencies)) {
|
||||||
|
natcasesort($dependencies);
|
||||||
|
foreach ($dependencies as $dependency => $status) {
|
||||||
|
if (!isset($extensionDependencies[$loadedExtension][strtolower($status)])) {
|
||||||
|
$extensionDependencies[$loadedExtension][strtolower($status)] = [];
|
||||||
|
}
|
||||||
|
$extensionDependencies[$loadedExtension][strtolower($status)][] = $dependency;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// $extension->getINIEntries()
|
||||||
|
// should we check the ini for configured extensions?
|
||||||
|
|
||||||
|
if (empty($classes) && empty($functions)) {
|
||||||
|
$uncheckedExtensions[] = $extension->getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->extensionMap = [
|
||||||
|
'classes' => $extensionClasses,
|
||||||
|
'functions' => $extensionFunctions,
|
||||||
|
'constants' => $extensionConstants,
|
||||||
|
'dependencies' => $extensionDependencies,
|
||||||
|
'unchecked' => $uncheckedExtensions
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
59
src/Parser/FileParser.php
Normal file
59
src/Parser/FileParser.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace Fbrinker\ExtensionCheck\Parser;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Fbrinker\ExtensionCheck\Parser\Visitors\ClassCollector;
|
||||||
|
use Fbrinker\ExtensionCheck\Parser\Visitors\ConstantCollector;
|
||||||
|
use Fbrinker\ExtensionCheck\Parser\Visitors\FunctionCollector;
|
||||||
|
use PhpParser\NodeTraverser;
|
||||||
|
use PhpParser\ParserFactory;
|
||||||
|
use Symfony\Component\Finder\Finder;
|
||||||
|
|
||||||
|
class FileParser {
|
||||||
|
private $finder;
|
||||||
|
|
||||||
|
public function __construct(Finder $finder)
|
||||||
|
{
|
||||||
|
$this->finder = $finder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scanForFiles(string $directory): void {
|
||||||
|
$this->finder->files()->name('*.php')->in($directory);
|
||||||
|
$this->finder->sortByName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFileCount(): int
|
||||||
|
{
|
||||||
|
return $this->finder->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parseFiles(Closure $callback) {
|
||||||
|
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
|
||||||
|
|
||||||
|
$traverser = new NodeTraverser();
|
||||||
|
|
||||||
|
$classVisitor = new ClassCollector();
|
||||||
|
$traverser->addVisitor($classVisitor);
|
||||||
|
|
||||||
|
$functionVisitor = new FunctionCollector();
|
||||||
|
$traverser->addVisitor($functionVisitor);
|
||||||
|
|
||||||
|
$constantCollector = new ConstantCollector();
|
||||||
|
$traverser->addVisitor($constantCollector);
|
||||||
|
|
||||||
|
foreach($this->finder as $file) {
|
||||||
|
$content = $file->getContents();
|
||||||
|
$stmts = $parser->parse($content);
|
||||||
|
|
||||||
|
$traverser->traverse($stmts);
|
||||||
|
$callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
$classVisitor->getCollected(),
|
||||||
|
$functionVisitor->getCollected(),
|
||||||
|
$constantCollector->getCollected(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
17
src/Parser/FileParserFactory.php
Normal file
17
src/Parser/FileParserFactory.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace Fbrinker\ExtensionCheck\Parser;
|
||||||
|
|
||||||
|
use Interop\Container\ContainerInterface;
|
||||||
|
use Laminas\ServiceManager\Factory\FactoryInterface;
|
||||||
|
use Symfony\Component\Finder\Finder;
|
||||||
|
|
||||||
|
class FileParserFactory implements FactoryInterface
|
||||||
|
{
|
||||||
|
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
|
||||||
|
{
|
||||||
|
return new FileParser(
|
||||||
|
new Finder()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
49
src/Parser/Visitors/ClassCollector.php
Normal file
49
src/Parser/Visitors/ClassCollector.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace Fbrinker\ExtensionCheck\Parser\Visitors;
|
||||||
|
|
||||||
|
use PhpParser\Node;
|
||||||
|
use PhpParser\NodeVisitorAbstract;
|
||||||
|
|
||||||
|
class ClassCollector extends NodeVisitorAbstract implements CollectorInferface {
|
||||||
|
private $classes = [];
|
||||||
|
|
||||||
|
public function getCollected(): array {
|
||||||
|
$list = array_keys($this->classes);
|
||||||
|
natcasesort($list);
|
||||||
|
|
||||||
|
return array_values($list);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
$this->classes[$implement->toString()] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node instanceof Node\Stmt\UseUse) {
|
||||||
|
if (!empty($node->name) && $node->name instanceof Node\Name) {
|
||||||
|
$this->classes[$node->name->toString()] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node instanceof Node\Expr\New_) {
|
||||||
|
if (!empty($node->class) && $node->class->name instanceof Node\Name) {
|
||||||
|
$this->classes[$node->class->toString()] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node instanceof Node\Expr\ClassConstFetch) {
|
||||||
|
if (!empty($node->class) && $node->class instanceof Node\Name) {
|
||||||
|
$this->classes[$node->class->toString()] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
src/Parser/Visitors/CollectorInferface.php
Normal file
7
src/Parser/Visitors/CollectorInferface.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace Fbrinker\ExtensionCheck\Parser\Visitors;
|
||||||
|
|
||||||
|
interface CollectorInferface {
|
||||||
|
public function getCollected(): array;
|
||||||
|
}
|
25
src/Parser/Visitors/ConstantCollector.php
Normal file
25
src/Parser/Visitors/ConstantCollector.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace Fbrinker\ExtensionCheck\Parser\Visitors;
|
||||||
|
|
||||||
|
use PhpParser\Node;
|
||||||
|
use PhpParser\NodeVisitorAbstract;
|
||||||
|
|
||||||
|
class ConstantCollector extends NodeVisitorAbstract implements CollectorInferface {
|
||||||
|
private $constants = [];
|
||||||
|
|
||||||
|
public function getCollected(): array {
|
||||||
|
$list = array_keys($this->constants);
|
||||||
|
natcasesort($list);
|
||||||
|
|
||||||
|
return array_values($list);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
src/Parser/Visitors/FunctionCollector.php
Normal file
25
src/Parser/Visitors/FunctionCollector.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace Fbrinker\ExtensionCheck\Parser\Visitors;
|
||||||
|
|
||||||
|
use PhpParser\Node;
|
||||||
|
use PhpParser\NodeVisitorAbstract;
|
||||||
|
|
||||||
|
class FunctionCollector extends NodeVisitorAbstract implements CollectorInferface {
|
||||||
|
private $functions = [];
|
||||||
|
|
||||||
|
public function getCollected(): array {
|
||||||
|
$list = array_keys($this->functions);
|
||||||
|
natcasesort($list);
|
||||||
|
|
||||||
|
return array_values($list);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user