Skip to content
intermediate Phase 12 · Testing & Quality

PHPStan Configuration & Rules

PHPStan configuration, levels, rules, extensions, and Magento PHPStan setup and rules

45m
0 problems
Topic Progress 0%

PHPStan Configuration

phpstan.neon Configuration File

parameters:
    level: 6
    paths:
        - app/code/Vendor/Module/Api/
        - app/code/Vendor/Module/Model/
        - app/code/Vendor/Module/Service/

    # Exclude test files and generated code
    excludePaths:
        - */Test/*
        - */_files/*
        - */generated/*

    # Ignore specific errors
    ignoreErrors:
        - '#Call to an undefined method Magento\\Framework\\DataObject::.*#'
        - '#Property .* is never assigned#'
        - '#has invalid typehints#'

    # Treat certain errors as specific level
    reportUnmatchedIgnoredErrors: false
    reportAlwaysUsedPhpDocTypesAsConstant: false

    # Check missing typehints on method parameters
    checkMissingIterableValueType: false
    checkGenericClassInNonGenericObjectType: false

    # Tuning
    polluteScopeWithAlwaysIterable: false
    rememberPossiblyAlwaysTrueConditionAndCheckType: true

Running PHPStan

# Basic analyse
vendor/bin/phpstan analyse

# With specific level override
vendor/bin/phpstan analyse src/ --level=8

# Generate baseline (for existing code)
vendor/bin/phpstan analyse --generate-baseline

# analyse with maximum memory
vendor/bin/phpstan analyse --memory-limit=2G

# Clear cache
vendor/bin/phpstan clear-cache

PHPStan Extensions

Magento PHPStan Extension

# phpstan.neon
includes:
    - vendor/magento/magento2-phpstan/extension.neon

parameters:
    level: 6
    paths:
        - app/code/Vendor/

This adds type stubs for:

  • Dynamic methods on DataObject
  • Magic __call methods
  • Magento's factory pattern
  • Scope config methods

Custom PHPStan Extension

namespace Vendor\Module\PHPStan\Rules;

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;

class NoDirectObjectManagerUsage implements Rule
{
    public function getNodeType(): string
    {
        return Node\Expr\MethodCall::class;
    }

    public function processNode(Node $node, Scope $scope): array
    {
        if ($this->isGetObjectCall($node, $scope)) {
            return [
                'Direct ObjectManager usage is not allowed. Use dependency injection.',
            ];
        }
        return [];
    }

    private function isGetObjectCall(Node $expr, Scope $scope): bool
    {
        if (!$expr->name instanceof Node\Identifier) {
            return false;
        }

        if ($expr->name->name !== 'get') {
            return false;
        }

        $type = $scope->getType($expr->var);
        return $type instanceof \PHPStan\Type\ObjectType
            && $type->getClassName() === \Magento\Framework\ObjectManager\ObjectManager::class;
    }
}

Register in extension:

# phpstan-extension.neon
services:
    -
        class: Vendor\Module\PHPStan\Rules\NoDirectObjectManagerUsage
        tags:
            - phpstan.rules.rule

PHPStan for Magento Best Practices

# Recommended Magento configuration
parameters:
    level: 6

    # Magento ignores
    ignoreErrors:
        # DataObject magic methods
        - '#Property .* does not exist on \*DataObject#'
        # Factory create() return types
        - '#returned type \*Factory::create\(\) is not \*Interface#'
        # Scope config dynamic methods
        - '#Call to undefined method .*getValue\(#'

    # Custom stubs for Magento
    stubs:
        - etc/phpstan/stubs/*.stub

CI/CD Integration

GitHub Actions Integration

# .github/workflows/phpstan.yml
name: PHPStan

on: [push, pull_request]

jobs:
  phpstan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.1'
          extensions: mbstring, intl
          coverage: none

      - name: Install Dependencies
        run: composer install --prefer-dist --no-progress

      - name: Run PHPStan
        run: vendor/bin/phpstan analyse --error-format=github --no-progress

      - name: Run PHPStan (strict)
        run: vendor/bin/phpstan analyse --level=8 --error-format=github
        continue-on-error: true  # Allow failure for strict level

Pre-commit Hook

#!/bin/bash
# .git/hooks/pre-commit

echo "Running PHPStan..."
vendor/bin/phpstan analyse app/code/Vendor/Module/ --no-progress --error-format=raw

if [ $? -ne 0 ]; then
    echo "PHPStan failed. Fix errors before committing."
    exit 1
fi

echo "PHPStan passed!"

PHPStan Baseline

# Generate baseline for existing code
vendor/bin/phpstan analyse --generate-baseline phpstan-baseline.neon

# phpstan.neon
includes:
    - phpstan-baseline.neon  # Existing errors are ignored

parameters:
    level: 6

The baseline lets you adopt PHPStan gradually — fix new errors but don't fix existing ones yet.

Quiz

1. What PHPStan level is recommended as a starting point for Magento?

Question 1 options

2. What does a PHPStan baseline do?

Question 2 options

3. PHPStan ignoreErrors is used for:

Question 3 options

Flashcards

Question

Recommended PHPStan level for Magento?

Answer

Level 6 as starting point, can increase gradually

Question

What is phpstan-baseline.neon?

Answer

Captures existing errors to allow gradual adoption

Question

How to run PHPStan?

Answer

vendor/bin/phpstan analyse src/ --level=6

Question

Magento PHPStan extension provides?

Answer

Type stubs for DataObject magic methods, factories, scope config

Revision Notes

Key Takeaways

  • 1. PHPStan level 6 is the recommended starting point for Magento
  • 2. Use the Magento PHPStan extension for dynamic method stubs
  • 3. Generate a baseline to adopt PHPStan gradually
  • 4. Integrate into CI pipeline for every push/PR
  • 5. Custom rules enforce project-specific coding standards

Interview Tips

  • Explain how to gradually adopt PHPStan (baseline, start at level 6)
  • Discuss Magento-specific PHPStan challenges (magic methods, dynamic config)
  • Give examples of custom rules (no ObjectManager usage, required type hints)

Cheat Sheet

PHPStan Setup:
  composer require --dev phpstan/phpstan
  vendor/bin/phpstan analyse src/ --level=6

Magento: vendor/magento/magento2-phpstan/extension.neon

Baseline: vendor/bin/phpstan analyse --generate-baseline
CI:       vendor/bin/phpstan analyse --error-format=github

Levels: 0-9 (9 = strictest)
  6 = recommended start
  8+ = for new codebases

Custom Rules:
  class X implements Rule { getNodeType(); processNode(); }