94d914b09f8087e4a8050a9008f8a97d6baede55
[yaffs-website] / vendor / zendframework / zend-feed / src / Reader / Extension / Atom / Feed.php
1 <?php
2 /**
3  * Zend Framework (http://framework.zend.com/)
4  *
5  * @link      http://github.com/zendframework/zf2 for the canonical source repository
6  * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7  * @license   http://framework.zend.com/license/new-bsd New BSD License
8  */
9
10 namespace Zend\Feed\Reader\Extension\Atom;
11
12 use DateTime;
13 use DOMElement;
14 use Zend\Feed\Reader;
15 use Zend\Feed\Reader\Collection;
16 use Zend\Feed\Reader\Extension;
17 use Zend\Feed\Uri;
18
19 class Feed extends Extension\AbstractFeed
20 {
21     /**
22      * Get a single author
23      *
24      * @param  int $index
25      * @return string|null
26      */
27     public function getAuthor($index = 0)
28     {
29         $authors = $this->getAuthors();
30
31         if (isset($authors[$index])) {
32             return $authors[$index];
33         }
34
35         return;
36     }
37
38     /**
39      * Get an array with feed authors
40      *
41      * @return Collection\Author
42      */
43     public function getAuthors()
44     {
45         if (array_key_exists('authors', $this->data)) {
46             return $this->data['authors'];
47         }
48
49         $list = $this->xpath->query('//atom:author');
50
51         $authors = [];
52
53         if ($list->length) {
54             foreach ($list as $author) {
55                 $author = $this->getAuthorFromElement($author);
56                 if (! empty($author)) {
57                     $authors[] = $author;
58                 }
59             }
60         }
61
62         if (count($authors) == 0) {
63             $authors = new Collection\Author();
64         } else {
65             $authors = new Collection\Author(
66                 Reader\Reader::arrayUnique($authors)
67             );
68         }
69
70         $this->data['authors'] = $authors;
71
72         return $this->data['authors'];
73     }
74
75     /**
76      * Get the copyright entry
77      *
78      * @return string|null
79      */
80     public function getCopyright()
81     {
82         if (array_key_exists('copyright', $this->data)) {
83             return $this->data['copyright'];
84         }
85
86         $copyright = null;
87
88         if ($this->getType() === Reader\Reader::TYPE_ATOM_03) {
89             $copyright = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:copyright)');
90         } else {
91             $copyright = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:rights)');
92         }
93
94         if (! $copyright) {
95             $copyright = null;
96         }
97
98         $this->data['copyright'] = $copyright;
99
100         return $this->data['copyright'];
101     }
102
103     /**
104      * Get the feed creation date
105      *
106      * @return DateTime|null
107      */
108     public function getDateCreated()
109     {
110         if (array_key_exists('datecreated', $this->data)) {
111             return $this->data['datecreated'];
112         }
113
114         $date = null;
115
116         if ($this->getType() === Reader\Reader::TYPE_ATOM_03) {
117             $dateCreated = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:created)');
118         } else {
119             $dateCreated = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:published)');
120         }
121
122         if ($dateCreated) {
123             $date = new DateTime($dateCreated);
124         }
125
126         $this->data['datecreated'] = $date;
127
128         return $this->data['datecreated'];
129     }
130
131     /**
132      * Get the feed modification date
133      *
134      * @return DateTime|null
135      */
136     public function getDateModified()
137     {
138         if (array_key_exists('datemodified', $this->data)) {
139             return $this->data['datemodified'];
140         }
141
142         $date = null;
143
144         if ($this->getType() === Reader\Reader::TYPE_ATOM_03) {
145             $dateModified = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:modified)');
146         } else {
147             $dateModified = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:updated)');
148         }
149
150         if ($dateModified) {
151             $date = new DateTime($dateModified);
152         }
153
154         $this->data['datemodified'] = $date;
155
156         return $this->data['datemodified'];
157     }
158
159     /**
160      * Get the feed description
161      *
162      * @return string|null
163      */
164     public function getDescription()
165     {
166         if (array_key_exists('description', $this->data)) {
167             return $this->data['description'];
168         }
169
170         $description = null;
171
172         if ($this->getType() === Reader\Reader::TYPE_ATOM_03) {
173             $description = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:tagline)');
174         } else {
175             $description = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:subtitle)');
176         }
177
178         if (! $description) {
179             $description = null;
180         }
181
182         $this->data['description'] = $description;
183
184         return $this->data['description'];
185     }
186
187     /**
188      * Get the feed generator entry
189      *
190      * @return string|null
191      */
192     public function getGenerator()
193     {
194         if (array_key_exists('generator', $this->data)) {
195             return $this->data['generator'];
196         }
197         // TODO: Add uri support
198         $generator = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:generator)');
199
200         if (! $generator) {
201             $generator = null;
202         }
203
204         $this->data['generator'] = $generator;
205
206         return $this->data['generator'];
207     }
208
209     /**
210      * Get the feed ID
211      *
212      * @return string|null
213      */
214     public function getId()
215     {
216         if (array_key_exists('id', $this->data)) {
217             return $this->data['id'];
218         }
219
220         $id = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:id)');
221
222         if (! $id) {
223             if ($this->getLink()) {
224                 $id = $this->getLink();
225             } elseif ($this->getTitle()) {
226                 $id = $this->getTitle();
227             } else {
228                 $id = null;
229             }
230         }
231
232         $this->data['id'] = $id;
233
234         return $this->data['id'];
235     }
236
237     /**
238      * Get the feed language
239      *
240      * @return string|null
241      */
242     public function getLanguage()
243     {
244         if (array_key_exists('language', $this->data)) {
245             return $this->data['language'];
246         }
247
248         $language = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:lang)');
249
250         if (! $language) {
251             $language = $this->xpath->evaluate('string(//@xml:lang[1])');
252         }
253
254         if (! $language) {
255             $language = null;
256         }
257
258         $this->data['language'] = $language;
259
260         return $this->data['language'];
261     }
262
263     /**
264      * Get the feed image
265      *
266      * @return array|null
267      */
268     public function getImage()
269     {
270         if (array_key_exists('image', $this->data)) {
271             return $this->data['image'];
272         }
273
274         $imageUrl = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:logo)');
275
276         if (! $imageUrl) {
277             $image = null;
278         } else {
279             $image = ['uri' => $imageUrl];
280         }
281
282         $this->data['image'] = $image;
283
284         return $this->data['image'];
285     }
286
287     /**
288      * Get the base URI of the feed (if set).
289      *
290      * @return string|null
291      */
292     public function getBaseUrl()
293     {
294         if (array_key_exists('baseUrl', $this->data)) {
295             return $this->data['baseUrl'];
296         }
297
298         $baseUrl = $this->xpath->evaluate('string(//@xml:base[1])');
299
300         if (! $baseUrl) {
301             $baseUrl = null;
302         }
303         $this->data['baseUrl'] = $baseUrl;
304
305         return $this->data['baseUrl'];
306     }
307
308     /**
309      * Get a link to the source website
310      *
311      * @return string|null
312      */
313     public function getLink()
314     {
315         if (array_key_exists('link', $this->data)) {
316             return $this->data['link'];
317         }
318
319         $link = null;
320
321         $list = $this->xpath->query(
322             $this->getXpathPrefix() . '/atom:link[@rel="alternate"]/@href' . '|' .
323             $this->getXpathPrefix() . '/atom:link[not(@rel)]/@href'
324         );
325
326         if ($list->length) {
327             $link = $list->item(0)->nodeValue;
328             $link = $this->absolutiseUri($link);
329         }
330
331         $this->data['link'] = $link;
332
333         return $this->data['link'];
334     }
335
336     /**
337      * Get a link to the feed's XML Url
338      *
339      * @return string|null
340      */
341     public function getFeedLink()
342     {
343         if (array_key_exists('feedlink', $this->data)) {
344             return $this->data['feedlink'];
345         }
346
347         $link = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:link[@rel="self"]/@href)');
348
349         $link = $this->absolutiseUri($link);
350
351         $this->data['feedlink'] = $link;
352
353         return $this->data['feedlink'];
354     }
355
356     /**
357      * Get an array of any supported Pusubhubbub endpoints
358      *
359      * @return array|null
360      */
361     public function getHubs()
362     {
363         if (array_key_exists('hubs', $this->data)) {
364             return $this->data['hubs'];
365         }
366         $hubs = [];
367
368         $list = $this->xpath->query($this->getXpathPrefix()
369             . '//atom:link[@rel="hub"]/@href');
370
371         if ($list->length) {
372             foreach ($list as $uri) {
373                 $hubs[] = $this->absolutiseUri($uri->nodeValue);
374             }
375         } else {
376             $hubs = null;
377         }
378
379         $this->data['hubs'] = $hubs;
380
381         return $this->data['hubs'];
382     }
383
384     /**
385      * Get the feed title
386      *
387      * @return string|null
388      */
389     public function getTitle()
390     {
391         if (array_key_exists('title', $this->data)) {
392             return $this->data['title'];
393         }
394
395         $title = $this->xpath->evaluate('string(' . $this->getXpathPrefix() . '/atom:title)');
396
397         if (! $title) {
398             $title = null;
399         }
400
401         $this->data['title'] = $title;
402
403         return $this->data['title'];
404     }
405
406     /**
407      * Get all categories
408      *
409      * @return Collection\Category
410      */
411     public function getCategories()
412     {
413         if (array_key_exists('categories', $this->data)) {
414             return $this->data['categories'];
415         }
416
417         if ($this->getType() == Reader\Reader::TYPE_ATOM_10) {
418             $list = $this->xpath->query($this->getXpathPrefix() . '/atom:category');
419         } else {
420             /**
421              * Since Atom 0.3 did not support categories, it would have used the
422              * Dublin Core extension. However there is a small possibility Atom 0.3
423              * may have been retrofittied to use Atom 1.0 instead.
424              */
425             $this->xpath->registerNamespace('atom10', Reader\Reader::NAMESPACE_ATOM_10);
426             $list = $this->xpath->query($this->getXpathPrefix() . '/atom10:category');
427         }
428
429         if ($list->length) {
430             $categoryCollection = new Collection\Category;
431             foreach ($list as $category) {
432                 $categoryCollection[] = [
433                     'term' => $category->getAttribute('term'),
434                     'scheme' => $category->getAttribute('scheme'),
435                     'label' => $category->getAttribute('label')
436                 ];
437             }
438         } else {
439             return new Collection\Category;
440         }
441
442         $this->data['categories'] = $categoryCollection;
443
444         return $this->data['categories'];
445     }
446
447     /**
448      * Get an author entry in RSS format
449      *
450      * @param  DOMElement $element
451      * @return string
452      */
453     protected function getAuthorFromElement(DOMElement $element)
454     {
455         $author = [];
456
457         $emailNode = $element->getElementsByTagName('email');
458         $nameNode  = $element->getElementsByTagName('name');
459         $uriNode   = $element->getElementsByTagName('uri');
460
461         if ($emailNode->length && strlen($emailNode->item(0)->nodeValue) > 0) {
462             $author['email'] = $emailNode->item(0)->nodeValue;
463         }
464
465         if ($nameNode->length && strlen($nameNode->item(0)->nodeValue) > 0) {
466             $author['name'] = $nameNode->item(0)->nodeValue;
467         }
468
469         if ($uriNode->length && strlen($uriNode->item(0)->nodeValue) > 0) {
470             $author['uri'] = $uriNode->item(0)->nodeValue;
471         }
472
473         if (empty($author)) {
474             return;
475         }
476         return $author;
477     }
478
479     /**
480      *  Attempt to absolutise the URI, i.e. if a relative URI apply the
481      *  xml:base value as a prefix to turn into an absolute URI.
482      */
483     protected function absolutiseUri($link)
484     {
485         if (! Uri::factory($link)->isAbsolute()) {
486             if ($this->getBaseUrl() !== null) {
487                 $link = $this->getBaseUrl() . $link;
488                 if (! Uri::factory($link)->isValid()) {
489                     $link = null;
490                 }
491             }
492         }
493         return $link;
494     }
495
496     /**
497      * Register the default namespaces for the current feed format
498      */
499     protected function registerNamespaces()
500     {
501         if ($this->getType() == Reader\Reader::TYPE_ATOM_10
502             || $this->getType() == Reader\Reader::TYPE_ATOM_03
503         ) {
504             return; // pre-registered at Feed level
505         }
506         $atomDetected = $this->getAtomType();
507         switch ($atomDetected) {
508             case Reader\Reader::TYPE_ATOM_03:
509                 $this->xpath->registerNamespace('atom', Reader\Reader::NAMESPACE_ATOM_03);
510                 break;
511             default:
512                 $this->xpath->registerNamespace('atom', Reader\Reader::NAMESPACE_ATOM_10);
513                 break;
514         }
515     }
516
517     /**
518      * Detect the presence of any Atom namespaces in use
519      */
520     protected function getAtomType()
521     {
522         $dom = $this->getDomDocument();
523         $prefixAtom03 = $dom->lookupPrefix(Reader\Reader::NAMESPACE_ATOM_03);
524         $prefixAtom10 = $dom->lookupPrefix(Reader\Reader::NAMESPACE_ATOM_10);
525         if ($dom->isDefaultNamespace(Reader\Reader::NAMESPACE_ATOM_10)
526             || ! empty($prefixAtom10)
527         ) {
528             return Reader\Reader::TYPE_ATOM_10;
529         }
530         if ($dom->isDefaultNamespace(Reader\Reader::NAMESPACE_ATOM_03)
531             || ! empty($prefixAtom03)
532         ) {
533             return Reader\Reader::TYPE_ATOM_03;
534         }
535     }
536 }