in src/parser/html/PhutilHTMLParser.php [121:226]
private function newTagDOMNode(array $part) {
if (!$part['tag']) {
return null;
}
$raw_content = $part['content'];
$content = $raw_content;
$content = trim($content);
$content_len = strlen($content);
// If the tag content begins with "/", like "</td>", strip the slash
// off and mark this as a closing tag.
$is_close = false;
if ($content_len > 0 && $content[0] === '/') {
$is_close = true;
$content = substr($content, 1);
$content = trim($content);
$content_len = strlen($content);
}
// If the tag content ends with "/", like "<td />", strip the slash off
// and mark this as self-closing.
$self_close = false;
if ($content_len > 0 && $content[$content_len - 1] === '/') {
$self_close = true;
$content = substr($content, 0, $content_len - 1);
$content = trim($content);
$content_len = strlen($content);
}
// If this tag is both a closing tag and a self-closing tag, it is
// not formatted correctly. Treat it as content.
if ($self_close && $is_close) {
return null;
}
// Now, split the rest of the tag into the tag name and tag attributes.
$pieces = preg_split('/\s+/', $content, 2);
$tag_name = $pieces[0];
if (count($pieces) > 1) {
$attributes = $pieces[1];
} else {
$attributes = '';
}
// If there's no tag name, this tag is not valid. Treat it as content.
if (!strlen($tag_name)) {
return null;
}
// If this is a closing tag with attributes, it's not valid. Treat it
// as content.
if ($is_close && strlen($attributes)) {
return null;
}
$tag_name = phutil_utf8_strtolower($tag_name);
// If we find a valid closing tag, try to find a matching tag on the stack.
// If we find a matching tag, close it.
// If we do not find a matching tag, treat the closing tag as content.
if ($is_close) {
$cursor = $this->getCursor();
while ($cursor) {
if ($cursor->getTagName() === $tag_name) {
// Add this raw content to the raw content of the tag we're closing.
$cursor->setRawTail('<'.$raw_content.'>');
$parent = $cursor->getParentNode();
$this->setCursor($parent);
return true;
}
$cursor = $cursor->getParentNode();
}
return null;
}
if (strlen($attributes)) {
$attribute_map = $this->parseAttributes($attributes);
// If the attributes can't be parsed, treat the tag as content.
if ($attribute_map === null) {
return null;
}
} else {
$attribute_map = array();
}
$node = id(new PhutilDOMNode())
->setTagName($tag_name)
->setAttributes($attribute_map)
->setRawHead('<'.$raw_content.'>');
$cursor = $this->getCursor();
$cursor->appendChild($node);
if (!$self_close) {
$this->setCursor($node);
}
return $node;
}