Skip to content

Commit

Permalink
Add no global const rule, entity in its namespace and no single imple…
Browse files Browse the repository at this point in the history
…menter (#132)

* Add no GlobalConstRule, no entity outside entity namespace, no single interface implementer

* update docs

* misc
  • Loading branch information
TomasVotruba authored Jun 2, 2024
1 parent 019cc50 commit 9f47728
Show file tree
Hide file tree
Showing 20 changed files with 664 additions and 40 deletions.
12 changes: 12 additions & 0 deletions config/code-complexity-rules.neon
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
rules:
- Symplify\PHPStanRules\Rules\NoDynamicNameRule
- Symplify\PHPStanRules\Rules\NoReturnArrayVariableListRule
- Symplify\PHPStanRules\Rules\NoSingleInterfaceImplementerRule

services:
-
class: Symplify\PHPStanRules\Collector\InterfaceCollector
tags:
- phpstan.collector

-
class: Symplify\PHPStanRules\Collector\ImplementedInterfaceCollector
tags:
- phpstan.collector
1 change: 1 addition & 0 deletions config/static-rules.neon
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
rules:
- Symplify\PHPStanRules\Rules\ForbiddenExtendOfNonAbstractClassRule
- Symplify\PHPStanRules\Rules\NoGlobalConstRule

# domain
- Symplify\PHPStanRules\Rules\Domain\RequireExceptionNamespaceRule
Expand Down
138 changes: 137 additions & 1 deletion docs/rules_overview.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 28 Rules Overview
# 32 Rules Overview

## AnnotateRegexClassConstWithRegexLinkRule

Expand Down Expand Up @@ -381,6 +381,40 @@ return strlen('...');

<br>

## ForbiddenStaticClassConstFetchRule

Avoid static access of constants, as they can change value. Use interface and contract method instead

- class: [`Symplify\PHPStanRules\Rules\ForbiddenStaticClassConstFetchRule`](../src/Rules/ForbiddenStaticClassConstFetchRule.php)

```php
class SomeClass
{
public function run()
{
return static::SOME_CONST;
}
}
```

:x:

<br>

```php
class SomeClass
{
public function run()
{
return self::SOME_CONST;
}
}
```

:+1:

<br>

## NoDuplicatedShortClassNameRule

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

<br>

## NoEntityOutsideEntityNamespaceRule

Class with #[Entity] attribute must be located in "Entity" namespace to be loaded by Doctrine

- class: [`Symplify\PHPStanRules\Rules\NoEntityOutsideEntityNamespaceRule`](../src/Rules/NoEntityOutsideEntityNamespaceRule.php)

```php
namespace App\ValueObject;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class Product
{
}
```

:x:

<br>

```php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class Product
{
}
```

:+1:

<br>

## NoGlobalConstRule

Global constants are forbidden. Use enum-like class list instead

- class: [`Symplify\PHPStanRules\Rules\NoGlobalConstRule`](../src/Rules/NoGlobalConstRule.php)

```php
const SOME_GLOBAL_CONST = 'value';
```

:x:

<br>

```php
class SomeClass
{
public function run()
{
return self::SOME_CONST;
}
}
```

:+1:

<br>

## NoInlineStringRegexRule

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

<br>

## NoSingleInterfaceImplementerRule

Interface "%s" has only single implementer. Consider using the class directly as there is no point in using the interface.

- class: [`Symplify\PHPStanRules\Rules\NoSingleInterfaceImplementerRule`](../src/Rules/NoSingleInterfaceImplementerRule.php)

```php
class SomeClass implements SomeInterface
{
}

interface SomeInterface
{
}
```

:x:

<br>

```php
class SomeClass implements SomeInterface
{
}

class AnotherClass implements SomeInterface
{
}

interface SomeInterface
{
}
```

:+1:

<br>

## NoTestMocksRule

Mocking "%s" class is forbidden. Use direct/anonymous class instead for better static analysis
Expand Down
14 changes: 4 additions & 10 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ parameters:
- config
- tests

# reportUnmatchedIgnoredErrors: false

excludePaths:
# parallel
- packages/*-phpstan-printer/tests/*ToPhpCompiler/Fixture*
Expand All @@ -23,20 +21,13 @@ parameters:

ignoreErrors:
-
message: '#Generator expects value type array<int, array<int, int\|string>\|string>, array<int, array<int, array<int, int\|string>>\|string> given#'
paths:
- tests/Rules

-
message: '#Generator expects value type array<array<int\|string>\|string>, array<int, array<int, array<int, int\|string>>\|string> given#'
message: '#Generator expects value type (.*?) given#'
paths:
- tests/Rules

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

- '#Parameter \#1 \$values of method Symplify\\PHPStanRules\\Rules\\Enum\\RequireUniqueEnumConstantRule\:\:filterDuplicatedValues\(\) expects array<int\|string>, array<bool\|float\|int\|string> given#'

- '#Class PHP_CodeSniffer\\Sniffs\\Sniff not found#'

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

# part of public contract
- '#Method Symplify\\PHPStanRules\\Tests\\Rules\\PHPUnit\\(.*?)\\(.*?)Test\:\:testRule\(\) has parameter \$expectedErrorMessagesWithLines with no value type specified in iterable type array#'

# overly detailed
- '#Class Symplify\\PHPStanRules\\Collector\\(.*?) implements generic interface PHPStan\\Collectors\\Collector but does not specify its types\: TNodeType, TValue#'
33 changes: 33 additions & 0 deletions src/Collector/ImplementedInterfaceCollector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Symplify\PHPStanRules\Collector;

use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Analyser\Scope;
use PHPStan\Collectors\Collector;

final class ImplementedInterfaceCollector implements Collector
{
public function getNodeType(): string
{
return Class_::class;
}

/**
* @param Class_ $node
* @return string[]
*/
public function processNode(Node $node, Scope $scope): array
{
$implementedInterfaceNames = [];

foreach ($node->implements as $implement) {
$implementedInterfaceNames[] = $implement->toString();
}

return $implementedInterfaceNames;
}
}
31 changes: 31 additions & 0 deletions src/Collector/InterfaceCollector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace Symplify\PHPStanRules\Collector;

use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Interface_;
use PHPStan\Analyser\Scope;
use PHPStan\Collectors\Collector;

final class InterfaceCollector implements Collector
{
public function getNodeType(): string
{
return Interface_::class;
}

/**
* @param Interface_ $node
*/
public function processNode(Node $node, Scope $scope): ?string
{
if (! $node->namespacedName instanceof Name) {
return null;
}

return $node->namespacedName->toString();
}
}
25 changes: 0 additions & 25 deletions src/Reflection/ReflectionParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use Nette\Utils\FileSystem;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Property;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\NameResolver;
use PhpParser\Parser;
Expand All @@ -16,13 +15,9 @@
use PHPStan\Reflection\MethodReflection;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
use Symplify\PHPStanRules\NodeFinder\TypeAwareNodeFinder;
use Throwable;

/**
* @api
*/
final class ReflectionParser
{
/**
Expand All @@ -49,26 +44,6 @@ public function parseMethodReflection(ReflectionMethod|MethodReflection $reflect
return $classLike->getMethod($reflectionMethod->getName());
}

public function parsePropertyReflection(ReflectionProperty $reflectionProperty): ?Property
{
$classLike = $this->parseNativeClassReflection($reflectionProperty->getDeclaringClass());
if (! $classLike instanceof ClassLike) {
return null;
}

return $classLike->getProperty($reflectionProperty->getName());
}

public function parseClassReflection(ClassReflection $classReflection): ?ClassLike
{
$fileName = $classReflection->getFileName();
if ($fileName === null) {
return null;
}

return $this->parseFilenameToClass($fileName);
}

private function parseNativeClassReflection(ReflectionClass|ClassReflection $reflectionClass): ?ClassLike
{
$fileName = $reflectionClass->getFileName();
Expand Down
4 changes: 2 additions & 2 deletions src/Rules/Enum/RequireUniqueEnumConstantRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ class SomeClass extends Enum
}

/**
* @param array<string|int> $values
* @return array<string|int>
* @param array<int|float|bool|string> $values
* @return array<int|float|bool|string>
*/
private function filterDuplicatedValues(array $values): array
{
Expand Down
3 changes: 1 addition & 2 deletions src/Rules/ForbiddenStaticClassConstFetchRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@
use PhpParser\Node\Name;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use Symplify\RuleDocGenerator\Contract\ConfigurableRuleInterface;
use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

/**
* @see \Symplify\PHPStanRules\Tests\Rules\ForbiddenStaticClassConstFetchRule\ForbiddenStaticClassConstFetchRuleTest
*/
final class ForbiddenStaticClassConstFetchRule implements Rule, DocumentedRuleInterface, ConfigurableRuleInterface
final class ForbiddenStaticClassConstFetchRule implements Rule, DocumentedRuleInterface
{
/**
* @var string
Expand Down
Loading

0 comments on commit 9f47728

Please sign in to comment.