comparison vendor/zendframework/zend-feed/src/Writer/AbstractFeed.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 7a779792577d
comparison
equal deleted inserted replaced
-1:000000000000 0:4c8ae668cc8c
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\Writer;
11
12 use DateTime;
13 use Zend\Feed\Uri;
14 use Zend\Validator;
15
16 class AbstractFeed
17 {
18 /**
19 * Contains all Feed level date to append in feed output
20 *
21 * @var array
22 */
23 protected $data = [];
24
25 /**
26 * Holds the value "atom" or "rss" depending on the feed type set when
27 * when last exported.
28 *
29 * @var string
30 */
31 protected $type = null;
32
33 /**
34 * @var $extensions
35 */
36 protected $extensions;
37
38 /**
39 * Constructor: Primarily triggers the registration of core extensions and
40 * loads those appropriate to this data container.
41 *
42 */
43 public function __construct()
44 {
45 Writer::registerCoreExtensions();
46 $this->_loadExtensions();
47 }
48
49 /**
50 * Set a single author
51 *
52 * The following option keys are supported:
53 * 'name' => (string) The name
54 * 'email' => (string) An optional email
55 * 'uri' => (string) An optional and valid URI
56 *
57 * @param array $author
58 * @throws Exception\InvalidArgumentException If any value of $author not follow the format.
59 * @return AbstractFeed
60 */
61 public function addAuthor(array $author)
62 {
63 // Check array values
64 if (!array_key_exists('name', $author)
65 || empty($author['name'])
66 || !is_string($author['name'])
67 ) {
68 throw new Exception\InvalidArgumentException(
69 'Invalid parameter: author array must include a "name" key with a non-empty string value');
70 }
71
72 if (isset($author['email'])) {
73 if (empty($author['email']) || !is_string($author['email'])) {
74 throw new Exception\InvalidArgumentException(
75 'Invalid parameter: "email" array value must be a non-empty string');
76 }
77 }
78 if (isset($author['uri'])) {
79 if (empty($author['uri']) || !is_string($author['uri']) ||
80 !Uri::factory($author['uri'])->isValid()
81 ) {
82 throw new Exception\InvalidArgumentException(
83 'Invalid parameter: "uri" array value must be a non-empty string and valid URI/IRI');
84 }
85 }
86
87 $this->data['authors'][] = $author;
88
89 return $this;
90 }
91
92 /**
93 * Set an array with feed authors
94 *
95 * @see addAuthor
96 * @param array $authors
97 * @return AbstractFeed
98 */
99 public function addAuthors(array $authors)
100 {
101 foreach ($authors as $author) {
102 $this->addAuthor($author);
103 }
104
105 return $this;
106 }
107
108 /**
109 * Set the copyright entry
110 *
111 * @param string $copyright
112 * @throws Exception\InvalidArgumentException
113 * @return AbstractFeed
114 */
115 public function setCopyright($copyright)
116 {
117 if (empty($copyright) || !is_string($copyright)) {
118 throw new Exception\InvalidArgumentException('Invalid parameter: parameter must be a non-empty string');
119 }
120 $this->data['copyright'] = $copyright;
121
122 return $this;
123 }
124
125 /**
126 * Set the feed creation date
127 *
128 * @param null|int|DateTime
129 * @throws Exception\InvalidArgumentException
130 * @return AbstractFeed
131 */
132 public function setDateCreated($date = null)
133 {
134 if ($date === null) {
135 $date = new DateTime();
136 } elseif (is_int($date)) {
137 $date = new DateTime('@' . $date);
138 } elseif (!$date instanceof DateTime) {
139 throw new Exception\InvalidArgumentException('Invalid DateTime object or UNIX Timestamp'
140 . ' passed as parameter');
141 }
142 $this->data['dateCreated'] = $date;
143
144 return $this;
145 }
146
147 /**
148 * Set the feed modification date
149 *
150 * @param null|int|DateTime
151 * @throws Exception\InvalidArgumentException
152 * @return AbstractFeed
153 */
154 public function setDateModified($date = null)
155 {
156 if ($date === null) {
157 $date = new DateTime();
158 } elseif (is_int($date)) {
159 $date = new DateTime('@' . $date);
160 } elseif (!$date instanceof DateTime) {
161 throw new Exception\InvalidArgumentException('Invalid DateTime object or UNIX Timestamp'
162 . ' passed as parameter');
163 }
164 $this->data['dateModified'] = $date;
165
166 return $this;
167 }
168
169 /**
170 * Set the feed last-build date. Ignored for Atom 1.0.
171 *
172 * @param null|int|DateTime
173 * @throws Exception\InvalidArgumentException
174 * @return AbstractFeed
175 */
176 public function setLastBuildDate($date = null)
177 {
178 if ($date === null) {
179 $date = new DateTime();
180 } elseif (is_int($date)) {
181 $date = new DateTime('@' . $date);
182 } elseif (!$date instanceof DateTime) {
183 throw new Exception\InvalidArgumentException('Invalid DateTime object or UNIX Timestamp'
184 . ' passed as parameter');
185 }
186 $this->data['lastBuildDate'] = $date;
187
188 return $this;
189 }
190
191 /**
192 * Set the feed description
193 *
194 * @param string $description
195 * @throws Exception\InvalidArgumentException
196 * @return AbstractFeed
197 */
198 public function setDescription($description)
199 {
200 if (empty($description) || !is_string($description)) {
201 throw new Exception\InvalidArgumentException('Invalid parameter: parameter must be a non-empty string');
202 }
203 $this->data['description'] = $description;
204
205 return $this;
206 }
207
208 /**
209 * Set the feed generator entry
210 *
211 * @param array|string $name
212 * @param null|string $version
213 * @param null|string $uri
214 * @throws Exception\InvalidArgumentException
215 * @return AbstractFeed
216 */
217 public function setGenerator($name, $version = null, $uri = null)
218 {
219 if (is_array($name)) {
220 $data = $name;
221 if (empty($data['name']) || !is_string($data['name'])) {
222 throw new Exception\InvalidArgumentException('Invalid parameter: "name" must be a non-empty string');
223 }
224 $generator = ['name' => $data['name']];
225 if (isset($data['version'])) {
226 if (empty($data['version']) || !is_string($data['version'])) {
227 throw new Exception\InvalidArgumentException('Invalid parameter: "version" must be a non-empty string');
228 }
229 $generator['version'] = $data['version'];
230 }
231 if (isset($data['uri'])) {
232 if (empty($data['uri']) || !is_string($data['uri']) || !Uri::factory($data['uri'])->isValid()) {
233 throw new Exception\InvalidArgumentException('Invalid parameter: "uri" must be a non-empty string and a valid URI/IRI');
234 }
235 $generator['uri'] = $data['uri'];
236 }
237 } else {
238 if (empty($name) || !is_string($name)) {
239 throw new Exception\InvalidArgumentException('Invalid parameter: "name" must be a non-empty string');
240 }
241 $generator = ['name' => $name];
242 if (isset($version)) {
243 if (empty($version) || !is_string($version)) {
244 throw new Exception\InvalidArgumentException('Invalid parameter: "version" must be a non-empty string');
245 }
246 $generator['version'] = $version;
247 }
248 if (isset($uri)) {
249 if (empty($uri) || !is_string($uri) || !Uri::factory($uri)->isValid()) {
250 throw new Exception\InvalidArgumentException('Invalid parameter: "uri" must be a non-empty string and a valid URI/IRI');
251 }
252 $generator['uri'] = $uri;
253 }
254 }
255 $this->data['generator'] = $generator;
256
257 return $this;
258 }
259
260 /**
261 * Set the feed ID - URI or URN (via PCRE pattern) supported
262 *
263 * @param string $id
264 * @throws Exception\InvalidArgumentException
265 * @return AbstractFeed
266 */
267 public function setId($id)
268 {
269 if ((empty($id) || !is_string($id) || !Uri::factory($id)->isValid())
270 && !preg_match("#^urn:[a-zA-Z0-9][a-zA-Z0-9\-]{1,31}:([a-zA-Z0-9\(\)\+\,\.\:\=\@\;\$\_\!\*\-]|%[0-9a-fA-F]{2})*#", $id)
271 && !$this->_validateTagUri($id)
272 ) {
273 throw new Exception\InvalidArgumentException('Invalid parameter: parameter must be a non-empty string and valid URI/IRI');
274 }
275 $this->data['id'] = $id;
276
277 return $this;
278 }
279
280 /**
281 * Validate a URI using the tag scheme (RFC 4151)
282 *
283 * @param string $id
284 * @return bool
285 */
286 protected function _validateTagUri($id)
287 {
288 if (preg_match('/^tag:(?P<name>.*),(?P<date>\d{4}-?\d{0,2}-?\d{0,2}):(?P<specific>.*)(.*:)*$/', $id, $matches)) {
289 $dvalid = false;
290 $date = $matches['date'];
291 $d6 = strtotime($date);
292 if ((strlen($date) == 4) && $date <= date('Y')) {
293 $dvalid = true;
294 } elseif ((strlen($date) == 7) && ($d6 < strtotime("now"))) {
295 $dvalid = true;
296 } elseif ((strlen($date) == 10) && ($d6 < strtotime("now"))) {
297 $dvalid = true;
298 }
299 $validator = new Validator\EmailAddress;
300 if ($validator->isValid($matches['name'])) {
301 $nvalid = true;
302 } else {
303 $nvalid = $validator->isValid('info@' . $matches['name']);
304 }
305 return $dvalid && $nvalid;
306 }
307 return false;
308 }
309
310 /**
311 * Set a feed image (URI at minimum). Parameter is a single array with the
312 * required key 'uri'. When rendering as RSS, the required keys are 'uri',
313 * 'title' and 'link'. RSS also specifies three optional parameters 'width',
314 * 'height' and 'description'. Only 'uri' is required and used for Atom rendering.
315 *
316 * @param array $data
317 * @throws Exception\InvalidArgumentException
318 * @return AbstractFeed
319 */
320 public function setImage(array $data)
321 {
322 if (empty($data['uri']) || !is_string($data['uri'])
323 || !Uri::factory($data['uri'])->isValid()
324 ) {
325 throw new Exception\InvalidArgumentException('Invalid parameter: parameter \'uri\''
326 . ' must be a non-empty string and valid URI/IRI');
327 }
328 $this->data['image'] = $data;
329
330 return $this;
331 }
332
333 /**
334 * Set the feed language
335 *
336 * @param string $language
337 * @throws Exception\InvalidArgumentException
338 * @return AbstractFeed
339 */
340 public function setLanguage($language)
341 {
342 if (empty($language) || !is_string($language)) {
343 throw new Exception\InvalidArgumentException('Invalid parameter: parameter must be a non-empty string');
344 }
345 $this->data['language'] = $language;
346
347 return $this;
348 }
349
350 /**
351 * Set a link to the HTML source
352 *
353 * @param string $link
354 * @throws Exception\InvalidArgumentException
355 * @return AbstractFeed
356 */
357 public function setLink($link)
358 {
359 if (empty($link) || !is_string($link) || !Uri::factory($link)->isValid()) {
360 throw new Exception\InvalidArgumentException('Invalid parameter: parameter must be a non-empty string and valid URI/IRI');
361 }
362 $this->data['link'] = $link;
363
364 return $this;
365 }
366
367 /**
368 * Set a link to an XML feed for any feed type/version
369 *
370 * @param string $link
371 * @param string $type
372 * @throws Exception\InvalidArgumentException
373 * @return AbstractFeed
374 */
375 public function setFeedLink($link, $type)
376 {
377 if (empty($link) || !is_string($link) || !Uri::factory($link)->isValid()) {
378 throw new Exception\InvalidArgumentException('Invalid parameter: "link"" must be a non-empty string and valid URI/IRI');
379 }
380 if (!in_array(strtolower($type), ['rss', 'rdf', 'atom'])) {
381 throw new Exception\InvalidArgumentException('Invalid parameter: "type"; You must declare the type of feed the link points to, i.e. RSS, RDF or Atom');
382 }
383 $this->data['feedLinks'][strtolower($type)] = $link;
384
385 return $this;
386 }
387
388 /**
389 * Set the feed title
390 *
391 * @param string $title
392 * @throws Exception\InvalidArgumentException
393 * @return AbstractFeed
394 */
395 public function setTitle($title)
396 {
397 if (empty($title) || !is_string($title)) {
398 throw new Exception\InvalidArgumentException('Invalid parameter: parameter must be a non-empty string');
399 }
400 $this->data['title'] = $title;
401
402 return $this;
403 }
404
405 /**
406 * Set the feed character encoding
407 *
408 * @param string $encoding
409 * @throws Exception\InvalidArgumentException
410 * @return AbstractFeed
411 */
412 public function setEncoding($encoding)
413 {
414 if (empty($encoding) || !is_string($encoding)) {
415 throw new Exception\InvalidArgumentException('Invalid parameter: parameter must be a non-empty string');
416 }
417 $this->data['encoding'] = $encoding;
418
419 return $this;
420 }
421
422 /**
423 * Set the feed's base URL
424 *
425 * @param string $url
426 * @throws Exception\InvalidArgumentException
427 * @return AbstractFeed
428 */
429 public function setBaseUrl($url)
430 {
431 if (empty($url) || !is_string($url) || !Uri::factory($url)->isValid()) {
432 throw new Exception\InvalidArgumentException('Invalid parameter: "url" array value'
433 . ' must be a non-empty string and valid URI/IRI');
434 }
435 $this->data['baseUrl'] = $url;
436
437 return $this;
438 }
439
440 /**
441 * Add a Pubsubhubbub hub endpoint URL
442 *
443 * @param string $url
444 * @throws Exception\InvalidArgumentException
445 * @return AbstractFeed
446 */
447 public function addHub($url)
448 {
449 if (empty($url) || !is_string($url) || !Uri::factory($url)->isValid()) {
450 throw new Exception\InvalidArgumentException('Invalid parameter: "url" array value'
451 . ' must be a non-empty string and valid URI/IRI');
452 }
453 if (!isset($this->data['hubs'])) {
454 $this->data['hubs'] = [];
455 }
456 $this->data['hubs'][] = $url;
457
458 return $this;
459 }
460
461 /**
462 * Add Pubsubhubbub hub endpoint URLs
463 *
464 * @param array $urls
465 * @return AbstractFeed
466 */
467 public function addHubs(array $urls)
468 {
469 foreach ($urls as $url) {
470 $this->addHub($url);
471 }
472
473 return $this;
474 }
475
476 /**
477 * Add a feed category
478 *
479 * @param array $category
480 * @throws Exception\InvalidArgumentException
481 * @return AbstractFeed
482 */
483 public function addCategory(array $category)
484 {
485 if (!isset($category['term'])) {
486 throw new Exception\InvalidArgumentException('Each category must be an array and '
487 . 'contain at least a "term" element containing the machine '
488 . ' readable category name');
489 }
490 if (isset($category['scheme'])) {
491 if (empty($category['scheme'])
492 || !is_string($category['scheme'])
493 || !Uri::factory($category['scheme'])->isValid()
494 ) {
495 throw new Exception\InvalidArgumentException('The Atom scheme or RSS domain of'
496 . ' a category must be a valid URI');
497 }
498 }
499 if (!isset($this->data['categories'])) {
500 $this->data['categories'] = [];
501 }
502 $this->data['categories'][] = $category;
503
504 return $this;
505 }
506
507 /**
508 * Set an array of feed categories
509 *
510 * @param array $categories
511 * @return AbstractFeed
512 */
513 public function addCategories(array $categories)
514 {
515 foreach ($categories as $category) {
516 $this->addCategory($category);
517 }
518
519 return $this;
520 }
521
522 /**
523 * Get a single author
524 *
525 * @param int $index
526 * @return string|null
527 */
528 public function getAuthor($index = 0)
529 {
530 if (isset($this->data['authors'][$index])) {
531 return $this->data['authors'][$index];
532 }
533
534 return;
535 }
536
537 /**
538 * Get an array with feed authors
539 *
540 * @return array
541 */
542 public function getAuthors()
543 {
544 if (!array_key_exists('authors', $this->data)) {
545 return;
546 }
547 return $this->data['authors'];
548 }
549
550 /**
551 * Get the copyright entry
552 *
553 * @return string|null
554 */
555 public function getCopyright()
556 {
557 if (!array_key_exists('copyright', $this->data)) {
558 return;
559 }
560 return $this->data['copyright'];
561 }
562
563 /**
564 * Get the feed creation date
565 *
566 * @return string|null
567 */
568 public function getDateCreated()
569 {
570 if (!array_key_exists('dateCreated', $this->data)) {
571 return;
572 }
573 return $this->data['dateCreated'];
574 }
575
576 /**
577 * Get the feed modification date
578 *
579 * @return string|null
580 */
581 public function getDateModified()
582 {
583 if (!array_key_exists('dateModified', $this->data)) {
584 return;
585 }
586 return $this->data['dateModified'];
587 }
588
589 /**
590 * Get the feed last-build date
591 *
592 * @return string|null
593 */
594 public function getLastBuildDate()
595 {
596 if (!array_key_exists('lastBuildDate', $this->data)) {
597 return;
598 }
599 return $this->data['lastBuildDate'];
600 }
601
602 /**
603 * Get the feed description
604 *
605 * @return string|null
606 */
607 public function getDescription()
608 {
609 if (!array_key_exists('description', $this->data)) {
610 return;
611 }
612 return $this->data['description'];
613 }
614
615 /**
616 * Get the feed generator entry
617 *
618 * @return string|null
619 */
620 public function getGenerator()
621 {
622 if (!array_key_exists('generator', $this->data)) {
623 return;
624 }
625 return $this->data['generator'];
626 }
627
628 /**
629 * Get the feed ID
630 *
631 * @return string|null
632 */
633 public function getId()
634 {
635 if (!array_key_exists('id', $this->data)) {
636 return;
637 }
638 return $this->data['id'];
639 }
640
641 /**
642 * Get the feed image URI
643 *
644 * @return array
645 */
646 public function getImage()
647 {
648 if (!array_key_exists('image', $this->data)) {
649 return;
650 }
651 return $this->data['image'];
652 }
653
654 /**
655 * Get the feed language
656 *
657 * @return string|null
658 */
659 public function getLanguage()
660 {
661 if (!array_key_exists('language', $this->data)) {
662 return;
663 }
664 return $this->data['language'];
665 }
666
667 /**
668 * Get a link to the HTML source
669 *
670 * @return string|null
671 */
672 public function getLink()
673 {
674 if (!array_key_exists('link', $this->data)) {
675 return;
676 }
677 return $this->data['link'];
678 }
679
680 /**
681 * Get a link to the XML feed
682 *
683 * @return string|null
684 */
685 public function getFeedLinks()
686 {
687 if (!array_key_exists('feedLinks', $this->data)) {
688 return;
689 }
690 return $this->data['feedLinks'];
691 }
692
693 /**
694 * Get the feed title
695 *
696 * @return string|null
697 */
698 public function getTitle()
699 {
700 if (!array_key_exists('title', $this->data)) {
701 return;
702 }
703 return $this->data['title'];
704 }
705
706 /**
707 * Get the feed character encoding
708 *
709 * @return string|null
710 */
711 public function getEncoding()
712 {
713 if (!array_key_exists('encoding', $this->data)) {
714 return 'UTF-8';
715 }
716 return $this->data['encoding'];
717 }
718
719 /**
720 * Get the feed's base url
721 *
722 * @return string|null
723 */
724 public function getBaseUrl()
725 {
726 if (!array_key_exists('baseUrl', $this->data)) {
727 return;
728 }
729 return $this->data['baseUrl'];
730 }
731
732 /**
733 * Get the URLs used as Pubsubhubbub hubs endpoints
734 *
735 * @return string|null
736 */
737 public function getHubs()
738 {
739 if (!array_key_exists('hubs', $this->data)) {
740 return;
741 }
742 return $this->data['hubs'];
743 }
744
745 /**
746 * Get the feed categories
747 *
748 * @return string|null
749 */
750 public function getCategories()
751 {
752 if (!array_key_exists('categories', $this->data)) {
753 return;
754 }
755 return $this->data['categories'];
756 }
757
758 /**
759 * Resets the instance and deletes all data
760 *
761 * @return void
762 */
763 public function reset()
764 {
765 $this->data = [];
766 }
767
768 /**
769 * Set the current feed type being exported to "rss" or "atom". This allows
770 * other objects to gracefully choose whether to execute or not, depending
771 * on their appropriateness for the current type, e.g. renderers.
772 *
773 * @param string $type
774 * @return AbstractFeed
775 */
776 public function setType($type)
777 {
778 $this->type = $type;
779 return $this;
780 }
781
782 /**
783 * Retrieve the current or last feed type exported.
784 *
785 * @return string Value will be "rss" or "atom"
786 */
787 public function getType()
788 {
789 return $this->type;
790 }
791
792 /**
793 * Unset a specific data point
794 *
795 * @param string $name
796 * @return AbstractFeed
797 */
798 public function remove($name)
799 {
800 if (isset($this->data[$name])) {
801 unset($this->data[$name]);
802 }
803 return $this;
804 }
805
806 /**
807 * Method overloading: call given method on first extension implementing it
808 *
809 * @param string $method
810 * @param array $args
811 * @return mixed
812 * @throws Exception\BadMethodCallException if no extensions implements the method
813 */
814 public function __call($method, $args)
815 {
816 foreach ($this->extensions as $extension) {
817 try {
818 return call_user_func_array([$extension, $method], $args);
819 } catch (Exception\BadMethodCallException $e) {
820 }
821 }
822 throw new Exception\BadMethodCallException(
823 'Method: ' . $method . ' does not exist and could not be located on a registered Extension'
824 );
825 }
826
827 /**
828 * Load extensions from Zend\Feed\Writer\Writer
829 *
830 * @throws Exception\RuntimeException
831 * @return void
832 */
833 protected function _loadExtensions()
834 {
835 $all = Writer::getExtensions();
836 $manager = Writer::getExtensionManager();
837 $exts = $all['feed'];
838 foreach ($exts as $ext) {
839 if (!$manager->has($ext)) {
840 throw new Exception\RuntimeException(sprintf('Unable to load extension "%s"; could not resolve to class', $ext));
841 }
842 $this->extensions[$ext] = $manager->get($ext);
843 $this->extensions[$ext]->setEncoding($this->getEncoding());
844 }
845 }
846 }