/** * Draw the template * * @param string $filePath: name of the template file or echo the output * @param string $return_string: return the code instead of eval * */ public function draw($filePath, $return_string = FALSE) { extract($this->vars); ob_start(); // Not security wise either way #include 'data:text/plain,' . $template; # requires allow_url_fopen to be allowed $result = @eval('?>' . $this->checkTemplate($filePath) . "<?php return true;"); #echo $this->checkTemplate($filePath); if ($result == FALSE) { # not valid for PHP7 $e = new Exception("Error! Failed to eval() " . $filePath . " template"); throw $e->templateFile($filePath); } $output = ob_get_clean(); if ($return_string) { return $output; } else { echo $output; } }
/** * Compile the file and save it in the cache * * @param string $config: global config * @param string $filePath: full path to the template to be compiled * */ public function compileFile($config, $filePath) { $this->config = $config; // read the template $code = file_get_contents($filePath); // xml substitution $code = preg_replace("/<\\?xml(.*?)\\?>/s", "##XML\\1XML##", $code); // disable php tag if (!$config['php_enabled']) { $code = str_replace(array("<?", "?>"), array("<?", "?>"), $code); } // xml re-substitution $code = preg_replace_callback("/##XML(.*?)XML##/s", function ($match) { return "<?php echo '<?xml " . stripslashes($match[1]) . " ?>'; ?>"; }, $code); // set tags $tagSplit = array(); $tagMatch = array(); foreach (static::$tags as $tag => $tagArray) { $tagSplit[$tag] = $tagArray[0]; $tagMatch[$tag] = $tagArray[1]; } //Remove comments if ($this->config['remove_comments']) { $code = preg_replace('/<!--(.*)-->/Uis', '', $code); } //split the code with the tags regexp $codeSplit = preg_split("/" . implode("|", $tagSplit) . "/", $code, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); unset($code); // we don't need it any longer //variables initialization $parsedCode = $commentIsOpen = $ignoreIsOpen = $auto_escape_old = NULL; $openIf = 0; // if the template is not empty if ($codeSplit) { //read all parsed code foreach ($codeSplit as $html) { switch (true) { //close ignore tag case !$commentIsOpen && preg_match($tagMatch['ignore_close'], $html): $ignoreIsOpen = FALSE; break; //code between tag ignore id deleted //code between tag ignore id deleted case $ignoreIsOpen: break; //close no parse tag //close no parse tag case preg_match($tagMatch['noparse_close'], $html): $commentIsOpen = FALSE; break; //code between tag noparse is not compiled //code between tag noparse is not compiled case $commentIsOpen: $parsedCode .= $html; break; //ignore //ignore case preg_match($tagMatch['ignore'], $html): $ignoreIsOpen = TRUE; break; //noparse //noparse case preg_match($tagMatch['noparse'], $html): $commentIsOpen = TRUE; break; //include tag //include tag case preg_match($tagMatch['include'], $html, $matches): // reduce the path $matches[1] = preg_replace(array("#(://(*SKIP)(*FAIL))|(/{2,})#", "#(/\\./+)#", "#\\\\#"), array("/", "/", "\\\\\\"), $matches[1]); while (preg_match('#\\w+\\.\\./#', $matches[1])) { $matches[1] = preg_replace('#\\w+/\\.\\./#', '', $matches[1]); } // parse $includeTemplate = $this->varReplace($matches[1]); //dynamic include if (strpos($matches[1], '$') !== FALSE) { $parsedCode .= '<?php echo $this->checkTemplate(' . $includeTemplate . ');?>'; } else { $parsedCode .= '<?php echo $this->checkTemplate("' . $includeTemplate . '");?>'; } break; //loop //loop case preg_match($tagMatch['loop'], $html, $matches): //replace the variable in the loop $var = $this->varReplace($matches['variable'], FALSE); $this->loopLevel++; // increase the loop counter if (preg_match('#\\(#', $var)) { $newvar = "\$newvar{$this->loopLevel}"; $assignNewVar = "{$newvar}={$var};"; } else { $newvar = $var; $assignNewVar = NULL; } //loop variables $counter = "\$counter{$this->loopLevel}"; // count iteration if (isset($matches['key']) && isset($matches['value'])) { $key = $matches['key']; $value = $matches['value']; } elseif (isset($matches['key'])) { $key = "\$key{$this->loopLevel}"; // key $value = $matches['key']; } else { $key = "\$key{$this->loopLevel}"; // key $value = "\$value{$this->loopLevel}"; // value } //loop code $parsedCode .= "<?php {$counter}=-1; {$assignNewVar} if( isset({$newvar}) && ( is_array({$newvar}) || {$newvar} instanceof Traversable ) && sizeof({$newvar}) ) foreach( {$newvar} as {$key} => {$value} ){ {$counter}++; ?>"; break; //close loop tag //close loop tag case preg_match($tagMatch['loop_close'], $html): $counter = "\$counter{$this->loopLevel}"; //iterator $this->loopLevel--; //decrease the loop counter $parsedCode .= "<?php } ?>"; //close loop code break; //break loop tag //break loop tag case preg_match($tagMatch['loop_break'], $html): $parsedCode .= "<?php break; ?>"; //close loop code break; //continue loop tag //continue loop tag case preg_match($tagMatch['loop_continue'], $html): $parsedCode .= "<?php continue; ?>"; //close loop code break; //if //if case preg_match($tagMatch['if'], $html, $matches): $openIf++; //increase open if counter (for intendation) //variable substitution into condition (no delimiter into the condition) $parsedCondition = $this->varReplace($matches[1], FALSE); $parsedCode .= "<?php if( {$parsedCondition} ){ ?>"; //if code break; //elseif //elseif case preg_match($tagMatch['elseif'], $html, $matches): //variable substitution into condition (no delimiter into the condition) $parsedCondition = $this->varReplace($matches[1], FALSE); $parsedCode .= "<?php }elseif( {$parsedCondition} ){ ?>"; //elseif code break; //else //else case preg_match($tagMatch['else'], $html): $parsedCode .= '<?php }else{ ?>'; //else code break; //close if tag //close if tag case preg_match($tagMatch['if_close'], $html): $openIf--; //decrease if counter $parsedCode .= '<?php } ?>'; // close if code break; // autoescape off // autoescape off case preg_match($tagMatch['autoescape'], $html, $matches): $auto_escape_old = $this->config['auto_escape']; $this->config['auto_escape'] = in_array($matches[1], array('off', 'false', '0', NULL)) ? FALSE : TRUE; break; // autoescape on // autoescape on case preg_match($tagMatch['autoescape_close'], $html, $matches): $this->config['auto_escape'] = $auto_escape_old; break; // function // function case preg_match($tagMatch['function'], $html, $matches): $parsedFunction = $matches[1] . (isset($matches[2]) ? $this->varReplace($matches[2], FALSE) : "()"); $parsedCode .= "<?php echo {$parsedFunction}; ?>"; // function break; //ternary //ternary case preg_match($tagMatch['ternary'], $html, $matches): $parsedCode .= "<?php echo " . '(' . $this->varReplace($matches[1]) . '?' . $this->varReplace($matches[2]) . ':' . $this->varReplace($matches[3]) . ')' . "; ?>"; break; //variables //variables case preg_match($tagMatch['variable'], $html, $matches): //variables substitution (es. {$title}) $parsedCode .= "<?php " . $this->varReplace($matches[1], TRUE, TRUE) . "; ?>"; break; //constants //constants case preg_match($tagMatch['constant'], $html, $matches): //$parsedCode .= "<?php echo " . $this->conReplace($matches[1], $this->loopLevel) . "; //Issue recorded as: https://github.com/rainphp/raintpl3/issues/178 $parsedCode .= "<?php echo " . $this->modifierReplace($matches[1]) . "; ?>"; break; default: $parsedCode .= $html; } } } if ($openIf > 0) { $e = new Exception("Error! You need to close an {if} tag in " . $filePath . " template"); throw $e->templateFile($filePath); } if ($this->loopLevel > 0) { $e = new Exception("Error! You need to close the {loop} tag in " . $filePath . " template"); throw $e->templateFile($filePath); } $parsedCode = "<?php if(!class_exists('Rain\\Tpl')){exit;}?>" . $parsedCode; // fix the php-eating-newline-after-closing-tag-problem $parsedCode = str_replace("?>\n", "?>\n\n", $parsedCode); return $parsedCode; }