PHP Ecosystem Deep Dive: The Code Quality Landscape

Share on Twitter Share on LinkedIn

A quick word about the approach

Hi! I'm Jean-François Lépine, and I've been interested in software quality for quite a few years now. I created PhpMetrics, then Ast-Metrics, and have helped many companies in industrializing their development processes.

I wanted to see the big picture of PHP code in the wild, so I went all in. I downloaded the entire PHP ecosystem—a whopping 236 GB of Git repositories containing a total of 2.3 billion lines of code.

The analysis script ran for three full days, processing every single file. I built it in Go for raw performance and precise CPU control - PHP analysis at this scale needs serious optimization.

TL;DR - Too Long? Didn’t Read? Here’s the Short Version!

PHP open-source is modern, modular, and maintainable. But beware of over-reliance on a few core packages and under-maintained projects.

Modern PHP is modular and concise
  • 36% of files have fewer than 100 lines of code
  • Heavy use of interfaces makes the ecosystem highly modular.
Complexity is mostly under control, but…
  • 76.5% of files have low cyclomatic complexity (0-10).
  • However, 23.5% of the code is complex, posing maintainability challenge
Extreme dependency on a small set of packages
  • Just 136 packages (0.05%) account for 50% of all downloads.
  • These core libraries are well-maintained, but they create strong dependency risks.
The "shadow infrastructure" problem
  • Some highly used packages receive very little recognition (few GitHub stars).
  • Critical dependencies may be maintained by very small teams.
The PHP ecosystem is healthy but aging
  • 70% of packages show minimal activity.
  • 23% appear abandoned, making dependency audits essential.

X-Ray of the PHP OpenSource Ecosystem

Open Source vs Enterprise Context:

It's important to note that these metrics come from open-source projects, which differ significantly from typical enterprise codebases. Open-source libraries often prioritize extensibility and abstraction (explaining the high interface-to-class ratio), while enterprise projects may focus more on business logic implementation and specific use cases. When benchmarking your own codebase, consider whether your context aligns more with library design patterns or application-specific implementations.

I analyzed 11.1 million PHP files, totaling over 707.6 million lines of PHP code—part of a staggering 2.3 billion lines of code across all languages in the Packagist ecosystem. These metrics offer a real-world snapshot of how PHP is structured in the wild, beyond theoretical best practices."

File Size Distribution

An overwhelming 36% of files contain fewer than 100 logical lines of code, with a median of just 4.0 lines.

This reveals a strong ecosystem-wide preference for small, focused implementations - much smaller than most style guides would suggest as maximums. It also reflects the massive use of interfaces in the ecosystem, with 3.7M interfaces compared to 7.4M classes - many of these interfaces containing no logical code, only method signatures.

Cyclomatic Complexity

76.5% of files maintain low complexity (0-10), while 23.5% venture into higher complexity territory.

Even with a focus on small files, nearly a quarter of the codebase still shows significant complexity – highlighting areas where maintainability might be challenging.

Analyzed Packages

411.6K

PHP packages from PHP ecosystem

Files

11.1M

Detected PHP files

Methods

53.4M

Analyzed PHP methods

Lines of Code

707.6M

Total lines of code
Including 362.9M logical lines of code

Logical lines of code distribution

Cyclomatic complexity distribution

Download Concentration: The Foundation of PHP

One of the most revealing aspects of the PHP ecosystem is the extreme concentration of downloads.

The data shows that just 136 packages (0.05 % of all packages) account for more than half of all monthly downloads in the entire ecosystem.

This concentration reflects several important dynamics in the PHP world:

Consolidated Foundation

The PHP ecosystem has matured to rely on a relatively small set of core libraries that provide essential functionality. This stable foundation means developers don't need to constantly evaluate competing implementations for basic needs.

Dependency Chain Effects

Many top packages are fundamental building blocks that get pulled in as transitive dependencies across thousands of projects. This cascading effect magnifies download counts for these foundational packages.

🎉 The Good News

The encouraging finding is that these high-impact packages are overwhelmingly well-maintained and healthy:

  • Nearly all top packages have active contributor communities
  • Most have robust test suites and CI/CD pipelines in place
  • Many are backed by companies or foundations ensuring long-term stability

Download Dominance

Packages that collectively represent 50% or more of monthly downloads

Number of dominant packages

136

% of total downloads

0.05

Monthly total downloads

2808.6M

Package Monthly downloads % Stars
symfony/deprecation-contracts 15.7M 0.55 2053.0
symfony/polyfill-mbstring 15.5M 0.55 7847.0
psr/log 15.2M 0.55 10421.0
psr/container 15.0M 0.55 9981.0
symfony/console 14.9M 0.55 9785.0
symfony/service-contracts 14.7M 0.5 2595.0
symfony/polyfill-intl-normalizer 14.6M 0.5 2026.0
psr/http-message 14.5M 0.5 6998.0
guzzlehttp/psr7 14.2M 0.5 7905.0
guzzlehttp/promises 14.1M 0.5 7645.0
symfony/process 14.1M 0.5 7453.0
symfony/polyfill-ctype 14.1M 0.5 4056.0
symfony/finder 14.0M 0.5 8436.0
guzzlehttp/guzzle 14.0M 0.5 23313.0
symfony/string 13.9M 0.5 1741.0
ralouphie/getallheaders 13.9M 0.5 3778.0
psr/http-factory 13.6M 0.5 1827.0
nikic/php-parser 13.6M 0.5 17179.0
symfony/polyfill-php80 13.6M 0.5 1731.0
symfony/polyfill-intl-grapheme 13.6M 0.5 1689.0
psr/http-client 13.3M 0.45 1685.0
symfony/event-dispatcher 13.3M 0.45 8534.0
monolog/monolog 13.1M 0.45 21132.0
symfony/event-dispatcher-contracts 13.1M 0.45 3394.0
symfony/var-dumper 13.0M 0.45 7426.0
myclabs/deep-copy 12.8M 0.45 8807.0
phpunit/phpunit 12.7M 0.45 19791.0
symfony/http-foundation 12.7M 0.45 8652.0
sebastian/diff 12.6M 0.45 7600.0
doctrine/lexer 12.6M 0.45 11102.0
symfony/translation-contracts 12.4M 0.45 2576.0
phpunit/php-code-coverage 12.3M 0.45 8863.0
symfony/polyfill-intl-idn 12.3M 0.45 3339.0
sebastian/comparator 12.3M 0.45 7024.0
sebastian/exporter 12.3M 0.45 6801.0
psr/event-dispatcher 12.2M 0.45 2209.0
phpunit/php-file-iterator 12.2M 0.45 7444.0
sebastian/recursion-context 12.2M 0.45 6556.0
sebastian/global-state 12.1M 0.45 6584.0
sebastian/version 12.1M 0.45 6557.0
phpunit/php-timer 12.1M 0.45 7684.0
sebastian/environment 12.1M 0.45 6752.0
webmozart/assert 12.1M 0.45 7588.0
phpunit/php-text-template 12.1M 0.45 7389.0
symfony/http-kernel 12.0M 0.45 8126.0
sebastian/object-enumerator 12.0M 0.45 6507.0
sebastian/code-unit-reverse-lookup 11.9M 0.4 6690.0
symfony/yaml 11.9M 0.4 3826.0
symfony/error-handler 11.9M 0.4 2623.0
theseer/tokenizer 11.9M 0.4 5177.0
phar-io/manifest 11.9M 0.4 7431.0
symfony/translation 11.9M 0.4 6620.0
sebastian/object-reflector 11.8M 0.4 6257.0
symfony/mime 11.8M 0.4 2794.0
phar-io/version 11.8M 0.4 7429.0
symfony/routing 11.8M 0.4 7623.0
psr/simple-cache 11.5M 0.4 8104.0
sebastian/type 11.5M 0.4 1434.0
doctrine/inflector 11.5M 0.4 11290.0
psr/cache 11.2M 0.4 5138.0
egulias/email-validator 11.2M 0.4 11517.0
sebastian/cli-parser 11.1M 0.4 1132.0
symfony/css-selector 11.1M 0.4 7440.0
sebastian/complexity 11.1M 0.4 1183.0
sebastian/lines-of-code 11.0M 0.4 1101.0
phpunit/php-invoker 11.0M 0.4 1256.0
ramsey/uuid 11.0M 0.4 12515.0
sebastian/code-unit 11.0M 0.4 1131.0
psr/clock 10.9M 0.4 526.0
doctrine/deprecations 10.7M 0.4 1707.0
brick/math 10.6M 0.4 1907.0
ramsey/collection 10.4M 0.35 1153.0
nesbot/carbon 10.1M 0.35 48.0
symfony/polyfill-php83 9.8M 0.35 357.0
league/flysystem 9.7M 0.35 13410.0
league/mime-type-detection 9.6M 0.35 1300.0
symfony/mailer 9.6M 0.35 1527.0
doctrine/instantiator 9.4M 0.35 10975.0
symfony/filesystem 9.2M 0.35 4621.0
vlucas/phpdotenv 9.0M 0.3 13265.0
phpoption/phpoption 8.9M 0.3 2644.0
psy/psysh 8.7M 0.3 9767.0
dragonmantank/cron-expression 8.6M 0.3 4594.0
nette/utils 8.5M 0.3 2034.0
carbonphp/carbon-doctrine-types 8.4M 0.3 156.0
tijsverkoyen/css-to-inline-styles 8.4M 0.3 5826.0
voku/portable-ascii 8.4M 0.3 555.0
composer/semver 8.4M 0.3 3195.0
fakerphp/faker 8.3M 0.3 3696.0
graham-campbell/result-type 8.3M 0.3 504.0
paragonie/random_compat 8.2M 0.3 8178.0
laravel/serializable-closure 8.2M 0.3 550.0
dflydev/dot-access-data 8.2M 0.3 666.0
symfony/options-resolver 8.1M 0.3 3222.0
phpdocumentor/type-resolver 8.1M 0.3 9161.0
phpstan/phpdoc-parser 8.0M 0.3 1398.0
symfony/polyfill-php81 8.0M 0.3 877.0
symfony/polyfill-uuid 8.0M 0.3 657.0
doctrine/dbal 8.0M 0.3 9554.0
phpdocumentor/reflection-common 7.9M 0.3 9045.0
league/commonmark 7.9M 0.3 2807.0
symfony/uid 7.9M 0.3 568.0
laravel/framework 7.9M 0.3 33237.0
nette/schema 7.8M 0.3 939.0
composer/pcre 7.8M 0.3 597.0
mockery/mockery 7.8M 0.3 10661.0
doctrine/event-manager 7.8M 0.3 5964.0
firebase/php-jwt 7.7M 0.3 9526.0
hamcrest/hamcrest-php 7.7M 0.3 6970.0
aws/aws-sdk-php 7.7M 0.3 6082.0
symfony/var-exporter 7.7M 0.25 2065.0
mtdowling/jmespath.php 7.7M 0.25 1954.0
sebastian/resource-operations 7.5M 0.25 6281.0
league/flysystem-local 7.5M 0.25 180.0
laravel/tinker 7.4M 0.25 7363.0
phpdocumentor/reflection-docblock 7.4M 0.25 9360.0
league/config 7.4M 0.25 519.0
doctrine/cache 7.4M 0.25 7868.0
paragonie/constant_time_encoding 7.1M 0.25 836.0
phpseclib/phpseclib 7.1M 0.25 5434.0
aws/aws-crt-php 7.1M 0.25 367.0
filp/whoops 7.0M 0.25 13218.0
phpstan/phpstan 7.0M 0.25 13240.0
guzzlehttp/uri-template 6.9M 0.25 154.0
nunomaduro/termwind 6.9M 0.25 2377.0
symfony/config 6.7M 0.25 4248.0
laravel/prompts 6.7M 0.25 562.0
fruitcake/php-cors 6.7M 0.25 261.0
symfony/dependency-injection 6.7M 0.25 4130.0
symfony/http-client-contracts 6.6M 0.25 1952.0
symfony/polyfill-php73 6.6M 0.25 2399.0
symfony/clock 6.5M 0.25 353.0
league/uri-interfaces 6.5M 0.25 482.0
symfony/cache 6.5M 0.25 4125.0
league/uri 6.5M 0.25 1060.0
doctrine/annotations 6.4M 0.25 6742.0

The Invisible Infrastructure: High-Impact, Low-Recognition Packages

While analyzing the relationship between GitHub stars and download counts, we discovered a fascinating subset of PHP packages: those with massive download numbers but surprisingly few stars. These packages form what might be called the "invisible infrastructure" of PHP—critically important components that operate silently beneath the surface of countless applications.

This "recognition gap" has several important implications:

  • Packages with high impact but low visibility might struggle to attract contributors
  • Critical dependencies may be maintained by small teams despite their outsized ecosystem importance
  • However, many represent opportunities for developers looking to make meaningful contributions to the PHP ecosystem

Popular packages

In contrast, these packages are both very popular and widely used. They are the cornerstone of PHP.

bubble size proportional to GitHub forks

Packagist Ecosystem Health Status

Analysis of PHP packages reveals significant health concerns in the Packagist ecosystem: 70% show minimal activity with few downloads and GitHub interactions, while 23% appear abandoned with no recent updates. With an average of just 60 stars per package (heavily skewed by popular outliers), these metrics highlight the typical "long tail" distribution in open source ecosystems. This data emphasizes the importance of carefully evaluating dependency health through metrics like bus factor, update frequency, and community engagement when integrating packages into production projects.

Empty Packages

94.9K

Abandonned packages

Shadow packages

288.5K

Packages with few downloads, few GitHub stars or forks

Average Popularity

60

Average stars per package
Excluding non-starred packages

But What About Quality?

Despite PHP's occasionally maligned reputation, the data tells a different story. PHP packages show surprisingly clean code that's easy to maintain and understand. Functions are refreshingly short, different parts of the code play well together, and packages stay reasonably sized. Turns out PHP developers aren't writing spaghetti code after all - they're crafting well-structured dishes that would make even the pickiest code chefs nod in approval.

Complexity Distribution

Average Cyclomatic

8.8

Median Cyclomatic

4.0

Code Quality

Maintainability Index

108

Lines per Method

3.0

Packages with Most Lines of Code

If you are curious about the packages with the most lines of code, you can find the top 100 packages below. Note that the biggest packages concerns geographical data, and embed a lot of data in their code.

What Else Is Hiding in PHP Repositories?

Analyzing PHP on Packagist is one thing, but what about everything around PHP? The reality is, modern PHP projects rely on a diverse tech stack.
🔍 Here’s what I found inside the repositories:
  • PHP dominates (707M+ lines of code), but it’s far from alone.
  • JavaScript is the most common companion (216M+ lines), proving that front-end and back-end integration is key.
  • JSON & YAML are everywhere (200M+ lines combined), highlighting the rise of configuration-driven architectures.
  • CSS & HTML (167M+ lines total) showcase the full-stack nature of many PHP applications.
  • SQL, XML, and Markdown reflect the importance of structured data and documentation in modern PHP projects.
💡 Surprising finds:
  • TypeScript is growing (2.7M lines) – are PHP projects becoming more structured in the front end?
  • C, Go, and Rust appear in small amounts – a sign of performance-critical extensions?
  • Even Lua, Perl, and Scheme pop up – legacy integrations or scripting tools within PHP projects?

👉 Modern PHP is more than just PHP. It’s an ecosystem that blends with various technologies, making cross-language skills increasingly valuable for PHP developers.

Repartition of lines of code by language (top 10)

What's Next?

Now that most of the process is scripted, I still have some code cleanup to tackle, but I think it would be valuable to run this analysis at least twice a year to keep an up-to-date view of the PHP ecosystem. So far, I've focused exclusively on Packagist, but it's not the only package manager out there - adding new code sources would provide a more comprehensive picture.

I'm also considering implementing more advanced code metrics to deepen the analysis. The current metrics give us a good foundation, but there's always more we could learn about code quality and ecosystem health.

If you have ideas for additional metrics or features that would enhance this dashboard, please reach out - your insights could help shape the future direction of this project!

blog comments powered by Disqus

© Jean-François Lépine, 2013 - 2025