PHP-CS-Fixer: Rule no_unneeded_curly_braces does not understand annotation scoping
Bug report
PHP-CS-Fixer’s no_unneeded_curly_braces
removes curly braces that are actually needed. Specifically, those that are defining the scope of annotations.
- PHP version: 7.4.21
- PHP CS Fixer version: 2.18.4
- the command used to run PHP CS Fixer (run with
-vvv
):php-cs-fixer my-file.php --config=.php_cs.dist --allow-risky=yes
- the configuration (file) you used:
<?php
$finder = PhpCsFixer\Finder::create()->in(__DIR__);
return PhpCsFixer\Config::create()
->setRules([
'@Symfony' => true,
'binary_operator_spaces' => [
'operators' => [
'=>' => 'align_single_space_minimal',
],
],
'echo_tag_syntax' => false,
'ordered_class_elements' => true,
'ordered_imports' => true,
'yoda_style' => false,
'blank_line_before_statement' => true,
'class_attributes_separation' => [
'elements' => [
'const',
'method',
'property'
],
],
'no_alternative_syntax' => false,
'phpdoc_no_empty_return' => true,
'concat_space' => [
'spacing' => 'one'
],
'phpdoc_no_alias_tag' => [
'type' => 'var',
'link' => 'see',
],
'single_line_throw' => false
])
->setFinder($finder)->setUsingCache(false);
Code snippet that reproduces the problem
/* @noinspection PhpFieldAssignmentTypeMismatchInspection */
{
$this->qa = $env->getContext(QualityAssurance::class);
$this->store = $env->getContext(StoreContext::class);
$this->web = $env->getContext(WebContext::class);
}
Expected output:
/* @noinspection PhpFieldAssignmentTypeMismatchInspection */
{
$this->qa = $env->getContext(QualityAssurance::class);
$this->store = $env->getContext(StoreContext::class);
$this->web = $env->getContext(WebContext::class);
}
Actual output:
/* @noinspection PhpFieldAssignmentTypeMismatchInspection */
$this->qa = $env->getContext(QualityAssurance::class);
$this->store = $env->getContext(StoreContext::class);
$this->web = $env->getContext(WebContext::class);
Repurcussions of the problem
This results in the @noinspection
annotation only being applied to the first line:
Before:
After:
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 18 (9 by maintainers)
I never knew this was a thing, so I didn’t consider it when making the rule. If the PHPDoc (annotation) followed by a curly block for scoping is something people really use I see no reason not to support it. It will likely harder to stop the other rule from turning the PHPDocs into comments, than stopping this rule from removing the braces. You would need both as I would not support scoped comments, only PHPDocs, but that is just my preference.
What about situations such as how Eloquent delegates to other classes?
Even using a simple
where()
call on an EloquentModel
results in PHPStorm complaining that the method does not exist, because it is not actually defined on the Model class, but delegated to theBuilder
class via the magic__call
method:So, I could use
Lab::query()->where()
here, but that only kicks the can further down the road, because there are plenty of other scenarios where you can get tangled on Eloquent’s web and not have a way to make the type system happy without significantly complicating your query by separating it into variables with@var
annotations.So instead, to keep the code simple and clean in the spirit of Laravel, we can use the same scoping here:
In the interests of completion, let’s continue the experiment and try to use
query()
:PHPStorm is happy now because I am coincidentally only using methods that exist on the
Builder
returned byquery()
. But if theLab
uses theSoftDeletes
trait, I also have access to thewithTrashed
method. So let’s try to use that:Well crap, now PHPStorm is unhappy again. And this is particularly challenging because
Builder
does not have awithTrashed
method, but it happily proxies the call to theLab
class which does have the method:So now we have two further problems:
$labQuery
because it is aBuilder
, not aLab
withTrashed
is declared as staticI could use
/** @var Builder|Lab $labQuery */
, but that’s still wrong becauseBuilder
will only proxy called to query scopes, not all methods on theLab
class.TL:DR: yes, you are right that some of the things I have to do are not ideal. But I am working within the constraints of the tools and frameworks that I have no option but to work with. So I am trying to do the best I can with it. I need
@noinspection
to allow me to remove some of the “noise” from the warnings so that developers can have faith that the warnings they see are legitimate. If I do not fix these, then they will lose faith in the warnings, and legitimate problems will get overlooked.