Belajar FrankenPHP - Symfony Integration (Runtime)
Episode 7 of 25

Belajar FrankenPHP - Symfony Integration (Runtime)

Menjalankan Symfony di atas FrankenPHP: paket frankenphp/symfony- runtime, variabel APP_RUNTIME untuk worker mode, HTTP cache, dan audit kompatibilitas worker dengan igor-php agar state leak tidak menyerang di produksi.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah episode 6 membahas Laravel, episode ini berpindah ke framework besar kedua ekosistem PHP: Symfony. FrankenPHP lahir dari tangan Kévin Dunglas — anggota core team Symfony — jadi wajar jika integrasinya sangat dalam. Kita akan membahas runtime/frankenphp-symfony, variabel APP_RUNTIME, HTTP cache, dan audit worker dengan igor.

Mengapa penting? Symfony adalah framework yang sangat modular dan ketat. Di mode worker, ketelitiannya menjadi keuntungan: kernel Symfony memang dirancang untuk di-reset antar request, dan integrasi resminya memanfaatkan itu. Hasilnya, aplikasi Symfony di worker mode bisa melayani ribuan request per detik dengan pola kode yang tetap terstruktur.

Cara 1: Symfony 7.4+ (Native Support)

Sejak Symfony 7.4, FrankenPHP worker mode didukung secara native — tanpa paket tambahan. Kalian cukup mengaktifkan worker di Caddyfile:

Caddyfile - Symfony native worker
localhost
 
root public/
php_server {
    worker ./public/index.php
}

Symfony akan mendeteksi bahwa ia berjalan di FrankenPHP dan menjalankan runtime worker secara otomatis. Ini jalur paling bersih untuk proyek baru.

Cara 2: runtime/frankenphp-symfony (Symfony Lama)

Untuk Symfony di bawah 7.4, gunakan paket PHP Runtime:

Install Symfony Runtime
composer require runtime/frankenphp-symfony

Lalu definisikan runtime lewat environment variable APP_RUNTIME:

Docker - worker Symfony dengan APP_RUNTIME
docker run \
  -e FRANKENPHP_CONFIG="worker ./public/index.php" \
  -e APP_RUNTIME="Runtime\\FrankenPhpSymfony\\Runtime" \
  -v $PWD:/app \
  -p 80:80 -p 443:443 -p 443:443/udp \
  dunglas/frankenphp

Perhatikan escaping ganda \\ — ini adalah nilai env var, jadi backslash harus ditulis ganda. APP_RUNTIME memberi tahu Symfony bahwa bootstrap harus dilakukan oleh runtime FrankenPHP, bukan runtime console/web standar.

Note

Gunakan integrasi resmi (native atau runtime package) untuk Symfony di worker mode. Jangan menulis loop frankenphp_handle_request() manual di atas Symfony — kernel, event system, dan profiler Symfony perlu di-reset dengan benar antar request, dan runtime resmi sudah menangani itu.

HTTP Cache di Worker Mode

Symfony memiliki komponen HTTP cache (HttpCache kernel) yang menyimpan response. Di worker mode, cache ini bisa hidup di memori — jauh lebih cepat daripada file cache di disk. Pastikan cache diaktifkan sesuai environment:

Caddyfile - aktifkan HTTP cache
{
    frankenphp {
        worker {
            file /app/public/index.php
            env APP_ENV prod
            env APP_DEBUG 0
        }
    }
}
 
localhost {
    root public/
    php_server {
        worker ./public/index.php
    }
}

Dengan APP_ENV=prod, Symfony mengaktifkan cache class, cache HTTP, dan optimasi produksi lainnya — kombinasi ideal dengan worker mode.

Audit Kompatibilitas Worker: igor-php

Symfony menyediakan tool audit untuk menemukan state leak sebelum mereka menggigit di produksi. Igor PHP adalah linter statis yang memindai proyek (termasuk vendor/) untuk:

  • Service yang menyimpan state per-request tapi tidak mengimplementasikan ResetInterface.
  • Static property yang mutable dan tidak di-reset.
  • Local static variabel di dalam fungsi.
  • Panggilan exit()/die() — yang akan mematikan seluruh worker!
  • Penulisan ke superglobal di dalam request.
Audit kompatibilitas worker
composer require --dev igor-php/igor-php
vendor/bin/igor-php .

Jalankan di CI sebelum deploy. Jika ada temuan, perbaiki dulu sebelum beralih ke worker mode.

Danger

exit() atau die() di dalam kode yang dieksekusi per-request akan menghentikan seluruh proses worker — bukan hanya request itu. Ini adalah bug paling merusak di worker mode. Igor PHP mendeteksi keduanya; jalankan secara rutin.

Memakai ResetInterface

Service Symfony yang menyimpan state per-request (misal holder token auth, current request context) harus di-reset antar request. Implementasikan ResetInterface:

Service dengan ResetInterface
<?php
namespace App\Service;
 
use Symfony\Contracts\Service\ResetInterface;
 
final class RequestContextHolder implements ResetInterface
{
    private array $data = [];
 
    public function set(string $key, mixed $value): void
    {
        $this->data[$key] = $value;
    }
 
    public function get(string $key): mixed
    {
        return $this->data[$key] ?? null;
    }
 
    public function reset(): void
    {
        $this->data = [];
    }
}

Kernel Symfony memanggil reset() pada semua service ber-tag kernel.reset setelah setiap request — state kalian kembali bersih tanpa menulis ulang logika aplikasi.

Symfony Docker: Jalur Termudah

Untuk proyek Symfony, jalur paling praktis adalah Symfony Docker — setup resmi yang dikelola penulis FrankenPHP dengan worker mode, HTTPS otomatis, HTTP/3, dan hot reload siap pakai:

Clone Symfony Docker
git clone https://github.com/dunglas/symfony-docker
cd symfony-docker
docker compose up --wait

Buka https://localhost — aplikasi Symfony berjalan di worker mode dengan cache produksi. Kita bedah struktur Docker-nya di episode 19.

Penutup

Pada episode 7 ini, kalian telah menjalankan Symfony di atas FrankenPHP.

Inti yang harus dibawa pulang:

  • Symfony 7.4+ mendukung worker mode native; versi lama pakai runtime/frankenphp-symfony + APP_RUNTIME.
  • APP_ENV=prod + APP_DEBUG=0 mengaktifkan HTTP cache dan optimasi produksi.
  • Igor PHP mengaudit state leak, exit()/die(), dan superglobal writes — jalankan di CI.
  • Service dengan state per-request harus mengimplementasikan ResetInterface.
  • Symfony Docker adalah cara tercepat untuk stack Symfony + FrankenPHP produksi.

Di episode 8 selanjutnya kita membahas lapisan yang sering diremehkan tapi menentukan performa: static files, gzip, dan HTTP caching — mengoptimalkan aset CSS/JS/image di Caddyfile dengan encode, precompressed, dan header cache. Sampai jumpa di episode 8!

Belajar FrankenPHP - Symfony Integration (Runtime) | Belajar FrankenPHP