# Phan: a PHP static analyzer that tries to prove incorrectness, not correctness

> Phan is a static analyzer for PHP that prefers to minimize false positives. It requires PHP 8.1+ and the php-ast extension, installs through Composer, and can bootstrap its own config with phan --init.

**phan/phan** — Phan is a static analyzer for PHP. Phan prefers to avoid false-positives and attempts to prove incorrectness rather than correctness.

- Repository: https://github.com/phan/phan
- Website: https://github.com/phan/phan/wiki
- Stars: 5,625 · Forks: 367
- Language: PHP
- License: NOASSERTION
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/phan-phan

## What Phan is for, and who ends up using it

Phan targets PHP codebases where a false alarm costs more than a missed defect. The README states the design goal directly: Phan prefers to minimize false-positives and attempts to prove incorrectness rather than correctness. That is a narrower promise than the one most static analyzers make, and it shapes everything else about the tool.

The practical audience is a team already running PHP 8.1 or newer that wants type compatibility checked on calls, return values and binary operations, plus detection of undefined classes, methods, functions, constants, properties and variables. It also suits projects that need backward compatibility checks across PHP 8.1 through 8.5, or that want to catch syntax-level features introduced in a later 8.x minor release than the one they deploy on.

It is a poor fit for anyone hoping to point a scanner at an untyped legacy codebase and receive a clean report. Phan verifies type compatibility when type information is available or can be deduced, so the quality of its output tracks the quality of the annotations and inferred types it has to work with.

## How Phan builds its picture of a codebase

Phan parses your source into an AST and then reasons over that tree. The primary parser is php-ast, which is why the extension is a hard requirement for the default setup. The README notes that Phan 6 requires PHP 8.1+ with php-ast 1.1.3+ for PHP 8.4+ support, and that it can analyze PHP 8.1-8.5 syntax.

There is a fallback path. Phan relies on phan/phan-tolerant for the fallback parser and language-server mapping, consumed as a Composer dependency rather than a vendored subtree. Running with --allow-polyfill-parser removes the php-ast requirement, but the README warns there are slight differences in the parsing of doc comments. For a tool whose type inference leans on PHPDoc annotations, that caveat matters more than it first appears.

The analysis layer understands flow control in a limited way. The README says Phan has a good but not comprehensive understanding of flow control and can track values in a few use cases, naming arrays, integers and strings. It infers types from assert() statements and from conditionals in if elements and loops. On top of that sits a type system that covers union types, generic types via @template, generic arrays such as int[] and array<int,UserObject>, and array shapes such as array{key:string,otherKey:?stdClass}, including optional fields written as array{requiredKey:string,optionalKey?:string}. Multi-line @param, @var and @return annotations are normalized before parsing, so nested array and generic types can be formatted across several lines.

## Installing Phan with Composer and running a first analysis

The README calls Composer the easiest way to use Phan. Run this in the project you want to analyze:

```bash
composer require phan/phan
```

That installs Phan and its dependencies into vendor/. The next step is configuration. For a new project the README points at phan --init, which creates a .phan/config.php file with recommended settings and bundled stubs:

```bash
./vendor/bin/phan --init
```

Phan ships built-in stubs for enhanced type checking of PHP extensions, so the generated config does not leave you starting from nothing. If you would rather write the file yourself, the wiki page Getting Started covers manually creating a config file.

With the config in place, the analyzer runs from the same binary:

```bash
./vendor/bin/phan
```

The default pass reports undefined and inaccessible elements, arity and type problems on calls, and inheritance sanity checks. Three checks are opt-in and change the character of the run considerably: --dead-code-detection, --unused-variable-detection and --redundant-condition-detection. A subset of issues, including unused use statements, can be rewritten with --automatic-fix.

## The opt-in flags are where Phan stops being quiet

The default configuration is deliberately restrained, and the extra checks are the ones most likely to produce noise on a mature codebase. Dead code detection, unused variable and parameter detection, and redundant or impossible condition detection all have to be requested explicitly.

That split is sensible, but it means the first run of ./vendor/bin/phan tells you less than people expect. If your goal is to find unreachable branches or pointless casts, a default invocation will not report them, and the absence of findings is not evidence of their absence.

The wiki page Incrementally Strengthening Analysis exists for exactly this reason. The recommended path is to raise strictness as the codebase becomes better equipped to be analyzed, rather than turning everything on at once and drowning in output. Treat the opt-in flags as a separate rollout from the initial install, because they are.

## Where Phan is the wrong tool, and what to use instead

The clearest boundary is the php-ast extension. If your build environment cannot install it, you are on the polyfill parser, and the README's own caveat about doc comment parsing differences applies. For a project whose type information lives mostly in PHPDoc blocks, that is a meaningful degradation rather than a cosmetic one.

The second boundary is the analysis model itself. Phan proves incorrectness rather than correctness. A clean run does not mean your code is correct; it means Phan found nothing it could prove wrong. Teams that need exhaustive coverage guarantees are looking at the wrong class of tool.

PHPStan is the natural alternative to weigh. It is a static analyzer for PHP as well, and the difference in approach is the default posture: Phan's stated preference is to avoid false positives and stay silent unless it can demonstrate a problem, while the practical experience of PHPStan users is a configuration where you pick a rule level and accept the resulting report volume. If you want a tool that pushes hard toward finding everything it can and you are willing to triage the output, PHPStan's level-based model is the more direct fit. If you want a checker that errs toward silence, Phan's stated design is the match.

Phan also does something PHPStan is not described as doing here: it can check whether code uses features unsupported in older PHP 8.x minor releases, such as property hooks, readonly classes, enums, union types and match expressions. If you maintain a library that must run on an older 8.x release, that check is the reason to pick Phan over the alternative.

## Maintenance, licensing and the cost of staying current

The repository is not archived, and the last push was on 2026-09-15. The most recent release listed is 6.0.7 on 2026-06-22, preceded by 6.0.6 the same day and 6.0.5 on 2026-03-27. The pattern suggests patch releases arrive when there is something to ship rather than on a schedule.

The upgrade cost is tied to the PHP version you analyze. Phan 6 needs PHP 8.1+ and php-ast 1.1.3+ for PHP 8.4+ support, so a runtime upgrade can force a php-ast upgrade alongside it. The tolerant parser is consumed as a Composer dependency rather than a vendored subtree, which means refreshing it is a constraint change in composer.json or a composer update microsoft/tolerant-php-parser followed by committing the lockfile change. That is ordinary dependency hygiene, but it does put the parser version under your control rather than the maintainers'.

On licensing, the repository lists the licence as NOASSERTION and carries several separate files: LICENSE, LICENSE.LANGUAGE_SERVER, LICENSE.PHPSTORM_STUBS and LICENSE.PHP_PARSER. Because the components are licensed separately, check which file covers the part you are redistributing rather than assuming a single project-wide licence. This is not legal advice.

## Conclusion

Adopt Phan if you maintain a PHP 8.1+ codebase and want a checker that would rather stay silent than report a guess. Do not adopt it if you cannot install the php-ast extension and are unwilling to live with the parser differences that --allow-polyfill-parser introduces in doc comment handling. Before rolling it out, run phan --init on a branch, commit the generated .phan/config.php, and compare the issue list against the strictness flags you actually intend to enable.

## FAQ

### Does Phan require the php-ast extension?

Phan 6 requires PHP 8.1+ with the php-ast extension, and php-ast 1.1.3+ is required for PHP 8.4+ support. It can run without php-ast by passing --allow-polyfill-parser, though the README notes slight differences in the parsing of doc comments.

### How do I install Phan?

The README gives Composer as the easiest route: run composer require phan/phan in your project. For a new project, phan --init creates a .phan/config.php with recommended settings and bundled stubs.

### Which PHP versions can Phan analyze?

The README states Phan supports analyzing PHP version 8.1 through 8.5 syntax, and it can check for backward compatibility with PHP 8.5, 8.4, 8.3, 8.2 and 8.1.

## Sources

- [Issues](https://github.com/phan/phan/issues)
- [phan/phan on GitHub](https://github.com/phan/phan)
- [Project website](https://github.com/phan/phan/wiki)
- [README](https://github.com/phan/phan/blob/v6/README.md)
- [Releases](https://github.com/phan/phan/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/phan-phan
