demirkartal/eaglephp-code-quality

Unified PHPStan Level 10 ruleset, Pint configuration, and code quality suite for EaglePHP

Maintainers

Package info

github.com/demirkartal/eaglephp-code-quality

Type:phpstan-extension

pkg:composer/demirkartal/eaglephp-code-quality

Transparency log

Statistics

Installs: 17

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.5 2026-09-02 06:26 UTC

This package is auto-updated.

Last update: 2026-09-02 10:18:12 UTC


README

Latest Stable Version Total Downloads License

Shared PHPStan Level 10 ruleset and Laravel Pint configuration for EaglePHP ecosystem projects.

Monorepo checkout: ../STACK.md (dev tooling row). Cursor agent rules: .cursor/rules/eaglephp-code-quality.mdc.

Installation

composer require --dev demirkartal/eaglephp-code-quality

Enable the extension installer plugin in your project composer.json:

{
  "config": {
    "allow-plugins": {
      "phpstan/extension-installer": true
    }
  }
}

The installer is a transitive dependency of this package; allow-plugins must be set in the consumer project.

Usage

PHPStan

After installation, phpstan/extension-installer registers this package and all bundled extensions automatically. Your project root phpstan.neon should only define paths, optional baseline, and project-specific overrides:

  • Do not manually include vendor/demirkartal/eaglephp-code-quality/phpstan.neon — the extension installer loads the base ruleset.
  • Do not set customRulesetUsed: true in the consumer — it is declared in this package's phpstan.neon.
  • With customRulesetUsed: true, built-in level rules are loaded via config.level10.neon in this package; setting only parameters.level in a consumer config does not register those rules.
# phpstan.neon (consumer project)
includes:
  - phpstan-baseline.neon

parameters:
  paths:
    - src
    - tests
./vendor/bin/phpstan analyse

PHPStan next (PHP 8.5 target)

Optional forward-compat check against phpVersion: 80500. Include your project phpstan.neon (paths/baseline) plus the vendor next profile — phpstan-next.neon only overrides phpVersion, so the base ruleset is not loaded twice:

# phpstan.next.neon (consumer project)
includes:
  - phpstan.neon
  - vendor/demirkartal/eaglephp-code-quality/phpstan-next.neon
./vendor/bin/phpstan analyse -c phpstan.next.neon

Laravel Pint

Pint is not auto-wired; pass the shared config explicitly:

./vendor/bin/pint --config vendor/demirkartal/eaglephp-code-quality/pint.json

Preset and standards

pint.json uses preset: per — PHP Evolving Recommendation (PER-CS), which builds on PSR-12 and PSR-1. A separate psr12 preset is not needed.

Explicit rules extend PER with:

  • Strict typesdeclare_strict_types, void_return, import ordering
  • PHPDoc (PHPStan Level 10 aligned) — native types preferred; when PHPDoc remains, use modern notation (array<T>, list<T>), remove deprecated tags (@access, @package), enforce param order and summary punctuation
  • Layout — trailing commas, blank lines before statements, class attribute separation

Rules not enabled (conflict with native-type / PHPStan policy): phpdoc_add_missing_param_annotation, phpdoc_to_param_type, phpdoc_to_return_type.

After composer update, run composer format in consumer repos — PHPDoc diffs (T[]array<T> / list<T>) are expected.

Example composer.json scripts:

{
  "scripts": {
    "format": "pint --config vendor/demirkartal/eaglephp-code-quality/pint.json",
    "format:check": "pint --config vendor/demirkartal/eaglephp-code-quality/pint.json --test",
    "phpstan": "phpstan analyse --memory-limit=512M"
  }
}

Features

  • PHPStan Level 10 — strict static analysis for PHP ^8.4 || ^8.5 (lowest target: 80400).
  • 100% type coverage — native types enforced for constants, properties, parameters, and return types.
  • Type Perfect rules — disallows empty() / isset() on objects and parameter type removal in child classes.
  • Auto-wired PHPStan extensions — via phpstan/extension-installer on composer install.
  • Unified Pint profileper preset (PER > PSR-12), modern PHPDoc types, strict imports

Core stack

Tool Constraint Resolves to (stable)
PHP ^8.4 || ^8.5
PHPStan ^2.2 2.2.x (e.g. 2.2.11)
Laravel Pint ^1.30 1.30.x (e.g. 1.30.5)
extension-installer ^1.4 1.4.x
phpstan-strict-rules ^2.0 2.0.x
phpstan-phpunit ^2.0 2.0.x
phpstan-deprecation-rules ^2.0 2.0.x
type-coverage ^2.3 2.3.x

Active config: phpstan.neonbleedingEdge, config.level10.neon, phpVersion: 80400.

Alignment with PHPStan “Want to go further?”

This package implements the PHPStan “Want to go further?” recommendations out of the box:

Recommendation How this package provides it
phpstan-strict-rules Bundled; auto-loaded via extension-installer
Bleeding Edge bleedingEdge.neon included in phpstan.neon
Level 10 config.level10.neon chain (not just parameters.level)
Extra strict parameters checkUninitializedProperties, checkBenevolentUnionTypes, rememberPossiblyImpureFunctionValues: false, array offset checks, reportUnsafeArrayStringKeyCasting: detect, exception checks — see table below
Framework extensions Consumer responsibility — install Symfony/Doctrine/Laravel PHPStan extensions in your project if needed

Consumers do not need to duplicate vendor includes, customRulesetUsed, or parameters.level in their own phpstan.neon.

PHPStan rule levels

Levels are cumulative (level 6 includes 0–6).
Source: PHPStan Rule Levels (PHPStan 2.x → 0–10).

Level Focus Checks
0 Basics Unknown classes/functions/methods on $this, wrong argument counts, always-undefined variables
1 Variables & magic Possibly undefined variables; unknown access on classes with __call / __get
2 Expressions & PHPDoc Unknown methods on any expression; PHPDoc validation
3 Return & property types Return types; types assigned to properties
4 Dead code Always-false instanceof / type checks, dead else, unreachable code after return
5 Argument types Argument type compatibility for calls
6 Missing typehints Missing parameter, return, and property type declarations
7 Union members Calling methods/properties that exist on only some union members
8 Null safety Method/property access on nullable (T|null) values without a null check
9 Explicit mixed Almost no operations on explicitly typed mixed (pass-through only)
10 Implicit mixed Untyped values treated as strictly as explicit mixed (PHPStan 2.0+)

Level 10 does not replace level 9: it also treats missing types as mixed and forbids the same unsafe operations.

Extensions & rule packages

phpstan/extension-installer automatically loads and activates:

Package Role
bleedingEdge Upcoming PHPStan rule tightening and cutting-edge checks
phpstan-strict-rules Strict ===, type casting, bans weak PHP practices
phpstan-phpunit Test case inference, mock returns, assertion narrowing
phpstan-deprecation-rules Deprecated classes, methods, and functions
type-coverage 100% native type coverage (tomasvotruba/type-coverage)
type-perfect Narrowing rules: no empty()/isset() on objects, no param type removal

Strict analysis parameters

These flags extend beyond Level 10:

Parameter Purpose
customRulesetUsed true — required when the base ruleset is loaded via extension-installer (PHPStan 2.x); paired with config.level10.neon to register built-in level rules
Type coverage (100%) constant, declare, param_type, property_type, return_type
Type Perfect no_empty_on_object, no_isset_on_object, no_param_type_removal
checkArgumentsPassedByReference Types of variables passed by reference
checkBenevolentUnionTypes No lenient union assumptions; explicit narrowing required
checkClassCaseSensitivity Exact case for class names and namespaces
checkMissingCallableSignature Rejects bare callable / Closure; requires full signature
checkMissingOverrideMethodAttribute Requires #[\Override] on overridden methods
checkTooWideParameterOutInProtectedAndPublicMethods @param-out broader than actual assignments
checkTooWideReturnTypesInProtectedAndPublicMethods Return type broader than actual returns
checkTooWideThrowTypesInProtectedAndPublicMethods @throws broader than thrown exceptions
checkUninitializedProperties Typed properties must be initialized before access
reportAlwaysTrueInLastCondition Unreachable branches in match / if chains
reportAnyTypeWideningInVarTag No widening via @var beyond inferred type
reportIgnoresWithoutComments Ignored errors require a descriptive comment
reportPossiblyNonexistentConstantArrayOffset Array access with possibly missing constant keys
reportPossiblyNonexistentGeneralArrayOffset Dynamic array keys not statically proven
reportUnmatchedIgnoredErrors Stale ignoreErrors baseline entries fail CI
reportUnsafeArrayStringKeyCasting detect — narrows array key types when decimal-int strings may be cast to int at runtime (PHPStan 2.2+)
rememberPossiblyImpureFunctionValues false — no assuming identical non-pure call results
treatPhpDocTypesAsCertain false — defensive checks even when PHPDoc is present
exceptions.reportUncheckedExceptionDeadCatch catch for exceptions never thrown in try
exceptions.uncheckedExceptionClasses LogicException, RuntimeException exempt from checked flow
exceptions.check.throwTypeCovariance Override may only throw same or narrower exceptions
exceptions.check.tooWideImplicitThrowType Broad implicit throws without documentation

phpstan-strict-rules (via rules.neon, not duplicated in phpstan.neon) also enables: checkDynamicProperties, checkExplicitMixedMissingReturn, reportMaybesInMethodSignatures, reportMaybesInPropertyPhpDocTypes, reportWrongPhpDocTypeInVarTag, reportNonIntStringArrayKey (with bleeding edge), polluteScopeWithLoopInitialAssignments: false, and related scope-pollution guards.

Optional consumer overrides

Not included in the shared ruleset (opt-in per project when ready):

# Consumer phpstan.neon — example: enforce @throws for checked exceptions
parameters:
  exceptions:
    check:
      missingCheckedExceptionInThrows: true
    checkedExceptionClasses:
      - 'YourApp\DomainException'

exceptions.check.missingCheckedExceptionInThrows requires defining checkedExceptionClasses or checkedExceptionRegexes. EaglePHP enables this as a project override; add it only when the codebase is ready.

PHPStan does not require PHPDoc when native types suffice; missing type info is enforced at level 6+ and via type coverage.

License

MIT — see LICENSE.