Skip to content

Commit 9f47728

Browse files
authored
Add no global const rule, entity in its namespace and no single implementer (#132)
* Add no GlobalConstRule, no entity outside entity namespace, no single interface implementer * update docs * misc
1 parent 019cc50 commit 9f47728

20 files changed

+664
-40
lines changed

config/code-complexity-rules.neon

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
11
rules:
22
- Symplify\PHPStanRules\Rules\NoDynamicNameRule
33
- Symplify\PHPStanRules\Rules\NoReturnArrayVariableListRule
4+
- Symplify\PHPStanRules\Rules\NoSingleInterfaceImplementerRule
5+
6+
services:
7+
-
8+
class: Symplify\PHPStanRules\Collector\InterfaceCollector
9+
tags:
10+
- phpstan.collector
11+
12+
-
13+
class: Symplify\PHPStanRules\Collector\ImplementedInterfaceCollector
14+
tags:
15+
- phpstan.collector

config/static-rules.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
rules:
22
- Symplify\PHPStanRules\Rules\ForbiddenExtendOfNonAbstractClassRule
3+
- Symplify\PHPStanRules\Rules\NoGlobalConstRule
34

45
# domain
56
- Symplify\PHPStanRules\Rules\Domain\RequireExceptionNamespaceRule

docs/rules_overview.md

Lines changed: 137 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# 28 Rules Overview
1+
# 32 Rules Overview
22

33
## AnnotateRegexClassConstWithRegexLinkRule
44

@@ -381,6 +381,40 @@ return strlen('...');
381381

382382
<br>
383383

384+
## ForbiddenStaticClassConstFetchRule
385+
386+
Avoid static access of constants, as they can change value. Use interface and contract method instead
387+
388+
- class: [`Symplify\PHPStanRules\Rules\ForbiddenStaticClassConstFetchRule`](../src/Rules/ForbiddenStaticClassConstFetchRule.php)
389+
390+
```php
391+
class SomeClass
392+
{
393+
public function run()
394+
{
395+
return static::SOME_CONST;
396+
}
397+
}
398+
```
399+
400+
:x:
401+
402+
<br>
403+
404+
```php
405+
class SomeClass
406+
{
407+
public function run()
408+
{
409+
return self::SOME_CONST;
410+
}
411+
}
412+
```
413+
414+
:+1:
415+
416+
<br>
417+
384418
## NoDuplicatedShortClassNameRule
385419

386420
Class with base "%s" name is already used in "%s". Use unique name to make classes easy to recognize
@@ -470,6 +504,70 @@ class SomeClass
470504

471505
<br>
472506

507+
## NoEntityOutsideEntityNamespaceRule
508+
509+
Class with #[Entity] attribute must be located in "Entity" namespace to be loaded by Doctrine
510+
511+
- class: [`Symplify\PHPStanRules\Rules\NoEntityOutsideEntityNamespaceRule`](../src/Rules/NoEntityOutsideEntityNamespaceRule.php)
512+
513+
```php
514+
namespace App\ValueObject;
515+
516+
use Doctrine\ORM\Mapping as ORM;
517+
518+
#[ORM\Entity]
519+
class Product
520+
{
521+
}
522+
```
523+
524+
:x:
525+
526+
<br>
527+
528+
```php
529+
namespace App\Entity;
530+
531+
use Doctrine\ORM\Mapping as ORM;
532+
533+
#[ORM\Entity]
534+
class Product
535+
{
536+
}
537+
```
538+
539+
:+1:
540+
541+
<br>
542+
543+
## NoGlobalConstRule
544+
545+
Global constants are forbidden. Use enum-like class list instead
546+
547+
- class: [`Symplify\PHPStanRules\Rules\NoGlobalConstRule`](../src/Rules/NoGlobalConstRule.php)
548+
549+
```php
550+
const SOME_GLOBAL_CONST = 'value';
551+
```
552+
553+
:x:
554+
555+
<br>
556+
557+
```php
558+
class SomeClass
559+
{
560+
public function run()
561+
{
562+
return self::SOME_CONST;
563+
}
564+
}
565+
```
566+
567+
:+1:
568+
569+
<br>
570+
473571
## NoInlineStringRegexRule
474572

475573
Use local named constant instead of inline string for regex to explain meaning by constant name
@@ -642,6 +740,44 @@ final class SomeClass
642740

643741
<br>
644742

743+
## NoSingleInterfaceImplementerRule
744+
745+
Interface "%s" has only single implementer. Consider using the class directly as there is no point in using the interface.
746+
747+
- class: [`Symplify\PHPStanRules\Rules\NoSingleInterfaceImplementerRule`](../src/Rules/NoSingleInterfaceImplementerRule.php)
748+
749+
```php
750+
class SomeClass implements SomeInterface
751+
{
752+
}
753+
754+
interface SomeInterface
755+
{
756+
}
757+
```
758+
759+
:x:
760+
761+
<br>
762+
763+
```php
764+
class SomeClass implements SomeInterface
765+
{
766+
}
767+
768+
class AnotherClass implements SomeInterface
769+
{
770+
}
771+
772+
interface SomeInterface
773+
{
774+
}
775+
```
776+
777+
:+1:
778+
779+
<br>
780+
645781
## NoTestMocksRule
646782

647783
Mocking "%s" class is forbidden. Use direct/anonymous class instead for better static analysis

phpstan.neon

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ parameters:
1010
- config
1111
- tests
1212

13-
# reportUnmatchedIgnoredErrors: false
14-
1513
excludePaths:
1614
# parallel
1715
- packages/*-phpstan-printer/tests/*ToPhpCompiler/Fixture*
@@ -23,20 +21,13 @@ parameters:
2321

2422
ignoreErrors:
2523
-
26-
message: '#Generator expects value type array<int, array<int, int\|string>\|string>, array<int, array<int, array<int, int\|string>>\|string> given#'
27-
paths:
28-
- tests/Rules
29-
30-
-
31-
message: '#Generator expects value type array<array<int\|string>\|string>, array<int, array<int, array<int, int\|string>>\|string> given#'
24+
message: '#Generator expects value type (.*?) given#'
3225
paths:
3326
- tests/Rules
3427

3528
# needless generics
3629
- '#Class Symplify\\PHPStanRules\\(.*?)Rule implements generic interface PHPStan\\Rules\\Rule but does not specify its types\: TNodeType#'
3730

38-
- '#Parameter \#1 \$values of method Symplify\\PHPStanRules\\Rules\\Enum\\RequireUniqueEnumConstantRule\:\:filterDuplicatedValues\(\) expects array<int\|string>, array<bool\|float\|int\|string> given#'
39-
4031
- '#Class PHP_CodeSniffer\\Sniffs\\Sniff not found#'
4132

4233
- '#Method Symplify\\PHPStanRules\\Reflection\\ReflectionParser\:\:parseNativeClassReflection\(\) has parameter \$reflectionClass with generic class ReflectionClass but does not specify its types\: T#'
@@ -49,3 +40,6 @@ parameters:
4940

5041
# part of public contract
5142
- '#Method Symplify\\PHPStanRules\\Tests\\Rules\\PHPUnit\\(.*?)\\(.*?)Test\:\:testRule\(\) has parameter \$expectedErrorMessagesWithLines with no value type specified in iterable type array#'
43+
44+
# overly detailed
45+
- '#Class Symplify\\PHPStanRules\\Collector\\(.*?) implements generic interface PHPStan\\Collectors\\Collector but does not specify its types\: TNodeType, TValue#'
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\Collector;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Stmt\Class_;
9+
use PHPStan\Analyser\Scope;
10+
use PHPStan\Collectors\Collector;
11+
12+
final class ImplementedInterfaceCollector implements Collector
13+
{
14+
public function getNodeType(): string
15+
{
16+
return Class_::class;
17+
}
18+
19+
/**
20+
* @param Class_ $node
21+
* @return string[]
22+
*/
23+
public function processNode(Node $node, Scope $scope): array
24+
{
25+
$implementedInterfaceNames = [];
26+
27+
foreach ($node->implements as $implement) {
28+
$implementedInterfaceNames[] = $implement->toString();
29+
}
30+
31+
return $implementedInterfaceNames;
32+
}
33+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\Collector;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Name;
9+
use PhpParser\Node\Stmt\Interface_;
10+
use PHPStan\Analyser\Scope;
11+
use PHPStan\Collectors\Collector;
12+
13+
final class InterfaceCollector implements Collector
14+
{
15+
public function getNodeType(): string
16+
{
17+
return Interface_::class;
18+
}
19+
20+
/**
21+
* @param Interface_ $node
22+
*/
23+
public function processNode(Node $node, Scope $scope): ?string
24+
{
25+
if (! $node->namespacedName instanceof Name) {
26+
return null;
27+
}
28+
29+
return $node->namespacedName->toString();
30+
}
31+
}

src/Reflection/ReflectionParser.php

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use Nette\Utils\FileSystem;
88
use PhpParser\Node\Stmt\ClassLike;
99
use PhpParser\Node\Stmt\ClassMethod;
10-
use PhpParser\Node\Stmt\Property;
1110
use PhpParser\NodeTraverser;
1211
use PhpParser\NodeVisitor\NameResolver;
1312
use PhpParser\Parser;
@@ -16,13 +15,9 @@
1615
use PHPStan\Reflection\MethodReflection;
1716
use ReflectionClass;
1817
use ReflectionMethod;
19-
use ReflectionProperty;
2018
use Symplify\PHPStanRules\NodeFinder\TypeAwareNodeFinder;
2119
use Throwable;
2220

23-
/**
24-
* @api
25-
*/
2621
final class ReflectionParser
2722
{
2823
/**
@@ -49,26 +44,6 @@ public function parseMethodReflection(ReflectionMethod|MethodReflection $reflect
4944
return $classLike->getMethod($reflectionMethod->getName());
5045
}
5146

52-
public function parsePropertyReflection(ReflectionProperty $reflectionProperty): ?Property
53-
{
54-
$classLike = $this->parseNativeClassReflection($reflectionProperty->getDeclaringClass());
55-
if (! $classLike instanceof ClassLike) {
56-
return null;
57-
}
58-
59-
return $classLike->getProperty($reflectionProperty->getName());
60-
}
61-
62-
public function parseClassReflection(ClassReflection $classReflection): ?ClassLike
63-
{
64-
$fileName = $classReflection->getFileName();
65-
if ($fileName === null) {
66-
return null;
67-
}
68-
69-
return $this->parseFilenameToClass($fileName);
70-
}
71-
7247
private function parseNativeClassReflection(ReflectionClass|ClassReflection $reflectionClass): ?ClassLike
7348
{
7449
$fileName = $reflectionClass->getFileName();

src/Rules/Enum/RequireUniqueEnumConstantRule.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ class SomeClass extends Enum
9393
}
9494

9595
/**
96-
* @param array<string|int> $values
97-
* @return array<string|int>
96+
* @param array<int|float|bool|string> $values
97+
* @return array<int|float|bool|string>
9898
*/
9999
private function filterDuplicatedValues(array $values): array
100100
{

src/Rules/ForbiddenStaticClassConstFetchRule.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@
99
use PhpParser\Node\Name;
1010
use PHPStan\Analyser\Scope;
1111
use PHPStan\Rules\Rule;
12-
use Symplify\RuleDocGenerator\Contract\ConfigurableRuleInterface;
1312
use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface;
1413
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
1514
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
1615

1716
/**
1817
* @see \Symplify\PHPStanRules\Tests\Rules\ForbiddenStaticClassConstFetchRule\ForbiddenStaticClassConstFetchRuleTest
1918
*/
20-
final class ForbiddenStaticClassConstFetchRule implements Rule, DocumentedRuleInterface, ConfigurableRuleInterface
19+
final class ForbiddenStaticClassConstFetchRule implements Rule, DocumentedRuleInterface
2120
{
2221
/**
2322
* @var string

0 commit comments

Comments
 (0)