-
Notifications
You must be signed in to change notification settings - Fork 219
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closes #7123: Rules about get_subscribed_events #7124
base: develop
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
<?php | ||
|
||
namespace WP_Rocket\Tests\phpstan\Rules; | ||
|
||
use PhpParser\Node; | ||
use PhpParser\Node\Stmt\Return_; | ||
use PHPStan\Analyser\Scope; | ||
use PHPStan\Rules\Rule; | ||
use PHPStan\Rules\RuleErrorBuilder; | ||
|
||
class EnsureCallbackMethodsExistsInSubscribedEvents implements Rule | ||
{ | ||
public function getNodeType(): string | ||
{ | ||
return Return_::class; // We want to inspect return statements | ||
} | ||
|
||
public function processNode(Node $node, Scope $scope): array | ||
{ | ||
$errors = []; | ||
|
||
// Check if the node is a return statement | ||
if ($node instanceof Return_ && $node->expr) { | ||
|
||
// Get the function/method name from the scope | ||
$functionName = $scope->getFunctionName(); | ||
if ('get_subscribed_events' === $functionName) { | ||
// Check if the return expression is an array | ||
if ($node->expr instanceof Node\Expr\Array_) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to bail out here too |
||
foreach ($node->expr->items as $item) { | ||
if ($item instanceof Node\Expr\ArrayItem) { | ||
Check failure on line 31 in tests/phpstan/Rules/EnsureCallbackMethodsExistsInSubscribedEvents.php GitHub Actions / WPRocket lint with PHP Stan. PHP 8.2 on ubuntu-latest.
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here with a |
||
// Ensure the key exists and is a valid type (like a string) before accessing the value | ||
$hookName = null; | ||
if ($item->key instanceof Node\Scalar\String_) { | ||
$hookName = $item->key->value; | ||
} | ||
|
||
// Analyze the value of the array item | ||
$methodValue = $item->value; | ||
$errors = array_merge($errors, $this->analyzeMethodValue($methodValue, $scope)); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
return $errors; // Return all collected errors | ||
} | ||
|
||
/** | ||
* Analyze the method value from the array structure. | ||
*/ | ||
private function analyzeMethodValue(Node $methodValue, Scope $scope): array | ||
{ | ||
$errors = []; | ||
|
||
if ($methodValue instanceof Node\Scalar\String_) { | ||
// Simple structure: array('hook_name' => 'method_name') | ||
$errors = $this->checkIfMethodExistsInClass($methodValue->value, $scope, $methodValue); | ||
Comment on lines
+57
to
+59
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can bail out here |
||
} elseif ($methodValue instanceof Node\Expr\Array_) { | ||
// More complex structures: array or nested array | ||
foreach ($methodValue->items as $subItem) { | ||
if ($subItem instanceof Node\Expr\ArrayItem) { | ||
Check failure on line 63 in tests/phpstan/Rules/EnsureCallbackMethodsExistsInSubscribedEvents.php GitHub Actions / WPRocket lint with PHP Stan. PHP 8.2 on ubuntu-latest.
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, we can bail out |
||
if ($subItem->value instanceof Node\Scalar\String_) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and same here with |
||
$errors = array_merge($errors, $this->checkIfMethodExistsInClass($subItem->value->value, $scope, $subItem->value)); | ||
} | ||
} | ||
} | ||
} | ||
|
||
return $errors; | ||
} | ||
|
||
public function checkIfMethodExistsInClass(string $methodName, Scope $scope, Node $node): array | ||
{ | ||
$classReflection = $scope->getClassReflection(); | ||
|
||
// Check if the class reflection is available and the method exists | ||
if ($classReflection && $classReflection->hasMethod($methodName)) { | ||
return []; | ||
} | ||
|
||
// Check if the method exists in the parent class or interfaces | ||
$parentClass = $classReflection ? $classReflection->getParentClass() : null; | ||
if ($parentClass && $parentClass->hasMethod($methodName)) { | ||
return []; | ||
} | ||
|
||
foreach ($classReflection->getInterfaces() as $interface) { | ||
if ($interface->hasMethod($methodName)) { | ||
return []; | ||
} | ||
} | ||
|
||
// If the method doesn't exist, return an error using RuleErrorBuilder | ||
$errorMessage = sprintf( | ||
"The callback function '%s' declared within 'get_subscribed_events' does not exist in the class '%s'.", | ||
$methodName, | ||
$classReflection ? $classReflection->getName() : 'unknown' | ||
); | ||
|
||
return [ | ||
RuleErrorBuilder::message($errorMessage) | ||
->line($node->getLine()) // Add the line number | ||
->build() | ||
]; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Miraeld two things:
Subscriber_Interface
to prevent side effects