dev.to
Web Developer = Embedded Developer: Real PHP Now Runs on the ESP32 (v1.0.0)
If you can write `index.php`, you can already program a microcontroller. That's not a metaphor.
**PHP on ESP32 just hit`1.0.0`** — the first official release. It takes the _real_ PHP interpreter, the unmodified Zend engine straight from php.net, cross-compiles it for a chip that costs **about $4** , and runs your PHP on the bare board. No operating system underneath. No transpiler. No "PHP-like" language subset. The same `index.php`, the same opcodes, the same `array_map` and `preg_match` and `json_encode` — because it is literally the same C code, built for the chip's own CPU.
Hand the same script to this engine or to a desktop `php` and you get the same output.
## Wait — real PHP? On a microcontroller?
Yes. The whole engine is compiled in: the lexer, the parser, the opcode compiler, the VM and executor, the garbage collector, the object/class/exception model. `<?php echo 1 + 1;` travels the exact same path here as on a server — source → tokens → AST → opcodes → execution.
It runs as **native code** , not under emulation. PHP's C is built with the chip's own cross-compiler (`riscv32-esp-elf` for the ESP32-P4, `xtensa-esp32s3-elf` for the ESP32-S3), and the opcodes execute on the board's CPU. The integration point is the official `embed` SAPI — the same interface any C program uses to host PHP.
The standard library comes with it: `ext/standard`, PCRE, JSON, hashing, SPL, reflection, the CSPRNG. Optional extensions layer on per project.
Captured on an ESP32-S3-Zero "Super Mini" (Quad 2MB PSRAM @ 80MHz, CPU 160MHz), PHP 8.4.25, no
opcache, with the native_gpio C extension compiled in. See README.md for the S3 / P4 comparison.
... (boot log) ...
I (562) esp_psram: Found 2MB PSRAM device
I (562) esp_psram: Speed: 80MHz
I (802) cpu_start: cpu freq: 160000000 Hz
I (805) esp_psram: Adding pool of 2048K of PSRAM memory to heap allocator
...
==== php-baremetal benchmark ====
PHP 8.4.25
(PSRAM size/speed and CPU freq are in the boot log above)
PSRAM: 744.7 KB free of 2,048.0 KB | internal RAM: 183.4 KB free of 326.4 KB
compiled footprint (PSRAM consumed to compile a source file):
file lines source compiled
bench_small.php 17 0.4 KB 2.3 KB
bench_medium.php 76 2.0 KB 10.3 KB
bench_large.php 167 4.2 KB 22.4 KB
GPIO tight loop (100000 writes on GPIO 2):
PHP loop: 3229 ns/write -> 309,687 writes/s ~154,843 Hz (empty loop 829 ns)
native C: 357 ns/write -> 2,804,239 writes/s ~1,402,119 Hz (9x faster than PHP)
execution: 1,090-row working set = 403.1 KB in PSRAM (710.1 KB was free)
==== done ====
Here's the "hello world" of embedded — blinking an LED — written entirely in PHP. `setup()` runs once, then `loop($tick)` is called forever (Arduino-style; the loop lives in C so the watchdog stays happy):
<?php
// index.php: blink an LED on GPIO2.
define('LED', 2);
function setup(): void {
gpio_mode(LED, GPIO_OUTPUT);
echo 'PHP ' . PHP_VERSION . " up\n";
}
function loop(int $tick): void {
gpio_write(LED, $tick % 2); // on for odd ticks, off for even
delay(500); // milliseconds
}
`gpio_mode`, `gpio_write`, `gpio_read` and `delay` come from a small built-in extension. `echo` goes to the serial console. That's it — three lines of PHP driving a physical pin.
## Your framework, browsable, on the chip
On a board with networking, the firmware can run an **HTTP server** and hand each request to a fresh PHP run — the way a script runs behind Apache or PHP-FPM. `$_SERVER`, `$_GET`, `$_POST`, cookies and sessions are populated per request. Whatever your script prints becomes the response body.
That's enough to make a **framework browsable** : on the ESP32-P4 (32 MB PSRAM), **stock Laravel and Symfony both serve pages this way**. Not a fork, not a cut-down build — the real thing.
And because every ESP32-S3 has WiFi on the die, a board can create _its own network_ and serve a page over it — no router, no cable. One of the examples, `wifi-ap-s3-rgb-manage`, boots a WiFi access point and serves a live PHP page that controls the board's onboard RGB LED from your phone.
Here's what happens end to end, and every piece of it is PHP. A one-time **init script** runs once at boot, before the server starts: it brings up a WiFi access point — SSID, password, `192.168.4.1` — and initializes the onboard RGB LED. Then the HTTP server takes over, and every request runs **`index.php`** fresh: it reads the color from `$_GET`/`$_POST`, drives the RGB LED (HSV), and prints the page back. So you connect your phone to the board's own WiFi, open the IP, move a slider — and the _physical_ LED on the chip changes color. No cloud, no MQTT broker, no companion app: the microcontroller **is** the web server, and the whole thing — network, page, and pin — is a couple of `.php` files.
## What actually runs on it
The standard library is always on. Everything else is opt-in per project:
* **Language.** Classes, closures, generators, exceptions, traits, namespaces, typed properties, enums, attributes — all present, because they _are_ the engine.
* **Text & data.** `ctype`, `mbstring` (optional oniguruma), `filter`, `tokenizer`, and **PDO SQLite** for an on-card or in-memory database.
* **State.** `session` on the web-server model; a reboot-persistent key-value store (`store_*`, backed by the chip's NVS) for values that survive a reset; and a volatile in-RAM twin (`mem_*`).
* **Config.** A project `.env` is baked into the firmware and read as `$_ENV` / `getenv()`.
* **OPcache.** The bundled Zend OPcache is ported (no JIT, static) — caches bytecode to the card or into PSRAM so a request stops recompiling the framework every time.
* **TLS.** The `openssl` extension has two builds — a compact mbedTLS-backed one, and full **OpenSSL 3.0** (RSA, EC, X.509) — driving an HTTPS client on a networked board.
## The hardware
Two things decide whether a chip qualifies: **external PSRAM** (the runtime heap is measured in megabytes) and **≥ 8 MB flash** (the firmware image is ~3 MB). Core architecture doesn't matter — the portable VM builds on both Xtensa and RISC-V.
Family | Core | PSRAM | Networking
---|---|---|---
**ESP32-P4** | dual-core RISC-V, up to 400 MHz | up to 32 MB | Ethernet (P4-ETH), **WiFi 6** via on-board C6 companion (P4-WiFi-C6)
**ESP32-S3** | dual-core Xtensa LX7, 240 MHz | 8 MB | Ethernet (S3-ETH), **WiFi on every S3**
PHP **8.3.33** , **8.4.25** and **8.5.10** all build today, selectable per project.
And the price tag is the fun part: an entry ESP32-S3 board with PSRAM runs the whole thing for **around $4**. A full PHP interpreter, on a chip that costs less than your coffee.
## Try it in five commands
The supported path is phpflash, a single-binary CLI that scaffolds a project, drives the build, flashes the board and opens the serial console:
phpflash system-setup # once: installs ESP-IDF + the firmware sources
phpflash init my-project # scaffold (asks for board, storage, extensions)
cd my-project
$EDITOR project-src/index.php # write your PHP
phpflash flash # build + flash the connected board
Change the script, reset, and the board runs the new one. That's the whole loop.
## So… are you an embedded developer now?
If you've shipped a PHP app, you already know the language, the standard library, the mental model. `1.0.0` means that skill set now reaches down to the metal — a real interpreter, running your real code, on a chip you can hold in your hand. Real PHP, bare metal, **about four bucks**.
It began as a holiday side-project. It's now a stable, documented `1.0.0` with a public roadmap — and **new contributors are very welcome**.
### An honest word on where it stands
`1.0.0` is a milestone, not a finish line. Right now this is firmly **for hobbyists and tinkerers** — it's a genuinely fun way to reuse your PHP skills on hardware, but it is not (yet) something I'd put in production. A lot is still missing or rough: proper **multi-core** handling, an **I²C bus** with real sensor/display drivers, an **event-driven** execution model, and plenty more.
The good news: that's exactly what **`2.0.0` is about, and it's already in progress** — dual-core done right, the I²C stack, event-driven mode, touch and IMU boards. If any of that sounds like your kind of thing, the roadmap is public and the door is open.
### Links
* 🧠 **How it works / deep dive:** Real PHP on the ESP32
* 🔬 **The full example in detail:** php-baremetal.com/blog/php-wifi-web-server-esp32-s3-control-led
* 📦 **Firmware repo:** github.com/php-baremetal/php-esp32
* 🛠️ **The phpflash CLI:** github.com/php-baremetal/flash-tool
* 🌐 **Project site:** php-baremetal.com
If real PHP running bare-metal on a microcontroller made you raise an eyebrow, a ⭐ on the repo helps more people find it. What would _you_ build with it?