Open-source project
overtrue/phplint avatar
overtrue/phplint

phplint: parallel PHP syntax checking for large codebases

:bug: A tool that can speed up linting of php files by running several lint processes at once.

1,021 stars95 forksPHPMIT

At a glance

What is it?
phplint is a PHP tool that speeds up syntax checking by running several lint processes at once, and its versioning is unusual because major version 9 is a full code rewrite that shares one source tree across all PHP 8 lines, with 3.x marked end of life. It is MIT licensed, distributed through Packagist, a PHAR, Docker, Phive and Composer, and it is in release candidate.
Who is it for?
phplint fits a PHP project with a large tree where a serial syntax check over every file is the slowest part of a pre-commit hook or a CI job. It does not fit a small codebase, where the parallelism is not worth the setup, and it is not a style linter, since it checks syntax only.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A syntax checker, not a style linter

phplint is a tool that can speed up linting of php files by running several lint processes at once, and the word lint in the name is doing a lot of work in the PHP world, so it is worth being clear about what this does.

The topic tags name it precisely: composer, lint, linter, linting, php, phplint, syntax, syntax-checker. The project is a syntax checker. It asks PHP whether each file parses, and it does that in parallel across processes.

That is a much narrower job than the tools most PHP developers call linters. PHP_CodeSniffer, PHPStan and Psalm are about style, about types, about finding bugs in code that parses perfectly well. phplint is about the bottom of the stack, the question of whether the file is valid PHP at all.

The value of that narrow job is a specific kind of value. A syntax error anywhere in a large tree is a hard failure: the application does not boot, a test run dies, a deploy stops. Catching them across thousands of files is mechanical work that parallelises well, and doing it serially is exactly the kind of thing that makes a developer disable a check.

The distinction also explains the design. If you only need to know whether a file parses, you can shell out to `php -l` many times at once, and the interesting engineering is in the process management, the file discovery, the configuration and the reporting rather than in any analysis. The repository's `src/` directory, the `.phplint.yml` configuration file and the `bin/` directory are consistent with that picture.

One source tree serving four major versions, which is unusual

The version table at the top of the readme is the most unusual thing about this project, and it is worth reading carefully before you pick a version.

code
| Version | Status                                    | Requirements   |
|:--------|:------------------------------------------|:---------------|
| **9.x** | **Active development**                    | **PHP >= 8.0** |
| 6.x     | Active support                            | PHP >= 8.2     |
| 5.x     | Active support                            | PHP >= 8.1     |
| 4.x     | Active support                            | PHP >= 8.0     |
| 3.x     | End Of Life                               | PHP >= 7.4     |

Four major versions are in active support or active development at the same time, and three of them are, in a sense, the same code.

The explanation is the sentence below the table. Major version 9.0 is a full code rewrite that has the same source code, from the `main` branch, for all PHP 8.x versions, meaning 4.x, 5.x and 6.x, with a lot of improvement, full documentation and full unit code tested.

So the 4.x, 5.x and 6.x lines are one implementation whose requirement differs by PHP version, and the 9.x line is a second implementation of the same tool. That is an unusual arrangement. It means the maintainer is running two code bases in parallel, one for PHP 8.0 through 8.1 users and one for the rewrite, and the table is the map of which one you get.

The 3.x line is the one marked End Of Life, and the note above it covers the case where you are on PHP older than 8.0 and should use 3.4.0.

The current release tags tell you where the active line is. They are 9.8.0-rc.3 from 2026-10-13, 9.8.0-rc.2 from 2026-09-09 and 9.8.0-rc.1 from 2026-09-23, so the 9.x line is in release candidate at 9.8.0, with the last push on 2026-10-15 and the default branch named 9.0 rather than main, which matches the rewrite line.

Five ways to install it, and the one that suits CI

The table of contents lists four installation routes under a single page, and the documentation splits them into Requirements, PHAR, Docker, Phive and Composer.

The PHAR route is the one with the least friction for a single developer, since a PHAR is a self-contained archive with the interpreter's own runtime bundled in, so there is nothing to install beyond PHP itself.

Composer is the route for a project that declares its tooling, and the badges at the top of the readme point at Packagist, so the package is published there under `overtrue/phplint`.

Phive is the PHAR manager, and it is the interesting option for teams that want the tool's binary version pinned and verified rather than managed as a project dependency.

Docker is the one that matters for continuous integration, and the Dockerfile at the root of the repository is a good example of a small, careful image. It starts from `php:${PHP_VERSION}-cli-alpine` with PHP_VERSION defaulting to 8.0, copies the development php.ini over the stock one, and creates a group and a user with `addgroup appgroup && adduser appuser -D -G appgroup` before switching to that user with `USER appuser`. Composer v2 is copied in from the `composer/composer:2-bin` image, and `COMPOSER_ALLOW_SUPERUSER` is set, because the install runs as root during the build. The package is installed globally with a version constraint of `9.0.x-dev`.

Running as a non-root user in the final image is the detail that makes the image safe to run in CI, and the version constraint pins the image to the rewrite line rather than to a stable release.

There is also a GitHub Action, given by the `action.yml` file at the root, and the `entrypoint.sh` script that the Dockerfile makes executable and sets as the entry point. The `examples/` directory holds fixtures for testing, including `no-source-to-lint.php` and `no-yaml-configuration.php`, which are the two degenerate cases a linter has to survive.

The configuration and the release tooling

Two files in the repository describe how the tool is configured and how it is released, and both point to a project that has been through several iterations of its own process.

The configuration is `.phplint.yml`, and the presence of `examples/no-yaml-configuration.php` as a test fixture tells you the YAML file is optional and that the tool has a default behaviour when it is absent. The `docs/configuration.md` page in the table of contents is where the options live, and the `docs/architecture/README.md` directory suggests the internals are documented in some depth.

The release tooling is a mix of several tools, and the names in the top-level listing describe the workflow. There is a `.changes/` directory with a `.changie.yaml` configuration, which is Changie, the changelog manager that keeps entries in a directory and generates the changelog at release time. There is a `box.json`, which is the configuration for Box, the PHAR build tool, and that is how the PHAR artefact is produced. There is a `phpunit.xml` and a `phpunit-9.xml` side by side, so the test suite runs under both PHPUnit 9 and a newer major. There is a `.php-cs-fixer.dist.php` for the project's own code style, and a `vendor-bin/` directory, which is the Composer pattern for isolating development tools into their own isolated installs.

The `CHANGELOG.md` and `mkdocs.yml` complete the picture. The documentation is built with MkDocs, and the `docs/` directory holds the pages the table of contents links to, including a dedicated `docs/upgrading.md`, which is exactly what you would expect from a project that maintains four major version lines at once.

The release badge points at a GitHub Actions workflow for releases, and the FOSSA badge is a licence and security scanning service, so the project runs automated licence compliance checks as part of its release process.

Where phplint is the wrong choice

The first case is a small codebase. If your project has two hundred PHP files, a serial syntax check finishes in under a second and the parallelism buys nothing, and the configuration file, the version matrix and the container all become overhead.

The second case is a team that thinks this replaces a real static analyser. It does not. A syntax checker answers one question, whether a file parses, and a project that wants to catch undefined variables, wrong argument types or unreachable code needs PHPStan or Psalm. Using phplint as though it were a static analyser produces a false sense of coverage, which is worse than not running it.

The third case is a project that is not PHP. The tool shells out to PHP itself, so it is useless for anything else, and the Dockerfile pins a PHP CLI base image accordingly.

The fourth is a team that has adopted a different parallel-lint tool. There are other approaches to the same problem, and if you already have one in your pipeline, adding this one means two tools with two configuration files and two version matrices to keep current.

A version-selection caution. With 9.x in release candidate at 9.8.0-rc.3 and 3.x at end of life, a project choosing a version has to make an explicit decision about which line it wants, and the readme's advice for anything below PHP 8.0 is to use 3.4.0. The practical point is that the 9.x line's release candidates are the current work, so a team that wants the rewrite should expect to move again when 9.8.0 goes stable, and a team that wants stability now should look at what the older active-support lines provide, since they are described as having the same source code with fewer improvements.

One last note on the licensing and the toolchain. The project is MIT licensed and sponsored by JetBrains, which provides the maintainer with a licence, and the readme also carries a course link for the author on building PHP packages from scratch, which tells you the project is part of a broader effort around PHP package development.

Editorial conclusion

phplint fits a PHP project with a large tree where a serial syntax check over every file is the slowest part of a pre-commit hook or a CI job. It does not fit a small codebase, where the parallelism is not worth the setup, and it is not a style linter, since it checks syntax only. Before you pin a version, read the version table in the readme, because 9.x is a rewrite while 4.x, 5.x and 6.x are separate lines with different PHP requirements, and 3.x is end of life, and note that the current tags are 9.8.0 release candidates rather than a stable 9.8.0.

Frequently asked questions

What does phplint actually check?

It is a syntax checker. The topic tags name it as a syntax-checker, and it works by running several PHP lint processes at once to determine whether each file parses. It is not a style linter or a static analyser, so it does not replace PHP_CodeSniffer, PHPStan or Psalm.

Which version of phplint should I use?

The 9.x line is in active development and requires PHP 8.0 or newer, and the current tags are 9.8.0 release candidates. The 6.x, 5.x and 4.x lines are in active support with the same source code and higher PHP requirements, and 3.x is end of life, where the recommended version is 3.4.0 for anything below PHP 8.0.

How do I install phplint?

There are four documented routes: a PHAR, Docker, Phive and Composer, with requirements in `docs/installation.md`. The Docker image is based on `php:${PHP_VERSION}-cli-alpine` and installs the package globally with Composer under a `9.0.x-dev` constraint, and the package is published on Packagist as `overtrue/phplint`.

Does phplint need a configuration file?

No. The configuration is optional and lives in `.phplint.yml`, and the repository includes an `examples/no-yaml-configuration.php` fixture, which indicates the tool has a default behaviour when the file is absent. The options are documented in `docs/configuration.md`.

Official sources

  1. Issues
  2. License: MIT
  3. overtrue/phplint on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/overtrue-phplint.svg)](https://hysenlabs.com/projects/overtrue-phplint)

Was this useful?