Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .phpstan/ForbidEmptyRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\AI\PHPStan;

use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;

/**
* PHPStan rule that forbids usage of empty() function.
*
* This rule enforces that empty() should not be used in favor of explicit checks
* like null checks, count() for arrays, or string length checks.
*
* @author Oskar Stark <oskarstark@googlemail.com>
*
* @implements Rule<FuncCall>
*/
final class ForbidEmptyRule implements Rule
{
public function getNodeType(): string
{
return FuncCall::class;
}

public function processNode(Node $node, Scope $scope): array
{
if (!$node instanceof FuncCall) {
return [];
}

if (!$node->name instanceof Node\Name) {
return [];
}

$functionName = $node->name->toString();

if ('empty' !== strtolower($functionName)) {
return [];
}

// Allow empty() in ai-bundle config file where validation logic can be complex
if (str_ends_with($scope->getFile(), 'ai-bundle/config/options.php')) {
return [];
}

return [
RuleErrorBuilder::message(
'Usage of empty() function is forbidden. Use explicit checks instead: null check, count() for arrays, or string length checks.'
)
->line($node->getLine())
->identifier('symfonyAi.forbidEmpty')
->tip('Replace empty() with explicit checks like $var !== null && $var !== \'\' for strings, count($var) > 0 for arrays, etc.')
->build(),
];
}
}
1 change: 1 addition & 0 deletions .phpstan/extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ rules:
- Symfony\AI\PHPStan\ForbidDeclareStrictTypesRule
- Symfony\AI\PHPStan\ForbidNativeExceptionRule
- Symfony\AI\PHPStan\ForbidTestCoverageAttributesRule
- Symfony\AI\PHPStan\ForbidEmptyRule
2 changes: 1 addition & 1 deletion src/agent/src/MockAgent.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ public function getCall(int $index): array
*/
public function getLastCall(): array
{
if (empty($this->calls)) {
if (0 === \count($this->calls)) {
throw new LogicException('No calls have been made yet.');
}

Expand Down
2 changes: 1 addition & 1 deletion src/agent/src/StructuredOutput/AgentProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public function processOutput(Output $output): void
$output->result->getMetadata()->set($originalResult->getMetadata()->all());
}

if (!empty($originalResult->getRawResult())) {
if (null !== $originalResult->getRawResult()) {
$output->result->setRawResult($originalResult->getRawResult());
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/platform/src/Bridge/Anthropic/ModelClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public function request(Model $model, array|string $payload, array $options = []
$options['tool_choice'] = ['type' => 'auto'];
}

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