[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Feed/ -> FeedParser.php (source)

   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  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer