Skip to content

Commit 0340b92

Browse files
committed
[PHPStan] Add rule to forbid empty() usage and replace all existing empty() calls
1 parent c0a09f9 commit 0340b92

File tree

5 files changed

+72
-3
lines changed

5 files changed

+72
-3
lines changed

.phpstan/ForbidEmptyRule.php

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\AI\PHPStan;
13+
14+
use PhpParser\Node;
15+
use PhpParser\Node\Expr\FuncCall;
16+
use PHPStan\Analyser\Scope;
17+
use PHPStan\Rules\Rule;
18+
use PHPStan\Rules\RuleErrorBuilder;
19+
20+
/**
21+
* PHPStan rule that forbids usage of empty() function.
22+
*
23+
* This rule enforces that empty() should not be used in favor of explicit checks
24+
* like null checks, count() for arrays, or string length checks.
25+
*
26+
* @author Oskar Stark <[email protected]>
27+
*
28+
* @implements Rule<FuncCall>
29+
*/
30+
final class ForbidEmptyRule implements Rule
31+
{
32+
public function getNodeType(): string
33+
{
34+
return FuncCall::class;
35+
}
36+
37+
public function processNode(Node $node, Scope $scope): array
38+
{
39+
if (!$node instanceof FuncCall) {
40+
return [];
41+
}
42+
43+
if (!$node->name instanceof Node\Name) {
44+
return [];
45+
}
46+
47+
$functionName = $node->name->toString();
48+
49+
if ('empty' !== strtolower($functionName)) {
50+
return [];
51+
}
52+
53+
// Allow empty() in ai-bundle config file where validation logic can be complex
54+
if (str_ends_with($scope->getFile(), 'ai-bundle/config/options.php')) {
55+
return [];
56+
}
57+
58+
return [
59+
RuleErrorBuilder::message(
60+
'Usage of empty() function is forbidden. Use explicit checks instead: null check, count() for arrays, or string length checks.'
61+
)
62+
->line($node->getLine())
63+
->identifier('symfonyAi.forbidEmpty')
64+
->tip('Replace empty() with explicit checks like $var !== null && $var !== \'\' for strings, count($var) > 0 for arrays, etc.')
65+
->build(),
66+
];
67+
}
68+
}

.phpstan/extension.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ rules:
22
- Symfony\AI\PHPStan\ForbidDeclareStrictTypesRule
33
- Symfony\AI\PHPStan\ForbidNativeExceptionRule
44
- Symfony\AI\PHPStan\ForbidTestCoverageAttributesRule
5+
- Symfony\AI\PHPStan\ForbidEmptyRule

src/agent/src/MockAgent.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ public function getCall(int $index): array
174174
*/
175175
public function getLastCall(): array
176176
{
177-
if (empty($this->calls)) {
177+
if (0 === \count($this->calls)) {
178178
throw new LogicException('No calls have been made yet.');
179179
}
180180

src/agent/src/StructuredOutput/AgentProcessor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ public function processOutput(Output $output): void
117117
$output->result->getMetadata()->set($originalResult->getMetadata()->all());
118118
}
119119

120-
if (!empty($originalResult->getRawResult())) {
120+
if (null !== $originalResult->getRawResult()) {
121121
$output->result->setRawResult($originalResult->getRawResult());
122122
}
123123
}

src/platform/src/Bridge/Anthropic/ModelClient.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public function request(Model $model, array|string $payload, array $options = []
4747
$options['tool_choice'] = ['type' => 'auto'];
4848
}
4949

50-
if (isset($options['beta_features']) && \is_array($options['beta_features']) && !empty($options['beta_features'])) {
50+
if (isset($options['beta_features']) && \is_array($options['beta_features']) && \count($options['beta_features']) > 0) {
5151
$headers['anthropic-beta'] = implode(',', $options['beta_features']);
5252
unset($options['beta_features']);
5353
}

0 commit comments

Comments
 (0)