Example #1
0
 /**
  * compiles the provided string down to php code
  *
  * @param Core      $dwoo
  * @param ITemplate $template the template to compile
  *
  * @return mixed|string a compiled php string
  * @throws Exception\CompilationException
  */
 public function compile(Core $dwoo, ITemplate $template)
 {
     // init vars
     $tpl = $template->getSource();
     $ptr = 0;
     $this->dwoo = $dwoo;
     $this->template = $template;
     $this->templateSource =& $tpl;
     $this->pointer =& $ptr;
     while (true) {
         // if pointer is at the beginning, reset everything, that allows a plugin to externally reset the compiler if everything must be reparsed
         if ($ptr === 0) {
             // resets variables
             $this->usedPlugins = array();
             $this->data = array();
             $this->scope =& $this->data;
             $this->scopeTree = array();
             $this->stack = array();
             $this->line = 1;
             $this->templatePlugins = array();
             // add top level block
             $compiled = $this->addBlock('TopLevel', array(), 0);
             $this->stack[0]['buffer'] = '';
             if ($this->debug) {
                 Debug::setMessage('COMPILER INIT<br>');
             }
             if ($this->debug) {
                 Debug::setMessage('PROCESSING PREPROCESSORS (' . count($this->processors['pre']) . ')<br>');
             }
             // runs preprocessors
             foreach ($this->processors['pre'] as $preProc) {
                 if (is_array($preProc) && isset($preProc['autoload'])) {
                     $preProc = $this->loadProcessor($preProc['class'], $preProc['name']);
                 }
                 if (is_array($preProc) && $preProc[0] instanceof Processor) {
                     $tpl = call_user_func($preProc, $tpl);
                 } else {
                     $tpl = call_user_func($preProc, $this, $tpl);
                 }
             }
             unset($preProc);
             // show template source if debug
             if ($this->debug) {
                 Debug::setMessage('<pre>' . print_r(htmlentities($tpl), true) . '</pre><hr />');
             }
             // strips php tags if required by the security policy
             if ($this->securityPolicy !== null) {
                 $search = array('{<\\?php.*?\\?>}');
                 if (ini_get('short_open_tags')) {
                     $search = array('{<\\?.*?\\?>}', '{<%.*?%>}');
                 }
                 switch ($this->securityPolicy->getPhpHandling()) {
                     case Policy::PHP_ALLOW:
                         break;
                     case Policy::PHP_ENCODE:
                         $tpl = preg_replace_callback($search, array($this, 'phpTagEncodingHelper'), $tpl);
                         break;
                     case Policy::PHP_REMOVE:
                         $tpl = preg_replace($search, '', $tpl);
                 }
             }
         }
         $pos = strpos($tpl, $this->ld, $ptr);
         if ($pos === false) {
             $this->push(substr($tpl, $ptr), 0);
             break;
         } elseif (substr($tpl, $pos - 1, 1) === '\\' && substr($tpl, $pos - 2, 1) !== '\\') {
             $this->push(substr($tpl, $ptr, $pos - $ptr - 1) . $this->ld);
             $ptr = $pos + strlen($this->ld);
         } elseif (preg_match('/^' . $this->ldr . ($this->allowLooseOpenings ? '\\s*' : '') . 'literal' . ($this->allowLooseOpenings ? '\\s*' : '') . $this->rdr . '/s', substr($tpl, $pos), $litOpen)) {
             if (!preg_match('/' . $this->ldr . ($this->allowLooseOpenings ? '\\s*' : '') . '\\/literal' . ($this->allowLooseOpenings ? '\\s*' : '') . $this->rdr . '/s', $tpl, $litClose, PREG_OFFSET_CAPTURE, $pos)) {
                 throw new Exception\CompilationException($this, 'The {literal} blocks must be closed explicitly with {/literal}');
             }
             $endpos = $litClose[0][1];
             $this->push(substr($tpl, $ptr, $pos - $ptr) . substr($tpl, $pos + strlen($litOpen[0]), $endpos - $pos - strlen($litOpen[0])));
             $ptr = $endpos + strlen($litClose[0][0]);
         } else {
             if (substr($tpl, $pos - 2, 1) === '\\' && substr($tpl, $pos - 1, 1) === '\\') {
                 $this->push(substr($tpl, $ptr, $pos - $ptr - 1));
                 $ptr = $pos;
             }
             $this->push(substr($tpl, $ptr, $pos - $ptr));
             $ptr = $pos;
             $pos += strlen($this->ld);
             if ($this->allowLooseOpenings) {
                 while (substr($tpl, $pos, 1) === ' ') {
                     $pos += 1;
                 }
             } else {
                 if (substr($tpl, $pos, 1) === ' ' || substr($tpl, $pos, 1) === "\r" || substr($tpl, $pos, 1) === "\n" || substr($tpl, $pos, 1) === "\t") {
                     $ptr = $pos;
                     $this->push($this->ld);
                     continue;
                 }
             }
             // check that there is an end tag present
             if (strpos($tpl, $this->rd, $pos) === false) {
                 throw new Exception\CompilationException($this, 'A template tag was not closed, started with "' . substr($tpl, $ptr, 30) . '"');
             }
             $ptr += strlen($this->ld);
             $subptr = $ptr;
             while (true) {
                 $parsed = $this->parse($tpl, $subptr, null, false, 'root', $subptr);
                 // reload loop if the compiler was reset
                 if ($ptr === 0) {
                     continue 2;
                 }
                 $len = $subptr - $ptr;
                 $this->push($parsed, substr_count(substr($tpl, $ptr, $len), "\n"));
                 $ptr += $len;
                 if ($parsed === false) {
                     break;
                 }
             }
         }
     }
     $compiled .= $this->removeBlock('TopLevel');
     if ($this->debug) {
         Debug::setMessage('PROCESSING POSTPROCESSORS<br>');
     }
     foreach ($this->processors['post'] as $postProc) {
         if (is_array($postProc) && isset($postProc['autoload'])) {
             $postProc = $this->loadProcessor($postProc['class'], $postProc['name']);
         }
         if (is_array($postProc) && $postProc[0] instanceof Processor) {
             $compiled = call_user_func($postProc, $compiled);
         } else {
             $compiled = call_user_func($postProc, $this, $compiled);
         }
     }
     unset($postProc);
     if ($this->debug) {
         Debug::setMessage('COMPILATION COMPLETE : MEM USAGE : ' . memory_get_usage(true) . '<br>');
     }
     $output = "<?php\n/* template head */\n";
     // build plugin preloader
     foreach ($this->usedPlugins as $plugin => $type) {
         if ($type & Core::CUSTOM_PLUGIN) {
             continue;
         }
         switch ($type) {
             case Core::BLOCK_PLUGIN:
             case Core::CLASS_PLUGIN:
                 foreach (array(Core::PLUGIN_BLOCK_CLASS_PREFIX_NAME, Core::PLUGIN_FUNC_CLASS_PREFIX_NAME) as $value) {
                     try {
                         $reflectionClass = new \ReflectionClass($value . Core::underscoreToCamel($plugin));
                         $output .= "if (class_exists('\\{$reflectionClass->name}')===false)\n\t\$this->getLoader()->loadPlugin('{$plugin}');\n";
                     } catch (\ReflectionException $Exception) {
                     }
                 }
                 break;
             case Core::FUNC_PLUGIN:
                 foreach (array(Core::PLUGIN_BLOCK_FUNCTION_PREFIX_NAME, Core::PLUGIN_FUNC_FUNCTION_PREFIX_NAME) as $value) {
                     try {
                         $reflectionFunction = new \ReflectionFunction($value . Core::underscoreToCamel($plugin));
                         $output .= "if (function_exists('\\{$reflectionFunction->name}')===false)\n\t\$this->getLoader()->loadPlugin('{$plugin}');\n";
                     } catch (\ReflectionException $Exception) {
                     }
                 }
                 break;
             case Core::SMARTY_MODIFIER:
                 $output .= "if (function_exists('smarty_modifier_{$plugin}')===false)\n\t\$this->getLoader()->loadPlugin('{$plugin}');\n";
                 break;
             case Core::SMARTY_FUNCTION:
                 $output .= "if (function_exists('smarty_function_{$plugin}')===false)\n\t\$this->getLoader()->loadPlugin('{$plugin}');\n";
                 break;
             case Core::SMARTY_BLOCK:
                 $output .= "if (function_exists('smarty_block_{$plugin}')===false)\n\t\$this->getLoader()->loadPlugin('{$plugin}');\n";
                 break;
             case Core::PROXY_PLUGIN:
                 $output .= $this->getDwoo()->getPluginProxy()->getPreloader($plugin);
                 break;
             default:
                 throw new Exception\CompilationException($this, 'Type error for ' . $plugin . ' with type' . $type);
         }
     }
     foreach ($this->templatePlugins as $function => $attr) {
         if (isset($attr['called']) && $attr['called'] === true && !isset($attr['checked'])) {
             $this->resolveSubTemplateDependencies($function);
         }
     }
     foreach ($this->templatePlugins as $function) {
         if (isset($function['called']) && $function['called'] === true) {
             $output .= $function['body'] . PHP_EOL;
         }
     }
     $output .= $compiled . "\n?>";
     $output = preg_replace('/(?<!;|\\}|\\*\\/|\\n|\\{)(\\s*' . preg_quote(self::PHP_CLOSE, '/') . preg_quote(self::PHP_OPEN, '/') . ')/', ";\n", $output);
     $output = str_replace(self::PHP_CLOSE . self::PHP_OPEN, "\n", $output);
     // handle <?xml tag at the beginning
     $output = preg_replace('#(/\\* template body \\*/ \\?>\\s*)<\\?xml#is', '$1<?php echo \'<?xml\'; ?>', $output);
     // add another line break after PHP closing tags that have a line break following,
     // as we do not know whether it's intended, and PHP will strip it otherwise
     $output = preg_replace('/(?<!"|<\\?xml)\\s*\\?>\\n/', '$0' . "\n", $output);
     if ($this->debug) {
         Debug::setMessage('<hr><pre>');
         $lines = preg_split('{\\r\\n|\\n|<br />}', highlight_string($output, true));
         array_shift($lines);
         foreach ($lines as $i => $line) {
             Debug::setMessage($i + 1 . '. ' . $line . "\r\n");
         }
     }
     if ($this->debug) {
         Debug::setMessage('<hr></pre></pre>');
     }
     $this->template = $this->dwoo = null;
     $tpl = null;
     return $output;
 }
 /**
  * [util function] triggers a dwoo error
  * @param string $message the error message
  * @param int    $level   the error level, one of the PHP's E_* constants
  */
 public function triggerError($message, $level = E_USER_NOTICE)
 {
     if (!($tplIdentifier = $this->template->getResourceIdentifier())) {
         $tplIdentifier = $this->template->getResourceName();
     }
     trigger_error('Dwoo error (in ' . $tplIdentifier . ') : ' . $message, $level);
 }