One language. One platform.
From code to fleet.

Phlo compiles its own full-stack language to PHP you can read. The same language writes your routes, drives your DOM, runs your server and manages your fleet.

poll.phlo
prop title = 'Phlo Poll'
prop options = ['Phlo', 'Svelte', 'Laravel', 'Rails']

route GET poll => view($this, $this->title)

view:
<main#app.poll>
	<h1>$this->title</h1>
	<foreach $this->options AS $option>
		<a.async href="/vote/$option">$option</a>
	</foreach>
</main>

<style ns=app>
.poll a {
	display: block
	background: $primary
	border-radius: 8px
}
</style>

Four layers, one system

Phlo is not just a language and not just a framework. Every layer is built on the previous one, in the same syntax, with the same mental model.

This is how Phlo reads

Five examples of Phlo source next to what the compiler makes of it. No toolchain, no magic: just readable output.

One line dispatches and renders. A route always ends in view(), apply() or location().

Phlo-bron
prop question = 'Which stack wins?'

route GET poll => view($this->question, 'Phlo Poll')
Output
// Compiles to readable PHP:
class poll extends obj {
	public $question = 'Which stack wins?';

	public static function GETpoll(){
		return view(phlo('poll')->question, 'Phlo Poll');
	}
}

HTML in the same file as the logic. Dot syntax for class and id. Blank line closes the view.

Phlo-bron
prop question = 'Which stack wins?'
prop options = ['Phlo', 'Next.js', 'Laravel', 'Rails']

view:
<main#app.poll>
	<h1>$this->question</h1>
	<foreach $this->options AS $option>
		<button>$option</button>
	</foreach>
</main>
Output
<!-- Server-rendered HTML: -->
<main id="app" class="poll">
	<h1>Which stack wins?</h1>
	<button>Phlo</button>
	<button>Next.js</button>
	<button>Laravel</button>
	<button>Rails</button>
</main>

Write in your source language. Phlo looks up translations via hash and falls back on the source.

Phlo-bron
view:
<h1>{en: Which stack wins?}</h1>
<button>{en: Vote}</button>
Output
<!-- /poll  (source language: English) -->
<h1>Which stack wins?</h1>
<button>Vote</button>

<!-- /nl/poll  (auto-translated via langs/nl.ini) -->
<h1>Welke stack wint?</h1>
<button>Stem</button>

One route serves both sync and async. In async, it sends DOM commands instead of HTML; the client applies them directly.

Phlo-bron
route both POST poll vote $id {
	if (!$option = type_poll::record(id: (int)$id)) return false
	type_poll::change('id=?', (int)$id, votes: $option->votes + 1)
	if (%req->async) return apply(outer: arr('#results' => $this->results))
	location('/poll')
}
Output
// Async vote: the server replies with DOM commands,
// the client applies them. No reload, no JS, no API layer.
{
	"outer": {
		"#results": "<section id=\"results\" class=\"card\">...</section>"
	}
}

// A plain browser (no async) gets a redirect to /poll instead.
// One route serves both.

CSS in the same file, one line per declaration, no semicolons.

Phlo-bron
<style ns=app>
.option {
	display: grid
	grid-template-columns: 1fr auto
	gap: 4px 12px
}
.bar {
	height: 8px
	border-radius: 4px
	background: $primary
	transition: width .4s
}
</style>
Output
/* Compiled into app.css: */
.option {
	display: grid;
	grid-template-columns: 1fr auto;
	gap: 4px 12px;
}
.bar {
	height: 8px;
	border-radius: 4px;
	background: var(--primary);
	transition: width .4s;
}

The power of Phlo is not in any single layer but in the vertical integration of all of them: the same language that writes your route drives your DOM, runs your server and manages your fleet.

Read the design philosophy →

Start in two minutes

With Docker you have a running app without installing anything. The installer asks for a name, host and resources and only finishes after a clean build.

$ docker run -it -v $(pwd)/app:/app ghcr.io/q-ainl/phlo php /phlo/install.php /app

Full installation guide →

A product of Q-AI

Q-AI develops practical products and technical solutions that simplify work and deliver real value. Phlo is the open-source foundation beneath that work.

Discover Q-AI →

We use essential cookies to make this site work. With your permission we also use analytics to improve the site.