1: Introduction
Phlo is an integrated platform with its own full-stack language. You write .phlo source files; Phlo compiles them to PHP, CSS and JavaScript you can open and read, with every runtime error pointing back at the .phlo line you wrote. The same language carries four layers: the language itself, the application platform (backend resources plus the phlo.js SPA engine), the server platform (FrankenPHP, phloWS, phloWA) and the operations platform (the Phlo Dashboard). This guide covers all of them. The production release runs on a shared runtime, usually in /srv/phlo/, while each app keeps its own source, data, generated PHP and webroot.
1.1: Philosophy
- Source first: you work in
.phlo, not in generated PHP, CSS or JavaScript. - A small runtime: the web entrypoint loads
/srv/phlo/phlo.phpand starts the app withphlo_app(...). - Explicit runtime resources:
data/app.jsonselects which Phlo resources an app loads from the runtime catalog; app code stays in the app. - Dev and release separated: dev builds, debugs and can use auth; release uses the generated output.
- HTML, routes and behavior close together: a route, view, style or script can live in the same topic file when that makes the app clearer.
1.2: Installation
Phlo requires PHP 8.3 or higher; the CLI build runs on the same PHP. For production, FrankenPHP is the recommended runtime (built-in web server, worker mode); classic PHP-FPM behind Nginx works too.
Fetch the runtime and scaffold your first app with the bundled installer:
git clone https://github.com/q-ainl/phlo.git /srv/phlo
php /srv/phlo/install.php /srv/example.nl/
The installer asks for a name, host and target, shows the runtime catalog and lets you pick resources (their @ requires are included automatically), writes the entrypoint, data/app.json, data/app.md, a first route and .gitignore, and only finishes after a clean build with concrete next steps.
Prefer a copy that cleans up after itself? Copy install.php into the new app directory and run it there; after a successful installation it removes itself:
cp /srv/phlo/install.php /srv/example.nl/ && cd /srv/example.nl && php install.php
The next sections describe what the installer sets up for you, and how to build the same thing by hand.
1.3: Project structure
A typical app:
/srv/example.nl/
app.phlo
page.home.phlo
data/
app.json
auth.ini
creds.ini
php/
app.php
classmap.php
release/
www/
app.php
www/
app.php
php/, www/app.js, www/app.css and release/ are build output. Only change them through the source and rebuild.
1.4: Entrypoint
Dev entrypoint in www/app.php:
<?php
require('/srv/phlo/phlo.php');
phlo_app (
id: 'Example',
host: 'dev.example.nl',
auth: true,
build: true,
debug: true,
app: '/srv/example.nl/',
data: '/srv/example.nl/data/',
);
Release entrypoint in release/www/app.php:
<?php
require('/srv/phlo/phlo.php');
phlo_app (
id: 'Example',
host: 'stage.example.nl',
app: '/srv/example.nl/release/',
php: '/srv/example.nl/release/',
data: '/srv/example.nl/data/',
);1.5: Build
Use the CLI from the dev entrypoint:
php www/app.php build::run
php www/app.php build::lint
php www/app.php build::release
build::run transpiles the dev output, build::lint checks the generated PHP, and build::release writes the release output.
1.6: Web server
The web server points to www/ for dev and to release/www/ for stage/production. Unknown paths are rewritten to app.php.
For FrankenPHP (recommended), a single Caddyfile block is enough:
example.nl {
root * /srv/example.nl/release/www
php_server
}
For Nginx:
root /srv/example.nl/release/www;
location / {
try_files $uri $uri/ /app.php?$query_string;
}