# The fixed cost of installing a PHP framework: disk size tells you nothing

> The same seven candidates, the same environment. No load this time: package counts, vendor size, files loaded per request, peak memory, and the first-request bill paid while opcache is cold.

- Kind: Measurement
- Question: 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.
- 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: 2026-08-20
- Confidence: High confidence
- Programme: Tooling comparison
- 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: Laravel, Symfony, CodeIgniter, Yii2, Phalcon, Laminas, Slim, PHP, Composer, Docker
- Raw data: https://github.com/muhammetsafak/php-framework-bench/blob/main/results/2026-08-20/static.json
- Published: 2026-08-20
- Source: https://www.muhammetsafak.com.tr/en/research/php-framework-ayak-izi/
- Language: en-US
- Author: Muhammet Şafak

---
The [load measurement](/en/research/php-framework-yuk-testi) 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 | 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 | 25 | 34.1 MB | 2,927 | 62 | 815 kB | 0.9 s |

Ordered by vendor size. The last two columns do not move together: Yii2 writes the largest tree to disk and opens the fewest files in the field per request.

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.

Kaynak: get_included_files() on a warm container, single request

|  | Phalcon | Yii2 | Slim | CodeIgniter | Symfony | Laminas | Laravel |
| --- | --- | --- | --- | --- | --- | --- | --- |
| files loaded | 7 | 62 | 79 | 96 | 224 | 269 | 359 |

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.

Kaynak: Fresh container, first HTTP request, front controller to response

|  | 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 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× |

The same request, the same container. The only difference is whether opcache is populated.

> **Sonuç**
>
> If you want to measure how "heavy" a framework is, the number to look at is not
> the size of the vendor directory but the first request served with a cold
> opcache. For Laravel that is 72 milliseconds; for Phalcon, 2. Those 70 ms are
> paid again after every deploy and every opcache reset — and not once, but
> separately for each php-fpm worker.

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

1. **Phalcon's `composer install` takes 0.2 s because there is nothing to
   install.** The project has 12 files and its `composer.lock` carries an empty
   package list; the framework is a C extension compiled into the PHP binary. The
   trade is on the other side: upgrading is not `composer update`, it is
   compiling an extension on the server.
2. **`ext-psr` forced 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.
3. **`ext-mysqli` is 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.
4. **Laminas's official skeleton has not been released for PHP 8.4.**
   `laminas-mvc-skeleton` 2.4.0 declares `~8.1.0 || ~8.2.0 || ~8.3.0` and 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.
5. **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.
6. **`composer install` times 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.
7. **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.
8. **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.
