%PDF- %PDF-
| Direktori : /usr/local/prospamfilter/library/Cpanel/Parser/ |
| Current File : //usr/local/prospamfilter/library/Cpanel/Parser/XML.php |
<?php
/**
* Cpanel_Parser_XML
*
* Copyright (c) 2011, cPanel, Inc.
* All rights reserved.
* http://cpanel.net
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of cPanel, Inc. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @category Cpanel
* @package Cpanel_Parser
* @author David Neimeyer <david.neimeyer@cpanel.net>
* @copyright Copyright (c) 2011, cPanel, Inc., All rights Reserved. (http://cpanel.net)
* @license http://sdk.cpanel.net/license/bsd.html BSD License
* @version 0.1.0
* @link http://sdk.cpanel.net
* @since 0.1.0
*/
/**
* XML Parser
*
* @class Cpanel_Parser_XML
* @category Cpanel
* @package Cpanel_Parser
* @author David Neimeyer <david.neimeyer@cpanel.net>
* @copyright Copyright (c) 2011, cPanel, Inc., All rights Reserved. (http://cpanel.net)
* @license http://sdk.cpanel.net/license/bsd.html BSD License
* @version 0.1.0
* @link http://sdk.cpanel.net
* @since 0.1.0
*/
class Cpanel_Parser_XML extends Cpanel_Core_Object implements Cpanel_Parser_Interface
{
/**
* Default DOMDocument XML decode mode
* NOTE: root node will not be preserved in array representation
*/
const DOM_MODE = 1;
/**
* Alternative DOMDocument XML decode mode
* NOTE: root XML node will be preserved as the first element of the array
* representation
*/
const DOM_MODE_EXTENDED = 2;
/**
* Alternative SimpleXML XML decode mode
* NOTE: root node will not be preserved in array representation
*/
const SIMPLEXML_MODE = 3;
/**
* Response format type that this parser can encode/decode
*/
const PARSER_TYPE = 'XML';
/**
* Generic prefix for parser error messaging for DOM decoder
*/
const ERROR_DOM = 'DOMDocument - ';
/**
* Generic prefix for parser error messaging for SimpleXML decoder
*/
const ERROR_SIMPLEXML = 'SimpleXML - ';
/**
* Container for recursive DOM node build
*/
private $_dom;
/**
* Observed encode/decode error
*/
private $_hasParseError;
/**
* Constructor
*
* By default, Cpanel_Parser_XML::DOM_MODE will be set. As well,
* all errors warnings generated by LibXML will be suppressed. This errors
* well be collected later, in the event of a decode error.
*
* @param arrays $optsArray Optional configuration data
*
* @return Cpanel_Parser_XML
*/
public function __construct($optsArray = array())
{
parent::__construct($optsArray);
$libErrMode = (bool)!$this->disableSuppressLibXML;
libxml_use_internal_errors($libErrMode);
$mode = $this->mode;
$this->setMode(self::DOM_MODE);
if ($mode) {
$this->setMode($mode);
}
return $this;
}
/**
* Determine if an RFT can be parsed with this parser
*
* @param string $type The name of a response format type to evaluate
*
* @see Cpanel_Parser_Interface::canParse()
*
* @return bool Whether this parser can parse a sting of $type
*/
public function canParse($type)
{
return (strtolower($type) == 'xml') ? true : false;
}
/**
* Parse a string into an array structure
*
* By default, Cpanel_Parser_XML::DOM_MODE will be used to decode
* $str. Alternative decode modes can be set prior to parsing via
* {@link setMode()}
*
* NOTE: After basic validation, if the parser cannot successfully parse
* $str, internal property "_hasParseError" will be set to true. An error
* will NOT be throw. This is to ensure a premature exit does not occur
* since the query (likely) succeeded. Problem as this level are ambiguous,
* and therefore left to the invoking script/application to manage.
*
* @param string $str String to parse
*
* @see Cpanel_Parser_XML::getParserInternalErrors
* @see Cpanel_Parser_Interface::parse()
*
* @return array|string Array representation of string on success,
* otherwise a string expressing error.
* @throws Exception If $str is not a string
*/
public function parse($str)
{
if (!is_string($str)) {
throw new Exception('Input must be a raw response string');
}
$mode = $this->mode;
if ($mode == self::SIMPLEXML_MODE) {
$rsXMLObj = $this->strToSimpleXML($str);
if ($this->_hasParseError == true) {
$prefix = self::ERROR_SIMPLEXML;
return $this->getParserInternalErrors($prefix);
}
$r = $this->simpleXMLToArray($rsXMLObj);
} else {
$tdom = $this->strToDOM($str);
if ($this->_hasParseError == true) {
$prefix = self::ERROR_DOM;
return $this->getParserInternalErrors($prefix);
}
$r = $this->DOMtoArray($tdom);
//note: empty xml root node response is empty str; coerce to array
if ($mode != self::DOM_MODE_EXTENDED && is_array($r) && !empty($r)) {
$r = array_shift($r);
}
}
return (is_array($r)) ? $r : array();
}
/**
* Encode array structure into this parser's format type
*
* Encoding is only performed with DOMDocument. Returned XML string will
* be formated with line breaking and whitespace characters.
*
* @param Cpanel_Query_Object $obj Response object containing data to
* encode
*
* @see Cpanel_Parser_Interface::encodeQueryObject()
*
* @return string XML document
* @throws Exception If $obj is an invalid response object
*/
public function encodeQueryObject($obj)
{
if (!($obj instanceof Cpanel_Query_Object)) {
throw new Exception('Parser can only encode known query object');
}
$arr = $obj->getResponse('array');
$this->_dom = new DOMDocument('1.0');
$this->_dom->preserveWhiteSpace = false;
$this->_dom->formatOutput = true;
// TODO: add error detection for empty arr
if (is_array($arr) && count($arr) > 1) {
$root = $this->_dom->createElement('result');
$this->_dom->appendChild($root);
$this->_recurse_node_build($arr, $root);
} elseif (is_array($arr) && count($arr) === 1) {
$this->_recurse_node_build($arr, $this->_dom);
}
$r = $this->_dom->saveXML();
unset($this->_dom);
return $r;
}
/**
* Recursive build DOMDocument from array
*
* NOTE: because objects are passed by reference, $obj will not be returned
*
* Credit to Matt Wiseman (trollboy at shoggoth.net) for recursion schema
*
* @param array $data Array to convert to DOMElements
* @param DOMDocument|DOMElement $obj DOMDocument|DOMElement to append to
*
* @return void
*/
private function _recurse_node_build($data, $obj)
{
// TODO: add error detection
$i = 0;
foreach ($data as $key => $value) {
if (is_array($value)) {
if (in_array(0, array_keys($value), true)) {
foreach ($value as $v) {
if (is_array($v)) {
$sub_obj[$i] = $this->_dom->createElement($key);
$obj->appendChild($sub_obj[$i]);
$this->_recurse_node_build($v, $sub_obj[$i]);
} else {
$sub_obj[$i] = $this->_dom->createElement($key, $v);
$obj->appendChild($sub_obj[$i]);
}
$i++;
}
} else {
$sub_obj[$i] = $this->_dom->createElement($key);
$obj->appendChild($sub_obj[$i]);
$this->_recurse_node_build($value, $sub_obj[$i]);
}
} else {
$sub_obj[$i] = $this->_dom->createElement($key, $value);
$obj->appendChild($sub_obj[$i]);
}
$i++;
}
}
/**
* Import XML string into new DOMDocument
*
* @param string $str XML string to import
*
* @return DOMDocument
*/
protected function strToDOM($str)
{
libxml_clear_errors();
$dom = new DOMDocument('1.0');
$r = $dom->loadXML($str);
if ($r === false) {
$this->_hasParseError = true;
return $r;
}
return $dom;
}
/**
* Convert DOMDocument into PHP array, recursing as necessary
*
* @param DOMDocument|DOMElement $node DOMDocument|DOMElement to recurse
*
* @return array PHP array representation of $node
*/
protected function DOMtoArray($node)
{
$result = array();
$children = $node->childNodes;
if ($children->length == 1) {
$child = $children->item(0);
if ($child->nodeType == XML_TEXT_NODE) {
return $child->nodeValue;
}
} elseif ($children->length < 1) {
return $node->nodeValue;
}
$group = array();
for ($i = 0; $i < $children->length; $i++) {
$child = $children->item($i);
if ($child->nodeName != '#text' && $child->nodeName != '#comment') {
if (!isset($result[$child->nodeName])) {
$result[$child->nodeName] = $this->DOMtoArray($child);
} else {
if (!isset($group[$child->nodeName])) {
$tmp = $result[$child->nodeName];
$result[$child->nodeName] = array(
$tmp
);
$group[$child->nodeName] = 1;
}
$result[$child->nodeName][] = $this->DOMtoArray($child);
}
}
}
return $result;
}
/**
* Import XML string into new SimpleXML object
*
* @param string $str XML string to import
*
* @return SimpleXMLElement
*/
protected function strToSimpleXML($str)
{
$str = trim($str);
libxml_clear_errors();
$sXMLObj = simplexml_load_string($str);
if ($sXMLObj === false) {
$this->_hasParseError = true;
return $sXMLObj;
}
return $sXMLObj;
}
/**
* Convert SimpleXMLElement into PHP array, recursing as necessary
*
* @param SimpleXMLEement $input SimpleXMLElement to recurse
* @param bool $recurse Whether this invocation is recursive
*
* @return array PHP array representation of $input
*/
protected function simpleXMLToArray($input, $recurse = false)
{
// Loading xml string with simplexml if its the top level of recursion
$data = ((!$recurse) && is_string($input)) ? simplexml_load_string($input) : $input;
// Convert SimpleXMLElements to array
if ($data instanceof SimpleXMLElement) {
$data = (array)$data;
if (empty($data)) {
$data = '';
}
}
// Recurse into arrays
if (is_array($data)) {
foreach ($data as & $item) {
$item = $this->simpleXMLToArray($item, true);
}
}
return $data;
}
/**
* Generate an error string to bubble up
*
* If LibXML errors have previously been suppress, libxml_get_errors() will
* be invoked to fetch them.
*
* @param string $prefix A string to prefix the returned error string for
* contextual reference
* @param string $default A default error message if one can not be
* determined (from the native PHP LibXML error functions)
*
* @see Cpanel_Parser_Interface::getParserInternalErrors()
*
* @return string String detailing an error has occurred
*/
public function getParserInternalErrors($prefix = '', $default = 'Could not load string.')
{
$errmsg = '';
if ($this->_hasParseError) {
$errmsg.= $prefix;
$libmsg = '';
if (!$this->disableSuppressLibXML) {
$errstrs = array();
foreach (libxml_get_errors() as $err) {
$errstrs[] = trim($err->message);
}
libxml_clear_errors();
$libmsg.= implode('. ', $errstrs);
}
if (empty($libmsg)) {
$errmsg.= $default;
} else {
$errmsg.= $libmsg;
}
}
return $errmsg;
}
/**
* Set special encode mode
*
* This parser supports use of DOM or SimpleXML. This primarily only has
* importance when iterating over interpreted response. i.e., in the legacy
* XML-API PHP client class, when a (SimpleXML) object was returned, the
* object was iterated starting at the docroot's first element. With the
* introduction of Cpanel_Parser_XML::DOM_MODE_EXTENDED it is
* possible to iterate at the document's root element itself. i.e
* ( $obj->firstchild vs. $obj->rootnode->childnode)
*
* @param int $flag Constant value to set encoding mode to
*
* @see Cpanel_Parser_XML::DOM_MODE
* @see Cpanel_Parser_XML::DOM_MODE_EXTENDED
* @see Cpanel_Parser_XML::SIMPLEXML_MODE
*
* @return Cpanel_Parser_XML
*/
public function setMode($flag)
{
if (empty($flag) || !is_int($flag)) {
$this->mode = self::DOM_MODE;
} elseif ($flag == self::DOM_MODE
|| $flag == self::DOM_MODE_EXTENDED
|| $flag == self::SIMPLEXML_MODE) {
$this->mode = $flag;
}
return $this;
}
}
?>