You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

163 lines
4.2 KiB

<?php
/*
** Zabbix
** Copyright (C) 2001-2023 Zabbix SIA
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
class CXmlImportReader extends CImportReader {
/**
* Convert string with xml data to php array.
*
* @throws Exception
*
* @param string $string
*
* @return array
*/
public function read($string) {
if ($string === '') {
throw new Exception(_s('Cannot read XML: %1$s.', _('XML is empty')));
}
libxml_use_internal_errors(true);
if (PHP_VERSION_ID < 80000) {
// Deprecated and disabled by default since PHP 8.0.
libxml_disable_entity_loader();
}
$result = simplexml_load_string($string, null, LIBXML_IMPORT_FLAGS);
if (!$result) {
$errors = libxml_get_errors();
libxml_clear_errors();
foreach ($errors as $error) {
throw new Exception(_s('Cannot read XML: %1$s.', _s('%1$s [Line: %2$s | Column: %3$s]',
'('.$error->code.') '.trim($error->message), $error->line, $error->column
)));
}
}
$xml = new XMLReader();
$xml->xml($string);
$data = $this->xml_to_array($xml);
$xml->close();
return $data;
}
/**
* Method for recursive processing of xml dom nodes.
*
* @param XMLReader $xml
* @param string $path
*
* @return array|string
*/
protected function xml_to_array(XMLReader $xml, $path = '') {
$data = null;
while ($xml->read()) {
switch ($xml->nodeType) {
case XMLReader::ELEMENT:
if ($data === null) {
$data = [];
}
elseif (!is_array($data)) {
throw new Exception(_s('Invalid tag "%1$s": %2$s.', $path,
_s('unexpected text "%1$s"', trim($data))
));
}
$node_name = $xml->name;
$sub_path = $path.'/'.$node_name;
if (array_key_exists($node_name, $data)) {
$node_name .= count($data);
$sub_path .= '('.count($data).')';
}
/*
* A special case for 1.8 import where attributes are still used attributes must be added to the
* array as if they where child elements.
*/
if ($xml->hasAttributes) {
while ($xml->moveToNextAttribute()) {
$data[$node_name][$xml->name] = $xml->value;
}
// it makes $this->isEmptyElement valid after processing attributes
$xml->moveToElement();
/*
* We assume that an element with attributes always contains child elements, not a text node
* works for 1.8 XML.
*/
if (!$xml->isEmptyElement) {
$child_data = $this->xml_to_array($xml, $sub_path);
if (is_array($child_data)) {
foreach ($child_data as $child_node_name => $child_node_value) {
if (array_key_exists($child_node_name, $data[$node_name])) {
$child_node_name .= count($data[$node_name]);
}
$data[$node_name][$child_node_name] = $child_node_value;
}
}
elseif ($child_data !== '') {
throw new Exception(_s('Invalid tag "%1$s": %2$s.', $sub_path,
_s('unexpected text "%1$s"', trim($child_data))
));
}
}
}
else {
$data[$node_name] = $xml->isEmptyElement ? '' : $this->xml_to_array($xml, $sub_path);
}
break;
case XMLReader::CDATA:
// falls through
case XMLReader::TEXT:
if ($data === null) {
$data = $xml->value;
}
elseif (is_array($data)) {
throw new Exception(_s('Invalid tag "%1$s": %2$s.', $path,
_s('unexpected text "%1$s"', trim($xml->value))
));
}
break;
case XMLReader::END_ELEMENT:
/*
* For tags with empty value: <dns></dns>.
*/
if ($data === null) {
$data = '';
}
return $data;
}
}
return $data;
}
}