function __construct() { parent::__construct(null, 'body', array()); }
/** * Parse the input as text until a closing tag has been found for the node. * * @param TagNode $node * @param string $name */ private function parseAsText($node, $name) { $closer = '/' . $name; if (($first = next($this->tokens)) === false) { return; } if (($second = next($this->tokens)) === false) { return; } if (($third = next($this->tokens)) === false) { return; } // Check that the inputs are [$closer]. If not then keep cycling until closed. while ($first !== '[' || $second !== $closer || $third !== ']') { $node->curText->text .= $first; $first = $second; $second = $third; if (($third = next($this->tokens)) === false) { $node->curText->text .= $first . $second; return; } } // Success! $node->close(); }
private function to_struct($message) { // Initialize a few useful things. // Such as the current position within the string. $cur_pos = 0; // The index within $struct that contains the last text index, this way // we can append anything to it if we have to (we don't want one text // node after another). $last_text_index = -1; $length = $this->strlen($message); $prev_pos = 0; $struct = array(); // We also don't want to recalculate the size of $struct over and over // again. $struct_length = 0; // The current level. $current_level = 0; $opened_tags = array(); $opened_count = 0; // Let's look for a possible tag. while (($pos = $this->strpos($message, '[', $cur_pos)) !== false && $pos + 1 < $length && $this->substr($message, $pos + 1, 1) != ' ') { // Before we handle the possible preceding text, why don't we make sure // that this tag will work? $last_pos = $pos; while ($pos < $length) { $amp_pos = $this->strpos($message, '&', $pos); $brk_pos = $this->strpos($message, ']', $pos); // Does the bracket come before the ampersand? // But the ampersand may not even be a quote as well! if ($amp_pos === false || $brk_pos === false || $brk_pos < $amp_pos || !in_array($quote_type = $this->substr($message, $amp_pos, 6), array('"', '''))) { // Sweet! $pos = $brk_pos; break; } // Now, can we find the next quote? while ($amp_pos + 6 < $length && ($amp_pos = $this->strpos($message, $quote_type, $amp_pos + 6)) !== false && $this->substr($message, $amp_pos - 1, 1) == '\\') { } // Did our search come up with nothing? if ($amp_pos + 6 >= $length || $amp_pos === false) { break; } $pos = $amp_pos + 6; } // So, did we find a valid tag? if ($this->substr($message, $pos, 1) == ']') { // Yup, we sure did! So we will want to make a node to contain the // text preceding the tag. $saved = false; if ($prev_pos != $last_pos) { $node = new TextNode($this->substr($message, $prev_pos, $last_pos - $prev_pos), $current_level); $struct[$struct_length++] = $node; $last_text_index = $struct_length - 1; $saved = true; } // Now for the tag itself. $node = new TagNode($this->substr($message, $last_pos, $pos - $last_pos + 1), $this->substr($message, $last_pos + 1, 1) == '/', 0, $this); // Woah there, horsey! Do we even have a tag by that name? if (isset($this->index['names'][$node->getTag()])) { // Yup, we do. $struct[$struct_length++] = $node; $last_text_index = -1; // Maybe this tag is being opened? if (!$node->is_closing()) { // Set the tag nodes current level, along with adding it to the // list of opened tags. $node->setLevel($current_level); // We need to set the parent node of $node, along with adding // $node to the children of the parent. // This is pretty straightforward, as it is the most recently // opened tag. $node->parentNode($opened_count > 0 ? $opened_tags[$opened_count - 1]['tag'] : null); // If there was no parent node, then it can't really be a child, // can it? if ($node->parentNode() !== null) { // This will add this node along with its other children. $node->parentNode()->childNodes($node); } // Before we add this to the list of opened tags, we will check // whether it is an empty tag, because if it is, it doesn't get // opened, so it won't need to be closed ;-) if (!$this->tag_is_empty($node->getTag())) { $opened_tags[$opened_count++] = array('tag' => $node->getTag(), 'level' => $current_level++, 'pos' => $struct_length - 1); } } else { // First we need to see if this tag was ever opened. $stop = $opened_count; for ($index = $opened_count - 1; $index >= 0; $index--) { if ($opened_tags[$index]['tag'] == $node->getTag()) { // It looks like the tag was opened, at some point. $stop = $index; break; } } // Did we find the opening tag? if ($stop < $opened_count) { // We may need to move the current node back so we can insert // any tags that need closing. $current = $opened_count - 1; // We will want to overwrite the current node (which will be // added back after the loop). $struct_length = $struct_length - 1; while ($current > $stop) { // Take off the last tag... $opened_tag = $opened_tags[$current]; unset($opened_tags[$current]); // Also subtract one from the total opened tag count. $opened_count--; // Add the closing tag to the structure. $struct[$struct_length++] = new TagNode('[/' . $opened_tag['tag'] . ']', true, $opened_tag['level']); // !!! Do closing tags need parents? // Let's tell the opening tag where the ending tag is // located, which will make some things quite a bit faster // later. $struct[$opened_tag['pos']]->setClosingAt($struct_length - 1); // Move to the next. $current--; } // Now add the current tag we were supposed to be dealing with // back to $struct. $struct[$struct_length++] = $node; // Set a couple important things. $node->setLevel($opened_tags[$opened_count - 1]['level']); $struct[$opened_tags[$opened_count - 1]['pos']]->setClosingAt($struct_length - 1); // !!! Do closing tags need parents? // Now remove it from the list of opened tags. unset($opened_tags[--$opened_count]); // Now set the proper level. $current_level = $node->level() - 1; } else { // We will just ignore this tag, then. $node->setIgnore(true); } } // Now, everything has been handled up to this point. $prev_pos = $pos + 1; } elseif (!empty($saved)) { // We want to save this with the previous text node if we can. if ($last_text_index > -1) { $struct[$last_text_index]->appendText($node->text()); } else { $struct[$struct_length++] = new TextNode($node->text(), $current_level); $last_text_index = $struct_length - 1; // Text nodes have parents, but they don't have any children. if ($opened_count > 0) { $struct[$struct_length - 1]->parentNode($opened_tags[$opened_count - 1]['tag']); } } $prev_pos = $pos + 1; } } // Alright, let's move on! $cur_pos = $pos + 1; } // Was there some text left? if ($prev_pos < $length) { // Yup, and we don't want to leave it out! $node = new TextNode($this->substr($message, $prev_pos), $current_level); $struct[$struct_length++] = $node; } // Were there any tags that weren't closed by the end of the message? // That's fine, we can fix that. if ($opened_count > 0) { while (--$opened_count >= 0) { $struct[$struct_length++] = new TagNode('[/' . $opened_tags[$opened_count]['tag'] . ']', true, $opened_tags[$opened_count]['level']); $struct[$opened_tags[$opened_count]['pos']]->setClosingAt($struct_length - 1); } } $start_time = microtime(true); for ($index = 0; $index < $struct_length; $index++) { if ($struct[$index]->type() == 'tag') { $struct[$index]->checkConstraints(); if ($struct[$index]->ignore()) { $struct[$struct[$index]->closingAt()]->setIgnore(true); } } } // And here you go. I did my job... We will send along the structure // length as well. No need to have the other method recalculate it. return array($struct, $struct_length); }
/** * Parse tag token. * * @return TagNode */ protected function parseTag() { $name = $this->lexer->getAdvancedToken()->value; $node = new TagNode($name, $this->lexer->getCurrentLine()); // Parse id, class, attributes token while (true) { switch ($this->lexer->predictToken()->type) { case 'id': case 'class': $token = $this->lexer->getAdvancedToken(); $node->setAttribute($token->type, $token->value); continue; case 'attributes': foreach ($this->lexer->getAdvancedToken()->attributes as $name => $value) { $node->setAttribute($name, $value); } continue; default: break 2; } } // Parse text/code token switch ($this->lexer->predictToken()->type) { case 'text': $node->setText($this->parseText(true)); break; case 'code': $node->setCode($this->parseCode()); break; } // Skip newlines while ('newline' === $this->lexer->predictToken()->type) { $this->lexer->getAdvancedToken(); } // Tag text on newline if ('text' === $this->lexer->predictToken()->type) { if ($text = $node->getText()) { $text->addLine(''); } else { $node->setText(new TextNode('', $this->lexer->getCurrentLine())); } } // Parse block indentation if ('indent' === $this->lexer->predictToken()->type) { $node->addChild($this->parseBlock()); } return $node; }
private function generateTagContent() { $content = $this->getSpaces() . "<?php " . $this->_code . " ?>\n"; $content .= $this->renderChildren(); $compiler = $this->getCompiler(); // checking if there is an appended tag $currLine = $this->getLineNumber() + $this->getChildrenCount() + 1; do { $line = $compiler->getLine($currLine); $currLine++; } while ($line !== null && (trim($line) == '' || $this->getIndentOfLine($line) > $this->getIndentationLevel())); $nextLineTag = null; if ($line !== null && $this->isTag($line)) { $nextLineTag = new TagNode($line); } if (!($nextLineTag !== null && strtolower($nextLineTag->getTagName()) !== 'if' && strlen($nextLineTag->getSpaces()) == strlen($this->getSpaces()) && !$nextLineTag->isLoud())) { $content .= $this->getSpaces() . "<?php " . $this->_tags[$this->_tag] . "; ?>"; } else { $content = rtrim($content); } return $content; }