/** * Helper function to find the next element and return its * complete definition including opening and closing tag. * * THIS FUNCTION DOES NOT HANDLE ELEMENTS WHICH CAN BE NESTED IN THEMSELVES!!! * * @param $element On success $element carries the name of the found element * @param $xmlCode The XML code to search through * @return string Found element or NULL */ public static function getNextElement(&$element, $xmlCode, &$endPos = NULL) { if (empty($xmlCode)) { return NULL; } $pos = 0; $max = strlen($xmlCode); // Search the opening tag first. while ($pos < $max) { $start = strpos($xmlCode, '<', $pos); if ($start === false) { // Nothing found. return NULL; } if (XMLUtil::isValidXMLName($xmlCode[$start + 1])) { // Extract element name. $read = $start + 1; $found_element = ''; while (XMLUtil::isValidXMLName($xmlCode[$read])) { $found_element .= $xmlCode[$read]; $read++; if ($read >= $max) { return NULL; } } $elementLength = strlen($found_element); $next = $xmlCode[$start + $elementLength + 1]; if ($next == '/' || $next == '>' || ctype_space($next)) { // Found it. break; } $pos = $start + $elementLength; } else { // Skip this one. $pos = $start + 2; } } $pos = $start + $elementLength; // Search next '>'. $angle = strpos($xmlCode, '>', $pos); if ($angle === false) { // Opening tag is not terminated. return NULL; } $pos = $angle + 1; // Is this already the end? if ($xmlCode[$angle - 1] == '/') { // Yes. $endPos = $angle + 1; $element = $found_element; return substr($xmlCode, $start, $angle - $start + 1); } // Now, search closing tag. // (Simple solution which expects there are no child elements // with the same name. This means we assume the element can not // be nested in itself!) $end = strpos($xmlCode, '</' . $found_element . '>', $pos); if ($end === false) { return NULL; } $end += 3 + $elementLength; // Found closing tag. $endPos = $end; $element = $found_element; return substr($xmlCode, $start, $end - $start); }