BetterXML
Everyone hates the way that PHP’s SimpleXML object handles variables. We find ourselves constantly checking if the string we’ve been given back contains numeric or boolean values. Well… here’s a quick fix.
<?php /** * A decorator for SimpleXML to add 2 important things: * 1) The "hasChildren() method". * 2) The "asVar()" method which uses type checking to discover integers and boolean values. * * @author John Wright */ class BetterXML implements ArrayAccess, Iterator { /** * The SimpleXML object * * @var SimpleXMLElement */ protected $simple_xml; /** * Integer value used for iteration. * * @var integer */ private $pos=0; /** * When methods don't exist in this class, check the decorated * SimpleXMLElement object. * * @param string $method * @param mixed[] $args * @return mixed */ public function __call($method, array $args=array()) { if (method_exists($this->simple_xml, $method)) { return call_user_func_array(array($this->simple_xml, $method), $args); } throw new BadMethodCallException("Method BetterXML::$method() does not exist."); } /** * Constructor * * @param string|SimpleXMLElement $content This can be a string containing XML content, a string representing a path to an XML file or a SimpleXML object * @return BetterXML */ public function __construct($content) { if ($content instanceof SimpleXMLElement) { $this->simple_xml = $content; } elseif (file_exists($content)) { $this->simple_xml = simplexml_load_file($content); } else { $this->simple_xml = simplexml_load_string($content); } } /** * Decorate all SimpleXML objects with the BetterXML class. * * @param string $var_name * @return BetterXML */ public function __get($var_name) { $var = $this->simple_xml->$var_name; return $var ? new BetterXML($var) : $var; } /** * Setting will actually set to the decorated SimpleXML object. * * @var string $name * @var mixed $value * @return void */ public function __set($name, $value) { $this->simple_xml->$name = $value; } /** * Decoration method for SimpleXML::_toString(). * * @return string */ public function __toString() { return (string)$this->simple_xml; } /** * Returns a variable other than BetterXML. * * If this has children, the method will just return itself. * If the content is numeric, the method will return an integer or float. * If the content is either "true" or "false" the method will return a boolean. * If all else fails, this will return the content as a string. * * @return mixed */ public function asVar() { if ($this->hasChildren()) { return $this; } $var = trim((string)$this->simple_xml); if (is_numeric($var)) { return strpos('.', $var) !== false ? (float)$var : (int)$var; } elseif (preg_match('/^true$/i', $var)) { return true; } elseif (preg_match('/^false$/i', $var)) { return false; } return $var; } /** * Returns the decorated SimpleXML object. * * @return SimpleXMLElement */ public function getRaw() { return $this->simple_xml; } /** * Returns a boolean depicting whether this element has * children. * * @return boolean */ public function hasChildren() { return $this->simple_xml->children() > 0 ? true : false; } /**#@+ * @see Iterator */ public function current() { return $this->offsetGet($this->pos); } /**#@-*/ public function key() { return $this->pos; } /**#@-*/ public function next() { ++$this->pos; return $this->offsetGet($this->pos); } /**#@-*/ public function rewind() { $this->pos = 0; return $this->offsetGet($this->pos); } /**#@-*/ public function valid() { return $this->offsetExists($this->pos); } /**#@+ * @see ArrayAccess */ public function offsetExists($offset) { return isset($this->simple_xml[$offset]); } /**#@-*/ public function offsetGet($offset) { $var = $this->simple_xml[$offset]; return $var ? new BetterXML($var) : $var; } /**#@-*/ public function offsetSet($offset, $value) { $this->simple_xml[$offset] = $value; } /**#@-*/ public function offsetUnset($offset) { unset($this->simple_xml[$offset]); } }
