Index: branches/5.2.x/core/units/helpers/xml_helper.php =================================================================== diff -u -N -r13840 -r14092 --- branches/5.2.x/core/units/helpers/xml_helper.php (.../xml_helper.php) (revision 13840) +++ branches/5.2.x/core/units/helpers/xml_helper.php (.../xml_helper.php) (revision 14092) @@ -1,6 +1,6 @@ XMLNodeClassName = 'kXMLNode5'; + k4_include_once( dirname(__FILE__) . DIRECTORY_SEPARATOR . 'xml_helper5.php' ); + } + } + /** * Parses XML data specified and returns root node * * @param string $xml + * @param int $mode + * @param bool $no_case_folding * @return kXMLNode */ - function &Parse($xml = null, $mode = XML_NO_TEXT_NODES) + function &Parse($xml = null, $mode = null, $no_case_folding = false) { - $this->Mode = $mode; + $xml = trim($xml); + $this->Mode = !isset($mode) ? XML_NO_TEXT_NODES : $mode; $this->Clear(); // in case if Parse method is called more then one time $xml_parser = xml_parser_create(); + + if ($no_case_folding) { + xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0); + } + xml_set_element_handler( $xml_parser, Array(&$this, 'startElement'), Array(&$this, 'endElement') ); xml_set_character_data_handler( $xml_parser, Array(&$this, 'characterData') ); - if (!xml_parse($xml_parser, $xml, 1)) { - $this->RootElement = new kXMLNode('ERROR', array('code'=>xml_get_error_code($xml_parser),'message'=>xml_error_string(xml_get_error_code($xml_parser)))); - trigger_error(sprintf('XML error: %s at line %d', + + if ( !xml_parse($xml_parser, $xml, 1) ) { + $class_name = $this->XMLNodeClassName; + $byte = xml_get_current_byte_index($xml_parser); + $extract = '...' . mb_substr($xml, $byte-50, 50) . ' !!![' . mb_substr($xml, $byte, 1) . ']!!! '.mb_substr($xml, $byte+1, 50) . '...'; + + $message = sprintf( + 'XML error number %s: %s at line %d col %d, byte %d, extract: %s', + xml_get_error_code($xml_parser), xml_error_string(xml_get_error_code($xml_parser)), - xml_get_current_line_number($xml_parser)), E_USER_WARNING); + xml_get_current_line_number($xml_parser), + xml_get_current_column_number($xml_parser), + xml_get_current_byte_index($xml_parser), + $extract + ); + + $this->RootElement =& new $class_name( + 'ERROR', + array( + 'code' => xml_get_error_code($xml_parser), + 'message' => $message + ) + ); + + trigger_error($message, E_USER_WARNING); } + xml_parser_free($xml_parser); $root_copy = $this->RootElement; + unset($this->RootElement); + unset($this->CurrentElement); return $root_copy; } - function ConvertHTMLEntities($s){ + function ConvertHTMLEntities($s) + { //build first an assoc. array with the entities we want to match $table1 = get_html_translation_table(HTML_ENTITIES, ENT_QUOTES); @@ -69,16 +113,20 @@ //now perform a replacement using preg_replace //each matched value in array 1 will be replaced with the corresponding value in array 2 $s = preg_replace($patterns,$replacements,$s); + return $s; } function startElement(&$Parser, &$Elem, $Attrs) { $parent =& $this->CurrentElement; // 1. $parent is now reference to $this->CurrentElement - $this->CurrentElement =& new kXMLNode($Elem, $Attrs); // 2. =& ensures, that new object won't be assigned to $parent as well (don't remove) + $class_name = $this->XMLNodeClassName; + $this->CurrentElement =& new $class_name($Elem, $Attrs); // 2. =& ensures, that new object won't be assigned to $parent as well (don't remove) + if (!isset($this->RootElement) || is_null($this->RootElement)) { $this->RootElement =& $this->CurrentElement; } + if (!is_null($parent)) { $parent->AddChild($this->CurrentElement); } @@ -87,7 +135,8 @@ function characterData($Parser, $Line) { if ($this->Mode == XML_WITH_TEXT_NODES) { - $text_node = new kXMLNode('_TEXT_'); + $class_name = $this->XMLNodeClassName; + $text_node = new $class_name('_TEXT_'); $text_node->AppendData($Line); $this->CurrentElement->AddChild( $text_node ); } @@ -103,6 +152,7 @@ $this->CurrentElement->Children = array(); }*/ } + if ($this->CurrentElement->Parent != null) { $this->CurrentElement =& $this->CurrentElement->Parent; } @@ -113,24 +163,51 @@ unset($this->RootElement); unset($this->CurrentElement); } + + function &CreateNode($name, $value=null, $attributes=array()) + { + $class_name = $this->XMLNodeClassName; + $node = new $class_name($name, $attributes); + /* @var $node kXMLNode */ + + if ($value) { + $node->SetData($value); + } + return $node; + } } class kXMLNode { + /** - * Name of this node + * Casefolded name of this node * * @var string */ var $Name = null; /** - * Attributes of this node + * Original name of this node * + * @var string + */ + var $OriginalName = null; + + /** + * Casefolded attributes of this node + * * @var Array */ var $Attributes = array(); /** + * Original attributes of this node + * + * @var Array + */ + var $OriginalAttributes = array(); + + /** * List of node childnodes * * @var Array @@ -145,7 +222,7 @@ var $Data = null; /** - * First child of this node + * Reference to first child * * @var kXMLNode */ @@ -182,12 +259,33 @@ function kXMLNode($name, $attrs = array()) { $this->Name = strtoupper($name); + $this->OriginalName = $name; + $this->OriginalAttributes = $attrs; + foreach ($attrs as $attr => $value) { - $this->Attributes[strtoupper($attr)] = $value; + $this->Attributes[ strtoupper($attr) ] = $value; } + $this->CRC = crc32($this->Name.join(array_keys($this->Attributes)).join(array_values($this->Attributes))); } + /** + * Returns attribute value, first checking it casesensitively, then caseinsensitively + * If attribute is not set returns default value (if passed), or false otherwise + * + * @param string $name + * @param mixed $default + * @return string + */ + function GetAttribute($name, $default=false) + { + if (isset($this->OriginalAttributes[$name])) { + return $this->OriginalAttributes[$name]; + } + + return isset($this->Attributes[strtoupper($name)]) ? $this->Attributes[strtoupper($name)] : $default; + } + function SetParent(&$elem) { $this->Parent =& $elem; @@ -243,6 +341,11 @@ return $this->Children[$cur]->GetChild($left); } + function &GetFirstChild() + { + return $this->firstChild; + } + /** * Returns node value by given path * @@ -294,6 +397,10 @@ return $child; } } + if (isset($child) && is_object($child)) { + $child->_destruct(); + } + unset($child); $false = false; return $false; } @@ -308,10 +415,12 @@ function FindChildValue($name, $attr=null) { $child =& $this->FindChild($name); + if ($child !== false) { if (isset($attr)) { return $child->Attributes[strtoupper($attr)]; } + return $child->Data; } } @@ -368,28 +477,84 @@ function GetXML($content_only = false) { $xml = ''; + $single = (!$this->Data && count($this->Children) == 0); + if (!$content_only) { - $xml = '<'.$this->Name; - if (count($this->Attributes)) { + $xml = '<'.$this->OriginalName; + + if (count($this->OriginalAttributes)) { $xml .= ' '; $att_contents = array(); - foreach ($this->Attributes as $name => $value) { - $att_contents[] = $name.'="'.$value.'"'; + foreach ($this->OriginalAttributes as $name => $value) { + $att_contents[] = $name.'="'.htmlspecialchars($value).'"'; } $xml .= implode(' ', $att_contents); } - $xml .= '>'; - } - $xml .= $this->Data; - foreach ($this->Children as $node) { - /* @var $node kXMLNode */ - $xml .= $node->GetXML($node->Name == '_TEXT_' ? true : false); + $xml .= $single ? '/>' : '>'; } - if (!$content_only) { - $xml .= 'Name.'>'; + if (!$single) { + if ($content_only) { + $xml .= $this->Data; + } + else { + $xml .= preg_match('/&|Data) ? 'Data.']]>' : $this->Data; + } + + foreach ($this->Children as $node) { + /* @var $node kXMLNode */ + + $xml .= $node->GetXML($node->Name == '_TEXT_' ? true : false); + } + + if (!$content_only) { + $xml .= 'OriginalName.'>'; + } } + return $xml; } + + function RemoveChild($name) + { + $child =& $this->FindChild($name); + $parent =& $child->Parent; + $pos = $child->Position; + array_splice($parent->Children, $pos, 1); + for ($i=$pos; $i < count($parent->Children); $i++) { + $parent->Children[$i]->Position = $i; + } + $parent->firstChild =& $parent->Children[0]; + $parent->lastChild =& $parent->Children[count($parent->Children)-1]; + } + + function ReplaceChild($name, &$replacement) + { + $child =& $this->FindChild($name); + $parent =& $child->Parent; + $pos = $child->Position; + array_splice($parent->Children, $pos, 1, array($replacement)); + $replacement->Parent =& $parent; + $replacement->Position = $pos; + $parent->firstChild =& $parent->Children[0]; + $parent->lastChild =& $parent->Children[count($parent->Children)-1]; + } + + function SetName($name) + { + $this->Name = strtoupper($name); + $this->OriginalName = $name; + } + + function SetData($data) + { + $this->Data = $data; + } + + function SetAttribute($name, $value) + { + $this->Attributes[strtoupper($name)] = $value; + $this->OriginalAttributes[$name] = $value; + } } \ No newline at end of file