From fac3c2bc6305f4c8d5eadb294dcecab1fe728350 Mon Sep 17 00:00:00 2001 From: Sebastien Monterisi Date: Thu, 18 Sep 2025 17:00:45 +0200 Subject: [PATCH] Disallow native php exceptions without message. If a native exception is thrown it should have an explicit message. --- extension.neon | 1 + src/Rules/ForbidEmptyExceptionMessageRule.php | 72 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 src/Rules/ForbidEmptyExceptionMessageRule.php diff --git a/extension.neon b/extension.neon index 7b95bcc..995e5f3 100644 --- a/extension.neon +++ b/extension.neon @@ -34,3 +34,4 @@ rules: - PHPStanGlpi\Rules\ForbidExitRule - PHPStanGlpi\Rules\ForbidHttpResponseCodeRule - PHPStanGlpi\Rules\MissingGlobalVarTypeRule + - PHPStanGlpi\Rules\ForbidEmptyExceptionMessageRule diff --git a/src/Rules/ForbidEmptyExceptionMessageRule.php b/src/Rules/ForbidEmptyExceptionMessageRule.php new file mode 100644 index 0000000..9970b9b --- /dev/null +++ b/src/Rules/ForbidEmptyExceptionMessageRule.php @@ -0,0 +1,72 @@ + + */ +final class ForbidEmptyExceptionMessageRule implements Rule +{ + private const EXCEPTIONS_TO_CATCH = [ + \Exception::class, + \RuntimeException::class, + \LogicException::class, + ]; + + public function getNodeType(): string + { + return New_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node instanceof New_) { + return []; + } + + + if (!$node->class instanceof Node\Name) { + return []; + } + + $className = $node->class->toString(); + // not an Exception -> return + if (!is_a($className, \Exception::class, true)) { + return []; + } + + // Not an exception to catch + if (!in_array($className, self::EXCEPTIONS_TO_CATCH, true)) { + return []; + } + + // Check message argument (empty or not provided) + // exceptions without message may not be reported + // if a single argument is provided with it's name (eg. new \Exception(code: 123)) + $no_args = count($node->args) === 0; + // message provided but empty + $emptyMessage = false; + if (!$no_args) { + $firstArg = $node->args[0]->value; + $emptyMessage = $firstArg instanceof Node\Scalar\String_ && trim($firstArg->value) === ''; + } + + if ($no_args || $emptyMessage) { + return [ + RuleErrorBuilder::message('Native PHP exceptions must have an explicit message. ' . $className)->identifier('glpi.forbidEmptyExceptionMessage')->build(), + ]; + } + + return []; + } +}