3 * Compiler for patTemplate
5 * $Id: Compiler.php 2 2007-11-03 04:59:01Z fishbone $
7 * WARNING: This is still experimental!
10 * @subpackage Compiler
11 * @author Stephan Schmidt <schst@php.net>
15 * Compiler for patTemplate
17 * $Id: Compiler.php 2 2007-11-03 04:59:01Z fishbone $
19 * WARNING: This is still experimental!
21 * @package patTemplate
22 * @subpackage Compiler
23 * @author Stephan Schmidt <schst@php.net>
25 * @todo implement all template types
26 * @todo implement variable modifiers
27 * @todo implement getParsedTemplate
28 * @todo check for existing compiled template
30 class patTemplate_Compiler extends patTemplate
33 * list of all templates that already have been compiled
38 var $_compiledTemplates = array();
41 * file pointer to the compiled template
51 * Creates a new patTemplate Compiler
54 * @param string type of the templates, either 'html' or 'tex'
56 function patTemplate_Compiler( $type = 'html' )
58 $GLOBALS['patTemplate_Compiler'] = &$this;
59 patTemplate::patTemplate( $type );
63 * compile the currently loaded templates
66 * @param string name of the input (filename, shm segment, etc.)
68 function compile( $compileName = null )
70 $this->_varRegexp = '/'.$this->_startTag.'([^a-z:]+)'.$this->_endTag.'/U';
71 $this->_depRegexp = '/'.$this->_startTag.'TMPL:([^a-z:]+)'.$this->_endTag.'/U';
73 $compileFolder = $this->getOption( 'compileFolder' );
74 $compileFile = sprintf( '%s/%s', $compileFolder, $compileName );
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 {' );
85 foreach( $this->_templates as $template => $spec )
87 $this->compileTemplate( $template );
90 $this->_addToCode( '}' );
91 $this->_addToCode( '?>' );
94 include_once $compileFile;
102 * @param string name of the template
104 function compileTemplate( $template )
106 $name = strtolower( $template );
108 if( !isset( $this->_templates[$template] ) )
110 return patErrorManager::raiseWarning(
111 PATTEMPLATE_WARNING_NO_TEMPLATE,
112 "Template '$name' does not exist."
118 * check, if the template has been loaded
119 * and load it if necessary.
121 if( $this->_templates[$template]['loaded'] !== true )
123 if( $this->_templates[$template]['attributes']['parse'] == 'on' )
125 $result = $this->readTemplatesFromInput( $this->_templates[$template]['attributes']['src'], $this->_templates[$template]['attributes']['reader'], null, $template );
129 $result = $this->loadTemplateFromInput( $this->_templates[$template]['attributes']['src'], $this->_templates[$template]['attributes']['reader'], $template );
131 if( patErrorManager::isError( $result ) )
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( ' */' );
148 $this->_addToCode( 'function '.$template.'()' );
149 $this->_addToCode( '{' );
150 $this->_addToCode( '$this->_prepareCompiledTemplate( "'.$template.'" );', 1 );
151 $this->_addToCode( '$this->prepareTemplate( "'.$template.'" );', 1 );
156 $this->_addToCode( '$this->_templates["'.$template.'"]["attributes"] = unserialize( \''.serialize($this->_templates[$template]['attributes']).'\' );', 1, 'Read the attributes' );
161 $this->_addToCode( '$this->_templates["'.$template.'"]["copyVars"] = unserialize( \''.serialize($this->_templates[$template]['copyVars']).'\' );', 1, 'Read the copyVars' );
166 $this->_addToCode( 'if( $this->_templates["'.$template.'"]["attributes"]["visibility"] != "hidden" ) {', 1, 'Check, whether template is hidden' );
169 * autoloop the template
171 $this->_addToCode( '$this->_templates["'.$template.'"]["iteration"] = 0;', 2, 'Reset the iteration' );
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 );
177 $this->_addToCode( 'for( $i = 0; $i < $loop; $i++ ) {', 2, 'Traverse all variables.' );
180 * fetch the variables
182 $this->_addToCode( 'unset( $this->_templates["'.$template.'"]["vars"] );', 3 );
183 $this->_addToCode( '$this->_fetchVariables("'.$template.'");', 3 );
186 * different templates have to be compiled differently
188 switch( $this->_templates[$template]['attributes']['type'] )
194 $this->_compileModuloTemplate( $template );
198 * simple condition template
200 case 'simplecondition':
201 $this->_compileSimpleConditionTemplate( $template );
208 $this->_compileConditionTemplate( $template );
215 $this->_compileStandardTemplate( $template );
218 $this->_addToCode( '$this->_templates["'.$template.'"]["iteration"]++;', 3 );
220 $this->_addToCode( '}', 2 );
222 $this->_addToCode( '}', 1 );
223 $this->_addToCode( '}' );
226 * remember this template
228 array_push( $this->_compiledTemplates, $template );
232 * compile a standard template
235 * @param string name of the template
237 function _compileStandardTemplate( $template )
239 $content = $this->_templateToPHP( $this->_templates[$template]['content'], $template );
240 $this->_addToCode( $content );
245 * compile a modulo template
247 * A modulo template will be compiled into a switch/case
251 * @param string name of the template
252 * @todo check special conditions (__first, __last, __default)
254 function _compileModuloTemplate( $template )
256 $this->_compileBuiltinConditions( $template );
259 $this->_addToCode( 'if( !$_displayed ) {', 3, 'Builtin condition has been displayed?' );
262 * build switch statement
264 $this->_addToCode( 'switch( ( $this->_templates["'.$template.'"]["iteration"] + 1 ) % '.$this->_templates[$template]['attributes']['modulo'].' ) {', 4 );
266 foreach( $this->_templates[$template]['subtemplates'] as $condition => $spec )
268 $this->_addToCode( 'case "'.$condition.'":', 5 );
269 $content = $this->_templateToPHP( $spec['data'], $template );
270 $this->_addToCode( $content );
271 $this->_addToCode( 'break;', 6 );
273 $this->_addToCode( '}', 4 );
274 $this->_addToCode( '}', 3 );
279 * compile a simpleCondition template
281 * A simpleCondition template will be compiled into an 'if'
285 * @param string name of the template
287 function _compileSimpleConditionTemplate( $template )
289 $conditions = array();
290 foreach( $this->_templates[$template]['attributes']['requiredvars'] as $var )
292 array_push( $conditions, 'isset( $this->_templates["'.$template.'"]["vars"]["'.$var.'"] )' );
296 * build switch statement
298 $this->_addToCode( 'if( '.implode( ' && ', $conditions ).' ) {', 3, 'Check for required variables' );
300 $content = $this->_templateToPHP( $this->_templates[$template]['content'], $template );
301 $this->_addToCode( $content );
302 $this->_addToCode( '}', 3 );
307 * compile a condition template
309 * A condition template will be compiled into an 'switch/case'
313 * @param string name of the template
315 function _compileConditionTemplate( $template )
320 $this->_compileBuiltinConditions( $template );
322 $this->_addToCode( 'if( !$_displayed ) {', 3, 'Builtin condition has been displayed?' );
325 * build switch statement
327 $this->_addToCode( 'switch( $this->_templates["'.$template.'"]["vars"]["'.$this->_templates[$template]["attributes"]["conditionvar"].'"] ) {', 4 );
329 foreach( $this->_templates[$template]['subtemplates'] as $condition => $spec )
331 if( $condition == '__default' )
333 $this->_addToCode( 'default:', 5 );
337 $this->_addToCode( 'case "'.$condition.'":', 5 );
339 $content = $this->_templateToPHP( $spec['data'], $template );
340 $this->_addToCode( $content );
341 $this->_addToCode( 'break;', 6 );
343 $this->_addToCode( '}', 4 );
344 $this->_addToCode( '}', 3 );
349 * compile built-in conditions
351 * This will create the neccessary PHP code for:
356 * @param string template name
358 function _compileBuiltinConditions( $template )
360 $this->_addToCode( '$_displayed = false;', 3 );
362 if( isset( $this->_templates[$template]['subtemplates']['__first'] ) )
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 );
371 if( isset( $this->_templates[$template]['subtemplates']['__last'] ) )
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 );
382 * build PHP code from a template
384 * This will replace the variables in a template with
388 * @param string template content
389 * @param string name of the template
390 * @return string PHP code
392 function _templateToPHP( $content, $template )
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';
402 * display the compiled template
404 * This is a replacement for patTemplate::displayParsedTemplate.
407 * @param string name of the template to display
409 function displayParsedTemplate( $name = null )
411 if( is_null( $name ) )
412 $name = $this->_root;
414 $name = strtolower( $name );
416 if( !is_callable( 'compiledTemplate', $name ) )
418 die( 'Unknown template' );
421 compiledTemplate::$name();
425 * add a line to the compiled code
428 * @param string line to add
429 * @param integer indentation
432 function _addToCode( $line, $indent = 0, $comment = null )
434 if( !is_null( $comment ) )
436 fputs( $this->_fp, "\n" );
438 fputs( $this->_fp, str_repeat( "\t", $indent ) );
439 fputs( $this->_fp, "/* $comment */\n" );
442 fputs( $this->_fp, str_repeat( "\t", $indent ) );
443 fputs( $this->_fp, $line."\n" );
447 * function, used by the compiler to get a value of a variable
449 * Checks, whether the value is locally or globally set
452 * @param string template
453 * @param string variable name
455 * @todo check for 'unusedvars' attribute
457 function _getVar( $template, $varname )
459 if( isset( $this->_templates[$template]['vars'][$varname] ) )
460 return $this->_templates[$template]['vars'][$varname];
462 if( isset( $this->_globals[$this->_startTag.$varname.$this->_endTag] ) )
463 return $this->_globals[$this->_startTag.$varname.$this->_endTag];
469 * prepare a template for the compiler
472 * @param string template name
474 function _prepareCompiledTemplate( $template )
476 $this->_templates[$template] = array(
477 'attributes' => array(),
478 'copyVars' => array(),