From d2cc23a9d39179a9c127fde7a89fe142ed724ecc Mon Sep 17 00:00:00 2001 From: Nigro Simone Date: Fri, 14 Aug 2026 08:02:32 +0200 Subject: [PATCH 1/2] Add laravel framework --- frameworks/laravel/Dockerfile | 26 +++++++++++++++ frameworks/laravel/README.md | 27 +++++++++++++++ frameworks/laravel/artisan | 14 ++++++++ frameworks/laravel/bootstrap/app.php | 19 +++++++++++ frameworks/laravel/composer.json | 20 +++++++++++ frameworks/laravel/meta.json | 19 +++++++++++ frameworks/laravel/php.ini | 6 ++++ frameworks/laravel/public/index.php | 11 ++++++ frameworks/laravel/routes/api.php | 50 ++++++++++++++++++++++++++++ 9 files changed, 192 insertions(+) create mode 100644 frameworks/laravel/Dockerfile create mode 100644 frameworks/laravel/README.md create mode 100644 frameworks/laravel/artisan create mode 100644 frameworks/laravel/bootstrap/app.php create mode 100644 frameworks/laravel/composer.json create mode 100644 frameworks/laravel/meta.json create mode 100644 frameworks/laravel/php.ini create mode 100644 frameworks/laravel/public/index.php create mode 100644 frameworks/laravel/routes/api.php diff --git a/frameworks/laravel/Dockerfile b/frameworks/laravel/Dockerfile new file mode 100644 index 000000000..106cb394b --- /dev/null +++ b/frameworks/laravel/Dockerfile @@ -0,0 +1,26 @@ +FROM dunglas/frankenphp:latest + +COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer + +RUN install-php-extensions pcntl opcache zip + +WORKDIR /app + +COPY composer.json ./ +RUN composer install --no-dev --optimize-autoloader --no-interaction --no-scripts + +COPY . . +RUN mkdir -p storage/framework/cache storage/framework/sessions storage/framework/views storage/logs bootstrap/cache + +# post_max_size unlimited so the 20 MB upload profile is not rejected, opcache +# with timestamp validation off as the Laravel deployment guide recommends. +COPY php.ini $PHP_INI_DIR/conf.d/99-httparena.ini + +ENV APP_ENV=production \ + APP_DEBUG=false \ + APP_KEY=base64:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= \ + LOG_CHANNEL=null \ + SERVER_NAME=:8080 + +EXPOSE 8080 +CMD ["php", "artisan", "octane:start", "--server=frankenphp", "--host=0.0.0.0", "--port=8080", "--admin-port=2019"] diff --git a/frameworks/laravel/README.md b/frameworks/laravel/README.md new file mode 100644 index 000000000..f12a93d2f --- /dev/null +++ b/frameworks/laravel/README.md @@ -0,0 +1,27 @@ +# laravel + +Laravel 13 served by Laravel Octane on FrankenPHP. + +## Stack + +- **Language:** PHP 8.5 +- **Framework:** Laravel 13 +- **Server:** Laravel Octane on FrankenPHP, one worker per core +- **Build:** `dunglas/frankenphp` + +## Endpoints + +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/pipeline` | GET | Returns `ok` (plain text) | +| `/baseline11` | GET | Sums query parameter values | +| `/baseline11` | POST | Sums query parameters + request body | +| `/json/{count}?m=N` | GET | First `count` dataset items with `total = price * quantity * m` | +| `/upload` | POST | Reads the body stream and returns the byte count | + +## Notes + +- Routes are registered through the `api` group, so no session, no cookies and no CSRF token +- Octane is the runtime Laravel documents for production, so the framework boots once per worker +- Compression comes from the Caddy `encode` directive in the Caddyfile Octane generates +- `post_max_size=0` because PHP otherwise rejects the 20 MB upload profile with 413 diff --git a/frameworks/laravel/artisan b/frameworks/laravel/artisan new file mode 100644 index 000000000..c95b6a9e3 --- /dev/null +++ b/frameworks/laravel/artisan @@ -0,0 +1,14 @@ +#!/usr/bin/env php +handleCommand(new ArgvInput)); diff --git a/frameworks/laravel/bootstrap/app.php b/frameworks/laravel/bootstrap/app.php new file mode 100644 index 000000000..03126a191 --- /dev/null +++ b/frameworks/laravel/bootstrap/app.php @@ -0,0 +1,19 @@ +withRouting( + api: __DIR__.'/../routes/api.php', + apiPrefix: '', + ) + ->withMiddleware(function (Middleware $middleware) { + // + }) + ->withExceptions(function (Exceptions $exceptions) { + // + })->create(); diff --git a/frameworks/laravel/composer.json b/frameworks/laravel/composer.json new file mode 100644 index 000000000..f9bc3ccd1 --- /dev/null +++ b/frameworks/laravel/composer.json @@ -0,0 +1,20 @@ +{ + "type": "project", + "license": "MIT", + "require": { + "php": "^8.3", + "laravel/framework": "^13.0", + "laravel/octane": "^2.19" + }, + "autoload": { + "psr-4": { + "App\\": "app/" + } + }, + "config": { + "optimize-autoloader": true, + "sort-packages": true + }, + "minimum-stability": "stable", + "prefer-stable": true +} diff --git a/frameworks/laravel/meta.json b/frameworks/laravel/meta.json new file mode 100644 index 000000000..c7675ad1d --- /dev/null +++ b/frameworks/laravel/meta.json @@ -0,0 +1,19 @@ +{ + "display_name": "laravel", + "language": "PHP", + "type": "flagship", + "mode": "standard", + "engine": "frankenphp", + "description": "Laravel 13 served by Laravel Octane on FrankenPHP, the first-party production runtime documented by Laravel. Routing and request handling through the Laravel API, responses via the response()/json() helpers, gzip and brotli through the Caddy encode directive Octane generates.", + "repo": "https://github.com/laravel/laravel", + "enabled": true, + "tests": [ + "baseline", + "pipelined", + "limited-conn", + "json", + "json-comp", + "upload" + ], + "maintainers": [] +} diff --git a/frameworks/laravel/php.ini b/frameworks/laravel/php.ini new file mode 100644 index 000000000..155737320 --- /dev/null +++ b/frameworks/laravel/php.ini @@ -0,0 +1,6 @@ +post_max_size=0 +memory_limit=512M +opcache.enable=1 +opcache.memory_consumption=256 +opcache.max_accelerated_files=20000 +opcache.validate_timestamps=0 diff --git a/frameworks/laravel/public/index.php b/frameworks/laravel/public/index.php new file mode 100644 index 000000000..b05dee5f6 --- /dev/null +++ b/frameworks/laravel/public/index.php @@ -0,0 +1,11 @@ +handleRequest(Request::capture()); diff --git a/frameworks/laravel/routes/api.php b/frameworks/laravel/routes/api.php new file mode 100644 index 000000000..a2603d6b8 --- /dev/null +++ b/frameworks/laravel/routes/api.php @@ -0,0 +1,50 @@ +header('Content-Type', 'text/plain'); +}); + +Route::match(['get', 'post'], '/baseline11', function (Request $request) { + $total = 0; + foreach ($request->query() as $value) { + if (is_numeric($value)) { + $total += (int) $value; + } + } + if ($request->isMethod('post')) { + $body = trim($request->getContent()); + if (is_numeric($body)) { + $total += (int) $body; + } + } + + return response((string) $total)->header('Content-Type', 'text/plain'); +}); + +Route::get('/json/{count}', function (Request $request, int $count) use ($dataset) { + $count = max(0, min($count, count($dataset))); + $m = (int) $request->query('m', 1) ?: 1; + + $items = []; + foreach (array_slice($dataset, 0, $count) as $item) { + $item['total'] = $item['price'] * $item['quantity'] * $m; + $items[] = $item; + } + + return response()->json(['items' => $items, 'count' => count($items)]); +}); + +Route::post('/upload', function (Request $request) { + $size = 0; + $stream = $request->getContent(true); + while (! feof($stream)) { + $size += strlen(fread($stream, 262144)); + } + + return response((string) $size)->header('Content-Type', 'text/plain'); +}); From f9846263e031bbae628c5906a6fb9a8acbb4f6a7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 14 Aug 2026 06:41:37 +0000 Subject: [PATCH 2/2] Benchmark results: laravel [skip ci] --- site/data/frameworks.json | 8 + site/data/results/laravel.json | 243 ++++++++++++++++++ site/static/logs/baseline/4096/laravel.log | 12 + site/static/logs/baseline/512/laravel.log | 12 + site/static/logs/json-comp/16384/laravel.log | 12 + site/static/logs/json-comp/4096/laravel.log | 12 + site/static/logs/json-comp/512/laravel.log | 12 + site/static/logs/json/4096/laravel.log | 12 + .../static/logs/limited-conn/4096/laravel.log | 12 + site/static/logs/limited-conn/512/laravel.log | 12 + site/static/logs/pipelined/4096/laravel.log | 12 + site/static/logs/pipelined/512/laravel.log | 12 + site/static/logs/upload/256/laravel.log | 12 + site/static/logs/upload/32/laravel.log | 12 + 14 files changed, 395 insertions(+) create mode 100644 site/data/results/laravel.json create mode 100644 site/static/logs/baseline/4096/laravel.log create mode 100644 site/static/logs/baseline/512/laravel.log create mode 100644 site/static/logs/json-comp/16384/laravel.log create mode 100644 site/static/logs/json-comp/4096/laravel.log create mode 100644 site/static/logs/json-comp/512/laravel.log create mode 100644 site/static/logs/json/4096/laravel.log create mode 100644 site/static/logs/limited-conn/4096/laravel.log create mode 100644 site/static/logs/limited-conn/512/laravel.log create mode 100644 site/static/logs/pipelined/4096/laravel.log create mode 100644 site/static/logs/pipelined/512/laravel.log create mode 100644 site/static/logs/upload/256/laravel.log create mode 100644 site/static/logs/upload/32/laravel.log diff --git a/site/data/frameworks.json b/site/data/frameworks.json index 7f879a980..5b26589ea 100644 --- a/site/data/frameworks.json +++ b/site/data/frameworks.json @@ -525,6 +525,14 @@ "engine": "netty", "mode": "standard" }, + "laravel": { + "dir": "laravel", + "description": "Laravel 13 served by Laravel Octane on FrankenPHP, the first-party production runtime documented by Laravel. Routing and request handling through the Laravel API, responses via the response()/json() helpers, gzip and brotli through the Caddy encode directive Octane generates.", + "repo": "https://github.com/laravel/laravel", + "type": "flagship", + "engine": "frankenphp", + "mode": "standard" + }, "libreactorng": { "dir": "libreactorng", "description": "libreactorng \u2014 Fredrik Widlund's io_uring-native event framework, the successor to the long-running epoll-based libreactor. Built directly on Linux io_uring syscalls with zero third-party runtime deps. Minimal server dispatches /pipeline, /baseline11, /baseline2 via the built-in HTTP parser; one reactor process per logical CPU via SO_REUSEPORT.", diff --git a/site/data/results/laravel.json b/site/data/results/laravel.json new file mode 100644 index 000000000..d8e4d7873 --- /dev/null +++ b/site/data/results/laravel.json @@ -0,0 +1,243 @@ +{ + "framework": "laravel", + "results": { + "baseline-4096": { + "framework": "laravel", + "language": "PHP", + "rps": 29496, + "avg_latency": "136.35ms", + "p99_latency": "393.90ms", + "cpu": "5917.4%", + "memory": "686MiB", + "connections": 4096, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "5.71MB/s", + "input_bw": "2.28MB/s", + "reconnects": 0, + "status_2xx": 147484, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "baseline-512": { + "framework": "laravel", + "language": "PHP", + "rps": 28454, + "avg_latency": "17.94ms", + "p99_latency": "115.00ms", + "cpu": "5742.0%", + "memory": "589MiB", + "connections": 512, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "5.51MB/s", + "input_bw": "2.20MB/s", + "reconnects": 0, + "status_2xx": 142274, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "json-4096": { + "framework": "laravel", + "language": "PHP", + "rps": 28891, + "avg_latency": "137.83ms", + "p99_latency": "417.10ms", + "cpu": "5770.6%", + "memory": "745MiB", + "connections": 4096, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "112.09MB/s", + "input_bw": "1.38MB/s", + "reconnects": 3768, + "status_2xx": 144458, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "json-comp-16384": { + "framework": "laravel", + "language": "PHP", + "rps": 25399, + "avg_latency": "577.81ms", + "p99_latency": "1.36s", + "cpu": "6099.7%", + "memory": "2.2GiB", + "connections": 16384, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "34.71MB/s", + "input_bw": "1.89MB/s", + "reconnects": 0, + "status_2xx": 126996, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "json-comp-4096": { + "framework": "laravel", + "language": "PHP", + "rps": 25824, + "avg_latency": "154.51ms", + "p99_latency": "825.50ms", + "cpu": "6065.3%", + "memory": "1.9GiB", + "connections": 4096, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "33.15MB/s", + "input_bw": "1.92MB/s", + "reconnects": 3054, + "status_2xx": 129121, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "json-comp-512": { + "framework": "laravel", + "language": "PHP", + "rps": 24871, + "avg_latency": "20.55ms", + "p99_latency": "139.30ms", + "cpu": "5849.8%", + "memory": "966MiB", + "connections": 512, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "32.34MB/s", + "input_bw": "1.85MB/s", + "reconnects": 4819, + "status_2xx": 124359, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "limited-conn-4096": { + "framework": "laravel", + "language": "PHP", + "rps": 29531, + "avg_latency": "135.87ms", + "p99_latency": "320.40ms", + "cpu": "5929.1%", + "memory": "749MiB", + "connections": 4096, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "5.71MB/s", + "input_bw": "2.28MB/s", + "reconnects": 12858, + "status_2xx": 147655, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "limited-conn-512": { + "framework": "laravel", + "language": "PHP", + "rps": 27813, + "avg_latency": "18.39ms", + "p99_latency": "117.50ms", + "cpu": "5728.5%", + "memory": "495MiB", + "connections": 512, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "5.38MB/s", + "input_bw": "2.15MB/s", + "reconnects": 13853, + "status_2xx": 139066, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "pipelined-4096": { + "framework": "laravel", + "language": "PHP", + "rps": 32207, + "avg_latency": "1.63s", + "p99_latency": "2.15s", + "cpu": "5745.5%", + "memory": "814MiB", + "connections": 4096, + "threads": 64, + "duration": "5s", + "pipeline": 16, + "bandwidth": "6.23MB/s", + "reconnects": 0, + "status_2xx": 161035, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "pipelined-512": { + "framework": "laravel", + "language": "PHP", + "rps": 31532, + "avg_latency": "253.53ms", + "p99_latency": "685.20ms", + "cpu": "5941.6%", + "memory": "502MiB", + "connections": 512, + "threads": 64, + "duration": "5s", + "pipeline": 16, + "bandwidth": "6.10MB/s", + "reconnects": 0, + "status_2xx": 157661, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "upload-256": { + "framework": "laravel", + "language": "PHP", + "rps": 1041, + "avg_latency": "244.44ms", + "p99_latency": "540.10ms", + "cpu": "6064.9%", + "memory": "654MiB", + "connections": 256, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "211.60KB/s", + "input_bw": "8.26GB/s", + "reconnects": 969, + "status_2xx": 5205, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "upload-32": { + "framework": "laravel", + "language": "PHP", + "rps": 949, + "avg_latency": "33.60ms", + "p99_latency": "85.70ms", + "cpu": "3260.0%", + "memory": "583MiB", + "connections": 32, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "193.02KB/s", + "input_bw": "7.53GB/s", + "reconnects": 944, + "status_2xx": 4747, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + } + } +} diff --git a/site/static/logs/baseline/4096/laravel.log b/site/static/logs/baseline/4096/laravel.log new file mode 100644 index 000000000..54019cd9d --- /dev/null +++ b/site/static/logs/baseline/4096/laravel.log @@ -0,0 +1,12 @@ + + INFO Server running…. + + + Local: http://0.0.0.0:8080 + + Press Ctrl+C to stop the server + + WARN HTTP/2 skipped because it requires TLS. + + WARN HTTP/3 skipped because it requires TLS. + diff --git a/site/static/logs/baseline/512/laravel.log b/site/static/logs/baseline/512/laravel.log new file mode 100644 index 000000000..54019cd9d --- /dev/null +++ b/site/static/logs/baseline/512/laravel.log @@ -0,0 +1,12 @@ + + INFO Server running…. + + + Local: http://0.0.0.0:8080 + + Press Ctrl+C to stop the server + + WARN HTTP/2 skipped because it requires TLS. + + WARN HTTP/3 skipped because it requires TLS. + diff --git a/site/static/logs/json-comp/16384/laravel.log b/site/static/logs/json-comp/16384/laravel.log new file mode 100644 index 000000000..54019cd9d --- /dev/null +++ b/site/static/logs/json-comp/16384/laravel.log @@ -0,0 +1,12 @@ + + INFO Server running…. + + + Local: http://0.0.0.0:8080 + + Press Ctrl+C to stop the server + + WARN HTTP/2 skipped because it requires TLS. + + WARN HTTP/3 skipped because it requires TLS. + diff --git a/site/static/logs/json-comp/4096/laravel.log b/site/static/logs/json-comp/4096/laravel.log new file mode 100644 index 000000000..54019cd9d --- /dev/null +++ b/site/static/logs/json-comp/4096/laravel.log @@ -0,0 +1,12 @@ + + INFO Server running…. + + + Local: http://0.0.0.0:8080 + + Press Ctrl+C to stop the server + + WARN HTTP/2 skipped because it requires TLS. + + WARN HTTP/3 skipped because it requires TLS. + diff --git a/site/static/logs/json-comp/512/laravel.log b/site/static/logs/json-comp/512/laravel.log new file mode 100644 index 000000000..54019cd9d --- /dev/null +++ b/site/static/logs/json-comp/512/laravel.log @@ -0,0 +1,12 @@ + + INFO Server running…. + + + Local: http://0.0.0.0:8080 + + Press Ctrl+C to stop the server + + WARN HTTP/2 skipped because it requires TLS. + + WARN HTTP/3 skipped because it requires TLS. + diff --git a/site/static/logs/json/4096/laravel.log b/site/static/logs/json/4096/laravel.log new file mode 100644 index 000000000..54019cd9d --- /dev/null +++ b/site/static/logs/json/4096/laravel.log @@ -0,0 +1,12 @@ + + INFO Server running…. + + + Local: http://0.0.0.0:8080 + + Press Ctrl+C to stop the server + + WARN HTTP/2 skipped because it requires TLS. + + WARN HTTP/3 skipped because it requires TLS. + diff --git a/site/static/logs/limited-conn/4096/laravel.log b/site/static/logs/limited-conn/4096/laravel.log new file mode 100644 index 000000000..54019cd9d --- /dev/null +++ b/site/static/logs/limited-conn/4096/laravel.log @@ -0,0 +1,12 @@ + + INFO Server running…. + + + Local: http://0.0.0.0:8080 + + Press Ctrl+C to stop the server + + WARN HTTP/2 skipped because it requires TLS. + + WARN HTTP/3 skipped because it requires TLS. + diff --git a/site/static/logs/limited-conn/512/laravel.log b/site/static/logs/limited-conn/512/laravel.log new file mode 100644 index 000000000..54019cd9d --- /dev/null +++ b/site/static/logs/limited-conn/512/laravel.log @@ -0,0 +1,12 @@ + + INFO Server running…. + + + Local: http://0.0.0.0:8080 + + Press Ctrl+C to stop the server + + WARN HTTP/2 skipped because it requires TLS. + + WARN HTTP/3 skipped because it requires TLS. + diff --git a/site/static/logs/pipelined/4096/laravel.log b/site/static/logs/pipelined/4096/laravel.log new file mode 100644 index 000000000..54019cd9d --- /dev/null +++ b/site/static/logs/pipelined/4096/laravel.log @@ -0,0 +1,12 @@ + + INFO Server running…. + + + Local: http://0.0.0.0:8080 + + Press Ctrl+C to stop the server + + WARN HTTP/2 skipped because it requires TLS. + + WARN HTTP/3 skipped because it requires TLS. + diff --git a/site/static/logs/pipelined/512/laravel.log b/site/static/logs/pipelined/512/laravel.log new file mode 100644 index 000000000..54019cd9d --- /dev/null +++ b/site/static/logs/pipelined/512/laravel.log @@ -0,0 +1,12 @@ + + INFO Server running…. + + + Local: http://0.0.0.0:8080 + + Press Ctrl+C to stop the server + + WARN HTTP/2 skipped because it requires TLS. + + WARN HTTP/3 skipped because it requires TLS. + diff --git a/site/static/logs/upload/256/laravel.log b/site/static/logs/upload/256/laravel.log new file mode 100644 index 000000000..54019cd9d --- /dev/null +++ b/site/static/logs/upload/256/laravel.log @@ -0,0 +1,12 @@ + + INFO Server running…. + + + Local: http://0.0.0.0:8080 + + Press Ctrl+C to stop the server + + WARN HTTP/2 skipped because it requires TLS. + + WARN HTTP/3 skipped because it requires TLS. + diff --git a/site/static/logs/upload/32/laravel.log b/site/static/logs/upload/32/laravel.log new file mode 100644 index 000000000..54019cd9d --- /dev/null +++ b/site/static/logs/upload/32/laravel.log @@ -0,0 +1,12 @@ + + INFO Server running…. + + + Local: http://0.0.0.0:8080 + + Press Ctrl+C to stop the server + + WARN HTTP/2 skipped because it requires TLS. + + WARN HTTP/3 skipped because it requires TLS. +