/**
  * 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("&lt;?", "?&gt;"), $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;
 }