Slide 1

Slide 1 text

Cra ft ing Custom PHPStan Rules for Symfony Apps Ondřej Mirtes SymfonyCon, December 6th 2024

Slide 2

Slide 2 text

$ composer require --dev phpstan/phpstan What is PHPStan? $ vendor/bin/phpstan analyse src/ tests/

Slide 3

Slide 3 text

What is PHPStan?

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

phpstan.org/merch order until December 8th

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

Motivation for custom rules Make code reviews easier Prevent bugs and production incidents Achieve consistency & quality

Slide 8

Slide 8 text

Custom rule new FooService() Do not instantiate DI service directly, inject it via constructor

Slide 9

Slide 9 text

Abstract syntax tree (AST) $this->foo && self::bar() BooleanAnd PropertyFetch StaticCall Variable ("this") "foo" "self" "bar" le f right var name class name

Slide 10

Slide 10 text

Abstract syntax tree (AST) github.com/nikic/PHP-Parser

Slide 11

Slide 11 text

apiref.phpstan.org

Slide 12

Slide 12 text

Writing custom rules interface Rule { /** * @return string Class implementing \PhpParser\Node */ public function getNodeType(): string; /** * @param \PhpParser\Node $node * @param \PHPStan\Analyser\Scope $scope * @return RuleError[] errors */ public function processNode(Node $node, Scope $scope): array; }

Slide 13

Slide 13 text

Demo time! Let's write our new FooService() rule

Slide 14

Slide 14 text

Scope getFile(): string getNamespace(): ?string isInClass(): bool getClassReflection(): ?ClassReflection isInTrait(): bool getTraitReflection(): ?ClassReflection getFunction(): ?FunctionReflection|?MethodReflection getType(Expr $expr): Type isInExpressionAssign(Expr $expr): bool canAccessProperty(PropertyReflection $property): bool canCallMethod(MethodReflection $methodReflection): bool

Slide 15

Slide 15 text

Demo time! Let's use Scope in our new FooService() rule!

Slide 16

Slide 16 text

Ideas for custom rules Enforce single-action controllers with a single public __invoke() method

Slide 17

Slide 17 text

Ideas for custom rules Do not allow extending from AbstractController

Slide 18

Slide 18 text

Ideas for custom rules Require named arguments for attributes: ✅ #[Autowire(service: 'app.foo.bar')] ❌ #[Autowire('app.foo.bar')]

Slide 19

Slide 19 text

Virtual nodes

Slide 20

Slide 20 text

Collectors phpstan.org Documentation » Developing Extensions » Collectors

Slide 21

Slide 21 text

Testing rules with PHPUnit phpstan.org Documentation » Developing Extensions » Testing

Slide 22

Slide 22 text

Core Concepts Abstract Syntax Tree Scope Type System Reflection Dependency Injection & Configuration

Slide 23

Slide 23 text

Documentation phpstan.org Documentation » Developing Extensions » Core Concepts Documentation » Developing Extensions » Custom Rules

Slide 24

Slide 24 text

ChatGPT

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

github.com/phpstan/phpstan

Slide 28

Slide 28 text

@OndrejMirtes @[email protected]