setFeedUrl($feed);
}
function getRemoteXmlFile($address, $timeout = 10)
{
return $this->getRemoteFile($address, $timeout);
}
}
/**
* Simple XML Parser
*
* @package e107
* @category e107_handlers
* @version 1.1
* @author McFly
* @copyright Copyright (c) 2009, e107 Inc.
*/
class xmlClass
{
/**
* Loaded XML string
*
* @var string
*/
public $xmlFileContents = '';
/**
* Set to FALSE if not enabled (default on initialisation)
* Otherwise mirrors the required subset of the loaded XML - set a field FALSE to accept all
* ...elements lower down the tree. e.g.:
*
* $filter = array(
* 'name' => FALSE,
* 'administration' => FALSE,
* 'management' => array('install' => FALSE)
* );
*
*
* @see setOptFilter()
* @see parseXml()
* @see xml2array()
* @var mixed
*/
public $filter = false; // Optional filter for loaded XML
/**
* Set true to strip all mention of comments from the returned array (default);
* FALSE to return comment markers (object SimpleXMLElement)
*
* @see setOptStripComments()
* @see parseXml()
* @see xml2array()
* @var boolean
*/
public $stripComments = true;
/**
* Log of all paths replaced.
*
* @var array
*/
public $fileConvertLog = array();
public $convertFilePaths = FALSE;
public $filePathDestination = FALSE;
public $convertFileTypes = array("jpg", "gif", "png", "jpeg");
public $filePathPrepend = array();
public $filePathConvKeys = array();
public $errors;
private $arrayTags = false;
private $stringTags = false;
/**
* Add root element to the result array
* Exmple:
*
*
*
* if
* $_optAddRoot = true;
* xml2array() result is array('root' => array('tag' => 'some value'));
*
* if
* $_optAddRoot = false;
* xml2array() result is array('tag' => 'some value');
*
* @see xml2array()
* @see setOptAddRoot()
* @var boolean
*/
protected $_optAddRoot = false;
/**
* Always return array, even for single first level tag => value pair
* Exmple:
*
*
*
* if
* $_optForceArray = true;
* xml2array() result is array('tag' => array('value' => 'some value'));
* where 'value' is the value of $_optValueKey
*
* If
* $_optForceArray = false;
* xml2array() result is array('tag' => 'some value');
*
* @see xml2array()
* @see setOptForceArray()
* @var boolean
*/
protected $_optForceArray = false;
/**
* Key name for simple tag => value pairs
*
* @see xml2array()
* @see setOptValueKey()
* @var string
*/
protected $_optValueKey = '@value';
protected $_feedUrl = FALSE;
/**
* Constructor - set defaults
*
*/
function __constructor()
{
$this->reset();
if(count($this->filePathConversions))
{
$this->filePathConvKeys = array_keys($this->filePathConversions);
}
}
/**
* Reset object
*
* @param boolean $xml_contents [optional]
* @return xmlClass
*/
function reset($xml_contents = true)
{
if($xml_contents)
{
$this->xmlFileContents = '';
}
$this->filter = false;
$this->stripComments = true;
$this->_optAddRoot = false;
$this->_optValueKey = '@value';
$this->_optForceArray = false;
return $this;
}
/**
* Set addRoot option
*
* @param boolean $flag
* @return xmlClass
*/
public function setOptAddRoot($flag)
{
$this->_optAddRoot = (boolean) $flag;
return $this;
}
/**
* Set Xml tags that should always return arrays.
*
* @param object $string (comma separated)
* @return xmlClass
*/
public function setOptArrayTags($string)
{
$this->arrayTags = (array) explode(",", $string);
return $this;
}
public function setOptStringTags($string)
{
$this->stringTags = (array) explode(",", $string);
return $this;
}
/**
* Set forceArray option
*
* @param boolean $flag
* @return xmlClass
*/
public function setOptForceArray($flag)
{
$this->_optForceArray = (boolean) $flag;
return $this;
}
/**
* Set valueKey option
*
* @param string $str
* @return xmlClass
*/
public function setOptValueKey($str)
{
$this->_optValueKey = trim((string) $str);
return $this;
}
/**
* Set strpComments option
*
* @param boolean $flag
* @return xmlClass
*/
public function setOptStripComments($flag)
{
$this->stripComments = (boolean) $flag;
return $this;
}
/**
* Set strpComments option
*
* @param array $filter
* @return xmlClass
*/
public function setOptFilter($filter)
{
$this->filter = (array) $filter;
return $this;
}
public function setFeedUrl($feed)
{
if($feed)
{
$this->_feedUrl = $feed;
}
}
/**
* Get Remote file contents
* use setOptArrayTags above if you require a consistent array result by in 1 item or many.
* @param string $address
* @param integer $timeout [optional] seconds
* @return string
*/
function getRemoteFile($address, $timeout = 10)
{
// Could do something like: if ($timeout <= 0) $timeout = $pref['get_remote_timeout']; here
$timeout = min($timeout, 120);
$timeout = max($timeout, 3);
$mes = e107::getMessage();
if($this->_feedUrl) // override option for use when part of the address needs to be encoded.
{
$mes->addDebug("getting Remote File: ".$this->_feedUrl);
}
else
{
$address = str_replace(array("\r", "\n", "\t"), '', $address); // May be paranoia, but streaky thought it might be a good idea
// ... and there shouldn't be unprintable characters in the URL anyway
}
// ... and there shouldn't be unprintable characters in the URL anyway
if (function_exists('file_get_contents') && ini_get('allow_url_fopen'))
{
$old_timeout = e107_ini_set('default_socket_timeout', $timeout);
$address = ($this->_feedUrl) ? $this->_feedUrl : urldecode($address);
$data = file_get_contents($address);
// $data = file_get_contents(htmlspecialchars($address)); // buggy - sometimes fails.
if ($old_timeout !== FALSE)
{
e107_ini_set('default_socket_timeout', $old_timeout);
}
if ($data !== FALSE)
{
$this->xmlFileContents = $data;
return $data;
}
$this->error = "File_get_contents(XML) error"; // Fill in more info later
return FALSE;
}
if (function_exists("curl_init"))
{
$cu = curl_init();
curl_setopt($cu, CURLOPT_URL, $address);
curl_setopt($cu, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($cu, CURLOPT_HEADER, 0);
curl_setopt($cu, CURLOPT_TIMEOUT, $timeout);
$this->xmlFileContents = curl_exec($cu);
if (curl_error($cu))
{
$this->error = "Curl error: ".curl_errno($cu).", ".curl_error($cu);
return FALSE;
}
curl_close($cu);
return $this->xmlFileContents;
}
if (ini_get("allow_url_fopen"))
{
$old_timeout = e107_ini_set('default_socket_timeout', $timeout);
$remote = @fopen($address, "r");
if (!$remote)
{
$this->error = "fopen: Unable to open remote XML file: ".$address;
return FALSE;
}
}
else
{
$old_timeout = $timeout;
$tmp = parse_url($address);
if (!$remote = fsockopen($tmp['host'], 80, $errno, $errstr, $timeout))
{
$this->error = "Sockets: Unable to open remote XML file: ".$address;
return FALSE;
}
else
{
socket_set_timeout($remote, $timeout);
fputs($remote, "GET ".urlencode($address)." HTTP/1.0\r\n\r\n");
}
}
$this->xmlFileContents = "";
while (!feof($remote))
{
$this->xmlFileContents .= fgets($remote, 4096);
}
fclose($remote);
if ($old_timeout != $timeout)
{
if ($old_timeout !== FALSE)
{
e107_ini_set('default_socket_timeout', $old_timeout);
}
}
return $this->xmlFileContents;
}
/**
* Parse $xmlFileContents XML string to array
*
* @param string $xml [optional]
* @param boolean $simple [optional] false - use xml2array(), true - use xml_convert_to_array()
* @return string
*/
function parseXml($xmlData = '', $simple = true)
{
if ($xmlData)
{
$this->xmlFileContents = $xmlData;
}
elseif ($this->xmlFileContents)
{
$xmlData = $this->xmlFileContents;
}
if (!$xmlData)
{
return FALSE;
}
$xmlData = str_replace('content:encoded', 'content_encoded', $xmlData);
if(!$xml = simplexml_load_string($xmlData))
{
$this->errors = $this->getErrors($xmlData);
return FALSE;
};
$xml = $simple ? $this->xml_convert_to_array($xml, $this->filter, $this->stripComments) : $this->xml2array($xml);
return $xml;
}
/**
* Advanced XML parser - handles tags with attributes and values
* properly.
* TODO - filter (see xml_convert_to_array)
*
* @param SimpleXMLElement $xml
* @param string $rec_parent used for recursive calls only
* @return array
*/
function xml2array($xml, $rec_parent = '')
{
$ret = array();
$tags = get_object_vars($xml);
//remove comments
if($this->stripComments && isset($tags['comment']))
{
unset($tags['comment']);
}
//first call
if(!$rec_parent)
{
//$ret = $this->xml2array($xml, true);
//repeating code because of the _optForceArray functionality
if(!is_object($xml))
{
return array();
}
$tags = array_keys($tags);
foreach ($tags as $tag)
{
if($tag == '@attributes')
{
$tmp = (array) $xml->attributes();
$ret['@attributes'] = $tmp['@attributes'];
continue;
}
$count = count($xml->{$tag});
if($count > 1)
{
for ($i = 0; $i < $count; $i++)
{
$ret[$tag][$i] = $this->xml2array($xml->{$tag}[$i], $tag);
}
continue;
}
$ret[$tag] = $this->xml2array($xml->{$tag}, $tag);
}
$ret = $this->parseArrayTags($ret);
$ret = $this->parseStringTags($ret);
return ($this->_optAddRoot ? array($xml->getName() => $ret) : $ret);
}
//Recursive calls start here
if($tags)
{
$tags = array_keys($tags);
$count_tags = count($tags);
//loop through tags
foreach ($tags as $tag)
{
switch($tag)
{
case '@attributes':
$tmp = (array) $xml->attributes();
$ret['@attributes'] = $tmp['@attributes'];
if($count_tags == 1) //only attributes & possible value
{
$ret[$this->_optValueKey] = trim((string) $xml);
return $ret;
}
break;
case 'comment':
$ret[$this->_optValueKey] = trim((string) $xml);
$ret['comment'] = $xml->comment;
break;
//more cases?
default:
$count = count($xml->{$tag});
if($count >= 1) //array of elements - loop
{
for ($i = 0; $i < $count; $i++)
{
$ret[$tag][$i] = $this->xml2array($xml->{$tag}[$i], $tag);
$ret[$tag][$i] = $this->parseStringTags($ret[$tag][$i]);
}
}
else //single element
{
$ret[$tag] = $this->xml2array($xml->{$tag}, $tag);
$ret[$tag] = $this->parseStringTags($ret[$tag]);
}
break;
}
}
$ret = $this->parseStringTags($ret);
return $ret;
}
//parse value only
$ret = trim((string) $xml);
return ($this->_optForceArray ? array($this->_optValueKey => $ret) : $ret);
}
// OLD
function xml_convert_to_array($xml, $localFilter = FALSE, $stripComments = TRUE)
{
if (is_object($xml))
{
$xml = (array) $xml;
}
if (is_array($xml))
{
foreach ($xml as $k=>$v)
{
if ($stripComments && ($k === 'comment'))
{
unset($xml[$k]);
continue;
}
$enabled = FALSE;
if ($localFilter === FALSE)
{
$enabled = TRUE;
$onFilter = FALSE;
}
elseif (isset($localFilter[$k]))
{
$enabled = TRUE;
$onFilter = $localFilter[$k];
}
if ($enabled)
{
if (is_object($v))
{
$v = (array) $v;
}
$xml[$k] = $this->xml_convert_to_array($v, $onFilter, $stripComments);
}
else
{
unset($xml[$k]);
}
}
if (count($xml) == 1 && isset($xml[0]))
{
$xml = $xml[0];
}
}
$xml = $this->parseArrayTags($xml);
// $xml = $this->parseStringTags($xml);
return $xml;
}
/**
* Convert Array(0) to String based on specified Tags.
*
* @param array|string $vars
* @return string
*/
function parseStringTags($vars)
{
if(!$this->stringTags || !is_array($vars))
{
return $vars;
}
foreach($this->stringTags as $vl)
{
if(isset($vars[$vl]) && isset($vars[$vl][0]))
{
$vars[$vl] = $vars[$vl][0];
}
}
return $vars;
}
/**
* Return as an array, even when a single xml tag value is found
* Use setArrayTags() to set which tags are affected.
*
* @param array $vars
* @return array
*/
private function parseArrayTags($vars)
{
if(!$this->arrayTags)
{
return $vars;
}
foreach($this->arrayTags as $p)
{
if(strpos($p,"/")!==false)
{
list($vl,$sub) = explode("/",$p);
}
else
{
$vl = $p;
$sub = false;
}
if($sub)
{
if(isset($vars[$vl][$sub]) && is_string($vars[$vl][$sub]))
{
$vars[$vl][$sub] = array($vars[$vl][$sub]);
}
continue;
}
if(isset($vars[$vl]) && is_array($vars[$vl]) && !varset($vars[$vl][0]))
{
$vars[$vl] = array($vars[$vl]);
}
}
return $vars;
}
/**
* Load XML file and parse it (optional)
*
* @param string $fname local or remote XML source file path
* @param boolean|string $parse false - no parse;
* true - use xml_convert_to_array();
* in any other case - use xml2array()
*
* @param boolean $replace_constants [optional]
* @return mixed
*/
function loadXMLfile($fname, $parse = false, $replace_constants = false)
{
$tp = e107::getParser();
if($this->_feedUrl !== false)
{
$fname = $this->_feedUrl;
}
if (empty($fname))
{
return false;
}
$xml = false;
if (strpos($fname, '://') !== false)
{
$this->getRemoteFile($fname);
}
else
{
if ($xml = file_get_contents($fname))
{
$this->xmlFileContents = $xml;
}
}
if ($this->xmlFileContents)
{
if ($replace_constants == true)
{
$this->xmlFileContents = $tp->replaceConstants($this->xmlFileContents, '', true);
}
if ($parse)
{
return $this->parseXML('', ($parse === true));
}
else
{
return $this->xmlFileContents;
}
}
return false;
}
/**
* Convert file path for inclusion in XML file.
* @see e107ExportValue()
* @param string $text - callback function
* @return string converted file path
*/
private function replaceFilePaths($text)
{
$fullpath = e107::getParser()->replaceConstants($text[1]);
$this->fileConvertLog[] = $fullpath;
$file = basename($fullpath);
return $this->filePathDestination.$file;
}
/**
* Process data values for XML file. If $this->convertFilePaths is TRUE, convert paths
*
* @see replaceFilePaths()
* @param mixed $val
* @param string $key key for the current value. Used for exception processing.
* @return mixed
*/
private function e107ExportValue($val, $key = '')
{
if($key && isset($this->filePathPrepend[$key]))
{
$val = $this->filePathPrepend[$key].$val;
}
if($this->convertFilePaths)
{
$types = implode("|",$this->convertFileTypes);
$val = preg_replace_callback("#({e_.*?\.(".$types."))#i", array($this,'replaceFilePaths'), $val);
}
if(is_array($val))
{
return "WriteArray($val,FALSE)."]]>";
}
if((strpos($val,"<")!==FALSE) || (strpos($val,">")!==FALSE) || (strpos($val,"&")!==FALSE))
{
return "";
}
return $val;
}
/**
* Create an e107 Export File in XML format
* Note: If $this->filePathDestination has a value, then the file will be saved there.
*
* @param array $prefs - see e_core_pref $aliases (eg. core, ipool etc)
* @param array $tables - table names without the prefix
* @param boolean $debug [optional]
* @return string text / file for download
*/
public function e107Export($xmlprefs, $tables, $debug = FALSE)
{
error_reporting(0);
require_once(e_ADMIN."ver.php");
$text = "\n";
$text .= "
".htmlentities($text).""; return TRUE; } else { if(!$text) { return FALSE; } $path = e107::getParser()->replaceConstants($this->filePathDestination); if($path) { file_put_contents($path."install.xml",$text,FILE_TEXT); return true; } header('Content-type: application/xml', TRUE); header("Content-disposition: attachment; filename= e107Export_" . date("Y-m-d").".xml"); header("Cache-Control: max-age=30"); header("Pragma: public"); echo $text; exit; } } /** * Return an Array of core preferences from e107 XML Dump data * * @param array $XMLData Raw XML e107 Export Data * @param string $prefType [optional] the type of core pref: core|emote|ipool|menu etc. * @return array preference array equivalent to the old $pref global; */ public function e107ImportPrefs($XMLData, $prefType='core') { if(!vartrue($XMLData['prefs'][$prefType])) { return array(); } //$mes = eMessage::getInstance(); $pref = array(); foreach($XMLData['prefs'][$prefType] as $val) { $name = $val['@attributes']['name']; // if(strpos($val['@value'], 'array (') === 0) // { // echo '
'.$val['@value']; // echo "\n"; // var_dump(e107::getArrayStorage()->ReadArray($val['@value'])); // echo $val['@value'].''; // } $value = strpos($val['@value'], 'array (') === 0 ? e107::getArrayStorage()->ReadArray($val['@value']) : $val['@value']; $pref[$name] = $value; // $mes->add("Setting up ".$prefType." Pref [".$name."] => ".$value, E_MESSAGE_DEBUG); } return $pref; } /** * Import an e107 XML file into site preferences and DB tables * * @param path $file - e107 XML file path * @param string $mode[optional] - add|replace * @param boolean $noLogs [optional] tells pref handler to disable admin logs when true (install issues) * @param boolean $debug [optional] * @return array with keys 'success' and 'failed' - DB table entry status. */ public function e107Import($file, $mode='replace', $noLogs = false, $debug=FALSE) { $xmlArray = $this->loadXMLfile($file, 'advanced'); if($debug) { //$message = print_r($xmlArray); echo "
".var_export($xmlArray,TRUE).""; return; } $ret = array(); if(vartrue($xmlArray['prefs'])) // Save Core Prefs { foreach($xmlArray['prefs'] as $type=>$array) { $pArray = $this->e107ImportPrefs($xmlArray,$type); if($mode == 'replace') // merge with existing, add new { e107::getConfig($type)->setPref($pArray); } else // 'add' only new prefs { foreach ($pArray as $pname => $pval) { e107::getConfig($type)->add($pname, $pval); // don't parse x/y/z } } if($debug == FALSE) { e107::getConfig($type) ->setParam('nologs', $noLogs) ->save(FALSE,TRUE); } } } if(vartrue($xmlArray['database'])) { foreach($xmlArray['database']['dbTable'] as $val) { $table = $val['@attributes']['name']; if(!isset($val['item'])) { continue; } foreach($val['item'] as $item) { $insert_array = array(); foreach($item['field'] as $f) { $fieldkey = $f['@attributes']['name']; $fieldval = (isset($f['@value'])) ? $f['@value'] : ""; $insert_array[$fieldkey] = $fieldval; } if(($mode == "replace") && e107::getDB()->db_Replace($table, $insert_array)!==FALSE) { $ret['success'][] = $table; } elseif(($mode == "add") && e107::getDB()->db_Insert($table, $insert_array)!==FALSE) { $ret['success'][] = $table; } else { $ret['failed'][] = $table; } } } } return $ret; } function getErrors($xml) { libxml_use_internal_errors(true); $sxe = simplexml_load_string($xml); $errors = array(); if (!$sxe) { foreach(libxml_get_errors() as $error) { $errors[] = $error->message. "Line:".$error->line." Column:".$error->column; } return $errors; } return FALSE; } } /** * DEPRECATED XML Class from v1.x */ class XMLParse { var $rawXML; var $valueArray = array(); var $keyArray = array(); var $parsed = array(); var $index = 0; var $attribKey = 'attributes'; var $valueKey = 'value'; var $cdataKey = 'cdata'; var $isError = false; var $error = ''; function XMLParse($xml = NULL) { $this->rawXML = $xml; $mes = e107::getMessage(); $mes->addDebug("Deprecated class XMLParse used. Please use 'xmlClass' instead"); } function parse($xml = NULL) { if (!is_null($xml)) { $this->rawXML = $xml; } $this->isError = false; if (!$this->parse_init()) { return false; } $this->index = 0; $this->parsed = $this->parse_recurse(); $this->status = 'parsing complete'; return $this->parsed; } function parse_recurse() { $found = array(); $tagCount = array(); while (isset($this->valueArray[$this->index])) { $tag = $this->valueArray[$this->index]; $this->index++; if ($tag['type'] == 'close') { return $found; } if ($tag['type'] == 'cdata') { $tag['tag'] = $this->cdataKey; $tag['type'] = 'complete'; } $tagName = $tag['tag']; if (isset($tagCount[$tagName])) { if ($tagCount[$tagName] == 1) { $found[$tagName] = array($found[$tagName]); } $tagRef =& $found[$tagName][$tagCount[$tagName]]; $tagCount[$tagName]++; } else { $tagCount[$tagName] = 1; $tagRef =& $found[$tagName]; } switch ($tag['type']) { case 'open': $tagRef = $this->parse_recurse(); if (isset($tag['attributes'])) { $tagRef[$this->attribKey] = $tag['attributes']; } if (isset($tag['value'])) { if (isset($tagRef[$this->cdataKey])) { $tagRef[$this->cdataKey] = (array)$tagRef[$this->cdataKey]; array_unshift($tagRef[$this->cdataKey], $tag['value']); } else { $tagRef[$this->cdataKey] = $tag['value']; } } break; case 'complete': if (isset($tag['attributes'])) { $tagRef[$this->attribKey] = $tag['attributes']; $tagRef =& $tagRef[$this->valueKey]; } if (isset($tag['value'])) { $tagRef = $tag['value']; } break; } } return $found; } function parse_init() { $this->parser = xml_parser_create(); $parser = $this->parser; xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); if (!$res = (bool)xml_parse_into_struct($parser, $this->rawXML, $this->valueArray, $this->keyArray)) { $this->isError = true; $this->error = 'error: '.xml_error_string(xml_get_error_code($parser)).' at line '.xml_get_current_line_number($parser); } xml_parser_free($parser); return $res; } }