Phpstan - Php Static Analysis Tool (Discover Bugs Inward Your Code Without Running It!)


PHPStan focuses on finding errors inwards your code without truly running it. It catches whole classes of bugs fifty-fifty before y'all write tests for the code. It moves PHP closer to compiled languages inwards the feel that the correctness of each trouble of the code tin last checked before y'all run the actual line.

Read to a greater extent than almost PHPStan on Medium.com
Try out PHPStan on the on-line playground!

Prerequisites
PHPStan requires PHP >= 7.1. You receive got to run it inwards surroundings alongside PHP 7.x but the actual code does non receive got to work PHP 7.x features. (Code written for PHP 5.6 together with before tin run on 7.x generally unmodified.)
PHPStan industrial plant best alongside modern object-oriented code. The to a greater extent than strongly-typed your code is, the to a greater extent than information y'all give PHPStan to piece of work with.
Properly annotated together with typehinted code (class properties, business office together with method arguments, render types) helps non alone static analysis tools but also other people that piece of work alongside the code to sympathise it.

Installation
To start performing analysis on your code, require PHPStan inwards Composer:
composer require --dev phpstan/phpstan
Composer volition install PHPStan's executable inwards its bin-dir which defaults to vendor/bin.
If y'all receive got conflicting dependencies or y'all desire to install PHPStan globally, the best agency is via a PHAR archive. You volition e'er regain the latest stable PHAR archive below the release notes. You tin also work the phpstan/phpstan-shim parcel to install PHPStan via Composer without the take a opportunity of conflicting dependencies.
You tin also work PHPStan via Docker.

First run
To allow PHPStan analyse your codebase, y'all receive got to work the analyse dominance together with signal it to the right directories.
So, for instance if y'all receive got your classes inwards directories src together with tests, y'all tin run PHPStan similar this:
vendor/bin/phpstan analyse src tests
PHPStan volition in all probability regain about errors, but don't worry, your code mightiness last exactly fine. Errors constitute on the origin run tend to be:
  • Extra arguments passed to functions (e. g. business office requires 2 arguments, the code passes three)
  • Extra arguments passed to print/sprintf functions (e. g. format string contains i placeholder, the code passes 2 values to replace)
  • Obvious errors inwards dead code
  • Magic conduct that needs to last defined. See Extensibility.
After fixing the obvious mistakes inwards the code, seem to the next department for all the configuration options that volition convey the number of reported errors to null making PHPStan suitable to run every bit component of your continuous integration script.

Rule levels
If y'all desire to work PHPStan but your codebase isn't upwards to speed alongside potent typing together with PHPStan's strict checks, y'all tin take from currently 8 levels (0 is the loosest together with vii is the strictest) yesteryear passing --level to analyse command. Default bird is 0.
This characteristic enables incremental adoption of PHPStan checks. You tin start using PHPStan alongside a lower dominion bird together with growth it when y'all experience similar it.
You tin also work --level max every bit an alias for the highest level. This volition ensure that y'all volition e'er work the highest bird when upgrading to novel versions of PHPStan. Please authorities annotation that this tin practise a pregnant obstruction when upgrading to a newer version because y'all mightiness receive got to prepare a lot of code to convey the number of errors downwards to zero.

Extensibility
Unique characteristic of PHPStan is the powerfulness to define together with statically banking enterprise check "magic" conduct of classes - accessing properties that are non defined inwards the bird but are created inwards __get together with __set together with invoking methods using __call.
See Class reflection extensions, Dynamic render type extensions together with Type-specifying extensions.
You tin also install official framework-specific extensions:
Unofficial extensions for other frameworks together with libraries are also available:
Unofficial extensions alongside third-party rules:
New extensions are becoming available on a regular basis!

Configuration
Config file is passed to the phpstan executable alongside -c option:
vendor/bin/phpstan analyse -l four -c phpstan.neon src tests
When using a custom projection config file, y'all receive got to exceed the --level (-l) selection to analyse dominance (default value does non apply here).
If y'all practise non render config file explicitly, PHPStan volition seem for files named phpstan.neon or phpstan.neon.dist inwards electrical current directory.
The resolution priority is every bit such:
  1. If config file is provided on dominance line, it is used.
  2. If config file phpstan.neon exists inwards electrical current directory, it volition last used.
  3. If config file phpstan.neon.dist exists inwards electrical current directory, it volition last used.
  4. If none of the inwards a higher identify is true, no config volition last used.
NEON file format is really similar to YAML. All the next options are component of the parameters section.

Configuration variables
  • %rootDir% - root directory where PHPStan resides (i.e. vendor/phpstan/phpstan inwards Composer installation)
  • %currentWorkingDirectory% - electrical current working directory where PHPStan was executed

Configuration options
  • tmpDir - specifies the temporary directory used yesteryear PHPStan cache (defaults to sys_get_temp_dir() . '/phpstan')
  • level - specifies analysis bird - if specified, -l selection is non required
  • paths - specifies analysed paths - if specified, paths are non required to last passed every bit arguments

Autoloading
PHPStan uses Composer autoloader thus the easiest agency how to autoload classes is through the autoload/autoload-dev sections inwards composer.json.

Specify paths to scan
If PHPStan complains almost about non-existent classes together with you're surely the classes be inwards the codebase AND y'all don't desire to work Composer autoloader for about reason, y'all tin specify directories to scan together with concrete files to include using autoload_directories together with autoload_files array parameters:
parameters:  autoload_directories:   - %rootDir%/../../../build  autoload_files:   - %rootDir%/../../../generated/routes/GeneratedRouteList.php
%rootDir% is expanded to the root directory where PHPStan resides.

Autoloading for global installation
PHPStan supports global installation using composer global or via a PHAR archive. In this case, it's non component of the projection autoloader, but it supports autodiscovery of the Composer autoloader from electrical current working directory residing inwards vendor/:
cd /path/to/project phpstan analyse src tests # looks for autoloader at /path/to/project/vendor/autoload.php
If y'all receive got your dependencies installed at a unlike path or you're running PHPStan from a unlike directory, y'all tin specify the path to the autoloader alongside the --autoload-file|-a option:
phpstan analyse --autoload-file=/path/to/autoload.php src tests

Exclude files from analysis
If your codebase contains about files that are broken on purpose (e. g. to bear witness conduct of your application on files alongside invalid PHP code), y'all tin exclude them using the excludes_analyse array parameter. String at each trouble is used every bit a blueprint for the fnmatch function.
parameters:  excludes_analyse:   - %rootDir%/../../../tests/*/data/*

Include custom extensions
If your codebase contains php files alongside extensions other than the measure .php extension together with thus y'all tin add together them to the fileExtensions array parameter:
parameters:  fileExtensions:   - php   - module   - inc

Universal object crates
Classes without predefined construction are mutual inwards PHP applications. They are used every bit universal holders of information - whatever belongings tin last laid together with read on them. Notable examples include stdClass, SimpleXMLElement (these are enabled yesteryear default), objects alongside results of database queries etc. Use universalObjectCratesClasses array parameter to allow PHPStan know which classes alongside these characteristics are used inwards your codebase:
parameters:  universalObjectCratesClasses:   - Dibi\Row   - Ratchet\ConnectionInterface

Add non-obviously assigned variables to scope
If y'all work about variables from a endeavour block inwards your take handgrip of blocks, laid polluteCatchScopeWithTryAssignments boolean parameter to true.
try {  $author = $this->getLoggedInUser();  $post = $this->postRepository->getById($id); } take handgrip of (PostNotFoundException $e) {  // $author is in all probability defined hither  throw novel ArticleByAuthorCannotBePublished($author); }
If y'all are enumerating over all possible situations inwards if-elseif branches together with PHPStan complains almost undefined variables after the conditions, y'all tin write an else branch alongside throwing an exception:
if (somethingIsTrue()) {  $foo = true; } elseif (orSomethingElseIsTrue()) {  $foo = false; } else {  throw novel ShouldNotHappenException(); }  doFoo($foo);
I recommend leaving polluteCatchScopeWithTryAssignments laid to false because it leads to a clearer together with to a greater extent than maintainable code.

Custom early on terminating method calls
Previous instance showed that if a status branches goal alongside throwing an exception, that branch does non receive got to define a variable used after the status branches end.
But exceptions are non the alone agency how to terminate execution of a method early. Some specific method calls tin last perceived yesteryear projection developers also every bit early on terminating - similar a redirect() that stops execution yesteryear throwing an internal exception.
if (somethingIsTrue()) {  $foo = true; } elseif (orSomethingElseIsTrue()) {  $foo = false; } else {  $this->redirect('homepage'); }  doFoo($foo);
These methods tin last configured yesteryear specifying a bird on whose instance they are called similar this:
parameters:  earlyTerminatingMethodCalls:   Nette\Application\UI\Presenter:    - redirect    - redirectUrl    - sendJson    - sendResponse

Ignore error messages alongside regular expressions
If about number inwards your code base of operations is non tardily to prepare or exactly but desire to bargain alongside it later, y'all tin exclude error messages from the analysis consequence alongside regular expressions:
parameters:  ignoreErrors:   - '#Call to an undefined method [a-zA-Z0-9\\_]+::method\(\)#'   - '#Call to an undefined method [a-zA-Z0-9\\_]+::expects\(\)#'   - '#Access to an undefined belongings PHPUnit_Framework_MockObject_MockObject::\$[a-zA-Z0-9_]+#'   - '#Call to an undefined method PHPUnit_Framework_MockObject_MockObject::[a-zA-Z0-9_]+\(\)#'
To exclude an error inwards a specific directory or file, specify a path or paths along alongside the message:
parameters:  ignoreErrors:   -    message: '#Call to an undefined method [a-zA-Z0-9\\_]+::method\(\)#'    path: %currentWorkingDirectory%/some/dir/SomeFile.php   -    message: '#Call to an undefined method [a-zA-Z0-9\\_]+::method\(\)#'    paths:     - %currentWorkingDirectory%/some/dir/*     - %currentWorkingDirectory%/other/dir/*   - '#Other error to take handgrip of anywhere#'
If about of the patterns practise non occur inwards the consequence anymore, PHPStan volition allow y'all know together with y'all volition receive got to withdraw the blueprint from the configuration. You tin plough off this conduct yesteryear setting reportUnmatchedIgnoredErrors to false inwards PHPStan configuration.

Bootstrap file
If y'all ask to initialize something inwards PHP runtime before PHPStan runs (like your ain autoloader), y'all tin render your ain bootstrap file:
parameters:  bootstrap: %rootDir%/../../../phpstan-bootstrap.php

Custom rules
PHPStan allows writing custom rules to banking enterprise check for specific situations inwards your ain codebase. Your dominion bird needs to implement the PHPStan\Rules\Rule interface together with registered every bit a service inwards the configuration file:
services:  -   class: MyApp\PHPStan\Rules\DefaultValueTypesAssignedToPropertiesRule   tags:    - phpstan.rules.rule
For inspiration on how to implement a dominion plough to src/Rules to run across a lot of built-in rules.
Check out also phpstan-strict-rules repository for extra strict together with opinionated rules for PHPStan!
Check every bit good phpstan-deprecation-rules for rules that honor usage of deprecated classes, methods, properties, constants together with traits!

Custom error formatters
PHPStan outputs errors via formatters. You tin customize the output yesteryear implementing the ErrorFormatter interface inwards a novel bird together with add together it to the configuration. For existing formatters, run across side yesteryear side chapter.
interface ErrorFormatter {   /**   * Formats the errors together with outputs them to the console.   *   * @param \PHPStan\Command\AnalysisResult $analysisResult   * @param \Symfony\Component\Console\Style\OutputStyle $style   * @return int Error code.   */  world business office formatErrors(   AnalysisResult $analysisResult,   \Symfony\Component\Console\Style\OutputStyle $style  ): int;  }
Register the formatter inwards your phpstan.neon:
services:  errorFormatter.awesome:   class: App\PHPStan\AwesomeErrorFormatter
Use the mention component after errorFormatter. every bit the CLI selection value:
vendor/bin/phpstan analyse -c phpstan.neon -l four --error-format awesome src tests

Existing error formatters to last used
You tin exceed the next keywords to the --error-format=X parameter inwards lodge to impact the output:
  • table: Default. Grouped errors yesteryear file, colorized. For human consumption.
  • raw: Contains i error per line, alongside path to file, trouble number, together with error description
  • checkstyle: Creates a checkstyle.xml compatible output. Note that you'd receive got to redirect output into a file inwards lodge to capture the results for afterwards processing.
  • json: Creates minified .json output without whitespaces. Note that you'd receive got to redirect output into a file inwards lodge to capture the results for afterwards processing.
  • prettyJson: Creates human readable .json output alongside whitespaces together with indentations. Note that you'd receive got to redirect output into a file inwards lodge to capture the results for afterwards processing.

Class reflection extensions
Classes inwards PHP tin expose "magical" properties together with methods decided inwards run-time using bird methods similar __get, __set together with __call. Because PHPStan is all almost static analysis (testing code for errors without running it), it has to know almost those properties together with methods beforehand.
When PHPStan stumbles upon a belongings or a method that is unknown to built-in bird reflection, it iterates over all registered bird reflection extensions until it finds i that defines the belongings or method.
Class reflection extension cannot receive got PHPStan\Broker\Broker (service for obtaining bird reflections) injected inwards the constructor due to circular reference issue, but the extensions tin implement PHPStan\Reflection\BrokerAwareExtension interface to obtain Broker via a setter.

Properties bird reflection extensions
This extension type must implement the next interface:
namespace PHPStan\Reflection;  interface PropertiesClassReflectionExtension {   world business office hasProperty(ClassReflection $classReflection, string $propertyName): bool;   world business office getProperty(ClassReflection $classReflection, string $propertyName): PropertyReflection;  }
Most probable y'all volition also receive got to implement a novel PropertyReflection class:
namespace PHPStan\Reflection;  interface PropertyReflection {   world business office getType(): Type;   world business office getDeclaringClass(): ClassReflection;   world business office isStatic(): bool;   world business office isPrivate(): bool;   world business office isPublic(): bool;  }
This is how y'all register the extension inwards project's PHPStan config file:
services:  -   class: App\PHPStan\PropertiesFromAnnotationsClassReflectionExtension   tags:    - phpstan.broker.propertiesClassReflectionExtension

Methods bird reflection extensions
This extension type must implement the next interface:
namespace PHPStan\Reflection;  interface MethodsClassReflectionExtension {   world business office hasMethod(ClassReflection $classReflection, string $methodName): bool;   world business office getMethod(ClassReflection $classReflection, string $methodName): MethodReflection;  }
Most probable y'all volition also receive got to implement a novel MethodReflection class:
namespace PHPStan\Reflection;  interface MethodReflection {   world business office getDeclaringClass(): ClassReflection;   world business office getPrototype(): self;   world business office isStatic(): bool;   world business office isPrivate(): bool;   world business office isPublic(): bool;   world business office getName(): string;   /**   * @return \PHPStan\Reflection\ParameterReflection[]   */  world business office getParameters(): array;   world business office isVariadic(): bool;   world business office getReturnType(): Type;  }
This is how y'all register the extension inwards project's PHPStan config file:
services:  -   class: App\PHPStan\EnumMethodsClassReflectionExtension   tags:    - phpstan.broker.methodsClassReflectionExtension

Dynamic render type extensions
If the render type of a method is non e'er the same, but depends on an declaration passed to the method, y'all tin specify the render type yesteryear writing together with registering an extension.
Because y'all receive got to write the code alongside the type-resolving logic, it tin last every bit complex every bit y'all want.
After writing the sample extension, the variable $mergedArticle volition receive got the right type:
$mergedArticle = $this->entityManager->merge($article); // $mergedArticle volition receive got the same type every bit $article
This is the interface for dynamic render type extension:
namespace PHPStan\Type;  work PhpParser\Node\Expr\MethodCall; work PHPStan\Analyser\Scope; work PHPStan\Reflection\MethodReflection;  interface DynamicMethodReturnTypeExtension {   world business office getClass(): string;   world business office isMethodSupported(MethodReflection $methodReflection): bool;   world business office getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type;  }
And this is how you'd write the extension that correctly resolves the EntityManager::merge() render type:
public business office getClass(): string {  render \Doctrine\ORM\EntityManager::class; }  world business office isMethodSupported(MethodReflection $methodReflection): bool {  render $methodReflection->getName() === 'merge'; }  world business office getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type {  if (count($methodCall->args) === 0) {   render \PHPStan\Reflection\ParametersAcceptorSelector::selectFromArgs(    $scope,    $methodCall->args,    $methodReflection->getVariants()   )->getReturnType();  }  $arg = $methodCall->args[0]->value;   render $scope->getType($arg); }
And finally, register the extension to PHPStan inwards the project's config file:
services:  -   class: App\PHPStan\EntityManagerDynamicReturnTypeExtension   tags:    - phpstan.broker.dynamicMethodReturnTypeExtension
There's also an analogous functionality for:
  • static methods using DynamicStaticMethodReturnTypeExtension interface together with phpstan.broker.dynamicStaticMethodReturnTypeExtension service tag.
  • functions using DynamicFunctionReturnTypeExtension interface together with phpstan.broker.dynamicFunctionReturnTypeExtension service tag.

Type-specifying extensions
These extensions allow y'all to specify types of expressions based on surely pre-existing conditions. This is best illustrated alongside twosome examples:
if (is_int($variable)) {     // hither nosotros tin last surely that $variable is integer }
// using PHPUnit's asserts  self::assertNotNull($variable); // hither nosotros tin last surely that $variable is non null
Type-specifying extension cannot receive got PHPStan\Analyser\TypeSpecifier injected inwards the constructor due to circular reference issue, but the extensions tin implement PHPStan\Analyser\TypeSpecifierAwareExtension interface to obtain TypeSpecifier via a setter.
This is the interface for type-specifying extension:
namespace PHPStan\Type;  work PhpParser\Node\Expr\StaticCall; work PHPStan\Analyser\Scope; work PHPStan\Analyser\SpecifiedTypes; work PHPStan\Analyser\TypeSpecifierContext; work PHPStan\Reflection\MethodReflection;  interface StaticMethodTypeSpecifyingExtension {   world business office getClass(): string;   world business office isStaticMethodSupported(MethodReflection $staticMethodReflection, StaticCall $node, TypeSpecifierContext $context): bool;   world business office specifyTypes(MethodReflection $staticMethodReflection, StaticCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes;  }
And this is how you'd write the extension for the minute instance above:
public business office getClass(): string {  render \PHPUnit\Framework\Assert::class; }  world business office isStaticMethodSupported(MethodReflection $staticMethodReflection, StaticCall $node, TypeSpecifierContext $context): bool; {  // The $context declaration tells us if we're inwards an if status or non (as inwards this case).  render $staticMethodReflection->getName() === 'assertNotNull' && $context->null(); }  world business office specifyTypes(MethodReflection $staticMethodReflection, StaticCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes {  // Assuming extension implements \PHPStan\Analyser\TypeSpecifierAwareExtension.  render $this->typeSpecifier->create($node->var, \PHPStan\Type\TypeCombinator::removeNull($scope->getType($node->var)), $context); }
And finally, register the extension to PHPStan inwards the project's config file:
services:  -   class: App\PHPStan\AssertNotNullTypeSpecifyingExtension   tags:    - phpstan.typeSpecifier.staticMethodTypeSpecifyingExtension
There's also an analogous functionality for:
  • dynamic methods using MethodTypeSpecifyingExtension interface together with phpstan.typeSpecifier.methodTypeSpecifyingExtension service tag.
  • functions using FunctionTypeSpecifyingExtension interface together with phpstan.typeSpecifier.functionTypeSpecifyingExtension service tag.

Building
You tin either run the whole build including linting together with coding standards using
vendor/bin/phing
or run alone tests using
vendor/bin/phing tests