Skip to content

Commit d210333

Browse files
authored
[symfony] Add NoBareAndSecurityIsGrantedContentsRule (#214)
1 parent 7cca318 commit d210333

File tree

8 files changed

+166
-0
lines changed

8 files changed

+166
-0
lines changed

config/symfony-rules.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ rules:
2626

2727
# attributes
2828
- Symplify\PHPStanRules\Rules\Symfony\RequireIsGrantedEnumRule
29+
- Symplify\PHPStanRules\Rules\Symfony\NoBareAndSecurityIsGrantedContentsRule

src/Enum/SensioClass.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77
final class SensioClass
88
{
99
public const IS_GRANTED = 'Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted';
10+
11+
public const SECURITY = 'Sensio\Bundle\FrameworkExtraBundle\Configuration\Security';
1012
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\Rules\Symfony;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Attribute;
9+
use PhpParser\Node\Scalar\String_;
10+
use PHPStan\Analyser\Scope;
11+
use PHPStan\Rules\Rule;
12+
use PHPStan\Rules\RuleErrorBuilder;
13+
use Symplify\PHPStanRules\Enum\RuleIdentifier\SymfonyRuleIdentifier;
14+
use Symplify\PHPStanRules\Enum\SensioClass;
15+
use Symplify\PHPStanRules\Enum\SymfonyClass;
16+
17+
/**
18+
* @see \Symplify\PHPStanRules\Tests\Rules\Symfony\NoBareAndSecurityIsGrantedContentsRule\NoBareAndSecurityIsGrantedContentsRuleTest
19+
*
20+
* @implements Rule<Attribute>
21+
*/
22+
final class NoBareAndSecurityIsGrantedContentsRule implements Rule
23+
{
24+
public const ERROR_MESSAGE = 'Instead of using one long "and" condition join, split into multiple standalone #[IsGranted] attributes';
25+
26+
public function getNodeType(): string
27+
{
28+
return Attribute::class;
29+
}
30+
31+
/**
32+
* @param Attribute $node
33+
*/
34+
public function processNode(Node $node, Scope $scope): array
35+
{
36+
if (! in_array($node->name->toString(), [SensioClass::SECURITY, SensioClass::IS_GRANTED, SymfonyClass::IS_GRANTED], true)) {
37+
return [];
38+
}
39+
40+
$attributeExpr = $node->args[0]->value;
41+
if (! $attributeExpr instanceof String_) {
42+
return [];
43+
}
44+
45+
// nothing to split
46+
if (str_contains($attributeExpr->value, ' or ')) {
47+
return [];
48+
}
49+
50+
if (! str_contains($attributeExpr->value, ' and ') && ! str_contains($attributeExpr->value, ' && ')) {
51+
return [];
52+
}
53+
54+
$identifierRuleError = RuleErrorBuilder::message(self::ERROR_MESSAGE)
55+
->identifier(SymfonyRuleIdentifier::REQUIRED_IS_GRANTED_ENUM)
56+
->build();
57+
58+
return [$identifierRuleError];
59+
}
60+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\Tests\Rules\Symfony\NoBareAndSecurityIsGrantedContentsRule\Fixture;
6+
7+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
8+
9+
#[IsGranted('(has_role("some_resource") and is_granted("another_resource")) or is_granted("yet_another_resource")')]
10+
final class SkipInnerOr
11+
{
12+
public function run()
13+
{
14+
}
15+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\Tests\Rules\Symfony\NoBareAndSecurityIsGrantedContentsRule\Fixture;
6+
7+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
8+
9+
#[IsGranted('some_resource')]
10+
#[IsGranted('another_resource')]
11+
final class SkipSplitOne
12+
{
13+
public function run()
14+
{
15+
}
16+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\Tests\Rules\Symfony\NoBareAndSecurityIsGrantedContentsRule\Fixture;
6+
7+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
8+
9+
#[IsGranted('has_role("some_resource") && is_granted("another_resource")')]
10+
final class SomeControllerWithAmpersand
11+
{
12+
public function run()
13+
{
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\Tests\Rules\Symfony\NoBareAndSecurityIsGrantedContentsRule\Fixture;
6+
7+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
8+
9+
#[IsGranted('has_role("some_resource") and is_granted("another_resource")')]
10+
final class SomeControllerWithComplexAttribute
11+
{
12+
public function run()
13+
{
14+
}
15+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\Tests\Rules\Symfony\NoBareAndSecurityIsGrantedContentsRule;
6+
7+
use Iterator;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Testing\RuleTestCase;
10+
use PHPUnit\Framework\Attributes\DataProvider;
11+
use Symplify\PHPStanRules\Rules\Symfony\NoBareAndSecurityIsGrantedContentsRule;
12+
13+
final class NoBareAndSecurityIsGrantedContentsRuleTest extends RuleTestCase
14+
{
15+
/**
16+
* @param mixed[] $expectedErrorMessagesWithLines
17+
*/
18+
#[DataProvider('provideData')]
19+
public function testRule(string $filePath, array $expectedErrorMessagesWithLines): void
20+
{
21+
$this->analyse([$filePath], $expectedErrorMessagesWithLines);
22+
}
23+
24+
public static function provideData(): Iterator
25+
{
26+
yield [__DIR__ . '/Fixture/SomeControllerWithComplexAttribute.php', [
27+
[NoBareAndSecurityIsGrantedContentsRule::ERROR_MESSAGE, 9],
28+
]];
29+
30+
yield [__DIR__ . '/Fixture/SomeControllerWithAmpersand.php', [
31+
[NoBareAndSecurityIsGrantedContentsRule::ERROR_MESSAGE, 9],
32+
]];
33+
34+
yield [__DIR__ . '/Fixture/SkipInnerOr.php', []];
35+
yield [__DIR__ . '/Fixture/SkipSplitOne.php', []];
36+
}
37+
38+
protected function getRule(): Rule
39+
{
40+
return new NoBareAndSecurityIsGrantedContentsRule();
41+
}
42+
}

0 commit comments

Comments
 (0)