EN | NL | 中文

1: Inleiding

Phlo is een programmeertaal en engine bovenop PHP 8+, bedoeld om compacte, overzichtelijke en performante webapps te bouwen. Routing, controllercode, views, styling en frontend-updates vormen één geheel. Phlo transpileert .phlo naar normale PHP-klassen en genereert de benodigde assets.

De webserver wijst naar je webroot /www. Onbekende paden worden herschreven naar /www/app.php, waar Phlo je configuratie inleest, instanties beheert en routes afhandelt. De buildfase draait automatisch (JIT) zodra bronbestanden veranderen.

1.1: Filosofie

1.2: Wat is Phlo

Phlo is een superset van PHP met automatische build. Een .phlo-bestand kan bevatten:

Phlo is modulair inzetbaar: full-stack, frontend-engine-only, class-writer/codegen of als asset-pipeline.

1.3: Installatie

1) Engine plaatsen

Plaats de map phlo/ in je project (buiten /www).

2) Webserver → webroot /www

Stel de webserver in met document root naar /www en rewrite onbekende paden naar app.php.

Nginx

server {
    root /pad/naar/project/www;

    location / {
        try_files $uri $uri/ /app.php?$query_string;
    }
}

Apache (.htaccess in /www)

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ app.php [QSA,L]

Statische bestanden (afbeeldingen, app.js, app.css) worden direct geserveerd; alleen onbekende paden gaan naar Phlo.

3) Inhoud van /www/app.php

Gebruik de werkende entrypoint zoals in je codebase:

<?php
require('/srv/phlo/phlo.php');
phlo_app_jsonfile($app = dirname(__DIR__).'/', "$app/data/app.json");

Wat dit doet:

4) Build (JIT) en scripts

1.4: Projectstructuur

Een Phlo-project heeft een vaste indeling:

/www/              ← webroot (publiek)
  app.php          ← centraal entrypoint
  app.js           ← automatisch gegenereerde frontend bundel
  app.css          ← automatisch gegenereerde CSS

/data/app.json     ← verplichte configuratie

/phlo/             ← Phlo engine
/php/              ← getranspileerde PHP (automatisch)

Belangrijke punten:

2: Configuratie

Elke Phlo-app heeft een verplicht configuratiebestand:

/data/app.json

De engine leest dit in vanuit app.php en bepaalt hiermee bronnen, libraries, bundling en asset-output.

2.1: Doel en positie

2.2: Minimale configuratie

{
  "id": "MijnApp",
  "version": ".1",
  "host": "localhost",
  "dashboard": "phlo",
  "debug": true,
  "build": {
    "libs": []
  }
}

Waarom zo?

Productie/release zonder (volledige) build behandelen we later apart.

2.3: `build.sources`

Alleen nodig als je meer bronpaden wilt dan het app-pad uit /www/app.php.

{
  "build": {
    "sources": [
      "%app/",
      "/srv/phloCMS/",
      "/srv/phloCMS/fields/"
    ],
    "libs": []
  }
}

2.4: `build.libs`

Declareert welke libraries (uit phlo/libs/) je vooraf wil laden en door je project gekend wil hebben.

{
  "build": {
    "libs": [
      "DB/DB",
      "DB/MySQL",
      "model",
      "payload"
    ]
  }
}

2.5: Namespaces & assets

Gebruik alleen als je bundling/asset-scopes wil sturen.

{
  "build": {
    "libs": [],
    "defaultNS": "app",
    "phloNS": ["app", "cms"],
    "iconNS": ["cms"],
    "icons": "/srv/icons"
  }
}

2.6: Frontend-buildopties

{
  "build": {
    "libs": [],
    "phloJS": false,
    "buildCSS": true,
    "minifyCSS": false,
    "buildJS": true,
    "minifyJS": false
  }
}
sleutel type betekenis
phloJS bool Phlo-frontendengine meebundelen (geavanceerde use-cases).
buildCSS bool Styles uit .phlo verwerken en CSS-assets schrijven (per namespace).
minifyCSS bool CSS minificeren.
buildJS bool Frontend scripts bundelen en JS-assets schrijven (per namespace).
minifyJS bool JS minificeren.

Belangrijk: Alles wat Phlo naar /www/ schrijft (zoals app.js, app.css en eventuele namespaced/extra assets) is gegenereerd en wordt bij builds overschreven. Niet handmatig bewerken.

2.7: Overige velden

3: Syntax & Structuur

Phlo is een superset van PHP met compacte, puntkomma-loze syntaxis. In één .phlo-bestand combineer je routes, props, methods, views, styles, scripts en controllercode. De builder transpilet naar gewone PHP/JS/CSS.

3.1: Bestand structuur

Top-level elementen:

Minimaal, geldig voorbeeld:

route GET home => $this->main

prop title = 'Welkom'

method main => view($this->home)

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

3.2: Controllercode

Top-level statements buiten blokken vormen de controller van het bestand. De controller draait nadat de instantie bestaat (niet in __construct) — dit voorkomt cirkelreferenties.

Voorbeeld (onschadelijke init):

prop initialized = false
$this->initialized = true

3.3: Statements

Voorbeelden:

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

foreach ($rows AS $r) $sum = $sum + $r->value

foreach ($rows AS $r){
  $sum = $sum + $r->value
  $n = $n + 1
}

chunk (
  title: 'Overzicht',
  main: view($this->home),
)

apply (
  title: 'Klaar',
  main: '<p>Gereed</p>',
)

3.4: Variabelen & Scopes

3.5: Constants

Exact zoals in de engine gedefinieerd:

Constant Betekenis / Waarde
phlo Huidige Phlo-versie (string)
cli true als er geen REQUEST_METHOD is (CLI)
async true als HTTP_X_REQUESTED_WITH gelijk is aan 'phlo'
method 'CLI' of de HTTP-methode van de request
jsonFlags JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
br '<br>'
bs '\\' (backslash)
bt '`' (backtick)
colon ':'
comma ','
cr "\r"
dash '-'
dot '.'
dq '"' (double quote)
eq '='
lf "\n"
nl cr.lf → effectief "\r\n"
perc '%'
pipe ' | '
qm '?'
semi ';'
slash '/'
space ' '
sq "'" (single quote)
tab "\t"
us '_'
void '' (lege string)

Deze constants zijn overal beschikbaar in .phlo.

3.6: Strings & Operators

Strings: 'single' en "double". Operators: conform PHP. Phlo voegt compactere syntaxis toe (arrow-bodies, named arguments); semantiek blijft gelijk.

3.7: Functies

Functies zijn project-globaal en worden geschreven naar /php/app.php.

Singleline:

function add($a, $b) => $a + $b

Meerlijns:

function sum($a, $b){
  return $a + $b
}

3.8: Methoden

Methoden horen bij de class die uit het .phlo-bestand wordt gegenereerd.

method hello($who) => 'Hi '.$who

method classify($x) {
  if ($x > 5) return 'groot'
  return 'klein'
}

Arrow voor éénregelige logica; meerlijns gebruikt accolades.

3.9: Props

Statisch (transpileert naar PHP-classproperty; géén function calls toegestaan):

prop title = 'App'
prop defaults = ['theme' => 'dark']

Computed (lazy + cached):

prop now => time()
prop fullName => $this->first.' '.$this->last

Props met arguments (alleen computed):

prop repeat($n) => str_repeat('*', $n)

# gebruik
$this->repeat(5)

3.10: Statics

Definitie:

static x = 1
static y => time()

Aanroepen binnen dezelfde class:

dx (
  static::$x,
  static::x(),
  static::y,
)

Aanroepen extern (via classnaam):

dx (
  test::$x,
  test::x(),
  test::y,
)

Belangrijk:

3.11: Named Arguments

Volledig ondersteund; maakt aanroepen expliciet en minder foutgevoelig.

%MySQL->delete (
  'Users',
  'id=?',
  id: 1,
)

3.12: Error-afhandeling

3.13: Stijlregels

4: Routing

Routing in Phlo koppelt een spatie-gescheiden pad + HTTP-methode aan een target (meestal een method). Routes uit alle .phlo-bestanden worden verzameld; de router wordt geactiveerd met app::route().

4.1: Basisvorm

route [async|both] [GET|POST|PUT|DELETE|PATCH] pad [pad2 ...] => target

Voorbeeld:

route GET home => $this->main
method main => view($this->home)

4.2: sync / async / both

Keyword Gedrag
(weglaten) Alleen sync (gewone HTTP)
async Alleen async (requests van Phlo-frontend)
both Sync én async toegestaan
route both GET data => $this->loadData
route async POST items save => $this->saveItems

4.3: Variabelen

Phlo parse’t ieder pad-segment. Segmenten die met $ beginnen zijn variabelen met extra mogelijkheden:

4.3.1 Verplicht (doorgeven aan target)

route GET user $id => $this->showUser($id)
method showUser($id) => view($this->profile)

4.3.2 Optioneel presence met ?boolean

route GET search $full? => $this->search($full)

4.3.3 Rest (variabele lengte) met =*

route GET file $path=* => $this->serveFile($path)

4.3.4 Default-waarde met =

route GET page $slug=home => $this->page($slug)

4.3.5 Lengte-eis met .N

route GET code $pin.6 => $this->enter($pin)

4.3.6 Keuzelijsten met :a,b,c

route GET report $range:daily,weekly,monthly => $this->report($range)

Je kunt deze vormen combineren. Voorbeelden:

# enum + verplicht id
route GET export $fmt:csv,json $id => $this->export($fmt, $id)

# enum + default
route GET theme $name:light,dark=light => $this->theme($name)

4.4: Payload check met `@`

Je specificeert exacte body-keys met één @ en comma-separated lijst. De router vergelijkt dit 1-op-1 met de keys uit %payload (exacte set; volgorde zoals de engine die aanlevert).

route POST user @name,email => $this->createUser

method createUser => dx(%payload->name, %payload->email)

Body-keys bind je niet als method-parameters; je leest ze via %payload.

4.5: Targets

Lokale method

route GET profile show => $this->show
method show => view($this->profile)

Externe class-method (static)

route GET api version $major => api::getVersion($major)

4.6: Router activeren

Routes worden pas gematcht na:

app::route()

Plaats deze call bijvoorbeeld in app.phlo (of een andere centrale controller) na je app initialisatie en voor een fallback voor 404 afhandeling.

4.7: Aanbevolen structuur

5: Views

In Phlo definieer je views direct in .phlo-bestanden. Een view is een named of naamloos blok dat met view begint en HTML (plus minimale Phlo-constructies) bevat.

5.1: Declaratie

5.2: Arguments

Views kunnen arguments hebben (incl. defaults):

view($x = 0):
<p>Value: $x</p>

view detail($input):
<p>Input: $input</p>

Aanroepen:

method show => view($this->detail('abc'))
method show2 => view($this->view(5))

Net als bij methods zijn haakjes verplicht zodra je arguments meegeeft.

5.3: Singleline en multiline

5.4: Shorthand HTML

Compacte HTML-shorthand wordt automatisch omgezet:

# shorthand
<p#id.class1.class2/>

# equivalent
<p id="id" class="class1 class2"></p>

5.5: Variabelen en expressies

5.5.1 Directe variabelen en single properties

Je mag direct in de view schrijven:

view($name):
<p>Hallo $name, het is nu $this->time.</p>

Direct toegestaan: gewone variabelen en enkelvoudige property-toegang (zoals $this->time). Geen chained access of function/method calls direct in de HTML.

5.5.2 Functies, methods, chained of complexe expressies

Gebruik expressie-markers:

view($x = 1):
<p>{{ $this->call('test') }}</p>
<p>{( $x > 1 ? 'Meerdere' : 'Enkele' )}</p>
<p>{{ $this->members->all }}</p>

(We leggen hier bewust geen extra semantiek op deze twee vormen; beide zijn bedoeld voor inline expressies.)

5.6: Attribuutwaarden

Attribuutwaarden mogen zonder quotes als ze géén spaties, @ of variabelen bevatten:

view:
<p title=correct>Correcte antwoord</p>
<a href=/test1 data-value="$this->value">Link</a>
<a href=/test2 data-value="{{ $this->compute('value') }}">Link</a>

5.7: Statements

Gebruik control-flow via tags in de HTML:

view:
<p>Lijst:</p>
<foreach $this->list AS $key => $value>
  <p>Item: $key</p>
  <if $value > 1>
    <p>Hoge waarde: $value</p>
  <elseif $value === 1>
    <p>Precies 1: $value</p>
  <else>
    <p>Andere waarde: $value</p>
  </if>
</foreach>

5.8: View renderen

Een view render je met view($this->Naam) (of de naamloze view) als call. Deze call stuurt de output en beëindigt het script. Hetzelfde geldt voor apply().

route GET home => $this->home

method home => view($this->home)

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

Niet doen:

# ❌ fout – views “aan elkaar plakken” bestaat niet; de eerste call beëindigt.
method dashboard {
  view($this->header)
  view($this->content)
}

Wil je meerdere stukken output: maak één view die ze samenvoegt, of bouw het in de view zelf op.

5.9: Best practices

6: CSS

Phlo gebruikt een compacte, puntkomma-vrije CSS-syntaxis binnen <style>-blokken. Je schrijft regels met dubbele punten als scheiding:

Regels:

6.1: `<style>`-blok

<style>
html: height: 100dvh
body {
  background: #947b6c
  font-family: Sans-serif
  p: line-height: 2em
}
</style>

Output (conceptueel):

html { height: 100dvh; }
body { background: #947b6c; font-family: Sans-serif; }
body p { line-height: 2em; }

6.2: Keten & groepen

Keten met dubbele punten; groepeer met komma — de volledige context wordt per item toegepast.

<style>
body: h1, p: \:first-letter: color: green
</style>

Output:

body h1:first-letter,
body p:first-letter { color: green; }

6.3: Media queries ín selector

Je mag @media (…) binnen het selector-blok schrijven; Phlo verplaatst het naar de juiste plek en behoudt de selector-context:

<style>
h1 {
  color: white
  @media (max-width: 768px): color: black
}
</style>

Output:

h1 { color: white; }
@media (max-width: 768px){
  h1 { color: black; }
}

6.4: Variabelen

Phlo ondersteunt CSS-variabelen via $namen. Je kunt variabelen definiëren in :root, of op elk ander niveau — maar :root is gebruikelijk voor globale theming.

<style>
:root {
  $background: #0d0d0d
  $surface: #1a1a1a
  $text: #ffffff
  $accent: #ff4a00
}

body {
  background: $background
  color: $text
}

button {
  background: $accent
  color: $text
}
</style>

Output

:root {
  --background: #0d0d0d;
  --surface: #1a1a1a;
  --text: #ffffff;
  --accent: #ff4a00;
}

body {
  background: var(--background);
  color: var(--text);
}

button {
  background: var(--accent);
  color: var(--text);
}

👉 Phlo zet $variabelen automatisch om naar --custom-properties en gebruikt var(--...) bij aanroepen. Je kunt variabelen overal hergebruiken — ook binnen media queries en nested selectors.

6.5: Dynamische variabelen

Phlo’s frontend engine bevat de library DOM/CSS.var, waarmee je gedefinieerde $variabelen in CSS direct kunt benaderen en aanpassen vanuit JavaScript, via het globale app.var object.

Elke $variabele in je CSS wordt automatisch beschikbaar onder app.var.<naam>.

Voorbeeld

<style>
:root {
  $background: #0d0d0d
  $text: #ffffff
}
</style>

<script>
app.var.background = '#000000'
const textColor = app.var.text
</script>

👉 Deze aanpassingen werken real-time in de browser en beïnvloeden direct alle elementen die de variabele gebruiken.

Je kunt dit gebruiken voor o.a.:

Werking

6.6: Volledig voorbeeld

Input

html: height: 100dvh
body {
  background: #947b6c
  font-family: Sans-serif
  p: line-height: 2em
}
body: h1, p: \:first-letter: color: green
h1 {
  color: white
  @media (max-width: 768px): color: black
}
p {
  color: navy
  \:last-child: color: yellow
}

Output

body {
  background: #947b6c;
  font-family: Sans-serif;
}
body h1:first-letter,
body p:first-letter {
  color: green;
}
body p {
  line-height: 2em;
}
h1 {
  color: white;
}
html {
  height: 100dvh;
}
p {
  color: navy;
}
p:last-child {
  color: yellow;
}
@media (max-width: 768px){
  h1 {
    color: black;
  }
}

6.7: Best practices

7: ORM

Phlo bevat een krachtige ingebouwde ORM waarmee je database-tabellen als klassen definieert. Modellen kunnen snel via columns of uitgebreid via een declaratief schema. Records worden als instances behandeld, met ondersteuning voor props, methods, views, relaties en meerdere database-engines.

7.1: Basisprincipes

Een ORM-model is een .phlo-bestand met:

Voorbeeld:

@ class: user
@ extends: model

view => $this->name

static table = 'users'
static columns = 'id,name,email,active,created'

7.2: Modellen definiëren

7.2.1 Plat met columns (snel en licht)

Gebruik columns voor eenvoudige tabellen:

@ class: shipment
@ extends: model

view: $this->destination ($this->user)

static table = 'shipments'
static order = 'changed DESC'
static columns = 'id,user,destination,costs,valid,weight,shipped,created,changed'
static objParents = ['user' => 'user']
@ class: user
@ extends: model

view => $this->name

static table = 'users'
static order = 'changed DESC'
static columns = 'id,name,email,level,active,created,changed'
static objChildren = ['shipments' => 'shipment']

7.2.2 Met schema en field(...) (rijk en declaratief)

Met schema definieer je velden, relaties en UI in één keer:

@ class: shipment
@ extends: model

view: $this->destination ($this->user)

static table = 'shipments'
static schema => arr (
    id: field (type: 'token', length: 4, title: 'ID'),
    destination: field (type: 'text', required: true, search: true),
    user: field (type: 'parent', obj: 'user', required: true),
    costs: field (type: 'price', prefix: '€ '),
    valid: field (type: 'bool'),
    attachments: field (type: 'child', obj: 'attachment', list: true),
)
@ class: user
@ extends: model

view => $this->name

static table = 'users'
static schema => arr (
    id: field (type: 'token'),
    name: field (type: 'text', search: true, required: true),
    email: field (type: 'email', required: true),
    shipments: field (type: 'child', obj: 'shipment'),
    groups: field (type: 'many', obj: 'group', table: 'user_groups'),
)

schema is vooral krachtig in combinatie met PhloCMS, maar werkt ook standalone.

7.3: CRUD

# Ophalen
$user = user::record(id: 1)
$list = shipment::records(order: 'created DESC')

# Aanmaken
$shipment = shipment::create(destination: 'Parijs', user: 1)

# Bewerken & opslaan
$shipment->destination = 'Lyon'
$shipment->objSave

# Verwijderen
shipment::delete('id=?', $shipment->id)

7.4: Relationele navigatie

Relaties zijn beschikbaar via properties:

Type Declaratie Gebruik
parent type: parent $shipment->user
child type: child $user->shipments
many type: many $user->groups

Many-to-many

type: many gebruikt een pivot-tabel:

groups: field (
    type: 'many',
    obj: 'group',
    table: 'user_groups',
)

Navigatie:

$user = user::record(id: 1)
foreach ($user->groups as $group)
  echo $group->title

Relaties worden batch-loaded voor performance. Er vinden geen cross-DB joins plaats; elke class laadt uit zijn eigen engine.

7.5: Instance dynamiek

Elke record is een echte instance van je modelklasse. Je kunt props, methods en views gebruiken om virtuele velden, berekeningen of representaties toe te voegen:

@ class: shipment
@ extends: model

prop summary => $this->destination.' ('.$this->user.')'
method tax => $this->costs * 0.21

view:
<p>$this->summary</p>
<p>{( $this->tax )}</p>

Gebruik:

$shipment = shipment::record(id: 'AB12')
echo $shipment->summary
echo $shipment   # gebruikt view als string

Props en methods werken altijd op de record-instance, niet statisch.

7.6: Filtering en queries

Alle querymethodes accepteren named arguments en SQL-achtige filters:

shipment::records(destination: 'Parijs')
shipment::records(where: 'valid=1 AND weight>10')
shipment::pair(columns: 'id,destination')

Ondersteund: where, order, group, joins, caching en schema-bewuste kolommen.

7.7: Caching en prestaties

De ORM gebruikt interne buffers (objRecords, objLoaded) voor relationele lookups en optionele APCu caching via:

static objCache = true       # 1 dag
# of
static objCache = 600        # 10 minuten

Records en relaties worden per batch geladen. Gebruik records() voor bulkselecties i.p.v. record() in lussen.

7.8: Meerdere engines

Phlo ondersteunt meerdere backends via prop DB. Standaard is %MySQL, maar je kunt elke engine per model instellen.

SQLite

prop DB => %SQLite(data.'users.db')
@ class: notes
@ extends: model

prop DB => %SQLite(data.'notes.db')
static table = 'notes'
static columns = 'id,title,body'

PostgreSQL

prop DB => %PostgreSQL
@ class: invoices
@ extends: model

prop DB => %PostgreSQL
static table = 'invoices'
static columns = 'id,customer_id,total,created'

Tabellen op verschillende engines kunnen gecombineerd worden in relaties; elke class haalt zijn eigen data op.


/data/creds.ini

Voor engines zoals MySQL en PostgreSQL plaats je je credentials in:

/data/creds.ini
[mysql]
host     = localhost
database = db_name
user     = db_user
password = db_password

[postgresql]
host     = localhost
database = my_pg_db
user     = pg_user
password = pg_pass

Phlo laadt deze automatisch via %creds->....

7.9: Overzicht functies

Functie / Property Type Beschrijving
record(...) static Haalt 1 record (of null)
records(...) static Haalt meerdere records (array)
create(...) static Insert + ophalen
objSave instance Insert of update
delete(where, …) static Verwijderen
pair, item, column static Snelle queryhelpers
objParents / schema: parent declaratief Parent-relaties
objChildren / schema: child declaratief Child-relaties
schema: many declaratief Many-to-many
objCache static Optionele APCu caching
prop DB static Per-model engine

7.10: Best practices

8: Instance Management

Phlo gebruikt een eigen instance manager om objecten efficiënt en voorspelbaar te initialiseren en te hergebruiken. Dit systeem bepaalt wanneer controllercode wordt uitgevoerd, hoe instanties worden opgeslagen en hoe cirkelreferenties worden voorkomen.

8.1: Basisuitleg

Wanneer je een .phlo-bestand definieert, wordt dit tijdens de buildfase omgezet naar een class. Elke aanroep naar een object via %naam gaat via de instance manager (phlo() in /phlo/phlo.php).

Voorbeeld:

prop title = 'Welkom'

route GET home => $this->main

method main => view($this->home)

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

Wanneer de route /home wordt aangeroepen:

  1. De instance manager kijkt of er al een instantie van deze class bestaat.
  2. Zo niet, dan wordt deze aangemaakt en opgeslagen.
  3. Na het aanmaken wordt de controllercode uitgevoerd (zie §8.2).
  4. Vervolgens wordt de gevraagde method aangeroepen.

8.2: Controllercode

Alle code in een .phlo-bestand die niet onder route, prop, static, method, function, view, <style> of <script> valt, is controllercode. Deze code wordt na instantiatie uitgevoerd, zodra de instantie volledig bestaat.

Voorbeeld:

prop ready = false

%session->start()   # controllercode (top-level)
$this->ready = true

8.3: De rol van `__handle()`

Phlo genereert voor elke class een speciale __handle() methode. Deze wordt aangeroepen door de instance manager wanneer een instantie wordt opgevraagd via %naam.

__handle():

Je hoeft __handle() zelf niet aan te roepen of te overschrijven — het is onderdeel van de gegenereerde klasse en de instance manager.

8.4: Lazy initialisatie

Omdat controllercode pas na constructie wordt uitgevoerd, kunnen instanties naar elkaar verwijzen zonder dat er ongewenste recursieve aanmaak plaatsvindt.

Voorbeeld:

# file a.phlo
prop message = 'A ready'

# file b.phlo
prop message = 'B ready'

# file main.phlo
route GET test => $this->show

method show {
  dx(%a->message, %b->message)
}

8.5: Best practices

9: Tooling & CLI

Phlo heeft een zeer lichtgewicht toolchain. Er is geen externe compiler of CLI-framework nodig: alles draait op de Phlo-engine zelf in /phlo/. Tijdens requests zorgt Phlo automatisch voor het bouwen en updaten van de gegenereerde PHP-, JS- en CSS-bestanden. Je kunt dit proces handmatig starten voor CI/CD of pre-release builds, maar in normale setups is dat niet nodig.

9.1: Buildproces

Phlo transpileert .phlo-bestanden naar PHP, JS en CSS. Dit gebeurt:

De entrypoints:

Bij elke request kijkt Phlo:

  1. of er wijzigingen zijn in de bronbestanden,
  2. of de gegenereerde bestanden up-to-date zijn,
  3. en voert zo nodig een incrementele build uit.

9.2: CLI

Phlo heeft geen extern CLI-framework nodig; je kunt gewoon php gebruiken. Voor speciale situaties (prebuilds, CI, staging) kun je handmatig builden door:

php -r "require 'phlo/build.php'; phlo_build();"

of vanuit een eigen PHP-script:

<?php
require 'phlo/build.php';
phlo_build();

Dit doorloopt hetzelfde buildproces als bij runtime, maar dan buiten de request context.

Er is dus geen phlo CLI command of package. Je gebruikt gewoon PHP zelf.

9.3: Debugmodus

Debugmodus wordt ingesteld in data/app.json via:

{
  "debug": true
}

In debugmodus:

9.4: Deployment

Voor productie:

  1. Zet "debug": false in data/app.json.
  2. Voer een handmatige phlo_build() uit zodat alle bestanden vooraf zijn getranspileerd.
  3. Zet de /www/ map online (inclusief app.php, app.js, app.css).
  4. Zorg dat de /php/ map met gegenereerde backend code meeverhuist.

Phlo heeft geen dependencies op npm, webpack of andere toolchains. Alle tooling is ingebouwd en draait direct onder PHP.

9.5: Best practices


✅ Juiste uitleg buildproces (JIT + handmatig via phlo_build()) ✅ Geen fake CLI commands ✅ Debugmodus zoals in app.json ✅ Deployment zoals Phlo dat echt doet ✅ Correcte omgang met /www/app.js en /www/app.css (nooit handmatig wijzigen)

10: Vertalingen

Phlo heeft ingebouwde ondersteuning voor meertaligheid en dynamische vertaling. De engine bevat functies en libraries waarmee je teksten inline kunt vertalen, taalwissels kunt toepassen, en async vertalingen kunt laden zonder de gebruikerservaring te onderbreken.

10.1: Taalhelpers

Phlo biedt voor de meest gebruikte talen korte helperfuncties, die je direct in views en code kunt gebruiken:

Voorbeeld in een view:

view:
<p>{( nl('Hallo wereld') )}</p>
<p>{( en('Hello world') )}</p>

De taalhelpers:

10.2: Overig gebruik

Voor dynamische vertalingen biedt Phlo twee kernfuncties: translate() en translation().

translate()

<p>{( translate('welcome_message') )}</p>

translation()

<p>{( translation('dynamic_intro_text') )}</p>

10.3: Dynamische taalkeuze

De actieve taal kan dynamisch ingesteld en gewijzigd worden via de frontend. Als de gebruiker van taal wisselt, wordt de UI automatisch hervertaald zonder reload.

Typisch verloop:

  1. App start met een standaardtaal (bijv. nl).
  2. De gebruiker kiest een andere taal.
  3. De frontend engine wisselt de taalcode.
  4. Teksten die via translation() zijn ingeladen, worden hervertaald zodra de nieuwe taal beschikbaar is.
  5. Teksten via helpers zoals nl() of en() blijven hun vaste waarde tonen.

10.4: Best practices

11: Geavanceerd

Phlo is ontworpen als een modulair systeem. Je kunt delen van de engine los inzetten, combineren met bestaande projecten of uitbreiden met eigen functionaliteit, zonder de kern te hoeven wijzigen.

11.1: Modulair gebruik

Phlo kan zowel als volledig framework worden ingezet, als in delen. Bijvoorbeeld:

Voorbeeld: alleen de frontend gebruiken in een statische site

<script src="/app.js" defer></script>

en vervolgens Phlo-frontend functies aanspreken zoals app.apply() en app.state zonder .phlo backend.

Of: Phlo opnemen in een bestaande PHP-applicatie en slechts een paar .phlo bestanden in sources toevoegen. Phlo transpileert die dan naar /php/, waarna je de gegenereerde classes direct kunt gebruiken.

11.2: Integratie

Je kunt Phlo integreren in een bestaande codebase door:

  1. De Phlo engine in een submap te plaatsen, bijv. /vendor/phlo/.
  2. app.php toe te voegen als centrale router voor alle niet-bestaande routes.
  3. In app.json het build.sources-veld aan te vullen met de paden van je bestaande code, naast de Phlo mappen.

Voorbeeld data/app.json:

{
  "id": "LegacyApp",
  "version": ".1",
  "host": "localhost",
  "dashboard": "phlo",
  "debug": true,
  "build": {
    "libs": ["session", "json"],
    "sources": [
      "%app/",
      "/legacy/phlo/"
    ]
  }
}

Hiermee kun je bestaande PHP code behouden en toch .phlo bestanden toevoegen. Phlo transpileert die en voegt ze toe aan je project, zonder dat je je hele structuur hoeft om te gooien.

11.3: Best practices

12: Bijlagen

De bijlagen bevatten kant-en-klare voorbeelden, projecttemplates en extra showcases die laten zien hoe Phlo in de praktijk wordt toegepast. Dit onderdeel is bedoeld als naslagwerk en inspiratiebron — niet als primaire documentatie.

12.1: Codevoorbeelden

Een verzameling kleine, goed leesbare .phlo-fragmenten die veelgebruikte patronen illustreren:

Basisroute met view

prop title = 'Welkom'

route GET home => $this->main

method main => view($this->home)

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

Route met variabele

route GET user $id => $this->show($id)

method show($id) {
  $user = %users->record(id: $id)
  view($this->profile, $user)
}

view profile($user):
<h1>$user->name</h1>

Async vertaling

view:
<p>{( translation('welcome_text') )}</p>

12.2: Projecttemplate

Een minimale projectstructuur met alle verplichte onderdelen:

/www/
  app.php       ← centraal entrypoint
  app.js        ← gegenereerde frontend bundle
  app.css       ← gegenereerde CSS
/data/
  app.json      ← verplichte configuratie
/phlo/          ← Phlo engine
/php/           ← getranspileerde backend code (automatisch)
/app/           ← je Phlo bronbestanden (*.phlo)

Dit is de aanbevolen basisstructuur. Extra sources kunnen in data/app.json worden gedefinieerd als je meerdere bronpaden wilt combineren.

12.3: CSS Showcase

Een visueel voorbeeld van Phlo’s compacte CSS-syntaxis:

Input

<style>
html: height: 100dvh
body {
  background: #947b6c
  font-family: Sans-serif
  p: line-height: 2em
}
body: h1, p: \:first-letter: color: green
h1 {
  color: white
  @media (max-width: 768px): color: black
}
p {
  color: navy
  \:last-child: color: yellow
}
</style>

Output

body {
  background: #947b6c;
  font-family: Sans-serif;
}
body h1:first-letter,
body p:first-letter {
  color: green;
}
body p {
  line-height: 2em;
}
h1 {
  color: white;
}
html {
  height: 100dvh;
}
p {
  color: navy;
}
p:last-child {
  color: yellow;
}
@media (max-width: 768px){
  h1 {
    color: black;
  }
}

12.4: ORM Modellen

Voorbeeld van een typische ORM-type-definitie in Phlo:

type users {
  static columns = null

  field(id, type: number, auto: true)
  field(name, type: text)
  field(email, type: text)
}

route GET users => $this->overview

method overview {
  foreach (%users->records(order: 'name') as $u)
    dx($u->id, $u->name)
}

Enkel record ophalen:

$user = %users->record(id: 1)
dx($user->name)

12.5: Verdere bronnen