[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Joomla! Content Management System 5 * 6 * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> 7 * @license GNU General Public License version 2 or later; see LICENSE.txt 8 */ 9 10 namespace Joomla\CMS\Feed; 11 12 use Joomla\CMS\Feed\Parser\NamespaceParserInterface; 13 use Joomla\CMS\Filter\InputFilter; 14 15 // phpcs:disable PSR1.Files.SideEffects 16 \defined('JPATH_PLATFORM') or die; 17 // phpcs:enable PSR1.Files.SideEffects 18 19 /** 20 * Feed Parser class. 21 * 22 * @since 3.1.4 23 */ 24 abstract class FeedParser 25 { 26 /** 27 * The feed element name for the entry elements. 28 * 29 * @var string 30 * @since 3.1.4 31 */ 32 protected $entryElementName = 'entry'; 33 34 /** 35 * Array of NamespaceParserInterface objects 36 * 37 * @var array 38 * @since 3.1.4 39 */ 40 protected $namespaces = array(); 41 42 /** 43 * The XMLReader stream object for the feed. 44 * 45 * @var \XMLReader 46 * @since 3.1.4 47 */ 48 protected $stream; 49 50 /** 51 * The InputFilter 52 * 53 * @var InputFilter 54 * @since 3.9.25 55 */ 56 protected $inputFilter; 57 58 /** 59 * Constructor. 60 * 61 * @param \XMLReader $stream The XMLReader stream object for the feed. 62 * @param InputFilter $inputFilter The InputFilter object to be used 63 * 64 * @since 3.1.4 65 */ 66 public function __construct(\XMLReader $stream, InputFilter $inputFilter = null) 67 { 68 $this->stream = $stream; 69 $this->inputFilter = $inputFilter ?: InputFilter::getInstance([], [], 1, 1); 70 } 71 72 /** 73 * Method to parse the feed into a JFeed object. 74 * 75 * @return Feed 76 * 77 * @since 3.1.4 78 */ 79 public function parse() 80 { 81 $feed = new Feed(); 82 83 // Detect the feed version. 84 $this->initialise(); 85 86 // Let's get this party started... 87 do { 88 // Expand the element for processing. 89 $el = new \SimpleXMLElement($this->stream->readOuterXml()); 90 91 // Get the list of namespaces used within this element. 92 $ns = $el->getNamespaces(true); 93 94 // Get an array of available namespace objects for the element. 95 $namespaces = array(); 96 97 foreach ($ns as $prefix => $uri) { 98 // Ignore the empty namespace prefix. 99 if (empty($prefix)) { 100 continue; 101 } 102 103 // Get the necessary namespace objects for the element. 104 $namespace = $this->fetchNamespace($prefix); 105 106 if ($namespace) { 107 $namespaces[] = $namespace; 108 } 109 } 110 111 // Process the element. 112 $this->processElement($feed, $el, $namespaces); 113 114 // Skip over this element's children since it has been processed. 115 $this->moveToClosingElement(); 116 } while ($this->moveToNextElement()); 117 118 return $feed; 119 } 120 121 /** 122 * Method to register a namespace handler object. 123 * 124 * @param string $prefix The XML namespace prefix for which to register the namespace object. 125 * @param NamespaceParserInterface $namespace The namespace object to register. 126 * 127 * @return FeedParser 128 * 129 * @since 3.1.4 130 */ 131 public function registerNamespace($prefix, NamespaceParserInterface $namespace) 132 { 133 $this->namespaces[$prefix] = $namespace; 134 135 return $this; 136 } 137 138 /** 139 * Method to initialise the feed for parsing. If child parsers need to detect versions or other 140 * such things this is where you'll want to implement that logic. 141 * 142 * @return void 143 * 144 * @since 3.1.4 145 */ 146 abstract protected function initialise(); 147 148 /** 149 * Method to parse a specific feed element. 150 * 151 * @param Feed $feed The Feed object being built from the parsed feed. 152 * @param \SimpleXMLElement $el The current XML element object to handle. 153 * @param array $namespaces The array of relevant namespace objects to process for the element. 154 * 155 * @return void 156 * 157 * @since 3.1.4 158 */ 159 protected function processElement(Feed $feed, \SimpleXMLElement $el, array $namespaces) 160 { 161 // Build the internal method name. 162 $method = 'handle' . ucfirst($el->getName()); 163 164 // If we are dealing with an item then it is feed entry time. 165 if ($el->getName() == $this->entryElementName) { 166 // Create a new feed entry for the item. 167 $entry = new FeedEntry(); 168 169 // First call the internal method. 170 $this->processFeedEntry($entry, $el); 171 172 foreach ($namespaces as $namespace) { 173 if ($namespace instanceof NamespaceParserInterface) { 174 $namespace->processElementForFeedEntry($entry, $el); 175 } 176 } 177 178 // Add the new entry to the feed. 179 $feed->addEntry($entry); 180 181 return; 182 } 183 184 // Otherwise we treat it like any other element. 185 186 // First call the internal method. 187 if (\is_callable(array($this, $method))) { 188 $this->$method($feed, $el); 189 } 190 191 foreach ($namespaces as $namespace) { 192 if ($namespace instanceof NamespaceParserInterface) { 193 $namespace->processElementForFeed($feed, $el); 194 } 195 } 196 } 197 198 /** 199 * Method to get a namespace object for a given namespace prefix. 200 * 201 * @param string $prefix The XML prefix for which to fetch the namespace object. 202 * 203 * @return mixed NamespaceParserInterface or false if none exists. 204 * 205 * @since 3.1.4 206 */ 207 protected function fetchNamespace($prefix) 208 { 209 if (isset($this->namespaces[$prefix])) { 210 return $this->namespaces[$prefix]; 211 } 212 213 $className = \get_class($this) . ucfirst($prefix); 214 215 if (class_exists($className)) { 216 $this->namespaces[$prefix] = new $className(); 217 218 return $this->namespaces[$prefix]; 219 } 220 221 return false; 222 } 223 224 /** 225 * Method to move the stream parser to the next XML element node. 226 * 227 * @param string $name The name of the element for which to move the stream forward until is found. 228 * 229 * @return boolean True if the stream parser is on an XML element node. 230 * 231 * @since 3.1.4 232 */ 233 protected function moveToNextElement($name = null) 234 { 235 // Only keep looking until the end of the stream. 236 while ($this->stream->read()) { 237 // As soon as we get to the next ELEMENT node we are done. 238 if ($this->stream->nodeType == \XMLReader::ELEMENT) { 239 // If we are looking for a specific name make sure we have it. 240 if (isset($name) && ($this->stream->name != $name)) { 241 continue; 242 } 243 244 return true; 245 } 246 } 247 248 return false; 249 } 250 251 /** 252 * Method to move the stream parser to the closing XML node of the current element. 253 * 254 * @return void 255 * 256 * @since 3.1.4 257 * @throws \RuntimeException If the closing tag cannot be found. 258 */ 259 protected function moveToClosingElement() 260 { 261 // If we are on a self-closing tag then there is nothing to do. 262 if ($this->stream->isEmptyElement) { 263 return; 264 } 265 266 // Get the name and depth for the current node so that we can match the closing node. 267 $name = $this->stream->name; 268 $depth = $this->stream->depth; 269 270 // Only keep looking until the end of the stream. 271 while ($this->stream->read()) { 272 // If we have an END_ELEMENT node with the same name and depth as the node we started with we have a bingo. :-) 273 if (($this->stream->name == $name) && ($this->stream->depth == $depth) && ($this->stream->nodeType == \XMLReader::END_ELEMENT)) { 274 return; 275 } 276 } 277 278 throw new \RuntimeException('Unable to find the closing XML node.'); 279 } 280 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |