Naslag
Templatesyntax
Routetemplates, herbruikbare blokken, waarden en voorwaardelijke compositie.
Templates gebruiken shortcodes tussen [# en #].
Templates zijn bewust zonder logica. Een template is schone, eenvoudige markup: hij toont waarden, herhaalt een template voor een lijst en kiest hooguit welke template volgt ([#if … tpl=…#]). Beslissingen, berekeningen en datavoorbereiding staan in libraries, waar je ze op één plek kunt lezen en testen. Er zijn geen logicablokken, lussen met voorwaarden of expressies in templates, zoals engines als Twig of Blade die toestaan; logica in markup mengen is precies wat Nimbly vermijdt.
[#shortcode#]
[#shortcode param#]
[#shortcode key=value key2=value2#]
Shortcodes kunnen genest worden: elke parameterwaarde of shortcodenaam kan zelf een shortcode bevatten:
[#get data.users.[#get selected-id#]#]
Dat werkt ook in tpl=-parameters en shortcodenamen, waarmee je templatenamen dynamisch samenstelt:
[#if current_image=(not-empty) tpl=card-image[#if current_image=(empty) echo=-placeholder#]#]
Dit wordt tpl=card-image als er een afbeelding is, en tpl=card-image-placeholder als die er niet is.
Een bijzonder krachtige toepassing is kiezen op contenttype: zet een typeveld direct in de naam van de shortcode om zonder vertakkingen naar een template voor dat type te gaan:
[#card-[#get item.type#]#]
Dit wordt [#card-news#], [#card-event#], [#card-download#] enzovoort, afhankelijk van het type van het item. Dit patroon maakt ketens van [#if item.type=X tpl=Y#] overbodig en groeit netjes mee als er types bijkomen.
Gebruik default= om een geldige shortcodenaam te garanderen als de waarde kan ontbreken of onbekend kan zijn; een shortcode die niet gevonden wordt, verschijnt als letterlijke tekst in de uitvoer:
[#card-[#get item.type default=unknown#]#]
Maak [#card-unknown#] als algemene terugvaloptie (of laat hem leeg om niets te tonen).
Templates zijn gewone bestanden in het beoogde uitvoerformaat (HTML, JSON, CSS, enzovoort). Ze hebben de extensie .tpl.
Routetemplates
Elke URL hoort bij een bestand in ext/uri/:
ext/uri/index.tpl → /
ext/uri/about/index.tpl → /about/
ext/uri/blog/(slug)/index.tpl → /blog/<anything>/
Een routetemplate maakt de pagina:
[#html#]
De main.tpl in dezelfde map bevat de body van de pagina, getoond binnen de HTML-shell. Die hoort altijd bij die route; zelfs ext/uri/main.tpl is alleen de body van de homepage (/), geen algemene layout.
Herbruikbare templates
Staan in ext/tpl/<name>/index.tpl. Je roept ze aan met hun naam als shortcode:
[#hero-section#]
Templates krijgen geen parameters rechtstreeks mee. Om data aan een template te geven, gebruik je [#set ... overwrite#] vóór de aanroep:
[#set back_url=/agenda overwrite#]
[#back-button#]
Lees de variabele in de template met [#get back_url default=/#]. Library-shortcodes (ext/lib/<name>/) werken anders: die krijgen $params en kun je met parameters aanroepen.
Naamgeving van templates
Geef elke herbruikbare template een naam met eerst het type, gevolgd door de route of het onderwerp waar hij bij hoort:
{type}-{route-or-subject}
Types en voorbeelden:
| Type | Gebruikt voor | Voorbeeld |
|---|---|---|
page- |
Template voor een hele pagina (één per route) | page-about, page-projects |
section- |
Een sectie of blok binnen een pagina | section-home-header, section-page-header |
card- |
Een herhaalbare kaart | card-project, card-team-member |
logo- |
Een component die een logo toont | logo-partner, logo-partner-image |
icon- |
Een inline icoon | icon-arrow-right, icon-chevron-right |
form- |
Een formuliercomponent | form-registration |
Subtemplates in een paginamap volgen dezelfde regel, eerst het type:
ext/tpl/page-projects/
section-project-list.tpl
card-project.tpl
card-project-image.tpl
Zet het type nooit aan het eind (project-card, about-page). Met het type vooraan staan alle kaarten, alle secties en alle paginatemplates bij elkaar in het bestandssysteem.
Templates binnen een route
Templates kunnen ook los in een URI-map staan (niet in een submap). Dat zijn geen routes, maar deeltemplates die vanuit de index.tpl of main.tpl van die route worden gebruikt.
ext/uri/new/page-settings-fields.tpl ← partial, not a route
ext/uri/new/index.tpl ← route
ext/uri/new/main.tpl ← route body
Belangrijk: een submap in ext/uri/ maakt altijd een nieuwe route. Een los .tpl-bestand in een URI-map is een deeltemplate voor die route.
route.inc
route.inc wordt alleen gebruikt voor dynamische routes: routes met (param)-segmenten in de URL die gematcht, gevalideerd en geaccepteerd of geweigerd moeten worden. Statische routes (gewone URL-paden zonder parameters) gebruiken geen route.inc; hun index.tpl wordt direct geladen.
Dynamische routes worden niet vanzelf gevonden. Elke route met een (param)-segment heeft ook een record in de resource .routes nodig, anders bereikt het request de route.inc nooit en volgt een 404. Zo heeft ext/uri/en/blog/(slug)/route.inc een .routes-record nodig met route: "en/blog/(slug)" en een passende order, meestal 200. Draai php core/cli/nimbly.php routes:sync om de bestanden ext/uri/**/route.inc te doorzoeken en ontbrekende .routes-records aan te maken.
Een route.inc staat naast index.tpl en bepaalt of het request bij deze route hoort:
<?php
$parts = router_match(__FILE__);
if ($parts === false) return;
$slug = $parts[0];
load_library('data');
load_library('util');
$records = data_read_index('articles', 'title_slug', md5_uuid($slug));
if (empty($records)) return;
set_variable_dot('record', reset($records));
router_accept();
Routerfuncties
| Functie | Beschrijving |
|---|---|
router_match(__FILE__) |
Vergelijkt de huidige URL met de dynamische segmenten van deze route. Geeft een array met de gevonden waarden (één per (param) in het pad), of false als de URL niet past. Roep deze altijd eerst aan. |
router_accept() |
Geeft aan dat deze route.inc het request accepteert. De template-engine gaat verder met index.tpl. |
router_deny() |
Geeft een weigering aan; de router zoekt verder naar een andere passende route. return zonder router_accept() heeft hetzelfde effect. |
Het standaardpatroon: roep router_match() aan, valideer het resultaat, laad en valideer de data, en roep alleen router_accept() aan als alles klopt. Mislukt er iets, doe dan gewoon return; het request valt door naar de volgende route of een 404.
Valideer alle invoer van de route aan de grens
Vertrouw geen enkele waarde die via een URL binnenkomt. Dat geldt voor dynamische padsegmenten, GET- en queryparameters, headers die de route gebruikt, en elke waarde uit het request die via het variabelensysteem van de templates binnenkomt. Geautomatiseerde scanners sturen standaard SQL-fragmenten, shellsyntax, padtraversals, te grote getallen en andere fuzzing naar elke publieke parameter, ook al gebruikt Nimbly geen SQL.
Valideer en normaliseer deze waarden in de route voordat je ze aan een library geeft of ze gebruikt in berekeningen, bij het opzoeken van resources, in bestandspaden, cachesleutels, redirects of commando's:
- Gebruik bij voorkeur een allowlist voor de exacte syntax die je accepteert; probeer niet bekende slechte fragmenten weg te halen.
- Controleer type, lengte, bereik en toegestane waarden waar dat van toepassing is.
- Zet gevalideerde getallen om naar
intoffloatvoordat je ermee rekent. - Houd ruwe tekst uit het request buiten bestands- en cachepaden, tenzij de volledige syntax met een allowlist is gecontroleerd en bewust deel uitmaakt van de cachesleutel.
- Weiger ongeldige invoer meteen met het normale foutantwoord van de route. Routes voor assets die op een API lijken, mogen een lege HTTP 400 geven als foute invoer zichtbaar moet zijn voor rate limiting of botdetectie.
- Valideer zelf in PHP, ook als Apache, een proxy of een CDN dezelfde parameter al filtert; filtering aan de rand is een optimalisatie, niet de vertrouwensgrens.
Een queryparameter ratio voor een thumbnail moet bijvoorbeeld als begrensd positief decimaal getal worden gevalideerd en naar float worden omgezet voordat er met de afbeelding wordt gerekend. Een tekst als ratio=1' OR 1=1 mag nooit bij de berekening van een thumbnail of het aanmaken van een cachebestand komen.
Houd route.inc beperkt tot routinglogica: matchen, valideren, een variabele of twee zetten, accepteren of weigeren. Bedrijfslogica hoort in een library die vanuit index.tpl via een shortcode wordt geladen.
Voeg nooit een
route.inctoe aan een statische route. Een statische route (zoalslogin/,about/) heeft geen(param)-segmenten en wordt altijd geaccepteerd; eenroute.inczonderrouter_accept()geeft een 404. Moet er logica draaien op een statische pagina, zet die dan in een shortcode die vanuitindex.tplwordt aangeroepen, niet inroute.inc.
routeis een gereserveerde shortcodenaam. Er is geen shortcode[#route#]en die kan ook niet worden gemaakt. De bestandsnaamroute.inchoort alleen bij het routingsysteem.