9: Instantiebeheer
Phlo gebruikt zijn eigen instance manager om objecten efficiënt en voorspelbaar te initialiseren en opnieuw te gebruiken. Dit systeem bepaalt wanneer controller code wordt uitgevoerd, hoe instanties worden opgeslagen en hoe circulaire referenties worden voorkomen.
9.1: De basisprincipes
Wanneer je een .phlo-bestand definieert, verandert de build-fase het in een klasse. Elke aanroep naar een object via %name gaat via de instance manager (phlo() in /phlo/phlo.php).
Voorbeeld:
prop title = 'Welcome'
route GET home => $this->main
method main => view($this->home)
view home:
<h1>$this->title</h1>
Wanneer de route /home wordt aangevraagd:
- De instantiebeheerder controleert, via de identiteit die
__handle()(of de standaardregel) afleidt van de argumenten, of er al een bijpassende instantie bestaat. - Zo niet, dan wordt deze gemaakt en opgeslagen.
- Na creatie wordt de controllercode uitgevoerd (zie §8.2).
- Vervolgens wordt de aangevraagde methode aangeroepen.
9.2: Controller code
Alle code in een .phlo-bestand die niet behoort tot route, prop, static, method, function, view, <style> of <script> is controller code. Deze code wordt uitgevoerd na de instantie, zodra de instantie volledig bestaat.
Voorbeeld:
prop ready = false
%session->start()
$this->ready = true
De laatste twee regels zijn controllercode omdat ze op het hoogste niveau staan.
- Deze code wordt uitgevoerd na de constructie, elke keer dat de manager een nieuwe instantie aanmaakt: eenmaal voor een gedeelde instantie, bij elke aanroep voor klassen waarvan
__handle()nullretourneert. - Het verschil met
__constructis dat de instantie volledig bestaat tegen de tijd dat de controllercode wordt uitgevoerd, wat cirkelreferenties en onvolledige objecten voorkomt.
9.3: De rol van `__handle()`: instantie-identiteit
__handle() is een optionele statische die je zelf definieert om de identiteit van instanties te beheersen. Het is verweven met __construct: de transpiler voegt de parameterlijst van je constructor eraan toe, zodat de handle-expressie spreekt in constructorargumenten, en de instantiebeheerder roept het aan met dezelfde argumenten waarmee het op het punt staat te construeren:
static __handle => "img/$file"
method __construct(public string $file)
De retourwaarde bepaalt hoe de manager de instantie behandelt:
__handle() retourneert |
Betekenis |
|---|---|
| een string | De registersleutel: elke aanroep die dezelfde sleutel produceert, retourneert dezelfde gedeelde instantie (multiton op argument). %img voor hetzelfde bestand is één object; een vlag kan ook deel uitmaken van de identiteit, zoals in "INI/$path$filename".(!$parse ? '/0' : void). |
null |
Nooit gecached: elke aanroep construeert een nieuwe instantie (cookiewall, field). |
true |
Hergebruik de instantie die geregistreerd is onder de klassenaam en objImport(...) de nieuwe argumenten erin. |
Een klasse zonder __handle() volgt de standaardregel: zonder argumenten aangeroepen is het een singleton die is gekeyed op zijn naam; met argumenten aangeroepen is het een nieuwe, niet-geregistreerde instantie.
Je definieert __handle(); de instantiebeheerder roept het aan. Roep het nooit zelf aan.
9.4: Luie initialisatie
Omdat controllercode alleen na constructie wordt uitgevoerd, kunnen instanties elkaar verwijzen zonder ongewenste recursieve creatie te activeren.
Voorbeeld:
a.phlo:
prop message = 'A ready'
b.phlo:
prop message = 'B ready'
main.phlo:
route GET test => $this->show
method show {
dx(%a->message, %b->message)
}
%aen%bworden lui aangemaakt.- De controllercode in beide bestanden wordt uitgevoerd zodra hun instantie volledig bestaat.
- Je kunt vrijelijk naar instanties van elkaar verwijzen, omdat de instantie al bestaat voordat de controllercode wordt uitgevoerd.
9.5: De obj basis klasse: powertools
Elke getranspileerde klasse breidt obj uit, en obj is meer dan __get/__set. Dit zijn de tools die je gebruikt wanneer een klasse dynamisch moet functioneren.
Het ad-hoc waardeobject. obj is ook de alledaagse container die je direct creëert: obj(x: 1, y: 2) geeft je een levend object in één aanroep, zonder dat een klasse-definitie nodig is. Het lezen van een sleutel die niet bestaat, retourneert null in plaats van een waarschuwing, zodat optionele gegevens geen isset dans nodig hebben. Wijs een closure toe en het wordt een gebonden lid: na $point->sum = fn() => $this->x + $this->y, evalueren zowel $point->sum als $point->sum() het met $this gebonden aan het object. foreach doorloopt de opgeslagen gegevens en json_encode($point) serializeert precies die gegevens (closures en berekende waarden blijven buiten beschouwing), zodat een obj schoon kan worden doorgegeven aan views, payloads en JSON-responses.
$point = obj(x: 1, y: 2)
$point->sum = fn() => $this->x + $this->y
$total = $point->sum
Interceptie hooks. Implementeer objCall, objGet of objSet om de toegangsketen te onderscheppen. Het retourneren van null valt terug op het normale gedrag; alles wat niet-null is, zorgt voor een kortsluiting:
method objGet($key) => $this->cache[$key] ?? null
method objCall($method, ...$args) => str_starts_with($method, 'find') ? $this->finder($method, $args) : null
method objSet($key, $value) => $key === 'id' ? true : null
objGet wordt uitgevoerd vóór de data/sluiting/methode/prop lookup bij elke lezing, objCall bij elke onbekende methode-aanroep, en objSet vóór elke schrijfoperatie (een niet-nul retourwaarde onderdrukt de schrijfoperatie). Dit is het mechanisme achter decorators, lazy loading en read-only guards.
Gebonden closures. Wijs een closure toe en deze bindt zich aan de instantie: $obj->greet = fn() => "Hi $this->name", later wordt $obj->greet() uitgevoerd met $this gebonden. Handig voor per-instantie gedrag zonder subclassing.
Data API. objImport(name: 'x', age: 3) wijst in bulk toe en retourneert $this (ketenbaar). objKeys(), objValues() en objLength() inspecteren de data; objClear() wist deze. Itereren over een obj (foreach $record AS $key => $value) en json_encode($record) onthullen precies de opgeslagen data. Elke schrijfoperatie schakelt objChanged in, de dirty flag die de ORM gebruikt om te beslissen of objSave iets schrijft.
Gecachede computed props. prop x => ... cachet bij de eerste toegang; de argumentvorm cachet per argumentset. Hetzelfde geldt voor computed statics, gecached per klasse.
Worker persistentie. prop objPers = true zorgt ervoor dat een instantie overleeft tussen requests in worker-modus: het phlo() register houdt alleen objPers instanties vast bij zijn reset per request. Juist voor DB-verbindingen en geparsed configuraties; fout voor alles wat request- of gebruikersgebonden is.
Les. Een gewone prop in een bovenliggende klasse SCHADUWT een computed prop in een kind.
prop dir = voidin een abstracte bovenliggende klasse transpileert naar een echte PHP-eigenschap, zodat de getter van een kindprop dir => guidenooit wordt geraadpleegd:$this->dirleest stilletjesvoid. Wanneer kinderen moeten overschrijven met een computed prop, verklaar de bovenliggende prop ook als computed:prop dir => void.
9.6: Best practices
- Gebruik controllercode voor initiële setup, niet voor logica die afhankelijk is van verzoeken.
- Plaats controllercode bovenaan of direct onder props voor de leesbaarheid.
__constructvangt de instantie-argumenten; houd het daarbij, idealiter met gepromote parameters (method __construct(public string $file)), omdat__handle()de identiteit van de instantie afleidt van diezelfde argumenten vóór de constructie. Zware taken horen thuis in lazy props; app-niveau opstarten in controllercode.- Laat instanties zichzelf lui initialiseren via
%namein plaats van ze handmatig te creëren. - Gebruik controllercode opzettelijk om circulaire referenties op te lossen.