function parseTag(&$data, &$pos, &$parent)
{
// Find tag, determine it's type, name and attributes.
$initialPos = $pos;
if ($pos >= strlen($data)) {
return true;
}
$tagBeginPos = strpos($data, '<', $pos);
if ($this->ParseLineBreaks) {
// Regard line break as a start tag position
$lineBreakPos = strpos($data, "\n", $pos);
if ($lineBreakPos !== false) {
$tagBeginPos = $tagBeginPos === false ? $lineBreakPos : min($tagBeginPos, $lineBreakPos);
}
}
$tagName = '';
$attributes = null;
// If it doesn't begin with '<' then its a text node.
if ($tagBeginPos != $pos || $tagBeginPos === false) {
$pos = $initialPos;
$tagName = $newTagName = '#text';
$noChildren = true;
if (!$tagBeginPos) {
$tagBeginPos = strlen($data);
}
$textContent = substr($data, $pos, $tagBeginPos - $pos);
$textContent = $this->washText($textContent);
$pos = $tagBeginPos;
if ($textContent === '') {
return false;
}
} elseif ($data[$tagBeginPos] == '<' && $tagBeginPos + 1 < strlen($data) && $data[$tagBeginPos + 1] == '/') {
$tagEndPos = strpos($data, '>', $tagBeginPos + 1);
if ($tagEndPos === false) {
$pos = $tagBeginPos + 1;
$this->handleError(self::ERROR_SYNTAX, ezpI18n::tr('kernel/classes/datatypes/ezxmltext', 'Wrong closing tag'));
return false;
}
$pos = $tagEndPos + 1;
$closedTagName = strtolower(trim(substr($data, $tagBeginPos + 2, $tagEndPos - $tagBeginPos - 2)));
// Find matching tag in ParentStack array
$firstLoop = true;
for ($i = count($this->ParentStack) - 1; $i >= 0; $i--) {
$parentNames = $this->ParentStack[$i];
if ($parentNames[0] == $closedTagName) {
array_pop($this->ParentStack);
if (!$firstLoop) {
$pos = $tagBeginPos;
return true;
} elseif ($parentNames[1] !== '') {
return true;
} else {
return false;
}
}
$firstLoop = false;
}
$this->handleError(self::ERROR_SYNTAX, ezpI18n::tr('kernel/classes/datatypes/ezxmltext', 'Wrong closing tag : </%1>.', false, array($closedTagName)));
return false;
} elseif ($this->ParseLineBreaks && $data[$tagBeginPos] == "\n") {
$newTagName = 'br';
$noChildren = true;
$pos = $tagBeginPos + 1;
} else {
$tagEndPos = $this->findEndOpeningTagPosition($data, $tagBeginPos);
if ($tagEndPos === false) {
$pos = $tagBeginPos + 1;
$this->handleError(self::ERROR_SYNTAX, ezpI18n::tr('kernel/classes/datatypes/ezxmltext', 'Wrong opening tag'));
return false;
}
$pos = $tagEndPos + 1;
$tagString = substr($data, $tagBeginPos + 1, $tagEndPos - $tagBeginPos - 1);
// Check for final backslash
$noChildren = substr($tagString, -1, 1) == '/' ? true : false;
// Remove final backslash and spaces
$tagString = preg_replace("/\\s*\\/\$/", "", $tagString);
$firstSpacePos = strpos($tagString, ' ');
if ($firstSpacePos === false) {
$tagName = strtolower(trim($tagString));
$attributeString = '';
} else {
$tagName = strtolower(substr($tagString, 0, $firstSpacePos));
$attributeString = substr($tagString, $firstSpacePos + 1);
$attributeString = trim($attributeString);
// Parse attribute string
if ($attributeString) {
$attributes = $this->parseAttributes($attributeString);
}
}
// Determine tag's name
if (isset($this->InputTags[$tagName])) {
$thisInputTag = $this->InputTags[$tagName];
if (isset($thisInputTag['name'])) {
$newTagName = $thisInputTag['name'];
} else {
$newTagName = $this->callInputHandler('nameHandler', $tagName, $attributes);
}
} else {
if ($this->XMLSchema->exists($tagName)) {
$newTagName = $tagName;
} else {
$this->handleError(self::ERROR_SYNTAX, ezpI18n::tr('kernel/classes/datatypes/ezxmltext', 'Unknown tag: <%1>.', false, array($tagName)));
return false;
}
}
// Check 'noChildren' property
if (isset($thisInputTag['noChildren'])) {
$noChildren = true;
}
$thisOutputTag = isset($this->OutputTags[$newTagName]) ? $this->OutputTags[$newTagName] : null;
// Implementation of 'autoCloseOn' rule ( Handling of unclosed tags, ex.: <p>, <li> )
if (isset($thisOutputTag['autoCloseOn']) && $parent && $parent->parentNode instanceof DOMElement && in_array($parent->nodeName, $thisOutputTag['autoCloseOn'])) {
// Wrong nesting: auto-close parent and try to re-parse this tag at higher level
array_pop($this->ParentStack);
$pos = $tagBeginPos;
return true;
}
// Append to parent stack
if (!$noChildren && $newTagName !== false) {
$this->ParentStack[] = array($tagName, $newTagName, $attributeString);
}
if (!$newTagName) {
// If $newTagName is an empty string then it's not a error
if ($newTagName === false) {
$this->handleError(self::ERROR_SYNTAX, ezpI18n::tr('kernel/classes/datatypes/ezxmltext', "Can't convert tag's name: <%1>.", false, array($tagName)));
}
return false;
}
// wordmatch.ini support
if ($attributeString) {
$attributes = $this->wordMatchSupport($newTagName, $attributes, $attributeString);
}
}
// Create text or normal node.
if ($newTagName == '#text') {
$element = $this->Document->createTextNode($textContent);
} else {
$element = $this->Document->createElement($newTagName);
}
if ($attributes) {
$this->setAttributes($element, $attributes);
}
// Append element as a child or set it as root if there is no parent.
if ($parent) {
$parent->appendChild($element);
} else {
$this->Document->appendChild($element);
}
$params = array();
$params[] =& $data;
$params[] =& $pos;
$params[] =& $tagBeginPos;
$result = $this->callOutputHandler('parsingHandler', $element, $params);
if ($result === false) {
// This tag is already parsed in handler
if (!$noChildren) {
array_pop($this->ParentStack);
}
return false;
}
if ($this->QuitProcess) {
return false;
}
// Process children
if (!$noChildren) {
do {
$parseResult = $this->parseTag($data, $pos, $element);
if ($this->QuitProcess) {
return false;
}
} while ($parseResult !== true);
}
return false;
}