Projet

Général

Profil

Paste
Télécharger (5,09 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / feeds_xpathparser / FeedsXPathParserDOMXPath.inc @ 135bbeb9

1
<?php
2

    
3
/**
4
 * @file
5
 * Provides a custom version of DOMXPath for use with feeds_xpathparser.
6
 */
7

    
8
/**
9
 * Wraps DOMXPath providing enhanced debugging and special namespace handling.
10
 */
11
class FeedsXPathParserDOMXPath extends DOMXPath {
12
  protected $config = array();
13
  protected $modifiedQueries = array();
14

    
15
  /**
16
   * Constructs a FeedsXPathParserDOMXPath.
17
   *
18
   * @param DOMDocument $doc
19
   *   The DOMDocument that we're operating on.
20
   */
21
  public function __construct(DOMDocument $doc) {
22
    $this->namespaces = array();
23
    $simple = simplexml_import_dom($doc);
24
    // An empty DOMDocument will make $simple NULL.
25
    if ($simple !== NULL) {
26
      $this->namespaces = $simple->getNamespaces(TRUE);
27
    }
28
    $this->doc = $doc;
29
    parent::__construct($doc);
30
  }
31

    
32
  /**
33
   * Sets the extended configuration.
34
   *
35
   * @param array $config
36
   *   The config array.
37
   */
38
  public function setConfig(array $config) {
39
    $this->config = $config;
40
  }
41

    
42
  /**
43
   * Renders our debug messages into a list.
44
   *
45
   * @param mixed $data
46
   *   The result of an XPath query. Either a scalar or a DOMNodeList.
47
   * @param string $source
48
   *   The source key that produced this query.
49
   */
50
  protected function debug($data, $source) {
51
    $output = "$source : <ul>";
52
    if ($data instanceof DOMNodeList) {
53
      foreach ($data as $node) {
54
        $output .= '<li>' . check_plain($this->doc->saveXML($node)) . '</li>';
55
      }
56
    }
57
    else {
58
      $output .= '<li>' . check_plain($data) . '</li>';
59
    }
60
    $output .= '</ul>';
61
    drupal_set_message($output);
62
  }
63

    
64
  /**
65
   * Executes an XPath query with namespace support.
66
   *
67
   * @param string $query
68
   *   An XPath query.
69
   * @param DOMNode $context
70
   *   The current context of the XPath query.
71
   * @param string $source
72
   *   The source key for this query.
73
   *
74
   * @return array
75
   *   An array containing the results of the query.
76
   */
77
  public function namespacedQuery($query, $context, $source) {
78
    $this->addDefaultNamespace($query);
79
    $results = $this->executeQuery($query, $context);
80
    if (in_array($source, $this->config['debug'])) {
81
      $this->debug($results, $source);
82
    }
83

    
84
    if (is_object($this->error) && $this->config['errors']) {
85

    
86
      if ($this->error->level == LIBXML_ERR_ERROR) {
87
        drupal_set_message(
88
          t('There was an error during the XPath query: %query.<br />Libxml returned the message: %message, with the error code: %code.', array(
89
            '%query' => $query,
90
            '%message' => trim($this->error->message),
91
            '%code' => $this->error->code,
92
          )),
93
          'error',
94
          FALSE);
95
      }
96
      elseif ($this->error->level == LIBXML_ERR_WARNING) {
97
        drupal_set_message(
98
          t('There was an error during the XPath query: %query.<br />Libxml returned the message: %message, with the error code: %code.', array(
99
            '%query' => $query,
100
            '%message' => trim($this->error->message),
101
            '%code' => $this->error->code,
102
          )),
103
          'warning',
104
          FALSE);
105
      }
106
    }
107

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

    
115
    return $results;
116
  }
117

    
118
  /**
119
   * Normalizes XPath queries, adding the default namespace.
120
   *
121
   * @param string $query
122
   *   An XPath query string
123
   */
124
  protected function addDefaultNamespace(&$query) {
125
    foreach ($this->namespaces as $prefix => $namespace) {
126
      if ($prefix === '') {
127
        $this->registerNamespace('__default__', $namespace);
128

    
129
        // Replace all the elements without prefix by the default prefix.
130
        if (!isset($this->modifiedQueries[$query])) {
131
          $parser = new FeedsXPathParserQueryParser($query);
132
          $mod_query = $parser->getQuery();
133
          $this->modifiedQueries[$query] = $mod_query;
134
          $query = $mod_query;
135
        }
136
        else {
137
          $query = $this->modifiedQueries[$query];
138
        }
139
      }
140
      else {
141
        $this->registerNamespace($prefix, $namespace);
142
      }
143
    }
144
  }
145

    
146
  /**
147
   * Performs a XPath query.
148
   *
149
   * Here we set libxml_use_internal_errors to TRUE because depending on the
150
   * libxml version, $xml->xpath() might return FALSE or an empty array() when
151
   * a query doesn't match.
152
   *
153
   * @param string $query
154
   *   The XPath query string.
155
   * @param DOMNode $context
156
   *   (Optional) A context object. Defaults to NULL.
157
   *
158
   * @return mixed
159
   *   The result of the XPath query.
160
   */
161
  protected function executeQuery($query, $context = NULL) {
162
    $use_errors = libxml_use_internal_errors(TRUE);
163

    
164
    // Perfom XPath query.
165
    // So, grrr. FALSE is returned when there is an error. However, FALSE is
166
    // also a valid return value from DOMXPath::evaluate(). Ex: '1 = 2'
167
    if ($context) {
168
      $results = $this->evaluate($query, $context);
169
    }
170
    else {
171
      $results = $this->query($query);
172
    }
173

    
174
    $this->error = libxml_get_last_error();
175
    libxml_clear_errors();
176
    libxml_use_internal_errors($use_errors);
177
    return $results;
178
  }
179

    
180
}