[PHP] Template Parser

Pagina: 1
Acties:
  • 173 views sinds 30-01-2008
  • Reageer

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Saven
  • Registratie: December 2006
  • Laatst online: 18:37

Saven

Administrator

Topicstarter
Aloa,

Ik was vandaag even bezig gegaan met een template parser (proberen althans :P).
Dit lukt volgensmij al redelijk. Alleen ben ik nu bij een stuk aangekomen waar ik echt geen idee heb hoe ik het moet aanpakken: De Blocks.

Voor zover ik weet moeten blocks meerdere keren kunnen worden weergegeven. (en ook op dezelfde plek blijven staan zegmaar in de tpl file)

Weet iemand hoe ik dit kan aanpakken?
Voor de geinteresseerde, hier wat ik al heb van mijn template parser:

Ik werk nu dus zegmaar met een var: $blocks[ template_id ] = blockname;
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
<?php

class TemplateParser
{
    /**
    * The template files dir
    */
    var $dir             = null;
    
    /**
    * All initiated templates
    */
    var $loadedTemplates = array();
    
    /**
    * Content of loaded templates
    */
    var $templateContent = array();
    
    /**
    * Global assigns
    */
    var $globalAssign    = array();
    
    /**
    * Assigns
    */
    var $assign          = array();
    
    /**
    * Blocks that need to be showed
    */
    var $blocks          = array();
    
    /*-------------------------------------------------------------------------*/
    // Main constructor
    /*-------------------------------------------------------------------------*/
    function TemplateParser()
    {
        $this->dir = root.'template/tpl/';
    }
    
    /*-------------------------------------------------------------------------*/
    // Get the content of a template file
    /*-------------------------------------------------------------------------*/
    function read($files)
    {
        $file_array = explode(',', $files);
        
        foreach( $file_array as $templateID => $templateName)
        {
            $template = $templateName.'.tpl';
            
            if( !file_exists($this->dir.$template) )
            {
                die('The template ('.$template.') does not exists');
            }
            else
            {
                if( !$content = file_get_contents($this->dir.$template) )
                {
                    die('The template ('.$template.') is unreadable');
                }
                
                $this->templateContent[] = $content;
                $this->loadedTemplates[] = $templateName;
            }
        }
    }
    
    /*-------------------------------------------------------------------------*/
    // Assign global vars
    /*-------------------------------------------------------------------------*/
    function global_assign($before, $after='')
    {
        if( is_array($before) )
        {
            foreach( $before as $in => $out )
            {
                $this->globalAssign[$in] = $out;
            }
        }
        else
        {
            $this->globalAssign[$before] = $after;
        }
    }
    
    /*-------------------------------------------------------------------------*/
    // Parse global assigns
    /*-------------------------------------------------------------------------*/
    function parse_global_assign()
    {
        foreach( $this->loadedTemplates as $templateID => $templateName )
        {
            foreach( $this->globalAssign as $before => $after )
            {
                $this->templateContent[$templateID] = str_replace('{'.$before.'}', $after, $this->templateContent[$templateID]);
            }
        }
    }
    
    /*-------------------------------------------------------------------------*/
    // Assign a var
    /*-------------------------------------------------------------------------*/
    function assign($before, $after='')
    {
        if( is_array($before) )
        {
            foreach( $before as $in => $out )
            {
                $expl   = explode('.', $in);
                $name   = $expl[0];
                $insert = end($expl);
                
                $this->prepare_assign($name, $insert, $out);
            }
        }
        else
        {
            $expl = explode('.', $before);
            
            $name = $expl[0];
            $in   = end($expl);
            
            $this->prepare_assign($name, $in, $after);
        }
    }
    
    /*-------------------------------------------------------------------------*/
    // Prepare the assign (for more templates with the same name)
    /*-------------------------------------------------------------------------*/
    function prepare_assign($name, $before, $after)
    {
        foreach( $this->loadedTemplates as $templateID => $templateName )
        {
            if( $name == $templateName )
            {
                $this->assign[$templateID][$before] = $after;
            }
        }
    }
    
    /*-------------------------------------------------------------------------*/
    // Parse assigns
    /*-------------------------------------------------------------------------*/
    function parse_assign()
    {
        foreach( $this->assign as $templateID => $arr )
        {
            foreach( $this->assign[$templateID] as $before => $after )
            {
                $this->templateContent[$templateID] = str_replace('{'.$before.'}', $after, $this->templateContent[$templateID]);
            }
        }
    }
    
    /*-------------------------------------------------------------------------*/
    // Show a block
    /*-------------------------------------------------------------------------*/
    function block($blockName)
    {
        if( is_array($blockName) )
        {
            foreach( $blockName as $blocks )
            {
                $expl = explode('.', $blocks);
                
                $template = $expl[0];
                $block    = end($expl);
                
                $this->prepare_block($template, $block);
            }
        }
        else
        {
            $expl = explode('.', $blockName);
            
            $template = $expl[0];
            $block    = end($expl);
            
            $this->prepare_block($template, $block);
        }
    }
    
    /*-------------------------------------------------------------------------*/
    // Prepare a block (get the template ID and such)
    /*-------------------------------------------------------------------------*/
    function prepare_block($name, $block)
    {
        foreach( $this->loadedTemplates as $templateID => $templateName )
        {
            if( $name == $templateName )
            {
                $this->block[$templateID] = $block;
            }
        }
    }
    
    /*-------------------------------------------------------------------------*/
    // Parse the blocks
    /*-------------------------------------------------------------------------*/
    function parse_blocks()
    {
        // tja, hoe te doen? :')
    }
    
    /*-------------------------------------------------------------------------*/
    // Parse the output
    /*-------------------------------------------------------------------------*/
    function parse()
    {
        $this->parse_global_assign();
        $this->parse_assign();
        $this->parse_blocks();
        
        $output = null;
        
        foreach( $this->templateContent as $templateID => $content )
        {
            $output .= $content."\n";
        }
        
        echo $output;
    }
}

?>

Als iemand nog tips/opmerkingen heeft over deze parser, graag :)

Acties:
  • 0 Henk 'm!

  • Spockz
  • Registratie: Augustus 2003
  • Laatst online: 10:08

Spockz

Live and Let Live

Ehm, als ik je goed begrijp wil je op een bepaalde manier een block definiëren en vervolgens datzelfde blok ergens weer neerzetten op de plek van een bepaalde identifier voor dat blok?

Stap 1:
Loop al je 'blok definities af' sla ze op.

Stap 2:
Kijk vervolgens waar je die blokken weer neer moet zetten, dus op de plek van de identifier, en haal de identifier weg.

Of mis ik je vraagstelling nu?

C'est le ton qui fait la musique. | Blog | @linkedin
R8 | 18-55 IS | 50mm 1.8 2 | 70-200 2.8 APO EX HSM | 85 1.8


Acties:
  • 0 Henk 'm!

  • Saven
  • Registratie: December 2006
  • Laatst online: 18:37

Saven

Administrator

Topicstarter
Spockz schreef op zaterdag 31 maart 2007 @ 17:32:
Ehm, als ik je goed begrijp wil je op een bepaalde manier een block definiëren en vervolgens datzelfde blok ergens weer neerzetten op de plek van een bepaalde identifier voor dat blok?

Stap 1:
Loop al je 'blok definities af' sla ze op.

Stap 2:
Kijk vervolgens waar je die blokken weer neer moet zetten, dus op de plek van de identifier, en haal de identifier weg.

Of mis ik je vraagstelling nu?
Ik wil "gewoon" en templateparser :P
Alleen heb ik geen idee hoe ik dat met blocks aan moet pakken.

Hoe je blocks wegfiltert (als je het block niet opvraagd) en hoe je blocks wel weergeeft (als je ze wél opvraagt) etc.

Acties:
  • 0 Henk 'm!

Verwijderd

Misschien is het handig als je eerst even vertelt wat jij verstaat onder een tempate parser en hoe blocks daarbij komen kijken.

Acties:
  • 0 Henk 'm!

Verwijderd

Daar kom je natuurlijk niet uit als je met replaces aan de gang gaat. Wat je dan beter kunt doen is de boel tokenizen, en met de "speciale" tokens doe je wat nodig is, en wat een gewone tekst token is stuur je direct door naar de output.

Maar is het je doel om een template parser te schrijven, want als dat niet zo is kun je beter Smarty gebruiken.

Acties:
  • 0 Henk 'm!

  • Saven
  • Registratie: December 2006
  • Laatst online: 18:37

Saven

Administrator

Topicstarter
Verwijderd schreef op zaterdag 31 maart 2007 @ 17:38:
Misschien is het handig als je eerst even vertelt wat jij verstaat onder een tempate parser en hoe blocks daarbij komen kijken.
Nou je hebt een tpl file (forum.tpl) (logisch)
En daar heb je een block in staan, zegmaar:

HTML:
1
2
3
4
5
6
De volgende berichten zijn geplaatst:<br /><br />

<!-- START BLOCK: berichten -->
{username} zegt:<br />
{bericht}
<!-- END BLOCK: berichten -->

Dat blok kun je dan als het waren "loopen" dus in een while() als je aan het fetchen bent.

bijvoorbeeld:
PHP:
1
2
3
4
5
6
7
8
9
<?php

while( $fetch = $db->fetch() )
{
  $tpl->block('forum.berichten');
  $tpl->assign( array('forum.username'=>$fetch['user'], 'forum.bericht'=>$fetch['bericht']) );
}

?>
Verwijderd schreef op zaterdag 31 maart 2007 @ 17:39:
Daar kom je natuurlijk niet uit als je met replaces aan de gang gaat. Wat je dan beter kunt doen is de boel tokenizen, en met de "speciale" tokens doe je wat nodig is, en wat een gewone tekst token is stuur je direct door naar de output.

Maar is het je doel om een template parser te schrijven, want als dat niet zo is kun je beter Smarty gebruiken.
Token? Sorry ik snap je daar even niet :P
Mijn doel is inderdaad een templateparser te maken, die ik kan gebruiken voor projecten. Is wel zo netjes als alles "uniek" is, en niet dat je een of andere templateparser van internet haalt :P

[ Voor 7% gewijzigd door Saven op 31-03-2007 17:49 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Zoals ik het vaak gezien heb wordt gewoon de hele template file in stukken gezet (met simpele codes om het begin van een nieuw stuk, en dus automatisch het einde van het vorige stuk) daarna kan je aan de template parser vragen een zo'n stuk te renderen.

Bij het renderen wil je dan natuurlijk bepaalde variabele vervangen, dit gaat denk ik het snelste met een str_replace waar je een stel arrays in gooit, maar in bepaalde omstandigheden zijn regex'en efficienter, dat moet je zelf even bekijken.

Je kunt ook aan de template parser vragen bijvoorbeeld de UBB parser of een HTML/script filter toe te passen.

Een keuze die je moet maken is of je de blokken allemaal een ID geeft (numeriek of textueel, wat je zelf wil) of dat je gewoon block 0, block 1, block 2 etc hebt (nadeel daarbij is dat je bij wijzigingen aan je template waarschijnlijk iets aan je code moet wijzigen, geen ideale scheiding van weergave en coding dus).

Acties:
  • 0 Henk 'm!

  • Saven
  • Registratie: December 2006
  • Laatst online: 18:37

Saven

Administrator

Topicstarter
Verwijderd schreef op zaterdag 31 maart 2007 @ 17:48:
Zoals ik het vaak gezien heb wordt gewoon de hele template file in stukken gezet (met simpele codes om het begin van een nieuw stuk, en dus automatisch het einde van het vorige stuk) daarna kan je aan de template parser vragen een zo'n stuk te renderen.

Bij het renderen wil je dan natuurlijk bepaalde variabele vervangen, dit gaat denk ik het snelste met een str_replace waar je een stel arrays in gooit, maar in bepaalde omstandigheden zijn regex'en efficienter, dat moet je zelf even bekijken.

Je kunt ook aan de template parser vragen bijvoorbeeld de UBB parser of een HTML/script filter toe te passen.

Een keuze die je moet maken is of je de blokken allemaal een ID geeft (numeriek of textueel, wat je zelf wil) of dat je gewoon block 0, block 1, block 2 etc hebt (nadeel daarbij is dat je bij wijzigingen aan je template waarschijnlijk iets aan je code moet wijzigen, geen ideale scheiding van weergave en coding dus).
ja :)
Maar dat is allemaal het probleem niet echt.

De template parser werkt nu zoals ik zou willen. Op de blocks na dus.

In mijn vorige bericht in mijn edit, heb ik even een voorbeeldje erbij gemaakt ;)

Acties:
  • 0 Henk 'm!

  • Justice
  • Registratie: Maart 2001
  • Laatst online: 07-08 15:02
Als je nMisschien wil je een paar template-parsers opzoeken downloaden en kijken hoe zij dat doen, en dat als inspiratie gebruiken voor jou eigen parser.

Human Bobby


Acties:
  • 0 Henk 'm!

  • Saven
  • Registratie: December 2006
  • Laatst online: 18:37

Saven

Administrator

Topicstarter
Justice schreef op zaterdag 31 maart 2007 @ 17:53:
Als je nMisschien wil je een paar template-parsers opzoeken downloaden en kijken hoe zij dat doen, en dat als inspiratie gebruiken voor jou eigen parser.
Weet je misschien een paar van die basic-parsers zo uit je hoofd?

Want ik hoef niet zo iets als smarty of templatepower :P

Acties:
  • 0 Henk 'm!

  • XKB
  • Registratie: Oktober 1999
  • Laatst online: 05-04-2021

XKB

Anonymous functional

Het klinkt alsof je een moet gaan kijken naar parsing theorie, waar hierboven ook al op gehint werd. Hierbij vorm je je input om naar een stel tokens. Een token is een verzameling characters die betekenis hebben. Bij jouw voorbeeld bv "START BLOCK" oid. Daarna kan je je template parser baseren op die tokens.

Zoek op internet eens op dit soort termen, misschien helpt dat.

Choose for Choice! Choose Linux! | src van icon


Acties:
  • 0 Henk 'm!

  • Saven
  • Registratie: December 2006
  • Laatst online: 18:37

Saven

Administrator

Topicstarter
XKB schreef op zaterdag 31 maart 2007 @ 18:33:
Het klinkt alsof je een moet gaan kijken naar parsing theorie, waar hierboven ook al op gehint werd. Hierbij vorm je je input om naar een stel tokens. Een token is een verzameling characters die betekenis hebben. Bij jouw voorbeeld bv "START BLOCK" oid. Daarna kan je je template parser baseren op die tokens.

Zoek op internet eens op dit soort termen, misschien helpt dat.
Ik zal even kijken, bedankt voor de tip :)

Acties:
  • 0 Henk 'm!

  • Spockz
  • Registratie: Augustus 2003
  • Laatst online: 10:08

Spockz

Live and Let Live

Het zou natuurlijk helemaal leuk zijn als je een parser schrijft zonder regexes te gebruiken. :)

Maar met blocks bedoel je dus eigenlijk een gedeelte in de template waarvan jij kan aangeven, dit is een een if gedeelte en dat is een for gedeelte?

C'est le ton qui fait la musique. | Blog | @linkedin
R8 | 18-55 IS | 50mm 1.8 2 | 70-200 2.8 APO EX HSM | 85 1.8


Acties:
  • 0 Henk 'm!

  • compufreak88
  • Registratie: November 2001
  • Laatst online: 02-05 17:51
Ik denk dat hij met een block bedoelt, een stukje template die herhaald kan worden. Zoals de template voor een post in een topic. Die definieer je maar 1 keer, maar herhaalt die telkens, maar dan met andere waarden...

Ik denk dat je het beste eerst alles in een array zet, en als alles geprint moet worden, dan replace je de boel.

[ Voor 21% gewijzigd door compufreak88 op 31-03-2007 21:42 ]


Acties:
  • 0 Henk 'm!

  • Spockz
  • Registratie: Augustus 2003
  • Laatst online: 10:08

Spockz

Live and Let Live

Dat kan je natuurlijk op twee niveau's regelen. Je template, en je script waarin je een paar keer een template parsed. Het laatste is het makkelijkste, het eerste is mooier. :)

C'est le ton qui fait la musique. | Blog | @linkedin
R8 | 18-55 IS | 50mm 1.8 2 | 70-200 2.8 APO EX HSM | 85 1.8


Acties:
  • 0 Henk 'm!

  • mjax
  • Registratie: September 2000
  • Laatst online: 20:52
Vergeet het geklooi met str_replace/preg_replace als je met blocken en vooral geneste blocken aan de gang gaat: daar komt alleen maar ellende van. Veel beter is om een stack based parser oplossing te implementeren met tokens.

Acties:
  • 0 Henk 'm!

  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
mjax schreef op zaterdag 31 maart 2007 @ 21:50:
Vergeet het geklooi met str_replace/preg_replace als je met blocken en vooral geneste blocken aan de gang gaat: daar komt alleen maar ellende van. Veel beter is om een stack based parser oplossing te implementeren met tokens.
Mee eens. Tenzij je echt de meest basic functionaliteit hoeft te implementeren, kom je onherroepelijk in de problemen met alleen maar string replacements. Hoe 'basic' jouw wensen zijn lijkt me alleen aan jou om te bepalen ;)

Kijk eens hier: Lexical analysis. Het omschrijft het principe van interpreteren van 'code' in een hierarchie van constructies. Het is misschien niet de meest duidelijke omschrijving, maar het geeft in ieder geval een overzicht van alle termen die in die context gebruikt worden.

Acties:
  • 0 Henk 'm!

Verwijderd

str_replace is toch nog prima te gebruiken voor het invullen van de variablen in token gebaseerde template engine? Dat is in ieder geval wel de snelste oplossing en werkt prima samen met geneste blocks en zelfs basis logica in de templates (zoals if statements die toch wel heel erg handig zijn in een template).

Acties:
  • 0 Henk 'm!

Verwijderd

Er is weinig reden om nog str_replace te gebruiken als je bijvoorbeeld een token {$melp} hebt. In plaats van die token stuur je de waarde van de template variabele melp naar de output.

[ Voor 34% gewijzigd door Verwijderd op 01-04-2007 10:53 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Voor tokens is dat handig ja, maar het is niet echt handig alle variablen als tokens erin te zetten als er verder niets hoeft te gebeuren als het erin plaatsen van de waarde.

Je kunt dan veel beter direct een str_replace van alle variablen op de output buffer doen wat een hele goede performance geeft.

Door een combinatie van tokens en variablen te gebruiken heb je het beste van beide wereld waarbij het alleen zoveel performance kost als daadwerkelijk nodig is.

Acties:
  • 0 Henk 'm!

  • Saven
  • Registratie: December 2006
  • Laatst online: 18:37

Saven

Administrator

Topicstarter
Verwijderd schreef op zondag 01 april 2007 @ 14:24:
Voor tokens is dat handig ja, maar het is niet echt handig alle variablen als tokens erin te zetten als er verder niets hoeft te gebeuren als het erin plaatsen van de waarde.

Je kunt dan veel beter direct een str_replace van alle variablen op de output buffer doen wat een hele goede performance geeft.

Door een combinatie van tokens en variablen te gebruiken heb je het beste van beide wereld waarbij het alleen zoveel performance kost als daadwerkelijk nodig is.
Dat zou inderdaad mooi zijn, alleen als ik de laatste comments zo lees begin ik er steeds minder van te snappen :P

[ Voor 14% gewijzigd door Saven op 01-04-2007 15:42 ]


Acties:
  • 0 Henk 'm!

  • chris
  • Registratie: September 2001
  • Laatst online: 11-03-2022
Saven: het is niet extreem moeilijk om een goede parser te bouwen, maar als je het nog nooit gedaan hebt is het ook niet extreem makkelijk. Ik kan je van harte aanraden om eens wat artikelen of zelfs een boek over parsers te lezen, absoluut de moeite waard.
Pagina: 1