Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHP and Symfony Apps As Standalone Binaries

PHP and Symfony Apps As Standalone Binaries

Distributing PHP applications is quite complicated. For instance, to run a Symfony project in production, a web server, the PHP engine, and the appropriate PHP extensions need to be installed. Their versions and configurations must be compatible with the app. Because PHP is an interpreted language, the application source code must also be available. Composer dependencies as well... and things are even more difficult if some of them are private.

Of course, these days, containers can help. But what if we could make things even simpler: a single distributable binary that's self-executing? Download a single file, give it the execution bit, and type "./my-app php-server": boom! Your Symfony application is up and running, served over HTTP/3 with a valid TLS certificate! If you also want to distribute CLI commands written in PHP, we've got your back.

Thanks to the latest features of FrankenPHP, the dream is now a reality! Let's discover how to ship PHP and Symfony applications as standalone binaries, web server, PHP included!

Kévin Dunglas

December 08, 2023

More Decks by Kévin Dunglas

Other Decks in Programming


  1. Kévin Dunglas: ➔ CEO @ Les-Tilleuls.coop ➔ FrankenPHP and API

    Platform creator ➔ Symfony Core Team ➔ PHP and Go contributor @dunglas
  2. 70+ API, Web and Cloud Experts ➔ Dev, consultancy and

    hosting ➔ 100% employee-owned co-op ✊ ➔ API Platform creators 🕷 ➔ Symfony backers 🎼 ➔ PHP Foundation advisory board 🐘 ➔ [email protected] 💌
  3. Prerequisites to Deploy a Symfony App in Prod ➔ A

    web server (NGINX, Caddy, Apache…) ➔ A PHP executor PHP-FPM (most likely) ➔ Usually, some PHP extensions curl, pdo_*, gd, apcu... ➔ Usually, a database server PostgreSQL, MariaDB, MongoDB…
  4. Deploying your Symfony App When your server(s) is ready, you

    need to: ➔ Get the latest stable version of app source code ➔ Install the dependencies of your project: composer install --no-dev -a
  5. FTP/SCP, Shared Hosting 👴 ➔ Easy (and usually cheap) ➔

    No (or limited) control over • PHP version • Extensions • Config ➔ Not possible to install custom extra services (Redis…)
  6. Your Own Servers (or Computer) ➔ Full control over everything

    ➔ You are on your own for everything • Initial setup • Security • Performance • Upgrades…
  7. Platforms As a Service ➔ git push and done… ➔

    … as long as your app is pretty standard ➔ The platform installs, maintains and optimizes the infrastructure (hardware and software) for you
  8. The Symfony Docker skeleton ➔ Skeleton with good defaults ➔

    Full control over everything ➔ Extensible ➔ Same environment in dev, in your CI pipelines and in prod ➔ You still need servers ➔ Can be hard in prod (knowledge!)
  9. Reminders: PHP ➔ PHP is an interpreted language (script) ➔

    The CPU cannot execute directly a PHP script ➔ An interpreter is needed ➔ The official interpreter (php-src) is written in C ➔ The PHP interpreter doesn’t contain a (production-grade) web server
  10. Reminders: dynamic vs static linking ➔ The PHP interpreter and

    its extensions depends on C and C++ libraries (ex: libcurl, OpenSSL, libsodium) ➔ These libraries can be linked with the interpreter dynamically or statically ➔ Dynamic linking: the library is a shared external binary (.so file on Linux) loaded at runtime by programs. Typical for PHP. ➔ Static linking: the library is embedded in the program binary. Hard to do for PHP. © Tatiana Fernández
  11. Our Goals ➔ Distribute PHP apps as standalone binaries ➔

    The binary must work without a local PHP or web server ➔ The end-user shouldn’t even know the app is written in PHP ➔ SO the binary must contain • A web server • The PHP executor • The PHP code and the assets • The vendors ➔ The binary must be statically compiled, with no external dependencies
  12. ➔ Written in Go 😻 • Can be compiled statically

    • Can embed static C libraries, such as PHP • Can embed arbitraries files, such as our app, assets and vendors ➔ Extensible with modules ➔ Prod ready • HTTP/2, HTTP/3, Early Hints… • Automatic HTTPS ⚔ Our Weapons: The Caddy Web Server
  13. ➔ Standalone Go library ➔ Embed the official PHP executor

    in your Go programs ➔ New SAPI for Go net/http ➔ Caddy module using the library ➔ Unique features: • Worker mode ⚡ • 103 Early Hints • Extensible in Go ⚔ Our Weapons: FrankenPHP
  14. ➔ Compile statically: • PHP (CLI, FPM, libphp…) • Its

    dependencies (pkgconfig…) • Most PHP extensions (curl, gd, apcu, intl…) • Most PHP extensions dependencies (libxml, libpng…) 🔁 ➔ CLI tool, written in PHP ➔ Entirely configurable ➔ Used by FrankenPHP, Native PHP, Laravel Laravel Herd… ⚔ Our Weapons: static-php-cli
  15. Summary We’ll create a static binary containing: ➔ A web

    server (Caddy) ➔ PHP (and its dependencies) ➔ The extensions we need (and their dependencies) ➔ FrankenPHP ➔ Our PHP code ➔ The dependencies of our PHP code ➔ Our assets
  16. Prepare your app $ echo APP_ENV=prod > .env.local $ echo

    APP_DEBUG=0 >> .env.local $ rm -Rf .git/, tests/ $ composer install --no-dev -a $ composer dump-env prod
  17. $ git clone \ https://github.com/dunglas/frankenphp $ cd frankenphp $ EMBED=/path/to/my/sfapp

    \ ./build-static.sh # 🎉🎉🎉 $ cp dist/frankenphp-<os>-<arch> my-app Build (Linux and Mac)!
  18. Run Web server (HTTPS, HTTP/3…) $ ./my-app php-server --domain=example.com Worker

    mode (HTTPS, HTTP/3) $ ./my-app php-server -w public/index.php CLI $ ./my-app php-cli bin/console app:my-cmd
  19. Ready-Made Dockerfile FROM dunglas/frankenphp:static-builder # Copy your app WORKDIR /go/src/app/dist/app

    COPY . . # Build the static binary, be sure to select only the PHP extensions you want WORKDIR /go/src/app/ RUN EMBED=dist/app/ \ PHP_EXTENSIONS=ctype,iconv,pdo_sqlite \ ./build-static.sh
  20. More Options Environment variables: ➔ PHP_VERSION: the PHP version to

    use (currently, 8.2 or 8.3) ➔ PHP_EXTENSIONS: PHP extensions to build (build only what you need to reduce the binary size) ➔ PHP_EXTENSIONS_LIB: extra libs (ex: libjpeg for GD) to build ➔ FRANKENPHP_VERSION: the FrankenPHP version to use