From 3946efd3fba086cfc2e043dc98761a3ce52abe0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20L=C3=A9pine?= Date: Tue, 1 Jul 2025 06:55:12 +0200 Subject: [PATCH 1/4] Updated tests in order to be executed in PHP 8.4, and supports Php 8.4 --- .github/FUNDING.yml | 3 + .github/workflows/ci.yml | 76 +++++++++++++++ .gitignore | 10 ++ .php-cs-fixer.php | 14 +++ Makefile | 10 +- artifacts/Makefile | 3 +- composer.json | 17 ++-- craft.yml | 6 ++ doc/contributing.md | 21 +++++ phpcs.xml.dist | 93 ------------------- phpunit.xml.dist | 16 +--- rector.php | 19 ++++ src/Hal/Application/Analyze.php | 39 +++++--- src/Hal/Application/Application.php | 13 ++- src/Hal/Application/Config/Config.php | 2 +- .../Application/Config/ConfigException.php | 6 +- .../Config/File/ConfigFileReaderJson.php | 2 +- src/Hal/Application/Config/Parser.php | 1 + src/Hal/Application/Config/Validator.php | 71 ++++++++------ src/Hal/Component/Ast/NodeTraverser.php | 9 +- src/Hal/Component/Ast/NodeTyper.php | 35 +++++++ src/Hal/Component/Ast/ParserFactoryBridge.php | 25 +++++ .../Ast/ParserTraverserVisitorsAssigner.php | 25 +++++ src/Hal/Component/Ast/Php5NodeTraverser.php | 1 + src/Hal/Component/Ast/Php7NodeTraverser.php | 22 ----- src/Hal/Component/Ast/Php8NodeTraverser.php | 14 +++ src/Hal/Component/Ast/Traverser.php | 2 +- src/Hal/Component/File/Finder.php | 1 - src/Hal/Component/Issue/Issuer.php | 44 +++++---- src/Hal/Component/Output/ProgressBar.php | 1 - src/Hal/Component/Output/TestOutput.php | 4 +- src/Hal/Component/Tree/GraphException.php | 4 +- src/Hal/Component/Tree/Node.php | 1 - src/Hal/Metric/BagTrait.php | 1 + src/Hal/Metric/ClassMetric.php | 1 + src/Hal/Metric/Class_/ClassEnumVisitor.php | 28 ++++-- .../CyclomaticComplexityVisitor.php | 12 +-- .../Class_/Complexity/KanDefectVisitor.php | 10 +- .../Component/MaintainabilityIndexVisitor.php | 14 ++- .../Class_/Coupling/ExternalsVisitor.php | 42 +++++---- .../Metric/Class_/Structural/LcomVisitor.php | 8 +- .../Structural/SystemComplexityVisitor.php | 11 ++- .../Metric/Class_/Text/HalsteadVisitor.php | 13 ++- src/Hal/Metric/Class_/Text/LengthVisitor.php | 16 +++- src/Hal/Metric/Consolidated.php | 9 +- src/Hal/Metric/FileMetric.php | 1 + src/Hal/Metric/FunctionMetric.php | 1 + .../Helper/MetricClassNameGenerator.php | 23 ----- .../Metric/Helper/RoleOfMethodDetector.php | 3 +- src/Hal/Metric/InterfaceMetric.php | 6 +- src/Hal/Metric/Metric.php | 1 + src/Hal/Metric/Metrics.php | 1 - .../Package/PackageCollectingVisitor.php | 6 +- src/Hal/Metric/Package/PackageDistance.php | 1 + src/Hal/Metric/Package/PackageInstability.php | 1 + src/Hal/Metric/PackageMetric.php | 6 +- src/Hal/Metric/ProjectMetric.php | 1 + src/Hal/Metric/Registry.php | 4 +- src/Hal/Metric/SearchMetric.php | 1 + src/Hal/Metric/System/Changes/GitChanges.php | 2 +- src/Hal/Metric/System/Coupling/Coupling.php | 1 - .../Coupling/DepthOfInheritanceTree.php | 1 - src/Hal/Metric/System/Coupling/PageRank.php | 1 - .../System/Packages/Composer/Composer.php | 19 ++-- .../System/Packages/Composer/Packagist.php | 8 +- .../Metric/System/UnitTesting/UnitTesting.php | 22 +++-- src/Hal/Report/Cli/Reporter.php | 2 +- src/Hal/Report/Cli/SearchReporter.php | 2 +- src/Hal/Report/Cli/SummaryWriter.php | 90 +++++++++--------- src/Hal/Report/Csv/Reporter.php | 1 + src/Hal/Report/Html/Reporter.php | 6 +- src/Hal/Report/Json/Reporter.php | 2 +- src/Hal/Report/Json/SummaryWriter.php | 2 +- src/Hal/Report/Violations/Xml/Reporter.php | 4 +- src/Hal/Search/Search.php | 8 +- src/Hal/Search/Searches.php | 1 - src/Hal/Search/SearchesValidator.php | 4 +- src/Hal/Violation/Class_/Blob.php | 12 +-- src/Hal/Violation/Class_/ProbablyBugged.php | 12 +-- .../Violation/Class_/TooComplexClassCode.php | 9 +- .../Violation/Class_/TooComplexMethodCode.php | 7 +- src/Hal/Violation/Class_/TooDependent.php | 8 +- src/Hal/Violation/Class_/TooLong.php | 10 +- .../Package/StableAbstractionsPrinciple.php | 6 +- .../Package/StableDependenciesPrinciple.php | 8 +- .../SearchShouldNotBeFoundPrinciple.php | 1 - src/Hal/Violation/Violation.php | 1 + src/Hal/Violation/ViolationParser.php | 3 +- src/Hal/Violation/Violations.php | 1 + src/functions.php | 30 ++++-- .../Config/File/ConfigFileReaderTest.php | 4 +- tests/Application/Config/ParserTest.php | 6 +- tests/Component/Ast/NodeTraverserTest.php | 2 +- tests/Component/Ast/SupportedVersionTest.php | 70 ++++++++++++++ .../Component/Ast/dataset/php7.0.coalesce.php | 9 ++ tests/Component/Ast/dataset/php7.1.void.php | 9 ++ .../Ast/dataset/php7.2.trailcomma.php | 2 + .../Component/Ast/dataset/php7.3.heredoc.php | 11 +++ .../Ast/dataset/php7.4.typedprop.php | 10 ++ .../Ast/dataset/php8.0.uniontype.php | 2 + .../Ast/dataset/php8.1.readonlyproperty.php | 11 +++ .../Component/Ast/dataset/php8.2.truetype.php | 8 ++ .../Ast/dataset/php8.3.jsonconst.php | 10 ++ .../Ast/dataset/php8.4.propertyhook.php | 13 +++ tests/Component/File/FinderTest.php | 4 +- tests/Component/Issuer/IssuerTest.php | 35 ++++--- .../Component/Tree/GraphDeduplicatedTest.php | 2 +- tests/Component/Tree/GraphTest.php | 26 ++---- tests/Component/Tree/HashMapTest.php | 4 +- tests/Component/Tree/NodeTest.php | 2 +- .../Tree/Operator/CycleDetectorTest.php | 8 +- .../Tree/Operator/SizeOfTreeTest.php | 14 ++- tests/Functions/GetNameOfNodeTest.php | 86 +++++++++++++++++ tests/Metric/Class_/ClassEnumVisitorTest.php | 21 +++-- .../CyclomaticComplexityVisitorTest.php | 42 ++++++--- .../Complexity/KanDefectVisitorTest.php | 19 ++-- .../SystemComplexityVisitorTest.php | 19 ++-- .../MaintainabilityIndexVisitorTest.php | 58 ++++++------ .../Class_/Coupling/ExternalsVisitorTest.php | 33 ++++--- .../Class_/Structural/LcomVisitorTest.php | 19 ++-- .../Class_/Text/HalsteadVisitorTest.php | 9 +- .../Metric/Class_/Text/LengthVisitorTest.php | 19 ++-- .../Helper/RoleOfMethodDetectorTest.php | 9 +- .../Metric/Package/PackageAbstractionTest.php | 2 +- .../Package/PackageCollectingVisitorTest.php | 18 ++-- .../Package/PackageDependenciesTest.php | 10 +- tests/Metric/Package/PackageDistanceTest.php | 7 +- .../Metric/Package/PackageInstabilityTest.php | 6 +- tests/Metric/PackageMetricTest.php | 28 +++--- .../System/UnitTesting/UnitTestingTest.php | 10 +- tests/PhpUnit/WithAnalyzer.php | 27 ++++++ tests/Polyfill/TestCaseCompatible.php | 24 +++++ tests/Polyfill/each.php | 38 ++++++++ tests/Polyfill/testcase.php | 8 ++ .../Html/ComplexityReportRegressionTest.php | 30 ++++-- tests/Report/Html/ReporterTest.php | 7 +- tests/Search/SearchFactoryTest.php | 2 +- tests/Search/SearchTest.php | 14 +-- tests/Violation/Class_/BlobTest.php | 6 +- .../StableAbstractionsPrincipleTest.php | 21 +++-- .../StableDependenciesPrincipleTest.php | 9 +- tests/binary/BinTest.php | 29 +++--- tests/binary/PharTest.php | 28 +++--- tests/binary/ReportTest.php | 31 ++++--- tests/binary/StandaloneTest.php | 51 ++++++++++ tests/bootstrap.php | 12 +++ tooling/README.md | 5 + tooling/composer.json | 18 ++++ 148 files changed, 1425 insertions(+), 727 deletions(-) create mode 100644 .github/FUNDING.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .php-cs-fixer.php create mode 100644 craft.yml delete mode 100644 phpcs.xml.dist create mode 100644 rector.php create mode 100644 src/Hal/Component/Ast/NodeTyper.php create mode 100644 src/Hal/Component/Ast/ParserFactoryBridge.php create mode 100644 src/Hal/Component/Ast/ParserTraverserVisitorsAssigner.php create mode 100644 src/Hal/Component/Ast/Php8NodeTraverser.php delete mode 100644 src/Hal/Metric/Helper/MetricClassNameGenerator.php create mode 100644 tests/Component/Ast/SupportedVersionTest.php create mode 100644 tests/Component/Ast/dataset/php7.0.coalesce.php create mode 100644 tests/Component/Ast/dataset/php7.1.void.php create mode 100644 tests/Component/Ast/dataset/php7.2.trailcomma.php create mode 100644 tests/Component/Ast/dataset/php7.3.heredoc.php create mode 100644 tests/Component/Ast/dataset/php7.4.typedprop.php create mode 100644 tests/Component/Ast/dataset/php8.0.uniontype.php create mode 100644 tests/Component/Ast/dataset/php8.1.readonlyproperty.php create mode 100644 tests/Component/Ast/dataset/php8.2.truetype.php create mode 100644 tests/Component/Ast/dataset/php8.3.jsonconst.php create mode 100644 tests/Component/Ast/dataset/php8.4.propertyhook.php create mode 100644 tests/Functions/GetNameOfNodeTest.php create mode 100644 tests/PhpUnit/WithAnalyzer.php create mode 100644 tests/Polyfill/TestCaseCompatible.php create mode 100644 tests/Polyfill/each.php create mode 100644 tests/Polyfill/testcase.php create mode 100644 tests/binary/StandaloneTest.php create mode 100644 tests/bootstrap.php create mode 100644 tooling/README.md create mode 100644 tooling/composer.json diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..821bcea0 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: [Halleck45] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..a59e6e7c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,76 @@ +name: CI + +on: + push: + branches: [ master, main ] + pull_request: + branches: [ master, main ] + release: + types: [created] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + php-version: [7.0, 7.1, 7.2, 7.3, 7.4, 8.0, 8.1, 8.2, 8.3, 8.4] + #php-version: [5.6, 7.0, 7.1, 7.2, 7.3, 7.4, 8.0, 8.1, 8.2, 8.3, 8.4] + include: + - php-version: nightly + fail-fast: false + + services: + docker: + image: docker:20.10.16 + options: --privileged + + steps: + - uses: actions/checkout@v4 + + - name: Set up PHP ${{ matrix.php-version }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + + - name: "Remove ': void' return type hint for older PHP versions" + run: | + # if php version < 7.1, remove ': void' return type hint + if [[ "${{ matrix.php-version }}" < "7.1" ]]; then + echo "Removing ': void' return type hint for PHP version ${{ matrix.php-version }}" + # Find and replace ': void' in all PHP files + find . -type f -name "*.php" -exec sed -i 's/: void//g' {} + + fi + + - name: Install Composer dependencies (${{ matrix.php-version }}) + run: composer install --no-interaction --prefer-dist --no-progress + + - name: Run tests (${{ matrix.php-version }}) + run: make test + + - name: Run Docker tests + run: docker run --rm test_phpmetrics + + build: + if: github.event_name == 'release' && startsWith(github.ref, 'refs/tags/') + needs: test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 7.0 + + - name: Build project + run: make build + + - name: Add build artifact + run: git add -f build/phpmetrics.phar + + - name: Upload release asset + uses: softprops/action-gh-release@v2 + with: + files: build/phpmetrics.phar + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 2799b96c..fb488c20 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ composer.lock vendor +tooling/vendor .idea md5sums control @@ -8,3 +9,12 @@ build/phpmetrics.dsc build/phpmetrics.deb build/phpmetrics.tar.gz build/phpmetrics.changes +.php-cs-fixer.cache +.phpunit.result.cache + +# build +buildroot/ +downloads/ +pkgroot/ +source/ +spc diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 00000000..779c7070 --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,14 @@ +in(__DIR__ . '/src') +; + +return (new PhpCsFixer\Config()) + ->setRules([ + '@PER-CS' => true, + '@PHP82Migration' => true, + ]) + ->setFinder($finder) + ->setUnsupportedPhpVersionAllowed(true) + ; diff --git a/Makefile b/Makefile index eeb073e8..25b695d1 100644 --- a/Makefile +++ b/Makefile @@ -2,17 +2,17 @@ include artifacts/Makefile -# Run unit tests +# Run unit tests test: ./vendor/bin/phpunit -c phpunit.xml.dist # Codesniffer check phpcs: - ./vendor/bin/phpcs src/ tests/ --extensions=php -n + ./tooling/vendor/bin/php-cs-fixer check src -# Codesniffer fix -phpcbf: - ./vendor/bin/phpcbf src/ tests/ --extensions=php -n +# Compatibility check +compatibility: + (docker run --rm -v `pwd`:/www --workdir=/www php:5.6-cli find src -iname "*.php" -exec php -l {} \; |grep -v "Php7NodeTraverser.php" | grep -v "No syntax errors detected") && echo OK # Used for tag releasing # Don't use directly, use `make release` instead diff --git a/artifacts/Makefile b/artifacts/Makefile index 32c41f3f..e763bf39 100644 --- a/artifacts/Makefile +++ b/artifacts/Makefile @@ -4,6 +4,7 @@ BUILD_DIR=releases include artifacts/phar/Makefile include artifacts/debian/Makefile +include artifacts/standalone/Makefile prepare-build: @# Disable the deletion of all releases packages as task build-debian is disabled. @@ -12,5 +13,5 @@ prepare-build: @# Only remove the phar that must be replaced by the new release. @rm -f ${BUILD_DIR}/phpmetrics.phar -build: prepare-build build-phar build-deb +build: prepare-build build-phar build-deb build-standalone diff --git a/composer.json b/composer.json index 475f1de7..edfdaebe 100644 --- a/composer.json +++ b/composer.json @@ -30,22 +30,23 @@ }, "files": ["./src/functions.php"] }, + "autoload-dev": { + "psr-4": { + "Test\\Hal\\": "tests/" + } + }, "require": { - "php": ">=5.5", "ext-dom": "*", "ext-tokenizer": "*", - "nikic/php-parser": "^3|^4" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14", - "sebastian/comparator": ">=1.2.3", - "squizlabs/php_codesniffer": "^3.5", - "symfony/dom-crawler": "^3.0 || ^4.0 || ^5.0" + "nikic/php-parser": "^3|^4|^5" }, "bin": [ "bin/phpmetrics" ], "config": { "sort-packages": true + }, + "require-dev": { + "phpunit/phpunit": "*" } } diff --git a/craft.yml b/craft.yml new file mode 100644 index 00000000..8f3ca668 --- /dev/null +++ b/craft.yml @@ -0,0 +1,6 @@ +php-version: 8.4 +extensions: "apcu,phar,curl,dom,fileinfo,filter,intl,mbstring,mysqlnd,openssl,tokenizer,zlib" +sapi: micro +build-options: + with-upx-pack: true + prefer-pre-built: true diff --git a/doc/contributing.md b/doc/contributing.md index 918470fa..be51ba63 100644 --- a/doc/contributing.md +++ b/doc/contributing.md @@ -11,6 +11,27 @@ Then, in order to run the test suite: Thanks for your help. +## Why the code is so old? + +### Philosophy + +PhpMetrics has several goals: ++ be stable ++ be performant ++ run on the **maximum of PHP versions** (PHP 5.3 to PHP 8.4) ++ be installable everywhere, **without dependency conflicts** + +For these reasons, the code of PhpMetrics is intentionally written in "legacy" PHP. + +### Dependencies + +Not all projects are on the latest version of PHP, Symfony, or Laravel. Yes, there are projects that use Symfony 2. And these projects may also need metrics and quality. + +For these reasons, PhpMetrics comes with the minimum of dependencies. Only the dependency on `nikic/php-parser` is accepted. + +No Pull Request that modifies the `require` block will be accepted. + + ## Releasing Please NEVER tag manually. diff --git a/phpcs.xml.dist b/phpcs.xml.dist deleted file mode 100644 index 28f5b8a4..00000000 --- a/phpcs.xml.dist +++ /dev/null @@ -1,93 +0,0 @@ - - - The coding standard of PhpMetrics package - - - - - - - - - - */tests/*/examples/*.php - */src/functions.php - - - - */tests/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 10 - - - 10 - - - 10 - - - - - diff --git a/phpunit.xml.dist b/phpunit.xml.dist index de4ebec4..ec987918 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,15 +1,11 @@ - @@ -23,10 +19,4 @@ binary - - - - ./src - - diff --git a/rector.php b/rector.php new file mode 100644 index 00000000..c9a4ae89 --- /dev/null +++ b/rector.php @@ -0,0 +1,19 @@ +withPaths([ + __DIR__ . '/src', + __DIR__ . '/tests', + ]) + ->withSets([ + \Rector\PHPUnit\Set\PHPUnitSetList::PHPUNIT_110, + ]) + ->withRules([ + \Rector\TypeDeclaration\Rector\Class_\AddTestsVoidReturnTypeWhereNoReturnRector::class, + \Rector\PHPUnit\CodeQuality\Rector\Class_\AddParentSetupCallOnSetupRector::class, + \Rector\PHPUnit\PHPUnit60\Rector\ClassMethod\ExceptionAnnotationRector::class, + \Rector\PHPUnit\PHPUnit100\Rector\StmtsAwareInterface\WithConsecutiveRector::class, + \Rector\PHPUnit\PHPUnit100\Rector\Class_\StaticDataProviderClassMethodRector::class, + ]); diff --git a/src/Hal/Application/Analyze.php b/src/Hal/Application/Analyze.php index eec78e2c..8fa01219 100644 --- a/src/Hal/Application/Analyze.php +++ b/src/Hal/Application/Analyze.php @@ -1,8 +1,11 @@ create(ParserFactory::PREFER_PHP7); - $traverser = new NodeTraverser(false, $whenToStop); - $traverser->addVisitor(new \PhpParser\NodeVisitor\NameResolver()); - $traverser->addVisitor(new ClassEnumVisitor($metrics)); - $traverser->addVisitor(new CyclomaticComplexityVisitor($metrics)); - $traverser->addVisitor(new ExternalsVisitor($metrics)); - $traverser->addVisitor(new LcomVisitor($metrics)); - $traverser->addVisitor(new HalsteadVisitor($metrics)); - $traverser->addVisitor(new LengthVisitor($metrics)); - $traverser->addVisitor(new CyclomaticComplexityVisitor($metrics)); - $traverser->addVisitor(new MaintainabilityIndexVisitor($metrics)); - $traverser->addVisitor(new KanDefectVisitor($metrics)); - $traverser->addVisitor(new SystemComplexityVisitor($metrics)); - $traverser->addVisitor(new PackageCollectingVisitor($metrics)); + $parser = (new ParserFactoryBridge())->create(); + $traverser = new NodeTraverser(); + + (new ParserTraverserVisitorsAssigner())->assign( + $traverser, + [ + new NameResolver(), + new ClassEnumVisitor($metrics), + new CyclomaticComplexityVisitor($metrics), + new ExternalsVisitor($metrics), + new LcomVisitor($metrics), + new HalsteadVisitor($metrics), + new LengthVisitor($metrics), + new CyclomaticComplexityVisitor($metrics), + new MaintainabilityIndexVisitor($metrics), + new KanDefectVisitor($metrics), + new SystemComplexityVisitor($metrics), + new PackageCollectingVisitor($metrics) + ] + ); // create a new progress bar (50 units) $progress = new ProgressBar($this->output, count($files)); diff --git a/src/Hal/Application/Application.php b/src/Hal/Application/Application.php index cc9544f1..5b755fe7 100644 --- a/src/Hal/Application/Application.php +++ b/src/Hal/Application/Application.php @@ -17,7 +17,6 @@ class Application { - /** * @param array $argv */ @@ -46,8 +45,10 @@ public function run(array $argv) // Version if ($config->has('version')) { - $output->writeln(sprintf("PhpMetrics %s \nby Jean-François Lépine \n", - getVersion())); + $output->writeln(sprintf( + "PhpMetrics %s \nby Jean-François Lépine \n", + getVersion() + )); exit(0); } @@ -113,8 +114,10 @@ public function run(array $argv) } if (!empty($shouldExitDueToCriticalViolationsCount)) { $output->writeln(''); - $output->writeln(sprintf('[ERR] Failed du to %d critical violations', - $shouldExitDueToCriticalViolationsCount)); + $output->writeln(sprintf( + '[ERR] Failed du to %d critical violations', + $shouldExitDueToCriticalViolationsCount + )); $output->writeln(''); exit(1); } diff --git a/src/Hal/Application/Config/Config.php b/src/Hal/Application/Config/Config.php index 9dc0cf28..ab4d991d 100644 --- a/src/Hal/Application/Config/Config.php +++ b/src/Hal/Application/Config/Config.php @@ -1,9 +1,9 @@ set('composer', (bool)$jsonData['composer']); + $config->set('composer', (bool) $jsonData['composer']); } // Search diff --git a/src/Hal/Application/Config/Parser.php b/src/Hal/Application/Config/Parser.php index 2e675366..7cec93d2 100644 --- a/src/Hal/Application/Config/Parser.php +++ b/src/Hal/Application/Config/Parser.php @@ -1,4 +1,5 @@ has('exclude')) { - $config->set('exclude', - 'vendor,test,Test,tests,Tests,testing,Testing,bower_components,node_modules,cache,spec'); + $config->set( + 'exclude', + 'vendor,test,Test,tests,Tests,testing,Testing,bower_components,node_modules,cache,spec' + ); } // retro-compatibility with excludes as string in config files @@ -61,7 +63,18 @@ public function validate(Config $config) if (!$config->has('composer')) { $config->set('composer', true); } - $config->set('composer', filter_var($config->get('composer'), FILTER_VALIDATE_BOOLEAN)); + + if (function_exists('filter_var')) { + $config->set('composer', filter_var($config->get('composer'), FILTER_VALIDATE_BOOLEAN)); + } else { + // When PHP is not compiled with the filter extension, we need to do it manually + $bool = $config->get('composer'); + if( is_string($bool) ) { + $bool = strtolower($bool); + $bool = in_array($bool, ['true', '1', 'yes', 'on'], true); + } + $config->set('composer', (bool) $bool); + } // Search $validator = new SearchesValidator(); @@ -86,50 +99,48 @@ public function validate(Config $config) public function help() { return << + phpmetrics [...options...] -Required: + Required: - List of directories to parse, separated by a comma (,) + List of directories to parse, separated by a comma (,) -Optional: + Optional: - --config= Use a file for configuration. File can be a JSON, YAML or INI file. - --exclude= List of directories to exclude, separated by a comma (,) - --extensions= List of extensions to parse, separated by a comma (,) - --metrics Display list of available metrics - --report-html= Folder where report HTML will be generated - --report-csv= File where report CSV will be generated - --report-json= File where report Json will be generated - --report-summary-json= File where the summary report Json will be generated - --report-violations= File where XML violations report will be generated - --git[=] Perform analyses based on Git History (default binary path: "git") - --junit[=] Evaluates metrics according to JUnit logs - --quiet Quiet mode - --version Display current version + --config= Use a file for configuration. File can be a JSON, YAML or INI file. + --exclude= List of directories to exclude, separated by a comma (,) + --extensions= List of extensions to parse, separated by a comma (,) + --metrics Display list of available metrics + --report-html= Folder where report HTML will be generated + --report-csv= File where report CSV will be generated + --report-json= File where report Json will be generated + --report-summary-json= File where the summary report Json will be generated + --report-violations= File where XML violations report will be generated + --git[=] Perform analyses based on Git History (default binary path: "git") + --junit[=] Evaluates metrics according to JUnit logs + --quiet Quiet mode + --version Display current version -Examples: + Examples: - phpmetrics --report-html="./report" ./src + phpmetrics --report-html="./report" ./src - Analyze the "./src" directory and generate a HTML report on the "./report" folder + Analyze the "./src" directory and generate a HTML report on the "./report" folder - phpmetrics --report-violations="./build/violations.xml" ./src,./lib - - Analyze the "./src" and "./lib" directories, and generate the "./build/violations.xml" file. This file could - be read by any Continuous Integration Platform, and follows the "PMD Violation" standards. + phpmetrics --report-violations="./build/violations.xml" ./src,./lib + Analyze the "./src" and "./lib" directories, and generate the "./build/violations.xml" file. This file could + be read by any Continuous Integration Platform, and follows the "PMD Violation" standards. EOT; } public function metrics() { $help = << * @@ -8,7 +9,9 @@ namespace Hal\Component\Ast; -if (PHP_VERSION_ID >= 70000) { +if (PHP_VERSION_ID >= 80000) { + class_alias(Php8NodeTraverser::class, __NAMESPACE__ . '\\ActualNodeTraverser'); +}elseif (PHP_VERSION_ID >= 70000) { class_alias(Php7NodeTraverser::class, __NAMESPACE__ . '\\ActualNodeTraverser'); } else { class_alias(Php5NodeTraverser::class, __NAMESPACE__ . '\\ActualNodeTraverser'); @@ -21,6 +24,4 @@ class_alias(Php5NodeTraverser::class, __NAMESPACE__ . '\\ActualNodeTraverser'); * @see https://github.com/phpmetrics/PhpMetrics/issues/373 */ /** @noinspection PhpUndefinedClassInspection */ -class NodeTraverser extends ActualNodeTraverser -{ -} +class NodeTraverser extends ActualNodeTraverser {} diff --git a/src/Hal/Component/Ast/NodeTyper.php b/src/Hal/Component/Ast/NodeTyper.php new file mode 100644 index 00000000..5a046eae --- /dev/null +++ b/src/Hal/Component/Ast/NodeTyper.php @@ -0,0 +1,35 @@ +create($kind); + } + + if ($kind !== null) { + return (new \PhpParser\ParserFactory())->createForVersion($kind); + } + + return (new \PhpParser\ParserFactory())->createForNewestSupportedVersion(); + } +} diff --git a/src/Hal/Component/Ast/ParserTraverserVisitorsAssigner.php b/src/Hal/Component/Ast/ParserTraverserVisitorsAssigner.php new file mode 100644 index 00000000..61811e10 --- /dev/null +++ b/src/Hal/Component/Ast/ParserTraverserVisitorsAssigner.php @@ -0,0 +1,25 @@ += v5, visitors are traversed in LIFO order. + // With nikic/php-parser < v5, visitors are traversed in FIFO order. + + if (! method_exists('PhpParser\ParserFactory', 'create')) { + $visitors = array_reverse($visitors); + } + + foreach ($visitors as $visitor) { + $traverser->addVisitor($visitor); + } + } +} diff --git a/src/Hal/Component/Ast/Php5NodeTraverser.php b/src/Hal/Component/Ast/Php5NodeTraverser.php index c2ac9420..29e9df60 100644 --- a/src/Hal/Component/Ast/Php5NodeTraverser.php +++ b/src/Hal/Component/Ast/Php5NodeTraverser.php @@ -1,4 +1,5 @@ traverser = new Traverser($this, $stopCondition); - } - - public function traverseNode(Node $node): Node - { - return parent::traverseNode($node); - } - - protected function traverseArray(array $nodes): array - { - return $this->traverser->traverseArray($nodes, $this->visitors); - } } diff --git a/src/Hal/Component/Ast/Php8NodeTraverser.php b/src/Hal/Component/Ast/Php8NodeTraverser.php new file mode 100644 index 00000000..bb8753f7 --- /dev/null +++ b/src/Hal/Component/Ast/Php8NodeTraverser.php @@ -0,0 +1,14 @@ +enterNode($node); if (Mother::DONT_TRAVERSE_CHILDREN === $return) { $traverseChildren = false; - } else if (null !== $return) { + } elseif (null !== $return) { $node = $return; } } diff --git a/src/Hal/Component/File/Finder.php b/src/Hal/Component/File/Finder.php index 5ae3ce03..6269b6ae 100644 --- a/src/Hal/Component/File/Finder.php +++ b/src/Hal/Component/File/Finder.php @@ -18,7 +18,6 @@ */ class Finder { - /** * Follow symlinks */ diff --git a/src/Hal/Component/Issue/Issuer.php b/src/Hal/Component/Issue/Issuer.php index 58c802e1..a8c54ad0 100644 --- a/src/Hal/Component/Issue/Issuer.php +++ b/src/Hal/Component/Issue/Issuer.php @@ -72,43 +72,41 @@ public function onError($errno, $errstr, $errfile, $errline) $message = <<We're sorry : an unexpected error occured. - -Can you help us ? Please open a new issue at https://github.com/phpmetrics/PhpMetrics/issues/new, and copy-paste the content -of this file: $logfile ? + We're sorry : an unexpected error occured. -Thanks for your help :) + Can you help us ? Please open a new issue at https://github.com/phpmetrics/PhpMetrics/issues/new, and copy-paste the content + of this file: $logfile ? + Thanks for your help :) EOT; $log = << - Details - ``` -$trace +
+ Details + ``` + $trace -$debug -``` -
- + $debug + ``` + EOT; $this->output->write($message); diff --git a/src/Hal/Component/Output/ProgressBar.php b/src/Hal/Component/Output/ProgressBar.php index c5715d75..8ffeb510 100644 --- a/src/Hal/Component/Output/ProgressBar.php +++ b/src/Hal/Component/Output/ProgressBar.php @@ -14,7 +14,6 @@ */ class ProgressBar { - /** * @var Output */ diff --git a/src/Hal/Component/Output/TestOutput.php b/src/Hal/Component/Output/TestOutput.php index 35700a52..f5b70ab2 100644 --- a/src/Hal/Component/Output/TestOutput.php +++ b/src/Hal/Component/Output/TestOutput.php @@ -47,9 +47,7 @@ public function err($message) /** * @inheritdoc */ - public function clearln() - { - } + public function clearln() {} /** * @inheritdoc diff --git a/src/Hal/Component/Tree/GraphException.php b/src/Hal/Component/Tree/GraphException.php index 4682071c..428d3202 100644 --- a/src/Hal/Component/Tree/GraphException.php +++ b/src/Hal/Component/Tree/GraphException.php @@ -9,6 +9,4 @@ namespace Hal\Component\Tree; -class GraphException extends \LogicException -{ -} +class GraphException extends \LogicException {} diff --git a/src/Hal/Component/Tree/Node.php b/src/Hal/Component/Tree/Node.php index f472a944..48f01025 100644 --- a/src/Hal/Component/Tree/Node.php +++ b/src/Hal/Component/Tree/Node.php @@ -11,7 +11,6 @@ class Node { - /** * @var mixed */ diff --git a/src/Hal/Metric/BagTrait.php b/src/Hal/Metric/BagTrait.php index c7440816..0a2d792f 100644 --- a/src/Hal/Metric/BagTrait.php +++ b/src/Hal/Metric/BagTrait.php @@ -1,4 +1,5 @@ namespacedName->toString()); $class->set('interface', true); $class->set('abstract', true); } else { - $name = (string)(isset($node->namespacedName) ? $node->namespacedName : 'anonymous@' . spl_object_hash($node)); + $name = getNameOfNode($node); $class = new ClassMetric($name); $class->set('interface', false); - $class->set('abstract', $node instanceof Stmt\Trait_ || $node->isAbstract()); - $class->set('final', !$node instanceof Stmt\Trait_ && $node->isFinal()); + + $isAbstract = false; + if ($node instanceof Stmt\Class_) { + $isAbstract = $node->isAbstract(); + } elseif ($node instanceof Stmt\Trait_) { + $isAbstract = true; // Traits are always abstract + } + + $isFinal = false; + if ($node instanceof Stmt\Class_) { + $isFinal = $node->isFinal(); + } elseif ($node instanceof Stmt\Trait_) { + $isFinal = false; // Traits cannot be final + } + + $class->set('abstract', $isAbstract); + $class->set('final', $isFinal); } $methods = []; diff --git a/src/Hal/Metric/Class_/Complexity/CyclomaticComplexityVisitor.php b/src/Hal/Metric/Class_/Complexity/CyclomaticComplexityVisitor.php index 52a19588..70aafcee 100644 --- a/src/Hal/Metric/Class_/Complexity/CyclomaticComplexityVisitor.php +++ b/src/Hal/Metric/Class_/Complexity/CyclomaticComplexityVisitor.php @@ -2,7 +2,7 @@ namespace Hal\Metric\Class_\Complexity; -use Hal\Metric\Helper\MetricClassNameGenerator; +use Hal\Component\Ast\NodeTyper; use Hal\Metric\Helper\RoleOfMethodDetector; use Hal\Metric\Metrics; use PhpParser\Node; @@ -53,11 +53,11 @@ public function __construct(Metrics $metrics) public function leaveNode(Node $node) { - if ($node instanceof Stmt\Class_ - || $node instanceof Stmt\Interface_ - || $node instanceof Stmt\Trait_ - ) { - $class = $this->metrics->get(MetricClassNameGenerator::getName($node)); + if (NodeTyper::isOrganizedStructure($node)) { + $class = $this->metrics->get(getNameOfNode($node)); + if ($class === null) { + throw new \RuntimeException('Class metric not found for ' . getNameOfNode($node)); + } $ccn = 1; $wmc = 0; diff --git a/src/Hal/Metric/Class_/Complexity/KanDefectVisitor.php b/src/Hal/Metric/Class_/Complexity/KanDefectVisitor.php index b2492260..bb055e75 100644 --- a/src/Hal/Metric/Class_/Complexity/KanDefectVisitor.php +++ b/src/Hal/Metric/Class_/Complexity/KanDefectVisitor.php @@ -1,8 +1,9 @@ metrics->get(MetricClassNameGenerator::getName($node)); + if (NodeTyper::isOrganizedStructure($node)) { + $class = $this->metrics->get(getNameOfNode($node)); $select = $while = $if = 0; diff --git a/src/Hal/Metric/Class_/Component/MaintainabilityIndexVisitor.php b/src/Hal/Metric/Class_/Component/MaintainabilityIndexVisitor.php index 3d33a4d9..cca79094 100644 --- a/src/Hal/Metric/Class_/Component/MaintainabilityIndexVisitor.php +++ b/src/Hal/Metric/Class_/Component/MaintainabilityIndexVisitor.php @@ -1,6 +1,8 @@ namespacedName) ? $node->namespacedName : 'anonymous@' . spl_object_hash($node)); + if (NodeTyper::isOrganizedLogicalClassStructure($node)) { + $name = getNameOfNode($node); $classOrFunction = $this->metrics->get($name); + if(null === $classOrFunction) { + throw new \LogicException('class or function ' . $name . ' not found in metrics'); + } + if (null === $lloc = $classOrFunction->get('lloc')) { throw new \LogicException('please enable length (lloc) visitor first'); } @@ -68,7 +73,8 @@ public function leaveNode(Node $node) // maintainability index without comment $MIwoC = max( - (171 + ( + 171 - (5.2 * \log($volume)) - (0.23 * $ccn) - (16.2 * \log($lloc)) diff --git a/src/Hal/Metric/Class_/Coupling/ExternalsVisitor.php b/src/Hal/Metric/Class_/Coupling/ExternalsVisitor.php index 86acf15c..be3870c4 100644 --- a/src/Hal/Metric/Class_/Coupling/ExternalsVisitor.php +++ b/src/Hal/Metric/Class_/Coupling/ExternalsVisitor.php @@ -1,7 +1,8 @@ uses = array_merge($this->uses, $node->uses); } - if ($node instanceof Stmt\Class_ - || $node instanceof Stmt\Interface_ - || $node instanceof Stmt\Trait_ - ) { - $class = $this->metrics->get(MetricClassNameGenerator::getName($node)); + if (NodeTyper::isOrganizedStructure($node)) { + $class = $this->metrics->get(getNameOfNode($node)); + + if(null === $class) { + throw new \RuntimeException('Class metric not found for ' . getNameOfNode($node)); + } + $parents = []; $interfaces = []; @@ -59,21 +61,21 @@ public function leaveNode(Node $node) // extends if (isset($node->extends)) { if (is_array($node->extends)) { - foreach ((array)$node->extends as $interface) { - $this->pushToDependencies($dependencies, (string)$interface); - array_push($parents, (string)$interface); + foreach ((array) $node->extends as $interface) { + $this->pushToDependencies($dependencies, (string) $interface); + array_push($parents, (string) $interface); } } else { - $this->pushToDependencies($dependencies, (string)$node->extends); - array_push($parents, (string)$node->extends); + $this->pushToDependencies($dependencies, (string) $node->extends); + array_push($parents, (string) $node->extends); } } // implements if (isset($node->implements)) { foreach ($node->implements as $interface) { - $this->pushToDependencies($dependencies, (string)$interface); - array_push($interfaces, (string)$interface); + $this->pushToDependencies($dependencies, (string) $interface); + array_push($interfaces, (string) $interface); } } @@ -82,7 +84,7 @@ public function leaveNode(Node $node) // return if (isset($stmt->returnType)) { if ($stmt->returnType instanceof Node\Name\FullyQualified) { - $this->pushToDependencies($dependencies, (string)$stmt->returnType); + $this->pushToDependencies($dependencies, (string) $stmt->returnType); } } @@ -90,7 +92,7 @@ public function leaveNode(Node $node) foreach ($stmt->params as $param) { if ($param->type) { if ($param->type instanceof Node\Name\FullyQualified) { - $this->pushToDependencies($dependencies, (string)$param->type); + $this->pushToDependencies($dependencies, (string) $param->type); } } } @@ -115,13 +117,13 @@ public function leaveNode(Node $node) foreach ($matches[1] as $check) { foreach ($this->uses as $use) { if (method_exists($use, 'getAlias')) { - if (((string)$use->getAlias()) === $check) { - $this->pushToDependencies($dependencies, (string)($use->name)); + if (((string) $use->getAlias()) === $check) { + $this->pushToDependencies($dependencies, (string) ($use->name)); } continue; } if ($use->alias === $check) { - $this->pushToDependencies($dependencies, (string)($use->name)); + $this->pushToDependencies($dependencies, (string) ($use->name)); } } } @@ -141,6 +143,6 @@ private function pushToDependencies(array &$dependencies, $dependency) if ('self' === $lowercase || 'parent' === $lowercase) { return; } - array_push($dependencies, (string)$dependency); + array_push($dependencies, (string) $dependency); } } diff --git a/src/Hal/Metric/Class_/Structural/LcomVisitor.php b/src/Hal/Metric/Class_/Structural/LcomVisitor.php index c537ca7b..4c93565b 100644 --- a/src/Hal/Metric/Class_/Structural/LcomVisitor.php +++ b/src/Hal/Metric/Class_/Structural/LcomVisitor.php @@ -1,9 +1,10 @@ metrics->get(MetricClassNameGenerator::getName($node)); + $class = $this->metrics->get(getNameOfNode($node)); $roleDetector = new RoleOfMethodDetector(); diff --git a/src/Hal/Metric/Class_/Structural/SystemComplexityVisitor.php b/src/Hal/Metric/Class_/Structural/SystemComplexityVisitor.php index e7aae6de..e4ccd95b 100644 --- a/src/Hal/Metric/Class_/Structural/SystemComplexityVisitor.php +++ b/src/Hal/Metric/Class_/Structural/SystemComplexityVisitor.php @@ -1,7 +1,8 @@ metrics->get(MetricClassNameGenerator::getName($node)); + if (NodeTyper::isOrganizedLogicalClassStructure($node)) { + $class = $this->metrics->get(getNameOfNode($node)); + if (null === $class) { + throw new \RuntimeException('Class metric not found for ' . getNameOfNode($node)); + } $sy = $dc = $sc = []; diff --git a/src/Hal/Metric/Class_/Text/HalsteadVisitor.php b/src/Hal/Metric/Class_/Text/HalsteadVisitor.php index f620f7a1..6e7d793b 100644 --- a/src/Hal/Metric/Class_/Text/HalsteadVisitor.php +++ b/src/Hal/Metric/Class_/Text/HalsteadVisitor.php @@ -2,6 +2,7 @@ namespace Hal\Metric\Class_\Text; +use Hal\Component\Ast\NodeTyper; use Hal\Metric\FunctionMetric; use Hal\Metric\Metrics; use Hoa\Ruler\Model\Bag\Scalar; @@ -23,7 +24,6 @@ */ class HalsteadVisitor extends NodeVisitorAbstract { - /** * @var Metrics */ @@ -42,12 +42,15 @@ public function __construct(Metrics $metrics) */ public function leaveNode(Node $node) { - if ($node instanceof Stmt\Class_ || $node instanceof Stmt\Function_ || $node instanceof Stmt\Trait_) { - if ($node instanceof Stmt\Class_ || $node instanceof Stmt\Trait_) { - $name = (string)(isset($node->namespacedName) ? $node->namespacedName : 'anonymous@' . spl_object_hash($node)); + if ( + NodeTyper::isOrganizedLogicalClassStructure($node) + || $node instanceof Stmt\Function_ + ) { + if (NodeTyper::isOrganizedLogicalClassStructure($node) ) { + $name = getNameOfNode($node); $classOrFunction = $this->metrics->get($name); } else { - $classOrFunction = new FunctionMetric((string)$node->name); + $classOrFunction = new FunctionMetric((string) $node->name); $this->metrics->attach($classOrFunction); } diff --git a/src/Hal/Metric/Class_/Text/LengthVisitor.php b/src/Hal/Metric/Class_/Text/LengthVisitor.php index e69c41f8..0fa26bf6 100644 --- a/src/Hal/Metric/Class_/Text/LengthVisitor.php +++ b/src/Hal/Metric/Class_/Text/LengthVisitor.php @@ -1,6 +1,8 @@ namespacedName) ? $node->namespacedName : 'anonymous@' . spl_object_hash($node)); + if ( + NodeTyper::isOrganizedLogicalClassStructure($node) + || $node instanceof Stmt\Function_ + ) { + if ( + NodeTyper::isOrganizedLogicalClassStructure($node) + ) { + $name = getNameOfNode($node); $classOrFunction = $this->metrics->get($name); } else { - $classOrFunction = new FunctionMetric((string)$node->name); + $classOrFunction = new FunctionMetric((string) $node->name); $this->metrics->attach($classOrFunction); } diff --git a/src/Hal/Metric/Consolidated.php b/src/Hal/Metric/Consolidated.php index df3e9bd7..02b92c48 100644 --- a/src/Hal/Metric/Consolidated.php +++ b/src/Hal/Metric/Consolidated.php @@ -1,4 +1,5 @@ 0, 'cloc' => 0, 'lloc' => 0, 'nbMethods' => 0, ]; - $avg = (object)[ + $avg = (object) [ 'wmc' => [], 'ccn' => [], 'bugs' => [], @@ -107,7 +108,7 @@ public function __construct(Metrics $metrics) foreach ($avg as &$a) { if (count($a) > 0) { - $a = round(array_sum((array)$a) / count($a), 2); + $a = round(array_sum((array) $a) / count($a), 2); } else { $a = 0; } @@ -166,7 +167,7 @@ public function __construct(Metrics $metrics) $violations[$name]++; } } - $sum->violations = (object)$violations; + $sum->violations = (object) $violations; $this->avg = $avg; $this->sum = $sum; diff --git a/src/Hal/Metric/FileMetric.php b/src/Hal/Metric/FileMetric.php index c4414910..67494477 100644 --- a/src/Hal/Metric/FileMetric.php +++ b/src/Hal/Metric/FileMetric.php @@ -1,4 +1,5 @@ isAnonymous()) ? - 'anonymous@' . spl_object_hash($node) : - $node->namespacedName->toString(); - } -} diff --git a/src/Hal/Metric/Helper/RoleOfMethodDetector.php b/src/Hal/Metric/Helper/RoleOfMethodDetector.php index ed9633f4..10e00949 100644 --- a/src/Hal/Metric/Helper/RoleOfMethodDetector.php +++ b/src/Hal/Metric/Helper/RoleOfMethodDetector.php @@ -1,4 +1,5 @@ namespace = (string)$node->name; + $this->namespace = (string) $node->name; } } @@ -51,8 +51,8 @@ public function leaveNode(Node $node) $this->metrics->attach($packageMetric); } /* @var PackageMetric $packageMetric */ - $elementName = isset($node->namespacedName) ? $node->namespacedName : 'anonymous@' . spl_object_hash($node); - $elementName = (string)$elementName; + $elementName = getNameOfNode($node); + $elementName = (string) $elementName; $packageMetric->addClass($elementName); $this->metrics->get($elementName)->set('package', $packageName); diff --git a/src/Hal/Metric/Package/PackageDistance.php b/src/Hal/Metric/Package/PackageDistance.php index 34f7a823..54881c8b 100644 --- a/src/Hal/Metric/Package/PackageDistance.php +++ b/src/Hal/Metric/Package/PackageDistance.php @@ -1,4 +1,5 @@ getOutgoingPackageDependencies()); $dependentInstabilities = array_combine( diff --git a/src/Hal/Metric/PackageMetric.php b/src/Hal/Metric/PackageMetric.php index ebc2cfce..46e31b49 100644 --- a/src/Hal/Metric/PackageMetric.php +++ b/src/Hal/Metric/PackageMetric.php @@ -18,7 +18,7 @@ public function getClasses() public function addClass($name) { $elements = $this->get('classes'); - $elements[] = (string)$name; + $elements[] = (string) $name; $this->set('classes', $elements); } @@ -26,7 +26,7 @@ public function addClass($name) public function setAbstraction($abstraction) { if ($abstraction !== null) { - $abstraction = (float)$abstraction; + $abstraction = (float) $abstraction; } $this->set('abstraction', $abstraction); } @@ -41,7 +41,7 @@ public function getAbstraction() public function setInstability($instability) { if ($instability !== null) { - $instability = (float)$instability; + $instability = (float) $instability; } $this->set('instability', $instability); } diff --git a/src/Hal/Metric/ProjectMetric.php b/src/Hal/Metric/ProjectMetric.php index 04ffde84..70cce4d3 100644 --- a/src/Hal/Metric/ProjectMetric.php +++ b/src/Hal/Metric/ProjectMetric.php @@ -1,4 +1,5 @@ "PageRank for component", ]; - public function allForProject() - { - } + public function allForProject() {} public function allForStructures() { diff --git a/src/Hal/Metric/SearchMetric.php b/src/Hal/Metric/SearchMetric.php index 1be1fdf5..5de228d9 100644 --- a/src/Hal/Metric/SearchMetric.php +++ b/src/Hal/Metric/SearchMetric.php @@ -1,4 +1,5 @@ config->get('exclude')); + $exclude = $this->config->has('exclude') ? $this->config->get('exclude') : []; + $finder = new Finder(['json'], $exclude); // include root dir by default - $files = array_merge($this->config->get('files'), ['./']); + $files = $this->config->has('files') ? $this->config->get('files') : []; + $files = array_merge($files, ['./']); $files = $finder->fetch($files); foreach ($files as $filename) { if (!\preg_match('/composer(-dist)?\.json/', $filename)) { continue; } - $composerJson = (object)\json_decode(\file_get_contents($filename)); + $composerJson = (object) \json_decode(\file_get_contents($filename)); if (!isset($composerJson->require)) { continue; } - $rawRequirements[] = (array)$composerJson->require; + $rawRequirements[] = (array) $composerJson->require; } return \call_user_func_array('array_merge', $rawRequirements); @@ -111,10 +112,12 @@ protected function getComposerLockInstalled($rootPackageRequirements) $rawInstalled = [[]]; // Find composer.lock file - $finder = new Finder(['lock'], $this->config->get('exclude')); + $exclude = $this->config->has('exclude') ? $this->config->get('exclude') : []; + $finder = new Finder(['lock'], $exclude); // include root dir by default - $files = array_merge($this->config->get('files'), ['./']); + $files = $this->config->has('files') ? $this->config->get('files') : []; + $files = array_merge($files, ['./']); $files = $finder->fetch($files); // List all composer.lock found in the project. @@ -122,7 +125,7 @@ protected function getComposerLockInstalled($rootPackageRequirements) if (false === \strpos($filename, 'composer.lock')) { continue; } - $composerLockJson = (object)\json_decode(\file_get_contents($filename)); + $composerLockJson = (object) \json_decode(\file_get_contents($filename)); if (!isset($composerLockJson->packages)) { continue; diff --git a/src/Hal/Metric/System/Packages/Composer/Packagist.php b/src/Hal/Metric/System/Packages/Composer/Packagist.php index 3c130361..2880f30e 100644 --- a/src/Hal/Metric/System/Packages/Composer/Packagist.php +++ b/src/Hal/Metric/System/Packages/Composer/Packagist.php @@ -1,4 +1,5 @@ latest = null; $response->license = []; $response->homepage = null; @@ -56,7 +56,7 @@ public function get($package) // get latest version $latest = '0.0.0'; - foreach ((array)$json->package->versions as $version => $datas) { + foreach ((array) $json->package->versions as $version => $datas) { if ($version[0] === 'v') { $version = substr($version, 1); } @@ -67,7 +67,7 @@ public function get($package) $latest = $version; $response->name = $package; $response->latest = $version; - $response->license = (array)$datas->license; + $response->license = (array) $datas->license; $response->homepage = $datas->homepage; $response->time = $datas->time; $response->zip = $datas->dist->url; diff --git a/src/Hal/Metric/System/UnitTesting/UnitTesting.php b/src/Hal/Metric/System/UnitTesting/UnitTesting.php index fcfd1681..d8614812 100644 --- a/src/Hal/Metric/System/UnitTesting/UnitTesting.php +++ b/src/Hal/Metric/System/UnitTesting/UnitTesting.php @@ -1,8 +1,11 @@ query('//testsuite[@file]') as $suite) { - array_push($testsuites, (object)[ + array_push($testsuites, (object) [ 'file' => $suite->getAttribute('file'), 'name' => $suite->getAttribute('name'), 'assertions' => $suite->getAttribute('assertions'), @@ -98,7 +100,7 @@ public function calculate(Metrics $metrics) $assertions = $case === $suite->firstChild->nextSibling ? $suite->getAttribute('assertions') : 0; } - $testsuites[$case->getAttribute('class')] = (object)[ + $testsuites[$case->getAttribute('class')] = (object) [ 'file' => $case->getAttribute('file'), 'name' => $case->getAttribute('class'), 'assertions' => $assertions, @@ -110,11 +112,13 @@ public function calculate(Metrics $metrics) // This code is slow and can be optimized foreach ($testsuites as $suite) { $metricsOfUnitTest = new Metrics(); - $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7); + $parser = (new ParserFactoryBridge())->create(); $traverser = new \PhpParser\NodeTraverser(); - $traverser->addVisitor(new \PhpParser\NodeVisitor\NameResolver()); - $traverser->addVisitor(new ClassEnumVisitor($metricsOfUnitTest)); - $traverser->addVisitor(new ExternalsVisitor($metricsOfUnitTest)); + (new ParserTraverserVisitorsAssigner())->assign($traverser, [ + new \PhpParser\NodeVisitor\NameResolver(), + new ClassEnumVisitor($metricsOfUnitTest), + new ExternalsVisitor($metricsOfUnitTest) + ]); if (!file_exists($suite->file) || !is_readable($suite->file)) { throw new \LogicException('Cannot find source file referenced in testsuite: ' . $suite->file); @@ -130,10 +134,10 @@ public function calculate(Metrics $metrics) // list of externals sources of unit test $metric = $metricsOfUnitTest->get($suite->name); - $externals = (array)$metric->get('externals'); + $externals = (array) $metric->get('externals'); // global stats for each test - $infoAboutTests[$suite->name] = (object)[ + $infoAboutTests[$suite->name] = (object) [ 'nbExternals' => count(array_unique($externals)), 'externals' => array_unique($externals), 'filename' => $suite->file, diff --git a/src/Hal/Report/Cli/Reporter.php b/src/Hal/Report/Cli/Reporter.php index 4a512ab1..d8e3f57a 100644 --- a/src/Hal/Report/Cli/Reporter.php +++ b/src/Hal/Report/Cli/Reporter.php @@ -1,4 +1,5 @@ config->get('searches')->get($searchName)->getConfig(); - if(!empty($foundSearch) && !empty($config->failIfFound) && true === $config->failIfFound) { + if (!empty($foundSearch) && !empty($config->failIfFound) && true === $config->failIfFound) { $title = sprintf( '[ERR] Found %d occurrences for search "%s"', sizeof($foundSearch), diff --git a/src/Hal/Report/Cli/SummaryWriter.php b/src/Hal/Report/Cli/SummaryWriter.php index 4f76ccbf..c3e204f8 100644 --- a/src/Hal/Report/Cli/SummaryWriter.php +++ b/src/Hal/Report/Cli/SummaryWriter.php @@ -9,50 +9,50 @@ class SummaryWriter extends SummaryProvider public function getReport() { $out = <<sum->loc} - Logical lines of code {$this->sum->lloc} - Comment lines of code {$this->sum->cloc} - Average volume {$this->avg->volume} - Average comment weight {$this->avg->commentWeight} - Average intelligent content {$this->avg->commentWeight} - Logical lines of code by class {$this->locByClass} - Logical lines of code by method {$this->locByMethod} -Object oriented programming - Classes {$this->sum->nbClasses} - Interface {$this->sum->nbInterfaces} - Methods {$this->sum->nbMethods} - Methods by class {$this->methodsByClass} - Lack of cohesion of methods {$this->avg->lcom} + LOC + Lines of code {$this->sum->loc} + Logical lines of code {$this->sum->lloc} + Comment lines of code {$this->sum->cloc} + Average volume {$this->avg->volume} + Average comment weight {$this->avg->commentWeight} + Average intelligent content {$this->avg->commentWeight} + Logical lines of code by class {$this->locByClass} + Logical lines of code by method {$this->locByMethod} + Object oriented programming + Classes {$this->sum->nbClasses} + Interface {$this->sum->nbInterfaces} + Methods {$this->sum->nbMethods} + Methods by class {$this->methodsByClass} + Lack of cohesion of methods {$this->avg->lcom} -Coupling - Average afferent coupling {$this->avg->afferentCoupling} - Average efferent coupling {$this->avg->efferentCoupling} - Average instability {$this->avg->instability} - Depth of Inheritance Tree {$this->treeInheritenceDepth} + Coupling + Average afferent coupling {$this->avg->afferentCoupling} + Average efferent coupling {$this->avg->efferentCoupling} + Average instability {$this->avg->instability} + Depth of Inheritance Tree {$this->treeInheritenceDepth} -Package - Packages {$this->sum->nbPackages} - Average classes per package {$this->avg->classesPerPackage} - Average distance {$this->avg->distance} - Average incoming class dependencies {$this->avg->incomingCDep} - Average outgoing class dependencies {$this->avg->outgoingCDep} - Average incoming package dependencies {$this->avg->incomingPDep} - Average outgoing package dependencies {$this->avg->outgoingPDep} -Complexity - Average Cyclomatic complexity by class {$this->avg->ccn} - Average Weighted method count by class {$this->avg->wmc} - Average Relative system complexity {$this->avg->relativeSystemComplexity} - Average Difficulty {$this->avg->difficulty} + Package + Packages {$this->sum->nbPackages} + Average classes per package {$this->avg->classesPerPackage} + Average distance {$this->avg->distance} + Average incoming class dependencies {$this->avg->incomingCDep} + Average outgoing class dependencies {$this->avg->outgoingCDep} + Average incoming package dependencies {$this->avg->incomingPDep} + Average outgoing package dependencies {$this->avg->outgoingPDep} + Complexity + Average Cyclomatic complexity by class {$this->avg->ccn} + Average Weighted method count by class {$this->avg->wmc} + Average Relative system complexity {$this->avg->relativeSystemComplexity} + Average Difficulty {$this->avg->difficulty} -Bugs - Average bugs by class {$this->avg->bugs} - Average defects by class (Kan) {$this->avg->kanDefect} -Violations - Critical {$this->sum->violations->critical} - Error {$this->sum->violations->error} - Warning {$this->sum->violations->warning} - Information {$this->sum->violations->information} + Bugs + Average bugs by class {$this->avg->bugs} + Average defects by class (Kan) {$this->avg->kanDefect} + Violations + Critical {$this->sum->violations->critical} + Error {$this->sum->violations->error} + Warning {$this->sum->violations->warning} + Information {$this->sum->violations->information} EOT; // git @@ -78,10 +78,10 @@ public function getReport() if ($this->config->has('junit')) { $out .= <<metrics->get('unitTesting')->get('nbSuites')} - Classes called by tests {$this->metrics->get('unitTesting')->get('nbCoveredClasses')} - Classes called by tests (percent) {$this->metrics->get('unitTesting')->get('percentCoveredClasses')} % + Unit testing + Number of unit tests {$this->metrics->get('unitTesting')->get('nbSuites')} + Classes called by tests {$this->metrics->get('unitTesting')->get('nbCoveredClasses')} + Classes called by tests (percent) {$this->metrics->get('unitTesting')->get('percentCoveredClasses')} % EOT; } diff --git a/src/Hal/Report/Csv/Reporter.php b/src/Hal/Report/Csv/Reporter.php index a09fa3e7..2e9ba022 100644 --- a/src/Hal/Report/Csv/Reporter.php +++ b/src/Hal/Report/Csv/Reporter.php @@ -1,4 +1,5 @@ $consolidated->getAvg(), - 'sum' => $consolidated->getSum() + 'sum' => $consolidated->getSum(), ]; $files = glob($logDir . '/js/history-*.json'); $next = count($files) + 1; @@ -252,7 +252,7 @@ protected function getTrend($type, $key, $lowIsBetter = false, $highIsBetter = f } $oldValue = $last->$type->$key; - $newValue = isset($this->$type->$key) ? $this->$type->$key : 0; + $newValue = isset($this->sum->$type->$key) ? $this->sum->$type->$key : 0; if ($newValue > $oldValue) { $r = 'gt'; } elseif ($newValue < $oldValue) { diff --git a/src/Hal/Report/Json/Reporter.php b/src/Hal/Report/Json/Reporter.php index 49e13b0c..35637c4c 100644 --- a/src/Hal/Report/Json/Reporter.php +++ b/src/Hal/Report/Json/Reporter.php @@ -1,4 +1,5 @@ $this->sum->violations->error, 'warning' => $this->sum->violations->warning, 'information' => $this->sum->violations->information, - ] + ], ]; } } diff --git a/src/Hal/Report/Violations/Xml/Reporter.php b/src/Hal/Report/Violations/Xml/Reporter.php index 9cf57b8d..11682dd4 100644 --- a/src/Hal/Report/Violations/Xml/Reporter.php +++ b/src/Hal/Report/Violations/Xml/Reporter.php @@ -1,4 +1,5 @@ output->writeln('The DOM extension is not available. Please install it if you want to use the Xml Violations report.'); return; } diff --git a/src/Hal/Search/Search.php b/src/Hal/Search/Search.php index 0cec8f6d..dec71bd7 100644 --- a/src/Hal/Search/Search.php +++ b/src/Hal/Search/Search.php @@ -26,7 +26,7 @@ class Search public function __construct($name, array $config) { $this->name = $name; - $this->config = (object)$config; + $this->config = (object) $config; } /** @@ -47,7 +47,7 @@ public function getConfig() public function matches(Metric $metric) { - $configAsArray = (array)$this->config; + $configAsArray = (array) $this->config; if (!empty($this->config->type)) { // Does the search concerns type ? @@ -125,7 +125,7 @@ private function matchInstanceOf(Metric $metric, $instanceOf) { foreach ($instanceOf as $expectedInterface) { $expectedInterface = ltrim($expectedInterface, '\\'); - if (!in_array($expectedInterface, (array)$metric->get('implements'))) { + if (!in_array($expectedInterface, (array) $metric->get('implements'))) { return false; } } @@ -136,7 +136,7 @@ private function matchInstanceOf(Metric $metric, $instanceOf) private function usesClasses(Metric $metric, $usesClasses) { foreach ($usesClasses as $expectedClass) { - foreach ((array)$metric->get('externals') as $use) { + foreach ((array) $metric->get('externals') as $use) { if (preg_match('@' . $expectedClass . '@i', $use)) { return true; } diff --git a/src/Hal/Search/Searches.php b/src/Hal/Search/Searches.php index 86aa4692..817ecc9e 100644 --- a/src/Hal/Search/Searches.php +++ b/src/Hal/Search/Searches.php @@ -4,7 +4,6 @@ class Searches { - /** * @var array */ diff --git a/src/Hal/Search/SearchesValidator.php b/src/Hal/Search/SearchesValidator.php index 38f526fd..f7014623 100644 --- a/src/Hal/Search/SearchesValidator.php +++ b/src/Hal/Search/SearchesValidator.php @@ -17,12 +17,12 @@ public function validates(Searches $searches) 'nameMatches', 'instanceOf', 'usesClasses', - 'failIfFound' + 'failIfFound', ]; $registry = new Registry(); $allowedKeys = array_merge($allowedKeys, $registry->allForStructures()); - $diff = array_diff(array_keys((array)$config), $allowedKeys); + $diff = array_diff(array_keys((array) $config), $allowedKeys); if (count($diff) > 0) { throw new ConfigException( sprintf( diff --git a/src/Hal/Violation/Class_/Blob.php b/src/Hal/Violation/Class_/Blob.php index de91e619..b90f0bdf 100644 --- a/src/Hal/Violation/Class_/Blob.php +++ b/src/Hal/Violation/Class_/Blob.php @@ -1,4 +1,5 @@ metric->get('nbMethodsPublic')}, excluding getters and setters) -* object has a high Lack of cohesion of methods (LCOM={$this->metric->get('lcom')}) -* object knows everything (and use lot of external classes) + * object has lot of public methods ({$this->metric->get('nbMethodsPublic')}, excluding getters and setters) + * object has a high Lack of cohesion of methods (LCOM={$this->metric->get('lcom')}) + * object knows everything (and use lot of external classes) -Maybe you should reducing the number of methods splitting this object in many sub objects. + Maybe you should reducing the number of methods splitting this object in many sub objects. EOT; } } diff --git a/src/Hal/Violation/Class_/ProbablyBugged.php b/src/Hal/Violation/Class_/ProbablyBugged.php index c0472de8..1b12fa26 100644 --- a/src/Hal/Violation/Class_/ProbablyBugged.php +++ b/src/Hal/Violation/Class_/ProbablyBugged.php @@ -1,4 +1,5 @@ metric->get('bugs')} bugs. + This component contains in theory {$this->metric->get('bugs')} bugs. -* Calculation is based on number of operators, operands, cyclomatic complexity -* See more details at https://en.wikipedia.org/wiki/Halstead_complexity_measures -* {$this->metric->get('numberOfUnitTests')} testsuites has dependency to this class. + * Calculation is based on number of operators, operands, cyclomatic complexity + * See more details at https://en.wikipedia.org/wiki/Halstead_complexity_measures + * {$this->metric->get('numberOfUnitTests')} testsuites has dependency to this class. -Maybe you should check your unit tests for this class. + Maybe you should check your unit tests for this class. EOT; } } diff --git a/src/Hal/Violation/Class_/TooComplexClassCode.php b/src/Hal/Violation/Class_/TooComplexClassCode.php index 1e697ad1..d772b003 100644 --- a/src/Hal/Violation/Class_/TooComplexClassCode.php +++ b/src/Hal/Violation/Class_/TooComplexClassCode.php @@ -1,4 +1,5 @@ metric->get('ccn')}) -* Component uses {$this->metric->get('number_operators')} operators + * Algorithms are complex (Total cyclomatic complexity of class is {$this->metric->get('ccn')}) + * Component uses {$this->metric->get('number_operators')} operators -Maybe you should delegate some code to other objects. + Maybe you should delegate some code to other objects. EOT; } } diff --git a/src/Hal/Violation/Class_/TooComplexMethodCode.php b/src/Hal/Violation/Class_/TooComplexMethodCode.php index 8e111c7a..38e9274b 100644 --- a/src/Hal/Violation/Class_/TooComplexMethodCode.php +++ b/src/Hal/Violation/Class_/TooComplexMethodCode.php @@ -1,4 +1,5 @@ metric->get('ccnMethodMax')}) + * Algorithms are complex (Max cyclomatic complexity of class methods is {$this->metric->get('ccnMethodMax')}) -Maybe you should delegate some code to other objects or split complex method. + Maybe you should delegate some code to other objects or split complex method. EOT; } } diff --git a/src/Hal/Violation/Class_/TooDependent.php b/src/Hal/Violation/Class_/TooDependent.php index a727346a..60d3fbfb 100644 --- a/src/Hal/Violation/Class_/TooDependent.php +++ b/src/Hal/Violation/Class_/TooDependent.php @@ -1,4 +1,5 @@ metric->get('efferentCoupling')}, so this class uses {$this->metric->get('efferentCoupling')} different external components. + * Efferent coupling is {$this->metric->get('efferentCoupling')}, so this class uses {$this->metric->get('efferentCoupling')} different external components. -Maybe you should check why this class has lot of dependencies. + Maybe you should check why this class has lot of dependencies. EOT; } } diff --git a/src/Hal/Violation/Class_/TooLong.php b/src/Hal/Violation/Class_/TooLong.php index 1c7b6da3..7d3ef77a 100644 --- a/src/Hal/Violation/Class_/TooLong.php +++ b/src/Hal/Violation/Class_/TooLong.php @@ -1,4 +1,5 @@ metric->get('lloc')} logical lines of code -* Class has {$this->metric->get('loc')} lines of code + * Class has {$this->metric->get('lloc')} logical lines of code + * Class has {$this->metric->get('loc')} lines of code -Maybe your class should not exceed 200 lines of logical code + Maybe your class should not exceed 200 lines of logical code EOT; } } diff --git a/src/Hal/Violation/Package/StableAbstractionsPrinciple.php b/src/Hal/Violation/Package/StableAbstractionsPrinciple.php index c8ad81b4..c5894b11 100644 --- a/src/Hal/Violation/Package/StableAbstractionsPrinciple.php +++ b/src/Hal/Violation/Package/StableAbstractionsPrinciple.php @@ -21,7 +21,7 @@ public function apply(Metric $metric) if (! $metric instanceof PackageMetric) { return; } - if (abs($metric->getDistance()) > sqrt(2)/4) { + if (abs($metric->getDistance()) > sqrt(2) / 4) { $this->metric = $metric; $metric->get('violations')->add($this); } @@ -39,9 +39,9 @@ public function getDescription() : 'stable and concrete'; return <<violatingInstabilities), $this->violatingInstabilities)); return <<all() as $metric) { - $metric->set('violations', new Violations); + $metric->set('violations', new Violations()); foreach ($violations as $violation) { $violation->apply($metric); diff --git a/src/Hal/Violation/Violations.php b/src/Hal/Violation/Violations.php index 70a1502a..91ae0193 100644 --- a/src/Hal/Violation/Violations.php +++ b/src/Hal/Violation/Violations.php @@ -1,4 +1,5 @@ namespacedName) && null !== $node->namespacedName) { + // Return the fully qualified name of the class, interface, or trait + return (string) $node->namespacedName; + } + if ($node instanceof \PhpParser\Node\Name\FullyQualified) { - return (string)$node; + return (string) $node; } + if ($node instanceof \PhpParser\Node\Expr\New_) { return getNameOfNode($node->class); } @@ -97,7 +102,10 @@ function getNameOfNode($node) } if ($node instanceof \PhpParser\Node\Name) { - return (string)implode($node->parts); + if(!property_exists($node, 'parts') || null === $node->parts) { + return (string) $node->name; + } + return (string) implode($node->parts); } if (isset($node->name) && $node->name instanceof \PhpParser\Node\Expr\Variable) { @@ -124,12 +132,13 @@ function getNameOfNode($node) return getNameOfNode($node->name); } - if (isset($node->name) && null === $node->name) { + // do not use isset here, we want to check if property is set + if (property_exists($node, 'name') && null === $node->name) { return 'anonymous@' . spl_object_hash($node); } if (isset($node->name)) { - return (string)$node->name; + return (string) $node->name; } return null; @@ -175,16 +184,16 @@ function gradientAlphaFor($array, $attribute, $currentValue) { // memory cache static $caches; - if(null === $caches) { + if (null === $caches) { $caches = []; } - if(!isset($caches[$attribute])) { + if (!isset($caches[$attribute])) { // avoid to iterate over array too many times $max = 0; $min = 1; - foreach($array as $item) { - if(!isset($item[$attribute])) { + foreach ($array as $item) { + if (!isset($item[$attribute])) { continue; } @@ -211,7 +220,8 @@ function gradientAlphaFor($array, $attribute, $currentValue) * @param mixed $currentValue * @return string */ -function gradientStyleFor($array, $attribute, $currentValue) { +function gradientStyleFor($array, $attribute, $currentValue) +{ return sprintf(' style="background-color: hsla(203, 82%%, 76%%, %s);"', gradientAlphaFor($array, $attribute, $currentValue)); } diff --git a/tests/Application/Config/File/ConfigFileReaderTest.php b/tests/Application/Config/File/ConfigFileReaderTest.php index 2dc8fa04..e843d766 100644 --- a/tests/Application/Config/File/ConfigFileReaderTest.php +++ b/tests/Application/Config/File/ConfigFileReaderTest.php @@ -11,7 +11,7 @@ */ class ConfigFileReaderTest extends \PHPUnit\Framework\TestCase { - public function testICanParseBasicJsonFIle() + public function testICanParseBasicJsonFIle(): void { $filename = __DIR__ . '/examples/config.json'; @@ -55,7 +55,7 @@ private function getExpectedData() ]; } - public function testICanParseConfigWithSearch() + public function testICanParseConfigWithSearch(): void { $filename = __DIR__ . '/examples/config-with-search.json'; diff --git a/tests/Application/Config/ParserTest.php b/tests/Application/Config/ParserTest.php index 62a57d20..aa4cfbcf 100644 --- a/tests/Application/Config/ParserTest.php +++ b/tests/Application/Config/ParserTest.php @@ -2,6 +2,7 @@ namespace Test\Hal\Application\Config; use Hal\Application\Config\Parser; +use PHPUnit\Framework\Attributes\DataProvider; /** * @group application @@ -12,14 +13,15 @@ class ParserTest extends \PHPUnit\Framework\TestCase /** * @dataProvider providesExample */ - public function testICanParseArguments($argv, $expected) + #[DataProvider('providesExample')] + public function testICanParseArguments($argv, $expected): void { $parser = new Parser(); $config = $parser->parse($argv); $this->assertEquals($expected, $config->all()); } - public function providesExample() + public static function providesExample() { return [ [ diff --git a/tests/Component/Ast/NodeTraverserTest.php b/tests/Component/Ast/NodeTraverserTest.php index 1593285f..601db156 100644 --- a/tests/Component/Ast/NodeTraverserTest.php +++ b/tests/Component/Ast/NodeTraverserTest.php @@ -7,7 +7,7 @@ class NodeTraverserTest extends TestCase { - public function testItCanBeInstantiated() + public function testItCanBeInstantiated(): void { $this->assertInstanceOf(BaseTraverser::class, new NodeTraverser()); } diff --git a/tests/Component/Ast/SupportedVersionTest.php b/tests/Component/Ast/SupportedVersionTest.php new file mode 100644 index 00000000..3d9ea49b --- /dev/null +++ b/tests/Component/Ast/SupportedVersionTest.php @@ -0,0 +1,70 @@ +analyze([__DIR__ . '/dataset/' . $filename . '.php']); + + /** @var ClassMetric $class */ + $class = $metrics->get($classname); + $this->assertInstanceOf(ClassMetric::class, $class); + foreach ($expected as $key => $value) { + $this->assertEquals($value, $class->get($key), "[PHP $filename] Class $classname metric $key does not match expected value"); + } + } + + public static function providesCases() + { + // for php < 7.0, the class is not supported + if (version_compare(PHP_VERSION, '7.0.0', '<')) { + return []; + } + + $cases = []; + if (version_compare(PHP_VERSION, '7.0.0', '>=')) { + $cases['php7.1.void'] = ['php7.1.void', 'Foo', ['nbMethods' => 1, 'number_operands_unique' => 1]]; + } + if (version_compare(PHP_VERSION, '7.2.0', '>=')) { + $cases['php7.2.trailcomma'] = ['php7.2.trailcomma', 'Foo', ['nbMethods' => 1, 'number_operands_unique' => 2]]; + } + if (version_compare(PHP_VERSION, '7.3.0', '>=')) { + $cases['php7.3.heredoc'] = ['php7.3.heredoc', 'Foo', ['nbMethods' => 1, 'number_operands_unique' => 2]]; + } + if (version_compare(PHP_VERSION, '7.4.0', '>=')) { + $cases['php7.4.typedprop'] = ['php7.4.typedprop', 'Foo', ['nbMethods' => 1, 'number_operands_unique' => 1]]; + } + if (version_compare(PHP_VERSION, '8.0.0', '>=')) { + $cases['php8.0.uniontype'] = ['php8.0.uniontype', 'Foo', ['nbMethods' => 1, 'number_operands_unique' => 1]]; + } + if (version_compare(PHP_VERSION, '8.1.0', '>=')) { + $cases['php8.1.readonlyproperty'] = ['php8.1.readonlyproperty', 'Foo', ['nbMethods' => 0, 'number_operands_unique' => 2]]; + } + if (version_compare(PHP_VERSION, '8.2.0', '>=')) { + $cases['php8.2.truetype'] = ['php8.2.truetype', 'Foo', ['nbMethods' => 1, 'number_operands_unique' => 1]]; + } + if (version_compare(PHP_VERSION, '8.3.0', '>=')) { + $cases['php8.3.jsonconst'] = ['php8.3.jsonconst', 'Foo', ['nbMethods' => 1, 'number_operands_unique' => 1]]; + } + if (version_compare(PHP_VERSION, '8.4.0', '>=')) { + $cases['php8.4.propertyhook'] = ['php8.4.propertyhook', 'Foo', ['nbMethods' => 0, 'number_operands_unique' => 2]]; + } + + return $cases; + } +} diff --git a/tests/Component/Ast/dataset/php7.0.coalesce.php b/tests/Component/Ast/dataset/php7.0.coalesce.php new file mode 100644 index 00000000..3bf68d37 --- /dev/null +++ b/tests/Component/Ast/dataset/php7.0.coalesce.php @@ -0,0 +1,9 @@ +bar = $bar; + } +} diff --git a/tests/Component/Ast/dataset/php8.2.truetype.php b/tests/Component/Ast/dataset/php8.2.truetype.php new file mode 100644 index 00000000..24857d33 --- /dev/null +++ b/tests/Component/Ast/dataset/php8.2.truetype.php @@ -0,0 +1,8 @@ +countryCode = strtoupper($countryCode); + } + } +} diff --git a/tests/Component/File/FinderTest.php b/tests/Component/File/FinderTest.php index 3e1a6590..ae971b0b 100644 --- a/tests/Component/File/FinderTest.php +++ b/tests/Component/File/FinderTest.php @@ -10,7 +10,7 @@ */ class FinderTest extends TestCase { - public function testPathsGivenAreRecoveredOverExcluded() + public function testPathsGivenAreRecoveredOverExcluded(): void { $exampleRoot = __DIR__ . DIRECTORY_SEPARATOR . 'examples'; @@ -37,7 +37,7 @@ public function testPathsGivenAreRecoveredOverExcluded() static::assertSame($expected, $files); } - public function testGivenPathsAreIgnoredRegardingExclusion() + public function testGivenPathsAreIgnoredRegardingExclusion(): void { $exampleRoot = __DIR__ . DIRECTORY_SEPARATOR . 'examples'; $actualFoundFiles = (new Finder(['php'], ['tests']))->fetch([$exampleRoot]); diff --git a/tests/Component/Issuer/IssuerTest.php b/tests/Component/Issuer/IssuerTest.php index cf7550c6..86bf48e5 100644 --- a/tests/Component/Issuer/IssuerTest.php +++ b/tests/Component/Issuer/IssuerTest.php @@ -2,36 +2,47 @@ namespace Test\Hal\Component\Issue; +use Hal\Component\Ast\ParserFactoryBridge; use Hal\Component\Issue\Issuer; use Hal\Component\Output\TestOutput; use PhpParser\ParserFactory; +use PHPUnit\Framework\Attributes\RequiresPhp; +use Polyfill\TestCaseCompatible; /** * @group issue */ class IssuerTest extends \PHPUnit\Framework\TestCase { - public function testICanEnableIssuerPhp5() + use TestCaseCompatible; + /** + * @requires PHP < 7.0 + */ + #[RequiresPhp('< 7.0')] + public function testICanEnableIssuerPhp5(): void { $output = new TestOutput(); $issuer = (new TestIssuer($output))->enable(); $issuer->set('Firstname', 'Jean-François'); try { - trigger_error('Object of class stdClass could not be converted to string', E_USER_ERROR); + trigger_error('Object of class stdClass could not be converted to string', E_USER_WARNING); } catch (\Exception $e) { } - $this->assertContains('Object of class stdClass could not be converted to string', $issuer->log); - $this->assertContains('Operating System', $issuer->log); - $this->assertContains('Details', $issuer->log); - $this->assertContains('https://github.com/phpmetrics/PhpMetrics/issues/new', $output->output); - $this->assertContains('Firstname: Jean-François', $issuer->log); - $this->assertContains('IssuerTest.php (line 21)', $issuer->log); + $this->assertStringContainsString('Object of class stdClass could not be converted to string', $issuer->log); + $this->assertStringContainsString('Operating System', $issuer->log); + $this->assertStringContainsString('Details', $issuer->log); + $this->assertStringContainsString('https://github.com/phpmetrics/PhpMetrics/issues/new', $output->output); + $this->assertStringContainsString('Firstname: Jean-François', $issuer->log); + $this->assertStringContainsString('IssuerTest.php (line 26)', $issuer->log); $issuer->disable(); } - public function testIssuerDisplayStatements() + /** + * @requires PHP < 7.0 + */ + public function testIssuerDisplayStatements(): void { $output = new TestOutput(); $issuer = (new TestIssuer($output))->enable(); @@ -39,12 +50,12 @@ public function testIssuerDisplayStatements() create(ParserFactory::PREFER_PHP7); + $parser = (new ParserFactoryBridge())->create(); $stmt = $parser->parse($code); $issuer->set('code', $stmt); @@ -54,7 +65,7 @@ public function foo() { } $issuer->disable(); - $this->assertContains('class A', $issuer->log); + $this->assertStringContainsString('class A', $issuer->log); } } diff --git a/tests/Component/Tree/GraphDeduplicatedTest.php b/tests/Component/Tree/GraphDeduplicatedTest.php index 139000e0..03ee7563 100644 --- a/tests/Component/Tree/GraphDeduplicatedTest.php +++ b/tests/Component/Tree/GraphDeduplicatedTest.php @@ -12,7 +12,7 @@ class GraphDeduplicatedTest extends \PHPUnit\Framework\TestCase { - public function testEdgeDeduplication() + public function testEdgeDeduplication(): void { $graph = new GraphDeduplicated(); $a = new Node('A'); diff --git a/tests/Component/Tree/GraphTest.php b/tests/Component/Tree/GraphTest.php index bc12875c..1f8aabfc 100644 --- a/tests/Component/Tree/GraphTest.php +++ b/tests/Component/Tree/GraphTest.php @@ -10,7 +10,7 @@ */ class GraphTest extends \PHPUnit\Framework\TestCase { - public function testICanAddEdge() + public function testICanAddEdge(): void { $graph = new Graph(); $a = new Node('node_a'); @@ -27,11 +27,9 @@ public function testICanAddEdge() $this->assertSame($b, $a->getEdges()[0]->getTo()); } - /** - * @expectedException \LogicException - */ - public function testICanAddEdgeWithUnexistantFromNode() + public function testICanAddEdgeWithUnexistantFromNode(): void { + $this->expectException(\LogicException::class); $graph = new Graph(); $a = new Node('A'); $b = new Node('B'); @@ -40,11 +38,9 @@ public function testICanAddEdgeWithUnexistantFromNode() $graph->addEdge($a, $b); } - /** - * @expectedException \LogicException - */ - public function testICanAddEdgeWithUnexistantToNode() + public function testICanAddEdgeWithUnexistantToNode(): void { + $this->expectException(\LogicException::class); $graph = new Graph(); $a = new Node('A'); $b = new Node('B'); @@ -53,18 +49,16 @@ public function testICanAddEdgeWithUnexistantToNode() $graph->addEdge($a, $b); } - /** - * @expectedException \LogicException - */ - public function testICanInsertSameNodeTwice() + public function testICanInsertSameNodeTwice(): void { + $this->expectException(\LogicException::class); $graph = new Graph(); $node = new Node('A'); $graph->insert($node); $graph->insert($node); } - public function testICanListEdges() + public function testICanListEdges(): void { $graph = new Graph(); $a = new Node('A'); @@ -77,7 +71,7 @@ public function testICanListEdges() $this->assertCount(1, $graph->getEdges()); } - public function testEdgeIsAddedToFromAndToNode() + public function testEdgeIsAddedToFromAndToNode(): void { $graph = new Graph(); $a = new Node('A'); @@ -96,7 +90,7 @@ public function testEdgeIsAddedToFromAndToNode() } - public function testICanListRootNodes() + public function testICanListRootNodes(): void { $graph = new Graph(); $a = new Node('A'); // root diff --git a/tests/Component/Tree/HashMapTest.php b/tests/Component/Tree/HashMapTest.php index 16b81004..b37d5779 100644 --- a/tests/Component/Tree/HashMapTest.php +++ b/tests/Component/Tree/HashMapTest.php @@ -11,7 +11,7 @@ class HashMapTest extends \PHPUnit\Framework\TestCase { - public function testICanWorkWithHashMap() + public function testICanWorkWithHashMap(): void { $hash = new HashMap; $hash @@ -28,7 +28,7 @@ public function testICanWorkWithHashMap() $this->assertEquals($node2, $hash->get('B')); } - public function testICanIterateThroughHashMap() + public function testICanIterateThroughHashMap(): void { $hash = new HashMap; $hash diff --git a/tests/Component/Tree/NodeTest.php b/tests/Component/Tree/NodeTest.php index cb6f2996..588c368f 100644 --- a/tests/Component/Tree/NodeTest.php +++ b/tests/Component/Tree/NodeTest.php @@ -10,7 +10,7 @@ */ class NodeTest extends \PHPUnit\Framework\TestCase { - public function testICanWorkWithNode() + public function testICanWorkWithNode(): void { $node = new Node('A'); $to = new Node('B'); diff --git a/tests/Component/Tree/Operator/CycleDetectorTest.php b/tests/Component/Tree/Operator/CycleDetectorTest.php index ab0986b8..87f5be52 100644 --- a/tests/Component/Tree/Operator/CycleDetectorTest.php +++ b/tests/Component/Tree/Operator/CycleDetectorTest.php @@ -11,7 +11,7 @@ */ class CycleDetectorTest extends \PHPUnit\Framework\TestCase { - public function testCycleIsDetected() + public function testCycleIsDetected(): void { $graph = new Graph(); $a = new Node('A'); @@ -45,7 +45,7 @@ public function testCycleIsDetected() $this->assertFalse($f->cyclic); } - public function testAllCyclesAreFound() + public function testAllCyclesAreFound(): void { $graph = new Graph(); $a = new Node('A'); @@ -79,7 +79,7 @@ public function testAllCyclesAreFound() $this->assertTrue($f->cyclic); } - public function testCycleIsNotDetected() + public function testCycleIsNotDetected(): void { $graph = new Graph(); $a = new Node('A'); @@ -112,7 +112,7 @@ public function testCycleIsNotDetected() $this->assertFalse($f->cyclic); } - public function testPartCycleIsDetected() + public function testPartCycleIsDetected(): void { $graph = new Graph(); $a = new Node('A'); diff --git a/tests/Component/Tree/Operator/SizeOfTreeTest.php b/tests/Component/Tree/Operator/SizeOfTreeTest.php index 10b62ef3..3db8befd 100644 --- a/tests/Component/Tree/Operator/SizeOfTreeTest.php +++ b/tests/Component/Tree/Operator/SizeOfTreeTest.php @@ -13,12 +13,10 @@ class SizeOfTreeTest extends \PHPUnit\Framework\TestCase { - /** - * @expectedException LogicException - * @expectedExceptionMessage Cannot get size informations of cyclic graph - */ - public function testICannotGetInfoAboutGraphWhenItIsCyclic() + public function testICannotGetInfoAboutGraphWhenItIsCyclic(): void { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Cannot get size informations of cyclic graph'); $graph = new Graph(); $a = new Node('A'); $b = new Node('B'); @@ -32,7 +30,7 @@ public function testICannotGetInfoAboutGraphWhenItIsCyclic() $size = new SizeOfTree($graph); } - public function testICanGetDepthOfNode() + public function testICanGetDepthOfNode(): void { $graph = new Graph(); $a = new Node('A'); @@ -58,7 +56,7 @@ public function testICanGetDepthOfNode() $this->assertEquals(1, $size->getDepthOfNode($e)); } - public function testICanGetNbChildsOfNode() + public function testICanGetNbChildsOfNode(): void { $graph = new Graph(); $a = new Node('A'); @@ -84,7 +82,7 @@ public function testICanGetNbChildsOfNode() $this->assertEquals(0, $size->getNumberOfChilds($e)); } - public function testICanGetInfoAboutAverageHeightOfTree() + public function testICanGetInfoAboutAverageHeightOfTree(): void { $graph = new Graph(); $a = new Node('A'); diff --git a/tests/Functions/GetNameOfNodeTest.php b/tests/Functions/GetNameOfNodeTest.php new file mode 100644 index 00000000..fc53d4b4 --- /dev/null +++ b/tests/Functions/GetNameOfNodeTest.php @@ -0,0 +1,86 @@ +create(); + $stmts = $parser->parse($code); + + $traverser = new NodeTraverser(); + (new ParserTraverserVisitorsAssigner())->assign($traverser, [ + new NameResolver() + ]); + $traverser->traverse($stmts); + + $node = $stmts[0]; + $name = getNameOfNode($node); + $this->assertEquals('Foo', $name); + } + + public function testNameInNamespaceIsCorrect(): void + { + $code = 'create(); + $stmts = $parser->parse($code); + + $traverser = new NodeTraverser(); + (new ParserTraverserVisitorsAssigner())->assign($traverser, [ + new NameResolver() + ]); + $traverser->traverse($stmts); + + // namespace + $node = $stmts[0]; + $name = getNameOfNode($node); + $this->assertEquals('Bar', $name); + + // class + $node = $node->stmts[0]; + $this->assertInstanceOf(Class_::class, $node); + $name = getNameOfNode($node); + $this->assertEquals('Bar\\Foo', $name); + } + public function testItCanNameAnonymousClass() : void + { + $code = 'create(); + $stmts = $parser->parse($code); + + $traverser = new NodeTraverser(); + (new ParserTraverserVisitorsAssigner())->assign($traverser, [ + new NameResolver() + ]); + $traverser->traverse($stmts); + + // class + $node = $stmts[0]; + $name = getNameOfNode($node); + $this->assertEquals('Foo', $name); + + // anonymous class + $node = $node->stmts[0]->stmts[0]->expr->class; + + if(method_exists($this, 'assertMatchesRegularExpression')) { + $this->assertMatchesRegularExpression('/^anonymous/', getNameOfNode($node)); + } else { + $this->assertRegExp('/^anonymous/', getNameOfNode($node));; + } + } +} diff --git a/tests/Metric/Class_/ClassEnumVisitorTest.php b/tests/Metric/Class_/ClassEnumVisitorTest.php index 76cb6c89..b44654fc 100644 --- a/tests/Metric/Class_/ClassEnumVisitorTest.php +++ b/tests/Metric/Class_/ClassEnumVisitorTest.php @@ -1,11 +1,13 @@ analyzeCode($code); @@ -62,7 +65,7 @@ public static function provideExamples() private function analyzeCode($code) { $metrics = new Metrics(); - $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7); + $parser = (new ParserFactoryBridge())->create(); $traverser = new NodeTraverser(); $traverser->addVisitor(new NameResolver()); $traverser->addVisitor(new ClassEnumVisitor($metrics)); @@ -73,7 +76,7 @@ private function analyzeCode($code) return $metrics; } - public function testAnonymousClassIsHandledCorrectly() + public function testAnonymousClassIsHandledCorrectly(): void { $code = 'analyzeCode($code); @@ -84,7 +87,7 @@ public function testAnonymousClassIsHandledCorrectly() /** * @link https://github.com/phpmetrics/PhpMetrics/issues/238 */ - public function testDynamicAttributeClassIsHandledCorrectly() + public function testDynamicAttributeClassIsHandledCorrectly(): void { $code = ' class A { @@ -105,7 +108,7 @@ public function foo() { /** * @link https://github.com/phpmetrics/PhpMetrics/issues/238#issuecomment-292466274 */ - public function testDynamicAttributeClassIsHandledCorrectly2() + public function testDynamicAttributeClassIsHandledCorrectly2(): void { $code = 'assertInstanceOf(Metrics::class, $metrics); } - public function testItDoesNotMarkClassesAsAbstract() + public function testItDoesNotMarkClassesAsAbstract(): void { $code = 'analyzeCode($code); $this->assertFalse($metrics->get('Foo')->get('abstract')); } - public function testItMarksAbstractClassesAsAbstract() + public function testItMarksAbstractClassesAsAbstract(): void { $code = 'analyzeCode($code); $this->assertTrue($metrics->get('Foo')->get('abstract')); } - public function testItMarksInterfacesAsAbstract() + public function testItMarksInterfacesAsAbstract(): void { $code = 'analyzeCode($code); $this->assertTrue($metrics->get('Foo')->get('abstract')); } - public function testItMarksTraitsAsAbstract() + public function testItMarksTraitsAsAbstract(): void { $code = 'analyzeCode($code); diff --git a/tests/Metric/Class_/Complexity/CyclomaticComplexityVisitorTest.php b/tests/Metric/Class_/Complexity/CyclomaticComplexityVisitorTest.php index 2f0c523d..be7fd42c 100644 --- a/tests/Metric/Class_/Complexity/CyclomaticComplexityVisitorTest.php +++ b/tests/Metric/Class_/Complexity/CyclomaticComplexityVisitorTest.php @@ -1,27 +1,33 @@ create(ParserFactory::PREFER_PHP7); + $parser = (new ParserFactoryBridge())->create(); $traverser = new NodeTraverser(); - $traverser->addVisitor(new NameResolver()); - $traverser->addVisitor(new ClassEnumVisitor($metrics)); - $traverser->addVisitor(new CyclomaticComplexityVisitor($metrics)); + (new ParserTraverserVisitorsAssigner())->assign($traverser, [ + new NameResolver(), + new ClassEnumVisitor($metrics), + new CyclomaticComplexityVisitor($metrics), + ]); $code = file_get_contents($example); $stmts = $parser->parse($code); @@ -33,15 +39,18 @@ public function testCcnOfClassesIsWellCalculated($example, $classname, $expected /** * @dataProvider provideExamplesForWmc */ - public function testWeightedMethodCountOfClassesIsWellCalculated($example, $classname, $expectedWmc) + #[DataProvider('provideExamplesForWmc')] + public function testWeightedMethodCountOfClassesIsWellCalculated($example, $classname, $expectedWmc): void { $metrics = new Metrics(); - $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); + $parser = (new ParserFactoryBridge)->create(); $traverser = new NodeTraverser(); - $traverser->addVisitor(new NameResolver()); - $traverser->addVisitor(new ClassEnumVisitor($metrics)); - $traverser->addVisitor(new CyclomaticComplexityVisitor($metrics)); + (new ParserTraverserVisitorsAssigner())->assign($traverser, [ + new NameResolver(), + new ClassEnumVisitor($metrics), + new CyclomaticComplexityVisitor($metrics), + ]); $code = file_get_contents($example); $stmts = $parser->parse($code); @@ -53,15 +62,18 @@ public function testWeightedMethodCountOfClassesIsWellCalculated($example, $clas /** * @dataProvider provideExamplesForMaxCc */ - public function testMaximalCyclomaticComplexityOfMethodsIsWellCalculated($example, $classname, $expectedCcnMethodMax) + #[DataProvider('provideExamplesForMaxCc')] + public function testMaximalCyclomaticComplexityOfMethodsIsWellCalculated($example, $classname, $expectedCcnMethodMax): void { $metrics = new Metrics(); - $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); + $parser = (new ParserFactoryBridge)->create(); $traverser = new NodeTraverser(); - $traverser->addVisitor(new NameResolver()); - $traverser->addVisitor(new ClassEnumVisitor($metrics)); - $traverser->addVisitor(new CyclomaticComplexityVisitor($metrics)); + (new ParserTraverserVisitorsAssigner())->assign($traverser, [ + new NameResolver(), + new ClassEnumVisitor($metrics), + new CyclomaticComplexityVisitor($metrics), + ]); $code = file_get_contents($example); $stmts = $parser->parse($code); diff --git a/tests/Metric/Class_/Complexity/KanDefectVisitorTest.php b/tests/Metric/Class_/Complexity/KanDefectVisitorTest.php index e78c51b5..d39b072f 100644 --- a/tests/Metric/Class_/Complexity/KanDefectVisitorTest.php +++ b/tests/Metric/Class_/Complexity/KanDefectVisitorTest.php @@ -1,10 +1,14 @@ create(ParserFactory::PREFER_PHP7); + $parser = (new ParserFactoryBridge())->create(); $traverser = new \PhpParser\NodeTraverser(); - $traverser->addVisitor(new \PhpParser\NodeVisitor\NameResolver()); - $traverser->addVisitor(new ClassEnumVisitor($metrics)); - $traverser->addVisitor(new KanDefectVisitor($metrics)); + (new ParserTraverserVisitorsAssigner())->assign($traverser, [ + new NameResolver(), + new ClassEnumVisitor($metrics), + new KanDefectVisitor($metrics) + ]); $code = file_get_contents($example); $stmts = $parser->parse($code); @@ -33,7 +40,7 @@ public function testLackOfCohesionOfMethodsIsWellCalculated($example, $classname $this->assertSame($expected, $metrics->get($classname)->get('kanDefect')); } - public function provideExamples() + public static function provideExamples() { return [ [ __DIR__ . '/../../examples/kan1.php', 'A', .89], diff --git a/tests/Metric/Class_/Complexity/SystemComplexityVisitorTest.php b/tests/Metric/Class_/Complexity/SystemComplexityVisitorTest.php index 91b5522d..b4266d9a 100644 --- a/tests/Metric/Class_/Complexity/SystemComplexityVisitorTest.php +++ b/tests/Metric/Class_/Complexity/SystemComplexityVisitorTest.php @@ -1,10 +1,14 @@ create(ParserFactory::PREFER_PHP7); + $parser = (new ParserFactoryBridge())->create(); $traverser = new \PhpParser\NodeTraverser(); - $traverser->addVisitor(new \PhpParser\NodeVisitor\NameResolver()); - $traverser->addVisitor(new ClassEnumVisitor($metrics)); - $traverser->addVisitor(new SystemComplexityVisitor($metrics)); + (new ParserTraverserVisitorsAssigner())->assign($traverser, [ + new NameResolver(), + new ClassEnumVisitor($metrics), + new SystemComplexityVisitor($metrics), + ]); $code = file_get_contents($filename); $stmts = $parser->parse($code); @@ -35,7 +42,7 @@ public function testLackOfCohesionOfMethodsIsWellCalculated($filename, $class, $ $this->assertSame($rsysc, $metrics->get('A')->get('relativeSystemComplexity')); } - public function provideExamples() + public static function provideExamples() { return [ [ __DIR__ . '/../../examples/systemcomplexity1.php', 'A', 0.5, 36.0, 36.5], diff --git a/tests/Metric/Class_/Component/MaintainabilityIndexVisitorTest.php b/tests/Metric/Class_/Component/MaintainabilityIndexVisitorTest.php index 0a7b3524..3b2a0e77 100644 --- a/tests/Metric/Class_/Component/MaintainabilityIndexVisitorTest.php +++ b/tests/Metric/Class_/Component/MaintainabilityIndexVisitorTest.php @@ -1,9 +1,13 @@ prophesize('Hal\Metric\ClassMetric'); - $prophet->getName()->willReturn('A'); - $prophet->get('lloc')->willReturn($lloc); - $prophet->get('loc')->willReturn($lloc + $cloc); - $prophet->get('ccn')->willReturn($ccn); - $prophet->get('cloc')->willReturn($cloc); - $prophet->get('volume')->willReturn($volume); + $classMetrics = new ClassMetric('A'); + $classMetrics->set('lloc', $lloc); + $classMetrics->set('loc', $lloc + $cloc); + $classMetrics->set('ccn', $ccn); + $classMetrics->set('cloc', $cloc); + $classMetrics->set('volume', $volume); + $metrics->attach($classMetrics); - // spy - $prophet->set('mIwoC', $mIwoC)->will(function () use ($prophet) { - return $prophet->reveal(); - })->shouldBeCalled(); - $prophet->set('mi', $mi)->will(function () use ($prophet) { - return $prophet->reveal(); - })->shouldBeCalled(); - $prophet->set('commentWeight', $commentWeight)->will(function () use ($prophet) { - return $prophet->reveal(); - })->shouldBeCalled(); - - $class = $prophet->reveal(); - $metrics->attach($class); - - $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); + $parser = (new ParserFactoryBridge())->create(); $traverser = new \PhpParser\NodeTraverser(); - $traverser->addVisitor(new \PhpParser\NodeVisitor\NameResolver()); - $traverser->addVisitor(new MaintainabilityIndexVisitor($metrics)); + (new ParserTraverserVisitorsAssigner())->assign($traverser, [ + new \PhpParser\NodeVisitor\NameResolver(), + new MaintainabilityIndexVisitor($metrics) + ]); $code = <<parse($code); $traverser->traverse($stmts); + + // And now, mi, mIwoC and commentWeight should be set + $this->assertEquals($mi, $classMetrics->get('mi')); + $this->assertEquals($mIwoC, $classMetrics->get('mIwoC')); + $this->assertEquals($commentWeight, $classMetrics->get('commentWeight')); } - public function provideValues() + public static function provideValues() { return [ // CC LLOC CLOC Volume MIwoC mi commentWeight diff --git a/tests/Metric/Class_/Coupling/ExternalsVisitorTest.php b/tests/Metric/Class_/Coupling/ExternalsVisitorTest.php index 8c51dfc2..140621e5 100644 --- a/tests/Metric/Class_/Coupling/ExternalsVisitorTest.php +++ b/tests/Metric/Class_/Coupling/ExternalsVisitorTest.php @@ -1,10 +1,13 @@ create(ParserFactory::PREFER_PHP7); + $parser = (new ParserFactoryBridge())->create(); $traverser = new \PhpParser\NodeTraverser(); - $traverser->addVisitor(new \PhpParser\NodeVisitor\NameResolver()); - $traverser->addVisitor(new ClassEnumVisitor($metrics)); - $traverser->addVisitor(new ExternalsVisitor($metrics)); + (new ParserTraverserVisitorsAssigner())->assign($traverser, [ + new \PhpParser\NodeVisitor\NameResolver(), + new ClassEnumVisitor($metrics), + new ExternalsVisitor($metrics) + ]); $code = file_get_contents($example); $stmts = $parser->parse($code); @@ -33,7 +39,7 @@ public function testDependenciesAreFound($example, $classname, $expected) $this->assertSame($expected, $metrics->get($classname)->get('externals')); } - public function provideExamples() + public static function provideExamples() { return [ [ __DIR__ . '/../../examples/externals1.php', 'A', ['H', 'C', 'B', 'D']], @@ -52,15 +58,18 @@ public function provideExamples() /** * @dataProvider provideExamplesAnnotation */ - public function testDependenciesAreFoundEvenInAnnotation($example, $classname, $expected) + #[DataProvider('provideExamplesAnnotation')] + public function testDependenciesAreFoundEvenInAnnotation($example, $classname, $expected): void { $metrics = new Metrics(); - $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); + $parser = (new ParserFactoryBridge())->create(); $traverser = new \PhpParser\NodeTraverser(); - $traverser->addVisitor(new \PhpParser\NodeVisitor\NameResolver()); - $traverser->addVisitor(new ClassEnumVisitor($metrics)); - $traverser->addVisitor(new ExternalsVisitor($metrics)); + (new ParserTraverserVisitorsAssigner())->assign($traverser, [ + new \PhpParser\NodeVisitor\NameResolver(), + new ClassEnumVisitor($metrics), + new ExternalsVisitor($metrics) + ]); $code = file_get_contents($example); $stmts = $parser->parse($code); @@ -69,7 +78,7 @@ public function testDependenciesAreFoundEvenInAnnotation($example, $classname, $ $this->assertSame($expected, $metrics->get($classname)->get('externals')); } - public function provideExamplesAnnotation() + public static function provideExamplesAnnotation() { return [ [ __DIR__ . '/../../examples/annotations1.php', 'C\\A', ['A\\Route', 'B\\Json']], diff --git a/tests/Metric/Class_/Structural/LcomVisitorTest.php b/tests/Metric/Class_/Structural/LcomVisitorTest.php index ad3a2525..ad77e921 100644 --- a/tests/Metric/Class_/Structural/LcomVisitorTest.php +++ b/tests/Metric/Class_/Structural/LcomVisitorTest.php @@ -1,25 +1,32 @@ create(ParserFactory::PREFER_PHP7); + $parser = (new ParserFactoryBridge())->create(); $traverser = new \PhpParser\NodeTraverser(); - $traverser->addVisitor(new \PhpParser\NodeVisitor\NameResolver()); - $traverser->addVisitor(new ClassEnumVisitor($metrics)); - $traverser->addVisitor(new LcomVisitor($metrics)); + (new ParserTraverserVisitorsAssigner())->assign($traverser, [ + new \PhpParser\NodeVisitor\NameResolver(), + new ClassEnumVisitor($metrics), + new LcomVisitor($metrics), + ]); $code = file_get_contents($example); $stmts = $parser->parse($code); @@ -28,7 +35,7 @@ public function testLackOfCohesionOfMethodsIsWellCalculated($example, $classname $this->assertEquals($expected, $metrics->get($classname)->get('lcom')); } - public function provideExamples() + public static function provideExamples() { return [ [ __DIR__ . '/../../examples/lcom1.php', 'MyClassA', 2] diff --git a/tests/Metric/Class_/Text/HalsteadVisitorTest.php b/tests/Metric/Class_/Text/HalsteadVisitorTest.php index 63f0cf3d..bff16fa7 100644 --- a/tests/Metric/Class_/Text/HalsteadVisitorTest.php +++ b/tests/Metric/Class_/Text/HalsteadVisitorTest.php @@ -1,10 +1,12 @@ create(ParserFactory::PREFER_PHP7); + $parser = (new ParserFactoryBridge())->create(); $traverser = new \PhpParser\NodeTraverser(); $traverser->addVisitor(new \PhpParser\NodeVisitor\NameResolver()); $traverser->addVisitor(new ClassEnumVisitor($metrics)); @@ -46,7 +49,7 @@ public function testLackOfCohesionOfMethodsIsWellCalculated($example, $functionN ); } - public function provideExamples() + public static function provideExamples() { return [ [ __DIR__ . '/../../examples/halstead1.php', 'twice', 2, 3, 1.5], diff --git a/tests/Metric/Class_/Text/LengthVisitorTest.php b/tests/Metric/Class_/Text/LengthVisitorTest.php index f83caf46..91801ec0 100644 --- a/tests/Metric/Class_/Text/LengthVisitorTest.php +++ b/tests/Metric/Class_/Text/LengthVisitorTest.php @@ -1,10 +1,14 @@ create(ParserFactory::PREFER_PHP7); + $parser = (new ParserFactoryBridge())->create(); $traverser = new \PhpParser\NodeTraverser(); - $traverser->addVisitor(new \PhpParser\NodeVisitor\NameResolver()); - $traverser->addVisitor(new ClassEnumVisitor($metrics)); - $traverser->addVisitor(new LengthVisitor($metrics)); + (new ParserTraverserVisitorsAssigner())->assign($traverser, [ + new \PhpParser\NodeVisitor\NameResolver(), + new ClassEnumVisitor($metrics), + new LengthVisitor($metrics) + ]); $code = file_get_contents($example); $stmts = $parser->parse($code); @@ -34,7 +41,7 @@ public function testLineCountsAreWellCalculated($example, $functionName, $loc, $ $this->assertEquals($loc, $metrics->get($functionName)->get('loc')); } - public function provideExamples() + public static function provideExamples() { return [ [ __DIR__ . '/../../examples/loc1.php', 'A', 21, 13, 8], diff --git a/tests/Metric/Helper/RoleOfMethodDetectorTest.php b/tests/Metric/Helper/RoleOfMethodDetectorTest.php index af16be3c..3a39b9e6 100644 --- a/tests/Metric/Helper/RoleOfMethodDetectorTest.php +++ b/tests/Metric/Helper/RoleOfMethodDetectorTest.php @@ -1,10 +1,12 @@ create(ParserFactory::PREFER_PHP7); + $parser = (new ParserFactoryBridge())->create(); $stmt = $parser->parse($code); $helper = new RoleOfMethodDetector(); @@ -35,7 +38,7 @@ public function testICanDetectRoleOfMethod($expected, $code) } } - public function provideExamples() + public static function provideExamples() { $examples = [ 'getter' => ['getter', 'name; } } ?>'], diff --git a/tests/Metric/Package/PackageAbstractionTest.php b/tests/Metric/Package/PackageAbstractionTest.php index cc427112..e51ab369 100644 --- a/tests/Metric/Package/PackageAbstractionTest.php +++ b/tests/Metric/Package/PackageAbstractionTest.php @@ -15,7 +15,7 @@ */ class PackageAbstractionTest extends TestCase { - public function testItCalculatesTheAbstractionOfEachPackage() + public function testItCalculatesTheAbstractionOfEachPackage(): void { $metrics = $this->metricsOf([ $this->aPackage('SemiAbstract\\', [ diff --git a/tests/Metric/Package/PackageCollectingVisitorTest.php b/tests/Metric/Package/PackageCollectingVisitorTest.php index aa8c49d5..942f143b 100644 --- a/tests/Metric/Package/PackageCollectingVisitorTest.php +++ b/tests/Metric/Package/PackageCollectingVisitorTest.php @@ -2,6 +2,8 @@ namespace Test\Hal\Metric\Package; +use Hal\Component\Ast\ParserFactoryBridge; +use Hal\Component\Ast\ParserTraverserVisitorsAssigner; use Hal\Metric\Class_\ClassEnumVisitor; use Hal\Metric\Metrics; use Hal\Metric\Package\PackageCollectingVisitor; @@ -17,7 +19,7 @@ */ class PackageCollectingVisitorTest extends TestCase { - public function testItUsesThePackageAndTheSubpackageAnnotationAsPackageName() + public function testItUsesThePackageAndTheSubpackageAnnotationAsPackageName(): void { $metrics = $this->analyzeCode(<<<'CODE' assertSame('PackA\\SubA\\', $metrics->get('PackageA\\ClassA')->get('package')); } - public function testItUsesThePackageAnnotationAsPackageNameIfNoSubpackageAnnotationExist() + public function testItUsesThePackageAnnotationAsPackageNameIfNoSubpackageAnnotationExist(): void { $metrics = $this->analyzeCode(<<<'CODE' assertSame('PackA\\', $metrics->get('PackageA\\ClassA')->get('package')); } - public function testItUsesTheNamespaceAsPackageNameIfNoPackageAnnotationAreAvailable() + public function testItUsesTheNamespaceAsPackageNameIfNoPackageAnnotationAreAvailable(): void { $metrics = $this->analyzeCode(<<<'CODE' create(ParserFactory::PREFER_PHP7); + $parser = (new ParserFactoryBridge())->create(); $traverser = new NodeTraverser(); - $traverser->addVisitor(new NameResolver()); - $traverser->addVisitor(new ClassEnumVisitor($metrics)); - $traverser->addVisitor(new PackageCollectingVisitor($metrics)); + (new ParserTraverserVisitorsAssigner())->assign($traverser, [ + new \PhpParser\NodeVisitor\NameResolver(), + new ClassEnumVisitor($metrics), + new PackageCollectingVisitor($metrics) + ]); $stmts = $parser->parse($code); $traverser->traverse($stmts); diff --git a/tests/Metric/Package/PackageDependenciesTest.php b/tests/Metric/Package/PackageDependenciesTest.php index da74d85c..6a217b09 100644 --- a/tests/Metric/Package/PackageDependenciesTest.php +++ b/tests/Metric/Package/PackageDependenciesTest.php @@ -14,7 +14,7 @@ */ class PackageDependenciesTest extends TestCase { - public function testItCollectsAllIncomingAndOutgoingPackageDependencies() + public function testItCollectsAllIncomingAndOutgoingPackageDependencies(): void { $packageA = new PackageMetric('PackageA\\'); $packageB = new PackageMetric('PackageB\\'); @@ -45,26 +45,26 @@ public function testItCollectsAllIncomingAndOutgoingPackageDependencies() $this->assertSame(['PackageA\\'], $packageB->getIncomingPackageDependencies()); } - public function testItSkipsClassesThatHasNoDependencies() + public function testItSkipsClassesThatHasNoDependencies(): void { $classMetric = (new ClassMetric('OneClass'))->set('package', 'PackageA\\'); $metrics = $this->getMockBuilder(Metrics::class)->disableOriginalConstructor()->getMock(); $metrics ->expects($this->once()) ->method('all') - ->will($this->returnValue([$classMetric])); + ->willReturn([$classMetric]); (new PackageDependencies())->calculate($metrics); } - public function testItSkipsClassesThatHasNoPackage() + public function testItSkipsClassesThatHasNoPackage(): void { $classMetric = (new ClassMetric('OneClass'))->set('externals', ['AnotherClass']); $metrics = $this->getMockBuilder(Metrics::class)->disableOriginalConstructor()->getMock(); $metrics ->expects($this->once()) ->method('all') - ->will($this->returnValue([$classMetric])); + ->willReturn([$classMetric]); (new PackageDependencies())->calculate($metrics); } diff --git a/tests/Metric/Package/PackageDistanceTest.php b/tests/Metric/Package/PackageDistanceTest.php index 205f2166..3099ec75 100644 --- a/tests/Metric/Package/PackageDistanceTest.php +++ b/tests/Metric/Package/PackageDistanceTest.php @@ -6,6 +6,7 @@ use Hal\Metric\Metrics; use Hal\Metric\Package\PackageDistance; use Hal\Metric\PackageMetric; +use PHPUnit\Framework\Attributes\DataProvider; use \PHPUnit\Framework\TestCase; /** @@ -16,11 +17,9 @@ class PackageDistanceTest extends TestCase { /** * @dataProvider provideExamples - * @param float|null $instability - * @param float|null $abstraction - * @param float|null $expectedDistance */ - public function testItCalculatesTheNormalizedDistanceOfAllPackages($instability, $abstraction, $expectedDistance) + #[DataProvider('provideExamples')] + public function testItCalculatesTheNormalizedDistanceOfAllPackages($instability, $abstraction, $expectedDistance): void { $metrics = new Metrics(); $metrics->attach(new ClassMetric('Ignored')); diff --git a/tests/Metric/Package/PackageInstabilityTest.php b/tests/Metric/Package/PackageInstabilityTest.php index 2eceac59..70b7514a 100644 --- a/tests/Metric/Package/PackageInstabilityTest.php +++ b/tests/Metric/Package/PackageInstabilityTest.php @@ -13,7 +13,7 @@ */ class PackageInstabilityTest extends TestCase { - public function testItCalculatesTheInstabilityOfEachPackage() + public function testItCalculatesTheInstabilityOfEachPackage(): void { $packageA = new PackageMetric('PackageA\\'); $packageB = new PackageMetric('PackageB\\'); @@ -37,7 +37,7 @@ public function testItCalculatesTheInstabilityOfEachPackage() $this->assertSame(0.0, $packageC->getInstability()); } - public function testItStoresTheInstabilityOfTheDependentPackagesOfEachPackage() + public function testItStoresTheInstabilityOfTheDependentPackagesOfEachPackage(): void { $packageA = new PackageMetric('PackageA\\'); $packageB = new PackageMetric('PackageB\\'); @@ -61,7 +61,7 @@ public function testItStoresTheInstabilityOfTheDependentPackagesOfEachPackage() $this->assertSame([], $packageC->getDependentInstabilities()); } - public function testItDoesNotCrashIfOnePackageHasNoIncomingAndNoOutgoingDependencies() + public function testItDoesNotCrashIfOnePackageHasNoIncomingAndNoOutgoingDependencies(): void { $package = new PackageMetric('PackageA\\'); diff --git a/tests/Metric/PackageMetricTest.php b/tests/Metric/PackageMetricTest.php index 88e5e609..422953bc 100644 --- a/tests/Metric/PackageMetricTest.php +++ b/tests/Metric/PackageMetricTest.php @@ -8,12 +8,12 @@ class PackageMetricTest extends TestCase { - public function testItIsAMetric() + public function testItIsAMetric(): void { $this->assertInstanceOf(Metric::class, new PackageMetric('PackageName\\')); } - public function testItAppendsClasses() + public function testItAppendsClasses(): void { $metric = new PackageMetric('PackageName\\'); @@ -26,7 +26,7 @@ public function testItAppendsClasses() $this->assertSame(['Foo', 'Bar'], $metric->getClasses()); } - public function testItMayHasAnAbstraction() + public function testItMayHasAnAbstraction(): void { $metric = new PackageMetric('PackageName\\'); $this->assertNull($metric->getAbstraction()); @@ -35,7 +35,7 @@ public function testItMayHasAnAbstraction() $this->assertSame(0.8, $metric->getAbstraction()); } - public function testItMayHasAnInstability() + public function testItMayHasAnInstability(): void { $metric = new PackageMetric('PackageName\\'); $this->assertNull($metric->getInstability()); @@ -44,7 +44,7 @@ public function testItMayHasAnInstability() $this->assertSame(0.8, $metric->getInstability()); } - public function testItHasAUniqueListOfOutgoingClassDependencies() + public function testItHasAUniqueListOfOutgoingClassDependencies(): void { $metric = new PackageMetric('PackageName\\'); $this->assertSame([], $metric->getOutgoingClassDependencies()); @@ -55,14 +55,14 @@ public function testItHasAUniqueListOfOutgoingClassDependencies() $this->assertSame(['PackageA\\AnyClass'], $metric->getOutgoingClassDependencies()); } - public function testItDoesNotAddClassesOfItselfAsOutgoingClassDependencies() + public function testItDoesNotAddClassesOfItselfAsOutgoingClassDependencies(): void { $metric = new PackageMetric('PackageA\\'); $metric->addOutgoingClassDependency('PackageA\\AnyClass', 'PackageA\\'); $this->assertSame([], $metric->getOutgoingClassDependencies()); } - public function testItHasAUniqueListOfOutgoingPackageDependencies() + public function testItHasAUniqueListOfOutgoingPackageDependencies(): void { $metric = new PackageMetric('PackageName\\'); $this->assertSame([], $metric->getOutgoingPackageDependencies()); @@ -73,14 +73,14 @@ public function testItHasAUniqueListOfOutgoingPackageDependencies() $this->assertSame(['PackageA\\'], $metric->getOutgoingPackageDependencies()); } - public function testItDoesNotAddItselfAsOutgoingClassDependencies() + public function testItDoesNotAddItselfAsOutgoingClassDependencies(): void { $metric = new PackageMetric('PackageA\\'); $metric->addOutgoingClassDependency('PackageA\\AnyClass', 'PackageA\\'); $this->assertSame([], $metric->getOutgoingPackageDependencies()); } - public function testItHasAUniqueListOfIncomingClassDependencies() + public function testItHasAUniqueListOfIncomingClassDependencies(): void { $metric = new PackageMetric('PackageName\\'); $this->assertSame([], $metric->getOutgoingClassDependencies()); @@ -91,14 +91,14 @@ public function testItHasAUniqueListOfIncomingClassDependencies() $this->assertSame(['PackageA\\AnyClass'], $metric->getIncomingClassDependencies()); } - public function testItDoesNotAddClassesOfItselfAsIncomingClassDependencies() + public function testItDoesNotAddClassesOfItselfAsIncomingClassDependencies(): void { $metric = new PackageMetric('PackageA\\'); $metric->addIncomingClassDependency('PackageA\\AnyClass', 'PackageA\\'); $this->assertSame([], $metric->getIncomingClassDependencies()); } - public function testItHasAUniqueListOfIncomingPackageDependencies() + public function testItHasAUniqueListOfIncomingPackageDependencies(): void { $metric = new PackageMetric('PackageName\\'); $this->assertSame([], $metric->getIncomingPackageDependencies()); @@ -109,14 +109,14 @@ public function testItHasAUniqueListOfIncomingPackageDependencies() $this->assertSame(['PackageA\\'], $metric->getIncomingPackageDependencies()); } - public function testItDoesNotAddItselfAsIncomingClassDependencies() + public function testItDoesNotAddItselfAsIncomingClassDependencies(): void { $metric = new PackageMetric('PackageA\\'); $metric->addIncomingClassDependency('PackageA\\AnyClass', 'PackageA\\'); $this->assertSame([], $metric->getIncomingPackageDependencies()); } - public function testItMayHasADistanceAndANormalizedDistance() + public function testItMayHasADistanceAndANormalizedDistance(): void { $metric = new PackageMetric('PackageA\\'); $this->assertNull($metric->getDistance()); @@ -127,7 +127,7 @@ public function testItMayHasADistanceAndANormalizedDistance() $this->assertSame(1/sqrt(2), $metric->getDistance()); } - public function testItMyaHasDependentInstabilities() + public function testItMyaHasDependentInstabilities(): void { $metric = new PackageMetric('PackageB\\'); $this->assertSame([], $metric->getDependentInstabilities()); diff --git a/tests/Metric/System/UnitTesting/UnitTestingTest.php b/tests/Metric/System/UnitTesting/UnitTestingTest.php index 31514f6d..0b80bacd 100644 --- a/tests/Metric/System/UnitTesting/UnitTestingTest.php +++ b/tests/Metric/System/UnitTesting/UnitTestingTest.php @@ -11,7 +11,7 @@ class UnitTestingTest extends \PHPUnit\Framework\TestCase { - public function testICanParseJunitXmlFile() + public function testICanParseJunitXmlFile(): void { $config = new Config(); $config->set('junit', __DIR__ . '/xml/junit1.xml'); @@ -31,11 +31,9 @@ public function testICanParseJunitXmlFile() $this->assertEquals(6, $tests['Test\Hal\Component\Issue\IssuerTest']->assertions); } - /** - * @expectedException Hal\Application\Config\ConfigException - */ - public function testExceptionIsThrownIfJunitFileDoesNotExist() + public function testExceptionIsThrownIfJunitFileDoesNotExist(): void { + $this->expectException(\Hal\Application\Config\ConfigException::class); $config = new Config(); $config->set('junit', __DIR__ . '/xml/junit-not-found.xml'); $unit = new UnitTesting($config, []); @@ -43,7 +41,7 @@ public function testExceptionIsThrownIfJunitFileDoesNotExist() $unit->calculate($metrics); } - public function testICanParseCodeceptionFile() + public function testICanParseCodeceptionFile(): void { $config = new Config(); $config->set('junit', __DIR__ . '/xml/codeception1.xml'); diff --git a/tests/PhpUnit/WithAnalyzer.php b/tests/PhpUnit/WithAnalyzer.php new file mode 100644 index 00000000..cf29f49f --- /dev/null +++ b/tests/PhpUnit/WithAnalyzer.php @@ -0,0 +1,27 @@ +fetch($filesToAnalyze); + + // disable composer + $config->set('composer', false); + + $output = new TestOutput(); + $issuer = new Issuer($output); + return (new Analyze($config, $output, $issuer))->run($files); + } +} diff --git a/tests/Polyfill/TestCaseCompatible.php b/tests/Polyfill/TestCaseCompatible.php new file mode 100644 index 00000000..55e038b3 --- /dev/null +++ b/tests/Polyfill/TestCaseCompatible.php @@ -0,0 +1,24 @@ +assertStringContainsString($needle, $haystack, $message); + } + + public function assertMatchesRegularExpression($pattern, $string, $message = '') + { + $this->assertMatchesRegularExpression($pattern, $string, $message); + } + } +} else { + trait TestCaseCompatible + { + } +} + + diff --git a/tests/Polyfill/each.php b/tests/Polyfill/each.php new file mode 100644 index 00000000..bfc53169 --- /dev/null +++ b/tests/Polyfill/each.php @@ -0,0 +1,38 @@ + $value, + 'value' => $value, + 0 => $key, + 'key' => $key + ); + } +} diff --git a/tests/Polyfill/testcase.php b/tests/Polyfill/testcase.php new file mode 100644 index 00000000..0a8727ca --- /dev/null +++ b/tests/Polyfill/testcase.php @@ -0,0 +1,8 @@ +assertEquals($expectedTableHeader, $actualTableHeader); } - public function tableHeaderDataProvider() + public static function tableHeaderDataProvider() { $defaultTableHeader = [ 'Class', @@ -80,12 +82,24 @@ public function tableHeaderDataProvider() private function getActualTableHeader($content) { - $tableHeaderColumnNodes = (new Crawler($content)) - ->filterXPath('.//table[contains(concat(" ",normalize-space(@class)," ")," js-sort-table ")]/thead/tr') - ->children(); + $dom = new \DOMDocument(); + @$dom->loadHTML($content); - return array_map(function (DomNode $node) { - return $node->textContent; - }, iterator_to_array($tableHeaderColumnNodes)); + $xpath = new \DOMXPath($dom); + $rows = $xpath->query('//table[contains(concat(" ",normalize-space(@class)," ")," js-sort-table ")]/thead/tr'); + + if ($rows->length === 0) { + return []; + } + + $headerRow = $rows->item(0); + $headers = []; + foreach ($headerRow->childNodes as $node) { + if ($node->nodeType === XML_ELEMENT_NODE) { + $headers[] = $node->textContent; + } + } + + return $headers; } } diff --git a/tests/Report/Html/ReporterTest.php b/tests/Report/Html/ReporterTest.php index 1d52aa5b..42d8bedb 100644 --- a/tests/Report/Html/ReporterTest.php +++ b/tests/Report/Html/ReporterTest.php @@ -8,6 +8,7 @@ use Hal\Metric\Metrics; use Hal\Report\Html\Reporter; use PHPUnit\Framework\TestCase; +use Polyfill\TestCaseCompatible; /** * @group reporter @@ -15,7 +16,9 @@ */ class ReporterTest extends TestCase { - public function testICanGenerateHtmlReport() + use TestCaseCompatible; + + public function testICanGenerateHtmlReport(): void { $config = new Config(); $output = new TestOutput(); @@ -46,6 +49,6 @@ public function testICanGenerateHtmlReport() // ensure basic content is generated $content = file_get_contents(sprintf('%s/index.html', $destination)); - $this->assertContains('PhpMetrics report', $content); + $this->assertStringContainsString('PhpMetrics report', $content); } } diff --git a/tests/Search/SearchFactoryTest.php b/tests/Search/SearchFactoryTest.php index 811ff49e..68a7c0e6 100644 --- a/tests/Search/SearchFactoryTest.php +++ b/tests/Search/SearchFactoryTest.php @@ -11,7 +11,7 @@ class SearchFactoryTest extends TestCase { - public function testIShoulBeAbleToFactorySearches() + public function testIShoulBeAbleToFactorySearches(): void { $config = [ 'search1' => [ diff --git a/tests/Search/SearchTest.php b/tests/Search/SearchTest.php index a6d96048..9ff43c9e 100644 --- a/tests/Search/SearchTest.php +++ b/tests/Search/SearchTest.php @@ -6,6 +6,7 @@ use Hal\Metric\InterfaceMetric; use Hal\Metric\Metric; use Hal\Search\Search; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; /** @@ -14,7 +15,7 @@ class SearchTest extends TestCase { - public function testSearchCanReduceSearchByName() + public function testSearchCanReduceSearchByName(): void { $config = [ 'nameMatches' => 'awesome' @@ -24,14 +25,14 @@ public function testSearchCanReduceSearchByName() $metric ->expects($this->once()) ->method('getName') - ->will($this->returnValue('My\\AwesomeClass')); + ->willReturn('My\\AwesomeClass'); $search = new Search('my-search', $config); $this->assertTrue($search->matches($metric)); } - public function testSearchCanReduceSearchByType() + public function testSearchCanReduceSearchByType(): void { $config = [ 'type' => 'class' @@ -49,7 +50,8 @@ public function testSearchCanReduceSearchByType() /** * @dataProvider providesMetrics */ - public function testSearchCanReduceSearchByMetric($searchExpression, $value, $expected) + #[DataProvider('providesMetrics')] + public function testSearchCanReduceSearchByMetric($searchExpression, $value, $expected): void { $config = [ 'ccn' => $searchExpression @@ -59,14 +61,14 @@ public function testSearchCanReduceSearchByMetric($searchExpression, $value, $ex $metric ->expects($this->once()) ->method('get') - ->will($this->returnValue($value)); + ->willReturn($value); $search = new Search('my-search', $config); $this->assertEquals($expected, $search->matches($metric)); } - public function providesMetrics() + public static function providesMetrics() { return [ ['>=2.5', 6, true], diff --git a/tests/Violation/Class_/BlobTest.php b/tests/Violation/Class_/BlobTest.php index be8da94c..457f70ff 100644 --- a/tests/Violation/Class_/BlobTest.php +++ b/tests/Violation/Class_/BlobTest.php @@ -5,6 +5,7 @@ use Hal\Metric\ClassMetric; use Hal\Violation\Class_\Blob; use Hal\Violation\Violations; +use PHPUnit\Framework\Attributes\DataProvider; /** * @group violation @@ -14,7 +15,8 @@ class BlobTest extends \PHPUnit\Framework\TestCase /** * @dataProvider provideExamples */ - public function testGlobIsFound($expected, $nbMethodsPublic, $lcom, $nbExternals) + #[DataProvider('provideExamples')] + public function testGlobIsFound($expected, $nbMethodsPublic, $lcom, $nbExternals): void { $class = $this->getMockBuilder(ClassMetric::class)->disableOriginalConstructor()->getMock(); @@ -42,7 +44,7 @@ public function testGlobIsFound($expected, $nbMethodsPublic, $lcom, $nbExternals $this->assertEquals($expected, $class->get('violations')->count()); } - public function provideExamples() + public static function provideExamples() { return [ [1, 9, 3, 10], diff --git a/tests/Violation/Package/StableAbstractionsPrincipleTest.php b/tests/Violation/Package/StableAbstractionsPrincipleTest.php index 17351468..2475dc6a 100644 --- a/tests/Violation/Package/StableAbstractionsPrincipleTest.php +++ b/tests/Violation/Package/StableAbstractionsPrincipleTest.php @@ -6,6 +6,7 @@ use Hal\Metric\PackageMetric; use Hal\Violation\Package\StableAbstractionsPrinciple; use Hal\Violation\Violations; +use PHPUnit\Framework\Attributes\DataProvider; use \PHPUnit\Framework\TestCase; /** @@ -13,24 +14,26 @@ */ class StableAbstractionsPrincipleTest extends TestCase { - public function testItIgnoresNonPackageMetrics() + public function testItIgnoresNonPackageMetrics(): void { - $metric = $this->prophesize(Metric::class); + $metric = $this->getMockBuilder(Metric::class) + ->disableOriginalConstructor() + ->getMock(); - $object = new StableAbstractionsPrinciple(); + $metric->expects($this->never()) + ->method('get') + ->with('violations'); - $object->apply($metric->reveal()); + $object = new StableAbstractionsPrinciple(); - $metric->get('violations')->shouldNotHaveBeenCalled(); + $object->apply($metric); } /** * @dataProvider provideExamples - * @param float $abstractness - * @param float $instability - * @param int $expectedViolationCount */ - public function testItAddsViolationsIfAPackageIsEitherStableAndConcreteOrInstableAndAbstract($abstractness, $instability, $expectedViolationCount) + #[DataProvider('provideExamples')] + public function testItAddsViolationsIfAPackageIsEitherStableAndConcreteOrInstableAndAbstract($abstractness, $instability, $expectedViolationCount): void { $metric = new PackageMetric('package'); $metric->set('violations', new Violations()); diff --git a/tests/Violation/Package/StableDependenciesPrincipleTest.php b/tests/Violation/Package/StableDependenciesPrincipleTest.php index 4eb870f4..6e4cbce3 100644 --- a/tests/Violation/Package/StableDependenciesPrincipleTest.php +++ b/tests/Violation/Package/StableDependenciesPrincipleTest.php @@ -6,6 +6,7 @@ use Hal\Metric\PackageMetric; use Hal\Violation\Package\StableDependenciesPrinciple; use Hal\Violation\Violations; +use PHPUnit\Framework\Attributes\DataProvider; use \PHPUnit\Framework\TestCase; /** @@ -13,7 +14,7 @@ */ class StableDependenciesPrincipleTest extends TestCase { - public function testItIgnoresNonPackageMetrics() + public function testItIgnoresNonPackageMetrics(): void { $metric = $this->getMockBuilder(Metric::class)->getMock(); $metric->expects($this->never())->method('get'); @@ -24,15 +25,13 @@ public function testItIgnoresNonPackageMetrics() /** * @dataProvider provideExamples - * @param float $packageInstability - * @param float[] $dependentInstabilities - * @param int $expectedViolationCount */ + #[DataProvider('provideExamples')] public function testItAddsViolationsIfOneDependentPackageIsMoreUnstableOrAsUnstableAsThePackageItself( $packageInstability, array $dependentInstabilities, $expectedViolationCount - ) { + ): void { $violations = new Violations(); $metric = $this->getMockBuilder(PackageMetric::class)->disableOriginalConstructor()->getMock(); $metric->method('getInstability')->willReturn($packageInstability); diff --git a/tests/binary/BinTest.php b/tests/binary/BinTest.php index aada20b1..7ffbdad8 100644 --- a/tests/binary/BinTest.php +++ b/tests/binary/BinTest.php @@ -1,35 +1,40 @@ phar = __DIR__ . '/../../bin/phpmetrics'; } - public function testICanRunBinFile() + public function testICanRunBinFile(): void { $command = sprintf('%s --version', $this->phar); $r = shell_exec($command); - $this->assertContains('PhpMetrics', $r); + $this->assertStringContainsString('PhpMetrics', $r); } - public function testICanProvideOneDirectoryToParse() + public function testICanProvideOneDirectoryToParse(): void { $command = sprintf('%s --exclude="" %s 2>&1', $this->phar, __DIR__ . '/examples/1'); $r = shell_exec($command); - $this->assertContains('Object oriented programming', $r); - $this->assertContains('LOC', $r); - $this->assertRegExp('!Classes\s+2!', $r); + $this->assertStringContainsString('Object oriented programming', $r); + $this->assertStringContainsString('LOC', $r); + $this->assertMatchesRegularExpression('!Classes\s+2!', $r); } - public function testICanProvideMultipleDirectoriesToParse() + public function testICanProvideMultipleDirectoriesToParse(): void { $command = sprintf( '%s --exclude="" %s,%s 2>&1', @@ -38,8 +43,8 @@ public function testICanProvideMultipleDirectoriesToParse() __DIR__ . '/examples/2' ); $r = shell_exec($command); - $this->assertContains('Object oriented programming', $r); - $this->assertContains('LOC', $r); - $this->assertRegExp('!Classes\s+4!', $r); + $this->assertStringContainsString('Object oriented programming', $r); + $this->assertStringContainsString('LOC', $r); + $this->assertMatchesRegularExpression('!Classes\s+4!', $r); } } diff --git a/tests/binary/PharTest.php b/tests/binary/PharTest.php index cecb304b..e340eae0 100644 --- a/tests/binary/PharTest.php +++ b/tests/binary/PharTest.php @@ -1,35 +1,41 @@ phar = __DIR__ . '/../../releases/phpmetrics.phar'; } - public function testICanRunPhar() + public function testICanRunPhar(): void { $command = sprintf('%s --version', $this->phar); $r = shell_exec($command); - $this->assertContains('PhpMetrics', $r); + $this->assertStringContainsString('PhpMetrics', $r); } - public function testICanProvideOneDirectoryToParse() + public function testICanProvideOneDirectoryToParse(): void { $command = sprintf('%s --exclude="" %s 2>&1', $this->phar, __DIR__ . '/examples/1'); $r = shell_exec($command); - $this->assertContains('Object oriented programming', $r); - $this->assertContains('LOC', $r); - $this->assertRegExp('!Classes\s+2!', $r); + $this->assertStringContainsString('Object oriented programming', $r); + $this->assertStringContainsString('LOC', $r); + $this->assertMatchesRegularExpression('!Classes\s+2!', $r); } - public function testICanProvideMultipleDirectoriesToParse() + public function testICanProvideMultipleDirectoriesToParse(): void { $command = sprintf( '%s --exclude="" %s,%s 2>&1', @@ -38,8 +44,8 @@ public function testICanProvideMultipleDirectoriesToParse() __DIR__ . '/examples/2' ); $r = shell_exec($command); - $this->assertContains('Object oriented programming', $r); - $this->assertContains('LOC', $r); - $this->assertRegExp('!Classes\s+4!', $r); + $this->assertStringContainsString('Object oriented programming', $r); + $this->assertStringContainsString('LOC', $r); + $this->assertMatchesRegularExpression('!Classes\s+4!', $r); } } diff --git a/tests/binary/ReportTest.php b/tests/binary/ReportTest.php index 351d9048..d7465036 100644 --- a/tests/binary/ReportTest.php +++ b/tests/binary/ReportTest.php @@ -1,35 +1,40 @@ phar = __DIR__ . '/../../bin/phpmetrics'; } - public function testICanRunBinFile() + public function testICanRunBinFile(): void { $command = sprintf('%s --version', $this->phar); $r = shell_exec($command); - $this->assertContains('PhpMetrics', $r); + $this->assertStringContainsString('PhpMetrics', $r); } - public function testICanProvideOneDirectoryToParse() + public function testICanProvideOneDirectoryToParse(): void { $command = sprintf('%s --exclude="" %s 2>&1', $this->phar, __DIR__ . '/examples/1'); $r = shell_exec($command); - $this->assertContains('Object oriented programming', $r); - $this->assertContains('LOC', $r); - $this->assertRegExp('!Classes\s+2!', $r); + $this->assertStringContainsString('Object oriented programming', $r); + $this->assertStringContainsString('LOC', $r); + $this->assertMatchesRegularExpression('!Classes\s+2!', $r); } - public function testICanProvideMultipleDirectoriesToParse() + public function testICanProvideMultipleDirectoriesToParse(): void { $command = sprintf( '%s --exclude="" %s,%s 2>&1', @@ -38,12 +43,12 @@ public function testICanProvideMultipleDirectoriesToParse() __DIR__ . '/examples/2' ); $r = shell_exec($command); - $this->assertContains('Object oriented programming', $r); - $this->assertContains('LOC', $r); - $this->assertRegExp('!Classes\s+4!', $r); + $this->assertStringContainsString('Object oriented programming', $r); + $this->assertStringContainsString('LOC', $r); + $this->assertMatchesRegularExpression('!Classes\s+4!', $r); } - public function testICanGenerateCsvReport() + public function testICanGenerateCsvReport(): void { $destination = '/tmp/report.csv'; if (file_exists($destination)) { @@ -60,7 +65,7 @@ public function testICanGenerateCsvReport() $this->assertFileExists($destination); } - public function testICanGenerateJsonReport() + public function testICanGenerateJsonReport(): void { $destination = '/tmp/report.json'; if (file_exists($destination)) { diff --git a/tests/binary/StandaloneTest.php b/tests/binary/StandaloneTest.php new file mode 100644 index 00000000..58789e3e --- /dev/null +++ b/tests/binary/StandaloneTest.php @@ -0,0 +1,51 @@ +phar = __DIR__ . '/../../releases/phpmetrics-linux-x86_64'; + } + + public function testICanRunPhar(): void + { + $command = sprintf('%s --version', $this->phar); + $r = shell_exec($command); + $this->assertStringContainsString('PhpMetrics', $r); + } + + public function testICanProvideOneDirectoryToParse(): void + { + $command = sprintf('%s --exclude="" %s 2>&1', $this->phar, __DIR__ . '/examples/1'); + $r = shell_exec($command); + $this->assertStringContainsString('Object oriented programming', $r); + $this->assertStringContainsString('LOC', $r); + $this->assertMatchesRegularExpression('!Classes\s+2!', $r); + } + + public function testICanProvideMultipleDirectoriesToParse(): void + { + $command = sprintf( + '%s --exclude="" %s,%s 2>&1', + $this->phar, + __DIR__ . '/examples/1', + __DIR__ . '/examples/2' + ); + $r = shell_exec($command); + $this->assertStringContainsString('Object oriented programming', $r); + $this->assertStringContainsString('LOC', $r); + $this->assertMatchesRegularExpression('!Classes\s+4!', $r); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 00000000..dc2a6a2a --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,12 @@ + Date: Fri, 4 Jul 2025 07:26:58 +0200 Subject: [PATCH 2/4] fixed issue introduced by phpcs, and removed phpcs --- Makefile | 4 -- src/Hal/Application/Config/Validator.php | 50 ++++++------- src/Hal/Component/Issue/Issuer.php | 44 ++++++------ src/Hal/Report/Cli/SummaryWriter.php | 90 ++++++++++++------------ tooling/composer.json | 3 +- 5 files changed, 93 insertions(+), 98 deletions(-) diff --git a/Makefile b/Makefile index 25b695d1..440c61a9 100644 --- a/Makefile +++ b/Makefile @@ -6,10 +6,6 @@ include artifacts/Makefile test: ./vendor/bin/phpunit -c phpunit.xml.dist -# Codesniffer check -phpcs: - ./tooling/vendor/bin/php-cs-fixer check src - # Compatibility check compatibility: (docker run --rm -v `pwd`:/www --workdir=/www php:5.6-cli find src -iname "*.php" -exec php -l {} \; |grep -v "Php7NodeTraverser.php" | grep -v "No syntax errors detected") && echo OK diff --git a/src/Hal/Application/Config/Validator.php b/src/Hal/Application/Config/Validator.php index d47e3c80..1e96187c 100644 --- a/src/Hal/Application/Config/Validator.php +++ b/src/Hal/Application/Config/Validator.php @@ -99,48 +99,48 @@ public function validate(Config $config) public function help() { return << + phpmetrics [...options...] - Required: +Required: - List of directories to parse, separated by a comma (,) + List of directories to parse, separated by a comma (,) - Optional: +Optional: - --config= Use a file for configuration. File can be a JSON, YAML or INI file. - --exclude= List of directories to exclude, separated by a comma (,) - --extensions= List of extensions to parse, separated by a comma (,) - --metrics Display list of available metrics - --report-html= Folder where report HTML will be generated - --report-csv= File where report CSV will be generated - --report-json= File where report Json will be generated - --report-summary-json= File where the summary report Json will be generated - --report-violations= File where XML violations report will be generated - --git[=] Perform analyses based on Git History (default binary path: "git") - --junit[=] Evaluates metrics according to JUnit logs - --quiet Quiet mode - --version Display current version + --config= Use a file for configuration. File can be a JSON, YAML or INI file. + --exclude= List of directories to exclude, separated by a comma (,) + --extensions= List of extensions to parse, separated by a comma (,) + --metrics Display list of available metrics + --report-html= Folder where report HTML will be generated + --report-csv= File where report CSV will be generated + --report-json= File where report Json will be generated + --report-summary-json= File where the summary report Json will be generated + --report-violations= File where XML violations report will be generated + --git[=] Perform analyses based on Git History (default binary path: "git") + --junit[=] Evaluates metrics according to JUnit logs + --quiet Quiet mode + --version Display current version - Examples: +Examples: - phpmetrics --report-html="./report" ./src + phpmetrics --report-html="./report" ./src - Analyze the "./src" directory and generate a HTML report on the "./report" folder + Analyze the "./src" directory and generate a HTML report on the "./report" folder - phpmetrics --report-violations="./build/violations.xml" ./src,./lib + phpmetrics --report-violations="./build/violations.xml" ./src,./lib - Analyze the "./src" and "./lib" directories, and generate the "./build/violations.xml" file. This file could - be read by any Continuous Integration Platform, and follows the "PMD Violation" standards. + Analyze the "./src" and "./lib" directories, and generate the "./build/violations.xml" file. This file could + be read by any Continuous Integration Platform, and follows the "PMD Violation" standards. EOT; } public function metrics() { $help = <<We're sorry : an unexpected error occured. +We're sorry : an unexpected error occured. - Can you help us ? Please open a new issue at https://github.com/phpmetrics/PhpMetrics/issues/new, and copy-paste the content - of this file: $logfile ? +Can you help us ? Please open a new issue at https://github.com/phpmetrics/PhpMetrics/issues/new, and copy-paste the content +of this file: $logfile ? - Thanks for your help :) +Thanks for your help :) EOT; - $log = << - Details - ``` - $trace +
+ Details + ``` +$trace - $debug - ``` -
+$debug +``` + EOT; $this->output->write($message); diff --git a/src/Hal/Report/Cli/SummaryWriter.php b/src/Hal/Report/Cli/SummaryWriter.php index c3e204f8..4f76ccbf 100644 --- a/src/Hal/Report/Cli/SummaryWriter.php +++ b/src/Hal/Report/Cli/SummaryWriter.php @@ -9,50 +9,50 @@ class SummaryWriter extends SummaryProvider public function getReport() { $out = <<sum->loc} - Logical lines of code {$this->sum->lloc} - Comment lines of code {$this->sum->cloc} - Average volume {$this->avg->volume} - Average comment weight {$this->avg->commentWeight} - Average intelligent content {$this->avg->commentWeight} - Logical lines of code by class {$this->locByClass} - Logical lines of code by method {$this->locByMethod} - Object oriented programming - Classes {$this->sum->nbClasses} - Interface {$this->sum->nbInterfaces} - Methods {$this->sum->nbMethods} - Methods by class {$this->methodsByClass} - Lack of cohesion of methods {$this->avg->lcom} +LOC + Lines of code {$this->sum->loc} + Logical lines of code {$this->sum->lloc} + Comment lines of code {$this->sum->cloc} + Average volume {$this->avg->volume} + Average comment weight {$this->avg->commentWeight} + Average intelligent content {$this->avg->commentWeight} + Logical lines of code by class {$this->locByClass} + Logical lines of code by method {$this->locByMethod} +Object oriented programming + Classes {$this->sum->nbClasses} + Interface {$this->sum->nbInterfaces} + Methods {$this->sum->nbMethods} + Methods by class {$this->methodsByClass} + Lack of cohesion of methods {$this->avg->lcom} - Coupling - Average afferent coupling {$this->avg->afferentCoupling} - Average efferent coupling {$this->avg->efferentCoupling} - Average instability {$this->avg->instability} - Depth of Inheritance Tree {$this->treeInheritenceDepth} +Coupling + Average afferent coupling {$this->avg->afferentCoupling} + Average efferent coupling {$this->avg->efferentCoupling} + Average instability {$this->avg->instability} + Depth of Inheritance Tree {$this->treeInheritenceDepth} - Package - Packages {$this->sum->nbPackages} - Average classes per package {$this->avg->classesPerPackage} - Average distance {$this->avg->distance} - Average incoming class dependencies {$this->avg->incomingCDep} - Average outgoing class dependencies {$this->avg->outgoingCDep} - Average incoming package dependencies {$this->avg->incomingPDep} - Average outgoing package dependencies {$this->avg->outgoingPDep} - Complexity - Average Cyclomatic complexity by class {$this->avg->ccn} - Average Weighted method count by class {$this->avg->wmc} - Average Relative system complexity {$this->avg->relativeSystemComplexity} - Average Difficulty {$this->avg->difficulty} +Package + Packages {$this->sum->nbPackages} + Average classes per package {$this->avg->classesPerPackage} + Average distance {$this->avg->distance} + Average incoming class dependencies {$this->avg->incomingCDep} + Average outgoing class dependencies {$this->avg->outgoingCDep} + Average incoming package dependencies {$this->avg->incomingPDep} + Average outgoing package dependencies {$this->avg->outgoingPDep} +Complexity + Average Cyclomatic complexity by class {$this->avg->ccn} + Average Weighted method count by class {$this->avg->wmc} + Average Relative system complexity {$this->avg->relativeSystemComplexity} + Average Difficulty {$this->avg->difficulty} - Bugs - Average bugs by class {$this->avg->bugs} - Average defects by class (Kan) {$this->avg->kanDefect} - Violations - Critical {$this->sum->violations->critical} - Error {$this->sum->violations->error} - Warning {$this->sum->violations->warning} - Information {$this->sum->violations->information} +Bugs + Average bugs by class {$this->avg->bugs} + Average defects by class (Kan) {$this->avg->kanDefect} +Violations + Critical {$this->sum->violations->critical} + Error {$this->sum->violations->error} + Warning {$this->sum->violations->warning} + Information {$this->sum->violations->information} EOT; // git @@ -78,10 +78,10 @@ public function getReport() if ($this->config->has('junit')) { $out .= <<metrics->get('unitTesting')->get('nbSuites')} - Classes called by tests {$this->metrics->get('unitTesting')->get('nbCoveredClasses')} - Classes called by tests (percent) {$this->metrics->get('unitTesting')->get('percentCoveredClasses')} % +Unit testing + Number of unit tests {$this->metrics->get('unitTesting')->get('nbSuites')} + Classes called by tests {$this->metrics->get('unitTesting')->get('nbCoveredClasses')} + Classes called by tests (percent) {$this->metrics->get('unitTesting')->get('percentCoveredClasses')} % EOT; } diff --git a/tooling/composer.json b/tooling/composer.json index 03d18c2a..fe7409db 100644 --- a/tooling/composer.json +++ b/tooling/composer.json @@ -12,7 +12,6 @@ } ], "require-dev": { - "rector/rector": "^2.1", - "friendsofphp/php-cs-fixer": "^3.76" + "rector/rector": "^2.1" } } From 73299aaced4414bdbb6da8296fe35f115ddd7379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20L=C3=A9pine?= Date: Fri, 4 Jul 2025 07:30:35 +0200 Subject: [PATCH 3/4] added build script for standalone build --- artifacts/standalone/Makefile | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 artifacts/standalone/Makefile diff --git a/artifacts/standalone/Makefile b/artifacts/standalone/Makefile new file mode 100644 index 00000000..7882e7d5 --- /dev/null +++ b/artifacts/standalone/Makefile @@ -0,0 +1,10 @@ +build-standalone: build-standalone-linux + +build-standalone-linux: + mkdir -p ${BUILD_DIR} + curl -fsSL -o spc.tgz https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-linux-x86_64.tar.gz && tar -zxvf spc.tgz && rm spc.tgz + ./spc download --with-php=8.4 --for-extensions "apcu,phar,curl,dom,fileinfo,filter,intl,mbstring,mysqlnd,openssl,tokenizer,zlib" --prefer-pre-built + ./spc install-pkg upx + ./spc build --build-micro "apcu,phar,curl,dom,fileinfo,filter,intl,mbstring,mysqlnd,openssl,tokenizer,zlib" --with-upx-pack + ./spc micro:combine ${BUILD_DIR}/phpmetrics.phar --output=${BUILD_DIR}/phpmetrics-linux-x86_64 + From f4d5d541ec9cd2f43180102d8843f860077149b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20L=C3=A9pine?= Date: Fri, 4 Jul 2025 07:44:03 +0200 Subject: [PATCH 4/4] fixed CI --- .github/workflows/ci.yml | 6 +-- .travis.yml | 73 --------------------------- Makefile | 2 +- tests/Component/Issuer/IssuerTest.php | 2 +- tests/Polyfill/TestCaseCompatible.php | 4 +- tests/binary/BinTest.php | 2 + tests/binary/PharTest.php | 2 + tests/binary/ReportTest.php | 2 + tests/binary/StandaloneTest.php | 14 ++--- 9 files changed, 19 insertions(+), 88 deletions(-) delete mode 100644 .travis.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a59e6e7c..68f6edee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,8 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-version: [7.0, 7.1, 7.2, 7.3, 7.4, 8.0, 8.1, 8.2, 8.3, 8.4] - #php-version: [5.6, 7.0, 7.1, 7.2, 7.3, 7.4, 8.0, 8.1, 8.2, 8.3, 8.4] + php-version: [5.6, 7.0, 7.1, 7.2, 7.3, 7.4, 8.0, 8.1, 8.2, 8.3, 8.4] include: - php-version: nightly fail-fast: false @@ -47,9 +46,6 @@ jobs: - name: Run tests (${{ matrix.php-version }}) run: make test - - name: Run Docker tests - run: docker run --rm test_phpmetrics - build: if: github.event_name == 'release' && startsWith(github.ref, 'refs/tags/') needs: test diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6f624134..00000000 --- a/.travis.yml +++ /dev/null @@ -1,73 +0,0 @@ -sudo: false -dist: trusty -language: php - -php: - - 5.5 - - 5.6 - - 7.0 - - 7.1 - - 7.2 - - 7.3 - - 7.4 - - 8.0 - - nightly - - hhvm - - hhvm-nightly - -env: - matrix: - - DEPENDENCIES="--prefer-lowest --prefer-stable" - - DEPENDENCIES="" - -matrix: - fast_finish: true - allow_failures: - - php: nightly - - php: hhvm - - php: hhvm-nightly - -services: - - docker - -install: - - docker build -t test_phpmetrics . - - composer self-update --quiet - - travis_retry composer update --no-interaction --prefer-dist --no-progress $DEPENDENCIES - -before_script: - # Disable JIT compilation in hhvm, as the JIT is useless for short live scripts like tests. - - if [[ $TRAVIS_PHP_VERSION = hhvm* ]]; then echo 'hhvm.jit = 0' >> /etc/hhvm/php.ini; fi - -script: - - make test - - make phpcs - - docker run --rm test_phpmetrics - -before_deploy: - - make build - - git add -f build/phpmetrics.phar # build/phpmetrics.deb - -deploy: - - provider: releases - api_key: - secure: DfUbGENVB2IK6bggZeI89zJizOCSUbYqMEpXYVjhlbP73c7a+s4P6MrmTk8BiLACBeoyaoQDHW06DTHuc3MzgadXcTsPxzdxmZmSHaNsmGuUUJPlBR44Ypw/6ZccILrMVowzMbgRSAvk63XEIaV18DwcDMQaMhYU9uPf2WNpHL4= - file: - - "releases/phpmetrics.phar" - on: - repo: phpmetrics/PhpMetrics - tags: true - php: '7.0' - condition: '-z "$DEPENDENCIES"' - skip_cleanup: true - - provider: bintray - file: artifacts/bintray.json - user: "halleck45" - key: - secure: Pa7uB1ePY9bYKGHovrdC/F2HlrjXGapKTp6McAFcnIksvuwu06Xug0BszqQeUSzKX/y1kivC7ksIp6kI3icHv0cm6K0MvAi+PLxplqtwLsKJPtwoxX5L6r0VOQ8uNaNU2K7+9Gb/5WNov7SfESXpyxOmUUj/QwwnRkj2RqlL9rg= - on: - repo: phpmetrics/PhpMetrics - tags: true - php: '7.0' - condition: '-z "$DEPENDENCIES"' - skip_cleanup: true diff --git a/Makefile b/Makefile index 440c61a9..3b255d1a 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ include artifacts/Makefile # Run unit tests test: - ./vendor/bin/phpunit -c phpunit.xml.dist + ./vendor/bin/phpunit -c phpunit.xml.dist --exclude-group binary # Compatibility check compatibility: diff --git a/tests/Component/Issuer/IssuerTest.php b/tests/Component/Issuer/IssuerTest.php index 86bf48e5..bfd830fc 100644 --- a/tests/Component/Issuer/IssuerTest.php +++ b/tests/Component/Issuer/IssuerTest.php @@ -35,7 +35,7 @@ public function testICanEnableIssuerPhp5(): void $this->assertStringContainsString('Details', $issuer->log); $this->assertStringContainsString('https://github.com/phpmetrics/PhpMetrics/issues/new', $output->output); $this->assertStringContainsString('Firstname: Jean-François', $issuer->log); - $this->assertStringContainsString('IssuerTest.php (line 26)', $issuer->log); + $this->assertStringContainsString('IssuerTest.php (line 29)', $issuer->log); $issuer->disable(); } diff --git a/tests/Polyfill/TestCaseCompatible.php b/tests/Polyfill/TestCaseCompatible.php index 55e038b3..08a6d0bb 100644 --- a/tests/Polyfill/TestCaseCompatible.php +++ b/tests/Polyfill/TestCaseCompatible.php @@ -7,12 +7,12 @@ trait TestCaseCompatible { public function assertStringContainsString($needle, $haystack, $message = '') { - $this->assertStringContainsString($needle, $haystack, $message); + $this->assertContains($needle, $haystack, $message); } public function assertMatchesRegularExpression($pattern, $string, $message = '') { - $this->assertMatchesRegularExpression($pattern, $string, $message); + $this->assertRegExp($pattern, $string, $message); } } } else { diff --git a/tests/binary/BinTest.php b/tests/binary/BinTest.php index 7ffbdad8..839d4154 100644 --- a/tests/binary/BinTest.php +++ b/tests/binary/BinTest.php @@ -1,11 +1,13 @@ phar = __DIR__ . '/../../releases/phpmetrics-linux-x86_64'; + $this->binary = __DIR__ . '/../../releases/phpmetrics-linux-x86_64'; } - public function testICanRunPhar(): void + public function testICanRunStandaloneBinary(): void { - $command = sprintf('%s --version', $this->phar); + $command = sprintf('%s --version', $this->binary); $r = shell_exec($command); $this->assertStringContainsString('PhpMetrics', $r); } public function testICanProvideOneDirectoryToParse(): void { - $command = sprintf('%s --exclude="" %s 2>&1', $this->phar, __DIR__ . '/examples/1'); + $command = sprintf('%s --exclude="" %s 2>&1', $this->binary, __DIR__ . '/examples/1'); $r = shell_exec($command); $this->assertStringContainsString('Object oriented programming', $r); $this->assertStringContainsString('LOC', $r); @@ -39,7 +41,7 @@ public function testICanProvideMultipleDirectoriesToParse(): void { $command = sprintf( '%s --exclude="" %s,%s 2>&1', - $this->phar, + $this->binary, __DIR__ . '/examples/1', __DIR__ . '/examples/2' );