From f0913af6ee19bef386969e9e01a66e31928c005e Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Wed, 31 Dec 2025 00:09:41 -0500 Subject: [PATCH 1/9] Start CyclomaticComplexityRule --- README.md | 1 + config/extension.neon | 17 +- docs/CyclomaticComplexity.md | 190 ++++++++++++ src/Rules/CyclomaticComplexity/Config.php | 27 ++ .../CyclomaticComplexityRule.php | 171 +++++++++++ .../CyclomaticComplexity/ClassesOnlyTest.php | 48 +++ .../CustomThresholdTest.php | 88 ++++++ .../CyclomaticComplexityRuleTest.php | 72 +++++ .../Fixture/ExampleClass.php | 275 ++++++++++++++++++ .../CyclomaticComplexity/MethodsOnlyTest.php | 58 ++++ .../config/classes_only.neon | 10 + .../config/configured_rule.neon | 5 + .../config/custom_threshold.neon | 10 + .../config/methods_only.neon | 10 + 14 files changed, 981 insertions(+), 1 deletion(-) create mode 100644 docs/CyclomaticComplexity.md create mode 100644 src/Rules/CyclomaticComplexity/Config.php create mode 100644 src/Rules/CyclomaticComplexity/CyclomaticComplexityRule.php create mode 100644 tests/Rules/CyclomaticComplexity/ClassesOnlyTest.php create mode 100644 tests/Rules/CyclomaticComplexity/CustomThresholdTest.php create mode 100644 tests/Rules/CyclomaticComplexity/CyclomaticComplexityRuleTest.php create mode 100644 tests/Rules/CyclomaticComplexity/Fixture/ExampleClass.php create mode 100644 tests/Rules/CyclomaticComplexity/MethodsOnlyTest.php create mode 100644 tests/Rules/CyclomaticComplexity/config/classes_only.neon create mode 100644 tests/Rules/CyclomaticComplexity/config/configured_rule.neon create mode 100644 tests/Rules/CyclomaticComplexity/config/custom_threshold.neon create mode 100644 tests/Rules/CyclomaticComplexity/config/methods_only.neon diff --git a/README.md b/README.md index 67b7c54..2fa28fe 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,7 @@ parameters: | **[ForbidEvalExpressions](docs/ForbidEvalExpressions.md)** | Detects and reports usage of eval expressions | Eval Expressions | | **[ForbidExitExpressions](docs/ForbidExitExpressions.md)** | Detects and reports usage of exit and die expressions | Exit Expressions | | **[ForbidGotoStatements](docs/ForbidGotoStatements.md)** | Detects and reports usage of goto statements | Goto Statements | +| **[CyclomaticComplexity](docs/CyclomaticComplexity.md)** | Detects methods with high cyclomatic complexity | Methods, Classes | | **[NumberOfChildren](docs/NumberOfChildren.md)** | Detects classes with too many direct child classes | Class Hierarchy | | **[TooManyMethods](docs/TooManyMethods.md)** | Detects classes with too many methods | Classes, Interfaces, Traits, Enums | diff --git a/config/extension.neon b/config/extension.neon index 685c35f..a436080 100644 --- a/config/extension.neon +++ b/config/extension.neon @@ -68,6 +68,11 @@ parametersSchema: max_methods: int(), ignore_pattern: string(), ]), + cyclomatic_complexity: structure([ + report_level: int(), + show_classes_complexity: bool(), + show_methods_complexity: bool(), + ]), ]) # default parameters @@ -126,6 +131,10 @@ parameters: too_many_methods: max_methods: 25 ignore_pattern: '^(get|set|is)' + cyclomatic_complexity: + report_level: 10 + show_classes_complexity: true + show_methods_complexity: true services: - @@ -214,4 +223,10 @@ services: factory: Orrison\MeliorStan\Rules\TooManyMethods\Config arguments: - %meliorstan.too_many_methods.max_methods% - - %meliorstan.too_many_methods.ignore_pattern% \ No newline at end of file + - %meliorstan.too_many_methods.ignore_pattern% + - + factory: Orrison\MeliorStan\Rules\CyclomaticComplexity\Config + arguments: + - %meliorstan.cyclomatic_complexity.report_level% + - %meliorstan.cyclomatic_complexity.show_classes_complexity% + - %meliorstan.cyclomatic_complexity.show_methods_complexity% \ No newline at end of file diff --git a/docs/CyclomaticComplexity.md b/docs/CyclomaticComplexity.md new file mode 100644 index 0000000..36deac5 --- /dev/null +++ b/docs/CyclomaticComplexity.md @@ -0,0 +1,190 @@ +# CyclomaticComplexity + +This rule checks if methods have high cyclomatic complexity, which indicates code that may be difficult to understand, test, and maintain. + +Cyclomatic complexity is determined by the number of decision points in a method plus one for the method entry. Decision points include `if`, `elseif`, `while`, `do`, `for`, `foreach`, `case`, `catch`, ternary operators (`?:`), null coalesce (`??`), and boolean operators (`&&`, `||`, `and`, `or`). + +Generally: +- 1-4 is low complexity +- 5-7 indicates moderate complexity +- 8-10 is high complexity +- 11+ is very high complexity + +## Configuration + +This rule supports the following configuration options: + +### `report_level` +- **Type**: `int` +- **Default**: `10` +- **Description**: The cyclomatic complexity threshold. Methods with complexity exceeding this value will trigger an error. + +### `show_classes_complexity` +- **Type**: `bool` +- **Default**: `true` +- **Description**: When enabled, reports an error if the average complexity of all methods in a class exceeds the threshold. + +### `show_methods_complexity` +- **Type**: `bool` +- **Default**: `true` +- **Description**: When enabled, reports an error for individual methods that exceed the complexity threshold. + +## Usage + +Add the rule to your PHPStan configuration: + +```neon +includes: + - vendor/orrison/meliorstan/config/extension.neon + +rules: + - Orrison\MeliorStan\Rules\CyclomaticComplexity\CyclomaticComplexityRule + +parameters: + meliorstan: + cyclomatic_complexity: + report_level: 10 + show_classes_complexity: true + show_methods_complexity: true +``` + +## Examples + +### Default Configuration + +```php +isExpired()) { // 1 + return false; + } + + if ($payment->amount <= 0) { // 2 + return false; + } + + return $payment->gateway === 'stripe' // 3 + ? $this->processStripe($payment) + : $this->processDefault($payment); + } + + // ✗ Error: The method complexValidation() has a Cyclomatic Complexity of 11. The allowed threshold is 10. + public function complexValidation(array $data): bool + { + if (!isset($data['email'])) { // 1 + return false; + } elseif (!isset($data['name'])) { // 2 + return false; + } + + if ($data['type'] === 'premium') { // 3 + if ($data['plan'] === 'yearly') { // 4 + return true; + } elseif ($data['plan'] === 'monthly') { // 5 + return true; + } + } elseif ($data['type'] === 'basic') { // 6 + return true; + } + + foreach ($data['items'] as $item) { // 7 + if ($item['valid'] ?? false) { // 8 (if), 9 (??) + continue; + } + } + + return $data['confirmed'] && $data['verified']; // 10 (&&) + } +} +``` + +### Configuration Examples + +#### Custom Threshold + +```neon +parameters: + meliorstan: + cyclomatic_complexity: + report_level: 5 +``` + +```php +isActive()) { // 1 + return false; + } + + if ($user->role === 'admin') { // 2 + return true; + } elseif ($user->role === 'mod') { // 3 + return $user->verified; + } + + return $user->email && $user->verified; // 4 (&&) + } +} +``` + +#### Methods Only (Disable Class Average) + +```neon +parameters: + meliorstan: + cyclomatic_complexity: + show_classes_complexity: false +``` + +```php +reportLevel; + } + + public function getShowClassesComplexity(): bool + { + return $this->showClassesComplexity; + } + + public function getShowMethodsComplexity(): bool + { + return $this->showMethodsComplexity; + } +} diff --git a/src/Rules/CyclomaticComplexity/CyclomaticComplexityRule.php b/src/Rules/CyclomaticComplexity/CyclomaticComplexityRule.php new file mode 100644 index 0000000..2170e18 --- /dev/null +++ b/src/Rules/CyclomaticComplexity/CyclomaticComplexityRule.php @@ -0,0 +1,171 @@ + + */ +class CyclomaticComplexityRule implements Rule +{ + public const string ERROR_MESSAGE_TEMPLATE_METHOD = 'The %s %s() has a Cyclomatic Complexity of %d. The allowed threshold is %d.'; + + public const string ERROR_MESSAGE_TEMPLATE_CLASS = 'The %s "%s" has an average Cyclomatic Complexity of %.2f. The allowed threshold is %d.'; + + public function __construct( + protected Config $config, + ) {} + + /** + * @return class-string + */ + public function getNodeType(): string + { + return ClassLike::class; + } + + /** + * @param ClassLike $node + * + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + if (! $node->name instanceof Identifier) { + return []; + } + + $errors = []; + $methods = $node->getMethods(); + + if (count($methods) === 0) { + return []; + } + + $totalComplexity = 0; + + foreach ($methods as $method) { + $complexity = $this->calculateComplexity($method); + $totalComplexity += $complexity; + + if ($this->config->getShowMethodsComplexity() && $complexity > $this->config->getReportLevel()) { + $errors[] = RuleErrorBuilder::message( + sprintf( + self::ERROR_MESSAGE_TEMPLATE_METHOD, + 'method', + $method->name->toString(), + $complexity, + $this->config->getReportLevel() + ) + ) + ->identifier('MeliorStan.cyclomaticComplexity.method') + ->line($method->getStartLine()) + ->build(); + } + } + + if ($this->config->getShowClassesComplexity()) { + $averageComplexity = $totalComplexity / count($methods); + + if ($averageComplexity > $this->config->getReportLevel()) { + $nodeType = $this->getNodeTypeName($node); + $errors[] = RuleErrorBuilder::message( + sprintf( + self::ERROR_MESSAGE_TEMPLATE_CLASS, + $nodeType, + $node->name->toString(), + $averageComplexity, + $this->config->getReportLevel() + ) + ) + ->identifier('MeliorStan.cyclomaticComplexity.class') + ->build(); + } + } + + return $errors; + } + + protected function calculateComplexity(ClassMethod $method): int + { + $complexity = 1; // Base complexity for method entry + + $nodeFinder = new NodeFinder(); + + /** @var Node[] $nodes */ + $nodes = $nodeFinder->find($method->stmts ?? [], function (Node $node): bool { + return $node instanceof If_ + || $node instanceof ElseIf_ + || $node instanceof While_ + || $node instanceof Do_ + || $node instanceof For_ + || $node instanceof Foreach_ + || $node instanceof Case_ + || $node instanceof Catch_ + || $node instanceof Ternary + || $node instanceof Coalesce + || $node instanceof BooleanAnd + || $node instanceof BooleanOr + || $node instanceof LogicalAnd + || $node instanceof LogicalOr; + }); + + foreach ($nodes as $node) { + // Skip default case in switch statements + if ($node instanceof Case_ && $node->cond === null) { + continue; + } + $complexity++; + } + + return $complexity; + } + + protected function getNodeTypeName(ClassLike $node): string + { + if ($node instanceof Class_) { + return 'class'; + } + + if ($node instanceof Interface_) { + return 'interface'; + } + + if ($node instanceof Trait_) { + return 'trait'; + } + + if ($node instanceof Enum_) { + return 'enum'; + } + + return 'class'; + } +} diff --git a/tests/Rules/CyclomaticComplexity/ClassesOnlyTest.php b/tests/Rules/CyclomaticComplexity/ClassesOnlyTest.php new file mode 100644 index 0000000..29ca64d --- /dev/null +++ b/tests/Rules/CyclomaticComplexity/ClassesOnlyTest.php @@ -0,0 +1,48 @@ + + */ +class ClassesOnlyTest extends RuleTestCase +{ + public function testRule(): void + { + // With show_methods_complexity: false, only class average errors should appear + $this->analyse([__DIR__ . '/Fixture/ExampleClass.php'], [ + // HighComplexityClass class average (12.00) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'HighComplexityClass', 12.00, 10), + 43, + ], + // VeryHighAverageComplexity class average (12.00) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'VeryHighAverageComplexity', 12.00, 10), + 153, + ], + // HighComplexityTrait class average (11.00) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'trait', 'HighComplexityTrait', 11.00, 10), + 221, + ], + ]); + } + + /** + * @return string[] + */ + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/config/classes_only.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(CyclomaticComplexityRule::class); + } +} diff --git a/tests/Rules/CyclomaticComplexity/CustomThresholdTest.php b/tests/Rules/CyclomaticComplexity/CustomThresholdTest.php new file mode 100644 index 0000000..41c6d14 --- /dev/null +++ b/tests/Rules/CyclomaticComplexity/CustomThresholdTest.php @@ -0,0 +1,88 @@ + + */ +class CustomThresholdTest extends RuleTestCase +{ + public function testRule(): void + { + // With report_level: 5, more methods should be flagged + $this->analyse([__DIR__ . '/Fixture/ExampleClass.php'], [ + // HighComplexityClass::methodWithComplexityTen (complexity 10) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'methodWithComplexityTen', 10, 5), + 46, + ], + // HighComplexityClass::methodWithComplexityEleven (complexity 11) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'methodWithComplexityEleven', 11, 5), + 74, + ], + // HighComplexityClass::veryComplexMethod (complexity 15) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'veryComplexMethod', 15, 5), + 106, + ], + // HighComplexityClass class average (12.00) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'HighComplexityClass', 12.00, 5), + 43, + ], + // VeryHighAverageComplexity::complexMethodOne (complexity 12) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'complexMethodOne', 12, 5), + 156, + ], + // VeryHighAverageComplexity::complexMethodTwo (complexity 12) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'complexMethodTwo', 12, 5), + 188, + ], + // VeryHighAverageComplexity class average (12) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'VeryHighAverageComplexity', 12.00, 5), + 153, + ], + // HighComplexityTrait::traitComplexMethod (complexity 11) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'traitComplexMethod', 11, 5), + 224, + ], + // HighComplexityTrait class average (11) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'trait', 'HighComplexityTrait', 11.00, 5), + 221, + ], + // CatchAndLogicalOperators::methodWithCatchBlocks (complexity 7) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'methodWithCatchBlocks', 7, 5), + 260, + ], + // CatchAndLogicalOperators class average (7) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'CatchAndLogicalOperators', 7.00, 5), + 257, + ], + ]); + } + + /** + * @return string[] + */ + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/config/custom_threshold.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(CyclomaticComplexityRule::class); + } +} diff --git a/tests/Rules/CyclomaticComplexity/CyclomaticComplexityRuleTest.php b/tests/Rules/CyclomaticComplexity/CyclomaticComplexityRuleTest.php new file mode 100644 index 0000000..84bb8e3 --- /dev/null +++ b/tests/Rules/CyclomaticComplexity/CyclomaticComplexityRuleTest.php @@ -0,0 +1,72 @@ + + */ +class CyclomaticComplexityRuleTest extends RuleTestCase +{ + public function testRule(): void + { + $this->analyse([__DIR__ . '/Fixture/ExampleClass.php'], [ + // HighComplexityClass::methodWithComplexityEleven (complexity 11) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'methodWithComplexityEleven', 11, 10), + 74, + ], + // HighComplexityClass::veryComplexMethod (complexity 15) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'veryComplexMethod', 15, 10), + 106, + ], + // HighComplexityClass class average (12.00) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'HighComplexityClass', 12.00, 10), + 43, + ], + // VeryHighAverageComplexity::complexMethodOne (complexity 12) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'complexMethodOne', 12, 10), + 156, + ], + // VeryHighAverageComplexity::complexMethodTwo (complexity 12) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'complexMethodTwo', 12, 10), + 188, + ], + // VeryHighAverageComplexity class average (12.00) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'VeryHighAverageComplexity', 12.00, 10), + 153, + ], + // HighComplexityTrait::traitComplexMethod (complexity 11) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'traitComplexMethod', 11, 10), + 224, + ], + // HighComplexityTrait class average (11.00) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'trait', 'HighComplexityTrait', 11.00, 10), + 221, + ], + ]); + } + + /** + * @return string[] + */ + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/config/configured_rule.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(CyclomaticComplexityRule::class); + } +} diff --git a/tests/Rules/CyclomaticComplexity/Fixture/ExampleClass.php b/tests/Rules/CyclomaticComplexity/Fixture/ExampleClass.php new file mode 100644 index 0000000..2bd3569 --- /dev/null +++ b/tests/Rules/CyclomaticComplexity/Fixture/ExampleClass.php @@ -0,0 +1,275 @@ + 0) { + echo 'positive'; + } elseif ($a < 0) { + echo 'negative'; + } + + while ($b > 0) { + $b--; + } + + for ($i = 0; $i < 5; $i++) { + echo $i; + } + } +} + +class HighComplexityClass +{ + // Complexity: 10 (exactly at threshold, should NOT trigger error) + public function methodWithComplexityTen(int $a): void + { + if ($a > 0) { // 1 + if ($a > 10) { // 2 + echo 'large'; + } elseif ($a > 5) { // 3 + echo 'medium'; + } + } elseif ($a < 0) { // 4 + while ($a < 0) { // 5 + $a++; + } + } + + for ($i = 0; $i < 3; $i++) { // 6 + echo $i; + } + + foreach ([1, 2] as $v) { // 7 + echo $v; + } + + $x = $a > 0 ? 'yes' : 'no'; // 8 + + $y = $a ?? 0; // 9 + } + + // Complexity: 11 (exceeds threshold, should trigger error) + public function methodWithComplexityEleven(int $a): void + { + if ($a > 0) { // 1 + if ($a > 10) { // 2 + echo 'large'; + } elseif ($a > 5) { // 3 + echo 'medium'; + } + } elseif ($a < 0) { // 4 + while ($a < 0) { // 5 + $a++; + } + } + + for ($i = 0; $i < 3; $i++) { // 6 + echo $i; + } + + foreach ([1, 2] as $v) { // 7 + echo $v; + } + + $x = $a > 0 ? 'yes' : 'no'; // 8 + + $y = $a ?? 0; // 9 + + if ($a === 100) { // 10 + echo 'exact'; + } + } + + // Complexity: 15 (well exceeds threshold) + public function veryComplexMethod(int $a, int $b): void + { + if ($a > 0) { // 1 + if ($a > 10) { // 2 + echo 'large'; + } elseif ($a > 5) { // 3 + echo 'medium'; + } elseif ($a > 2) { // 4 + echo 'small'; + } + } elseif ($a < 0) { // 5 + while ($a < 0) { // 6 + $a++; + } + } + + for ($i = 0; $i < 3; $i++) { // 7 + foreach ([1, 2] as $v) { // 8 + echo $v; + } + } + + switch ($b) { + case 1: // 9 + echo 'one'; + + break; + case 2: // 10 + echo 'two'; + + break; + case 3: // 11 + echo 'three'; + + break; + + default: + echo 'other'; + } + + $x = $a > 0 && $b > 0 ? 'both' : 'not'; // 12 (&&), 13 (ternary) + + $y = $a || $b; // 14 + } +} + +// Class with high average complexity (should trigger class-level error) +class VeryHighAverageComplexity +{ + // Complexity: 12 + public function complexMethodOne(int $a): void + { + if ($a > 0) { // 1 + if ($a > 10) { // 2 + echo 'large'; + } elseif ($a > 5) { // 3 + echo 'medium'; + } + } elseif ($a < 0) { // 4 + while ($a < 0) { // 5 + $a++; + } + } + + for ($i = 0; $i < 3; $i++) { // 6 + echo $i; + } + + foreach ([1, 2] as $v) { // 7 + echo $v; + } + + $x = $a > 0 ? 'yes' : 'no'; // 8 + + $y = $a ?? 0; // 9 + + if ($a === 100 && $a > 50) { // 10 (if), 11 (&&) + echo 'exact'; + } + } + + // Complexity: 12 + public function complexMethodTwo(int $b): void + { + if ($b > 0) { // 1 + if ($b > 10) { // 2 + echo 'large'; + } elseif ($b > 5) { // 3 + echo 'medium'; + } + } elseif ($b < 0) { // 4 + do { // 5 + $b++; + } while ($b < 0); // not counted, do is the decision point + } + + for ($i = 0; $i < 3; $i++) { // 6 + echo $i; + } + + foreach ([1, 2] as $v) { // 7 + echo $v; + } + + $x = $b > 0 ? 'yes' : 'no'; // 8 + + $y = $b ?? 0; // 9 + + if ($b === 100 || $b < 0) { // 10 (if), 11 (||) + echo 'edge'; + } + } +} + +// Trait with high complexity method +trait HighComplexityTrait +{ + // Complexity: 11 + public function traitComplexMethod(int $a): void + { + if ($a > 0) { // 1 + if ($a > 10) { // 2 + echo 'large'; + } elseif ($a > 5) { // 3 + echo 'medium'; + } + } elseif ($a < 0) { // 4 + while ($a < 0) { // 5 + $a++; + } + } + + for ($i = 0; $i < 3; $i++) { // 6 + echo $i; + } + + foreach ([1, 2] as $v) { // 7 + echo $v; + } + + $x = $a > 0 ? 'yes' : 'no'; // 8 + + $y = $a ?? 0; // 9 + + if ($a === 100) { // 10 + echo 'exact'; + } + } +} + +// Test catch blocks and logical operators +class CatchAndLogicalOperators +{ + // Complexity: 7 (2 catch + if + 3 logical operators) + public function methodWithCatchBlocks(): void + { + try { + throw new Exception(); + } catch (RuntimeException $e) { // 1 + echo 'runtime'; + } catch (Exception $e) { // 2 + echo 'exception'; + } + + $a = true and false; // 3 + $b = true or false; // 4 + $c = true && false; // 5 + $d = true || false; // 6 + } +} diff --git a/tests/Rules/CyclomaticComplexity/MethodsOnlyTest.php b/tests/Rules/CyclomaticComplexity/MethodsOnlyTest.php new file mode 100644 index 0000000..5b75f6f --- /dev/null +++ b/tests/Rules/CyclomaticComplexity/MethodsOnlyTest.php @@ -0,0 +1,58 @@ + + */ +class MethodsOnlyTest extends RuleTestCase +{ + public function testRule(): void + { + // With show_classes_complexity: false, only method errors should appear + $this->analyse([__DIR__ . '/Fixture/ExampleClass.php'], [ + // HighComplexityClass::methodWithComplexityEleven (complexity 11) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'methodWithComplexityEleven', 11, 10), + 74, + ], + // HighComplexityClass::veryComplexMethod (complexity 15) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'veryComplexMethod', 15, 10), + 106, + ], + // VeryHighAverageComplexity::complexMethodOne (complexity 12) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'complexMethodOne', 12, 10), + 156, + ], + // VeryHighAverageComplexity::complexMethodTwo (complexity 12) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'complexMethodTwo', 12, 10), + 188, + ], + // HighComplexityTrait::traitComplexMethod (complexity 11) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'traitComplexMethod', 11, 10), + 224, + ], + ]); + } + + /** + * @return string[] + */ + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/config/methods_only.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(CyclomaticComplexityRule::class); + } +} diff --git a/tests/Rules/CyclomaticComplexity/config/classes_only.neon b/tests/Rules/CyclomaticComplexity/config/classes_only.neon new file mode 100644 index 0000000..bac493d --- /dev/null +++ b/tests/Rules/CyclomaticComplexity/config/classes_only.neon @@ -0,0 +1,10 @@ +includes: + - ../../../../config/extension.neon + +rules: + - Orrison\MeliorStan\Rules\CyclomaticComplexity\CyclomaticComplexityRule + +parameters: + meliorstan: + cyclomatic_complexity: + show_methods_complexity: false diff --git a/tests/Rules/CyclomaticComplexity/config/configured_rule.neon b/tests/Rules/CyclomaticComplexity/config/configured_rule.neon new file mode 100644 index 0000000..395160c --- /dev/null +++ b/tests/Rules/CyclomaticComplexity/config/configured_rule.neon @@ -0,0 +1,5 @@ +includes: + - ../../../../config/extension.neon + +rules: + - Orrison\MeliorStan\Rules\CyclomaticComplexity\CyclomaticComplexityRule diff --git a/tests/Rules/CyclomaticComplexity/config/custom_threshold.neon b/tests/Rules/CyclomaticComplexity/config/custom_threshold.neon new file mode 100644 index 0000000..61e6c17 --- /dev/null +++ b/tests/Rules/CyclomaticComplexity/config/custom_threshold.neon @@ -0,0 +1,10 @@ +includes: + - ../../../../config/extension.neon + +rules: + - Orrison\MeliorStan\Rules\CyclomaticComplexity\CyclomaticComplexityRule + +parameters: + meliorstan: + cyclomatic_complexity: + report_level: 5 diff --git a/tests/Rules/CyclomaticComplexity/config/methods_only.neon b/tests/Rules/CyclomaticComplexity/config/methods_only.neon new file mode 100644 index 0000000..515a020 --- /dev/null +++ b/tests/Rules/CyclomaticComplexity/config/methods_only.neon @@ -0,0 +1,10 @@ +includes: + - ../../../../config/extension.neon + +rules: + - Orrison\MeliorStan\Rules\CyclomaticComplexity\CyclomaticComplexityRule + +parameters: + meliorstan: + cyclomatic_complexity: + show_classes_complexity: false From c72eb2cff53d885643ec83cb782fb218c8411691 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Wed, 31 Dec 2025 00:32:10 -0500 Subject: [PATCH 2/9] Start CyclomaticComplexityRule --- CLAUDE.md | 42 +- docs/CyclomaticComplexity.md | 1 + .../AllOptionsTrueTest.php | 20 +- .../Fixture/AllOptions.php | 25 -- .../Fixture/AllOptionsFunctions.php | 7 + .../Fixture/AllOptionsIgnoredClass.php | 12 + .../Fixture/AllOptionsNotIgnored.php | 12 + .../{IgnoredClasses.php => IgnoredClass.php} | 11 - .../Fixture/IgnoredClassFunctions.php | 7 + .../Fixture/NotIgnoredClass.php | 10 + .../IgnoredInClassesTest.php | 20 +- .../{InterfaceEnum.php => ExampleEnum.php} | 7 - .../Fixture/ExampleInterface.php | 10 + .../InterfaceEnumTest.php | 9 +- .../DefaultOptionsTest.php | 18 +- .../Fixture/AnotherExample.php | 9 + .../Fixture/ExampleClass.php | 18 - .../Fixture/ExampleInterface.php | 9 + .../Fixture/ExampleTrait.php | 9 + .../CyclomaticComplexity/ClassesOnlyTest.php | 37 +- .../CustomThresholdTest.php | 117 +++--- .../CyclomaticComplexityRuleTest.php | 87 ++-- .../Fixture/CatchAndLogicalOperators.php | 27 ++ .../Fixture/ExampleClass.php | 275 ------------ .../Fixture/HighComplexityClass.php | 112 +++++ .../Fixture/HighComplexityTrait.php | 39 ++ .../Fixture/LowComplexityClass.php | 38 ++ .../Fixture/VeryHighAverageComplexity.php | 71 ++++ .../CyclomaticComplexity/MethodsOnlyTest.php | 57 +-- ...NameThatExceedsTheDefaultMaximumLength.php | 5 + ...ameThatExceedsTheDefaultMaximumLength.php} | 2 - ...NameThatExceedsTheDefaultMaximumLength.php | 5 + .../LongClassName/LongClassNameRuleTest.php | 12 +- .../ShortClassName/CustomMinimumTest.php | 9 +- tests/Rules/ShortClassName/ExceptionsTest.php | 11 +- tests/Rules/ShortClassName/Fixture/A.php | 5 + tests/Rules/ShortClassName/Fixture/AB.php | 5 + .../Fixture/{ExampleClass.php => ABC.php} | 2 - tests/Rules/ShortClassName/Fixture/E.php | 5 + .../Fixture/{OtherTypes.php => I.php} | 2 - tests/Rules/ShortClassName/Fixture/T.php | 5 + .../ShortClassName/ShortClassNameRuleTest.php | 21 +- .../TooManyMethods/CustomMaxMethodsTest.php | 57 ++- .../Fixture/ClassExceedingLimit.php | 62 +++ .../Fixture/ClassWithFewMethods.php | 20 + .../Fixture/ClassWithManyMethods.php | 121 ++++++ .../Fixture/ClassWithSixMethods.php | 21 + .../Fixture/EnumExceedingLimit.php | 64 +++ .../TooManyMethods/Fixture/ExampleClass.php | 394 ------------------ .../Fixture/InterfaceExceedingLimit.php | 62 +++ .../Fixture/TraitExceedingLimit.php | 62 +++ .../TooManyMethods/NoIgnorePatternTest.php | 49 ++- .../TooManyMethods/TooManyMethodsRuleTest.php | 41 +- 53 files changed, 1205 insertions(+), 953 deletions(-) delete mode 100644 tests/Rules/BooleanArgumentFlag/Fixture/AllOptions.php create mode 100644 tests/Rules/BooleanArgumentFlag/Fixture/AllOptionsFunctions.php create mode 100644 tests/Rules/BooleanArgumentFlag/Fixture/AllOptionsIgnoredClass.php create mode 100644 tests/Rules/BooleanArgumentFlag/Fixture/AllOptionsNotIgnored.php rename tests/Rules/BooleanArgumentFlag/Fixture/{IgnoredClasses.php => IgnoredClass.php} (54%) create mode 100644 tests/Rules/BooleanArgumentFlag/Fixture/IgnoredClassFunctions.php create mode 100644 tests/Rules/BooleanArgumentFlag/Fixture/NotIgnoredClass.php rename tests/Rules/ConstantNamingConventions/Fixture/{InterfaceEnum.php => ExampleEnum.php} (64%) create mode 100644 tests/Rules/ConstantNamingConventions/Fixture/ExampleInterface.php create mode 100644 tests/Rules/ConstructorWithNameAsEnclosingClass/Fixture/AnotherExample.php create mode 100644 tests/Rules/ConstructorWithNameAsEnclosingClass/Fixture/ExampleInterface.php create mode 100644 tests/Rules/ConstructorWithNameAsEnclosingClass/Fixture/ExampleTrait.php create mode 100644 tests/Rules/CyclomaticComplexity/Fixture/CatchAndLogicalOperators.php delete mode 100644 tests/Rules/CyclomaticComplexity/Fixture/ExampleClass.php create mode 100644 tests/Rules/CyclomaticComplexity/Fixture/HighComplexityClass.php create mode 100644 tests/Rules/CyclomaticComplexity/Fixture/HighComplexityTrait.php create mode 100644 tests/Rules/CyclomaticComplexity/Fixture/LowComplexityClass.php create mode 100644 tests/Rules/CyclomaticComplexity/Fixture/VeryHighAverageComplexity.php create mode 100644 tests/Rules/LongClassName/Fixture/VeryLongEnumNameThatExceedsTheDefaultMaximumLength.php rename tests/Rules/LongClassName/Fixture/{OtherTypes.php => VeryLongInterfaceNameThatExceedsTheDefaultMaximumLength.php} (54%) create mode 100644 tests/Rules/LongClassName/Fixture/VeryLongTraitNameThatExceedsTheDefaultMaximumLength.php create mode 100644 tests/Rules/ShortClassName/Fixture/A.php create mode 100644 tests/Rules/ShortClassName/Fixture/AB.php rename tests/Rules/ShortClassName/Fixture/{ExampleClass.php => ABC.php} (78%) create mode 100644 tests/Rules/ShortClassName/Fixture/E.php rename tests/Rules/ShortClassName/Fixture/{OtherTypes.php => I.php} (80%) create mode 100644 tests/Rules/ShortClassName/Fixture/T.php create mode 100644 tests/Rules/TooManyMethods/Fixture/ClassExceedingLimit.php create mode 100644 tests/Rules/TooManyMethods/Fixture/ClassWithFewMethods.php create mode 100644 tests/Rules/TooManyMethods/Fixture/ClassWithManyMethods.php create mode 100644 tests/Rules/TooManyMethods/Fixture/ClassWithSixMethods.php create mode 100644 tests/Rules/TooManyMethods/Fixture/EnumExceedingLimit.php delete mode 100644 tests/Rules/TooManyMethods/Fixture/ExampleClass.php create mode 100644 tests/Rules/TooManyMethods/Fixture/InterfaceExceedingLimit.php create mode 100644 tests/Rules/TooManyMethods/Fixture/TraitExceedingLimit.php diff --git a/CLAUDE.md b/CLAUDE.md index f6155bc..41bf711 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,8 +14,7 @@ Each rule follows a 3-component structure: ### Key Files - `config/extension.neon` - Central dependency injection & parameter schema definition - `tests/Rules/*/config/*.neon` - Per-test configuration files that override defaults -- `tests/Rules/*/Fixture/ExampleClass.php` - Test fixtures with various naming patterns - - Though this is the primary fixture we have used so far, feel free to add more or different fixture files if needed for specific test cases. +- `tests/Rules/*/Fixture/*.php` - Test fixtures with various naming patterns (one class/trait/interface/enum per file) ## Critical Development Workflows @@ -148,6 +147,45 @@ $pattern .= '$/'; ## Test Conventions +### PSR-4 Fixture File Standards +**CRITICAL**: All test fixture files MUST follow PSR-4 autoloading standards: + +1. **One class/trait/interface/enum per file**: Each PHP type must be in its own file +2. **Filename matches type name**: A class `MyClass` must be in `MyClass.php` +3. **No multi-type files**: Never put multiple classes, traits, interfaces, or enums in the same file + +**Example - CORRECT:** +``` +tests/Rules/MyRule/Fixture/ +├── ExampleClass.php # Contains only: class ExampleClass +├── AnotherClass.php # Contains only: class AnotherClass +├── ExampleTrait.php # Contains only: trait ExampleTrait +├── ExampleInterface.php # Contains only: interface ExampleInterface +└── ExampleEnum.php # Contains only: enum ExampleEnum +``` + +**Example - INCORRECT:** +```php +// ExampleClass.php - DON'T DO THIS +class ExampleClass {} +class AnotherClass {} // Should be in AnotherClass.php +trait ExampleTrait {} // Should be in ExampleTrait.php +``` + +When tests need multiple fixture types, list them all in the `$this->analyse()` call: +```php +$this->analyse( + [ + __DIR__ . '/Fixture/ExampleClass.php', + __DIR__ . '/Fixture/AnotherClass.php', + __DIR__ . '/Fixture/ExampleTrait.php', + ], + [ + // expected errors with line numbers + ] +); +``` + ### Test Structure Each rule has multiple test classes covering all possible different configuration combinations, examples are: - `DefaultOptionsTest` - Default configuration diff --git a/docs/CyclomaticComplexity.md b/docs/CyclomaticComplexity.md index 36deac5..58fe95a 100644 --- a/docs/CyclomaticComplexity.md +++ b/docs/CyclomaticComplexity.md @@ -34,6 +34,7 @@ This rule supports the following configuration options: Add the rule to your PHPStan configuration: ```neon + includes: - vendor/orrison/meliorstan/config/extension.neon diff --git a/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php b/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php index 17eb49b..4550d01 100644 --- a/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php +++ b/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php @@ -13,12 +13,24 @@ class AllOptionsTrueTest extends RuleTestCase { public function testAllOptions(): void { + // AllOptionsIgnoredClass.php should have no errors as the class is in the ignored list $this->analyse([ - __DIR__ . '/Fixture/AllOptions.php', + __DIR__ . '/Fixture/AllOptionsIgnoredClass.php', + ], []); + + // AllOptionsNotIgnored.php - setOption is ignored by pattern, but handle and processData should error + $this->analyse([ + __DIR__ . '/Fixture/AllOptionsNotIgnored.php', + ], [ + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\AllOptionsNotIgnored', 'handle', 'flag'), 9], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\AllOptionsNotIgnored', 'processData', 'flag'), 11], + ]); + + // AllOptionsFunctions.php - setConfig is ignored by pattern, but processConfig should error + $this->analyse([ + __DIR__ . '/Fixture/AllOptionsFunctions.php', ], [ - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\AllOptionsNotIgnored', 'handle', 'flag'), 18], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\AllOptionsNotIgnored', 'processData', 'flag'), 20], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'processConfig', 'value'), 25], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'processConfig', 'value'), 7], ]); } diff --git a/tests/Rules/BooleanArgumentFlag/Fixture/AllOptions.php b/tests/Rules/BooleanArgumentFlag/Fixture/AllOptions.php deleted file mode 100644 index 5bf68e9..0000000 --- a/tests/Rules/BooleanArgumentFlag/Fixture/AllOptions.php +++ /dev/null @@ -1,25 +0,0 @@ -analyse([ - __DIR__ . '/Fixture/IgnoredClasses.php', + __DIR__ . '/Fixture/IgnoredClass.php', + ], []); + + // NotIgnoredClass.php should have errors as the class is not in the ignored list + $this->analyse([ + __DIR__ . '/Fixture/NotIgnoredClass.php', + ], [ + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\NotIgnoredClass', 'methodWithBool', 'flag'), 7], + ]); + + // Functions and closures should have errors as they are not in any class + $this->analyse([ + __DIR__ . '/Fixture/IgnoredClassFunctions.php', ], [ - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\NotIgnoredClass', 'methodWithBool', 'flag'), 16], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'functionWithBool', 'flag'), 21], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 23], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'functionWithBool', 'flag'), 5], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 7], ]); } diff --git a/tests/Rules/ConstantNamingConventions/Fixture/InterfaceEnum.php b/tests/Rules/ConstantNamingConventions/Fixture/ExampleEnum.php similarity index 64% rename from tests/Rules/ConstantNamingConventions/Fixture/InterfaceEnum.php rename to tests/Rules/ConstantNamingConventions/Fixture/ExampleEnum.php index d8953e8..e64a181 100644 --- a/tests/Rules/ConstantNamingConventions/Fixture/InterfaceEnum.php +++ b/tests/Rules/ConstantNamingConventions/Fixture/ExampleEnum.php @@ -2,13 +2,6 @@ namespace Orrison\MeliorStan\Tests\Rules\ConstantNamingConventions\Fixture; -interface ExampleInterface -{ - public const INTERFACE_CONSTANT = 'valid'; - - public const interfaceConstant = 'invalid'; -} - enum ExampleEnum { public const ENUM_CONSTANT = 'valid'; diff --git a/tests/Rules/ConstantNamingConventions/Fixture/ExampleInterface.php b/tests/Rules/ConstantNamingConventions/Fixture/ExampleInterface.php new file mode 100644 index 0000000..0da73bd --- /dev/null +++ b/tests/Rules/ConstantNamingConventions/Fixture/ExampleInterface.php @@ -0,0 +1,10 @@ +analyse([ - __DIR__ . '/Fixture/InterfaceEnum.php', + __DIR__ . '/Fixture/ExampleInterface.php', ], [ [sprintf(ConstantNamingConventionsRule::ERROR_MESSAGE_TEMPLATE, 'interfaceConstant'), 9], - [sprintf(ConstantNamingConventionsRule::ERROR_MESSAGE_TEMPLATE, 'enumConstant'), 16], + ]); + + $this->analyse([ + __DIR__ . '/Fixture/ExampleEnum.php', + ], [ + [sprintf(ConstantNamingConventionsRule::ERROR_MESSAGE_TEMPLATE, 'enumConstant'), 9], ]); } diff --git a/tests/Rules/ConstructorWithNameAsEnclosingClass/DefaultOptionsTest.php b/tests/Rules/ConstructorWithNameAsEnclosingClass/DefaultOptionsTest.php index 7c03707..0e5a30b 100644 --- a/tests/Rules/ConstructorWithNameAsEnclosingClass/DefaultOptionsTest.php +++ b/tests/Rules/ConstructorWithNameAsEnclosingClass/DefaultOptionsTest.php @@ -13,12 +13,18 @@ class DefaultOptionsTest extends RuleTestCase { public function testExampleClass(): void { - $this->analyse([ - __DIR__ . '/Fixture/ExampleClass.php', - ], [ - [sprintf(ConstructorWithNameAsEnclosingClassRule::ERROR_MESSAGE_TEMPLATE, 'ExampleClass', 'ExampleClass'), 14], - [sprintf(ConstructorWithNameAsEnclosingClassRule::ERROR_MESSAGE_TEMPLATE, 'AnotherExample', 'AnotherExample'), 23], - ]); + $this->analyse( + [ + __DIR__ . '/Fixture/ExampleClass.php', + __DIR__ . '/Fixture/AnotherExample.php', + __DIR__ . '/Fixture/ExampleTrait.php', + __DIR__ . '/Fixture/ExampleInterface.php', + ], + [ + [sprintf(ConstructorWithNameAsEnclosingClassRule::ERROR_MESSAGE_TEMPLATE, 'ExampleClass', 'ExampleClass'), 14], + [sprintf(ConstructorWithNameAsEnclosingClassRule::ERROR_MESSAGE_TEMPLATE, 'AnotherExample', 'AnotherExample'), 8], + ] + ); } public static function getAdditionalConfigFiles(): array diff --git a/tests/Rules/ConstructorWithNameAsEnclosingClass/Fixture/AnotherExample.php b/tests/Rules/ConstructorWithNameAsEnclosingClass/Fixture/AnotherExample.php new file mode 100644 index 0000000..88332f4 --- /dev/null +++ b/tests/Rules/ConstructorWithNameAsEnclosingClass/Fixture/AnotherExample.php @@ -0,0 +1,9 @@ +analyse([__DIR__ . '/Fixture/ExampleClass.php'], [ - // HighComplexityClass class average (12.00) + $this->analyse( [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'HighComplexityClass', 12.00, 10), - 43, + __DIR__ . '/Fixture/LowComplexityClass.php', + __DIR__ . '/Fixture/HighComplexityClass.php', + __DIR__ . '/Fixture/VeryHighAverageComplexity.php', + __DIR__ . '/Fixture/HighComplexityTrait.php', + __DIR__ . '/Fixture/CatchAndLogicalOperators.php', ], - // VeryHighAverageComplexity class average (12.00) [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'VeryHighAverageComplexity', 12.00, 10), - 153, - ], - // HighComplexityTrait class average (11.00) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'trait', 'HighComplexityTrait', 11.00, 10), - 221, - ], - ]); + // HighComplexityClass class average (12.00) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'HighComplexityClass', 12.00, 10), + 5, + ], + // VeryHighAverageComplexity class average (12.00) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'VeryHighAverageComplexity', 12.00, 10), + 6, + ], + // HighComplexityTrait class average (11.00) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'trait', 'HighComplexityTrait', 11.00, 10), + 6, + ], + ] + ); } /** diff --git a/tests/Rules/CyclomaticComplexity/CustomThresholdTest.php b/tests/Rules/CyclomaticComplexity/CustomThresholdTest.php index 41c6d14..ea1f647 100644 --- a/tests/Rules/CyclomaticComplexity/CustomThresholdTest.php +++ b/tests/Rules/CyclomaticComplexity/CustomThresholdTest.php @@ -14,63 +14,72 @@ class CustomThresholdTest extends RuleTestCase public function testRule(): void { // With report_level: 5, more methods should be flagged - $this->analyse([__DIR__ . '/Fixture/ExampleClass.php'], [ - // HighComplexityClass::methodWithComplexityTen (complexity 10) + $this->analyse( [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'methodWithComplexityTen', 10, 5), - 46, + __DIR__ . '/Fixture/LowComplexityClass.php', + __DIR__ . '/Fixture/HighComplexityClass.php', + __DIR__ . '/Fixture/VeryHighAverageComplexity.php', + __DIR__ . '/Fixture/HighComplexityTrait.php', + __DIR__ . '/Fixture/CatchAndLogicalOperators.php', ], - // HighComplexityClass::methodWithComplexityEleven (complexity 11) [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'methodWithComplexityEleven', 11, 5), - 74, - ], - // HighComplexityClass::veryComplexMethod (complexity 15) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'veryComplexMethod', 15, 5), - 106, - ], - // HighComplexityClass class average (12.00) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'HighComplexityClass', 12.00, 5), - 43, - ], - // VeryHighAverageComplexity::complexMethodOne (complexity 12) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'complexMethodOne', 12, 5), - 156, - ], - // VeryHighAverageComplexity::complexMethodTwo (complexity 12) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'complexMethodTwo', 12, 5), - 188, - ], - // VeryHighAverageComplexity class average (12) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'VeryHighAverageComplexity', 12.00, 5), - 153, - ], - // HighComplexityTrait::traitComplexMethod (complexity 11) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'traitComplexMethod', 11, 5), - 224, - ], - // HighComplexityTrait class average (11) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'trait', 'HighComplexityTrait', 11.00, 5), - 221, - ], - // CatchAndLogicalOperators::methodWithCatchBlocks (complexity 7) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'methodWithCatchBlocks', 7, 5), - 260, - ], - // CatchAndLogicalOperators class average (7) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'CatchAndLogicalOperators', 7.00, 5), - 257, - ], - ]); + // HighComplexityClass::methodWithComplexityTen (complexity 10) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'methodWithComplexityTen', 10, 5), + 8, + ], + // HighComplexityClass::methodWithComplexityEleven (complexity 11) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'methodWithComplexityEleven', 11, 5), + 36, + ], + // HighComplexityClass::veryComplexMethod (complexity 15) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'veryComplexMethod', 15, 5), + 68, + ], + // HighComplexityClass class average (12.00) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'HighComplexityClass', 12.00, 5), + 5, + ], + // VeryHighAverageComplexity::complexMethodOne (complexity 12) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'complexMethodOne', 12, 5), + 9, + ], + // VeryHighAverageComplexity::complexMethodTwo (complexity 12) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'complexMethodTwo', 12, 5), + 41, + ], + // VeryHighAverageComplexity class average (12.00) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'VeryHighAverageComplexity', 12.00, 5), + 6, + ], + // HighComplexityTrait::traitComplexMethod (complexity 11) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'traitComplexMethod', 11, 5), + 9, + ], + // HighComplexityTrait class average (11.00) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'trait', 'HighComplexityTrait', 11.00, 5), + 6, + ], + // CatchAndLogicalOperators::methodWithCatchBlocks (complexity 7) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'methodWithCatchBlocks', 7, 5), + 12, + ], + // CatchAndLogicalOperators class average (7.00) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'CatchAndLogicalOperators', 7.00, 5), + 9, + ], + ] + ); } /** diff --git a/tests/Rules/CyclomaticComplexity/CyclomaticComplexityRuleTest.php b/tests/Rules/CyclomaticComplexity/CyclomaticComplexityRuleTest.php index 84bb8e3..ad3ed96 100644 --- a/tests/Rules/CyclomaticComplexity/CyclomaticComplexityRuleTest.php +++ b/tests/Rules/CyclomaticComplexity/CyclomaticComplexityRuleTest.php @@ -13,48 +13,57 @@ class CyclomaticComplexityRuleTest extends RuleTestCase { public function testRule(): void { - $this->analyse([__DIR__ . '/Fixture/ExampleClass.php'], [ - // HighComplexityClass::methodWithComplexityEleven (complexity 11) + $this->analyse( [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'methodWithComplexityEleven', 11, 10), - 74, + __DIR__ . '/Fixture/LowComplexityClass.php', + __DIR__ . '/Fixture/HighComplexityClass.php', + __DIR__ . '/Fixture/VeryHighAverageComplexity.php', + __DIR__ . '/Fixture/HighComplexityTrait.php', + __DIR__ . '/Fixture/CatchAndLogicalOperators.php', ], - // HighComplexityClass::veryComplexMethod (complexity 15) [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'veryComplexMethod', 15, 10), - 106, - ], - // HighComplexityClass class average (12.00) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'HighComplexityClass', 12.00, 10), - 43, - ], - // VeryHighAverageComplexity::complexMethodOne (complexity 12) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'complexMethodOne', 12, 10), - 156, - ], - // VeryHighAverageComplexity::complexMethodTwo (complexity 12) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'complexMethodTwo', 12, 10), - 188, - ], - // VeryHighAverageComplexity class average (12.00) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'VeryHighAverageComplexity', 12.00, 10), - 153, - ], - // HighComplexityTrait::traitComplexMethod (complexity 11) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'traitComplexMethod', 11, 10), - 224, - ], - // HighComplexityTrait class average (11.00) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'trait', 'HighComplexityTrait', 11.00, 10), - 221, - ], - ]); + // HighComplexityClass::methodWithComplexityEleven (complexity 11) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'methodWithComplexityEleven', 11, 10), + 36, + ], + // HighComplexityClass::veryComplexMethod (complexity 15) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'veryComplexMethod', 15, 10), + 68, + ], + // HighComplexityClass class average (12.00) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'HighComplexityClass', 12.00, 10), + 5, + ], + // VeryHighAverageComplexity::complexMethodOne (complexity 12) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'complexMethodOne', 12, 10), + 9, + ], + // VeryHighAverageComplexity::complexMethodTwo (complexity 12) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'complexMethodTwo', 12, 10), + 41, + ], + // VeryHighAverageComplexity class average (12.00) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'class', 'VeryHighAverageComplexity', 12.00, 10), + 6, + ], + // HighComplexityTrait::traitComplexMethod (complexity 11) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'traitComplexMethod', 11, 10), + 9, + ], + // HighComplexityTrait class average (11.00) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_CLASS, 'trait', 'HighComplexityTrait', 11.00, 10), + 6, + ], + ] + ); } /** diff --git a/tests/Rules/CyclomaticComplexity/Fixture/CatchAndLogicalOperators.php b/tests/Rules/CyclomaticComplexity/Fixture/CatchAndLogicalOperators.php new file mode 100644 index 0000000..85fbcee --- /dev/null +++ b/tests/Rules/CyclomaticComplexity/Fixture/CatchAndLogicalOperators.php @@ -0,0 +1,27 @@ + 0) { - echo 'positive'; - } elseif ($a < 0) { - echo 'negative'; - } - - while ($b > 0) { - $b--; - } - - for ($i = 0; $i < 5; $i++) { - echo $i; - } - } -} - -class HighComplexityClass -{ - // Complexity: 10 (exactly at threshold, should NOT trigger error) - public function methodWithComplexityTen(int $a): void - { - if ($a > 0) { // 1 - if ($a > 10) { // 2 - echo 'large'; - } elseif ($a > 5) { // 3 - echo 'medium'; - } - } elseif ($a < 0) { // 4 - while ($a < 0) { // 5 - $a++; - } - } - - for ($i = 0; $i < 3; $i++) { // 6 - echo $i; - } - - foreach ([1, 2] as $v) { // 7 - echo $v; - } - - $x = $a > 0 ? 'yes' : 'no'; // 8 - - $y = $a ?? 0; // 9 - } - - // Complexity: 11 (exceeds threshold, should trigger error) - public function methodWithComplexityEleven(int $a): void - { - if ($a > 0) { // 1 - if ($a > 10) { // 2 - echo 'large'; - } elseif ($a > 5) { // 3 - echo 'medium'; - } - } elseif ($a < 0) { // 4 - while ($a < 0) { // 5 - $a++; - } - } - - for ($i = 0; $i < 3; $i++) { // 6 - echo $i; - } - - foreach ([1, 2] as $v) { // 7 - echo $v; - } - - $x = $a > 0 ? 'yes' : 'no'; // 8 - - $y = $a ?? 0; // 9 - - if ($a === 100) { // 10 - echo 'exact'; - } - } - - // Complexity: 15 (well exceeds threshold) - public function veryComplexMethod(int $a, int $b): void - { - if ($a > 0) { // 1 - if ($a > 10) { // 2 - echo 'large'; - } elseif ($a > 5) { // 3 - echo 'medium'; - } elseif ($a > 2) { // 4 - echo 'small'; - } - } elseif ($a < 0) { // 5 - while ($a < 0) { // 6 - $a++; - } - } - - for ($i = 0; $i < 3; $i++) { // 7 - foreach ([1, 2] as $v) { // 8 - echo $v; - } - } - - switch ($b) { - case 1: // 9 - echo 'one'; - - break; - case 2: // 10 - echo 'two'; - - break; - case 3: // 11 - echo 'three'; - - break; - - default: - echo 'other'; - } - - $x = $a > 0 && $b > 0 ? 'both' : 'not'; // 12 (&&), 13 (ternary) - - $y = $a || $b; // 14 - } -} - -// Class with high average complexity (should trigger class-level error) -class VeryHighAverageComplexity -{ - // Complexity: 12 - public function complexMethodOne(int $a): void - { - if ($a > 0) { // 1 - if ($a > 10) { // 2 - echo 'large'; - } elseif ($a > 5) { // 3 - echo 'medium'; - } - } elseif ($a < 0) { // 4 - while ($a < 0) { // 5 - $a++; - } - } - - for ($i = 0; $i < 3; $i++) { // 6 - echo $i; - } - - foreach ([1, 2] as $v) { // 7 - echo $v; - } - - $x = $a > 0 ? 'yes' : 'no'; // 8 - - $y = $a ?? 0; // 9 - - if ($a === 100 && $a > 50) { // 10 (if), 11 (&&) - echo 'exact'; - } - } - - // Complexity: 12 - public function complexMethodTwo(int $b): void - { - if ($b > 0) { // 1 - if ($b > 10) { // 2 - echo 'large'; - } elseif ($b > 5) { // 3 - echo 'medium'; - } - } elseif ($b < 0) { // 4 - do { // 5 - $b++; - } while ($b < 0); // not counted, do is the decision point - } - - for ($i = 0; $i < 3; $i++) { // 6 - echo $i; - } - - foreach ([1, 2] as $v) { // 7 - echo $v; - } - - $x = $b > 0 ? 'yes' : 'no'; // 8 - - $y = $b ?? 0; // 9 - - if ($b === 100 || $b < 0) { // 10 (if), 11 (||) - echo 'edge'; - } - } -} - -// Trait with high complexity method -trait HighComplexityTrait -{ - // Complexity: 11 - public function traitComplexMethod(int $a): void - { - if ($a > 0) { // 1 - if ($a > 10) { // 2 - echo 'large'; - } elseif ($a > 5) { // 3 - echo 'medium'; - } - } elseif ($a < 0) { // 4 - while ($a < 0) { // 5 - $a++; - } - } - - for ($i = 0; $i < 3; $i++) { // 6 - echo $i; - } - - foreach ([1, 2] as $v) { // 7 - echo $v; - } - - $x = $a > 0 ? 'yes' : 'no'; // 8 - - $y = $a ?? 0; // 9 - - if ($a === 100) { // 10 - echo 'exact'; - } - } -} - -// Test catch blocks and logical operators -class CatchAndLogicalOperators -{ - // Complexity: 7 (2 catch + if + 3 logical operators) - public function methodWithCatchBlocks(): void - { - try { - throw new Exception(); - } catch (RuntimeException $e) { // 1 - echo 'runtime'; - } catch (Exception $e) { // 2 - echo 'exception'; - } - - $a = true and false; // 3 - $b = true or false; // 4 - $c = true && false; // 5 - $d = true || false; // 6 - } -} diff --git a/tests/Rules/CyclomaticComplexity/Fixture/HighComplexityClass.php b/tests/Rules/CyclomaticComplexity/Fixture/HighComplexityClass.php new file mode 100644 index 0000000..a55bcf1 --- /dev/null +++ b/tests/Rules/CyclomaticComplexity/Fixture/HighComplexityClass.php @@ -0,0 +1,112 @@ + 0) { // 1 + if ($a > 10) { // 2 + echo 'large'; + } elseif ($a > 5) { // 3 + echo 'medium'; + } + } elseif ($a < 0) { // 4 + while ($a < 0) { // 5 + $a++; + } + } + + for ($i = 0; $i < 3; $i++) { // 6 + echo $i; + } + + foreach ([1, 2] as $v) { // 7 + echo $v; + } + + $x = $a > 0 ? 'yes' : 'no'; // 8 + + $y = $a ?? 0; // 9 + } + + // Complexity: 11 (exceeds threshold, should trigger error) + public function methodWithComplexityEleven(int $a): void + { + if ($a > 0) { // 1 + if ($a > 10) { // 2 + echo 'large'; + } elseif ($a > 5) { // 3 + echo 'medium'; + } + } elseif ($a < 0) { // 4 + while ($a < 0) { // 5 + $a++; + } + } + + for ($i = 0; $i < 3; $i++) { // 6 + echo $i; + } + + foreach ([1, 2] as $v) { // 7 + echo $v; + } + + $x = $a > 0 ? 'yes' : 'no'; // 8 + + $y = $a ?? 0; // 9 + + if ($a === 100) { // 10 + echo 'exact'; + } + } + + // Complexity: 15 (well exceeds threshold) + public function veryComplexMethod(int $a, int $b): void + { + if ($a > 0) { // 1 + if ($a > 10) { // 2 + echo 'large'; + } elseif ($a > 5) { // 3 + echo 'medium'; + } elseif ($a > 2) { // 4 + echo 'small'; + } + } elseif ($a < 0) { // 5 + while ($a < 0) { // 6 + $a++; + } + } + + for ($i = 0; $i < 3; $i++) { // 7 + foreach ([1, 2] as $v) { // 8 + echo $v; + } + } + + switch ($b) { + case 1: // 9 + echo 'one'; + + break; + case 2: // 10 + echo 'two'; + + break; + case 3: // 11 + echo 'three'; + + break; + + default: + echo 'other'; + } + + $x = $a > 0 && $b > 0 ? 'both' : 'not'; // 12 (&&), 13 (ternary) + + $y = $a || $b; // 14 + } +} diff --git a/tests/Rules/CyclomaticComplexity/Fixture/HighComplexityTrait.php b/tests/Rules/CyclomaticComplexity/Fixture/HighComplexityTrait.php new file mode 100644 index 0000000..190ab4b --- /dev/null +++ b/tests/Rules/CyclomaticComplexity/Fixture/HighComplexityTrait.php @@ -0,0 +1,39 @@ + 0) { // 1 + if ($a > 10) { // 2 + echo 'large'; + } elseif ($a > 5) { // 3 + echo 'medium'; + } + } elseif ($a < 0) { // 4 + while ($a < 0) { // 5 + $a++; + } + } + + for ($i = 0; $i < 3; $i++) { // 6 + echo $i; + } + + foreach ([1, 2] as $v) { // 7 + echo $v; + } + + $x = $a > 0 ? 'yes' : 'no'; // 8 + + $y = $a ?? 0; // 9 + + if ($a === 100) { // 10 + echo 'exact'; + } + } +} diff --git a/tests/Rules/CyclomaticComplexity/Fixture/LowComplexityClass.php b/tests/Rules/CyclomaticComplexity/Fixture/LowComplexityClass.php new file mode 100644 index 0000000..310f491 --- /dev/null +++ b/tests/Rules/CyclomaticComplexity/Fixture/LowComplexityClass.php @@ -0,0 +1,38 @@ + 0) { + echo 'positive'; + } elseif ($a < 0) { + echo 'negative'; + } + + while ($b > 0) { + $b--; + } + + for ($i = 0; $i < 5; $i++) { + echo $i; + } + } +} diff --git a/tests/Rules/CyclomaticComplexity/Fixture/VeryHighAverageComplexity.php b/tests/Rules/CyclomaticComplexity/Fixture/VeryHighAverageComplexity.php new file mode 100644 index 0000000..e149853 --- /dev/null +++ b/tests/Rules/CyclomaticComplexity/Fixture/VeryHighAverageComplexity.php @@ -0,0 +1,71 @@ + 0) { // 1 + if ($a > 10) { // 2 + echo 'large'; + } elseif ($a > 5) { // 3 + echo 'medium'; + } + } elseif ($a < 0) { // 4 + while ($a < 0) { // 5 + $a++; + } + } + + for ($i = 0; $i < 3; $i++) { // 6 + echo $i; + } + + foreach ([1, 2] as $v) { // 7 + echo $v; + } + + $x = $a > 0 ? 'yes' : 'no'; // 8 + + $y = $a ?? 0; // 9 + + if ($a === 100 && $a > 50) { // 10 (if), 11 (&&) + echo 'exact'; + } + } + + // Complexity: 12 + public function complexMethodTwo(int $b): void + { + if ($b > 0) { // 1 + if ($b > 10) { // 2 + echo 'large'; + } elseif ($b > 5) { // 3 + echo 'medium'; + } + } elseif ($b < 0) { // 4 + do { // 5 + $b++; + } while ($b < 0); // not counted, do is the decision point + } + + for ($i = 0; $i < 3; $i++) { // 6 + echo $i; + } + + foreach ([1, 2] as $v) { // 7 + echo $v; + } + + $x = $b > 0 ? 'yes' : 'no'; // 8 + + $y = $b ?? 0; // 9 + + if ($b === 100 || $b < 0) { // 10 (if), 11 (||) + echo 'edge'; + } + } +} diff --git a/tests/Rules/CyclomaticComplexity/MethodsOnlyTest.php b/tests/Rules/CyclomaticComplexity/MethodsOnlyTest.php index 5b75f6f..9f94b6b 100644 --- a/tests/Rules/CyclomaticComplexity/MethodsOnlyTest.php +++ b/tests/Rules/CyclomaticComplexity/MethodsOnlyTest.php @@ -14,33 +14,42 @@ class MethodsOnlyTest extends RuleTestCase public function testRule(): void { // With show_classes_complexity: false, only method errors should appear - $this->analyse([__DIR__ . '/Fixture/ExampleClass.php'], [ - // HighComplexityClass::methodWithComplexityEleven (complexity 11) + $this->analyse( [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'methodWithComplexityEleven', 11, 10), - 74, + __DIR__ . '/Fixture/LowComplexityClass.php', + __DIR__ . '/Fixture/HighComplexityClass.php', + __DIR__ . '/Fixture/VeryHighAverageComplexity.php', + __DIR__ . '/Fixture/HighComplexityTrait.php', + __DIR__ . '/Fixture/CatchAndLogicalOperators.php', ], - // HighComplexityClass::veryComplexMethod (complexity 15) [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'veryComplexMethod', 15, 10), - 106, - ], - // VeryHighAverageComplexity::complexMethodOne (complexity 12) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'complexMethodOne', 12, 10), - 156, - ], - // VeryHighAverageComplexity::complexMethodTwo (complexity 12) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'complexMethodTwo', 12, 10), - 188, - ], - // HighComplexityTrait::traitComplexMethod (complexity 11) - [ - sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'traitComplexMethod', 11, 10), - 224, - ], - ]); + // HighComplexityClass::methodWithComplexityEleven (complexity 11) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'methodWithComplexityEleven', 11, 10), + 36, + ], + // HighComplexityClass::veryComplexMethod (complexity 15) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'veryComplexMethod', 15, 10), + 68, + ], + // VeryHighAverageComplexity::complexMethodOne (complexity 12) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'complexMethodOne', 12, 10), + 9, + ], + // VeryHighAverageComplexity::complexMethodTwo (complexity 12) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'complexMethodTwo', 12, 10), + 41, + ], + // HighComplexityTrait::traitComplexMethod (complexity 11) + [ + sprintf(CyclomaticComplexityRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'method', 'traitComplexMethod', 11, 10), + 9, + ], + ] + ); } /** diff --git a/tests/Rules/LongClassName/Fixture/VeryLongEnumNameThatExceedsTheDefaultMaximumLength.php b/tests/Rules/LongClassName/Fixture/VeryLongEnumNameThatExceedsTheDefaultMaximumLength.php new file mode 100644 index 0000000..e55f9ee --- /dev/null +++ b/tests/Rules/LongClassName/Fixture/VeryLongEnumNameThatExceedsTheDefaultMaximumLength.php @@ -0,0 +1,5 @@ +analyse([__DIR__ . '/Fixture/OtherTypes.php'], [ + $this->analyse([__DIR__ . '/Fixture/VeryLongInterfaceNameThatExceedsTheDefaultMaximumLength.php'], [ [ 'Interface name "VeryLongInterfaceNameThatExceedsTheDefaultMaximumLength" is too long (55 chars). Maximum allowed length is 40 characters.', 5, ], + ]); + + $this->analyse([__DIR__ . '/Fixture/VeryLongTraitNameThatExceedsTheDefaultMaximumLength.php'], [ [ 'Trait name "VeryLongTraitNameThatExceedsTheDefaultMaximumLength" is too long (51 chars). Maximum allowed length is 40 characters.', - 6, + 5, ], + ]); + + $this->analyse([__DIR__ . '/Fixture/VeryLongEnumNameThatExceedsTheDefaultMaximumLength.php'], [ [ 'Enum name "VeryLongEnumNameThatExceedsTheDefaultMaximumLength" is too long (50 chars). Maximum allowed length is 40 characters.', - 7, + 5, ], ]); } diff --git a/tests/Rules/ShortClassName/CustomMinimumTest.php b/tests/Rules/ShortClassName/CustomMinimumTest.php index d4f436d..6b20e83 100644 --- a/tests/Rules/ShortClassName/CustomMinimumTest.php +++ b/tests/Rules/ShortClassName/CustomMinimumTest.php @@ -13,12 +13,19 @@ class CustomMinimumTest extends RuleTestCase { public function testRule(): void { - $this->analyse([__DIR__ . '/Fixture/ExampleClass.php'], [ + // With minimum of 2, only A.php should trigger an error + $this->analyse([__DIR__ . '/Fixture/A.php'], [ [ 'Class name "A" is too short (1 chars). Minimum allowed length is 2 characters.', 5, ], ]); + + // AB.php should have no errors with minimum of 2 + $this->analyse([__DIR__ . '/Fixture/AB.php'], []); + + // ABC.php should have no errors with minimum of 2 + $this->analyse([__DIR__ . '/Fixture/ABC.php'], []); } /** diff --git a/tests/Rules/ShortClassName/ExceptionsTest.php b/tests/Rules/ShortClassName/ExceptionsTest.php index d8b3fbc..5bde8fd 100644 --- a/tests/Rules/ShortClassName/ExceptionsTest.php +++ b/tests/Rules/ShortClassName/ExceptionsTest.php @@ -13,12 +13,19 @@ class ExceptionsTest extends RuleTestCase { public function testRule(): void { - $this->analyse([__DIR__ . '/Fixture/ExampleClass.php'], [ + // A.php is in exceptions list, so should have no errors + $this->analyse([__DIR__ . '/Fixture/A.php'], []); + + // AB.php is not in exceptions list, so should have an error + $this->analyse([__DIR__ . '/Fixture/AB.php'], [ [ 'Class name "AB" is too short (2 chars). Minimum allowed length is 3 characters.', - 6, + 5, ], ]); + + // ABC.php meets minimum length of 3, so should have no errors + $this->analyse([__DIR__ . '/Fixture/ABC.php'], []); } /** diff --git a/tests/Rules/ShortClassName/Fixture/A.php b/tests/Rules/ShortClassName/Fixture/A.php new file mode 100644 index 0000000..e5e6582 --- /dev/null +++ b/tests/Rules/ShortClassName/Fixture/A.php @@ -0,0 +1,5 @@ +analyse([__DIR__ . '/Fixture/ExampleClass.php'], [ + $this->analyse([__DIR__ . '/Fixture/A.php'], [ [ 'Class name "A" is too short (1 chars). Minimum allowed length is 3 characters.', 5, ], + ]); + + $this->analyse([__DIR__ . '/Fixture/AB.php'], [ [ 'Class name "AB" is too short (2 chars). Minimum allowed length is 3 characters.', - 6, + 5, ], ]); - $this->analyse([__DIR__ . '/Fixture/OtherTypes.php'], [ + // ABC.php should have no errors as it meets the minimum length of 3 + + $this->analyse([__DIR__ . '/Fixture/I.php'], [ [ 'Interface name "I" is too short (1 chars). Minimum allowed length is 3 characters.', 5, ], + ]); + + $this->analyse([__DIR__ . '/Fixture/T.php'], [ [ 'Trait name "T" is too short (1 chars). Minimum allowed length is 3 characters.', - 6, + 5, ], + ]); + + $this->analyse([__DIR__ . '/Fixture/E.php'], [ [ 'Enum name "E" is too short (1 chars). Minimum allowed length is 3 characters.', - 7, + 5, ], ]); } diff --git a/tests/Rules/TooManyMethods/CustomMaxMethodsTest.php b/tests/Rules/TooManyMethods/CustomMaxMethodsTest.php index 871b73d..c7f98a2 100644 --- a/tests/Rules/TooManyMethods/CustomMaxMethodsTest.php +++ b/tests/Rules/TooManyMethods/CustomMaxMethodsTest.php @@ -15,32 +15,43 @@ class CustomMaxMethodsTest extends RuleTestCase { public function testRule(): void { - $this->analyse([__DIR__ . '/Fixture/ExampleClass.php'], [ + $this->analyse( [ - sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Class', 'ClassWithManyMethods', 10, 5), - 15, + __DIR__ . '/Fixture/ClassWithManyMethods.php', + __DIR__ . '/Fixture/ClassExceedingLimit.php', + __DIR__ . '/Fixture/ClassWithFewMethods.php', + __DIR__ . '/Fixture/TraitExceedingLimit.php', + __DIR__ . '/Fixture/InterfaceExceedingLimit.php', + __DIR__ . '/Fixture/EnumExceedingLimit.php', + __DIR__ . '/Fixture/ClassWithSixMethods.php', ], [ - sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Class', 'ClassExceedingLimit', 26, 5), - 127, - ], - [ - sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Trait', 'TraitExceedingLimit', 26, 5), - 203, - ], - [ - sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Interface', 'InterfaceExceedingLimit', 26, 5), - 262, - ], - [ - sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Enum', 'EnumExceedingLimit', 26, 5), - 321, - ], - [ - sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Class', 'ClassWithSixMethods', 6, 5), - 381, - ], - ]); + [ + sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Class', 'ClassWithManyMethods', 10, 5), + 15, + ], + [ + sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Class', 'ClassExceedingLimit', 26, 5), + 9, + ], + [ + sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Trait', 'TraitExceedingLimit', 26, 5), + 9, + ], + [ + sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Interface', 'InterfaceExceedingLimit', 26, 5), + 9, + ], + [ + sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Enum', 'EnumExceedingLimit', 26, 5), + 9, + ], + [ + sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Class', 'ClassWithSixMethods', 6, 5), + 8, + ], + ] + ); } /** diff --git a/tests/Rules/TooManyMethods/Fixture/ClassExceedingLimit.php b/tests/Rules/TooManyMethods/Fixture/ClassExceedingLimit.php new file mode 100644 index 0000000..6d03e65 --- /dev/null +++ b/tests/Rules/TooManyMethods/Fixture/ClassExceedingLimit.php @@ -0,0 +1,62 @@ +analyse([__DIR__ . '/Fixture/ExampleClass.php'], [ + $this->analyse( [ - sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Class', 'ClassWithManyMethods', 30, 25), - 15, + __DIR__ . '/Fixture/ClassWithManyMethods.php', + __DIR__ . '/Fixture/ClassExceedingLimit.php', + __DIR__ . '/Fixture/ClassWithFewMethods.php', + __DIR__ . '/Fixture/TraitExceedingLimit.php', + __DIR__ . '/Fixture/InterfaceExceedingLimit.php', + __DIR__ . '/Fixture/EnumExceedingLimit.php', + __DIR__ . '/Fixture/ClassWithSixMethods.php', ], [ - sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Class', 'ClassExceedingLimit', 26, 25), - 127, - ], - [ - sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Trait', 'TraitExceedingLimit', 26, 25), - 203, - ], - [ - sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Interface', 'InterfaceExceedingLimit', 26, 25), - 262, - ], - [ - sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Enum', 'EnumExceedingLimit', 26, 25), - 321, - ], - ]); + [ + sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Class', 'ClassWithManyMethods', 30, 25), + 15, + ], + [ + sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Class', 'ClassExceedingLimit', 26, 25), + 9, + ], + [ + sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Trait', 'TraitExceedingLimit', 26, 25), + 9, + ], + [ + sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Interface', 'InterfaceExceedingLimit', 26, 25), + 9, + ], + [ + sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Enum', 'EnumExceedingLimit', 26, 25), + 9, + ], + ] + ); } /** diff --git a/tests/Rules/TooManyMethods/TooManyMethodsRuleTest.php b/tests/Rules/TooManyMethods/TooManyMethodsRuleTest.php index 9b3d6ea..be131db 100644 --- a/tests/Rules/TooManyMethods/TooManyMethodsRuleTest.php +++ b/tests/Rules/TooManyMethods/TooManyMethodsRuleTest.php @@ -13,24 +13,35 @@ class TooManyMethodsRuleTest extends RuleTestCase { public function testRule(): void { - $this->analyse([__DIR__ . '/Fixture/ExampleClass.php'], [ + $this->analyse( [ - sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Class', 'ClassExceedingLimit', 26, 25), - 127, + __DIR__ . '/Fixture/ClassWithManyMethods.php', + __DIR__ . '/Fixture/ClassExceedingLimit.php', + __DIR__ . '/Fixture/ClassWithFewMethods.php', + __DIR__ . '/Fixture/TraitExceedingLimit.php', + __DIR__ . '/Fixture/InterfaceExceedingLimit.php', + __DIR__ . '/Fixture/EnumExceedingLimit.php', + __DIR__ . '/Fixture/ClassWithSixMethods.php', ], [ - sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Trait', 'TraitExceedingLimit', 26, 25), - 203, - ], - [ - sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Interface', 'InterfaceExceedingLimit', 26, 25), - 262, - ], - [ - sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Enum', 'EnumExceedingLimit', 26, 25), - 321, - ], - ]); + [ + sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Class', 'ClassExceedingLimit', 26, 25), + 9, + ], + [ + sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Trait', 'TraitExceedingLimit', 26, 25), + 9, + ], + [ + sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Interface', 'InterfaceExceedingLimit', 26, 25), + 9, + ], + [ + sprintf(TooManyMethodsRule::ERROR_MESSAGE_TEMPLATE, 'Enum', 'EnumExceedingLimit', 26, 25), + 9, + ], + ] + ); } /** From 5ce8692da9462b1cf297916dfd110068d8e6e740 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Wed, 31 Dec 2025 09:13:03 -0500 Subject: [PATCH 3/9] Fix some namespaces of test files --- .../AllOptionsTrueTest.php | 4 ++-- .../DefaultOptionsTest.php | 24 +++++++++---------- .../Fixture/AllOptionsFunctions.php | 2 +- .../Fixture/AllOptionsIgnoredClass.php | 2 +- .../Fixture/AllOptionsNotIgnored.php | 2 +- .../Fixture/DefaultExample.php | 2 +- .../Fixture/IgnorePattern.php | 2 +- .../Fixture/IgnoredClass.php | 2 +- .../Fixture/IgnoredClassFunctions.php | 2 +- .../Fixture/NotIgnoredClass.php | 2 +- .../BooleanArgumentFlag/IgnorePatternTest.php | 4 ++-- .../IgnoredInClassesTest.php | 2 +- .../config/all-options.neon | 2 +- .../config/ignored-classes.neon | 2 +- 14 files changed, 27 insertions(+), 27 deletions(-) diff --git a/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php b/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php index 4550d01..e6315ac 100644 --- a/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php +++ b/tests/Rules/BooleanArgumentFlag/AllOptionsTrueTest.php @@ -22,8 +22,8 @@ public function testAllOptions(): void $this->analyse([ __DIR__ . '/Fixture/AllOptionsNotIgnored.php', ], [ - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\AllOptionsNotIgnored', 'handle', 'flag'), 9], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\AllOptionsNotIgnored', 'processData', 'flag'), 11], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\AllOptionsNotIgnored', 'handle', 'flag'), 9], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\AllOptionsNotIgnored', 'processData', 'flag'), 11], ]); // AllOptionsFunctions.php - setConfig is ignored by pattern, but processConfig should error diff --git a/tests/Rules/BooleanArgumentFlag/DefaultOptionsTest.php b/tests/Rules/BooleanArgumentFlag/DefaultOptionsTest.php index f90f7bb..2154f81 100644 --- a/tests/Rules/BooleanArgumentFlag/DefaultOptionsTest.php +++ b/tests/Rules/BooleanArgumentFlag/DefaultOptionsTest.php @@ -16,18 +16,18 @@ public function testDefaultExample(): void $this->analyse([ __DIR__ . '/Fixture/DefaultExample.php', ], [ - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', '__construct', 'debug'), 7], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'processWithFlag', 'flag'), 9], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'handleNullable', 'option'), 11], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'unionType', 'value'), 13], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multiUnion', 'mixed'), 15], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'staticMethod', 'enabled'), 19], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', '__set', 'value'), 21], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multipleParams', 'enabled'), 23], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multipleBools', 'first'), 25], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'multipleBools', 'second'), 25], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'protectedMethod', 'flag'), 27], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\DefaultExample', 'privateMethod', 'flag'), 29], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\DefaultExample', '__construct', 'debug'), 7], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\DefaultExample', 'processWithFlag', 'flag'), 9], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\DefaultExample', 'handleNullable', 'option'), 11], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\DefaultExample', 'unionType', 'value'), 13], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\DefaultExample', 'multiUnion', 'mixed'), 15], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\DefaultExample', 'staticMethod', 'enabled'), 19], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\DefaultExample', '__set', 'value'), 21], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\DefaultExample', 'multipleParams', 'enabled'), 23], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\DefaultExample', 'multipleBools', 'first'), 25], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\DefaultExample', 'multipleBools', 'second'), 25], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\DefaultExample', 'protectedMethod', 'flag'), 27], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\DefaultExample', 'privateMethod', 'flag'), 29], [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'namedFunction', 'flag'), 32], [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 36], [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'flag'), 40], diff --git a/tests/Rules/BooleanArgumentFlag/Fixture/AllOptionsFunctions.php b/tests/Rules/BooleanArgumentFlag/Fixture/AllOptionsFunctions.php index 1013248..b5bf205 100644 --- a/tests/Rules/BooleanArgumentFlag/Fixture/AllOptionsFunctions.php +++ b/tests/Rules/BooleanArgumentFlag/Fixture/AllOptionsFunctions.php @@ -1,6 +1,6 @@ analyse([ __DIR__ . '/Fixture/IgnorePattern.php', ], [ - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\IgnorePatternExample', 'processWithFlag', 'flag'), 13], - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\IgnorePatternExample', 'handleBool', 'value'), 15], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\IgnorePatternExample', 'processWithFlag', 'flag'), 13], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\IgnorePatternExample', 'handleBool', 'value'), 15], [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_FUNCTION, 'processGlobal', 'value'), 24], [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'value'), 26], [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_CLOSURE, 'value'), 28], diff --git a/tests/Rules/BooleanArgumentFlag/IgnoredInClassesTest.php b/tests/Rules/BooleanArgumentFlag/IgnoredInClassesTest.php index cdbf296..ab8cc86 100644 --- a/tests/Rules/BooleanArgumentFlag/IgnoredInClassesTest.php +++ b/tests/Rules/BooleanArgumentFlag/IgnoredInClassesTest.php @@ -22,7 +22,7 @@ public function testIgnoredClasses(): void $this->analyse([ __DIR__ . '/Fixture/NotIgnoredClass.php', ], [ - [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Fixtures\BooleanArgumentFlag\NotIgnoredClass', 'methodWithBool', 'flag'), 7], + [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\NotIgnoredClass', 'methodWithBool', 'flag'), 7], ]); // Functions and closures should have errors as they are not in any class diff --git a/tests/Rules/BooleanArgumentFlag/config/all-options.neon b/tests/Rules/BooleanArgumentFlag/config/all-options.neon index 697ea56..4be3afd 100644 --- a/tests/Rules/BooleanArgumentFlag/config/all-options.neon +++ b/tests/Rules/BooleanArgumentFlag/config/all-options.neon @@ -8,5 +8,5 @@ parameters: meliorstan: boolean_argument_flag: ignored_in_classes: - - 'Fixtures\BooleanArgumentFlag\AllOptionsIgnoredClass' + - 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\AllOptionsIgnoredClass' ignore_pattern: '/^set/' diff --git a/tests/Rules/BooleanArgumentFlag/config/ignored-classes.neon b/tests/Rules/BooleanArgumentFlag/config/ignored-classes.neon index c465573..af923a0 100644 --- a/tests/Rules/BooleanArgumentFlag/config/ignored-classes.neon +++ b/tests/Rules/BooleanArgumentFlag/config/ignored-classes.neon @@ -8,5 +8,5 @@ parameters: meliorstan: boolean_argument_flag: ignored_in_classes: - - 'Fixtures\BooleanArgumentFlag\IgnoredClass' + - 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\IgnoredClass' ignore_pattern: '' From 950e36702437b9dae39ccfdcf29853673fee6ef4 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Wed, 31 Dec 2025 09:17:35 -0500 Subject: [PATCH 4/9] Comment out contributing reference for now as we need to write this --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2fa28fe..8707821 100644 --- a/README.md +++ b/README.md @@ -164,9 +164,10 @@ Originally inspired by [**PHPMD - PHP Mess Detector**](https://phpmd.org/), this > **Note**: While inspired by PHPMD, these rules are not exact replicas. They offer additional customization options and are adapted for PHPStan's architecture and modern PHP practices. -## 🤝 Contributing +[//]: # (## 🤝 Contributing) -We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details. +[//]: # () +[//]: # (We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.) ### Development Setup From 70c48999eeae8695015c673ae2fff14ec3fed945 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Wed, 31 Dec 2025 09:22:56 -0500 Subject: [PATCH 5/9] Fix more namespaces --- .../Fixture/{IgnorePattern.php => IgnorePatternExample.php} | 0 tests/Rules/BooleanArgumentFlag/IgnorePatternTest.php | 2 +- tests/Rules/BooleanArgumentFlag/InvalidPatternTest.php | 2 +- tests/Rules/LongClassName/CustomMaximumTest.php | 2 +- tests/Rules/LongClassName/LongClassNameRuleTest.php | 2 +- tests/Rules/LongClassName/SubtractPrefixesTest.php | 2 +- tests/Rules/LongClassName/SubtractSuffixesTest.php | 2 +- .../PascalCaseClassName/AllowConsecutiveUppercaseTrueTest.php | 2 +- tests/Rules/PascalCaseClassName/PascalCaseClassNameRuleTest.php | 2 +- tests/Rules/ShortClassName/CustomMinimumTest.php | 2 +- tests/Rules/ShortClassName/ExceptionsTest.php | 2 +- tests/Rules/ShortClassName/ShortClassNameRuleTest.php | 2 +- 12 files changed, 11 insertions(+), 11 deletions(-) rename tests/Rules/BooleanArgumentFlag/Fixture/{IgnorePattern.php => IgnorePatternExample.php} (100%) diff --git a/tests/Rules/BooleanArgumentFlag/Fixture/IgnorePattern.php b/tests/Rules/BooleanArgumentFlag/Fixture/IgnorePatternExample.php similarity index 100% rename from tests/Rules/BooleanArgumentFlag/Fixture/IgnorePattern.php rename to tests/Rules/BooleanArgumentFlag/Fixture/IgnorePatternExample.php diff --git a/tests/Rules/BooleanArgumentFlag/IgnorePatternTest.php b/tests/Rules/BooleanArgumentFlag/IgnorePatternTest.php index 2b206d1..c503ed8 100644 --- a/tests/Rules/BooleanArgumentFlag/IgnorePatternTest.php +++ b/tests/Rules/BooleanArgumentFlag/IgnorePatternTest.php @@ -14,7 +14,7 @@ class IgnorePatternTest extends RuleTestCase public function testIgnorePattern(): void { $this->analyse([ - __DIR__ . '/Fixture/IgnorePattern.php', + __DIR__ . '/Fixture/IgnorePatternExample.php', ], [ [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\IgnorePatternExample', 'processWithFlag', 'flag'), 13], [sprintf(BooleanArgumentFlagRule::ERROR_MESSAGE_TEMPLATE_METHOD, 'Orrison\MeliorStan\Tests\Rules\BooleanArgumentFlag\Fixture\IgnorePatternExample', 'handleBool', 'value'), 15], diff --git a/tests/Rules/BooleanArgumentFlag/InvalidPatternTest.php b/tests/Rules/BooleanArgumentFlag/InvalidPatternTest.php index 8d873c9..af1fb51 100644 --- a/tests/Rules/BooleanArgumentFlag/InvalidPatternTest.php +++ b/tests/Rules/BooleanArgumentFlag/InvalidPatternTest.php @@ -18,7 +18,7 @@ public function testInvalidPattern(): void $this->expectExceptionMessage('Invalid regex pattern in ignore_pattern configuration'); $this->analyse([ - __DIR__ . '/Fixture/IgnorePattern.php', + __DIR__ . '/Fixture/IgnorePatternExample.php', ], []); } diff --git a/tests/Rules/LongClassName/CustomMaximumTest.php b/tests/Rules/LongClassName/CustomMaximumTest.php index 32f7351..bb52574 100644 --- a/tests/Rules/LongClassName/CustomMaximumTest.php +++ b/tests/Rules/LongClassName/CustomMaximumTest.php @@ -1,6 +1,6 @@ Date: Wed, 31 Dec 2025 09:26:03 -0500 Subject: [PATCH 6/9] Remove contributing mention --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 8707821..5add5c0 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,6 @@ - [📚 Available Rules](#-available-rules) - [🔧 Configuration](#-configuration) - [🎯 Inspiration](#-inspiration) -- [🤝 Contributing](#-contributing) - [📄 License](#-license) - [🙏 Acknowledgments](#-acknowledgments) From b40484f5edf256314f3e855d99e24d0c422dbb58 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Wed, 31 Dec 2025 09:27:24 -0500 Subject: [PATCH 7/9] Update composer deps --- composer.lock | 813 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 497 insertions(+), 316 deletions(-) diff --git a/composer.lock b/composer.lock index f26de62..6b0a6e2 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,11 @@ "packages": [ { "name": "phpstan/phpstan", - "version": "2.1.22", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "41600c8379eb5aee63e9413fe9e97273e25d57e4" - }, + "version": "2.1.33", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/41600c8379eb5aee63e9413fe9e97273e25d57e4", - "reference": "41600c8379eb5aee63e9413fe9e97273e25d57e4", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9e800e6bee7d5bd02784d4c6069b48032d16224f", + "reference": "9e800e6bee7d5bd02784d4c6069b48032d16224f", "shasum": "" }, "require": { @@ -62,22 +57,22 @@ "type": "github" } ], - "time": "2025-08-04T19:17:37+00:00" + "time": "2025-12-05T10:24:31+00:00" } ], "packages-dev": [ { "name": "brianium/paratest", - "version": "v7.10.3", + "version": "v7.16.0", "source": { "type": "git", "url": "https://github.com/paratestphp/paratest.git", - "reference": "cfee22cc949d170e61e7111c89ea9fc86aa02ffb" + "reference": "a10878ed0fe0bbc2f57c980f7a08065338b970b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/cfee22cc949d170e61e7111c89ea9fc86aa02ffb", - "reference": "cfee22cc949d170e61e7111c89ea9fc86aa02ffb", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/a10878ed0fe0bbc2f57c980f7a08065338b970b6", + "reference": "a10878ed0fe0bbc2f57c980f7a08065338b970b6", "shasum": "" }, "require": { @@ -85,28 +80,27 @@ "ext-pcre": "*", "ext-reflection": "*", "ext-simplexml": "*", - "fidry/cpu-core-counter": "^1.2.0", + "fidry/cpu-core-counter": "^1.3.0", "jean85/pretty-package-versions": "^2.1.1", - "php": "~8.3.0 || ~8.4.0", - "phpunit/php-code-coverage": "^12.3.1", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "phpunit/php-code-coverage": "^12.5.1", "phpunit/php-file-iterator": "^6", "phpunit/php-timer": "^8", - "phpunit/phpunit": "^12.2.3", - "sebastian/environment": "^8.0.2", - "symfony/console": "^6.4.20 || ^7.3.0", - "symfony/process": "^6.4.20 || ^7.3.0" + "phpunit/phpunit": "^12.5.2", + "sebastian/environment": "^8.0.3", + "symfony/console": "^7.3.4 || ^8.0.0", + "symfony/process": "^7.3.4 || ^8.0.0" }, "require-dev": { - "doctrine/coding-standard": "^13.0.1", + "doctrine/coding-standard": "^14.0.0", "ext-pcntl": "*", "ext-pcov": "*", "ext-posix": "*", - "phpstan/phpstan": "^2.1.17", + "phpstan/phpstan": "^2.1.33", "phpstan/phpstan-deprecation-rules": "^2.0.3", - "phpstan/phpstan-phpunit": "^2.0.6", - "phpstan/phpstan-strict-rules": "^2.0.4", - "squizlabs/php_codesniffer": "^3.13.2", - "symfony/filesystem": "^6.4.13 || ^7.3.0" + "phpstan/phpstan-phpunit": "^2.0.10", + "phpstan/phpstan-strict-rules": "^2.0.7", + "symfony/filesystem": "^7.3.2 || ^8.0.0" }, "bin": [ "bin/paratest", @@ -146,7 +140,7 @@ ], "support": { "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.10.3" + "source": "https://github.com/paratestphp/paratest/tree/v7.16.0" }, "funding": [ { @@ -158,7 +152,7 @@ "type": "paypal" } ], - "time": "2025-06-22T16:27:15+00:00" + "time": "2025-12-09T20:03:26+00:00" }, { "name": "clue/ndjson-react", @@ -305,16 +299,16 @@ }, { "name": "composer/semver", - "version": "3.4.3", + "version": "3.4.4", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", "shasum": "" }, "require": { @@ -366,7 +360,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.3" + "source": "https://github.com/composer/semver/tree/3.4.4" }, "funding": [ { @@ -376,13 +370,9 @@ { "url": "https://github.com/composer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2024-09-19T14:15:21+00:00" + "time": "2025-08-20T19:15:30+00:00" }, { "name": "composer/xdebug-handler", @@ -499,16 +489,16 @@ }, { "name": "fidry/cpu-core-counter", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "8520451a140d3f46ac33042715115e290cf5785f" + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", - "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", "shasum": "" }, "require": { @@ -518,10 +508,10 @@ "fidry/makefile": "^0.2.0", "fidry/php-cs-fixer-config": "^1.1.2", "phpstan/extension-installer": "^1.2.0", - "phpstan/phpstan": "^1.9.2", - "phpstan/phpstan-deprecation-rules": "^1.0.0", - "phpstan/phpstan-phpunit": "^1.2.2", - "phpstan/phpstan-strict-rules": "^1.4.4", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^8.5.31 || ^9.5.26", "webmozarts/strict-phpunit": "^7.5" }, @@ -548,7 +538,7 @@ ], "support": { "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" }, "funding": [ { @@ -556,63 +546,62 @@ "type": "github" } ], - "time": "2024-08-06T10:04:20+00:00" + "time": "2025-08-14T07:29:31+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.76.0", + "version": "v3.92.3", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "0e3c484cef0ae9314b0f85986a36296087432c40" + "reference": "2ba8f5a60f6f42fb65758cfb3768434fa2d1c7e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/0e3c484cef0ae9314b0f85986a36296087432c40", - "reference": "0e3c484cef0ae9314b0f85986a36296087432c40", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/2ba8f5a60f6f42fb65758cfb3768434fa2d1c7e8", + "reference": "2ba8f5a60f6f42fb65758cfb3768434fa2d1c7e8", "shasum": "" }, "require": { - "clue/ndjson-react": "^1.0", + "clue/ndjson-react": "^1.3", "composer/semver": "^3.4", "composer/xdebug-handler": "^3.0.5", "ext-filter": "*", "ext-hash": "*", "ext-json": "*", "ext-tokenizer": "*", - "fidry/cpu-core-counter": "^1.2", + "fidry/cpu-core-counter": "^1.3", "php": "^7.4 || ^8.0", "react/child-process": "^0.6.6", - "react/event-loop": "^1.0", - "react/promise": "^2.11 || ^3.0", - "react/socket": "^1.0", - "react/stream": "^1.0", + "react/event-loop": "^1.5", + "react/socket": "^1.16", + "react/stream": "^1.4", "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0", - "symfony/console": "^5.4.45 || ^6.4.13 || ^7.0", - "symfony/event-dispatcher": "^5.4.45 || ^6.4.13 || ^7.0", - "symfony/filesystem": "^5.4.45 || ^6.4.13 || ^7.0", - "symfony/finder": "^5.4.45 || ^6.4.17 || ^7.0", - "symfony/options-resolver": "^5.4.45 || ^6.4.16 || ^7.0", - "symfony/polyfill-mbstring": "^1.32", - "symfony/polyfill-php80": "^1.32", - "symfony/polyfill-php81": "^1.32", - "symfony/process": "^5.4.47 || ^6.4.20 || ^7.2", - "symfony/stopwatch": "^5.4.45 || ^6.4.19 || ^7.0" + "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.33", + "symfony/polyfill-php80": "^1.33", + "symfony/polyfill-php81": "^1.33", + "symfony/polyfill-php84": "^1.33", + "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2 || ^8.0", + "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0" }, "require-dev": { - "facile-it/paraunit": "^1.3.1 || ^2.6", - "infection/infection": "^0.29.14", - "justinrainbow/json-schema": "^5.3 || ^6.4", + "facile-it/paraunit": "^1.3.1 || ^2.7", + "infection/infection": "^0.31.0", + "justinrainbow/json-schema": "^6.5", "keradus/cli-executor": "^2.2", "mikey179/vfsstream": "^1.6.12", - "php-coveralls/php-coveralls": "^2.8", - "php-cs-fixer/accessible-object": "^1.1", + "php-coveralls/php-coveralls": "^2.9", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", - "phpunit/phpunit": "^9.6.23 || ^10.5.47 || ^11.5.25", - "symfony/polyfill-php84": "^1.32", - "symfony/var-dumper": "^5.4.48 || ^6.4.23 || ^7.3.1", - "symfony/yaml": "^5.4.45 || ^6.4.23 || ^7.3.1" + "phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34", + "symfony/polyfill-php85": "^1.33", + "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2 || ^8.0", + "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2 || ^8.0" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -627,7 +616,7 @@ "PhpCsFixer\\": "src/" }, "exclude-from-classmap": [ - "src/Fixer/Internal/*" + "src/**/Internal/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -653,7 +642,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.76.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.92.3" }, "funding": [ { @@ -661,7 +650,7 @@ "type": "github" } ], - "time": "2025-06-30T14:15:06+00:00" + "time": "2025-12-18T10:45:02+00:00" }, { "name": "jean85/pretty-package-versions", @@ -725,16 +714,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.13.1", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", - "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -773,7 +762,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -781,20 +770,20 @@ "type": "tidelift" } ], - "time": "2025-04-29T12:36:36+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nikic/php-parser", - "version": "v5.5.0", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { @@ -813,7 +802,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -837,9 +826,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2025-05-31T08:24:38+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { "name": "phar-io/manifest", @@ -961,34 +950,34 @@ }, { "name": "phpunit/php-code-coverage", - "version": "12.3.1", + "version": "12.5.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "ddec29dfc128eba9c204389960f2063f3b7fa170" + "reference": "4a9739b51cbcb355f6e95659612f92e282a7077b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ddec29dfc128eba9c204389960f2063f3b7fa170", - "reference": "ddec29dfc128eba9c204389960f2063f3b7fa170", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4a9739b51cbcb355f6e95659612f92e282a7077b", + "reference": "4a9739b51cbcb355f6e95659612f92e282a7077b", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^5.4.0", + "nikic/php-parser": "^5.7.0", "php": ">=8.3", "phpunit/php-file-iterator": "^6.0", "phpunit/php-text-template": "^5.0", "sebastian/complexity": "^5.0", - "sebastian/environment": "^8.0", + "sebastian/environment": "^8.0.3", "sebastian/lines-of-code": "^4.0", "sebastian/version": "^6.0", - "theseer/tokenizer": "^1.2.3" + "theseer/tokenizer": "^2.0.1" }, "require-dev": { - "phpunit/phpunit": "^12.1" + "phpunit/phpunit": "^12.5.1" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -997,7 +986,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "12.3.x-dev" + "dev-main": "12.5.x-dev" } }, "autoload": { @@ -1026,7 +1015,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.3.1" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.2" }, "funding": [ { @@ -1046,7 +1035,7 @@ "type": "tidelift" } ], - "time": "2025-06-18T08:58:13+00:00" + "time": "2025-12-24T07:03:04+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1295,16 +1284,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.2.5", + "version": "12.5.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "b71849b29f7a8d7574e4401873cb8b539896613f" + "reference": "4ba0e923f9d3fc655de22f9547c01d15a41fc93a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b71849b29f7a8d7574e4401873cb8b539896613f", - "reference": "b71849b29f7a8d7574e4401873cb8b539896613f", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4ba0e923f9d3fc655de22f9547c01d15a41fc93a", + "reference": "4ba0e923f9d3fc655de22f9547c01d15a41fc93a", "shasum": "" }, "require": { @@ -1314,23 +1303,23 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.1", + "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.3", - "phpunit/php-code-coverage": "^12.3.1", + "phpunit/php-code-coverage": "^12.5.1", "phpunit/php-file-iterator": "^6.0.0", "phpunit/php-invoker": "^6.0.0", "phpunit/php-text-template": "^5.0.0", "phpunit/php-timer": "^8.0.0", - "sebastian/cli-parser": "^4.0.0", - "sebastian/comparator": "^7.1.0", + "sebastian/cli-parser": "^4.2.0", + "sebastian/comparator": "^7.1.3", "sebastian/diff": "^7.0.0", - "sebastian/environment": "^8.0.2", - "sebastian/exporter": "^7.0.0", - "sebastian/global-state": "^8.0.0", + "sebastian/environment": "^8.0.3", + "sebastian/exporter": "^7.0.2", + "sebastian/global-state": "^8.0.2", "sebastian/object-enumerator": "^7.0.0", - "sebastian/type": "^6.0.2", + "sebastian/type": "^6.0.3", "sebastian/version": "^6.0.0", "staabm/side-effects-detector": "^1.0.5" }, @@ -1340,7 +1329,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "12.2-dev" + "dev-main": "12.5-dev" } }, "autoload": { @@ -1372,7 +1361,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.2.5" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.4" }, "funding": [ { @@ -1396,7 +1385,7 @@ "type": "tidelift" } ], - "time": "2025-06-27T04:37:55+00:00" + "time": "2025-12-15T06:05:34+00:00" }, { "name": "psr/container", @@ -1625,16 +1614,16 @@ }, { "name": "react/child-process", - "version": "v0.6.6", + "version": "v0.6.7", "source": { "type": "git", "url": "https://github.com/reactphp/child-process.git", - "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159" + "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/child-process/zipball/1721e2b93d89b745664353b9cfc8f155ba8a6159", - "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/970f0e71945556422ee4570ccbabaedc3cf04ad3", + "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3", "shasum": "" }, "require": { @@ -1688,7 +1677,7 @@ ], "support": { "issues": "https://github.com/reactphp/child-process/issues", - "source": "https://github.com/reactphp/child-process/tree/v0.6.6" + "source": "https://github.com/reactphp/child-process/tree/v0.6.7" }, "funding": [ { @@ -1696,20 +1685,20 @@ "type": "open_collective" } ], - "time": "2025-01-01T16:37:48+00:00" + "time": "2025-12-23T15:25:20+00:00" }, { "name": "react/dns", - "version": "v1.13.0", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/reactphp/dns.git", - "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5" + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", - "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "url": "https://api.github.com/repos/reactphp/dns/zipball/7562c05391f42701c1fccf189c8225fece1cd7c3", + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3", "shasum": "" }, "require": { @@ -1764,7 +1753,7 @@ ], "support": { "issues": "https://github.com/reactphp/dns/issues", - "source": "https://github.com/reactphp/dns/tree/v1.13.0" + "source": "https://github.com/reactphp/dns/tree/v1.14.0" }, "funding": [ { @@ -1772,20 +1761,20 @@ "type": "open_collective" } ], - "time": "2024-06-13T14:18:03+00:00" + "time": "2025-11-18T19:34:28+00:00" }, { "name": "react/event-loop", - "version": "v1.5.0", + "version": "v1.6.0", "source": { "type": "git", "url": "https://github.com/reactphp/event-loop.git", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/ba276bda6083df7e0050fd9b33f66ad7a4ac747a", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a", "shasum": "" }, "require": { @@ -1836,7 +1825,7 @@ ], "support": { "issues": "https://github.com/reactphp/event-loop/issues", - "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" + "source": "https://github.com/reactphp/event-loop/tree/v1.6.0" }, "funding": [ { @@ -1844,27 +1833,27 @@ "type": "open_collective" } ], - "time": "2023-11-13T13:48:05+00:00" + "time": "2025-11-17T20:46:25+00:00" }, { "name": "react/promise", - "version": "v3.2.0", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/reactphp/promise.git", - "reference": "8a164643313c71354582dc850b42b33fa12a4b63" + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63", - "reference": "8a164643313c71354582dc850b42b33fa12a4b63", + "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a", "shasum": "" }, "require": { "php": ">=7.1.0" }, "require-dev": { - "phpstan/phpstan": "1.10.39 || 1.4.10", + "phpstan/phpstan": "1.12.28 || 1.4.10", "phpunit/phpunit": "^9.6 || ^7.5" }, "type": "library", @@ -1909,7 +1898,7 @@ ], "support": { "issues": "https://github.com/reactphp/promise/issues", - "source": "https://github.com/reactphp/promise/tree/v3.2.0" + "source": "https://github.com/reactphp/promise/tree/v3.3.0" }, "funding": [ { @@ -1917,20 +1906,20 @@ "type": "open_collective" } ], - "time": "2024-05-24T10:39:05+00:00" + "time": "2025-08-19T18:57:03+00:00" }, { "name": "react/socket", - "version": "v1.16.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/reactphp/socket.git", - "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1" + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", - "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "url": "https://api.github.com/repos/reactphp/socket/zipball/ef5b17b81f6f60504c539313f94f2d826c5faa08", + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08", "shasum": "" }, "require": { @@ -1989,7 +1978,7 @@ ], "support": { "issues": "https://github.com/reactphp/socket/issues", - "source": "https://github.com/reactphp/socket/tree/v1.16.0" + "source": "https://github.com/reactphp/socket/tree/v1.17.0" }, "funding": [ { @@ -1997,7 +1986,7 @@ "type": "open_collective" } ], - "time": "2024-07-26T10:38:09+00:00" + "time": "2025-11-19T20:47:34+00:00" }, { "name": "react/stream", @@ -2079,16 +2068,16 @@ }, { "name": "sebastian/cli-parser", - "version": "4.0.0", + "version": "4.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c" + "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/6d584c727d9114bcdc14c86711cd1cad51778e7c", - "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/90f41072d220e5c40df6e8635f5dafba2d9d4d04", + "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04", "shasum": "" }, "require": { @@ -2100,7 +2089,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "4.2-dev" } }, "autoload": { @@ -2124,28 +2113,40 @@ "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.0.0" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser", + "type": "tidelift" } ], - "time": "2025-02-07T04:53:50+00:00" + "time": "2025-09-14T09:36:45+00:00" }, { "name": "sebastian/comparator", - "version": "7.1.0", + "version": "7.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "03d905327dccc0851c9a08d6a979dfc683826b6f" + "reference": "dc904b4bb3ab070865fa4068cd84f3da8b945148" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/03d905327dccc0851c9a08d6a979dfc683826b6f", - "reference": "03d905327dccc0851c9a08d6a979dfc683826b6f", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/dc904b4bb3ab070865fa4068cd84f3da8b945148", + "reference": "dc904b4bb3ab070865fa4068cd84f3da8b945148", "shasum": "" }, "require": { @@ -2204,7 +2205,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.0" + "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.3" }, "funding": [ { @@ -2224,7 +2225,7 @@ "type": "tidelift" } ], - "time": "2025-06-17T07:41:58+00:00" + "time": "2025-08-20T11:27:00+00:00" }, { "name": "sebastian/complexity", @@ -2353,16 +2354,16 @@ }, { "name": "sebastian/environment", - "version": "8.0.2", + "version": "8.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "d364b9e5d0d3b18a2573351a1786fbf96b7e0792" + "reference": "24a711b5c916efc6d6e62aa65aa2ec98fef77f68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d364b9e5d0d3b18a2573351a1786fbf96b7e0792", - "reference": "d364b9e5d0d3b18a2573351a1786fbf96b7e0792", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/24a711b5c916efc6d6e62aa65aa2ec98fef77f68", + "reference": "24a711b5c916efc6d6e62aa65aa2ec98fef77f68", "shasum": "" }, "require": { @@ -2405,7 +2406,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/8.0.2" + "source": "https://github.com/sebastianbergmann/environment/tree/8.0.3" }, "funding": [ { @@ -2425,20 +2426,20 @@ "type": "tidelift" } ], - "time": "2025-05-21T15:05:44+00:00" + "time": "2025-08-12T14:11:56+00:00" }, { "name": "sebastian/exporter", - "version": "7.0.0", + "version": "7.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "76432aafc58d50691a00d86d0632f1217a47b688" + "reference": "016951ae10980765e4e7aee491eb288c64e505b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/76432aafc58d50691a00d86d0632f1217a47b688", - "reference": "76432aafc58d50691a00d86d0632f1217a47b688", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/016951ae10980765e4e7aee491eb288c64e505b7", + "reference": "016951ae10980765e4e7aee491eb288c64e505b7", "shasum": "" }, "require": { @@ -2495,28 +2496,40 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.0" + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2025-02-07T04:56:42+00:00" + "time": "2025-09-24T06:16:11+00:00" }, { "name": "sebastian/global-state", - "version": "8.0.0", + "version": "8.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc" + "reference": "ef1377171613d09edd25b7816f05be8313f9115d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/570a2aeb26d40f057af686d63c4e99b075fb6cbc", - "reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ef1377171613d09edd25b7816f05be8313f9115d", + "reference": "ef1377171613d09edd25b7816f05be8313f9115d", "shasum": "" }, "require": { @@ -2557,15 +2570,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.0" + "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" } ], - "time": "2025-02-07T04:56:59+00:00" + "time": "2025-08-29T11:29:25+00:00" }, { "name": "sebastian/lines-of-code", @@ -2741,16 +2766,16 @@ }, { "name": "sebastian/recursion-context", - "version": "7.0.0", + "version": "7.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c" + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/c405ae3a63e01b32eb71577f8ec1604e39858a7c", - "reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", "shasum": "" }, "require": { @@ -2793,28 +2818,40 @@ "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.0" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2025-02-07T05:00:01+00:00" + "time": "2025-08-13T04:44:59+00:00" }, { "name": "sebastian/type", - "version": "6.0.2", + "version": "6.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "1d7cd6e514384c36d7a390347f57c385d4be6069" + "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/1d7cd6e514384c36d7a390347f57c385d4be6069", - "reference": "1d7cd6e514384c36d7a390347f57c385d4be6069", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/e549163b9760b8f71f191651d22acf32d56d6d4d", + "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d", "shasum": "" }, "require": { @@ -2850,15 +2887,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/type/issues", "security": "https://github.com/sebastianbergmann/type/security/policy", - "source": "https://github.com/sebastianbergmann/type/tree/6.0.2" + "source": "https://github.com/sebastianbergmann/type/tree/6.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" } ], - "time": "2025-03-18T13:37:31+00:00" + "time": "2025-08-09T06:57:12+00:00" }, { "name": "sebastian/version", @@ -2968,47 +3017,39 @@ }, { "name": "symfony/console", - "version": "v7.3.1", + "version": "v8.0.3", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101" + "reference": "6145b304a5c1ea0bdbd0b04d297a5864f9a7d587" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/9e27aecde8f506ba0fd1d9989620c04a87697101", - "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101", + "url": "https://api.github.com/repos/symfony/console/zipball/6145b304a5c1ea0bdbd0b04d297a5864f9a7d587", + "reference": "6145b304a5c1ea0bdbd0b04d297a5864f9a7d587", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2" - }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" + "symfony/string": "^7.4|^8.0" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -3042,7 +3083,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.1" + "source": "https://github.com/symfony/console/tree/v8.0.3" }, "funding": [ { @@ -3053,12 +3094,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-27T19:55:54+00:00" + "time": "2025-12-23T14:52:06+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3129,24 +3174,24 @@ }, { "name": "symfony/event-dispatcher", - "version": "v7.3.0", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "497f73ac996a598c92409b44ac43b6690c4f666d" + "reference": "573f95783a2ec6e38752979db139f09fec033f03" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/497f73ac996a598c92409b44ac43b6690c4f666d", - "reference": "497f73ac996a598c92409b44ac43b6690c4f666d", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/573f95783a2ec6e38752979db139f09fec033f03", + "reference": "573f95783a2ec6e38752979db139f09fec033f03", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<6.4", + "symfony/security-http": "<7.4", "symfony/service-contracts": "<2.5" }, "provide": { @@ -3155,13 +3200,14 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/error-handler": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0" + "symfony/stopwatch": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -3189,7 +3235,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.0" }, "funding": [ { @@ -3200,12 +3246,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-22T09:11:45+00:00" + "time": "2025-10-30T14:17:19+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -3285,25 +3335,25 @@ }, { "name": "symfony/filesystem", - "version": "v7.3.0", + "version": "v8.0.1", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" + "reference": "d937d400b980523dc9ee946bb69972b5e619058d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/d937d400b980523dc9ee946bb69972b5e619058d", + "reference": "d937d400b980523dc9ee946bb69972b5e619058d", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^6.4|^7.0" + "symfony/process": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -3331,7 +3381,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.3.0" + "source": "https://github.com/symfony/filesystem/tree/v8.0.1" }, "funding": [ { @@ -3342,32 +3392,36 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-25T15:15:23+00:00" + "time": "2025-12-01T09:13:36+00:00" }, { "name": "symfony/finder", - "version": "v7.3.0", + "version": "v8.0.3", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d" + "reference": "dd3a2953570a283a2ba4e17063bb98c734cf5b12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/ec2344cf77a48253bbca6939aa3d2477773ea63d", - "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d", + "url": "https://api.github.com/repos/symfony/finder/zipball/dd3a2953570a283a2ba4e17063bb98c734cf5b12", + "reference": "dd3a2953570a283a2ba4e17063bb98c734cf5b12", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0" + "symfony/filesystem": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -3395,7 +3449,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.3.0" + "source": "https://github.com/symfony/finder/tree/v8.0.3" }, "funding": [ { @@ -3406,29 +3460,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-30T19:00:26+00:00" + "time": "2025-12-23T14:52:06+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.3.0", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "afb9a8038025e5dbc657378bfab9198d75f10fca" + "reference": "d2b592535ffa6600c265a3893a7f7fd2bad82dd7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/afb9a8038025e5dbc657378bfab9198d75f10fca", - "reference": "afb9a8038025e5dbc657378bfab9198d75f10fca", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/d2b592535ffa6600c265a3893a7f7fd2bad82dd7", + "reference": "d2b592535ffa6600c265a3893a7f7fd2bad82dd7", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", @@ -3462,7 +3520,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.3.0" + "source": "https://github.com/symfony/options-resolver/tree/v8.0.0" }, "funding": [ { @@ -3473,16 +3531,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-04T13:12:05+00:00" + "time": "2025-11-12T15:55:31+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -3541,7 +3603,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -3552,6 +3614,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -3561,16 +3627,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { @@ -3619,7 +3685,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -3630,16 +3696,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -3700,7 +3770,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -3711,6 +3781,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -3720,7 +3794,7 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", @@ -3781,7 +3855,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -3792,6 +3866,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -3801,7 +3879,7 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -3861,7 +3939,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -3872,6 +3950,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -3881,7 +3963,7 @@ }, { "name": "symfony/polyfill-php81", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", @@ -3937,7 +4019,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0" }, "funding": [ { @@ -3948,6 +4030,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -3955,22 +4041,102 @@ ], "time": "2024-09-09T11:45:10+00:00" }, + { + "name": "symfony/polyfill-php84", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-24T13:30:11+00:00" + }, { "name": "symfony/process", - "version": "v7.3.0", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" + "reference": "a0a750500c4ce900d69ba4e9faf16f82c10ee149" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "url": "https://api.github.com/repos/symfony/process/zipball/a0a750500c4ce900d69ba4e9faf16f82c10ee149", + "reference": "a0a750500c4ce900d69ba4e9faf16f82c10ee149", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "type": "library", "autoload": { @@ -3998,7 +4164,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.0" + "source": "https://github.com/symfony/process/tree/v8.0.0" }, "funding": [ { @@ -4009,25 +4175,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-17T09:11:12+00:00" + "time": "2025-10-16T16:25:44+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.6.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", "shasum": "" }, "require": { @@ -4081,7 +4251,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" }, "funding": [ { @@ -4092,29 +4262,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-25T09:37:31+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { "name": "symfony/stopwatch", - "version": "v7.3.0", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd" + "reference": "67df1914c6ccd2d7b52f70d40cf2aea02159d942" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd", - "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/67df1914c6ccd2d7b52f70d40cf2aea02159d942", + "reference": "67df1914c6ccd2d7b52f70d40cf2aea02159d942", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/service-contracts": "^2.5|^3" }, "type": "library", @@ -4143,7 +4317,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v7.3.0" + "source": "https://github.com/symfony/stopwatch/tree/v8.0.0" }, "funding": [ { @@ -4154,44 +4328,47 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-02-24T10:49:57+00:00" + "time": "2025-08-04T07:36:47+00:00" }, { "name": "symfony/string", - "version": "v7.3.0", + "version": "v8.0.1", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125" + "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125", - "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125", + "url": "https://api.github.com/repos/symfony/string/zipball/ba65a969ac918ce0cc3edfac6cdde847eba231dc", + "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" }, "conflict": { "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/emoji": "^7.1", - "symfony/error-handler": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/var-exporter": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4230,7 +4407,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.0" + "source": "https://github.com/symfony/string/tree/v8.0.1" }, "funding": [ { @@ -4241,32 +4418,36 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-20T20:19:01+00:00" + "time": "2025-12-01T09:13:36+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" + "php": "^8.1" }, "type": "library", "autoload": { @@ -4288,7 +4469,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/2.0.1" }, "funding": [ { @@ -4296,7 +4477,7 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-12-08T11:19:18+00:00" } ], "aliases": [], From b921cefe074c55dcc55f1bc8bb6be1d6899d7fe7 Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Wed, 31 Dec 2025 09:28:54 -0500 Subject: [PATCH 8/9] Remove reference to PHP_CS_FIXER_IGNORE_ENV --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f09a6bf..b001c94 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ "paratest --configuration phpunit.xml" ], "cs-fix": [ - "export PHP_CS_FIXER_IGNORE_ENV=1 ; ./vendor/friendsofphp/php-cs-fixer/php-cs-fixer fix -v --config=./php-cs-fixer.php" + "./vendor/friendsofphp/php-cs-fixer/php-cs-fixer fix -v --config=./php-cs-fixer.php" ], "format": [ "@cs-fix" From 0b6ac8c394d23836a488279e900771b63eebbffa Mon Sep 17 00:00:00 2001 From: Kevin Ullyott Date: Wed, 31 Dec 2025 09:46:42 -0500 Subject: [PATCH 9/9] Fix deps --- composer.lock | 181 ++++++++++++++++++++++++++------------------------ 1 file changed, 95 insertions(+), 86 deletions(-) diff --git a/composer.lock b/composer.lock index 6b0a6e2..51e0a22 100644 --- a/composer.lock +++ b/composer.lock @@ -3017,39 +3017,47 @@ }, { "name": "symfony/console", - "version": "v8.0.3", + "version": "v7.4.3", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "6145b304a5c1ea0bdbd0b04d297a5864f9a7d587" + "reference": "732a9ca6cd9dfd940c639062d5edbde2f6727fb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/6145b304a5c1ea0bdbd0b04d297a5864f9a7d587", - "reference": "6145b304a5c1ea0bdbd0b04d297a5864f9a7d587", + "url": "https://api.github.com/repos/symfony/console/zipball/732a9ca6cd9dfd940c639062d5edbde2f6727fb6", + "reference": "732a9ca6cd9dfd940c639062d5edbde2f6727fb6", "shasum": "" }, "require": { - "php": ">=8.4", - "symfony/polyfill-mbstring": "^1.0", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.4|^8.0" + "symfony/string": "^7.2|^8.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^7.4|^8.0", - "symfony/dependency-injection": "^7.4|^8.0", - "symfony/event-dispatcher": "^7.4|^8.0", - "symfony/http-foundation": "^7.4|^8.0", - "symfony/http-kernel": "^7.4|^8.0", - "symfony/lock": "^7.4|^8.0", - "symfony/messenger": "^7.4|^8.0", - "symfony/process": "^7.4|^8.0", - "symfony/stopwatch": "^7.4|^8.0", - "symfony/var-dumper": "^7.4|^8.0" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -3083,7 +3091,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v8.0.3" + "source": "https://github.com/symfony/console/tree/v7.4.3" }, "funding": [ { @@ -3103,7 +3111,7 @@ "type": "tidelift" } ], - "time": "2025-12-23T14:52:06+00:00" + "time": "2025-12-23T14:50:43+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3174,24 +3182,24 @@ }, { "name": "symfony/event-dispatcher", - "version": "v8.0.0", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "573f95783a2ec6e38752979db139f09fec033f03" + "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/573f95783a2ec6e38752979db139f09fec033f03", - "reference": "573f95783a2ec6e38752979db139f09fec033f03", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9dddcddff1ef974ad87b3708e4b442dc38b2261d", + "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.2", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/security-http": "<7.4", + "symfony/dependency-injection": "<6.4", "symfony/service-contracts": "<2.5" }, "provide": { @@ -3200,14 +3208,14 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^7.4|^8.0", - "symfony/dependency-injection": "^7.4|^8.0", - "symfony/error-handler": "^7.4|^8.0", - "symfony/expression-language": "^7.4|^8.0", - "symfony/framework-bundle": "^7.4|^8.0", - "symfony/http-foundation": "^7.4|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^7.4|^8.0" + "symfony/stopwatch": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -3235,7 +3243,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.0" }, "funding": [ { @@ -3255,7 +3263,7 @@ "type": "tidelift" } ], - "time": "2025-10-30T14:17:19+00:00" + "time": "2025-10-28T09:38:46+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -3335,25 +3343,25 @@ }, { "name": "symfony/filesystem", - "version": "v8.0.1", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "d937d400b980523dc9ee946bb69972b5e619058d" + "reference": "d551b38811096d0be9c4691d406991b47c0c630a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/d937d400b980523dc9ee946bb69972b5e619058d", - "reference": "d937d400b980523dc9ee946bb69972b5e619058d", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/d551b38811096d0be9c4691d406991b47c0c630a", + "reference": "d551b38811096d0be9c4691d406991b47c0c630a", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^7.4|^8.0" + "symfony/process": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -3381,7 +3389,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v8.0.1" + "source": "https://github.com/symfony/filesystem/tree/v7.4.0" }, "funding": [ { @@ -3401,27 +3409,27 @@ "type": "tidelift" } ], - "time": "2025-12-01T09:13:36+00:00" + "time": "2025-11-27T13:27:24+00:00" }, { "name": "symfony/finder", - "version": "v8.0.3", + "version": "v7.4.3", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "dd3a2953570a283a2ba4e17063bb98c734cf5b12" + "reference": "fffe05569336549b20a1be64250b40516d6e8d06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/dd3a2953570a283a2ba4e17063bb98c734cf5b12", - "reference": "dd3a2953570a283a2ba4e17063bb98c734cf5b12", + "url": "https://api.github.com/repos/symfony/finder/zipball/fffe05569336549b20a1be64250b40516d6e8d06", + "reference": "fffe05569336549b20a1be64250b40516d6e8d06", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.2" }, "require-dev": { - "symfony/filesystem": "^7.4|^8.0" + "symfony/filesystem": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -3449,7 +3457,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v8.0.3" + "source": "https://github.com/symfony/finder/tree/v7.4.3" }, "funding": [ { @@ -3469,24 +3477,24 @@ "type": "tidelift" } ], - "time": "2025-12-23T14:52:06+00:00" + "time": "2025-12-23T14:50:43+00:00" }, { "name": "symfony/options-resolver", - "version": "v8.0.0", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "d2b592535ffa6600c265a3893a7f7fd2bad82dd7" + "reference": "b38026df55197f9e39a44f3215788edf83187b80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/d2b592535ffa6600c265a3893a7f7fd2bad82dd7", - "reference": "d2b592535ffa6600c265a3893a7f7fd2bad82dd7", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/b38026df55197f9e39a44f3215788edf83187b80", + "reference": "b38026df55197f9e39a44f3215788edf83187b80", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", @@ -3520,7 +3528,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v8.0.0" + "source": "https://github.com/symfony/options-resolver/tree/v7.4.0" }, "funding": [ { @@ -3540,7 +3548,7 @@ "type": "tidelift" } ], - "time": "2025-11-12T15:55:31+00:00" + "time": "2025-11-12T15:39:26+00:00" }, { "name": "symfony/polyfill-ctype", @@ -4123,20 +4131,20 @@ }, { "name": "symfony/process", - "version": "v8.0.0", + "version": "v7.4.3", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "a0a750500c4ce900d69ba4e9faf16f82c10ee149" + "reference": "2f8e1a6cdf590ca63715da4d3a7a3327404a523f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/a0a750500c4ce900d69ba4e9faf16f82c10ee149", - "reference": "a0a750500c4ce900d69ba4e9faf16f82c10ee149", + "url": "https://api.github.com/repos/symfony/process/zipball/2f8e1a6cdf590ca63715da4d3a7a3327404a523f", + "reference": "2f8e1a6cdf590ca63715da4d3a7a3327404a523f", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.2" }, "type": "library", "autoload": { @@ -4164,7 +4172,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v8.0.0" + "source": "https://github.com/symfony/process/tree/v7.4.3" }, "funding": [ { @@ -4184,7 +4192,7 @@ "type": "tidelift" } ], - "time": "2025-10-16T16:25:44+00:00" + "time": "2025-12-19T10:00:43+00:00" }, { "name": "symfony/service-contracts", @@ -4275,20 +4283,20 @@ }, { "name": "symfony/stopwatch", - "version": "v8.0.0", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "67df1914c6ccd2d7b52f70d40cf2aea02159d942" + "reference": "8a24af0a2e8a872fb745047180649b8418303084" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/67df1914c6ccd2d7b52f70d40cf2aea02159d942", - "reference": "67df1914c6ccd2d7b52f70d40cf2aea02159d942", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/8a24af0a2e8a872fb745047180649b8418303084", + "reference": "8a24af0a2e8a872fb745047180649b8418303084", "shasum": "" }, "require": { - "php": ">=8.4", + "php": ">=8.2", "symfony/service-contracts": "^2.5|^3" }, "type": "library", @@ -4317,7 +4325,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v8.0.0" + "source": "https://github.com/symfony/stopwatch/tree/v7.4.0" }, "funding": [ { @@ -4337,38 +4345,39 @@ "type": "tidelift" } ], - "time": "2025-08-04T07:36:47+00:00" + "time": "2025-08-04T07:05:15+00:00" }, { "name": "symfony/string", - "version": "v8.0.1", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc" + "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ba65a969ac918ce0cc3edfac6cdde847eba231dc", - "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc", + "url": "https://api.github.com/repos/symfony/string/zipball/d50e862cb0a0e0886f73ca1f31b865efbb795003", + "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003", "shasum": "" }, "require": { - "php": ">=8.4", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-intl-grapheme": "^1.33", - "symfony/polyfill-intl-normalizer": "^1.0", - "symfony/polyfill-mbstring": "^1.0" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.33", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/emoji": "^7.4|^8.0", - "symfony/http-client": "^7.4|^8.0", - "symfony/intl": "^7.4|^8.0", + "symfony/emoji": "^7.1|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^7.4|^8.0" + "symfony/var-exporter": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -4407,7 +4416,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.0.1" + "source": "https://github.com/symfony/string/tree/v7.4.0" }, "funding": [ { @@ -4427,7 +4436,7 @@ "type": "tidelift" } ], - "time": "2025-12-01T09:13:36+00:00" + "time": "2025-11-27T13:27:24+00:00" }, { "name": "theseer/tokenizer",