/** * Method that does the actual compiling. * * @param string $templateName Template name that should be compiled. * * @return Layout Compiled template in form of a string. * @throws HtplException */ private function compileLayout($templateName) { // before we can do the template compile, we need to solve the template inheritance $layout = LayoutTree::getLayout($this->htpl->getTemplateProvider(), $templateName); $template = $layout->getSource(); // validate that the raw template doesn't contain any PHP code if (strpos($template, '<?') !== false) { throw new HtplException(sprintf('Template "%s" contains PHP tags which are not allowed.', $templateName)); } // strip out w-literal content before parsing any tags or variables $literals = TagLexer::parse($template)->select('w-literal'); $literalReplacements = []; foreach ($literals as $l) { $id = '<!-- htpl: w-literal => ' . uniqid() . ' -->'; $literalReplacements[$id] = $l['content']; $template = str_replace($l['outerHtml'], $id, $template); } // parse the variables $template = VarLexer::parse($template, $this->htpl); // get a list of possible functions (tags) that we support $functions = $this->htpl->getFunctions(); $lexedTemplate = TagLexer::parse($template); foreach ($functions as $tag => $callback) { $tags = $lexedTemplate->select($tag); foreach ($tags as $t) { $lexedTemplate = TagLexer::parse($template); $currentMatch = $lexedTemplate->select($tag); if (count($currentMatch) < 1) { continue; } else { $currentMatch = $currentMatch[0]; } $content = $currentMatch['content']; $attributes = isset($currentMatch['attributes']) ? $currentMatch['attributes'] : []; try { // extract the opening and closing tag $outerContent = str_replace($currentMatch['content'], '', $currentMatch['outerHtml']); $closingTag = '</' . $tag . '>'; $openingTag = str_replace($closingTag, '', $outerContent); $instance = new $callback(); $result = $instance->parseTag($content, $attributes, $this->htpl); if (!$result) { continue; } // check if we have context defined $contextStart = ''; $contextEnd = ''; if (isset($result['contexts'])) { foreach ($result['contexts'] as $c) { $contextStart .= '<!-- htpl-context-start:' . $c . ' -->' . "\n"; $contextEnd = '<!-- htpl-context-end:' . $c . ' -->' . "\n" . $contextEnd; } } // do the replacement if (isset($result['content'])) { $replacement = $contextStart . $result['openingTag'] . $result['content'] . $result['closingTag'] . $contextEnd; // we replace with offset 1 cause, we always do the replacement on the current template instance //$template = str_replace($currentMatch['outerHtml'], $replacement, $template); $template = preg_replace('/(\\s+|)' . preg_quote($currentMatch['outerHtml'], '/') . '(\\s+|)/', $replacement, $template); } else { $replacement = $contextStart . $result['openingTag'] . $currentMatch['content'] . $result['closingTag'] . $contextEnd; $template = preg_replace('/(\\s+|)' . preg_quote($currentMatch['outerHtml'], '/') . '(\\s+|)/', $replacement, $template); /* //$template = str_replace($openingTag, $result['openingTag'], $template); $template = preg_replace('/(\s+|)' . preg_quote($openingTag, '/') . '(\s+|)/', $result['openingTag'], $template); if (isset($result['closingTag'])) { //$template = str_replace($closingTag, $result['closingTag'], $template); $template = preg_replace('/(\s+|)' . preg_quote($closingTag, '/') . '(\s+|)/', $result['closingTag'], $template); } */ } } catch (HtplException $e) { throw new HtplException('Htpl in unable to parse your template near: ' . $openingTag . "\n\n " . $e->getMessage()); } } } // adjust contexts $template = $this->adjustContexts($template); // optimize template execution /*$template = preg_replace('/\?>(\s+|)\<\?php/', "\n", $template);*/ // put back the literals foreach ($literalReplacements as $lrId => $lrVal) { $template = str_replace($lrId, $lrVal, $template); } // save the new source $layout->setSource($template); return $layout; }
public function testRegisterFunction() { $provider = new ArrayProvider(['test.htpl' => '{var}']); $htpl = new Htpl($provider); $functions = $htpl->getFunctions(); $this->assertArrayHasKey('w-if', $functions); $this->assertArrayHasKey('w-else', $functions); $this->assertArrayHasKey('w-elseif', $functions); $this->assertArrayHasKey('w-include', $functions); $this->assertArrayHasKey('w-loop', $functions); $this->assertArrayHasKey('w-minify', $functions); require_once __DIR__ . '/Mocks/WMockFunction.php'; $mockFunction = new WMockFunction(); $htpl->registerFunction($mockFunction); $functions = $htpl->getFunctions(); $this->assertArrayHasKey('w-mock', $functions); $this->assertArrayHasKey('w-if', $functions); $this->assertArrayHasKey('w-else', $functions); $this->assertArrayHasKey('w-elseif', $functions); $this->assertArrayHasKey('w-include', $functions); $this->assertArrayHasKey('w-loop', $functions); $this->assertArrayHasKey('w-minify', $functions); $this->assertInstanceOf('Webiny\\Htpl\\UnitTests\\Mocks\\WMockFunction', $functions['w-mock']); }