3: Runtime-configuratie
data/app.json beschrijft de build, wat getranspiled wordt. Dit hoofdstuk behandelt de runtime, wat er gebeurt bij elke aanvraag. Die configuratie bevindt zich in www/app.php (en, voor stage/release, release/www/app.php) als argumenten voor phlo_app(...).
<?php
require('/opt/phlo/phlo.php');
phlo_app (
id: 'Example',
host: 'dev.example.nl',
auth: true,
build: true,
debug: true,
app: '/var/www/example.nl/',
)
Elke sleutel is een genaamd argument dat ook een PHP constante wordt, overal in je app beschikbaar als een los woord (host, data, composer, enzovoort). Hetzelfde geldt voor je eigen aangepaste sleutels.
3.1: Identiteit en host
| Sleutel | Standaard | Doel |
|---|---|---|
id |
geen | Vrije naam voor de app, gebruikt door het Phlo Control Center, het Phlo Dashboard en logs |
host |
null |
Vhost waarop dit toegangspunt is toegestaan om te draaien. Phlo weigert verzoeken die niet overeenkomen, geen onbedoelde cross-host reacties |
3.2: Modi: `build`, `debug`, `auth`, `thread`
| Sleutel | Standaard | Effect |
|---|---|---|
build |
false |
Schakelt de build/lint/reflect CLI in en laat Phlo gewijzigde bronnen per verzoek detecteren en opnieuw opbouwen |
debug |
false |
Laadt debug.php, activeert debug() / dx() / debug helpers, geeft volledige stack traces in plaats van generieke 500-fouten |
auth |
false |
Site-brede HTTP Basic auth met inloggegevens in data/auth.ini |
thread |
false |
Worker-modus (zie X.7), true = onbeperkt, integer = aantal verzoeken per worker |
Combineer niet: build: true en thread: true. Build schrijft bestanden tussen verzoeken; in een langdurige worker is dat onveilig. Phlo gooit een runtime-fout als beide aan staan.
Vereist: auth: true heeft build: true nodig. Phlo geeft een foutmelding bij het opstarten als auth is ingesteld zonder build. (Dit is een implementatiedetail: de site-brede auth handler wordt meegeleverd in de build-laag. Om een host zonder build-fase te beschermen, plaats je HTTP Basic auth in je webserver voor Phlo.)
Control Center pad: wanneer build: true en debug: true, wordt het Phlo Control Center automatisch gemonteerd op /phlo, geen configuratie nodig. Gebruik de optionele control: sleutel om het op een ander pad te monteren (control: 'admin' serveert het op /admin) of stel control: false in om het uit te schakelen. Buiten build+debug is het ongeacht uitgeschakeld.
3.3: Paden
Alleen app is vereist. De rest valt terug op subdirectories van app:
| Sleutel | Standaard | Bedoeld voor |
|---|---|---|
app |
(vereist) | App root |
data |
<app>/data/ |
Config (app.json, auth.ini), referenties, runtime status |
php |
<app>/php/ |
Gegenereerde PHP, wijzig dit alleen wanneer de release-uitvoer elders is opgeslagen |
www |
<app>/www/ |
Web root |
Je release entrypoint stelt doorgaans app: '<app>/release/' en php: '<app>/release/' in, zodat de release-uitvoer wordt geserveerd zonder buildmodus.
3.4: Control Center en WebSocket
| Sleutel | Standaard | Effect |
|---|---|---|
control |
'phlo' met build+debug, anders false |
URL-prefix waaronder de control UI zich bevindt. Bijvoorbeeld 'beheer' → /beheer; false = uit |
daemon |
null |
Poort van de Phlo Daemon voor deze app. Wordt de constante daemon, gebruikt door wsCast() en de runtime helpers |
De control UI vereist build: true. Zie de hoofdstukken over Daemon en WebSocket voor de setup van de Phlo Daemon.
3.5: Composer autoload
Wil je PHP-pakketten uit vendor/ gebruiken? Geef het pad op:
phlo_app (
composer: '/var/www/example.nl/',
...
)
Phlo registreert vervolgens een lazy autoloader die alleen <composer>/vendor/autoload.php laadt wanneer een onbekende klasse moet worden opgelost. Geen opstartkosten als je nooit Composer-pakketten aanraakt; volledige Composer autoload zodra je dat wel doet.
Conventie: composer: '<app>/data/' (de composer.json en vendor/ bevinden zich dan in data/, buiten de webroot).
3.6: Trace en CLI
| Sleutel | Standaard | Effect |
|---|---|---|
trace |
false |
Schakelt de trace-modus in, zie het Trace-hoofdstuk |
cli |
'php-zts' (indien ZTS) of 'php' |
Pad naar de PHP-binaire die Phlo gebruikt voor subprocessen (build, taken, websocket). Overschrijf dit als je systeem niet-standaard PHP-binaries heeft |
cliis een string in v4 (een pad), niet een boolean zoals in v1.
3.7: Werknormregels
thread: true zet Phlo in de modus voor langdurige worker (FrankenPHP, ReactPHP, RoadRunner). De runtime blijft in het geheugen tussen verzoeken. Drie regels die je moet weten:
1. Geen die() of exit() in het HTTP-pad. Beide beëindigen de hele worker. Gebruik return of laat een beëindigende oproep (view(), apply(), location()) de respons verzenden.
2. Geen verzoekstatus in statische eigenschappen. Statics overleven tussen verzoeken in een worker; gebruikersgegevens van verzoek A lekken naar verzoek B. Voor caches die worker-veilig zijn (klasstructuur, berekende metadata), zijn statics prima, niet voor sessie-, gebruikers-, payload-, tijd- of DB-status.
3. Markeer langdurige objecten met $objPers = true. Standaard wist Phlo zijn instantiekaart tussen verzoeken. Voor objecten die je expliciet wilt hergebruiken (DB-verbinding, voorbereide statements), stel $this->objPers = true in zodat de opruiming ze met rust laat.
3.8: Aangepaste sleutels: je eigen constanten
Elke extra benoemde parameter die je doorgeeft aan phlo_app() wordt automatisch een PHP-constante. Dat is de manier om app-brede paden of functie-vlaggen te declareren:
phlo_app (
app: '/var/www/example.nl/',
langs: '/var/www/example.nl/langs/',
files: '/var/www/example.nl/files/',
uploads: '/var/www/example.nl/data/uploads/',
)
Directly bruikbaar in .phlo daarna:
$dict = parse_ini_file(langs.'en.ini')
$path = files.'avatars/'.$user->id.'.jpg'
$url = uploads
reflect::runtime toont alle gedefinieerde constanten. Handig om te ontdekken wat een app biedt zonder www/app.php te openen.
3.9: Voorbeeld: dev en release naast elkaar
phlo_app (
id: 'Example',
host: 'dev.example.nl',
auth: true,
build: true,
debug: true,
trace: true,
app: '/var/www/example.nl/',
composer: '/var/www/example.nl/data/',
daemon: 3001,
langs: '/var/www/example.nl/langs/',
)
phlo_app (
id: 'Example',
host: 'example.nl',
thread: true,
app: '/var/www/example.nl/release/',
php: '/var/www/example.nl/release/',
data: '/var/www/example.nl/data/',
composer: '/var/www/example.nl/data/',
daemon: 3001,
langs: '/var/www/example.nl/langs/',
)
Dev heeft build, debug, auth, het Control Center en trace. Release heeft worker mode (thread) en wijst de webroot/php output naar release/. data, composer, langs blijven hetzelfde, dat is gedeelde staat.
3.10: De request- en response-objecten
Twee runtime-objecten dragen elke aanvraag. %req (lezen) en %res (schrijven) zijn altijd beschikbaar.
%req berekende eigenschappen:
| Eigenschap | Bevat |
|---|---|
%req->method |
HTTP-werkwoord, in hoofdletters |
%req->path |
Aanvraagpad zonder leidende schuine streep |
%req->part($i) |
Padsegment op index |
%req->query |
Geparsed querystring-array |
%req->async |
true wanneer de aanvraag afkomstig is van phlo.js (SPA-navigatie, asynchrone formulieren) |
%req->cli |
true wanneer uitgevoerd vanaf de opdrachtregel |
%req->secure, %req->scheme, %req->base, %req->url |
URL-onderdelen, eenmaal berekend |
%req->referer, %req->acceptLanguage |
Veelvoorkomende headers, genormaliseerd |
%res oppervlak:
| Lid | Doet |
|---|---|
%res->header($key, $value) |
Plaatst een responsheader in de wachtrij (verzonden bij renderen) |
%res->type |
Content-Type voor de respons |
%res->text($body) / %res->json(...) / %res->xml($body) |
Stelt de body in (en type voor json/xml); ketenbaar |
%res->render($code = null) |
Verzendt status, headers en body; markeert de respons als voltooid |
%res->streaming |
true schakelt apply() over naar onmiddellijke flush-per-opdracht (zie het WebSocket-hoofdstuk) |
%res->status, %res->done |
Statuscode; of de output al is verzonden |
output($content, $filename = null, $attachment = null, $file = null, $code = null, $type = null) is de responsfunctie voor bestanden, blobs en JSON-met-een-status: het dient een bestand (mime op naam, optionele attachment), of JSON wanneer $content een array is (output(['id' => $id], code: 201), output(['error' => 'not found'], code: 404)); type overschrijft de content-type voor een vooraf-gecodeerde string body. view()/apply()/output()/error()/location() zijn de responsfuncties die app-code gebruikt; de %res->json/text/xml/render leden hierboven zijn de low-level primitieve waar ze op bouwen, voor de zeldzame hand-geassembleerde respons (aangepaste headers, een speciale content-type). Verpak ze niet in per-app jsonOut()/respond() helpers.
Les.
die($content)lijkt alsof het "een respons verzendt", maar het omzeiltrender(): geplaatste headers (inclusief Content-Type) verlaten nooit de server, en in worker-modus beëindigtdie()de hele worker. Deze site heeft zijn machine-leesbare eindpunten wekenlang alstext/htmlop deze manier aangeboden. Eindig altijd metoutput(...),view(),apply(),location()of een expliciete%res->...->render().
Runtime-fouten komen terecht in data/errors.json, gekeyd door een stabiele 8-teken referentie-id (een hash van host, locatie en het pad-afgekapte bericht) zodat dezelfde fout in één vermelding met een teller en een laatste-verschijnings-tijdstempel wordt gededupliceerd. Elke vermelding registreert ook het bron-gemapte .phlo bestand en regel, de host en het aanvraagpad; de nieuwste 200 worden bewaard. Lees ze met reflect::errors [limit] of in het Phlo Control Center.
Die referentie-id is de veilige handle tussen een gebruiker en jou: het verschijnt op de productie-foutpagina (Reference: a3f9c1d2) en reist mee in de error payload van asynchrone/JSON-responsen, zodat een gebruiker deze kan citeren en jij de volledige vermelding kunt opzoeken in errors.json. De productiepagina drukt nooit het bericht of de trace af; die zijn alleen voor debug.
Een app kan zijn eigen gebrandmerkte foutpagina renderen door een statische errorPage(int $code, string $id, ?string $msg): string op zijn app klasse te declareren. De engine roept deze statisch aan met de HTTP-code, de ondoorzichtige referentie-id, en het echte foutbericht alleen onder debug (null anders), zodat een productiepagina geen interne details kan lekken terwijl een debug-omgeving het bericht op een gestileerde pagina kan tonen. Als de hook een fout gooit of niets retourneert, valt de engine terug op zijn eigen pagina: de diagnostiek onder debug, de minimale pagina anders.