You are here

class DOMXPath in Feeds XPath Parser 8

Wraps DOMXPath providing enhanced debugging and special namespace handling.

Hierarchy

  • class \Drupal\feeds_xpathparser\DOMXPath
    • class \Drupal\feeds_xpathparser\DOMXPath

Expanded class hierarchy of DOMXPath

File

lib/Drupal/feeds_xpathparser/DOMXPath.php, line 13
Contains \Druapl\feeds_xpathparser\DOMXPath.

Namespace

Drupal\feeds_xpathparser
View source
class DOMXPath extends \DOMXPath {

  /**
   * The DOMDocument to parse.
   *
   * @var \DOMDocument
   */
  protected $doc;

  /**
   * Configuration array.
   *
   * @var array
   */
  protected $config = array();

  /**
   * Modified query cache.
   *
   * @var arrray
   */
  protected $modifiedQueries = array();

  /**
   * The namespaces in the document.
   *
   * @var arrray
   */
  protected $namepsaces = array();

  /**
   * The most recent error from parsing.
   *
   * @var \stdClass
   */
  protected $error;

  /**
   * Constructs a DOMXPath object.
   *
   * @param \DOMDocument $doc
   *   The DOMDocument that we're operating on.
   */
  public function __construct(\DOMDocument $doc) {
    $simple = simplexml_import_dom($doc);

    // An empty DOMDocument will make $simple NULL.
    if ($simple !== NULL) {
      $this->namespaces = $simple
        ->getNamespaces(TRUE);
    }
    $this->doc = $doc;
    parent::__construct($doc);
  }

  /**
   * Sets the extended configuration.
   *
   * @param array $config
   *   The config array.
   */
  public function setConfig(array $config) {
    $this->config = $config;
  }

  /**
   * Renders our debug messages into a list.
   *
   * @param mixed $data
   *   The result of an XPath query. Either a scalar or a DOMNodeList.
   * @param string $source
   *   The source key that produced this query.
   *
   * @todo Use theme_item_list().
   */
  protected function debug($data, $source) {
    $output = "{$source} : <ul>";
    if ($data instanceof \DOMNodeList) {
      foreach ($data as $node) {
        $output .= '<li>' . check_plain($this->doc
          ->saveXML($node)) . '</li>';
      }
    }
    else {
      $output .= '<li>' . check_plain($data) . '</li>';
    }
    $output .= '</ul>';
    drupal_set_message($output);
  }

  /**
   * Executes an XPath query with namespace support.
   *
   * @param string $query
   *   An XPath query.
   * @param string $source
   *   The source key for this query.
   * @param \DOMNode $context
   *   (optional) The current context of the XPath query. Defaults to null.
   *
   * @return array
   *   An array containing the results of the query.
   */
  public function namespacedQuery($query, $source, \DOMNode $context = NULL) {
    $this
      ->addDefaultNamespace($query);
    $results = $this
      ->executeQuery($query, $context);
    if (in_array($source, $this->config['debug'])) {
      $this
        ->debug($results, $source);
    }
    if (is_object($this->error) && $this->config['errors']) {
      if ($this->error->level == LIBXML_ERR_ERROR) {
        drupal_set_message(t('There was an error during the XPath query: %query.<br />Libxml returned the message: %message, with the error code: %code.', array(
          '%query' => $query,
          '%message' => trim($this->error->message),
          '%code' => $this->error->code,
        )), 'error', FALSE);
      }
      elseif ($this->error->level == LIBXML_ERR_WARNING) {
        drupal_set_message(t('There was an error during the XPath query: %query.<br />Libxml returned the message: %message, with the error code: %code.', array(
          '%query' => $query,
          '%message' => trim($this->error->message),
          '%code' => $this->error->code,
        )), 'warning', FALSE);
      }
    }

    // DOMXPath::evaluate() and DOMXPath::query() will return FALSE on error or
    // if the value is FALSE. We check error result and return NULL in case
    // of error.
    if (is_object($this->error) && $this->error->level == LIBXML_ERR_ERROR) {
      return NULL;
    }
    return $results;
  }

  /**
   * Normalizes XPath queries, adding the default namespace.
   *
   * @param string $query
   *   An XPath query string
   */
  protected function addDefaultNamespace(&$query) {
    foreach ($this->namespaces as $prefix => $namespace) {
      if ($prefix === '') {
        $this
          ->registerNamespace('__default__', $namespace);

        // Replace all the elements without prefix by the default prefix.
        if (!isset($this->modifiedQueries[$query])) {
          $parser = new XPathQueryParser($query);
          $mod_query = $parser
            ->getQuery();
          $this->modifiedQueries[$query] = $mod_query;
          $query = $mod_query;
        }
        else {
          $query = $this->modifiedQueries[$query];
        }
      }
      else {
        $this
          ->registerNamespace($prefix, $namespace);
      }
    }
  }

  /**
   * Performs a XPath query.
   *
   * Here we set libxml_use_internal_errors() to true because depending on the
   * libxml version, $xml->xpath() might return false or an empty array() when
   * a query doesn't match.
   *
   * @param string $query
   *   The XPath query string.
   * @param \DOMNode $context
   *   (optional) A context object. Defaults to NULL.
   *
   * @return mixed
   *   The result of the XPath query.
   */
  protected function executeQuery($query, \DOMNode $context = NULL) {
    $use_errors = libxml_use_internal_errors(TRUE);

    // Perfom XPath query.
    // So, grrr. FALSE is returned when there is an error. However, FALSE is
    // also a valid return value from DOMXPath::evaluate(). Ex: '1 = 2'
    if ($context) {
      $results = $this
        ->evaluate($query, $context);
    }
    else {
      $results = $this
        ->query($query);
    }
    $this->error = libxml_get_last_error();
    libxml_clear_errors();
    libxml_use_internal_errors($use_errors);
    return $results;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DOMXPath::$config protected property Configuration array. Overrides DOMXPath::$config 1
DOMXPath::$doc protected property The DOMDocument to parse. Overrides DOMXPath::$doc 1
DOMXPath::$error protected property The most recent error from parsing. Overrides DOMXPath::$error 1
DOMXPath::$modifiedQueries protected property Modified query cache. Overrides DOMXPath::$modifiedQueries 1
DOMXPath::$namepsaces protected property The namespaces in the document. Overrides DOMXPath::$namepsaces 1
DOMXPath::addDefaultNamespace protected function Normalizes XPath queries, adding the default namespace. Overrides DOMXPath::addDefaultNamespace 1
DOMXPath::debug protected function Renders our debug messages into a list. Overrides DOMXPath::debug 1
DOMXPath::executeQuery protected function Performs a XPath query. Overrides DOMXPath::executeQuery 1
DOMXPath::namespacedQuery public function Executes an XPath query with namespace support. Overrides DOMXPath::namespacedQuery 1
DOMXPath::setConfig public function Sets the extended configuration. Overrides DOMXPath::setConfig 1
DOMXPath::__construct public function Constructs a DOMXPath object. Overrides DOMXPath::__construct 1