diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4686feb..8b45c7a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - php-version: ['8.1', '8.2', '8.3'] + php-version: ['8.1', '8.4'] dependencies: ['highest'] include: - php-version: '8.1' @@ -68,7 +68,7 @@ jobs: uses: ramsey/composer-install@v2 - name: Run phpcs - run: vendor/bin/phpcs --report=checkstyle src/ tests/ | cs2pr + run: vendor/bin/phpcs --report=checkstyle | cs2pr - name: Run phpstan if: always() diff --git a/README.md b/README.md index 5dbf0aa..7404ced 100644 --- a/README.md +++ b/README.md @@ -148,13 +148,48 @@ This rule check if the options (args) passed to Table::find and SelectQuery are ### TableGetMatchOptionsTypesRule This rule check if the options (args) passed to Table::get are valid find options types. -To enable this rule update your phpstan.neon with: +### ControllerMethodMustBeUsedRule +This rule enforces that controller methods like `render()` and `redirect()` must be used (returned or assigned) to prevent unreachable code. These methods should not be called in void context - use them with a `return` statement or assign them to a variable to make the control flow explicit. +
+ Examples: + +```php +// Bad - code after render() is unreachable +public function myAction() +{ + $this->render('edit'); + $this->set('data', 'value'); // This will never execute +} + +// Good - explicit return prevents confusion +public function myAction() +{ + return $this->render('edit'); +} + +// Also good - assignment is valid +public function myAction() +{ + $response = $this->render('edit'); + + return $response; +} + +// Bad - code after redirect() is unreachable +public function myAction() +{ + $this->redirect(['action' => 'index']); + $this->Flash->success('Done'); // This will never execute +} + +// Good - explicit return prevents confusion +public function myAction() +{ + return $this->redirect(['action' => 'index']); +} ``` -parameters: - cakeDC: - disallowEntityArrayAccessRule: true -``` +
### How to disable a rule Each rule has a parameter in cakeDC 'namespace' to enable or disable, it is the same name of the diff --git a/composer.json b/composer.json index 6c6acef..8edb7a2 100644 --- a/composer.json +++ b/composer.json @@ -12,12 +12,12 @@ ], "require": { "php": ">=8.1.0", - "phpstan/phpstan": "^2.0", + "phpstan/phpstan": "^2.1.26", "cakephp/cakephp": "^5.0" }, "require-dev": { "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^10.1", + "phpunit/phpunit": "^10.5 || ^11.5 || ^12.1", "cakephp/cakephp-codesniffer": "^5.0", "phpstan/phpstan-deprecation-rules": "^2.0", "phpstan/phpstan-strict-rules": "^2.0" @@ -42,8 +42,8 @@ } }, "scripts": { - "cs-check": "phpcs -p src/ tests", - "cs-fix": "phpcbf -p src/ tests", + "cs-check": "phpcs -p", + "cs-fix": "phpcbf -p", "test": "phpunit --stderr", "stan-integration": [ "phpstan analyse --debug -c phpstan-test-app.neon", diff --git a/phpcs.xml b/phpcs.xml index 1ae027b..ffbe1e6 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -1,8 +1,11 @@ - - + + - + - - \ No newline at end of file + src/ + tests/ + + /tests/data/ + diff --git a/rules.neon b/rules.neon index 30f5bba..4b637f4 100644 --- a/rules.neon +++ b/rules.neon @@ -8,6 +8,7 @@ parameters: disallowEntityArrayAccessRule: false getMailerExistsClassRule: true loadComponentExistsClassRule: true + controllerMethodMustBeUsedRule: true parametersSchema: cakeDC: structure([ addAssociationExistsTableClassRule: anyOf(bool(), arrayOf(bool())) @@ -18,6 +19,7 @@ parametersSchema: disallowEntityArrayAccessRule: anyOf(bool(), arrayOf(bool())) getMailerExistsClassRule: anyOf(bool(), arrayOf(bool())) loadComponentExistsClassRule: anyOf(bool(), arrayOf(bool())) + controllerMethodMustBeUsedRule: anyOf(bool(), arrayOf(bool())) ]) conditionalTags: @@ -25,6 +27,8 @@ conditionalTags: phpstan.parser.richParserNodeVisitor: %cakeDC.addAssociationExistsTableClassRule% CakeDC\PHPStan\Rule\Controller\LoadComponentExistsClassRule: phpstan.rules.rule: %cakeDC.loadComponentExistsClassRule% + CakeDC\PHPStan\Rule\Controller\ControllerMethodMustBeUsedRule: + phpstan.rules.rule: %cakeDC.controllerMethodMustBeUsedRule% CakeDC\PHPStan\Rule\Model\AddAssociationExistsTableClassRule: phpstan.rules.rule: %cakeDC.addAssociationExistsTableClassRule% CakeDC\PHPStan\Rule\Model\AddAssociationMatchOptionsTypesRule: @@ -45,6 +49,8 @@ services: class: CakeDC\PHPStan\Visitor\AddAssociationSetClassNameVisitor - class: CakeDC\PHPStan\Rule\Controller\LoadComponentExistsClassRule + - + class: CakeDC\PHPStan\Rule\Controller\ControllerMethodMustBeUsedRule - class: CakeDC\PHPStan\Rule\Model\AddAssociationExistsTableClassRule - diff --git a/src/Method/AssociationTableMixinClassReflectionExtension.php b/src/Method/AssociationTableMixinClassReflectionExtension.php index 52e578f..4fed680 100644 --- a/src/Method/AssociationTableMixinClassReflectionExtension.php +++ b/src/Method/AssociationTableMixinClassReflectionExtension.php @@ -49,11 +49,11 @@ protected function getTableReflection(): ClassReflection public function hasMethod(ClassReflection $classReflection, string $methodName): bool { // magic findBy* method - if ($classReflection->isSubclassOf(Table::class) && preg_match('/^find(?:\w+)?By/', $methodName) > 0) { + if ($classReflection->is(Table::class) && preg_match('/^find(?:\w+)?By/', $methodName) > 0) { return true; } - if (!$classReflection->isSubclassOf(Association::class)) { + if (!$classReflection->is(Association::class)) { return false; } @@ -68,7 +68,7 @@ public function hasMethod(ClassReflection $classReflection, string $methodName): public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection { // magic findBy* method - if ($classReflection->isSubclassOf(Table::class) && preg_match('/^find(?:\w+)?By/', $methodName) > 0) { + if ($classReflection->is(Table::class) && preg_match('/^find(?:\w+)?By/', $methodName) > 0) { return new TableFindByPropertyMethodReflection($methodName, $classReflection); } @@ -82,11 +82,11 @@ public function getMethod(ClassReflection $classReflection, string $methodName): */ public function hasProperty(ClassReflection $classReflection, string $propertyName): bool { - if (!$classReflection->isSubclassOf(Association::class)) { + if (!$classReflection->is(Association::class)) { return false; } - return $this->getTableReflection()->hasProperty($propertyName); + return $this->getTableReflection()->hasInstanceProperty($propertyName); } /** diff --git a/src/Method/DummyParameter.php b/src/Method/DummyParameter.php index 443e3a5..a2c383a 100644 --- a/src/Method/DummyParameter.php +++ b/src/Method/DummyParameter.php @@ -48,7 +48,7 @@ public function __construct( bool $optional, ?PassedByReference $passedByReference, bool $variadic, - ?Type $defaultValue + ?Type $defaultValue, ) { $this->name = $name; $this->type = $type; diff --git a/src/Method/TableFindByPropertyMethodReflection.php b/src/Method/TableFindByPropertyMethodReflection.php index 7b2813c..a9b8b6b 100644 --- a/src/Method/TableFindByPropertyMethodReflection.php +++ b/src/Method/TableFindByPropertyMethodReflection.php @@ -45,13 +45,13 @@ public function __construct(string $name, ClassReflection $declaringClass) $this->name = $name; $this->declaringClass = $declaringClass; - $params = array_map(fn ($field) => new DummyParameter( + $params = array_map(fn($field) => new DummyParameter( $field, new MixedType(), false, null, false, - null + null, ), $this->getParams($name)); $returnType = new ObjectType(SelectQuery::class); @@ -62,7 +62,7 @@ public function __construct(string $name, ClassReflection $declaringClass) null, $params, false, - $returnType + $returnType, ), ]; } diff --git a/src/PhpDoc/TableAssociationTypeNodeResolverExtension.php b/src/PhpDoc/TableAssociationTypeNodeResolverExtension.php index 6fb3265..0013d58 100644 --- a/src/PhpDoc/TableAssociationTypeNodeResolverExtension.php +++ b/src/PhpDoc/TableAssociationTypeNodeResolverExtension.php @@ -80,7 +80,7 @@ public function resolve(TypeNode $typeNode, NameScope $nameScope): ?Type if ($config['table'] !== null && $config['association'] !== null) { return new GenericObjectType( $config['association']->getObjectClassNames()[0], - [$config['table']] + [$config['table']], ); } diff --git a/src/Rule/Controller/ControllerMethodMustBeUsedRule.php b/src/Rule/Controller/ControllerMethodMustBeUsedRule.php new file mode 100644 index 0000000..680e128 --- /dev/null +++ b/src/Rule/Controller/ControllerMethodMustBeUsedRule.php @@ -0,0 +1,107 @@ + + */ + protected array $methodsRequiringUsage = [ + 'render', + 'redirect', + ]; + + /** + * @inheritDoc + */ + public function getNodeType(): string + { + return Expression::class; + } + + /** + * @param \PhpParser\Node $node + * @param \PHPStan\Analyser\Scope $scope + * @return list<\PHPStan\Rules\IdentifierRuleError> + */ + public function processNode(Node $node, Scope $scope): array + { + assert($node instanceof Expression); + + // Check if this is a method call + if (!$node->expr instanceof MethodCall) { + return []; + } + + $methodCall = $node->expr; + + // Check if the method name is one we care about + if (!$methodCall->name instanceof Node\Identifier) { + return []; + } + + $methodName = $methodCall->name->toString(); + if (!in_array($methodName, $this->methodsRequiringUsage, true)) { + return []; + } + + // Check if the method is being called on $this + $callerType = $scope->getType($methodCall->var); + if (!$callerType->isObject()->yes()) { + return []; + } + + // Check if it's a Controller class + $classReflections = $callerType->getObjectClassReflections(); + $isController = false; + foreach ($classReflections as $classReflection) { + if ($classReflection->is(Controller::class)) { + $isController = true; + break; + } + } + + if (!$isController) { + return []; + } + + // If we reach here, it means the method call is wrapped in an Expression node + // which means it's used as a statement (not returned or assigned) + return [ + RuleErrorBuilder::message(sprintf( + 'Method `%s()` must be used to prevent unreachable code. ' . + 'Use `return $this->%s()` or assign it to a variable.', + $methodName, + $methodName, + )) + ->identifier('cake.controller.' . $methodName . 'MustBeUsed') + ->build(), + ]; + } +} diff --git a/src/Rule/LoadObjectExistsCakeClassRule.php b/src/Rule/LoadObjectExistsCakeClassRule.php index 7cbea77..6f4646b 100644 --- a/src/Rule/LoadObjectExistsCakeClassRule.php +++ b/src/Rule/LoadObjectExistsCakeClassRule.php @@ -71,7 +71,7 @@ public function processNode(Node $node, Scope $scope): array if ($inputClassName === null) { $inputClassName = $this->getInputClassName( $details['alias']->value, - $details['options'] + $details['options'], ); } if ($inputClassName === null || $this->getTargetClassName($inputClassName) !== null) { diff --git a/src/Rule/Model/AddAssociationMatchOptionsTypesRule.php b/src/Rule/Model/AddAssociationMatchOptionsTypesRule.php index 9dccf4d..c3f801c 100644 --- a/src/Rule/Model/AddAssociationMatchOptionsTypesRule.php +++ b/src/Rule/Model/AddAssociationMatchOptionsTypesRule.php @@ -96,7 +96,7 @@ public function processNode(Node $node, Scope $scope): array $details, $properties[$item->key->value], $item, - $scope + $scope, ); if ($error !== null) { $errors[] = $error; @@ -106,7 +106,7 @@ public function processNode(Node $node, Scope $scope): array 'Call to %s::%s with unknown option "%s".', $reference, $node->name->name, - $item->key->value + $item->key->value, )) ->identifier('cake.addAssociationWithValidOption.unknownOption') ->build(); @@ -164,13 +164,13 @@ protected function processPropertyTypeCheck( array $details, string $property, ArrayItem $item, - Scope $scope + Scope $scope, ): ?IdentifierRuleError { $object = new ObjectType($details['type']); $classReflection = $object->getClassReflection(); assert($classReflection instanceof ClassReflection); $propertyType = $classReflection - ->getProperty('_' . $property, $scope) + ->getInstanceProperty('_' . $property, $scope) ->getWritableType(); $assignedValueType = $scope->getType($item->value); $accepts = $this->ruleLevelHelper->accepts($propertyType, $assignedValueType, true); @@ -182,7 +182,7 @@ protected function processPropertyTypeCheck( 'Call to %s::%s with option "%s"', $details['reference'], $details['methodName'], - $item->key->value + $item->key->value, ); $verbosityLevel = VerbosityLevel::getRecommendedLevelByType($propertyType, $assignedValueType); @@ -191,8 +191,8 @@ protected function processPropertyTypeCheck( '%s (%s) does not accept %s.', $propertyDescription, $propertyType->describe($verbosityLevel), - $assignedValueType->describe($verbosityLevel) - ) + $assignedValueType->describe($verbosityLevel), + ), ) ->acceptsReasonsTip($accepts->reasons) ->identifier('cake.addAssociationWithValidOption.invalidType') diff --git a/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php b/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php index 40cbc34..a37db68 100644 --- a/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php +++ b/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php @@ -129,7 +129,7 @@ public function processNode(Node $node, Scope $scope): array $parameterType, $scope->getType($item), $details, - $name + $name, ) : null; if ($error !== null) { @@ -191,7 +191,7 @@ protected function processPropertyTypeCheck( Type $inputType, Type $assignedValueType, array $details, - string $property + string $property, ): ?RuleError { $accepts = $this->ruleLevelHelper->accepts($inputType, $assignedValueType, true); @@ -202,7 +202,7 @@ protected function processPropertyTypeCheck( 'Call to %s::%s with option "%s"', $details['reference'], $details['methodName'], - $property + $property, ); $verbosityLevel = VerbosityLevel::getRecommendedLevelByType($inputType, $assignedValueType); @@ -211,8 +211,8 @@ protected function processPropertyTypeCheck( '%s (%s) does not accept %s.', $propertyDescription, $inputType->describe($verbosityLevel), - $assignedValueType->describe($verbosityLevel) - ) + $assignedValueType->describe($verbosityLevel), + ), ) ->acceptsReasonsTip($accepts->reasons) ->identifier('cake.tableGetMatchOptionsTypes.invalidType') @@ -423,7 +423,7 @@ protected function checkMissingRequiredOptions(array $specificFinderOptions, arr 'Call to %s::%s is missing required finder option "%s".', $details['reference'], $details['methodName'], - $requireOptionName + $requireOptionName, ); $errors[] = RuleErrorBuilder::message($errorMessage) ->identifier('cake.tableGetMatchOptionsTypes.invalidType') diff --git a/src/Traits/BaseCakeRegistryReturnTrait.php b/src/Traits/BaseCakeRegistryReturnTrait.php index 8df3ab5..540ff6d 100644 --- a/src/Traits/BaseCakeRegistryReturnTrait.php +++ b/src/Traits/BaseCakeRegistryReturnTrait.php @@ -34,7 +34,7 @@ trait BaseCakeRegistryReturnTrait public function getTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, - Scope $scope + Scope $scope, ): ?Type { if (count($methodCall->getArgs()) === 0) { return null; diff --git a/src/Type/BaseTraitExpressionTypeResolverExtension.php b/src/Type/BaseTraitExpressionTypeResolverExtension.php index 0d88c5c..3b5d090 100644 --- a/src/Type/BaseTraitExpressionTypeResolverExtension.php +++ b/src/Type/BaseTraitExpressionTypeResolverExtension.php @@ -41,7 +41,7 @@ public function __construct( protected string $targetTrait, protected string $methodName, protected string $namespaceFormat, - protected ?string $propertyDefaultValue = null + protected ?string $propertyDefaultValue = null, ) { } diff --git a/src/Type/ControllerFetchTableDynamicReturnTypeExtension.php b/src/Type/ControllerFetchTableDynamicReturnTypeExtension.php index 6102969..9262551 100644 --- a/src/Type/ControllerFetchTableDynamicReturnTypeExtension.php +++ b/src/Type/ControllerFetchTableDynamicReturnTypeExtension.php @@ -27,7 +27,7 @@ class ControllerFetchTableDynamicReturnTypeExtension extends TableLocatorDynamic protected function getReturnTypeWithoutArgs( MethodReflection $methodReflection, MethodCall $methodCall, - ClassReflection $targetClassReflection + ClassReflection $targetClassReflection, ): ?Type { $type = parent::getReturnTypeWithoutArgs($methodReflection, $methodCall, $targetClassReflection); if ($type !== null) { @@ -47,7 +47,7 @@ protected function getReturnTypeWithoutArgs( */ protected function getDefaultTableByControllerClass(ClassReflection $targetClassReflection): ?string { - $hasProperty = $targetClassReflection->hasProperty('defaultTable'); + $hasProperty = $targetClassReflection->hasInstanceProperty('defaultTable'); if (!$hasProperty) { return null; } @@ -63,7 +63,7 @@ protected function getDefaultTableByControllerClass(ClassReflection $targetClass $tableClassName = sprintf( '%s\\Model\\Table\\%sTable', $baseNamespace, - $shortName + $shortName, ); if (class_exists($tableClassName)) { diff --git a/src/Type/RepositoryEntityDynamicReturnTypeExtension.php b/src/Type/RepositoryEntityDynamicReturnTypeExtension.php index 1745cf5..1b1fc5b 100644 --- a/src/Type/RepositoryEntityDynamicReturnTypeExtension.php +++ b/src/Type/RepositoryEntityDynamicReturnTypeExtension.php @@ -85,7 +85,7 @@ public function isMethodSupported(MethodReflection $methodReflection): bool public function getTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, - Scope $scope + Scope $scope, ): ?Type { $className = $this->getReferenceClass($scope, $methodCall); if ($className === null || $className === Table::class) { diff --git a/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php b/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php index fc60958..818868f 100644 --- a/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php +++ b/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php @@ -85,7 +85,7 @@ public function isMethodSupported(MethodReflection $methodReflection): bool public function getTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, - Scope $scope + Scope $scope, ): ?Type { $args = $methodCall->getArgs(); if (count($args) === 0) { diff --git a/src/Type/TableLocatorDynamicReturnTypeExtension.php b/src/Type/TableLocatorDynamicReturnTypeExtension.php index 954f844..2584872 100644 --- a/src/Type/TableLocatorDynamicReturnTypeExtension.php +++ b/src/Type/TableLocatorDynamicReturnTypeExtension.php @@ -65,7 +65,7 @@ public function __construct(string $className, string $methodName) public function getTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, - Scope $scope + Scope $scope, ): ?Type { if (count($methodCall->getArgs()) === 0) { $targetClassReflection = $this->getTargetClassReflection($scope, $methodCall); @@ -109,7 +109,7 @@ protected function getDefaultTable(ClassReflection $target): ?string protected function getReturnTypeWithoutArgs( MethodReflection $methodReflection, MethodCall $methodCall, - ClassReflection $targetClassReflection + ClassReflection $targetClassReflection, ): ?Type { try { $defaultTable = $this->getDefaultTable($targetClassReflection); diff --git a/tests/TestCase/Rule/Controller/ControllerMethodMustBeUsedRuleTest.php b/tests/TestCase/Rule/Controller/ControllerMethodMustBeUsedRuleTest.php new file mode 100644 index 0000000..4d4e6eb --- /dev/null +++ b/tests/TestCase/Rule/Controller/ControllerMethodMustBeUsedRuleTest.php @@ -0,0 +1,62 @@ +analyse([__DIR__ . '/Fake/FailingControllerMethodReturnLogic.php'], [ + [ + 'Method `render()` must be used to prevent unreachable code. Use `return $this->render()` or assign it to a variable.', + 17, + ], + [ + 'Method `redirect()` must be used to prevent unreachable code. Use `return $this->redirect()` or assign it to a variable.', + 29, + ], + [ + 'Method `render()` must be used to prevent unreachable code. Use `return $this->render()` or assign it to a variable.', + 62, + ], + [ + 'Method `redirect()` must be used to prevent unreachable code. Use `return $this->redirect()` or assign it to a variable.', + 74, + ], + ]); + } + + /** + * @inheritDoc + */ + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/../../../../extension.neon']; + } +} diff --git a/tests/TestCase/Rule/Controller/Fake/FailingControllerMethodReturnLogic.php b/tests/TestCase/Rule/Controller/Fake/FailingControllerMethodReturnLogic.php new file mode 100644 index 0000000..7d5d0be --- /dev/null +++ b/tests/TestCase/Rule/Controller/Fake/FailingControllerMethodReturnLogic.php @@ -0,0 +1,101 @@ +render('edit'); + // This code is unreachable + $this->set('data', 'value'); + } + + /** + * Action with redirect() not returned + * + * @return \Cake\Http\Response|null + */ + public function actionWithoutReturnRedirect() + { + $this->redirect(['action' => 'index']); + // This code is unreachable + $this->set('data', 'value'); + } + + /** + * Action with render() properly returned + * + * @return \Cake\Http\Response|null + */ + public function actionWithCorrectRender() + { + return $this->render('edit'); + } + + /** + * Action with redirect() properly returned + * + * @return \Cake\Http\Response|null + */ + public function actionWithCorrectRedirect() + { + return $this->redirect(['action' => 'index']); + } + + /** + * Action with conditional render() not returned + * + * @return \Cake\Http\Response|null + */ + public function actionWithConditionalRenderNoReturn() + { + if ($this->request->is('ajax')) { + $this->render('ajax_view'); + } + } + + /** + * Action with conditional redirect() not returned + * + * @return \Cake\Http\Response|null + */ + public function actionWithConditionalRedirectNoReturn() + { + if (!$this->request->getData('id')) { + $this->redirect(['action' => 'add']); + } + } + + /** + * Action with render() assigned to variable (valid usage) + * + * @return \Cake\Http\Response|null + */ + public function actionWithRenderAssigned() + { + $response = $this->render('edit'); + + return $response; + } + + /** + * Action with redirect() assigned to variable (valid usage) + * + * @return \Cake\Http\Response|null + */ + public function actionWithRedirectAssigned() + { + $response = $this->redirect(['action' => 'index']); + + return $response; + } +} diff --git a/tests/TestCase/Rule/Model/AddAssociationMatchOptionsTypesRuleTest.php b/tests/TestCase/Rule/Model/AddAssociationMatchOptionsTypesRuleTest.php index 96ce888..336d352 100644 --- a/tests/TestCase/Rule/Model/AddAssociationMatchOptionsTypesRuleTest.php +++ b/tests/TestCase/Rule/Model/AddAssociationMatchOptionsTypesRuleTest.php @@ -17,7 +17,7 @@ protected function getRule(): Rule { // getRule() method needs to return an instance of the tested rule return new AddAssociationMatchOptionsTypesRule( - static::getContainer()->getByType(RuleLevelHelper::class) + static::getContainer()->getByType(RuleLevelHelper::class), ); } diff --git a/tests/TestCase/Rule/Model/Fake/FailingOrmFindRuleItemsLogic.php b/tests/TestCase/Rule/Model/Fake/FailingOrmFindRuleItemsLogic.php index b8b9a01..79ff7ce 100644 --- a/tests/TestCase/Rule/Model/Fake/FailingOrmFindRuleItemsLogic.php +++ b/tests/TestCase/Rule/Model/Fake/FailingOrmFindRuleItemsLogic.php @@ -49,7 +49,7 @@ public function process() groupBy: false, having: new stdClass(), contain: true, - page: 'Other' + page: 'Other', ); $Table->find( 'all', //Good options @@ -60,7 +60,7 @@ public function process() offset: 3, group: ['Notes.type'], contain: ['Users'], - page: 3 + page: 3, ); $Table->find( 'all', //some good options but not all @@ -71,7 +71,7 @@ public function process() offset: 3, group: true, //bad contain: ['Users'], - page: 3 + page: 3, ); $query = $Table->find(); $query->find(//bad information @@ -123,13 +123,13 @@ public function process() 'featured', //custom finder is known fields: ['Notes.id', 'Notes.note', 'Notes.created'], year: 2024, - fun: true + fun: true, ); $Table->find( 'featured', //custom finder is known but options are invalid fields: ['Notes.id', 'Notes.note', 'Notes.created'], year: 10.0, - fun: 1 + fun: 1, ); $Table->find( 'featured', //custom finder is known but required options are missing only have basic find options @@ -165,18 +165,18 @@ public function process() $Table->find( 'optionsPacked', sort: ['Notes.note' => 'ASC'], - labelField: 'id' + labelField: 'id', ); $Table->find( 'optionsPacked', sort: ['Notes.note' => 'ASC'], - labelField: 'id' + labelField: 'id', ); $Table->find('argsPacked'); $Table->find( 'argsPacked', sort: ['Notes.note' => 'ASC'], - groupLabel: 'type' + groupLabel: 'type', ); $Table->find('argsPacked', [ 'sort' => ['Notes.note' => 'ASC'], @@ -192,13 +192,13 @@ public function process() $Table->find( 'twoArgsButNotLegacy', sort: ['Notes.note' => 'ASC'], - myType: 'featured' + myType: 'featured', ); $Table->find('twoArgsButNotLegacy'); $Table->find( 'twoArgsButNotLegacy', sort: ['Notes.note' => 'ASC'], - myType: 19 + myType: 19, ); $field = $Table->getTypeTestTwoArgsButNotLegacy(); $value = 'featured'; diff --git a/tests/TestCase/Rule/Model/Fake/FailingRuleItemsTable.php b/tests/TestCase/Rule/Model/Fake/FailingRuleItemsTable.php index 8172a5e..24a2fde 100644 --- a/tests/TestCase/Rule/Model/Fake/FailingRuleItemsTable.php +++ b/tests/TestCase/Rule/Model/Fake/FailingRuleItemsTable.php @@ -68,7 +68,7 @@ public function initialize(array $config): void 'cascadeCallbacks' => 1,//Can't be integer, it should be bool 'conditions' => 'Users.active = 1',//Can't be string, it should be Closure or array 'dependent' => 0,//Must be - 'finder' => fn () => 'f', + 'finder' => fn() => 'f', 'bindingKey' => 10, 'foreignKey' => 11, 'joinType' => 12, @@ -97,11 +97,11 @@ public function initialize(array $config): void ]); $this->belongsToMany('SadUsers', [ 'className' => UsersTable::class, - 'targetForeignKey' => fn () => 10, + 'targetForeignKey' => fn() => 10, 'through' => new stdClass(), - 'saveStrategy' => fn () => 'na', + 'saveStrategy' => fn() => 'na', 'sort' => false, - 'joinTable' => fn () => 'my_users_failing', + 'joinTable' => fn() => 'my_users_failing', ]); $this->hasOne('MainArticles', [ 'className' => VeryCustomize00009ArticlesTable::class, @@ -122,7 +122,7 @@ public function initialize(array $config): void 'cascadeCallbacks' => 1,//Can't be integer, it should be bool 'conditions' => 'parent_id = id',//Can't be string, it should be Closure or array 'dependent' => 0,//Must be - 'finder' => fn () => 'f', + 'finder' => fn() => 'f', 'bindingKey' => 10, 'foreignKey' => 11, 'joinType' => 12, @@ -150,7 +150,7 @@ public function initialize(array $config): void 'cascadeCallbacks' => 1,//Can't be integer, it should be bool 'conditions' => 'parent_id = id',//Can't be string, it should be Closure or array 'dependent' => 0,//Must be - 'finder' => fn () => 'f', + 'finder' => fn() => 'f', 'bindingKey' => 10, 'foreignKey' => 11, 'joinType' => 12, diff --git a/tests/TestCase/Rule/Model/Fake/FailingTableGetRuleItemsLogic.php b/tests/TestCase/Rule/Model/Fake/FailingTableGetRuleItemsLogic.php index 5e98901..c4a4685 100644 --- a/tests/TestCase/Rule/Model/Fake/FailingTableGetRuleItemsLogic.php +++ b/tests/TestCase/Rule/Model/Fake/FailingTableGetRuleItemsLogic.php @@ -49,7 +49,7 @@ public function process() groupBy: false, having: new stdClass(), contain: true, - page: 'Other' + page: 'Other', ); $Table->get( 1, //Good options @@ -60,7 +60,7 @@ public function process() offset: 3, group: ['Notes.type'], contain: ['Users'], - page: 3 + page: 3, ); $Table->get(1, 'all', ...[ 'order' => false, diff --git a/tests/TestCase/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRuleTest.php b/tests/TestCase/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRuleTest.php index 273886c..8331638 100644 --- a/tests/TestCase/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRuleTest.php +++ b/tests/TestCase/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRuleTest.php @@ -17,7 +17,7 @@ protected function getRule(): Rule { // getRule() method needs to return an instance of the tested rule return new OrmSelectQueryFindMatchOptionsTypesRule( - static::getContainer()->getByType(RuleLevelHelper::class) + static::getContainer()->getByType(RuleLevelHelper::class), ); } diff --git a/tests/TestCase/Rule/Model/TableGetMatchOptionsTypesRuleTest.php b/tests/TestCase/Rule/Model/TableGetMatchOptionsTypesRuleTest.php index 16cc824..fba9259 100644 --- a/tests/TestCase/Rule/Model/TableGetMatchOptionsTypesRuleTest.php +++ b/tests/TestCase/Rule/Model/TableGetMatchOptionsTypesRuleTest.php @@ -17,7 +17,7 @@ protected function getRule(): Rule { // getRule() method needs to return an instance of the tested rule return new TableGetMatchOptionsTypesRule( - static::getContainer()->getByType(RuleLevelHelper::class) + static::getContainer()->getByType(RuleLevelHelper::class), ); } diff --git a/tests/data/Rule/addBehaviorRule.php b/tests/data/Rule/addBehaviorRule.php deleted file mode 100644 index 30ec340..0000000 --- a/tests/data/Rule/addBehaviorRule.php +++ /dev/null @@ -1,43 +0,0 @@ -setTable('notes'); - $this->setDisplayField('note'); - $this->setPrimaryKey('id'); - $this->addBehavior('Timestamp');//valid name - $this->addBehavior('Timtamp');//Invalid name - $this->addBehavior('Tree', [ - 'className' => 'MyTreeBehavior',//invalid name - ]); - $this->addBehavior('Translate', [ - 'className' => 'Cake\Behavior\TranslateBehavior',//invalid full className - ]); - $this->addBehavior('CounterCache', [ - 'className' => 'Cake\ORM\Behavior\CounterCacheBehavior',//valid className - ]); - $this->addBehavior('SampleTestCustomMethod');//valid behavior from test_app - } -} diff --git a/tests/test_app/Model/Table/NotesTable.php b/tests/test_app/Model/Table/NotesTable.php index 9ce9669..d6e33ec 100644 --- a/tests/test_app/Model/Table/NotesTable.php +++ b/tests/test_app/Model/Table/NotesTable.php @@ -65,7 +65,7 @@ public function warning(): array $this->find( 'twoArgsButNotLegacy', sort: ['Notes.note' => 'ASC'], - myType: 'featured' + myType: 'featured', ); $this->find('argsPacked'); @@ -89,7 +89,7 @@ public function findFeatured(SelectQuery $query, string|int $year, bool $fun): S if ($fun === true) { $where[] = $query->newExpr()->in( 'type', - ['funny_stuff', 'funny_songs', 'funny_messages'] + ['funny_stuff', 'funny_songs', 'funny_messages'], ); } diff --git a/tests/test_app/Model/Table/VeryCustomize00009ArticlesTable.php b/tests/test_app/Model/Table/VeryCustomize00009ArticlesTable.php index 3fc87e8..eb6c90f 100644 --- a/tests/test_app/Model/Table/VeryCustomize00009ArticlesTable.php +++ b/tests/test_app/Model/Table/VeryCustomize00009ArticlesTable.php @@ -66,7 +66,7 @@ public function newSample() [ 'title' => 'This is my title', 'content' => 'Sample content for test', - ] + ], ); } }