4: Syntax & Structure

Phlo compiles .phlo source files to plain PHP classes and generated assets. The syntax is PHP-like, but without semicolons at the end of statements.

4.1: File structure

A .phlo file can contain top-level controller code and nodes:

Example:

route both GET home => view($this)

prop title = 'Welcome'

view:
<h1>$this->title</h1>

4.2: Statements and the line parser

Statements end at a line break, not at a semicolon:

$count = 1
if ($active) $count++

This works because the compiler appends a ; to every line and only removes it where the line clearly continues. The full rule set:

$label = $visitors === 1 \
    ? 'visitor' \
    : 'visitors'

The mental model: stop thinking about semicolons entirely. Only ask "is my statement complete on this line?" If not, end the line with one of ( [ { , . (which happens naturally in most multiline code) or with an explicit \.

Without an implicit or explicit continuation, every line becomes its own statement. That is why in multiline calls every argument line must end with a comma, including the last one:

apply (
    title: 'Done',
    main: '<p>Ready</p>',
)

Lesson. The file-level node parser tracks multiline node bodies by counting parentheses, not square brackets. A multiline prop x => [ ... ] ends the node after its first line and the remaining lines become stray controller code (Controller must be in one place). Open multiline node bodies with a parenthesis: prop x => arr(...) or prop x => array_merge(...).

4.3: Controller code

Top-level code that is not a node becomes controller code of the generated class. That code runs when the instance is first retrieved.

prop ready = false

$this->ready = true

Use controller code for light initialization. Put request logic in routes and methods instead.

4.4: Instances

Use %name to retrieve a Phlo object through the instance manager:

%payload->name
%session->user
%creds->mysql->database

The instance is created lazily and then reused within the request.

Lesson. The compiler rewrites %name EVERYWHERE in a .phlo file, including inside string literals. A page that tried to print the literal text %session in a code example shipped phlo('session') to its visitors. Example code that must stay verbatim belongs in external files (.txt, .md) loaded at runtime, never in .phlo string literals.

4.5: Props

Static prop:

prop title = 'App'

Computed prop:

prop fullName => $this->first.' '.$this->last

Computed props are generated as getters and can be read as a property:

$this->fullName

4.6: Methods

Single-line method:

method label($value) => ucfirst($value)

Multiline method:

method label($value){
    if (!$value) return void
    return ucfirst($value)
}

4.7: Functions

You define global functions with function:

function initials($name){
    return strtoupper(substr($name, 0, 1))
}

Use this sparingly. For app code a regular app class is usually better; only framework-wide helpers belong in a runtime resource.

4.8: Statics

Static value:

static table = 'users'

Static method:

static label($value) => ucfirst($value)

Call:

user::label('jordi')

4.9: Strings and operators

Strings and operators follow PHP:

$name = 'Jordi'
$title = "Hello $name"
$active = $count > 0 && !$archived

Use void for an empty string when that makes the intent clear.

4.10: Named arguments

Named arguments work as in PHP and keep calls readable:

%DB->load (
    table: 'users',
    where: 'active=1',
    order: 'name',
)

4.11: Error handling and JSON responses

error('message', $code = 500) aborts a request with a status. It throws (no return needed), and the engine renders it to fit the request:

if (!$record) error('Record not found', 404)

Client errors ($code < 500) keep their message; server errors (>= 500) stay generic ("Error") unless debug: true, so uncaught-exception internals are not exposed by default.

For a successful JSON response use output($data, code:): an array is encoded automatically and code sets the status. A JSON API route therefore uses output() for results and error() for failures, with no per-app response wrapper.

debug: true (set in www/app.php) enables verbose debug output; runtime errors are logged to data/errors.json.

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