/** * Parses the conditions and returns a compiled PHP string to execute the conditions. * * @param string $conditions A string of conditions. * * @return string */ protected function parseConditions($conditions, $htpl) { // extract the strings preg_match_all('/([\'])([A-z]?[A-z\\.0-9]+)\\1/', $conditions, $matches, PREG_OFFSET_CAPTURE); $vars = []; if (count($matches[0]) > 0) { $countOffset = 0; foreach ($matches[0] as $m) { $varName = 'htpl_' . uniqid(); $conditions = substr_replace($conditions, $varName, $m[1] + $countOffset, strlen($m[0])); $countOffset += strlen($varName) - strlen($m[0]); $vars[$varName] = $m[0]; } } $testFunctions = ['isset' => 'isset', 'isnull' => 'isnull', 'empty' => 'empty']; // protected var names $protectedVarNames = ['false', 'true', 'null']; // extract the variables preg_match_all('/([A-z][\\w\\.|]+|[A-z]+)/', $conditions, $matches, PREG_OFFSET_CAPTURE); if (count($matches[0]) > 0) { $countOffset = 0; foreach ($matches[0] as $m) { if (!in_array($m[0], $testFunctions) && !in_array($m[0], $protectedVarNames)) { if (isset($vars[$m[0]])) { $var = $vars[$m[0]]; } else { $var = VarLexer::parse('{' . $m[0] . '}', $htpl, false); } $conditions = substr_replace($conditions, $var, $m[1] + $countOffset, strlen($m[0])); $countOffset += strlen($var) - strlen($m[0]); } } } return $conditions; }
/** * 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; }
/** * @param $str * @param $expectedResult * * @throws \Webiny\Htpl\HtplException * @dataProvider parseProvider */ public function testParse($str, $expectedResult) { $htpl = new Htpl(new ArrayProvider([])); $result = VarLexer::parse($str, $htpl); $this->assertSame($expectedResult, $result); }