/** * 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); }