18: Philosophy

This chapter explains why Phlo is built the way it is: the deliberate trade-offs behind the parser, the compiler, the runtime and the tooling. The short version: Phlo is an integrated platform with its own full-stack language, and its power is in the vertical integration of all layers, one language and one mental model from code to fleet.

Underneath every trade-off is one intent: to give a person or a small team back ownership and oversight of what they build. A lot of modern web development quietly works against that. Dependency trees too deep to audit, frameworks that churn faster than you can learn them, ceremony and boilerplate that bury what the code actually does, and usage-metered hosting whose bill grows precisely when you succeed. Each one adds a layer between you and your software, until no single person holds the whole picture anymore and the cost of running it is no longer yours to predict. Phlo treats those as paradigms to step away from, not to adopt. Code carries no decoration it does not need, so it stays readable and reviewable in one sitting. The output is plain PHP you own. It runs on a machine you control, at a cost that tracks the machine rather than every request. Legibility, ownership and affordability are not features bolted on top; they are the point, and the sections below are how the language earns them.

18.1: One system, four layers

Phlo is not a library you add to a stack; it is the stack. Four layers share one design:

Each layer would be unremarkable on its own. Together they mean you never translate between ecosystems: the same conventions, the same error pages, the same CLI, from a single view to a fleet of servers. A comparison like "a compact alternative to Laravel or HTMX" touches only one layer and misses the point.

18.2: A line-based parser, no AST

Phlo reads source line by line: a statement ends at a line ending, a node header opens a block, a blank line closes a view. There is no tokenizer building an abstract syntax tree. The whole parser is a few hundred lines, readable in a sitting, and because line N in maps to a known line out, the sourcemap and the error pages come almost for free.

The line-terminator model is complete and small: every line gets a ;, lines ending in ( [ { } , or . are implicit continuations, and a trailing \ continues explicitly (see chapter 4).

The cost is strictness: no multiline quoted strings, a blank line ends a view, CSS declarations stay on one line. Phlo's answer is better diagnostics, not a more forgiving parser: violations stop the build with the .phlo file and line. A real grammar would relax the rules at the price of the parser's legibility, and that is not a trade Phlo makes.

18.3: Compiles to readable PHP

.phlo compiles to plain PHP classes: one class per file, a header naming the source, and a per-class sourcemap recording PHP line to .phlo line. You can always drop into the generated PHP to see exactly what runs; there is no hidden runtime interpreting templates. And when something breaks at runtime, the error is translated back to the .phlo line you wrote, in the error page and in the Phlo Control Center.

The cost is a build step. In development it disappears behind rebuild-on-request (X.6); in production you build once.

18.4: The `obj` magic base class

Every compiled class extends obj: arbitrary data via __get/__set, bound closures, and computed properties written as _name() methods, called without parentheses and cached on first access. In .phlo, prop now => time() compiles to a cached _now(). One access model replaces getters, lazy initialization and value objects; the same object serves as view model, record and config bag.

The cost: magic access is less statically analysable than explicit properties, and the model demands discipline about caches. The static structure caches in obj only ever hold class shape, never request- or user-scoped values, which is what keeps it safe under worker mode.

18.5: `phlo()` as a tiny service registry

phlo('MySQL') returns a shared instance; in .phlo source, %MySQL compiles to exactly that call. Each class can implement __handle() to decide its own identity (singleton, multiton by argument, always new) and opt into surviving between worker requests. You get dependency lookup without a container framework, configuration or annotations, and the %name shorthand keeps call sites short and greppable.

The honest label is service location, not injection: dependencies are implicit at the call site. In exchange there is zero wiring, and the registry itself is a dozen lines.

18.6: Rebuild on request in development

With build: true, Phlo checks whether any source changed and recompiles before handling the request, throttled so the check is cheap in a hot loop. The edit-refresh loop feels interpreted while the runtime stays compiled, with no watcher process and no manual build.

The cost: building writes files during a request, which is unsafe in a long-running worker, so build and thread are mutually exclusive. Development uses on-request builds; production runs worker mode on a release build. The split is intentional, not a limitation to engineer around.

18.7: Zero dependencies

The engine ships its own CSS transpiler, JS minifier, icon-sprite builder and SPA runtime, plus more than 150 bundled resources instead of a package tree. Composer is supported, but lazy and optional. The engine has nothing to audit but itself, upgrades on its own schedule, and stays small enough to hold in your head. For a platform whose premise is legibility, a vendor tree would undercut the premise.

The cost: Phlo reimplements things mature libraries already solve, so those implementations must be tested and can have edge cases a large library would not. The bet is that a small, owned surface is worth more here than breadth.

18.8: Self-hosted ownership

A Phlo app is a directory of files on a server you control. It runs as one FrankenPHP process, stays in memory in worker mode, and answers requests without a per-invocation meter running. Its cost is a server, not a usage bill: the same machine serves your hundredth visitor and your hundred-thousandth, and the line on the invoice does not bend upward with success.

This is deliberate distance from the cloud and serverless default, where the bill is smallest exactly when you have no users and grows with every request once you do. For a product that works, that model can turn growth into a liability: the more it is used, the more it costs to keep running, sometimes past the point where the numbers still make sense. Phlo bets the other way. You own the language output (readable PHP), the data (a file or a database you administer), and the machine it all runs on. The Phlo Dashboard manages that as your fleet, not as rented capacity you never see.

Self-hosted does not mean unscalable. A stateless PHP application tier behind a load balancer, with shared state in a database or cache, is the architecture that has run the largest sites on the web for over 25 years. Phlo drops straight into it: it compiles to ordinary PHP on FrankenPHP, and worker mode is share-nothing per request, so you scale the application tier by running more identical nodes behind the load balancer, at a predictable per-node cost. Multiple nodes need shared session state (point PHP's session store at Redis or a database, or use sticky sessions), and the database becomes the tier that takes the real scaling work, replicas and partitioning, exactly as in any such stack. The application tier is the cheap, easy part; the data tier is where the engineering goes. The deployment docs cover the concrete multi-node setup.

The cost: ownership is also responsibility. You provision the server, you patch it, you take the backups, you carry the pager. There is no autoscaling to zero and no managed control plane absorbing the operational load. The bet is that for most products a predictable server you understand beats an elastic bill you cannot.

18.9: Agent-first by design

SKILL.md is a complete language and build reference written so an AI agent can work without prior knowledge; the reflect:: CLI exposes routes, views, the parsed structure, search and dependency graphs as JSON; apps keep a data/app.md scratchpad an agent reads first and updates after. The same properties that make Phlo legible to you (one closed loop, source-mapped errors, a single skill document) make it tractable for an agent, and treating that as a first-class goal multiplies a small team.

The cost: SKILL.md and reflect:: are part of the contract. A change to the language, the build or the CLI is not done until the documentation reflects it. That maintenance burden is accepted on purpose.

18.10: What Phlo is not

Honesty about the boundaries:

If those constraints fit, you get the payoff this chapter described: one language and one mental model, vertically integrated from your first view to your whole fleet.

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