Projet

Général

Profil

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

root / drupal7 / sites / all / modules / feeds_xpathparser / FeedsXPathParserDOMXPath.inc @ 9d13637e

1 85ad3d82 Assos Assos
<?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 f066bdb5 Assos Assos
13
  /**
14
   * The DOMDocument to parse.
15
   *
16
   * @var DOMDocument
17
   */
18
  protected $doc;
19
20
  /**
21
   * Configuration array.
22
   *
23
   * @var array
24
   */
25 85ad3d82 Assos Assos
  protected $config = array();
26 f066bdb5 Assos Assos
27
  /**
28
   * Modified query cache.
29
   *
30
   * @var arrray
31
   */
32 85ad3d82 Assos Assos
  protected $modifiedQueries = array();
33
34
  /**
35 f066bdb5 Assos Assos
   * The namespaces in the document.
36
   *
37
   * @var arrray
38
   */
39
  protected $namepsaces = array();
40
41
  /**
42
   * The most recent error from parsing.
43
   *
44
   * @var stdClass
45
   */
46
  protected $error;
47
48
  /**
49
   * Constructs a FeedsXPathParserDOMXPath object.
50 85ad3d82 Assos Assos
   *
51
   * @param DOMDocument $doc
52
   *   The DOMDocument that we're operating on.
53
   */
54
  public function __construct(DOMDocument $doc) {
55 f066bdb5 Assos Assos
56 85ad3d82 Assos Assos
    $simple = simplexml_import_dom($doc);
57 f066bdb5 Assos Assos
58 85ad3d82 Assos Assos
    // An empty DOMDocument will make $simple NULL.
59
    if ($simple !== NULL) {
60
      $this->namespaces = $simple->getNamespaces(TRUE);
61
    }
62
    $this->doc = $doc;
63 f066bdb5 Assos Assos
64 85ad3d82 Assos Assos
    parent::__construct($doc);
65
  }
66
67
  /**
68
   * Sets the extended configuration.
69
   *
70
   * @param array $config
71
   *   The config array.
72
   */
73
  public function setConfig(array $config) {
74
    $this->config = $config;
75
  }
76
77
  /**
78
   * Renders our debug messages into a list.
79
   *
80
   * @param mixed $data
81
   *   The result of an XPath query. Either a scalar or a DOMNodeList.
82
   * @param string $source
83
   *   The source key that produced this query.
84 f066bdb5 Assos Assos
   *
85
   * @todo Use theme_item_list().
86 85ad3d82 Assos Assos
   */
87
  protected function debug($data, $source) {
88
    $output = "$source : <ul>";
89 f066bdb5 Assos Assos
90 85ad3d82 Assos Assos
    if ($data instanceof DOMNodeList) {
91
      foreach ($data as $node) {
92
        $output .= '<li>' . check_plain($this->doc->saveXML($node)) . '</li>';
93
      }
94
    }
95
    else {
96
      $output .= '<li>' . check_plain($data) . '</li>';
97
    }
98
    $output .= '</ul>';
99 f066bdb5 Assos Assos
100 85ad3d82 Assos Assos
    drupal_set_message($output);
101
  }
102
103
  /**
104
   * Executes an XPath query with namespace support.
105
   *
106
   * @param string $query
107
   *   An XPath query.
108
   * @param DOMNode $context
109
   *   The current context of the XPath query.
110
   * @param string $source
111
   *   The source key for this query.
112
   *
113
   * @return array
114
   *   An array containing the results of the query.
115
   */
116
  public function namespacedQuery($query, $context, $source) {
117
    $this->addDefaultNamespace($query);
118 f066bdb5 Assos Assos
119 85ad3d82 Assos Assos
    $results = $this->executeQuery($query, $context);
120 f066bdb5 Assos Assos
121 85ad3d82 Assos Assos
    if (in_array($source, $this->config['debug'])) {
122
      $this->debug($results, $source);
123
    }
124
125
    if (is_object($this->error) && $this->config['errors']) {
126
127
      if ($this->error->level == LIBXML_ERR_ERROR) {
128
        drupal_set_message(
129
          t('There was an error during the XPath query: %query.<br />Libxml returned the message: %message, with the error code: %code.', array(
130
            '%query' => $query,
131
            '%message' => trim($this->error->message),
132
            '%code' => $this->error->code,
133
          )),
134
          'error',
135
          FALSE);
136
      }
137
      elseif ($this->error->level == LIBXML_ERR_WARNING) {
138
        drupal_set_message(
139
          t('There was an error during the XPath query: %query.<br />Libxml returned the message: %message, with the error code: %code.', array(
140
            '%query' => $query,
141
            '%message' => trim($this->error->message),
142
            '%code' => $this->error->code,
143
          )),
144
          'warning',
145
          FALSE);
146
      }
147
    }
148
149
    // DOMXPath::evaluate() and DOMXPath::query() will return FALSE on error or
150 f066bdb5 Assos Assos
    // if the value is FALSE. We check error result and return NULL in case
151 85ad3d82 Assos Assos
    // of error.
152
    if (is_object($this->error) && $this->error->level == LIBXML_ERR_ERROR) {
153
      return NULL;
154
    }
155
156
    return $results;
157
  }
158
159
  /**
160
   * Normalizes XPath queries, adding the default namespace.
161
   *
162
   * @param string $query
163
   *   An XPath query string
164
   */
165
  protected function addDefaultNamespace(&$query) {
166
    foreach ($this->namespaces as $prefix => $namespace) {
167
      if ($prefix === '') {
168
        $this->registerNamespace('__default__', $namespace);
169
170
        // Replace all the elements without prefix by the default prefix.
171
        if (!isset($this->modifiedQueries[$query])) {
172
          $parser = new FeedsXPathParserQueryParser($query);
173
          $mod_query = $parser->getQuery();
174
          $this->modifiedQueries[$query] = $mod_query;
175
          $query = $mod_query;
176
        }
177
        else {
178
          $query = $this->modifiedQueries[$query];
179
        }
180
      }
181
      else {
182
        $this->registerNamespace($prefix, $namespace);
183
      }
184
    }
185
  }
186
187
  /**
188
   * Performs a XPath query.
189
   *
190 f066bdb5 Assos Assos
   * Here we set libxml_use_internal_errors() to TRUE because depending on the
191 85ad3d82 Assos Assos
   * libxml version, $xml->xpath() might return FALSE or an empty array() when
192
   * a query doesn't match.
193
   *
194
   * @param string $query
195
   *   The XPath query string.
196
   * @param DOMNode $context
197 f066bdb5 Assos Assos
   *   (optional) A context object. Defaults to NULL.
198 85ad3d82 Assos Assos
   *
199
   * @return mixed
200
   *   The result of the XPath query.
201
   */
202 f066bdb5 Assos Assos
  protected function executeQuery($query, DOMNode $context = NULL) {
203 85ad3d82 Assos Assos
    $use_errors = libxml_use_internal_errors(TRUE);
204
205
    // Perfom XPath query.
206
    // So, grrr. FALSE is returned when there is an error. However, FALSE is
207
    // also a valid return value from DOMXPath::evaluate(). Ex: '1 = 2'
208
    if ($context) {
209
      $results = $this->evaluate($query, $context);
210
    }
211
    else {
212
      $results = $this->query($query);
213
    }
214
215
    $this->error = libxml_get_last_error();
216
    libxml_clear_errors();
217
    libxml_use_internal_errors($use_errors);
218
    return $results;
219
  }
220
221
}