Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2150 mathias 1
<?php
2
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
 
4
/**
5
 * Key gateway class for XML_Feed_Parser package
6
 *
7
 * PHP versions 5
8
 *
9
 * LICENSE: This source file is subject to version 3.0 of the PHP license
10
 * that is available through the world-wide-web at the following URI:
11
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
12
 * the PHP License and are unable to obtain it through the web, please
13
 * send a note to license@php.net so we can mail you a copy immediately.
14
 *
15
 * @category   XML
16
 * @package    XML_Feed_Parser
17
 * @author     James Stewart <james@jystewart.net>
18
 * @copyright  2005 James Stewart <james@jystewart.net>
19
 * @license    http://www.gnu.org/copyleft/lesser.html  GNU LGPL
20
 * @version    CVS: $Id: Parser.php 304308 2010-10-11 12:05:50Z clockwerx $
21
 * @link       http://pear.php.net/package/XML_Feed_Parser/
22
 */
23
 
24
/**
25
 * This is the core of the XML_Feed_Parser package. It identifies feed types
26
 * and abstracts access to them. It is an iterator, allowing for easy access
27
 * to the entire feed.
28
 *
29
 * @author  James Stewart <james@jystewart.net>
30
 * @version Release: @package_version@
31
 * @package XML_Feed_Parser
32
 */
33
class XmlFeedParser implements Iterator {
34
    /**
35
     * This is where we hold the feed object
36
     * @var Object
37
     */
38
    private $feed;
39
 
40
    /**
41
     * To allow for extensions, we make a public reference to the feed model
42
     * @var DOMDocument
43
     */
44
    public $model;
45
 
46
    /**
47
     * A map between entry ID and offset
48
     * @var array
49
     */
50
    protected $idMappings = array();
51
 
52
    /**
53
     * A storage space for Namespace URIs.
54
     * @var array
55
     */
56
    private $feedNamespaces = array(
57
        'rss2' => array(
58
            'http://backend.userland.com/rss',
59
            'http://backend.userland.com/rss2',
60
            'http://blogs.law.harvard.edu/tech/rss'));
61
    /**
62
     * Detects feed types and instantiate appropriate objects.
63
     *
64
     * Our constructor takes care of detecting feed types and instantiating
65
     * appropriate classes. For now we're going to treat Atom 0.3 as Atom 1.0
66
     * but raise a warning. I do not intend to introduce full support for
67
     * Atom 0.3 as it has been deprecated, but others are welcome to.
68
     *
69
     * @param    string    $feed    XML serialization of the feed
70
     * @param    bool    $strict    Whether or not to validate the feed
71
     * @param    bool    $suppressWarnings Trigger errors for deprecated feed types?
72
     * @param    bool    $tidy    Whether or not to try and use the tidy library on input
73
     */
74
    function __construct($feed, $strict = false, $suppressWarnings = true, $tidy = true) {
75
        $this->model = new DOMDocument;
76
        if (! $this->model->loadXML($feed)) {
77
            if (extension_loaded('tidy') && $tidy) {
78
                $tidy = new tidy;
79
                $tidy->parseString($feed,  array('input-xml' => true, 'output-xml' => true));
80
                $tidy->cleanRepair();
81
                if (! $this->model->loadXML((string) $tidy)) {
82
                    throw new XmlFeedParserException("Entrée invalide : le flux n'est pas du XML valide");
83
                }
84
            } else {
85
                throw new XmlFeedParserException("Entrée invalide : le flux n'est pas du XML valide");
86
            }
87
        }
88
 
89
        /* detect feed type */
90
        $doc_element = $this->model->documentElement;
91
        $error = false;
92
 
93
        switch (true) {
94
            case ($doc_element->namespaceURI == 'http://www.w3.org/2005/Atom'):
95
                $class = 'XmlFeedParserAtom';
96
                break;
97
            case ($doc_element->namespaceURI == 'http://purl.org/atom/ns#'):
98
                $class = 'XmlFeedParserAtom';
99
                $error = "Atom 0.3 est déprécié, le parseur en version 1.0 sera utilisé mais toutes les options ne seront pas disponibles.";
100
                break;
101
            case ($doc_element->namespaceURI == 'http://purl.org/rss/1.0/'
102
                	|| ($doc_element->hasChildNodes() && $doc_element->childNodes->length > 1
103
                	&& $doc_element->childNodes->item(1)->namespaceURI == 'http://purl.org/rss/1.0/')):
104
                $class = 'XmlFeedParserRss1';
105
                break;
106
            case ($doc_element->namespaceURI == 'http://purl.org/rss/1.1/'
107
                	|| ($doc_element->hasChildNodes()
108
                	&& $doc_element->childNodes->length > 1
109
                	&& $doc_element->childNodes->item(1)->namespaceURI == 'http://purl.org/rss/1.1/')):
110
                $class = 'XmlFeedParserRss11';
111
                break;
112
            case (($doc_element->hasChildNodes()
113
            		&& $doc_element->childNodes->length > 1
114
	                && $doc_element->childNodes->item(1)->namespaceURI == 'http://my.netscape.com/rdf/simple/0.9/')
115
	                || $doc_element->namespaceURI == 'http://my.netscape.com/rdf/simple/0.9/'):
116
                $class = 'XmlFeedParserRss09';
117
                break;
118
            case ($doc_element->tagName == 'rss'
119
                	and $doc_element->hasAttribute('version')
120
                	&& $doc_element->getAttribute('version') == 0.91):
121
                $error = 'RSS 0.91 has been superceded by RSS2.0. Using RSS2.0 parser.';
122
                $class = 'XmlFeedParserRss2';
123
                break;
124
            case ($doc_element->tagName == 'rss'
125
                	and $doc_element->hasAttribute('version')
126
                	&& $doc_element->getAttribute('version') == 0.92):
127
                $error = 'RSS 0.92 has been superceded by RSS2.0. Using RSS2.0 parser.';
128
                $class = 'XmlFeedParserRss2';
129
                break;
130
            case (in_array($doc_element->namespaceURI, $this->feedNamespaces['rss2'])
131
                	|| $doc_element->tagName == 'rss'):
132
                if (! $doc_element->hasAttribute('version') || $doc_element->getAttribute('version') != 2) {
133
                    $error = 'RSS version not specified. Parsing as RSS2.0';
134
                }
135
                $class = 'XmlFeedParserRss2';
136
                break;
137
            default:
138
                throw new XmlFeedParserException('Type de flux de syndicaton inconnu');
139
                break;
140
        }
141
 
142
        if (! $suppressWarnings && ! empty($error)) {
143
            trigger_error($error, E_USER_WARNING);
144
        }
145
 
146
        /* Instantiate feed object */
147
        $this->feed = new $class($this->model, $strict);
148
    }
149
 
150
    /**
151
     * Proxy to allow feed element names to be used as method names
152
     *
153
     * For top-level feed elements we will provide access using methods or
154
     * attributes. This function simply passes on a request to the appropriate
155
     * feed type object.
156
     *
157
     * @param   string  $call - the method being called
158
     * @param   array   $attributes
159
     */
160
    function __call($call, $attributes) {
161
        $attributes = array_pad($attributes, 5, false);
162
        list($a, $b, $c, $d, $e) = $attributes;
163
        return $this->feed->$call($a, $b, $c, $d, $e);
164
    }
165
 
166
    /**
167
     * Proxy to allow feed element names to be used as attribute names
168
     *
169
     * To allow variable-like access to feed-level data we use this
170
     * method. It simply passes along to __call() which in turn passes
171
     * along to the relevant object.
172
     *
173
     * @param   string  $val - the name of the variable required
174
     */
175
    function __get($val) {
176
        return $this->feed->$val;
177
    }
178
 
179
    /**
180
     * Provides iteration functionality.
181
     *
182
     * Of course we must be able to iterate... This function simply increases
183
     * our internal counter.
184
     */
185
    function next() {
186
        if (isset($this->current_item) &&
187
            $this->current_item <= $this->feed->numberEntries - 1) {
188
            ++$this->current_item;
189
        } else if (! isset($this->current_item)) {
190
            $this->current_item = 0;
191
        } else {
192
            return false;
193
        }
194
    }
195
 
196
    /**
197
     * Return XML_Feed_Type object for current element
198
     *
199
     * @return    XML_Feed_Parser_Type Object
200
     */
201
    function current() {
202
        return $this->getEntryByOffset($this->current_item);
203
    }
204
 
205
    /**
206
     * For iteration -- returns the key for the current stage in the array.
207
     *
208
     * @return    int
209
     */
210
    function key() {
211
        return $this->current_item;
212
    }
213
 
214
    /**
215
     * For iteration -- tells whether we have reached the
216
     * end.
217
     *
218
     * @return    bool
219
     */
220
    function valid() {
221
        return $this->current_item < $this->feed->numberEntries;
222
    }
223
 
224
    /**
225
     * For iteration -- resets the internal counter to the beginning.
226
     */
227
    function rewind() {
228
        $this->current_item = 0;
229
    }
230
 
231
    /**
232
     * Provides access to entries by ID if one is specified in the source feed.
233
     *
234
     * As well as allowing the items to be iterated over we want to allow
235
     * users to be able to access a specific entry. This is one of two ways of
236
     * doing that, the other being by offset. This method can be quite slow
237
     * if dealing with a large feed that hasn't yet been processed as it
238
     * instantiates objects for every entry until it finds the one needed.
239
     *
240
     * @param    string    $id  Valid ID for the given feed format
241
     * @return    XML_Feed_Parser_Type|false
242
     */
243
    function getEntryById($id) {
244
        if (isset($this->idMappings[$id])) {
245
            return $this->getEntryByOffset($this->idMappings[$id]);
246
        }
247
 
248
        /*
249
         * Since we have not yet encountered that ID, let's go through all the
250
         * remaining entries in order till we find it.
251
         * This is a fairly slow implementation, but it should work.
252
         */
253
        return $this->feed->getEntryById($id);
254
    }
255
 
256
    /**
257
     * Retrieve entry by numeric offset, starting from zero.
258
     *
259
     * As well as allowing the items to be iterated over we want to allow
260
     * users to be able to access a specific entry. This is one of two ways of
261
     * doing that, the other being by ID.
262
     *
263
     * @param    int    $offset The position of the entry within the feed, starting from 0
264
     * @return    XML_Feed_Parser_Type|false
265
     */
266
    function getEntryByOffset($offset) {
267
        if ($offset < $this->feed->numberEntries) {
268
            if (isset($this->feed->entries[$offset])) {
269
                return $this->feed->entries[$offset];
270
            } else {
271
                try {
272
                    $this->feed->getEntryByOffset($offset);
273
                } catch (Exception $e) {
274
                    return false;
275
                }
276
                $id = $this->feed->entries[$offset]->getID();
277
                $this->idMappings[$id] = $offset;
278
                return $this->feed->entries[$offset];
279
            }
280
        } else {
281
            return false;
282
        }
283
    }
284
 
285
    /**
286
     * Retrieve version details from feed type class.
287
     *
288
     * @return void
289
     * @author James Stewart
290
     */
291
    function version() {
292
        return $this->feed->version;
293
    }
294
 
295
    /**
296
     * Returns a string representation of the feed.
297
     *
298
     * @return String
299
     **/
300
    function __toString() {
301
        return $this->feed->__toString();
302
    }
303
}
304
?>