diff --git a/composer.lock b/composer.lock index 8719206..818bda3 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "nikic/php-parser", - "version": "v5.2.0", + "version": "v5.3.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "23c79fbbfb725fb92af9bcf41065c8e9a0d49ddb" + "reference": "3abf7425cd284141dc5d8d14a9ee444de3345d1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/23c79fbbfb725fb92af9bcf41065c8e9a0d49ddb", - "reference": "23c79fbbfb725fb92af9bcf41065c8e9a0d49ddb", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3abf7425cd284141dc5d8d14a9ee444de3345d1a", + "reference": "3abf7425cd284141dc5d8d14a9ee444de3345d1a", "shasum": "" }, "require": { @@ -60,9 +60,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.2.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.0" }, - "time": "2024-09-15T16:40:33+00:00" + "time": "2024-09-29T13:56:26+00:00" }, { "name": "psr/clock", @@ -2593,16 +2593,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.4", + "version": "1.12.5", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "ffa517cb918591b93acc9b95c0bebdcd0e4538bd" + "reference": "7e6c6cb7cecb0a6254009a1a8a7d54ec99812b17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ffa517cb918591b93acc9b95c0bebdcd0e4538bd", - "reference": "ffa517cb918591b93acc9b95c0bebdcd0e4538bd", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/7e6c6cb7cecb0a6254009a1a8a7d54ec99812b17", + "reference": "7e6c6cb7cecb0a6254009a1a8a7d54ec99812b17", "shasum": "" }, "require": { @@ -2647,7 +2647,7 @@ "type": "github" } ], - "time": "2024-09-19T07:58:01+00:00" + "time": "2024-09-26T12:45:22+00:00" }, { "name": "phpunit/php-code-coverage", @@ -3078,12 +3078,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "4d2e39c44028ba729fe50efdf731d3d2ede4046b" + "reference": "5aa91c2f219db1bd35df0db7a24e45083b853276" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/4d2e39c44028ba729fe50efdf731d3d2ede4046b", - "reference": "4d2e39c44028ba729fe50efdf731d3d2ede4046b", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/5aa91c2f219db1bd35df0db7a24e45083b853276", + "reference": "5aa91c2f219db1bd35df0db7a24e45083b853276", "shasum": "" }, "conflict": { @@ -3094,7 +3094,7 @@ "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.04.6", "aimeos/ai-admin-jsonadm": "<2020.10.13|>=2021.04.1,<2021.10.6|>=2022.04.1,<2022.10.3|>=2023.04.1,<2023.10.4|==2024.04.1", "aimeos/ai-client-html": ">=2020.04.1,<2020.10.27|>=2021.04.1,<2021.10.22|>=2022.04.1,<2022.10.13|>=2023.04.1,<2023.10.15|>=2024.04.1,<2024.04.7", - "aimeos/ai-controller-frontend": "<2020.10.15|>=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.8|>=2023.04.1,<2023.10.9", + "aimeos/ai-controller-frontend": "<2020.10.15|>=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.8|>=2023.04.1,<2023.10.9|==2024.04.1", "aimeos/aimeos-core": ">=2022.04.1,<2022.10.17|>=2023.04.1,<2023.10.17|>=2024.04.1,<2024.04.7", "aimeos/aimeos-typo3": "<19.10.12|>=20,<20.10.5", "airesvsg/acf-to-rest-api": "<=3.1", @@ -3184,7 +3184,7 @@ "concrete5/core": "<8.5.8|>=9,<9.1", "contao-components/mediaelement": ">=2.14.2,<2.21.1", "contao/comments-bundle": ">=2,<4.13.40|>=5.0.0.0-RC1-dev,<5.3.4", - "contao/contao": ">=3,<3.5.37|>=4,<4.4.56|>=4.5,<4.9.40|>=4.10,<4.11.7|>=4.13,<4.13.21|>=5.1,<5.1.4", + "contao/contao": "<=5.4.1", "contao/core": "<3.5.39", "contao/core-bundle": "<4.13.49|>=5,<5.3.15|>=5.4,<5.4.3", "contao/listing-bundle": ">=3,<=3.5.30|>=4,<4.4.8", @@ -3221,9 +3221,9 @@ "dolibarr/dolibarr": "<19.0.2", "dompdf/dompdf": "<2.0.4", "doublethreedigital/guest-entries": "<3.1.2", - "drupal/core": ">=6,<6.38|>=7,<7.96|>=8,<=11.0.4", - "drupal/core-recommended": ">=8,<=11.0.4", - "drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.80|>=8,<=11.0.4", + "drupal/core": ">=6,<6.38|>=7,<7.96|>=8,<10.3.6|>=11,<11.0.5", + "drupal/core-recommended": ">=8,<10.3.6|>=11,<11.0.5", + "drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.80|>=8,<10.3.6|>=11,<11.0.5", "duncanmcclean/guest-entries": "<3.1.2", "dweeves/magmi": "<=0.7.24", "ec-cube/ec-cube": "<2.4.4|>=2.11,<=2.17.1|>=3,<=3.0.18.0-patch4|>=4,<=4.1.2", @@ -3266,6 +3266,8 @@ "feehi/cms": "<=2.1.1", "feehi/feehicms": "<=2.1.1", "fenom/fenom": "<=2.12.1", + "filament/infolists": ">=3,<3.2.115", + "filament/tables": ">=3,<3.2.115", "filegator/filegator": "<7.8", "filp/whoops": "<2.1.13", "fineuploader/php-traditional-server": "<=1.2.2", @@ -3423,7 +3425,7 @@ "magneto/core": "<1.9.4.4-dev", "maikuolan/phpmussel": ">=1,<1.6", "mainwp/mainwp": "<=4.4.3.3", - "mantisbt/mantisbt": "<2.26.2", + "mantisbt/mantisbt": "<=2.26.3", "marcwillmann/turn": "<0.3.3", "matyhtf/framework": "<3.0.6", "mautic/core": "<4.4.13|>=5,<5.1.1", @@ -3485,7 +3487,7 @@ "nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1", "october/backend": "<1.1.2", "october/cms": "<1.0.469|==1.0.469|==1.0.471|==1.1.1", - "october/october": "<=3.4.4", + "october/october": "<=3.6.4", "october/rain": "<1.0.472|>=1.1,<1.1.2", "october/system": "<1.0.476|>=1.1,<1.1.12|>=2,<2.2.34|>=3,<3.5.15", "omeka/omeka-s": "<4.0.3", @@ -3657,6 +3659,7 @@ "spoonity/tcpdf": "<6.2.22", "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", "ssddanbrown/bookstack": "<24.05.1", + "starcitizentools/citizen-skin": ">=2.6.3,<2.31", "statamic/cms": "<4.46|>=5.3,<5.6.2", "stormpath/sdk": "<9.9.99", "studio-42/elfinder": "<=2.1.64", @@ -3664,7 +3667,7 @@ "subhh/libconnect": "<7.0.8|>=8,<8.1", "sukohi/surpass": "<1", "sulu/form-bundle": ">=2,<2.5.3", - "sulu/sulu": "<1.6.44|>=2,<2.4.17|>=2.5,<2.5.13", + "sulu/sulu": "<1.6.44|>=2,<2.6.5", "sumocoders/framework-user-bundle": "<1.4", "superbig/craft-audit": "<3.0.2", "swag/paypal": "<5.4.4", @@ -3736,7 +3739,7 @@ "topthink/thinkphp": "<=3.2.3", "torrentpier/torrentpier": "<=2.4.3", "tpwd/ke_search": "<4.0.3|>=4.1,<4.6.6|>=5,<5.0.2", - "tribalsystems/zenario": "<9.5.60602", + "tribalsystems/zenario": "<=9.7.61188", "truckersmp/phpwhois": "<=4.3.1", "ttskch/pagination-service-provider": "<1", "twbs/bootstrap": "<=3.4.1|>=4,<=4.6.2", @@ -3895,7 +3898,7 @@ "type": "tidelift" } ], - "time": "2024-09-23T20:04:53+00:00" + "time": "2024-10-03T19:05:02+00:00" }, { "name": "sanmai/later", diff --git a/readme.md b/readme.md index c475f9d..ceff5ec 100644 --- a/readme.md +++ b/readme.md @@ -101,30 +101,6 @@ Class: Doctrine\ORM\Tools\Pagination\Paginator │ convertWhereInIdentifiersToDatabaseValues │ 11 (0) │ 1 (0) │ 1 (0) │ 5 (0.47) │ 1 (0) │ 0 (0) │ 0 (0) │ 0 (0) │ 0.47 │ └───────────────────────────────────────────┴────────┴───────────┴─────────┴───────────┴──────────┴───────┴────────────┴───────────┴────────────┘ -``` - -### Halstead Metrics - -```txt -Class: WP_Debug_Data -File: /home/florian/projects/cognitive-code-checker/tests/TestCode/FileWithTwoClasses.php -┌─────────────┬─────────────┬───────────┬──────────┬─────────┬────────────┬──────────┬────────────┬───────────┬──────────┐ -│ n1 Distinct │ n2 Distinct │ Total │ Total │ Program │ Program │ Volume │ Difficulty │ Effort │ Possible │ -│ Operators │ Operands │ Operators │ Operands │ Length │ Vocabulary │ │ │ │ Bugs │ -├─────────────┼─────────────┼───────────┼──────────┼─────────┼────────────┼──────────┼────────────┼───────────┼──────────┤ -│ 13 │ 563 │ 869 │ 2123 │ 2992 │ 576 │ 27436.42 │ 24.51 │ 672484.58 │ 9.15 │ -└─────────────┴─────────────┴───────────┴──────────┴─────────┴────────────┴──────────┴────────────┴───────────┴──────────┘ - -Class: Doctrine\ORM\Tools\Pagination\Paginator -File: /home/florian/projects/cognitive-code-checker/tests/TestCode/FileWithTwoClasses.php -┌─────────────┬─────────────┬───────────┬──────────┬─────────┬────────────┬─────────┬────────────┬──────────┬──────────┐ -│ n1 Distinct │ n2 Distinct │ Total │ Total │ Program │ Program │ Volume │ Difficulty │ Effort │ Possible │ -│ Operators │ Operands │ Operators │ Operands │ Length │ Vocabulary │ │ │ │ Bugs │ -├─────────────┼─────────────┼───────────┼──────────┼─────────┼────────────┼─────────┼────────────┼──────────┼──────────┤ -│ 7 │ 36 │ 108 │ 161 │ 269 │ 43 │ 1459.67 │ 15.65 │ 22847.82 │ 0.49 │ -└─────────────┴─────────────┴───────────┴──────────┴─────────┴────────────┴─────────┴────────────┴──────────┴──────────┘ -``` - ## License Copyright Florian Krämer diff --git a/src/Business/Cognitive/Exporter/CsvExporter.php b/src/Business/Cognitive/Exporter/CsvExporter.php index 88a1c89..b4a28ca 100644 --- a/src/Business/Cognitive/Exporter/CsvExporter.php +++ b/src/Business/Cognitive/Exporter/CsvExporter.php @@ -19,11 +19,23 @@ class CsvExporter implements DataExporterInterface 'Class', 'Method', 'Line Count', + 'Line Count Weight', + 'Line Count Weight Delta', 'Argument Count', + 'Argument Count Weight', + 'Argument Count Weight Delta', 'Return Count', + 'Return Count Weight', + 'Return Count Weight Delta', 'Variable Count', + 'Variable Count Weight', + 'Variable Count Weight Delta', 'Property Call Count', + 'Property Call Count Weight', + 'Property Call Count Weight Delta', 'If Nesting Level', + 'If Nesting Level Weight', + 'If Nesting Level Weight Delta', 'Else Count', 'Combined Cognitive Complexity' ]; @@ -49,13 +61,35 @@ public function export(CognitiveMetricsCollection $metrics, string $filename): v fputcsv($file, [ $data->getClass(), $data->getMethod(), + $data->getLineCount(), + $data->getLineCountWeight(), + (string)$data->getLineCountWeightDelta(), + $data->getArgCount(), + $data->getArgCountWeight(), + (string)$data->getArgCountWeightDelta(), + $data->getReturnCount(), + $data->getReturnCountWeight(), + (string)$data->getReturnCountWeightDelta(), + $data->getVariableCount(), + $data->getVariableCountWeight(), + (string)$data->getVariableCountWeightDelta(), + $data->getPropertyCallCount(), + $data->getPropertyCallCountWeight(), + (string)$data->getPropertyCallCountWeightDelta(), + $data->getIfNestingLevel(), + $data->getIfNestingLevelWeight(), + (string)$data->getIfNestingLevelWeightDelta(), + $data->getElseCount(), + $data->getElseCountWeight(), + (string)$data->getElseCountWeightDelta(), + $data->getScore() ]); } diff --git a/src/Business/Cognitive/Exporter/HtmlExporter.php b/src/Business/Cognitive/Exporter/HtmlExporter.php index ac69022..cbfb6a2 100644 --- a/src/Business/Cognitive/Exporter/HtmlExporter.php +++ b/src/Business/Cognitive/Exporter/HtmlExporter.php @@ -5,6 +5,7 @@ namespace Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\Exporter; use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\CognitiveMetricsCollection; +use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\Delta; use RuntimeException; /** @@ -16,7 +17,6 @@ class HtmlExporter implements DataExporterInterface * @var array */ private array $header = [ - 'Class', 'Method', 'Line Count', 'Argument Count', @@ -45,6 +45,29 @@ public function export(CognitiveMetricsCollection $metrics, string $filename): v } } + public function escape(string $string): string + { + return htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); + } + + public function formatNumber(float $number): string + { + return number_format($number, 3); + } + + public function generateMetricRow(int $count, float $weight, ?Delta $delta): string + { + $metricRow = '' . $count . ' (' . $this->formatNumber($weight) . ')'; + + if ($delta !== null && !$delta->hasNotChanged()) { + $badgeClass = $delta->hasIncreased() ? 'bg-danger' : 'bg-success'; + $deltaValue = $this->formatNumber($delta->getValue()); + $metricRow .= '
Δ ' . $deltaValue . ''; + } + + return $metricRow . ''; + } + /** * Generate HTML content using the metrics data. * @@ -66,37 +89,44 @@ private function generateHtml(CognitiveMetricsCollection $metrics): string -
-

Cognitive Metrics Report

- +
+

Cognitive Metrics Report -

+

+ This report contains the cognitive complexity metrics for the analyzed code in classes. +

+ + $methods) : ?> +
+ + + header as $column) : ?> - + - - - - - - - - - - - - - - - - - + + + + generateMetricRow($data->getLineCount(), $data->getLineCountWeight(), $data->getLineCountWeightDelta()); ?> + generateMetricRow($data->getArgCount(), $data->getArgCountWeight(), $data->getArgCountWeightDelta()); ?> + generateMetricRow($data->getIfCount(), $data->getIfCountWeight(), $data->getIfCountWeightDelta()); ?> + generateMetricRow($data->getIfNestingLevel(), $data->getIfNestingLevelWeight(), $data->getIfNestingLevelWeightDelta()); ?> + generateMetricRow($data->getElseCount(), $data->getElseCountWeight(), $data->getElseCountWeightDelta()); ?> + generateMetricRow($data->getReturnCount(), $data->getReturnCountWeight(), $data->getReturnCountWeightDelta()); ?> + generateMetricRow($data->getVariableCount(), $data->getVariableCountWeight(), $data->getVariableCountWeightDelta()); ?> + generateMetricRow($data->getPropertyCallCount(), $data->getPropertyCallCountWeight(), $data->getPropertyCallCountWeightDelta()); ?> + + +
escape((string)$class); ?>
escape($column); ?>
getClass(), ENT_QUOTES, 'UTF-8'); ?>getMethod(), ENT_QUOTES, 'UTF-8'); ?>getLineCount(), ENT_QUOTES, 'UTF-8') . ' (' . number_format($data->getLineCountWeight(), 3) . ')'; ?>getArgCount(), ENT_QUOTES, 'UTF-8') . ' (' . number_format($data->getArgCountWeight(), 3) . ')'; ?>getIfCount(), ENT_QUOTES, 'UTF-8') . ' (' . number_format($data->getIfCountWeight(), 3) . ')'; ?>getIfNestingLevel(), ENT_QUOTES, 'UTF-8') . ' (' . number_format($data->getIfNestingLevelWeight(), 3) . ')'; ?>getElseCount(), ENT_QUOTES, 'UTF-8') . ' (' . number_format($data->getElseCountWeight(), 3) . ')'; ?>getReturnCount(), ENT_QUOTES, 'UTF-8') . ' (' . number_format($data->getReturnCountWeight(), 3) . ')'; ?>getVariableCount(), ENT_QUOTES, 'UTF-8') . ' (' . number_format($data->getVariableCountWeight(), 3) . ')'; ?>getPropertyCallCount(), ENT_QUOTES, 'UTF-8') . ' (' . number_format($data->getPropertyCallCountWeight(), 3) . ')'; ?>getScore(), 3); ?>
escape($data->getMethod()); ?>formatNumber($data->getScore()); ?>
-
+ + + Δ ' . $delta->getValue() . ''; + $row[$key] .= PHP_EOL . 'Δ -' . $delta->getValue() . ''; } return $row; diff --git a/tests/Unit/Business/Cognitive/Exporter/CsvExporterTest.php b/tests/Unit/Business/Cognitive/Exporter/CsvExporterTest.php index d8b3de9..2074890 100644 --- a/tests/Unit/Business/Cognitive/Exporter/CsvExporterTest.php +++ b/tests/Unit/Business/Cognitive/Exporter/CsvExporterTest.php @@ -69,30 +69,56 @@ public function testExportCreatesFile(): void $header = fgetcsv($file); $this->assertSame([ - 0 => "Class", - 1 => "Method", - 2 => "Line Count", - 3 => "Argument Count", - 4 => "Return Count", - 5 => "Variable Count", - 6 => "Property Call Count", - 7 => "If Nesting Level", - 8 => "Else Count", - 9 => "Combined Cognitive Complexity", + 0 => 'Class', + 1 => 'Method', + 2 => 'Line Count', + 3 => 'Line Count Weight', + 4 => 'Line Count Weight Delta', + 5 => 'Argument Count', + 6 => 'Argument Count Weight', + 7 => 'Argument Count Weight Delta', + 8 => 'Return Count', + 9 => 'Return Count Weight', + 10 => 'Return Count Weight Delta', + 11 => 'Variable Count', + 12 => 'Variable Count Weight', + 13 => 'Variable Count Weight Delta', + 14 => 'Property Call Count', + 15 => 'Property Call Count Weight', + 16 => 'Property Call Count Weight Delta', + 17 => 'If Nesting Level', + 18 => 'If Nesting Level Weight', + 19 => 'If Nesting Level Weight Delta', + 20 => 'Else Count', + 21 => 'Combined Cognitive Complexity' ], $header); $data = fgetcsv($file); $this->assertSame([ - 0 => "TestClass", - 1 => "testMethod", - 2 => "10", - 3 => "2", - 4 => "1", - 5 => "5", - 6 => "3", - 7 => "2", - 8 => "1", - 9 => "0", + 0 => 'TestClass', + 1 => 'testMethod', + 2 => '10', + 3 => '0.5', + 4 => '', + 5 => '2', + 6 => '0.3', + 7 => '', + 8 => '1', + 9 => '0.2', + 10 => '', + 11 => '5', + 12 => '0.4', + 13 => '', + 14 => '3', + 15 => '0.3', + 16 => '', + 17 => '2', + 18 => '0.5', + 19 => '', + 20 => '1', + 21 => '0.2', + 22 => '', + 23 => '0' ], $data); fclose($file);