1<?php 2 3/** 4 * @see https://github.com/laminas/laminas-feed for the canonical source repository 5 * @copyright https://github.com/laminas/laminas-feed/blob/master/COPYRIGHT.md 6 * @license https://github.com/laminas/laminas-feed/blob/master/LICENSE.md New BSD License 7 */ 8 9namespace Laminas\Feed\Writer\Renderer\Entry; 10 11use DateTime; 12use DOMDocument; 13use DOMElement; 14use Laminas\Feed\Uri; 15use Laminas\Feed\Writer; 16use Laminas\Feed\Writer\Renderer; 17 18class Rss extends Renderer\AbstractRenderer implements Renderer\RendererInterface 19{ 20 public function __construct(Writer\Entry $container) 21 { 22 parent::__construct($container); 23 } 24 25 /** 26 * Render RSS entry 27 * 28 * @return $this 29 */ 30 public function render() 31 { 32 $this->dom = new DOMDocument('1.0', $this->container->getEncoding()); 33 $this->dom->formatOutput = true; 34 $this->dom->substituteEntities = false; 35 $entry = $this->dom->createElement('item'); 36 $this->dom->appendChild($entry); 37 38 $this->_setTitle($this->dom, $entry); 39 $this->_setDescription($this->dom, $entry); 40 $this->_setDateCreated($this->dom, $entry); 41 $this->_setDateModified($this->dom, $entry); 42 $this->_setLink($this->dom, $entry); 43 $this->_setId($this->dom, $entry); 44 $this->_setAuthors($this->dom, $entry); 45 $this->_setEnclosure($this->dom, $entry); 46 $this->_setCommentLink($this->dom, $entry); 47 $this->_setCategories($this->dom, $entry); 48 foreach ($this->extensions as $ext) { 49 $ext->setType($this->getType()); 50 $ext->setRootElement($this->getRootElement()); 51 $ext->setDomDocument($this->getDomDocument(), $entry); 52 $ext->render(); 53 } 54 55 return $this; 56 } 57 58 /** 59 * Set entry title 60 * 61 * @param DOMDocument $dom 62 * @param DOMElement $root 63 * @return void 64 * @throws Writer\Exception\InvalidArgumentException 65 */ 66 // @codingStandardsIgnoreStart 67 protected function _setTitle(DOMDocument $dom, DOMElement $root) 68 { 69 // @codingStandardsIgnoreEnd 70 if (! $this->getDataContainer()->getDescription() 71 && ! $this->getDataContainer()->getTitle() 72 ) { 73 $message = 'RSS 2.0 entry elements SHOULD contain exactly one' 74 . ' title element but a title has not been set. In addition, there' 75 . ' is no description as required in the absence of a title.'; 76 $exception = new Writer\Exception\InvalidArgumentException($message); 77 if (! $this->ignoreExceptions) { 78 throw $exception; 79 } else { 80 $this->exceptions[] = $exception; 81 return; 82 } 83 } 84 $title = $dom->createElement('title'); 85 $root->appendChild($title); 86 $text = $dom->createTextNode($this->getDataContainer()->getTitle()); 87 $title->appendChild($text); 88 } 89 90 /** 91 * Set entry description 92 * 93 * @param DOMDocument $dom 94 * @param DOMElement $root 95 * @return void 96 * @throws Writer\Exception\InvalidArgumentException 97 */ 98 // @codingStandardsIgnoreStart 99 protected function _setDescription(DOMDocument $dom, DOMElement $root) 100 { 101 // @codingStandardsIgnoreEnd 102 if (! $this->getDataContainer()->getDescription() 103 && ! $this->getDataContainer()->getTitle() 104 ) { 105 $message = 'RSS 2.0 entry elements SHOULD contain exactly one' 106 . ' description element but a description has not been set. In' 107 . ' addition, there is no title element as required in the absence' 108 . ' of a description.'; 109 $exception = new Writer\Exception\InvalidArgumentException($message); 110 if (! $this->ignoreExceptions) { 111 throw $exception; 112 } else { 113 $this->exceptions[] = $exception; 114 return; 115 } 116 } 117 if (! $this->getDataContainer()->getDescription()) { 118 return; 119 } 120 $subtitle = $dom->createElement('description'); 121 $root->appendChild($subtitle); 122 $text = $dom->createCDATASection($this->getDataContainer()->getDescription()); 123 $subtitle->appendChild($text); 124 } 125 126 /** 127 * Set date entry was last modified 128 * 129 * @param DOMDocument $dom 130 * @param DOMElement $root 131 * @return void 132 */ 133 // @codingStandardsIgnoreStart 134 protected function _setDateModified(DOMDocument $dom, DOMElement $root) 135 { 136 // @codingStandardsIgnoreEnd 137 if (! $this->getDataContainer()->getDateModified()) { 138 return; 139 } 140 141 $updated = $dom->createElement('pubDate'); 142 $root->appendChild($updated); 143 $text = $dom->createTextNode( 144 $this->getDataContainer()->getDateModified()->format(DateTime::RSS) 145 ); 146 $updated->appendChild($text); 147 } 148 149 /** 150 * Set date entry was created 151 * 152 * @param DOMDocument $dom 153 * @param DOMElement $root 154 * @return void 155 */ 156 // @codingStandardsIgnoreStart 157 protected function _setDateCreated(DOMDocument $dom, DOMElement $root) 158 { 159 // @codingStandardsIgnoreEnd 160 if (! $this->getDataContainer()->getDateCreated()) { 161 return; 162 } 163 if (! $this->getDataContainer()->getDateModified()) { 164 $this->getDataContainer()->setDateModified( 165 $this->getDataContainer()->getDateCreated() 166 ); 167 } 168 } 169 170 /** 171 * Set entry authors 172 * 173 * @param DOMDocument $dom 174 * @param DOMElement $root 175 * @return void 176 */ 177 // @codingStandardsIgnoreStart 178 protected function _setAuthors(DOMDocument $dom, DOMElement $root) 179 { 180 // @codingStandardsIgnoreEnd 181 $authors = $this->container->getAuthors(); 182 if (! $authors || empty($authors)) { 183 return; 184 } 185 foreach ($authors as $data) { 186 $author = $this->dom->createElement('author'); 187 $name = $data['name']; 188 if (array_key_exists('email', $data)) { 189 $name = $data['email'] . ' (' . $data['name'] . ')'; 190 } 191 $text = $dom->createTextNode($name); 192 $author->appendChild($text); 193 $root->appendChild($author); 194 } 195 } 196 197 /** 198 * Set entry enclosure 199 * 200 * @param DOMDocument $dom 201 * @param DOMElement $root 202 * @return void 203 * @throws Writer\Exception\InvalidArgumentException 204 */ 205 // @codingStandardsIgnoreStart 206 protected function _setEnclosure(DOMDocument $dom, DOMElement $root) 207 { 208 // @codingStandardsIgnoreEnd 209 $data = $this->container->getEnclosure(); 210 if (! $data || empty($data)) { 211 return; 212 } 213 if (! isset($data['type'])) { 214 $exception = new Writer\Exception\InvalidArgumentException('Enclosure "type" is not set'); 215 if (! $this->ignoreExceptions) { 216 throw $exception; 217 } else { 218 $this->exceptions[] = $exception; 219 return; 220 } 221 } 222 if (! isset($data['length'])) { 223 $exception = new Writer\Exception\InvalidArgumentException('Enclosure "length" is not set'); 224 if (! $this->ignoreExceptions) { 225 throw $exception; 226 } else { 227 $this->exceptions[] = $exception; 228 return; 229 } 230 } 231 if ((int) $data['length'] < 0 || ! ctype_digit((string) $data['length'])) { 232 $exception = new Writer\Exception\InvalidArgumentException( 233 'Enclosure "length" must be an integer indicating the content\'s length in bytes' 234 ); 235 if (! $this->ignoreExceptions) { 236 throw $exception; 237 } 238 239 $this->exceptions[] = $exception; 240 return; 241 } 242 $enclosure = $this->dom->createElement('enclosure'); 243 $enclosure->setAttribute('type', $data['type']); 244 $enclosure->setAttribute('length', $data['length']); 245 $enclosure->setAttribute('url', $data['uri']); 246 $root->appendChild($enclosure); 247 } 248 249 /** 250 * Set link to entry 251 * 252 * @param DOMDocument $dom 253 * @param DOMElement $root 254 * @return void 255 */ 256 // @codingStandardsIgnoreStart 257 protected function _setLink(DOMDocument $dom, DOMElement $root) 258 { 259 // @codingStandardsIgnoreEnd 260 if (! $this->getDataContainer()->getLink()) { 261 return; 262 } 263 $link = $dom->createElement('link'); 264 $root->appendChild($link); 265 $text = $dom->createTextNode($this->getDataContainer()->getLink()); 266 $link->appendChild($text); 267 } 268 269 /** 270 * Set entry identifier 271 * 272 * @param DOMDocument $dom 273 * @param DOMElement $root 274 * @return void 275 */ 276 // @codingStandardsIgnoreStart 277 protected function _setId(DOMDocument $dom, DOMElement $root) 278 { 279 // @codingStandardsIgnoreEnd 280 if (! $this->getDataContainer()->getId() 281 && ! $this->getDataContainer()->getLink() 282 ) { 283 return; 284 } 285 286 $id = $dom->createElement('guid'); 287 $root->appendChild($id); 288 if (! $this->getDataContainer()->getId()) { 289 $this->getDataContainer()->setId( 290 $this->getDataContainer()->getLink() 291 ); 292 } 293 $text = $dom->createTextNode($this->getDataContainer()->getId()); 294 $id->appendChild($text); 295 296 $uri = Uri::factory($this->getDataContainer()->getId()); 297 if (! $uri->isValid() || ! $uri->isAbsolute()) { 298 /** @see http://www.rssboard.org/rss-profile#element-channel-item-guid */ 299 $id->setAttribute('isPermaLink', 'false'); 300 } 301 } 302 303 /** 304 * Set link to entry comments 305 * 306 * @param DOMDocument $dom 307 * @param DOMElement $root 308 * @return void 309 */ 310 // @codingStandardsIgnoreStart 311 protected function _setCommentLink(DOMDocument $dom, DOMElement $root) 312 { 313 // @codingStandardsIgnoreEnd 314 $link = $this->getDataContainer()->getCommentLink(); 315 if (! $link) { 316 return; 317 } 318 $clink = $this->dom->createElement('comments'); 319 $text = $dom->createTextNode($link); 320 $clink->appendChild($text); 321 $root->appendChild($clink); 322 } 323 324 /** 325 * Set entry categories 326 * 327 * @param DOMDocument $dom 328 * @param DOMElement $root 329 * @return void 330 */ 331 // @codingStandardsIgnoreStart 332 protected function _setCategories(DOMDocument $dom, DOMElement $root) 333 { 334 // @codingStandardsIgnoreEnd 335 $categories = $this->getDataContainer()->getCategories(); 336 if (! $categories) { 337 return; 338 } 339 foreach ($categories as $cat) { 340 $category = $dom->createElement('category'); 341 if (isset($cat['scheme'])) { 342 $category->setAttribute('domain', $cat['scheme']); 343 } 344 $text = $dom->createCDATASection($cat['term']); 345 $category->appendChild($text); 346 $root->appendChild($category); 347 } 348 } 349} 350