<?php
/**
* Claw configuration handler. Deals with XML files.
*
* @package claw.ext
* @author Tomas Varaneckas <tomas [dot] varaneckas [at] gmail [dot] com>
* @version $Id: XMLClawConfig.php 120 2006-03-19 19:26:44Z spajus $
*/
class XMLClawConfig implements ClawConfig
{
/**
* Config xml object
*
* @var SimpleXMLElement
*/
protected $config;
/**
* Constructor, which also loads default configuration file
*
* @param string $default default configuration file (optional)
*/
public function __construct($default = null)
{
$default = $default == null ? 'default.cfg.xml' : $default;
$this->load($default);
}
/**
* Loads xml file and prepares for data retrieval with $this->get()
*
* @param string $xml_file path to XML file
* @return XMLClawConfig return self (for fluent interfaces)
*/
public function load($xml_file)
{
$xml = @simplexml_load_string(file_get_contents($xml_file, true));
if (!($xml instanceof SimpleXMLElement))
{
throw new ClawException('Invalid XML file: ' . $xml_file);
}
if ($this->config instanceof SimpleXMLElement)
{
$this->merge($this->config, $xml);
}
else
{
$this->config = $xml;
}
return $this;
}
/**
* Gets configuration variable from loaded xmls.
* Accepts infinite xml tree depth.
*
* Usage:
* $clawconfig->get('some', 'other') would get
* <some><other>VALUE</other></some>
* $clawconfig->get('third') would get <third>CONTENTS</third>
* $clawconfig->get(array('some', 'other'))
* would get same as example 1
*
* Returns mixed value if xml node has no children
* OR SimpleXMLElement object if children exist.
*
* @return string
*/
public function get()
{
return trim(strval($this->getPart(func_get_args())));
}
/**
* Checks if a variable or variable group exists in configuration file
* (depending on implementation)
*
* @return bool
*/
public function has()
{
$args = func_get_args();
if (is_array($args[0]))
{
$args = $args[0];
}
$var = $this->config;
foreach ($args as $arg)
{
$var = & $var->$arg;
if (empty($var))
{
return false;
}
}
return true;
}
/**
* Gets a segment of XML config as SimpleXMLElement
*
* @return SimpleXMLElement
*/
public function getPart()
{
$args = func_get_args();
if (is_array($args[0]))
{
$args = $args[0];
}
$var = $this->config;
foreach ($args as $arg)
{
$var = & $var->$arg;
if (empty($var))
{
throw new ClawException('Config part '
. implode('->', $args) . ' is not defined.');
}
}
return $var;
}
/**
* Gets part of configuration as array
* Limitation of 1 level of depth...
*
* @return Array
*/
public function getArray()
{
$array = array();
$xml = $this->getPart(func_get_args());
foreach ($xml->children() as $key => $value)
{
$array[$key] = strval($value);
}
return $array;
}
/**
* Sets configuration variable.
* At least two parameters (key and value) are required.
* First arguments are keys (can be one to infinity)
* Last argument is value
*
* Usage:
* $cfg->set('claw_renderer', 'engine', 'SmartyClawRenderer');
* would virtually append configuration file with:
* <claw_renderer><engine>SmartyClawRenderer</engine></claw_renderer>
* Safely merges variables.
*
* @return XMLClawConfig return self (for fluent interfaces)
*/
public function set()
{
$args = func_get_args();
if (count($args) < 2)
{
throw new ClawException('At least two parameters are required.');
}
$value = array_pop($args);
while ($arg = array_pop($args))
{
$value = "<$arg>$value</$arg>";
}
$this->merge($this->config,
simplexml_load_string('<?xml version="1.0"?><config>'
. $value . '</config>'));
return $this;
}
/**
* Either returns or saves current config as XML string/file
*
* @param string $xml_file_path Path to new file
* (optional, if not given - a string will be returned)
* @return string|bool
*/
public function asXML($xml_file_path = null)
{
if (strlen($xml_file_path))
{
@touch($xml_file_path);
if (!is_writable($xml_file_path))
{
throw new ClawException('File not writable: '
. $xml_file_path);
}
}
return $this->config->asXML($xml_file_path);
}
/**
* Merges two SimpleXMLElement objects into one.
* Overwrites existing values!
*
* @param SimpleXMLElement $xml1 first object
* (that will be filled with second one)
* @param SimpleXMLElement $xml2 second object
*/
protected function merge(SimpleXMLElement &$xml1, SimpleXMLElement $xml2)
{
$dom1 = new DomDocument();
$dom2 = new DomDocument();
$dom1->loadXML($xml1->asXML());
$dom2->loadXML($xml2->asXML());
$xpath = new domXPath($dom1);
$xpath_query = $xpath->query('/*/*');
for ($i = 0; $i < $xpath_query->length; $i++)
{
$dom2->documentElement->appendChild(
$dom2->importNode($xpath_query->item($i), true));
}
$xml1 = simplexml_import_dom($dom2);
}
}
?>