[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Updater/ -> Update.php (source)

   1  <?php
   2  
   3  /**
   4   * Joomla! Content Management System
   5   *
   6   * @copyright  (C) 2008 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\Updater;
  11  
  12  use Joomla\CMS\Factory;
  13  use Joomla\CMS\Filter\InputFilter;
  14  use Joomla\CMS\Http\HttpFactory;
  15  use Joomla\CMS\Language\Text;
  16  use Joomla\CMS\Log\Log;
  17  use Joomla\CMS\Object\CMSObject;
  18  use Joomla\CMS\Version;
  19  use Joomla\Registry\Registry;
  20  
  21  // phpcs:disable PSR1.Files.SideEffects
  22  \defined('JPATH_PLATFORM') or die;
  23  // phpcs:enable PSR1.Files.SideEffects
  24  
  25  /**
  26   * Update class. It is used by Updater::update() to install an update. Use Updater::findUpdates() to find updates for
  27   * an extension.
  28   *
  29   * @since  1.7.0
  30   */
  31  class Update extends CMSObject
  32  {
  33      /**
  34       * Update manifest `<name>` element
  35       *
  36       * @var    string
  37       * @since  1.7.0
  38       */
  39      protected $name;
  40  
  41      /**
  42       * Update manifest `<description>` element
  43       *
  44       * @var    string
  45       * @since  1.7.0
  46       */
  47      protected $description;
  48  
  49      /**
  50       * Update manifest `<element>` element
  51       *
  52       * @var    string
  53       * @since  1.7.0
  54       */
  55      protected $element;
  56  
  57      /**
  58       * Update manifest `<type>` element
  59       *
  60       * @var    string
  61       * @since  1.7.0
  62       */
  63      protected $type;
  64  
  65      /**
  66       * Update manifest `<version>` element
  67       *
  68       * @var    string
  69       * @since  1.7.0
  70       */
  71      protected $version;
  72  
  73      /**
  74       * Update manifest `<infourl>` element
  75       *
  76       * @var    string
  77       * @since  1.7.0
  78       */
  79      protected $infourl;
  80  
  81      /**
  82       * Update manifest `<client>` element
  83       *
  84       * @var    string
  85       * @since  1.7.0
  86       */
  87      protected $client;
  88  
  89      /**
  90       * Update manifest `<group>` element
  91       *
  92       * @var    string
  93       * @since  1.7.0
  94       */
  95      protected $group;
  96  
  97      /**
  98       * Update manifest `<downloads>` element
  99       *
 100       * @var    string
 101       * @since  1.7.0
 102       */
 103      protected $downloads;
 104  
 105      /**
 106       * Update manifest `<downloadsource>` elements
 107       *
 108       * @var    DownloadSource[]
 109       * @since  3.8.3
 110       */
 111      protected $downloadSources = array();
 112  
 113      /**
 114       * Update manifest `<tags>` element
 115       *
 116       * @var    string
 117       * @since  1.7.0
 118       */
 119      protected $tags;
 120  
 121      /**
 122       * Update manifest `<maintainer>` element
 123       *
 124       * @var    string
 125       * @since  1.7.0
 126       */
 127      protected $maintainer;
 128  
 129      /**
 130       * Update manifest `<maintainerurl>` element
 131       *
 132       * @var    string
 133       * @since  1.7.0
 134       */
 135      protected $maintainerurl;
 136  
 137      /**
 138       * Update manifest `<category>` element
 139       *
 140       * @var    string
 141       * @since  1.7.0
 142       */
 143      protected $category;
 144  
 145      /**
 146       * Update manifest `<relationships>` element
 147       *
 148       * @var    string
 149       * @since  1.7.0
 150       */
 151      protected $relationships;
 152  
 153      /**
 154       * Update manifest `<targetplatform>` element
 155       *
 156       * @var    string
 157       * @since  1.7.0
 158       */
 159      protected $targetplatform;
 160  
 161      /**
 162       * Extra query for download URLs
 163       *
 164       * @var    string
 165       * @since  3.2.0
 166       */
 167      protected $extra_query;
 168  
 169      /**
 170       * Resource handle for the XML Parser
 171       *
 172       * @var    resource
 173       * @since  3.0.0
 174       */
 175      protected $xmlParser;
 176  
 177      /**
 178       * Element call stack
 179       *
 180       * @var    array
 181       * @since  3.0.0
 182       */
 183      protected $stack = array('base');
 184  
 185      /**
 186       * Unused state array
 187       *
 188       * @var    array
 189       * @since  3.0.0
 190       */
 191      protected $stateStore = array();
 192  
 193      /**
 194       * Object containing the current update data
 195       *
 196       * @var    \stdClass
 197       * @since  3.0.0
 198       */
 199      protected $currentUpdate;
 200  
 201      /**
 202       * Object containing the latest update data
 203       *
 204       * @var    \stdClass
 205       * @since  3.0.0
 206       */
 207      protected $latest;
 208  
 209      /**
 210       * The minimum stability required for updates to be taken into account. The possible values are:
 211       * 0    dev         Development snapshots, nightly builds, pre-release versions and so on
 212       * 1    alpha       Alpha versions (work in progress, things are likely to be broken)
 213       * 2    beta        Beta versions (major functionality in place, show-stopper bugs are likely to be present)
 214       * 3    rc          Release Candidate versions (almost stable, minor bugs might be present)
 215       * 4    stable      Stable versions (production quality code)
 216       *
 217       * @var    integer
 218       * @since  14.1
 219       *
 220       * @see    Updater
 221       */
 222      protected $minimum_stability = Updater::STABILITY_STABLE;
 223  
 224      /**
 225       * Array with compatible versions used by the pre-update check
 226       *
 227       * @var    array
 228       * @since  3.10.2
 229       */
 230      protected $compatibleVersions = array();
 231  
 232      /**
 233       * Gets the reference to the current direct parent
 234       *
 235       * @return  string
 236       *
 237       * @since   1.7.0
 238       */
 239      protected function _getStackLocation()
 240      {
 241          return implode('->', $this->stack);
 242      }
 243  
 244      /**
 245       * Get the last position in stack count
 246       *
 247       * @return  string
 248       *
 249       * @since   1.7.0
 250       */
 251      protected function _getLastTag()
 252      {
 253          return $this->stack[\count($this->stack) - 1];
 254      }
 255  
 256      /**
 257       * XML Start Element callback
 258       *
 259       * @param   object  $parser  Parser object
 260       * @param   string  $name    Name of the tag found
 261       * @param   array   $attrs   Attributes of the tag
 262       *
 263       * @return  void
 264       *
 265       * @note    This is public because it is called externally
 266       * @since   1.7.0
 267       */
 268      public function _startElement($parser, $name, $attrs = array())
 269      {
 270          $this->stack[] = $name;
 271          $tag           = $this->_getStackLocation();
 272  
 273          // Reset the data
 274          if (isset($this->$tag)) {
 275              $this->$tag->_data = '';
 276          }
 277  
 278          switch ($name) {
 279              // This is a new update; create a current update
 280              case 'UPDATE':
 281                  $this->currentUpdate = new \stdClass();
 282                  break;
 283  
 284              // Handle the array of download sources
 285              case 'DOWNLOADSOURCE':
 286                  $source = new DownloadSource();
 287  
 288                  foreach ($attrs as $key => $data) {
 289                      $key = strtolower($key);
 290                      $source->$key = $data;
 291                  }
 292  
 293                  $this->downloadSources[] = $source;
 294  
 295                  break;
 296  
 297              // Don't do anything
 298              case 'UPDATES':
 299                  break;
 300  
 301              // For everything else there's...the default!
 302              default:
 303                  $name = strtolower($name);
 304  
 305                  if (!isset($this->currentUpdate->$name)) {
 306                      $this->currentUpdate->$name = new \stdClass();
 307                  }
 308  
 309                  $this->currentUpdate->$name->_data = '';
 310  
 311                  foreach ($attrs as $key => $data) {
 312                      $key = strtolower($key);
 313                      $this->currentUpdate->$name->$key = $data;
 314                  }
 315                  break;
 316          }
 317      }
 318  
 319      /**
 320       * Callback for closing the element
 321       *
 322       * @param   object  $parser  Parser object
 323       * @param   string  $name    Name of element that was closed
 324       *
 325       * @return  void
 326       *
 327       * @note    This is public because it is called externally
 328       * @since   1.7.0
 329       */
 330      public function _endElement($parser, $name)
 331      {
 332          array_pop($this->stack);
 333  
 334          switch ($name) {
 335              // Closing update, find the latest version and check
 336              case 'UPDATE':
 337                  $product = strtolower(InputFilter::getInstance()->clean(Version::PRODUCT, 'cmd'));
 338  
 339                  // Check that the product matches and that the version matches (optionally a regexp)
 340                  if (
 341                      isset($this->currentUpdate->targetplatform->name)
 342                      && $product == $this->currentUpdate->targetplatform->name
 343                      && preg_match('/^' . $this->currentUpdate->targetplatform->version . '/', $this->get('jversion.full', JVERSION))
 344                  ) {
 345                      $phpMatch = false;
 346  
 347                      // Check if PHP version supported via <php_minimum> tag, assume true if tag isn't present
 348                      if (!isset($this->currentUpdate->php_minimum) || version_compare(PHP_VERSION, $this->currentUpdate->php_minimum->_data, '>=')) {
 349                          $phpMatch = true;
 350                      }
 351  
 352                      $dbMatch = false;
 353  
 354                      // Check if DB & version is supported via <supported_databases> tag, assume supported if tag isn't present
 355                      if (isset($this->currentUpdate->supported_databases)) {
 356                          $db           = Factory::getDbo();
 357                          $dbType       = strtolower($db->getServerType());
 358                          $dbVersion    = $db->getVersion();
 359                          $supportedDbs = $this->currentUpdate->supported_databases;
 360  
 361                          // MySQL and MariaDB use the same database driver but not the same version numbers
 362                          if ($dbType === 'mysql') {
 363                              // Check whether we have a MariaDB version string and extract the proper version from it
 364                              if (stripos($dbVersion, 'mariadb') !== false) {
 365                                  // MariaDB: Strip off any leading '5.5.5-', if present
 366                                  $dbVersion = preg_replace('/^5\.5\.5-/', '', $dbVersion);
 367                                  $dbType    = 'mariadb';
 368                              }
 369                          }
 370  
 371                          // Do we have an entry for the database?
 372                          if (isset($supportedDbs->$dbType)) {
 373                              $minimumVersion = $supportedDbs->$dbType;
 374                              $dbMatch        = version_compare($dbVersion, $minimumVersion, '>=');
 375                          }
 376                      } else {
 377                          // Set to true if the <supported_databases> tag is not set
 378                          $dbMatch = true;
 379                      }
 380  
 381                      // Check minimum stability
 382                      $stabilityMatch = true;
 383  
 384                      if (isset($this->currentUpdate->stability) && ($this->currentUpdate->stability < $this->minimum_stability)) {
 385                          $stabilityMatch = false;
 386                      }
 387  
 388                      if ($phpMatch && $stabilityMatch && $dbMatch) {
 389                          if (!empty($this->currentUpdate->downloadurl) && !empty($this->currentUpdate->downloadurl->_data)) {
 390                              $this->compatibleVersions[] = $this->currentUpdate->version->_data;
 391                          }
 392  
 393                          if (
 394                              !isset($this->latest)
 395                              || version_compare($this->currentUpdate->version->_data, $this->latest->version->_data, '>')
 396                          ) {
 397                              $this->latest = $this->currentUpdate;
 398                          }
 399                      }
 400                  }
 401                  break;
 402              case 'UPDATES':
 403                  // If the latest item is set then we transfer it to where we want to
 404                  if (isset($this->latest)) {
 405                      foreach (get_object_vars($this->latest) as $key => $val) {
 406                          $this->$key = $val;
 407                      }
 408  
 409                      unset($this->latest);
 410                      unset($this->currentUpdate);
 411                  } elseif (isset($this->currentUpdate)) {
 412                      // The update might be for an older version of j!
 413                      unset($this->currentUpdate);
 414                  }
 415                  break;
 416          }
 417      }
 418  
 419      /**
 420       * Character Parser Function
 421       *
 422       * @param   object  $parser  Parser object.
 423       * @param   object  $data    The data.
 424       *
 425       * @return  void
 426       *
 427       * @note    This is public because its called externally.
 428       * @since   1.7.0
 429       */
 430      public function _characterData($parser, $data)
 431      {
 432          $tag = $this->_getLastTag();
 433  
 434          // Throw the data for this item together
 435          $tag = strtolower($tag);
 436  
 437          if ($tag === 'tag') {
 438              $this->currentUpdate->stability = $this->stabilityTagToInteger((string) $data);
 439  
 440              return;
 441          }
 442  
 443          if ($tag === 'downloadsource') {
 444              // Grab the last source so we can append the URL
 445              $source = end($this->downloadSources);
 446              $source->url = $data;
 447  
 448              return;
 449          }
 450  
 451          if (isset($this->currentUpdate->$tag)) {
 452              $this->currentUpdate->$tag->_data .= $data;
 453          }
 454      }
 455  
 456      /**
 457       * Loads an XML file from a URL.
 458       *
 459       * @param   string  $url               The URL.
 460       * @param   int     $minimumStability  The minimum stability required for updating the extension {@see Updater}
 461       *
 462       * @return  boolean  True on success
 463       *
 464       * @since   1.7.0
 465       */
 466      public function loadFromXml($url, $minimumStability = Updater::STABILITY_STABLE)
 467      {
 468          $version    = new Version();
 469          $httpOption = new Registry();
 470          $httpOption->set('userAgent', $version->getUserAgent('Joomla', true, false));
 471  
 472          try {
 473              $http = HttpFactory::getHttp($httpOption);
 474              $response = $http->get($url);
 475          } catch (\RuntimeException $e) {
 476              $response = null;
 477          }
 478  
 479          if ($response === null || $response->code !== 200) {
 480              // @todo: Add a 'mark bad' setting here somehow
 481              Log::add(Text::sprintf('JLIB_UPDATER_ERROR_EXTENSION_OPEN_URL', $url), Log::WARNING, 'jerror');
 482  
 483              return false;
 484          }
 485  
 486          $this->minimum_stability = $minimumStability;
 487  
 488          $this->xmlParser = xml_parser_create('');
 489          xml_set_object($this->xmlParser, $this);
 490          xml_set_element_handler($this->xmlParser, '_startElement', '_endElement');
 491          xml_set_character_data_handler($this->xmlParser, '_characterData');
 492  
 493          if (!xml_parse($this->xmlParser, $response->body)) {
 494              Log::add(
 495                  sprintf(
 496                      'XML error: %s at line %d',
 497                      xml_error_string(xml_get_error_code($this->xmlParser)),
 498                      xml_get_current_line_number($this->xmlParser)
 499                  ),
 500                  Log::WARNING,
 501                  'updater'
 502              );
 503  
 504              return false;
 505          }
 506  
 507          xml_parser_free($this->xmlParser);
 508  
 509          return true;
 510      }
 511  
 512      /**
 513       * Converts a tag to numeric stability representation. If the tag doesn't represent a known stability level (one of
 514       * dev, alpha, beta, rc, stable) it is ignored.
 515       *
 516       * @param   string  $tag  The tag string, e.g. dev, alpha, beta, rc, stable
 517       *
 518       * @return  integer
 519       *
 520       * @since   3.4
 521       */
 522      protected function stabilityTagToInteger($tag)
 523      {
 524          $constant = '\\Joomla\\CMS\\Updater\\Updater::STABILITY_' . strtoupper($tag);
 525  
 526          if (\defined($constant)) {
 527              return \constant($constant);
 528          }
 529  
 530          return Updater::STABILITY_STABLE;
 531      }
 532  }


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