Real PHP · on the metal

Real PHP, running bare-metal on microcontrollers.

php-baremetal runs the official PHP interpreter on microcontrollers. It is not a reimplementation, a language subset, or a transpiler: the upstream Zend engine from php.net is cross-compiled for the chip and executes your index.php opcode by opcode — the same way it would behind a web server.

$ phpflash init my-project
The idea

The same interpreter, on a $5 chip.

The PHP source tree is vendored unchanged; the only modifications are separate patches applied at build time, for the handful of places where a bare-metal RTOS differs from a Unix host. Hand the same script to this engine or to a desktop php and you get the same output — because underneath it is the same interpreter.

8.3–8.5

PHP versions

2

Chip families

0

Lines re-implemented

8–32 MB

PSRAM

What you get

A full PHP runtime, not a toy.

The complete Zend engine and standard library, real extensions, and the applications that ride on top — all cross-compiled to the chip's own CPU and run through the official embed SAPI.

01

The unmodified engine

Namespaces, classes, traits, enums, closures, generators, match, exceptions, typed properties and attributes — plus strings, arrays, JSON, PCRE, hashing, SPL, Reflection and the CSPRNG.

02

Native, not emulation

PHP's C is built with the chip's own cross-compiler and runs on the board's CPU through the official embed SAPI. No interpreter-on-an-interpreter.

03

Silicon-agnostic by design

Runs today on the ESP32-P4 (RISC-V) and ESP32-S3 (Xtensa) — but a new chip family is a directory, not an engine change. The port targets microcontrollers, not one vendor, so more silicon can follow.

04

Real extensions

date, mbstring, filter, ctype, session, PDO SQLite, full OpenSSL 3.0 with an HTTPS client, and Zend OPcache (no JIT, statically linked).

05

Composer & frameworks

Composer works with unmodified packages from Packagist. With the web-server model and OPcache, the ESP32-P4 serves stock Laravel and Symfony pages over HTTP.

06

Storage, your way

Run the source from a microSD (swap the card to change the program, no rebuild) or embedded in the flash image. The partition layout is generated per build.

07

State across requests

A reboot-persistent store_* (NVS) and a volatile in-RAM mem_*, so the shared-nothing web-server model can still hand data from one request to the next.

08

Extend it in C

Drop native code under firmware/exts/ and it compiles into the firmware — no fork required. A GPIO extension drives pins straight from PHP.

09

One tool for all of it

phpflash — a single static binary that scaffolds, builds, flashes and monitors a project, and identifies a connected board. It reads what the firmware can build, so it only ever offers real choices.

10

WiFi, from PHP

Scan for networks, join one, or bring up your own access point — straight from PHP. Even the radio-less ESP32-P4 gets WiFi through an on-board companion, so a board can create its own network and serve its pages over it, no router in sight.

11

HTTP & WebSocket

In the event-driven model an HTTP request or a WebSocket frame is just another event. Route a REST API, and push live data to every connected browser with ws_broadcast() — all on one resident reactor.

12

Sensors on a second core

Poll an I²C sensor on core 1 at a fixed rate; each new sample arrives as an event on core 0. Your PHP reacts to fresh data without a busy loop and without stalling on the bus.

Write PHP

Arduino-style, in the language you already know.

A setup()/loop() sketch drives the board; a built-in gpio extension talks to the pins. This is the whole program that blinks an LED.

php index.php
<?php
// runs once at boot
function setup(): void {
    gpio_mode(2, GPIO_OUTPUT);
}

// runs forever, like an Arduino loop
function loop(): void {
    gpio_write(2, 1); delay(500);
    gpio_write(2, 0); delay(500);
}
An LED on GPIO2 blinking on and off every 500 ms, driven from PHP.

Live on the board

Ways to run it

A sketch, a web server, or a reactor.

One line in the project config picks how PHP runs on the board: drive hardware in an Arduino-style loop, stand up an HTTP server that runs your script per request, or react to events on a resident reactor. The same engine, three execution models.

type = "init-loop"

Firmware sketch

setup() runs once at boot, then loop($tick) repeats forever — driven from C so the watchdog stays fed. The way to blink an LED, read a sensor, or drive a display.

Arduino-style GPIO · I²C · SPI runs continuously
type = "web-server"

HTTP web server

A built-in server runs your index.php per request and returns the response over HTTP. Stock Laravel and Symfony pages served straight off the chip, through a per-request SAPI with OPcache.

per-request SAPI routing · sessions Laravel · Symfony
type = "event-driven"

Event-driven reactor

No loop(): register listeners and a reactor delivers each event — a timer, a GPIO interrupt, an HTTP request or a WebSocket frame. Serve a REST API and push live data to every browser, all on one resident engine.

HTTP + WebSocket sensors on core 1 reacts & sleeps
New · event-driven

React to the world, and stream it live.

The event-driven model is here. No loop(): register listeners and a reactor delivers each event. Poll a sensor on the second core, push its readings to every browser over a WebSocket, and serve the page from the same engine — this is the whole program.

php index.php
<?php
use Baremetal\Events;
use Baremetal\I2c\{Bus, Driver\Qmi8658};
use Baremetal\Sensor\Imu\SamplesReady;

wifi_ap_start('php-imu', 'baremetal');            // the board makes its own network

$imu = new Qmi8658(new Bus(sda: 11, scl: 10));
$imu->poll(hz: 30, event: SamplesReady::class);   // sampled on core 1

// stream every reading to all connected browsers, live
Events::listen(SamplesReady::class, fn (SamplesReady $e) =>
    ws_broadcast(json_encode($e->device->accel())));

serve_ws('/ws');
serve_http(80);
Two pieces

The firmware and the tool.

php-baremetal is two repositories: the PHP port itself, and a single command-line tool that scaffolds, builds, flashes and monitors a project without touching ESP-IDF by hand.

Get started

Flash your first board in minutes.

Install the tool, scaffold a project, write your index.php, and flash it. Your PHP is running on the chip.