## Apache Zeta Components Manual :: File Source for feed.php

## Source for file feed.php

Documentation is available at feed.php

1. <?php
2. /**
3. * File containing the ezcFeed class.
4. *
5. * Licensed to the Apache Software Foundation (ASF) under one
6. * or more contributor license agreements. See the NOTICE file
7. * distributed with this work for additional information
8. * regarding copyright ownership. The ASF licenses this file
9. * to you under the Apache License, Version 2.0 (the
10. * "License"); you may not use this file except in compliance
11. * with the License. You may obtain a copy of the License at
12. *
13. * http://www.apache.org/licenses/LICENSE-2.0
14. *
15. * Unless required by applicable law or agreed to in writing,
16. * software distributed under the License is distributed on an
17. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18. * KIND, either express or implied. See the License for the
19. * specific language governing permissions and limitations
20. * under the License.
21. *
22. * @package Feed
23. * @version //autogentag//
24. * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
25. * @filesource
26. */
28. /**
29. * Class defining a feed.
30. *
31. * A feed has a type (eg. RSS1, RSS2 or ATOM). The feed type defines which
32. * processor is used to parse and generate that type.
33. *
34. * The following feed processors are supported by the Feed component:
35. * - ATOM ({@link ezcFeedAtom}) -
36. * {@link http://atompub.org/rfc4287.html RFC4287}
37. * - RSS1 ({@link ezcFeedRss1}) -
38. * {@link http://web.resource.org/rss/1.0/spec Specifications}
39. * - RSS2 ({@link ezcFeedRss2}) -
40. * {@link http://www.rssboard.org/rss-specification Specifications}
41. *
42. * A new processor can be defined by creating a class which extends the class
43. * {@link ezcFeedProcessor} and implements the interface {@link ezcFeedParser}.
44. * The new class needs to be added to the supported feed types list by calling
45. * the function {@link registerFeed}.
46. *
47. * The following modules are supported by the Feed component:
48. * - Content ({@link ezcFeedContentModule}) -
49. * {@link http://purl.org/rss/1.0/modules/content/ Specifications}
50. * - CreativeCommons ({@link ezcFeedCreativeCommonsModule}) -
51. * {@link http://backend.userland.com/creativeCommonsRssModule Specifications}
52. * - DublinCore ({@link ezcFeedDublinCoreModule}) -
53. * {@link http://dublincore.org/documents/dces/ Specifications}
54. * - Geo ({@link ezcFeedGeoModule}) -
55. * {@link http://www.w3.org/2003/01/geo/ Specifications}
56. * - GeoRss ({@link ezcFeedGeoRssModule}) -
57. * {@link http://www.georss.org/georss/ Specifications}
58. * - iTunes ({@link ezcFeedITunesModule}) -
59. * {@link http://www.apple.com/itunes/store/podcaststechspecs.html Specifications}
60. *
61. * A new module can be defined by creating a class which extends the class
62. * {@link ezcFeedModule}. The new class needs to be added to the supported modules
63. * list by calling {@link registerModule}.
64. *
65. * A feed object can be created in different ways:
66. * - by calling the constructor (with the optional feed type). Example:
67. *
68. * <code>
69. * $feed = new ezcFeed();
70. * </code>
71. *
72. * - by parsing an existing XML file or URL. The feed type of the resulting
73. * ezcFeed object will be autodetected. Example:
74. *
75. * <code>
76. * $feed = ezcFeed::parse( 'http://www.example.com/rss2.xml' ); // URL
77. * $feed = ezcFeed::parse( 'http://username:password@www.example.com/rss2.xml' ); // URL with HTTP authentication
78. * $feed = ezcFeed::parse( '/tmp/rss2.xml' ); // local file
79. * </code>
80. *
81. * - by parsing an XML document stored in a string variable. The feed type of
82. * the resulting ezcFeed object will be autodetected. Example:
83. *
84. * <code>
85. * $feed = ezcFeed::parseContent( $xmlString );
86. * </code>
87. *
88. * Parsing a feed (in the following examples $feed is an existing ezcFeed object):
89. * - get a value from the feed object. Example:
90. *
91. * <code>
92. * $title = $feed->title->__toString();
93. * </code>
94. *
95. * - iterate over the items in the feed. Example:
96. *
97. * <code>
98. * <?php
99. * // retrieve the titles from the feed items
100. * foreach ( $feed->item as $item )
101. * {
102. * $titles[] = $item->title->__toString();
103. * }
104. * </code>
105. *
106. * - parse a module. Example of parsing the Geo module (ezcFeedGeoModule):
107. *
108. * <code>
109. * <?php
110. * $locations = array();
111. * foreach ( $feed->item as $item )
112. * {
113. * if ( isset( $item->Geo ) )
114. * {
115. * $locations[] = array(
116. * 'title' => $item->title->__toString(),
117. * 'alt' => isset( $item->Geo->alt ) ? $item->Geo->alt->__toString() : null,
118. * 'lat' => isset( $item->Geo->lat ) ? $item->Geo->lat->__toString() : null,
119. * 'long' => isset( $item->Geo->long ) ? $item->Geo->long->__toString() : null
120. * );
121. * }
122. * }
123. * ?>
124. * </code>
125. *
126. * - iterate over the loaded modules in a feed item. Example:
127. *
128. * <code>
129. * <?php
130. * // display the names and namespaces of the modules loaded in the feed item $item
131. * foreach ( $item->getModules() as $moduleName => $module )
132. * {
133. * echo $moduleName . ':' . $module->getNamespace();
134. * }
135. * ?>
136. * </code>
137. *
138. * Generating a feed:
139. * - create a feed object. Example:
140. *
141. * <code>
142. * $feed = new ezcFeed();
143. * </code>
144. *
145. * - set a value to the feed object. Example:
146. *
147. * <code>
148. * $feed->title = 'News';
149. * </code>
150. *
151. * - add a new item to the feed. Example:
152. *
153. * <code>
154. * <?php
155. * $item = $feed->add( 'item' );
156. * $item->title = 'Item title';
157. * ?>
158. * </code>
159. *
160. * - add a new module to the feed item. Example:
161. *
162. * <code>
163. * <?php
164. * $item = $feed->add( 'item' );
165. * $module = $item->addModule( 'Content' );
166. * $content->encoded = 'text content which will be encoded';
167. * ?>
168. * </code>
169. *
170. * - generate an XML document from the {@link ezcFeed} object. The result
171. * string should be saved to a file, and a link to a file made accessible
172. * to users of the application. Example:
173. *
174. * <code>
175. * <?php
176. * $xmlAtom = $feed->generate( 'atom' );
177. * $xmlRss1 = $feed->generate( 'rss1' );
178. * $xmlRss2 = $feed->generate( 'rss2' );
179. * ?>
180. * </code>
181. *
182. * Note: Assigning values to feed elements should be done in a way that will not
183. * break the resulting XML document. In other words, encoding of special characters
184. * to HTML entities is not done by default, and the developer is responsible with
185. * calling htmlentities() himself when assigning values to feed elements. Example:
186. * if the feed title contains the "&" character, it is the responsability of the
187. * developer to encode it properly as "&amp;".
188. *
189. * Example of creating a feed with a user-defined type:
190. *
191. * <code>
192. * <?php
193. * ezcFeed::registerFeed( 'opml', 'myOpmlHandler');
194. *
195. * $feed = new ezcFeed();
196. * // add properties to $feed
197. *
198. * $xml = $feed->generate( 'opml' );
199. * ?>
200. * </code>
201. *
202. * In the above example, myOpmlHandler extends {@link ezcFeedProcessor} and
203. * implements {@link ezcFeedParser}.
204. *
205. * Example of creating a feed with a user-defined module:
206. *
207. * <code>
208. * <?php
209. * ezcFeed::registerModule( 'Slash', 'mySlashHandler', 'slash');
210. *
211. * $feed = new ezcFeed();
212. * $item = $feed->add( 'item' );
213. * $slash = $item->addModule( 'Slash' );
214. * // add properties for the Slash module to $slash
215. *
216. * $xml = $feed->generate( 'rss2' ); // or the feed type which is needed
217. * ?>
218. * </code>
219. *
220. * In the above example mySlashHandler extends {@link ezcFeedModule}.
221. *
222. * @property array(ezcFeedPersonElement) $author
223. * Author(s) of the feed. Equivalents:
224. * ATOM-author (required, multiple),
225. * RSS1-none,
226. * RSS2-managingEditor (optional, recommended, single).
227. * @property array(ezcFeedCategoryElement) $category
228. * Categories for the feed. Equivalents:
229. * ATOM-category (optional, multiple),
230. * RSS1-none,
231. * RSS2-category (optional, multiple).
232. * @property ezcFeedCloudElement $cloud
233. * Allows processes to register with a cloud to be notified of updates
234. * to the channel, implementing a lightweight publish-subscribe
235. * protocol for RSS feeds. Equivalents:
236. * ATOM-none,
237. * RSS1-none,
238. * RSS2-cloud (optional, not recommended, single).
239. * @property array(ezcFeedPersonElement) $contributor
240. * Contributor(s) for the feed. Equivalents:
241. * ATOM-contributor (optional, not recommended, multiple),
242. * RSS1-none,
243. * RSS2-none.
244. * @property ezcFeedTextElement $copyright
245. * Copyright information for the feed. Equivalents:
246. * ATOM-rights (optional, single),
247. * RSS1-none,
248. * RSS2-copyright (optional, single).
249. * @property ezcFeedTextElement $description
250. * A short description of the feed. Equivalents:
251. * ATOM-subtitle (required, single),
252. * RSS1-description (required, single),
253. * RSS2-description (required, single).
254. * @property ezcFeedTextElement $docs
255. * An URL that points to the documentation for the format used in the
256. * feed file. Equivalents:
257. * ATOM-none,
258. * RSS1-none,
259. * RSS2-docs (optional, not recommended, single) - usual value is
260. * {@link http://www.rssboard.org/rss-specification}.
261. * @property ezcFeedGeneratorElement $generator
262. * Indicates the software used to generate the feed. Equivalents:
263. * ATOM-generator (optional, single),
264. * RSS1-none,
265. * RSS2-generator (optional, single).
266. * @property ezcFeedImageElement $icon
267. * An icon for a feed, similar with favicon.ico for websites. Equivalents:
268. * ATOM-icon (optional, not recommended, single),
269. * RSS1-none,
270. * RSS2-none.
271. * @property ezcFeedIdElement $id
272. * A universally unique and permanent identifier for a feed. For
273. * example, it can be an Internet domain name. Equivalents:
274. * ATOM-id (required, single),
275. * RSS1-about (required, single),
276. * RSS2-id (optional, single).
277. * @property ezcFeedImageElement $image
278. * An image associated with the feed. Equivalents:
279. * ATOM-logo (optional, single),
280. * RSS1-image (optional, single),
281. * RSS2-image (optional, single).
282. * @property-read array(ezcFeedEntryElement) $item
283. * Feed items (entries). Equivalents:
284. * ATOM-entry (optional, recommended, multiple),
285. * RSS1-item (required, multiple),
286. * RSS2-item (required, multiple).
287. * @property ezcFeedTextElement $language
288. * The language for the feed. Equivalents:
289. * ATOM-xml:lang attribute for title, description, copyright, content,
290. * comments (optional, single) - accessed as language through ezcFeed,
291. * RSS1-none,
292. * RSS2-language (optional, single).
293. * @property array(ezcFeedLinkElement) $link
294. * URLs to the HTML websites corresponding to the channel. Equivalents:
295. * ATOM-link (required one link with rel='self', multiple),
296. * RSS1-link (required, single),
297. * RSS2-link (required, single).
298. * @property ezcFeedDateElement $published
299. * The time the feed was published. Equivalents:
300. * ATOM-none,
301. * RSS1-none,
302. * RSS2-pubDate (optional, not recommended, single).
303. * @property ezcFeedTextElement $rating
304. * The {@link http://www.w3.org/PICS/ PICS} rating for the channel. Equivalents:
305. * ATOM-none,
306. * RSS1-none,
307. * RSS2-rating (optional, not recommended, single).
308. * @property ezcFeedSkipDaysElement $skipDays
309. * A hint for aggregators telling them which days they can skip when
310. * reading the feed. Equivalents:
311. * ATOM-none,
312. * RSS1-none,
313. * RSS2-skipDays (optional, not recommended, single).
314. * @property ezcFeedSkipHoursElement $skipHours
315. * A hint for aggregators telling them which hours they can skip when
316. * reading the feed. Equivalents:
317. * ATOM-none,
318. * RSS1-none,
319. * RSS2-skipHours (optional, not recommended, single).
320. * @property ezcFeedTextInputElement $textInput
321. * Specifies a text input box that can be displayed with the feed. Equivalents:
322. * ATOM-none,
323. * RSS1-textinput (optional, not recommended, single),
324. * RSS2-textInput (optional, not recommended, single).
325. * @property ezcFeedTextElement $title
326. * Human readable title for the feed. For example, it can be the same
327. * as the website title. Equivalents:
328. * ATOM-title (required, single),
329. * RSS1-title (required, single),
330. * RSS2-title (required, single).
331. * @property ezcFeedTextElement $ttl
332. * Number of minutes that indicates how long a channel can be cached
333. * before refreshing from the source. Equivalents:
334. * ATOM-none,
335. * RSS1-none,
336. * RSS2-ttl (optional, not recommended, single).
337. * @property ezcFeedDateElement $updated
338. * The last time the feed was updated. Equivalents:
339. * ATOM-updated (required, single),
340. * RSS1-none,
341. * RSS2-lastBuildDate (optional, recommended, single).
342. * @property ezcFeedPersonElement $webMaster
343. * The email address of the webmaster responsible for the feed. Equivalents:
344. * ATOM-none,
345. * RSS1-none,
346. * RSS2-webMaster (optional, not recommended, single).
347. *
348. * @todo parse() and parseContent() should(?) handle common broken XML files
349. * (for example if the first line is not <?xml version="1.0"?>)
350. *
351. * @package Feed
352. * @version //autogentag//
353. * @mainclass
354. */
355. class ezcFeed
356. {
357. /**
358. * The version of the feed generator, to be included in the generated feeds.
359. */
360. const GENERATOR_VERSION = '//autogentag//';
362. /**
363. * The uri of the feed generator, to be included in the generated feeds.
364. */
365. const GENERATOR_URI = 'http://ezcomponents.org/docs/tutorials/Feed';
367. /**
368. * Holds a list of all supported feed types.
369. *
370. * @var array(string=>string)
371. */
372. private static $supportedFeedTypes = array();
374. /**
375. * Holds a list of all supported modules.
376. *
377. * @var array(string=>string)
378. */
379. private static $supportedModules = array();
381. /**
382. * Holds a list of all supported modules prefixes.
383. *
384. * @var array(string=>string)
385. */
386. private static $supportedModulesPrefixes = array();
388. /**
389. * Holds the feed type (eg. 'rss1').
390. *
391. * @var string
392. */
393. private $feedType;
395. /**
396. * Holds the feed content type (eg. 'application/rss+xml').
397. *
398. * @var string
399. */
400. private $contentType;
402. /**
403. * Holds the feed elements (ezcFeedElement).
404. *
405. * @var array(string=>mixed)
406. */
407. private $elements;
409. /**
410. * Holds the modules used by this feed.
411. *
412. * @var array(ezcFeedModule)
413. */
414. private $modules = array();
416. /**
417. * Creates a new feed object.
418. *
419. * The $type value is used when calling generate() without specifying a
420. * feed type to output.
421. *
422. * @throws ezcFeedUnsupportedTypeException
423. * if the feed type $type is not supported
424. *
425. * @param string $type The type of feed to create
426. */
427. public function __construct ( $type = null )
428. {
429. self::initSupportedTypes();
431. if ( $type !== null )
432. {
433. $type = strtolower ( $type );
435. if ( !isset( self::$supportedFeedTypes[$type] ) )
436. {
437. throw new ezcFeedUnsupportedTypeException( $type );
438. }
440. $this->feedType = $type;
441. $className = self::$supportedFeedTypes[$type];
442. $this->contentType = constant( "{$className}::CONTENT_TYPE" );
443. }
445. // set default values
446. $version = ( ezcFeed ::GENERATOR_VERSION === '//auto' . 'gentag//' ) ? 'dev' : ezcFeed ::GENERATOR_VERSION;
448. $generator = $this-> add ( 'generator' );
449. $generator->name = 'eZ Components Feed';
450. $generator->version = $version;
451. $generator->url = ezcFeed ::GENERATOR_URI;
452. }
454. /**
455. * Sets the property $name to $value.
456. *
457. * @param string $name The property name
458. * @param mixed $value The property value
459. * @ignore
460. */
461. public function __set( $name, $value )
462. {
463. switch ( $name )
464. {
465. case 'author':
466. case 'contributor':
467. case 'webMaster':
468. $element = $this-> add ( $name );
469. $element->name = $value;
470. break;
472. case 'title':
473. case 'description':
474. case 'docs':
475. case 'ttl':
476. case 'rating':
477. case 'language':
478. case 'copyright':
479. $element = $this-> add ( $name );
480. $element->text = $value;
481. break;
483. case 'generator':
484. $element = $this-> add ( $name );
485. $element->name = $value;
486. break;
488. case 'item':
489. $element = $this-> add ( $name );
490. break;
492. case 'published':
493. case 'updated':
494. $element = $this-> add ( $name );
495. $element->date = $value;
496. break;
498. case 'textInput':
499. $element = $this-> add ( $name );
500. break;
502. case 'skipDays':
503. $element = $this-> add ( $name );
504. break;
506. case 'skipHours':
507. $element = $this-> add ( $name );
508. break;
510. case 'link':
511. $element = $this-> add ( $name );
512. $element->href = $value;
513. break;
515. case 'generator':
516. $element = $this-> add ( $name );
517. $element->name = $value;
518. break;
520. case 'image':
521. case 'icon':
522. $element = $this-> add ( $name );
523. $element->link = $value;
524. break;
526. case 'id':
527. $element = $this-> add ( $name );
528. $element->id = $value;
529. break;
531. default:
532. $supportedModules = ezcFeed :: getSupportedModules ();
533. if ( isset( $supportedModules[$name] ) )
534. {
535. $this-> setModule ( $name, $value );
536. return;
537. }
538. }
539. }
541. /**
542. * Returns the value of property $name.
543. *
544. * @throws ezcBasePropertyNotFoundException
545. * If the property $name does not exist.
546. *
547. * @param string $name The property name
548. * @return mixed
549. * @ignore
550. */
551. public function __get( $name )
552. {
553. switch ( $name )
554. {
555. case 'author':
556. case 'category':
557. case 'cloud':
558. case 'contributor':
559. case 'copyright':
560. case 'description':
561. case 'docs':
562. case 'generator':
563. case 'icon':
564. case 'id':
565. case 'image':
566. case 'item':
567. case 'language':
568. case 'link':
569. case 'published':
570. case 'rating':
571. case 'skipDays':
572. case 'skipHours':
573. case 'textInput':
574. case 'title':
575. case 'ttl':
576. case 'updated':
577. case 'webMaster':
578. if ( isset( $this->elements[$name] ) )
579. {
580. return $this->elements[$name];
581. }
582. break;
584. default:
585. $supportedModules = ezcFeed :: getSupportedModules ();
586. if ( isset( $supportedModules[$name] ) )
587. {
588. if ( $this-> hasModule ( $name ) )
589. {
590. return $this-> getModule ( $name );
591. }
592. else
593. {
594. throw new ezcFeedUndefinedModuleException ( $name );
595. }
596. }
598. throw new ezcFeedUnsupportedModuleException ( $name );
599. }
600. }
602. /**
603. * Returns if the property $name is set.
604. *
605. * @param string $name The property name
606. * @return bool
607. * @ignore
608. */
609. public function __isset( $name )
610. {
611. switch ( $name )
612. {
613. case 'author':
614. case 'category':
615. case 'cloud':
616. case 'contributor':
617. case 'copyright':
618. case 'description':
619. case 'docs':
620. case 'generator':
621. case 'icon':
622. case 'id':
623. case 'image':
624. case 'item':
625. case 'language':
626. case 'link':
627. case 'published':
628. case 'rating':
629. case 'skipDays':
630. case 'skipHours':
631. case 'textInput':
632. case 'title':
633. case 'ttl':
634. case 'updated':
635. case 'webMaster':
636. return isset( $this->elements[$name] );
638. default:
639. $supportedModules = ezcFeed :: getSupportedModules ();
640. if ( isset( $supportedModules[$name] ) )
641. {
642. return $this-> hasModule ( $name );
643. }
644. }
646. return false;
647. }
649. /**
650. * Adds a new module to this item and returns it.
651. *
652. * @param string $name The name of the module to add
653. * @return ezcFeedModule
654. */
655. public function addModule ( $name )
656. {
657. $this->$name = ezcFeedModule :: create ( $name, 'feed' );
658. return $this->$name;
659. }
661. /**
662. * Associates the module $module with the name $name.
663. *
664. * @param string $name The name of the module associate
665. * @param ezcFeedModule $module The module to set under the name $name
666. */
667. public function setModule ( $name, ezcFeedModule $module )
668. {
669. $this->modules[$name] = $module;
670. }
672. /**
673. * Returns the loaded module $name.
674. *
675. * @param string $name The name of the module to return
676. * @return ezcFeedModule
677. */
678. public function getModule ( $name )
679. {
680. return $this->modules[$name];
681. }
683. /**
684. * Returns true if the module $name is loaded, false otherwise.
685. *
686. * @param string $name The name of the module to check if loaded for this item
687. * @return bool
688. */
689. public function hasModule ( $name )
690. {
691. return isset( $this->modules[$name] );
692. }
694. /**
695. * Returns an array with all the modules loaded at feed-level.
696. *
697. * @return array(ezcFeedModule)
698. */
699. public function getModules ()
700. {
701. return $this->modules;
702. }
704. /**
705. * Adds a new ezcFeedElement element with name $name and returns it.
706. *
707. * @throws ezcFeedUnsupportedElementException
708. * if the element $name is not supported
709. *
710. * @param string $name The element name
711. * @return ezcFeedElement|null
712. */
713. public function add ( $name )
714. {
715. switch ( $name )
716. {
717. case 'item':
718. $element = new ezcFeedEntryElement ();
719. $this->elements[$name][] = $element;
720. break;
722. case 'author':
723. case 'contributor':
724. case 'webMaster':
725. $element = new ezcFeedPersonElement ();
726. $this->elements[$name][] = $element;
727. break;
729. case 'image':
730. case 'icon':
731. $element = new ezcFeedImageElement ();
732. $this->elements[$name] = $element;
733. break;
735. case 'category':
736. $element = new ezcFeedCategoryElement ();
737. $this->elements[$name][] = $element;
738. break;
740. case 'textInput':
741. $element = new ezcFeedTextInputElement ();
742. $this->elements[$name] = $element;
743. break;
745. case 'title':
746. case 'description':
747. case 'copyright':
748. case 'rating':
749. case 'comments':
750. case 'ttl':
751. case 'language':
752. case 'docs':
753. $element = new ezcFeedTextElement ();
754. $this->elements[$name] = $element;
755. break;
757. case 'skipDays':
758. $element = new ezcFeedSkipDaysElement ();
759. $this->elements[$name] = $element;
760. break;
762. case 'skipHours':
763. $element = new ezcFeedSkipHoursElement ();
764. $this->elements[$name] = $element;
765. break;
767. case 'link':
768. $element = new ezcFeedLinkElement ();
769. $this->elements[$name][] = $element;
770. break;
772. case 'generator':
773. $element = new ezcFeedGeneratorElement ();
774. $this->elements[$name] = $element;
775. break;
777. case 'cloud':
778. $element = new ezcFeedCloudElement ();
779. $this->elements[$name] = $element;
780. break;
782. case 'id':
783. $element = new ezcFeedIdElement ();
784. $this->elements[$name] = $element;
785. break;
787. case 'updated':
788. case 'published':
789. $element = new ezcFeedDateElement ();
790. $this->elements[$name] = $element;
791. break;
793. default:
794. throw new ezcFeedUnsupportedElementException ( $name );
795. }
797. return $element;
798. }
800. /**
801. * Generates and returns an XML document of type $type from the current
802. * object.
803. *
804. * If the type was defined when creating the ezcFeed object, then that
805. * type will be used if no type is specified when calling generate().
806. *
807. * If no type was specified when calling the constructor and no type
808. * was specified when calling generate then an exception will be thrown.
809. *
810. * @throws ezcFeedUnsupportedTypeException
811. * if the feed type $type is not supported
812. *
813. * @param string $type The feed type to generate
814. * @return string
815. */
816. public function generate ( $type = null )
817. {
818. if ( $this->feedType === null
819. && $type === null )
820. {
821. throw new ezcFeedUnsupportedTypeException ( null );
822. }
824. if ( $type !== null )
825. {
826. $type = strtolower ( $type );
828. if ( !isset( self::$supportedFeedTypes[$type] ) )
829. {
830. throw new ezcFeedUnsupportedTypeException( $type );
831. }
832. }
834. if ( $type !== null )
835. {
836. $this->feedType = $type;
837. }
839. $className = self::$supportedFeedTypes[$this->feedType];
840. $generator = new $className( $this );
842. $this->contentType = constant( "{$className}::CONTENT_TYPE" );
843. return $generator->generate();
844. }
846. /**
847. * Parses the XML document in the $uri and returns an ezcFeed object with
848. * the type autodetected from the XML document.
849. *
850. * Example of parsing an XML document stored at an URL:
851. * <code>
852. * $feed = ezcFeed::parse( 'http://www.example.com/rss2.xml' );
853. * </code>
854. *
855. * Example of parsing an XML document protected with HTTP authentication:
856. * <code>
857. * $feed = ezcFeed::parse( 'http://username:password@www.example.com/rss2.xml' );
858. * </code>
859. *
860. * If trying to parse an XML document protected with HTTP authentication
861. * without providing a valid username and password, the exception
862. * {@link ezcFeedParseErrorException} will be thrown.
863. *
864. * Example of parsing an XML document stored in a local file:
865. * <code>
866. * $feed = ezcFeed::parse( '/tmp/feed.xml' );
867. * </code>
868. *
869. * @throws ezcBaseFileNotFoundException
870. * If the XML file at $uri could not be found.
871. * @throws ezcFeedParseErrorException
872. * If the content at $uri is not a valid XML document.
873. *
874. * @param string $uri An URI which stores an XML document
875. * @return ezcFeed
876. */
877. public static function parse ( $uri )
878. {
879. if ( ! file_exists ( $uri ) )
880. {
881. // hide the notices caused by getaddrinfo (php_network_getaddresses)
882. // in case of unreachable hosts ("Name or service not known")
883. $headers = @ get_headers ( $uri );
884. // HTTP headers
885. // 200 = OK
886. // 301 = moved permanently
887. // 302 = found
888. // 307 = temporary redirect
889. if ( preg_match ( "@200|301|302|307@", $headers[0] ) === 0 )
890. {
891. throw new ezcBaseFileNotFoundException ( $uri );
892. }
893. }
895. $xml = new DOMDocument();
896. $oldSetting = libxml_use_internal_errors ( true );
897. $retval = $xml->load( $uri );
898. libxml_use_internal_errors ( $oldSetting );
900. if ( $retval === false )
901. {
902. libxml_clear_errors ();
903. throw new ezcFeedParseErrorException ( $uri, "It is not a valid XML file" );
904. }
906. return self::dispatchXml( $xml );
907. }
909. /**
910. * Parses the XML document stored in $content and returns an ezcFeed object
911. * with the type autodetected from the XML document.
912. *
913. * Example of parsing an XML document stored in a string:
914. * <code>
915. * // $xmlString contains a valid XML string
916. * $feed = ezcFeed::parseContent( $xmlString );
917. * </code>
918. *
919. * @throws ezcFeedParseErrorException
920. * If $content is not a valid XML document.
921. *
922. * @param string $content A string variable which stores an XML document
923. * @return ezcFeed
924. */
925. public static function parseContent ( $content )
926. {
927. if ( empty( $content ) )
928. {
929. throw new ezcFeedParseErrorException ( null, "Content is empty" );
930. }
932. $xml = new DOMDocument();
933. $oldSetting = libxml_use_internal_errors ( true );
934. $retval = $xml->loadXML( $content );
935. libxml_use_internal_errors ( $oldSetting );
937. if ( $retval === false )
938. {
939. libxml_clear_errors ();
940. throw new ezcFeedParseErrorException ( null, "Content is no valid XML" );
941. }
943. return self::dispatchXml( $xml );
944. }
946. /**
947. * Returns the supported feed types.
948. *
949. * The array returned is (default):
950. * <code>
951. * array(
952. * 'rss1' => 'ezcFeedRss1',
953. * 'rss2' => 'ezcFeedRss2',
954. * 'atom' => 'ezcFeedAtom'
955. * );
956. * </code>
957. *
958. * If the function {@link registerFeed} was used to add another supported feed
959. * type to ezcFeed, it will show up in the returned array as well.
960. *
961. * @return array(string)
962. */
963. public static function getSupportedTypes ()
964. {
965. return self::$supportedFeedTypes;
966. }
968. /**
969. * Returns the supported feed modules.
970. *
971. * The array returned is (default):
972. * <code>
973. * array(
974. * 'Content' => 'ezcFeedContentModule',
975. * 'CreativeCommons' => 'ezcFeedCreativeCommonsModule',
976. * 'DublinCore' => 'ezcFeedDublinCoreModule',
977. * 'Geo' => 'ezcFeedGeoModule',
978. * 'GeoRss' => 'ezcFeedGeoRssModule',
979. * 'iTunes' => 'ezcFeedITunesModule'
980. * );
981. * </code>
982. *
983. * If the function {@link registerModule} was used to add another supported
984. * module type to ezcFeed, it will show up in the returned array as well.
985. *
986. * @return array(string=>string)
987. */
988. public static function getSupportedModules ()
989. {
990. return self::$supportedModules;
991. }
993. /**
994. * Returns the supported feed modules prefixes.
995. *
996. * The array returned is (default):
997. * <code>
998. * array(
999. * 'content' => 'Content',
1000. * 'creativeCommons' => 'CreativeCommons',
1001. * 'dc' => 'DublinCore',
1002. * 'geo' => 'Geo',
1003. * 'georss' => 'GeoRss',
1004. * 'itunes' => 'iTunes'
1005. * );
1006. * </code>
1007. *
1008. * If the function {@link registerModule} was used to add another supported
1009. * module type to ezcFeed, it will show up in the returned array as well.
1010. *
1011. * @return array(string=>string)
1012. */
1013. public static function getSupportedModulesPrefixes ()
1014. {
1015. return self::$supportedModulesPrefixes;
1016. }
1018. /**
1019. * Returns the feed type of this feed object (eg. 'rss2').
1020. *
1021. * @return string
1022. */
1023. public function getFeedType ()
1024. {
1025. return $this->feedType;
1026. }
1028. /**
1029. * Returns the feed content type of this feed object
1030. * (eg. 'application/rss+xml').
1031. *
1032. * @return string
1033. */
1034. public function getContentType ()
1035. {
1036. return $this->contentType;
1037. }
1039. /**
1040. * Adds the feed type $name to the supported list of feed types.
1041. *
1042. * After registering a feed type, it can be used to create or parse feed
1043. * documents.
1044. *
1045. * Example of creating a feed with a user-defined type:
1046. * <code>
1047. * ezcFeed::registerFeed( 'opml', 'myOpmlHandler');
1048. *
1049. * $feed = new ezcFeed( 'opml' );
1050. * // add properties for the Opml feed type to $feed
1051. * </code>
1052. *
1053. * In the above example, myOpmlHandler extends {@link ezcFeedProcessor}
1054. * and implements {@link ezcFeedParser}.
1055. *
1056. * @param string $name The feed type (eg. 'opml' )
1057. * @param string $class The handler class for this feed type (eg. 'myOpmlHandler')
1058. */
1059. public static function registerFeed ( $name, $class )
1060. {
1061. self::$supportedFeedTypes[$name] = $class;
1062. }
1064. /**
1065. * Removes a previously registered feed type from the list of supported
1066. * feed types.
1067. *
1068. * @param string $name The name of the feed type to remove (eg. 'opml')
1069. */
1070. public static function unregisterFeed ( $name )
1071. {
1072. if ( isset( self::$supportedFeedTypes[$name] ) )
1073. {
1074. unset( self::$supportedFeedTypes[$name] );
1075. }
1076. }
1078. /**
1079. * Adds the module $name to the supported list of modules.
1080. *
1081. * After registering a module, it can be used to create or parse feed
1082. * documents.
1083. *
1084. * Example of creating a feed with a user-defined module:
1085. * <code>
1086. * ezcFeed::registerModule( 'Slash', 'mySlashHandler', 'slash');
1087. *
1088. * $feed = new ezcFeed( 'rss2' );
1089. * $item = $feed->add( 'item' );
1090. * $slash = $item->addModule( 'Slash' );
1091. * // add properties for the Slash module to $slash
1092. * </code>
1093. *
1094. * @param string $name The module name (eg. 'Slash' )
1095. * @param string $class The handler class for this module (eg. 'mySlashHandler')
1096. * @param string $namespacePrefix The XML namespace prefix for this module (eg. 'slash')
1097. */
1098. public static function registerModule ( $name, $class, $namespacePrefix )
1099. {
1100. self::$supportedModules[$name] = $class;
1101. self::$supportedModulesPrefixes[$namespacePrefix] = $name;
1102. }
1104. /**
1105. * Removes a previously registered module from the list of supported modules.
1106. *
1107. * @param string $name The name of the module to remove (eg. 'Slash')
1108. */
1109. public static function unregisterModule ( $name )
1110. {
1111. if ( isset( self::$supportedModules[$name] ) )
1112. {
1113. $namePrefix = null;
1114. foreach ( self::$supportedModulesPrefixes as $prefix => $module )
1115. {
1116. if ( $module === $name )
1117. {
1118. $namePrefix = $prefix;
1119. break;
1120. }
1121. }
1122. unset( self::$supportedModulesPrefixes[$prefix] );
1123. unset( self::$supportedModules[$name] );
1124. }
1125. }
1127. /**
1128. * Parses the $xml object by dispatching it to the processor that can
1129. * handle it.
1130. *
1131. * @throws ezcFeedParseErrorException
1132. * If the $xml object could not be parsed by any available processor.
1133. *
1134. * @param DOMDocument $xml The XML object to parse
1135. * @return ezcFeed
1136. */
1137. private static function dispatchXml( DOMDocument $xml )
1138. {
1139. if ( count( self::getSupportedTypes() ) === 0 )
1140. {
1141. self::initSupportedTypes();
1142. }
1144. foreach ( self::getSupportedTypes() as $feedType => $feedClass )
1145. {
1146. $canParse = call_user_func ( array( $feedClass, 'canParse' ), $xml );
1147. if ( $canParse === true )
1148. {
1149. $feed = new ezcFeed ( $feedType );
1150. $parser = new $feedClass( $feed );
1151. return $parser->parse( $xml );
1152. }
1153. }
1155. throw new ezcFeedParseErrorException ( $xml->documentURI, 'Feed type not recognized' );
1156. }
1158. /**
1159. * Initializes the supported feed types and modules to the default values.
1160. */
1161. private static function initSupportedTypes()
1162. {
1163. self::registerFeed( 'rss1', 'ezcFeedRss1' );
1164. self::registerFeed( 'rss2', 'ezcFeedRss2' );
1165. self::registerFeed( 'atom', 'ezcFeedAtom' );
1167. self::registerModule( 'Content', 'ezcFeedContentModule', 'content' );
1168. self::registerModule( 'CreativeCommons', 'ezcFeedCreativeCommonsModule', 'creativeCommons' );
1169. self::registerModule( 'DublinCore', 'ezcFeedDublinCoreModule', 'dc' );
1170. self::registerModule( 'Geo', 'ezcFeedGeoModule', 'geo' );
1171. self::registerModule( 'GeoRss', 'ezcFeedGeoRssModule', 'georss' );
1172. self::registerModule( 'iTunes', 'ezcFeedITunesModule', 'itunes' );
1173. }
1174. }
1175. ?>

Documentation generated by phpDocumentor 1.4.3