While loop in template parser
Het idee was in eerste instantie dan ook om alleen variabelen in een .phtml bestand af te kunnen drukken, dit is inmiddels uitgebreid met een aantal functies.
Ik kom nu op een lastig punt waar ik al lang mee bezig ben en eigenlijk niet uit kom. Mijn wens is om een set resultaten af te kunnen drukken.
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public function setLoop ($sKey, $aValues)
{
if (is_string($sKey) && is_array($aValues))
{
$this->aLoop[] = $sKey;
$this->aLoopVar[$sKey] = $aValues;
}
else
{
errors::setError('De eigenschappen voor de functie setLoop zijn niet correct.');
}
}
public function regexLoop ()
{
preg_replace_callback ('/(\[loop:(\w+)\])(.*?)(\[\/loop:(\w+)\])/i', array($this, 'replaceLoop'), $this->sContent);
}
public function replaceLoop ($matches)
{
if (in_array($matches[2], $this->aLoop))
{
$this->aLoopSubstr[$matches[2]] = $matches[3];
$this->sContent = str_replace ($matches[0], '<?php for ($i = 0; $i < count($this->aLoopVar[\'test\'][\'id\']); $i++)
{
print(preg_replace("/(\[loopvar:(.*?)\])/", $this->aLoopVar[\'test\'][\'id\'][$i], $this->aLoopSubstr[\'test\']));
}
?>', $this->sContent);
}
else
{
errors::setError('De loop: ' . $matches[2] . ' is niet bekend.');
}
}
test.php
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
error_reporting(E_ALL);
include ('../config.php');
include (FRAMEWORK . 'autoload.php');
// Verbinding maken
$sql = new MySQL('SQL.ini');
// Bestand inladen
$tpl = new parser(HTML . 'test.html');
// Vars, blocks, loops
$tpl->setVar('pagina_titel', 'Kluswerk.NL');
$tpl->setVar('url', ROOT);
$tpl->setBlock('menu');
$tpl->setVar('gebruiker', 'Bas');
$tpl->setVar('is_admin', true);
$aEen = array(1, 2, 3, 4 ,5, 6);
$aTwee = array('eerste titel', 'tweede titel', 'derde titel', 'vierde titel', 'vijfde titel', 'vierde titel', 'vijfde titel');
$tpl->setLoop('test', array('id' => $aEen, 'titel' => $aTwee));
$tpl->parse();
?>
error_reporting(E_ALL);
include ('../config.php');
include (FRAMEWORK . 'autoload.php');
// Verbinding maken
$sql = new MySQL('SQL.ini');
// Bestand inladen
$tpl = new parser(HTML . 'test.html');
// Vars, blocks, loops
$tpl->setVar('pagina_titel', 'Kluswerk.NL');
$tpl->setVar('url', ROOT);
$tpl->setBlock('menu');
$tpl->setVar('gebruiker', 'Bas');
$tpl->setVar('is_admin', true);
$aEen = array(1, 2, 3, 4 ,5, 6);
$aTwee = array('eerste titel', 'tweede titel', 'derde titel', 'vierde titel', 'vijfde titel', 'vierde titel', 'vijfde titel');
$tpl->setLoop('test', array('id' => $aEen, 'titel' => $aTwee));
$tpl->parse();
?>
test.html
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<html>
<head>
<title>{{pagina_titel}}</title>
</head>
Welkom {{gebruiker}}, <br /><br />
[block:menu]Beginpagina | Over mij | Contact<br /><br />[/block:menu]
[if:istrue:is_admin]Bas je bent een admin!<br /><br />[/endif:istrue:is_admin]
[loop:test]De id: [loopvar:id] met de titel [loopvar:titel]!<br /><hr>[/loop:test]<br /><br />
[if:isset:foutieve_prijs]{{foutieve_prijs}}[/endif:isset:foutieve_prijs]
[block:footer]Copyright 2017.[/block:footer]
</html>
<head>
<title>{{pagina_titel}}</title>
</head>
Welkom {{gebruiker}}, <br /><br />
[block:menu]Beginpagina | Over mij | Contact<br /><br />[/block:menu]
[if:istrue:is_admin]Bas je bent een admin!<br /><br />[/endif:istrue:is_admin]
[loop:test]De id: [loopvar:id] met de titel [loopvar:titel]!<br /><hr>[/loop:test]<br /><br />
[if:isset:foutieve_prijs]{{foutieve_prijs}}[/endif:isset:foutieve_prijs]
[block:footer]Copyright 2017.[/block:footer]
</html>
en het resultaat.
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
Welkom Bas,
Beginpagina | Over mij | Contact
Bas je bent een admin!
De id: 1 met de titel 1!
De id: 2 met de titel 2!
De id: 3 met de titel 3!
De id: 4 met de titel 4!
De id: 5 met de titel 5!
De id: 6 met de titel 6!
Beginpagina | Over mij | Contact
Bas je bent een admin!
De id: 1 met de titel 1!
De id: 2 met de titel 2!
De id: 3 met de titel 3!
De id: 4 met de titel 4!
De id: 5 met de titel 5!
De id: 6 met de titel 6!
Ik heb al ontzettend veel manieren geprobeerd om de variabelen in de loop op te slaan etc. dat deze niet wordt overschreven, maar het is mij nog niet gelukt.
ik gebruik nu: $this->aLoopVar[\'test\'][\'id\'][$i] omdat er anders helemaal geen resultaat is.
Gewijzigd op 03/02/2018 17:30:20 door Bas hooff
Het stuk tussen de "loop" tags is dan een aparte parse ronde met de waarden uit het record als "vars".
Ik sla de array ook iets anders op:
Dan hou je alles netjes bij elkaar, en kun je dus het hele record ook in 1x meegeven aan de sub-parse ronde.
Ik merge 'm dan ook nog met de "vars" van de parent parse (dus "gebruiker" en "is_admin" in je voorbeeld), en voeg ook nog een paar "magic" vars toe om handige dingen mee te doen:
- k = key van loop array (soms heeft de "loop" array een key).
- i = index in de array (0, 1, 2, ...)
- r = reverse index in de array (0 = laatste, 1 = op-1-na-laatste, enz)
Die laatste twee zijn handig om opmaak technische dingen te doen als:
En je wilt dat dit "aap, noot, en mies" wordt (laatste record geen trailing komma, wel een "en" d'r voor).
Gewijzigd op 03/02/2018 18:20:41 door Rob Doemaarwat
Bedankt voor je antwoord. Ik zie het verband alleen nog niet tussen het moment van parsen en mijn probleem.
Wellicht had ik zelf wat duidelijker moeten zijn mbt de gewenste oplossing.
De data is zeker aanwezig, zie onderstaande print_r:
Code (php)
1
Array ( [id] => Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 5 [5] => 6 ) [titel] => Array ( [0] => eerste titel [1] => tweede titel [2] => derde titel [3] => vierde titel [4] => vijfde titel [5] => vierde titel [6] => vijfde titel ) )
Deze waarden zou ik graag vervangen voor id en titel:
Code (php)
1
print(preg_replace("/(\[loopvar:(.*?)\])/", $this->aLoopVar[<blok naam(test)>][<array key (id / titel)>][$i], $this->aLoopSubstr[<blok naam(test)>]));
Is dit uberhaupt mogelijk om te bereiken op deze manier ?
Ik bedoel te zeggen dat je nu voor het vervangen van waarden in je loop een andere oplossing kiest dan voor "normale" variabelen ("[loopvar:id]" vs "{{id}}"). Door de constructie binnen je loop gelijk te houden aan die van normale tekst (maar dan ge-escaped) kun je voor het vervangen binnen-de-loop dezelfde techniek gebruiken als voor de rest.
{{id}} is een simpele integer die vervangen kan worden.
[loopvar:id] is een array die doorlopen en afgedrukt dient te worden.
Ik heb daarom een array van records met per record de (herhalende) keys. Binnen zo'n record is de situatie in mijn geval dus gelijk aan die van normale tekst, en kan ik de "vertaling" ook als zodanig afhandelen (de tekst is de "zin" die ik wil herhalen, de data zit in het record). Hopelijk maakt een voorbeeld een beetje duidelijker:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
//platte tekst
$tags = ['name' => 'Rob','id' = 5];
$trans->str('Hallo [name] je ID is [id]',$tags); //-> "Hallo Rob je ID is 5"
//tekst met array
$tags = ['name' => 'Donald','neefjes' => [
['name' => 'Kwik'],
['name' => 'Kwek'],
['name' => 'Kwak']
]];
$trans->str('Hallo [name], je neefjes zijn [{array}neefjes|\[name\], ]',$tags);
//-> "Hallo Donald, je neefjes zijn Kwik, Kwek, Kwak, "
$tags = ['name' => 'Rob','id' = 5];
$trans->str('Hallo [name] je ID is [id]',$tags); //-> "Hallo Rob je ID is 5"
//tekst met array
$tags = ['name' => 'Donald','neefjes' => [
['name' => 'Kwik'],
['name' => 'Kwek'],
['name' => 'Kwak']
]];
$trans->str('Hallo [name], je neefjes zijn [{array}neefjes|\[name\], ]',$tags);
//-> "Hallo Donald, je neefjes zijn Kwik, Kwek, Kwak, "
Het stuk "binnen de loop" (de zin) is dus (zonder escape backslashes) "[name], ". Deze zin wordt voor elk record in de "neefjes" array verwerkt (net als elk ander stuk tekst verwerkt zou worden), en achter elkaar geplakt. Ik hoef dus niet expliciet naar een "loopvar" oid te refereren.
Zoals gezegd heb ik dan nog wat "magic" om het wat mooier te maken:
In jouw voorbeeld lijkt het resultaat meer op een for-lus dan het doel wat ik probeer te bereiken. Of zie ik dit verkeerd ?
Daarnaast ben ik vooral benieuwd hoe zon functie er uit moet komen te zien.
Bas hooff op 03/02/2018 17:29:09:
Ik ben al een langere tijd bezig met mijn eigen templateparser. Dit was niet mijn eerste keuze maar het aanbod is vaak: te complex, traag, groot en bevat veel functionaliteiten die ik niet gebruik.
Maar waarom wil je dit dan per se gebruiken als er alleen maar nadelen lijken te zijn? Waar verdient al deze abstractie zich terug? Ik zie een heleboel code die in wezen een bestaande language construct simuleert? Vanwaar deze aversie voor native PHP?
En nu ben je nog met lijstjes bezig, maar wat als je straks met recursieve boomstructuren moet werken?
Als het voor jou werkt: by all means, maar af en toe kan een bezinningsmoment ook geen kwaad lijkt mij.
Je zou ook kunnen beargumenteren dat PHP zelf ook prima geschikt is als template engine, of iets wat heel dicht bij native PHP staat je ook een heel eind op weg zou kunnen helpen. Zonder het leren van een compleet nieuwe pseudo syntax.
In mijn onderzoek hebben al genoeg mensen aangegeven dat PHP op zich een templateparser is. Daarnaast zijn er nog meer nadelen / gevaren zoals het gebruik van eval(); toch zijn er meer pluspunten dan bezwaren in mijn ogen.
Ik zie zelf een enorm groot voordeel in het feit dat een designer in de HTML pagina's kan werken terwijl ik in de PHP code zit. In de huidige opzet kan de designer alle mogelijke waarden dumpen op het scherm en hier direct mee aan de slag gaan.
Overige pluspunten:
1. Geen overhead (bij geen templateparser gaat dit natuurlijk niet op)
2. Eigen gekozen syntax die ik fijner vind werken dan bijv. Smarty
3. Toekomstige devices die niet ondersteund worden met de huidige HTML / CSS, hoef ik alleen voor deze nieuwe devices een specifieke font-end te realiseren, zonder dat mijn server-side code daar door heen is geweven.
Ik ben inderdaad nu past bezig met een class om er voor te zorgen dat ik lijsten (lussen) vanuit de client-side aan kan sturen. Ik ben circa 10 uur bezig geweest met het scripten van de templateparser, ik weet niet of dat snel of traag is.
EDIT:
Dat ik een langere tijd bezig ben heeft er vooral mee te maken dat het elke dag een half uur is waardoor het gevoelsmatig lang duurt. Dit komt dus enigszins krom over...
Wat betreft de boomstructuren heb je wel helemaal gelijk. In de HTML kan er wel gewoon PHP code ingevoerd worden, het doel is om dit zo veel mogelijk gescheiden te houden. Zodoende kan ik server en clientside zo veel mogelijk, onafhankelijk en efficiënt updaten, bij nieuwe versies van de gebruikte technologieën.
Gewijzigd op 04/02/2018 00:21:54 door bas hooff
Dan is het voor designers prima duidelijk wat wat is, daarnaast, designers zijn ook niet van achter de maan. Ik kende een designer die een bloedhekel had aan alles wat op PHP-code leek, maar je zult dan toch een beetje moeten schipperen want de logica ("PHP") en presentatie ("HTML") zijn onlosmakelijk met elkaar verbonden in dynamische websites, hoeveel abstractielagen je hier ook tussen metselt. Dit brengt ook weer andere problemen met zich mee, het introduceert mogelijk "muren" waarbij het lastig wordt om data op de goede plaats te krijgen, je moet extra dingen doen in een bepaald formaat (pseudo taal)... ik bedoel, het is nogal omslachtig allemaal. Daarnaast introduceert het extra complexiteit, wat mogelijk debugging en ontwikkeling vertraagt. Wanneer iets complexer is gaat dit alles doorgaans langzamer, wat het dus ook duurder maakt. Plus, het kan dan een kathedraal van een ding zijn, maar een draak om mee te werken... je moet ook een beetje (veel) praktisch blijven.
Ik weet niet helemaal waar je met dat eval() voorbeeld naartoe wilt? Sja, als je compleet lijpe dingen doet in PHP op plaatsen waar PHP beschikbaar is... Dat is een kwestie van afspraken maken, vertrouwen in collega's en het nemen van eigen verantwoordelijkheid.
Imo simpeler = doorgaans beter. Als er een simpelere manier is om hetzelfde te bereiken (de pluspunten zijn min of meer doelen?) - proberen, en ook vooral kijken hoe dat dan vervolgens in de praktijk werkt.
MVC is voor mij op dit moment een stap te hoog gegrepen, het is erg abstract, maar vereist heel veel meer kennis van OOP. Daarnaast (op basis van 1 voorbeeld) zie ik dat de HTML (bijv. textfield) in een aparte class staat, dit heeft denk ik niet mijn voorkeur.
Een tussenoplossing: De meeste logica en communicatie met de database in een model (PHP) bestand zet, Vervolgens include ik een PHP bestand waar de template in staat, evenals de verwerking / laatste afhandeling van PHP code (procedureel) ? Zo voorkom ik hele lange bestanden, blijft server en client wel in elkaar verweven, maar in mindere mate.
Thomas van den Heuvel op 04/02/2018 01:57:13:
... maar je zult dan toch een beetje moeten schipperen want de logica ("PHP") en presentatie ("HTML") zijn onlosmakelijk met elkaar verbonden in dynamische websites ...
En toch wil je het af en toe 100% los trekken. Ik heb een website die we als een soort white label aanbieden, waarbij de klant zelf de volledige opmaak mag doen, en die wil ik dus echt geen PHP in handen geven.
Rob Doemaarwat op 04/02/2018 11:18:56:
En toch wil je het af en toe 100% los trekken. Ik heb een website die we als een soort white label aanbieden, waarbij de klant zelf de volledige opmaak mag doen, en die wil ik dus echt geen PHP in handen geven.
En daarvoor betaal je een prijs. Het hangt er natuurlijk ook vanaf wat je wilt doen moet je applicatie.
Ben ik toch benieuwd in hoeverre de klant de opmaak dan zelf kan verzorgen. Biedt je sjablonen aan? En een WYSIWYG-editor en dat soort zaken? Op een of andere manier moet nog steeds vastliggen hoe (en welke) data aan templates gekoppeld is. Daar kom je simpelweg niet omheen.
Ik zelf gebruik ook templates (via Smarty) en ik vind het als enkel developer ideaal als je aan een update werkt met een verbeterde gebruiksinterface in de website.
Maar als je een groot project hebt kan je op een gegeven moment niet meer zonder gescheiden lagen. Uiteindelijk is het MVC-pattern de beste opzet voor een applicatie.
Gewijzigd op 04/02/2018 17:04:58 door - Ariën -
- Ariën - op 04/02/2018 17:03:18:
Uiteindelijk is het MVC-pattern de beste opzet voor een applicatie.
Dit is zoiets als zeggen dat een hamer altijd het beste gereedschap is, ongeacht de klus. Sorry, maar dat is simpelweg niet het geval. Als dat zo zou zijn, waarom is dan niet alles al MVC?
Het maakt tot op zekere hoogte niet uit hoe je code er uitziet om iets voor elkaar te krijgen. Wat er -wat mij betreft- wel toe doet is hoe je met een bepaalde aanpak tot een bepaald resultaat komt, en ook vooral waarom juist die aanpak geoorloofd zou zijn. En met hoeveel gemak je dingen kunt maken en aanpassen, want het praktische aspect uit het oog verliezen om maar van methode X gebruik te kunnen maken... dan sla je de plank ook mis, dan doe je iets om de vorm, en niet om een specifieke reden. Iets met deegrollers en poepgaten.
Maar op voorhand zeggen dat één aanpak altijd volstaat is onzin.
Thomas van den Heuvel op 04/02/2018 16:46:57:
Ben ik toch benieuwd in hoeverre de klant de opmaak dan zelf kan verzorgen.
"De klant" is meestal "de webdesigner van de klant" die voldoende verstand van zaken heeft om een layout in HTML op te bouwen (en evt. lacunes met javascript en een paar asynchrone calls op te vullen). Wij vertellen 'm uiteraard welke waarden er beschikbaar zijn, en hoe ie die in het template kan krijgen.
Thomas van den Heuvel op 04/02/2018 16:46:57:
En daarvoor betaal je een prijs.
Ik maak meestal websites die heel erg op "data" leunen. In mijn ervaring gaat een groot deel van de render tijd van een pagina vooral in het ophalen van data zitten. Dat de template parser dan een paar ms meer nodig heeft boeit me niet zo.
Ik doelde meer op abstractie + overhead in ontwikkeling daardoor en niet zozeer op snelheid.
reinventing the wheel?
Zonde van je tijd man :=/ In minder tijd kun jij de basics van OOP leren én één van de populaire PHP frameworks voldoende onder de knie krijgen zodat je daarna op een juiste wijze van start kunt met hetgeen dat je uiteindelijk wilt bereiken. Vergelijk dat programmeren met het bouwen van een (groot) gebouw. Ga je een gebouw bouwen in je eentje en wil je alles zelf maken en niets kant en klaars kopen en gebruiken dan komt dat gebouw er in geen 80 jaar en is jouw leven voorbij. Je moet ook wel meters willen maken of er simpelweg helemaal niet aan gaan beginnen.
In iedergeval is het argument "ik beheers nog geen OOP" mijns inziens het slechtste argument om dan maar verder te blijven rommelen. Tip: Op youtube staat een heleboel dat je achterover leunend kunt bekijken met een zak chips op de bank die je vaak toch een duwtje in de goede richting kunnen geven. (let wel op of de filmpjes een beetje recent zijn en wellicht ten overvloede: chips zijn natuurlijk niet zo gezond :p)
Ik zal de huidige situatie toelichten. Ik heb 13 responsive pagina's, er komen nog een aantal bij, maar het zijn allemaal templates die slechts als doorgeefluik fungeren. Als ik naar de componenten kijk dan hebben we het over menu's, teksten, lijsten, afbeeldingen, maps en voor de rapporten en mails heb ik twee populaire classes.
Op de lijsten na kan ik tot nu toe alles afhandelen met de door mij ontwikkelde parser. Zodra ik while-loops vanuit de HTML kan aansturen kan ik alles gescheiden houden. Zodoende heb ik zo min mogelijk framework, maar net voldoende om precies te realiseren wat ik nu en hoogstwaarschijnlijk in de toekomst nodig heb. Dit heeft dan ook nog steeds mijn voorkeur.
Mocht er in de toekomst een hybride app toegevoegd worden (klein beetje native en de rest react) kan ik volgens mij gewoon een front-end koppelen aan mijn huidige structuur en de PHP code hergebruiken.
Ik weet dat ik een lid ben en geen betalende klant, maar als iemand mij in de juiste richting wil helpen, kan ik de ingeslagen koers voort blijven zetten.
Code (php)
Dit zou beter kunnen zijn:
Code (php)
Dit is namelijk ook de manier hoe het uit je database komt; altijd een rij tegelijk. Bovendien kun je de hele array in één loop doorlopen zonder super ingewikkeld te hoeven doen. Het is daarnaast ook verweg de meest gebruikte methode. Merk op dat de buitenste array numeriek is en de binnenste associatief. Een numerieke array loop je door met een foreach. Een associatieve array lijkt meer op een object waarin je de elementen van een naam voorziet.
Toevoeging op 06/02/2018 10:19:50:
Code (php)
Gewijzigd op 06/02/2018 10:10:05 door Frank Nietbelangrijk
Bedankt voor je reactie. En dan dus met een replace [loopvar:id] vervangen voor $row['\\\1'] ?
En ik zou dus moeten achterhalen welke key bij welke index hoort om deze rechtstreeks aan te kunnen spreken ? [loopvar:(.*?)\] levert uiteraard geen nummer op.
Gewijzigd op 06/02/2018 22:20:46 door bas hooff