diff --git a/extension.neon b/extension.neon index 5d2a70a..39e0472 100644 --- a/extension.neon +++ b/extension.neon @@ -95,6 +95,10 @@ services: class: SzepeViktor\PHPStan\WordPress\WpParseUrlFunctionDynamicReturnTypeExtension tags: - phpstan.broker.dynamicFunctionReturnTypeExtension + - + class: SzepeViktor\PHPStan\WordPress\WpDieDynamicFunctionReturnTypeExtension + tags: + - phpstan.broker.dynamicFunctionReturnTypeExtension - class: SzepeViktor\PHPStan\WordPress\HookDocsVisitor tags: @@ -124,7 +128,6 @@ parameters: - SAVEQUERIES - SCRIPT_DEBUG earlyTerminatingFunctionCalls: - - wp_die - wp_send_json - wp_send_json_success - wp_send_json_error diff --git a/src/WpDieDynamicFunctionReturnTypeExtension.php b/src/WpDieDynamicFunctionReturnTypeExtension.php new file mode 100644 index 0000000..bce6590 --- /dev/null +++ b/src/WpDieDynamicFunctionReturnTypeExtension.php @@ -0,0 +1,53 @@ +getName() === 'wp_die'; + } + + // phpcs:ignore SlevomatCodingStandard.Functions.UnusedParameter + public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): Type + { + $args = $functionCall->getArgs(); + + // Called without $args parameter + if (count($args) < 3) { + return new NeverType(); + } + + $argType = $scope->getType($args[2]->value); + + // Return void for non constant arrays. + if (! $argType->isConstantArray()->yes()) { + return new VoidType(); + } + + // Return never if the key 'exit' is not set. + if (! $argType->hasOffsetValueType(new ConstantStringType('exit'))->yes()) { + return new NeverType(); + } + + // Note WP's wp_die handlers do lazy comparison + return $argType->getOffsetValueType(new ConstantStringType('exit'))->toBoolean()->isTrue()->yes() + ? new NeverType() + : new VoidType(); + } +} diff --git a/tests/DynamicReturnTypeExtensionTest.php b/tests/DynamicReturnTypeExtensionTest.php index 92ea1a1..69471ae 100644 --- a/tests/DynamicReturnTypeExtensionTest.php +++ b/tests/DynamicReturnTypeExtensionTest.php @@ -31,6 +31,7 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/term_exists.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/wp_error_parameter.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/wp_parse_url.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/wp_die.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/wp_theme_get.php'); } diff --git a/tests/data/wp_die.php b/tests/data/wp_die.php new file mode 100644 index 0000000..0f2744f --- /dev/null +++ b/tests/data/wp_die.php @@ -0,0 +1,15 @@ + true])); +assertType('void', wp_die('', '', ['exit' => false])); +assertType('void', wp_die('', '', $array));