The fixed cost of installing a PHP framework: disk size tells you nothing
How many megabytes on disk, how many files per request and how many milliseconds on the first request do seven PHP frameworks cost — and which of those numbers actually predicts throughput under load?
Finding
Disk size predicts nothing: Yii2 has the largest vendor tree at 34.1 MB and loads only 62 files per request, among the fewest in the field. Files per request predicts nothing either: CodeIgniter loads 96 files and serves 6,431 req/s, Symfony loads 224 and serves 13,067. The one number that genuinely separates them is the first request served with a cold opcache: 2.2 ms for Phalcon, 72.2 ms for Laravel — thirty-three times. That is the compile bill the first visitor pays after every deploy, and it is measured in tens of milliseconds, not kilobytes.
- Cold first request, slowest
- 72.2 ms
- Cold first request, fastest
- 2.2 ms
- Largest vendor tree
- 34.1 MB
- Most files per request
- 359
Method
The same images, the same PHP 8.4.24 build and the same production set-up as the load measurement. For each candidate a fresh container was started and the following recorded: the package count in composer.lock, `du -sb vendor`, `find vendor -type f | wc -l`, the duration of the **first** request the fresh container serves (opcache empty — cold), the duration of the same request after a 5-second warm-up (warm), and on that request `get_included_files()` and `memory_get_peak_usage(false)`. Boot time is measured from the first line of the front controller to the point the response is produced. `composer install` time was taken as the median of three runs in a container with a pre-warmed package cache, using `--no-dev --optimize-autoloader --classmap-authoritative`, so it measures installation work rather than download speed. Every counter here is deterministic and reproduces exactly; installation time is the only noisy column.
- Measured on
- Published
measured 2 days ago
Environment
- PHP
- 8.4.24 NTS · opcache on · JIT off
- opcache
- validate_timestamps=0 · 256 MB · no preload
- Composer
- 2.x · --no-dev --optimize-autoloader --classmap-authoritative
- Server
- nginx 1.29.1 + php-fpm · pm=static · 8 workers
- Hardware
- Apple M4 Pro · 12 cores · 24 GB · macOS 26.6.1
- Virtualisation
- Docker Desktop 29.7.2 · 12 vCPU · aarch64
Technologies
To reproduce
STAMP=$(date -u +%F) ./bench/static-metrics.sh The load measurement says how many requests a second each framework serves. This record chases a quieter question: what does installing that framework cost you when nothing is happening? How much disk does it occupy, how many files does it open per request, and what does the first visitor after a deploy pay?
The practical version of the question is: which of these numbers can you look at to decide that a framework is “heavy”? The answer turned out to be shorter than I expected.
Size on disk predicts nothing
| Candidate | Packages | vendor | Files | Loaded per request | Peak memory | composer install |
|---|---|---|---|---|---|---|
| Phalcon a C extension — nothing to install | 0 | 0 MB | 12 | 7 | 502 kB | 0.2 s |
| Slim | 12 | 0.9 MB | 229 | 79 | 398 kB | 0.5 s |
| CodeIgniter | 3 | 4.6 MB | 781 | 96 | 535 kB | 0.6 s |
| Laminas | 23 | 7.7 MB | 1,362 | 269 | 607 kB | 0.6 s |
| Symfony | 51 | 15.8 MB | 3,362 | 224 | 517 kB | 0.9 s |
| Laravel | 76 | 29.6 MB | 6,447 | 359 | 703 kB | 1.2 s |
| Yii2 largest vendor, fewest files loaded | 25 | 34.1 MB | 2,927 | 62 | 815 kB | 0.9 s |
The most instructive row is Yii2. Its 34.1 MB vendor tree is the largest here — larger than Laravel’s — and yet it loads only 62 files to serve a request. Laravel loads 359, Laminas 269, Symfony 224. Most of what Yii2 writes to disk never sits in the path of a request.
The reverse holds too: CodeIgniter ships with just 3 composer packages, the leanest dependency tree in the field. That leanness does not turn into speed.
Files per request does not predict it either
Files loaded while serving one request
Intuition says fewer files means faster. CodeIgniter serves 6,431 req/s on 96 files; Symfony serves 13,067 on 224 — twice the files, twice the requests.
lower is better Source: get_included_files() on a warm container, single request
Data table
| Series | Phalcon | Yii2 | Slim | CodeIgniter | Symfony | Laminas | Laravel |
|---|---|---|---|---|---|---|---|
| files loaded | 7 | 62 | 79 | 96 | 224 | 269 | 359 |
The chart is drawn in the browser; the table below carries the same data.
Put the load figures next to this table and the relationship falls apart. CodeIgniter loads 96 files per request and serves 6,431 req/s; Symfony loads 224 and serves 13,067. The framework opening more than twice as many files serves more than twice as many requests.
The reason is opcache. With validate_timestamps=0 a file is compiled once and
stays in shared memory, so on later requests “loading” it means binding to ready
opcode rather than reading from disk. File count is therefore cheap on the
warm path. To see where it is expensive, you have to empty the opcache.
Where the difference actually is: the cold first request
Duration of the first request a fresh container serves (opcache empty)
The compile bill the first visitor pays after every deploy. This is what Phalcon does not pay: the framework is already inside the PHP binary.
ms lower is better Source: Fresh container, first HTTP request, front controller to response
Data table
| Series | Phalcon | Slim | Yii2 | CodeIgniter | Laminas | Symfony | Laravel |
|---|---|---|---|---|---|---|---|
| cold first request | 2.2 ms | 7.9 ms | 17.4 ms | 22.2 ms | 27.7 ms | 35.7 ms | 72.2 ms |
The chart is drawn in the browser; the table below carries the same data.
The ratio between cold and warm gives the compile bill directly:
| Candidate | Cold first request | Warm request | Ratio |
|---|---|---|---|
| Phalcon | 2.2 ms | 0.41 ms | 5× |
| Slim | 7.9 ms | 0.35 ms | 23× |
| Yii2 | 17.4 ms | 0.45 ms | 39× |
| CodeIgniter | 22.2 ms | 1.04 ms | 21× |
| Laminas | 27.7 ms | 0.79 ms | 35× |
| Symfony | 35.7 ms | 0.69 ms | 52× |
| Laravel | 72.2 ms | 1.26 ms | 57× |
In a pool of eight workers that means close to half a second of accumulated latency across the first eight requests after a Laravel deploy. On a busy site it shows up as a brief spike and disappears; on a page that gets a handful of visitors a day, the first visitor pays it every time.
Peak memory: the order nobody expects
The most inverted result in the table is memory. Laravel, which loads the most files per request (359), peaks at 703 kB; Yii2, which loads the fewest (62), is the highest in the field at 815 kB. File count misleads here too: memory depends on how many objects those files construct, not on how many were opened.
Slim is lowest at 398 kB, as expected from how few objects it builds. Phalcon’s 502 kB is the interesting one: it runs almost no PHP code, yet still constructs its objects in PHP’s memory space.
Findings that did not fit the table
- Phalcon’s
composer installtakes 0.2 s because there is nothing to install. The project has 12 files and itscomposer.lockcarries an empty package list; the framework is a C extension compiled into the PHP binary. The trade is on the other side: upgrading is notcomposer update, it is compiling an extension on the server. ext-psrforced the shared image to fork. Phalcon requires it, and with it loaded Symfony is fatal on PHP 8.4. Phalcon runs on its own runtime image; the PHP version, opcache settings, nginx and php-fpm pool are byte-identical, and only those two extensions differ.ext-mysqliis installed for all seven because CodeIgniter 4 has no PDO driver. The other six never use it; it is there so the extension set stays identical.- Laminas’s official skeleton has not been released for PHP 8.4.
laminas-mvc-skeleton2.4.0 declares~8.1.0 || ~8.2.0 || ~8.3.0and refuses to install without--ignore-platform-req=php. So the 23 packages and 1,362 files in the table were installed on a version that is not officially supported. - Most of Yii2’s 34.1 MB is bower and npm assets. The basic skeleton pulls jQuery and Bootstrap into the vendor tree; none of it is used by the routes in this measurement.
composer installtimes were measured with a warm package cache. On a cold cache the figures would track network speed and say nothing about the framework. The median of three runs is reported; this is the only noisy column in the table.- opcache preload was not enabled for any candidate. CodeIgniter ships a
preload.php, and there are well-known recipes for Laravel and Symfony. Enabling it changes the cold-first-request figures fundamentally — but it is not a floor all seven share, so it was left out of scope. It deserves its own measurement. - Image sizes do not separate the frameworks. They range from 486 to 518 MB and nearly all of that is the shared PHP base image; the vendor tree is small beside it. That is why the column is not in the table, though it is in the raw data.
Related posts
Building Applications with PHP in 2026: State of the Ecosystem
The real state of PHP in 2026: language maturity, ecosystem health, and why the 'it's dead' narrative is still wrong.
Is PHP Dead? The 2026 Picture, by the Numbers
A view from 18 years of PHP development — the 2026 state of PHP through W3Techs, Stack Overflow, JetBrains, and Packagist data, including the language's often-ignored weaknesses.