From 9ac0fe07dd4937cd53a89811aa8e5f733b4f29f1 Mon Sep 17 00:00:00 2001 From: Florian Brinker Date: Sat, 8 May 2021 23:09:48 +0200 Subject: [PATCH] Base Application --- .gitignore | 2 + .vscode/launch.json | 17 + bin/extension-check | 21 + composer.json | 20 + composer.lock | 1174 ++++++++++++++++++++ config/serviceManager.php | 18 + docker-compose.yaml | 11 + docker/php7.4/Dockerfile | 18 + src/Command/CheckCommand.php | 141 +++ src/Command/CheckCommandFactory.php | 23 + src/Extension/ExtensionCheck.php | 112 ++ src/Extension/ExtensionCheckFactory.php | 16 + src/Extension/ExtensionDetails.php | 118 ++ src/Parser/FileParser.php | 59 + src/Parser/FileParserFactory.php | 17 + src/Parser/Visitors/ClassCollector.php | 49 + src/Parser/Visitors/CollectorInferface.php | 7 + src/Parser/Visitors/ConstantCollector.php | 25 + src/Parser/Visitors/FunctionCollector.php | 25 + 19 files changed, 1873 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100755 bin/extension-check create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 config/serviceManager.php create mode 100644 docker-compose.yaml create mode 100644 docker/php7.4/Dockerfile create mode 100644 src/Command/CheckCommand.php create mode 100644 src/Command/CheckCommandFactory.php create mode 100644 src/Extension/ExtensionCheck.php create mode 100644 src/Extension/ExtensionCheckFactory.php create mode 100644 src/Extension/ExtensionDetails.php create mode 100644 src/Parser/FileParser.php create mode 100644 src/Parser/FileParserFactory.php create mode 100644 src/Parser/Visitors/ClassCollector.php create mode 100644 src/Parser/Visitors/CollectorInferface.php create mode 100644 src/Parser/Visitors/ConstantCollector.php create mode 100644 src/Parser/Visitors/FunctionCollector.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6ee0aa6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/vendor/ +*.phar \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..0417424 --- /dev/null +++ b/.vscode/launch.json @@ -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}/" + } + } + ] +} \ No newline at end of file diff --git a/bin/extension-check b/bin/extension-check new file mode 100755 index 0000000..b6158d6 --- /dev/null +++ b/bin/extension-check @@ -0,0 +1,21 @@ +#!/usr/bin/env php +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)); \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..b87bb03 --- /dev/null +++ b/composer.json @@ -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" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..275b22e --- /dev/null +++ b/composer.lock @@ -0,0 +1,1174 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "4d0c01d1e550e999706292ad2896eaad", + "packages": [ + { + "name": "container-interop/container-interop", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/container-interop/container-interop.git", + "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8", + "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8", + "shasum": "" + }, + "require": { + "psr/container": "^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Interop\\Container\\": "src/Interop/Container/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", + "homepage": "https://github.com/container-interop/container-interop", + "support": { + "issues": "https://github.com/container-interop/container-interop/issues", + "source": "https://github.com/container-interop/container-interop/tree/master" + }, + "abandoned": "psr/container", + "time": "2017-02-14T19:40:03+00:00" + }, + { + "name": "laminas/laminas-servicemanager", + "version": "3.6.4", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-servicemanager.git", + "reference": "b1445e1a7077c21b0fad0974a1b7a11b9dbe0828" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/b1445e1a7077c21b0fad0974a1b7a11b9dbe0828", + "reference": "b1445e1a7077c21b0fad0974a1b7a11b9dbe0828", + "shasum": "" + }, + "require": { + "container-interop/container-interop": "^1.2", + "laminas/laminas-stdlib": "^3.2.1", + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^7.3 || ~8.0.0", + "psr/container": "^1.0" + }, + "conflict": { + "laminas/laminas-code": "<3.3.1", + "zendframework/zend-code": "<3.3.1" + }, + "provide": { + "container-interop/container-interop-implementation": "^1.2", + "psr/container-implementation": "^1.0" + }, + "replace": { + "zendframework/zend-servicemanager": "^3.4.0" + }, + "require-dev": { + "composer/package-versions-deprecated": "^1.0", + "laminas/laminas-coding-standard": "~1.0.0", + "laminas/laminas-container-config-test": "^0.3", + "laminas/laminas-dependency-plugin": "^2.1", + "mikey179/vfsstream": "^1.6.8", + "ocramius/proxy-manager": "^2.2.3", + "phpbench/phpbench": "^1.0.0-alpha3", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.4" + }, + "suggest": { + "ocramius/proxy-manager": "ProxyManager ^2.1.1 to handle lazy initialization of services" + }, + "bin": [ + "bin/generate-deps-for-config-factory", + "bin/generate-factory-for-class" + ], + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\ServiceManager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Factory-Driven Dependency Injection Container", + "homepage": "https://laminas.dev", + "keywords": [ + "PSR-11", + "dependency-injection", + "di", + "dic", + "laminas", + "service-manager", + "servicemanager" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-servicemanager/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-servicemanager/issues", + "rss": "https://github.com/laminas/laminas-servicemanager/releases.atom", + "source": "https://github.com/laminas/laminas-servicemanager" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2021-02-03T08:44:41+00:00" + }, + { + "name": "laminas/laminas-stdlib", + "version": "3.3.1", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-stdlib.git", + "reference": "d81c7ffe602ed0e6ecb18691019111c0f4bf1efe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/d81c7ffe602ed0e6ecb18691019111c0f4bf1efe", + "reference": "d81c7ffe602ed0e6ecb18691019111c0f4bf1efe", + "shasum": "" + }, + "require": { + "laminas/laminas-zendframework-bridge": "^1.0", + "php": "^7.3 || ^8.0" + }, + "replace": { + "zendframework/zend-stdlib": "^3.2.1" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~1.0.0", + "phpbench/phpbench": "^0.17.1", + "phpunit/phpunit": "~9.3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Stdlib\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "SPL extensions, array utilities, error handlers, and more", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "stdlib" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-stdlib/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-stdlib/issues", + "rss": "https://github.com/laminas/laminas-stdlib/releases.atom", + "source": "https://github.com/laminas/laminas-stdlib" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2020-11-19T20:18:59+00:00" + }, + { + "name": "laminas/laminas-zendframework-bridge", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-zendframework-bridge.git", + "reference": "6cccbddfcfc742eb02158d6137ca5687d92cee32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/6cccbddfcfc742eb02158d6137ca5687d92cee32", + "reference": "6cccbddfcfc742eb02158d6137ca5687d92cee32", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.1 || ^9.3", + "psalm/plugin-phpunit": "^0.15.1", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.6" + }, + "type": "library", + "extra": { + "laminas": { + "module": "Laminas\\ZendFrameworkBridge" + } + }, + "autoload": { + "files": [ + "src/autoload.php" + ], + "psr-4": { + "Laminas\\ZendFrameworkBridge\\": "src//" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Alias legacy ZF class names to Laminas Project equivalents.", + "keywords": [ + "ZendFramework", + "autoloading", + "laminas", + "zf" + ], + "support": { + "forum": "https://discourse.laminas.dev/", + "issues": "https://github.com/laminas/laminas-zendframework-bridge/issues", + "rss": "https://github.com/laminas/laminas-zendframework-bridge/releases.atom", + "source": "https://github.com/laminas/laminas-zendframework-bridge" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2021-02-25T21:54:58+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.10.5", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "4432ba399e47c66624bc73c8c0f811e5c109576f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4432ba399e47c66624bc73c8c0f811e5c109576f", + "reference": "4432ba399e47c66624bc73c8c0f811e5c109576f", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.5" + }, + "time": "2021-05-03T19:11:20+00:00" + }, + { + "name": "psr/container", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.1" + }, + "time": "2021-03-05T17:36:06+00:00" + }, + { + "name": "symfony/console", + "version": "v5.2.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "90374b8ed059325b49a29b55b3f8bb4062c87629" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/90374b8ed059325b49a29b55b3f8bb4062c87629", + "reference": "90374b8ed059325b49a29b55b3f8bb4062c87629", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.8", + "symfony/polyfill-php80": "^1.15", + "symfony/service-contracts": "^1.1|^2", + "symfony/string": "^5.1" + }, + "conflict": { + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "^4.4|^5.0", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/event-dispatcher": "^4.4|^5.0", + "symfony/lock": "^4.4|^5.0", + "symfony/process": "^4.4|^5.0", + "symfony/var-dumper": "^4.4|^5.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.2.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-04-19T14:07:32+00:00" + }, + { + "name": "symfony/finder", + "version": "v5.2.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "0d639a0943822626290d169965804f79400e6a04" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/0d639a0943822626290d169965804f79400e6a04", + "reference": "0d639a0943822626290d169965804f79400e6a04", + "shasum": "" + }, + "require": { + "php": ">=7.2.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v5.2.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-02-15T18:55:04+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "c6c942b1ac76c82448322025e084cadc56048b4e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e", + "reference": "c6c942b1ac76c82448322025e084cadc56048b4e", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-07T16:49:33+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5601e09b69f26c1828b13b6bb87cb07cddba3170", + "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.22.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-22T09:19:47+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248", + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-22T09:19:47+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1", + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-22T09:19:47+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", + "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-07T16:49:33+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91", + "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-07T16:49:33+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", + "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-04-01T10:43:52+00:00" + }, + { + "name": "symfony/string", + "version": "v5.2.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572", + "reference": "ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "~1.15" + }, + "require-dev": { + "symfony/error-handler": "^4.4|^5.0", + "symfony/http-client": "^4.4|^5.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "files": [ + "Resources/functions.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v5.2.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-03-17T17:12:15+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.2" + }, + "platform-dev": [], + "plugin-api-version": "2.0.0" +} diff --git a/config/serviceManager.php b/config/serviceManager.php new file mode 100644 index 0000000..6c6dea7 --- /dev/null +++ b/config/serviceManager.php @@ -0,0 +1,18 @@ + [ + CheckCommand::class =>CheckCommandFactory::class, + ExtensionCheck::class => ExtensionCheckFactory::class, + ExtensionDetails::class => InvokableFactory::class, + FileParser::class => FileParserFactory::class, + ], +]; \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..b456bd2 --- /dev/null +++ b/docker-compose.yaml @@ -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" \ No newline at end of file diff --git a/docker/php7.4/Dockerfile b/docker/php7.4/Dockerfile new file mode 100644 index 0000000..190df31 --- /dev/null +++ b/docker/php7.4/Dockerfile @@ -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"] \ No newline at end of file diff --git a/src/Command/CheckCommand.php b/src/Command/CheckCommand.php new file mode 100644 index 0000000..34ffa47 --- /dev/null +++ b/src/Command/CheckCommand.php @@ -0,0 +1,141 @@ +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( + "%d Used, %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(' %s %s [Usage: %s]', + "\u{2713}", + $usedExtension, + implode(", ", array_keys($reason)) + )); + } + + $io->newLine(); + + $io->text("Unused Extensions:"); + foreach($unusedExtensions as $unusedExtension) { + $io->text(sprintf(' %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('='); + $progressBar->setEmptyBarCharacter(' '); + $progressBar->setProgressCharacter('>'); + + return $progressBar; + } +} \ No newline at end of file diff --git a/src/Command/CheckCommandFactory.php b/src/Command/CheckCommandFactory.php new file mode 100644 index 0000000..e28e623 --- /dev/null +++ b/src/Command/CheckCommandFactory.php @@ -0,0 +1,23 @@ +get(FileParser::class), + $container->get(ExtensionDetails::class), + $container->get(ExtensionCheck::class), + ); + } +} \ No newline at end of file diff --git a/src/Extension/ExtensionCheck.php b/src/Extension/ExtensionCheck.php new file mode 100644 index 0000000..60eb61d --- /dev/null +++ b/src/Extension/ExtensionCheck.php @@ -0,0 +1,112 @@ +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']; + } + +} \ No newline at end of file diff --git a/src/Extension/ExtensionCheckFactory.php b/src/Extension/ExtensionCheckFactory.php new file mode 100644 index 0000000..33a6e89 --- /dev/null +++ b/src/Extension/ExtensionCheckFactory.php @@ -0,0 +1,16 @@ +get(ExtensionDetails::class) + ); + } +} \ No newline at end of file diff --git a/src/Extension/ExtensionDetails.php b/src/Extension/ExtensionDetails.php new file mode 100644 index 0000000..cc056b1 --- /dev/null +++ b/src/Extension/ExtensionDetails.php @@ -0,0 +1,118 @@ +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 + ]; + } + +} \ No newline at end of file diff --git a/src/Parser/FileParser.php b/src/Parser/FileParser.php new file mode 100644 index 0000000..81988bb --- /dev/null +++ b/src/Parser/FileParser.php @@ -0,0 +1,59 @@ +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(), + ]; + } +} \ No newline at end of file diff --git a/src/Parser/FileParserFactory.php b/src/Parser/FileParserFactory.php new file mode 100644 index 0000000..21113e5 --- /dev/null +++ b/src/Parser/FileParserFactory.php @@ -0,0 +1,17 @@ +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; + } + } + } +} \ No newline at end of file diff --git a/src/Parser/Visitors/CollectorInferface.php b/src/Parser/Visitors/CollectorInferface.php new file mode 100644 index 0000000..fb903b8 --- /dev/null +++ b/src/Parser/Visitors/CollectorInferface.php @@ -0,0 +1,7 @@ +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; + } + } + } +} \ No newline at end of file diff --git a/src/Parser/Visitors/FunctionCollector.php b/src/Parser/Visitors/FunctionCollector.php new file mode 100644 index 0000000..8d0c268 --- /dev/null +++ b/src/Parser/Visitors/FunctionCollector.php @@ -0,0 +1,25 @@ +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; + } + } + } +} \ No newline at end of file