OSDN Git Service

初回コミット(v2.6.17.1)
[magic3/magic3.git] / include / lib / patTemplate / patTemplate / Compiler.php
1 <?PHP
2 /**
3  * Compiler for patTemplate
4  *
5  * $Id: Compiler.php 2 2007-11-03 04:59:01Z fishbone $
6  *
7  * WARNING: This is still experimental!
8  *
9  * @package             patTemplate
10  * @subpackage  Compiler
11  * @author              Stephan Schmidt <schst@php.net>
12  */
13
14 /**
15  * Compiler for patTemplate
16  *
17  * $Id: Compiler.php 2 2007-11-03 04:59:01Z fishbone $
18  *
19  * WARNING: This is still experimental!
20  *
21  * @package             patTemplate
22  * @subpackage  Compiler
23  * @author              Stephan Schmidt <schst@php.net>
24  *
25  * @todo                implement all template types
26  * @todo                implement variable modifiers
27  * @todo                implement getParsedTemplate
28  * @todo                check for existing compiled template
29  */
30 class patTemplate_Compiler extends patTemplate
31 {
32    /**
33         * list of all templates that already have been compiled
34         *
35         * @access       private
36         * @var          array()
37         */
38     var $_compiledTemplates = array();
39         
40    /**
41         * file pointer to the compiled template
42         *
43         * @access       private
44         * @var          resource
45         */
46     var $_fp;
47     
48    /**
49         * constructor
50         *
51         * Creates a new patTemplate Compiler
52         *
53         * @access       public
54         * @param        string          type of the templates, either 'html' or 'tex'
55         */
56         function patTemplate_Compiler( $type = 'html' )
57         {
58                 $GLOBALS['patTemplate_Compiler']        =       &$this;
59                 patTemplate::patTemplate( $type );
60         }
61
62    /**
63         * compile the currently loaded templates
64         *
65         * @access       public
66         * @param        string  name of the input (filename, shm segment, etc.)
67         */
68         function compile( $compileName = null )
69         {
70                 $this->_varRegexp = '/'.$this->_startTag.'([^a-z:]+)'.$this->_endTag.'/U';
71                 $this->_depRegexp = '/'.$this->_startTag.'TMPL:([^a-z:]+)'.$this->_endTag.'/U';
72
73                 $compileFolder  =       $this->getOption( 'compileFolder' );
74                 $compileFile    =       sprintf( '%s/%s', $compileFolder, $compileName );
75                 
76                 $this->_fp      =       fopen( $compileFile, 'w' );
77                 $this->_addToCode( '<?PHP' );
78                 $this->_addToCode( '/**' );
79                 $this->_addToCode( ' * compiled patTemplate file' );
80                 $this->_addToCode( ' *' );
81                 $this->_addToCode( ' * compiled on '. date( 'Y-m-d H:i:s' ) );
82                 $this->_addToCode( ' */' );
83                 $this->_addToCode( 'class compiledTemplate {' );
84
85                 foreach( $this->_templates as $template => $spec )
86                 {
87                         $this->compileTemplate( $template );
88                 }
89
90                 $this->_addToCode( '}' );
91                 $this->_addToCode( '?>' );
92                 fclose( $this->_fp );
93                 
94                 include_once $compileFile;
95                 return true;
96         }
97
98    /**
99         * compile a template
100         *
101         * @access       public
102         * @param        string  name of the template
103         */
104         function compileTemplate( $template )
105         {
106                 $name   =       strtolower( $template );
107                 
108                 if( !isset( $this->_templates[$template] ) )
109                 {
110                         return  patErrorManager::raiseWarning(
111                                                                                                         PATTEMPLATE_WARNING_NO_TEMPLATE,
112                                                                                                         "Template '$name' does not exist."
113                                                                                                 );
114                 }
115                 
116
117                 /**
118                  * check, if the template has been loaded
119                  * and load it if necessary.
120                  */
121                 if( $this->_templates[$template]['loaded'] !== true )
122                 {
123                         if( $this->_templates[$template]['attributes']['parse'] == 'on' )
124                         {
125                                 $result = $this->readTemplatesFromInput( $this->_templates[$template]['attributes']['src'], $this->_templates[$template]['attributes']['reader'], null, $template );
126                         }
127                         else
128                         {
129                                 $result = $this->loadTemplateFromInput( $this->_templates[$template]['attributes']['src'], $this->_templates[$template]['attributes']['reader'], $template );
130                         }
131                         if( patErrorManager::isError( $result ) )
132                         {
133                                 return $result;
134                         }
135                 }
136
137                 $this->_addToCode( '' );
138                 $this->_addToCode( '/**' );
139                 $this->_addToCode( ' * Compiled version of '.$template );
140                 $this->_addToCode( ' *' );
141                 $this->_addToCode( ' * Template type is '.$this->_templates[$template]['attributes']['type'] );
142                 $this->_addToCode( ' */' );
143
144
145                 /**
146                  * start the output
147                  */
148                 $this->_addToCode( 'function '.$template.'()' );
149                 $this->_addToCode( '{' );
150                 $this->_addToCode( '$this->_prepareCompiledTemplate( "'.$template.'" );', 1 );
151                 $this->_addToCode( '$this->prepareTemplate( "'.$template.'" );', 1 );
152
153                 /**
154                  * attributes
155                  */
156                 $this->_addToCode( '$this->_templates["'.$template.'"]["attributes"] = unserialize( \''.serialize($this->_templates[$template]['attributes']).'\' );', 1, 'Read the attributes' );
157
158                 /**
159                  * copyVars
160                  */
161                 $this->_addToCode( '$this->_templates["'.$template.'"]["copyVars"] = unserialize( \''.serialize($this->_templates[$template]['copyVars']).'\' );', 1, 'Read the copyVars' );
162                 
163                 /**
164                  * check visibility
165                  */
166                 $this->_addToCode( 'if( $this->_templates["'.$template.'"]["attributes"]["visibility"] != "hidden" ) {', 1, 'Check, whether template is hidden' );
167
168                         /**
169                          * autoloop the template
170                          */
171                         $this->_addToCode( '$this->_templates["'.$template.'"]["iteration"] = 0;', 2, 'Reset the iteration' );
172
173                         $this->_addToCode( '$loop = count( $this->_vars["'.$template.'"]["rows"] );', 2, 'Get the amount of loops' );
174                         $this->_addToCode( '$loop = max( $loop, 1 );', 2 );
175                         $this->_addToCode( '$this->_templates["'.$template.'"]["loop"] = $loop;', 2 );
176                         
177                         $this->_addToCode( 'for( $i = 0; $i < $loop; $i++ ) {', 2, 'Traverse all variables.' );
178         
179                                 /**
180                                  * fetch the variables
181                                  */
182                                 $this->_addToCode( 'unset( $this->_templates["'.$template.'"]["vars"] );', 3 );
183                                 $this->_addToCode( '$this->_fetchVariables("'.$template.'");', 3 );
184
185                                 /**
186                                  * different templates have to be compiled differently
187                                  */
188                                 switch( $this->_templates[$template]['attributes']['type'] )
189                                 {
190                                         /**
191                                          * modulo template
192                                          */
193                                         case 'modulo':
194                                                 $this->_compileModuloTemplate( $template );
195                                                 break;
196                                                 
197                                         /**
198                                          * simple condition template
199                                          */
200                                         case 'simplecondition':
201                                                 $this->_compileSimpleConditionTemplate( $template );
202                                                 break;
203                                                 
204                                         /**
205                                          * condition template
206                                          */
207                                         case 'condition':
208                                                 $this->_compileConditionTemplate( $template );
209                                                 break;
210                                                 
211                                         /**
212                                          * standard template
213                                          */
214                                         default:
215                                                 $this->_compileStandardTemplate( $template );
216                                                 break;
217                                 }
218                         $this->_addToCode( '$this->_templates["'.$template.'"]["iteration"]++;', 3 );
219                                 
220                         $this->_addToCode( '}', 2 );
221
222                 $this->_addToCode( '}', 1 );
223                 $this->_addToCode( '}' );
224
225         /**
226          * remember this template
227          */
228         array_push( $this->_compiledTemplates, $template );
229         }
230
231    /**
232         * compile a standard template
233         *
234         * @access       private
235         * @param        string          name of the template
236         */
237         function _compileStandardTemplate( $template )
238         {
239                 $content = $this->_templateToPHP( $this->_templates[$template]['content'], $template );
240                 $this->_addToCode( $content );
241                 return true;
242         }
243
244    /**
245         * compile a modulo template
246         *
247         * A modulo template will be compiled into a switch/case
248         * statement.
249         *
250         * @access       private
251         * @param        string          name of the template
252         * @todo         check special conditions (__first, __last, __default)
253         */
254         function _compileModuloTemplate( $template )
255         {
256                 $this->_compileBuiltinConditions( $template );
257
258
259                 $this->_addToCode( 'if( !$_displayed ) {', 3, 'Builtin condition has been displayed?' );
260
261                 /**
262                  * build switch statement
263                  */
264                 $this->_addToCode( 'switch( ( $this->_templates["'.$template.'"]["iteration"] + 1 ) % '.$this->_templates[$template]['attributes']['modulo'].' ) {', 4 );
265
266                 foreach( $this->_templates[$template]['subtemplates'] as $condition => $spec )
267                 {
268                         $this->_addToCode( 'case "'.$condition.'":', 5 );
269                         $content = $this->_templateToPHP( $spec['data'], $template );
270                         $this->_addToCode( $content );
271                         $this->_addToCode( 'break;', 6 );
272                 }
273                 $this->_addToCode( '}', 4 );
274                 $this->_addToCode( '}', 3 );
275                 return true;
276         }
277
278    /**
279         * compile a simpleCondition template
280         *
281         * A simpleCondition template will be compiled into an 'if'
282         * statement.
283         *
284         * @access       private
285         * @param        string          name of the template
286         */
287         function _compileSimpleConditionTemplate( $template )
288         {
289                 $conditions     =       array();
290                 foreach( $this->_templates[$template]['attributes']['requiredvars'] as $var )
291                 {
292                         array_push( $conditions, 'isset( $this->_templates["'.$template.'"]["vars"]["'.$var.'"] )' );
293                 }
294
295                 /**
296                  * build switch statement
297                  */
298                 $this->_addToCode( 'if( '.implode( ' && ', $conditions ).' ) {', 3, 'Check for required variables' );
299
300                 $content = $this->_templateToPHP( $this->_templates[$template]['content'], $template );
301                 $this->_addToCode( $content );
302                 $this->_addToCode( '}', 3 );
303                 return true;
304         }
305
306    /**
307         * compile a condition template
308         *
309         * A condition template will be compiled into an 'switch/case'
310         * statement.
311         *
312         * @access       private
313         * @param        string          name of the template
314         */
315         function _compileConditionTemplate( $template )
316         {
317                 /**
318                  * __first, __last
319                  */
320                 $this->_compileBuiltinConditions( $template );
321
322                 $this->_addToCode( 'if( !$_displayed ) {', 3, 'Builtin condition has been displayed?' );
323
324                 /**
325                  * build switch statement
326                  */
327                 $this->_addToCode( 'switch( $this->_templates["'.$template.'"]["vars"]["'.$this->_templates[$template]["attributes"]["conditionvar"].'"] ) {', 4 );
328
329                 foreach( $this->_templates[$template]['subtemplates'] as $condition => $spec )
330                 {
331                         if( $condition == '__default' )
332                         {
333                                 $this->_addToCode( 'default:', 5 );
334                         }
335                         else
336                         {
337                                 $this->_addToCode( 'case "'.$condition.'":', 5 );
338                         }
339                         $content = $this->_templateToPHP( $spec['data'], $template );
340                         $this->_addToCode( $content );
341                         $this->_addToCode( 'break;', 6 );
342                 }
343                 $this->_addToCode( '}', 4 );
344                 $this->_addToCode( '}', 3 );
345                 return true;
346         }
347
348    /**
349         * compile built-in conditions
350         *
351         * This will create the neccessary PHP code for:
352         * - __first
353         * - __last
354         *
355         * @access       private
356         * @param        string  template name
357         */
358         function _compileBuiltinConditions( $template )
359         {
360                 $this->_addToCode( '$_displayed = false;', 3 );
361
362                 if( isset( $this->_templates[$template]['subtemplates']['__first'] ) )
363                 {
364                         $this->_addToCode( 'if( $this->_templates["'.$template.'"]["iteration"] == 0 ) {', 3, 'Check for first entry' );
365                         $content = $this->_templateToPHP( $this->_templates[$template]['subtemplates']['__first']['data'], $template );
366                         $this->_addToCode( $content );
367                         $this->_addToCode( '$_displayed = true;', 4 );
368                         $this->_addToCode( '}', 3 );
369                 }
370
371                 if( isset( $this->_templates[$template]['subtemplates']['__last'] ) )
372                 {
373                         $this->_addToCode( 'if( $this->_templates["'.$template.'"]["iteration"] == ($this->_templates["'.$template.'"]["loop"]-1) ) {', 3, 'Check for last entry' );
374                         $content = $this->_templateToPHP( $this->_templates[$template]['subtemplates']['__last']['data'], $template );
375                         $this->_addToCode( $content );
376                         $this->_addToCode( '$_displayed = true;', 4 );
377                         $this->_addToCode( '}', 3 );
378                 }
379         }
380
381    /**
382         * build PHP code from a template
383         *
384         * This will replace the variables in a template with
385         * PHP Code.
386         *
387         * @access       private
388         * @param        string          template content
389         * @param        string          name of the template
390         * @return       string          PHP code
391         */
392         function _templateToPHP( $content, $template )
393         {
394                 $content = preg_replace( $this->_varRegexp, '<?PHP echo $this->_getVar( "'.$template.'", "$1"); ?>', $content  );
395                 $content = preg_replace( $this->_depRegexp, '<?PHP compiledTemplate::$1(); ?>', $content  );
396                 $content = '?>'.$content.'<?PHP';
397                 return $content;        
398         }
399         
400
401    /**
402         * display the compiled template
403         *
404         * This is a replacement for patTemplate::displayParsedTemplate.
405         *
406         * @access       public
407         * @param        string          name of the template to display
408         */
409     function displayParsedTemplate( $name = null )
410     {
411                 if( is_null( $name ) )
412                         $name = $this->_root;
413                 
414                 $name   =       strtolower( $name );
415
416                 if( !is_callable( 'compiledTemplate', $name ) )
417                 {
418                         die( 'Unknown template' );
419                 }
420                 
421         compiledTemplate::$name();
422     }
423
424    /**
425         * add a line to the compiled code
426         *
427         * @access       public
428         * @param        string          line to add
429         * @param        integer         indentation
430         * @return       void
431         */
432         function _addToCode( $line, $indent = 0, $comment = null )
433         {
434                 if( !is_null( $comment ) )
435                 {
436                         fputs( $this->_fp, "\n" );
437                 if( $indent > 0 )
438                     fputs( $this->_fp, str_repeat( "\t", $indent ) );
439                         fputs( $this->_fp, "/* $comment */\n" );
440                 }
441         if( $indent > 0 )
442             fputs( $this->_fp, str_repeat( "\t", $indent ) );
443                 fputs( $this->_fp, $line."\n" );
444         }
445
446    /**
447         * function, used by the compiler to get a value of a variable
448         *
449         * Checks, whether the value is locally or globally set
450         *
451         * @access       private
452         * @param        string          template
453         * @param        string          variable name
454         *
455         * @todo         check for 'unusedvars' attribute
456         */
457         function _getVar( $template, $varname )
458         {
459                 if( isset( $this->_templates[$template]['vars'][$varname] ) )
460                         return $this->_templates[$template]['vars'][$varname];
461         
462                 if( isset( $this->_globals[$this->_startTag.$varname.$this->_endTag] ) )
463                         return $this->_globals[$this->_startTag.$varname.$this->_endTag];
464
465                 return '';
466         }
467
468    /**
469         * prepare a template for the compiler
470         *
471         * @access       private
472         * @param        string          template name
473         */
474         function _prepareCompiledTemplate( $template )
475         {
476                 $this->_templates[$template]    =       array(
477                                                                                                         'attributes' => array(),
478                                                                                                         'copyVars'   => array(),
479                                                                                                 );
480         }
481 }
482 ?>