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]);
  }
}

Leave a Reply

You must be logged in to post a comment.