[ Index ]

PHP Cross Reference of phpBB-3.1.12-deutsch

title

Body

[close]

/phpbb/ -> version_helper.php (source)

   1  <?php
   2  /**
   3  *
   4  * This file is part of the phpBB Forum Software package.
   5  *
   6  * @copyright (c) phpBB Limited <https://www.phpbb.com>
   7  * @license GNU General Public License, version 2 (GPL-2.0)
   8  *
   9  * For full copyright and license information, please see
  10  * the docs/CREDITS.txt file.
  11  *
  12  */
  13  
  14  namespace phpbb;
  15  
  16  /**
  17   * Class to handle version checking and comparison
  18   */
  19  class version_helper
  20  {
  21      /**
  22       * @var string Host
  23       */
  24      protected $host = 'version.phpbb.com';
  25  
  26      /**
  27       * @var string Path to file
  28       */
  29      protected $path = '/phpbb';
  30  
  31      /**
  32       * @var string File name
  33       */
  34      protected $file = 'versions.json';
  35  
  36      /**
  37       * @var bool Use SSL or not
  38       */
  39      protected $use_ssl = false;
  40  
  41      /**
  42       * @var string Current version installed
  43       */
  44      protected $current_version;
  45  
  46      /**
  47       * @var null|string Null to not force stability, 'unstable' or 'stable' to
  48       *                    force the corresponding stability
  49       */
  50      protected $force_stability;
  51  
  52      /** @var \phpbb\cache\service */
  53      protected $cache;
  54  
  55      /** @var \phpbb\config\config */
  56      protected $config;
  57  
  58      /** @var \phpbb\file_downloader */
  59      protected $file_downloader;
  60  
  61      /** @var \phpbb\user */
  62      protected $user;
  63  
  64      protected $version_schema = array(
  65          'stable' => array(
  66              'current'        => 'version',
  67              'download'        => 'url',
  68              'announcement'    => 'url',
  69              'eol'            => 'url',
  70              'security'        => 'bool',
  71          ),
  72          'unstable' => array(
  73              'current'        => 'version',
  74              'download'        => 'url',
  75              'announcement'    => 'url',
  76              'eol'            => 'url',
  77              'security'        => 'bool',
  78          ),
  79      );
  80  
  81      /**
  82       * Constructor
  83       *
  84       * @param \phpbb\cache\service $cache
  85       * @param \phpbb\config\config $config
  86       * @param \phpbb\file_downloader $file_downloader
  87       * @param \phpbb\user $user
  88       */
  89  	public function __construct(\phpbb\cache\service $cache, \phpbb\config\config $config, \phpbb\file_downloader $file_downloader, \phpbb\user $user)
  90      {
  91          $this->cache = $cache;
  92          $this->config = $config;
  93          $this->file_downloader = $file_downloader;
  94          $this->user = $user;
  95  
  96          if (defined('PHPBB_QA'))
  97          {
  98              $this->force_stability = 'unstable';
  99          }
 100  
 101          $this->current_version = $this->config['version'];
 102      }
 103  
 104      /**
 105       * Set location to the file
 106       *
 107       * @param string $host Host (e.g. version.phpbb.com)
 108       * @param string $path Path to file (e.g. /phpbb)
 109       * @param string $file File name (Default: versions.json)
 110       * @param bool $use_ssl Use SSL or not (Default: false)
 111       * @return version_helper
 112       */
 113  	public function set_file_location($host, $path, $file = 'versions.json', $use_ssl = false)
 114      {
 115          $this->host = $host;
 116          $this->path = $path;
 117          $this->file = $file;
 118          $this->use_ssl = $use_ssl;
 119  
 120          return $this;
 121      }
 122  
 123      /**
 124       * Set current version
 125       *
 126       * @param string $version The current version
 127       * @return version_helper
 128       */
 129  	public function set_current_version($version)
 130      {
 131          $this->current_version = $version;
 132  
 133          return $this;
 134      }
 135  
 136      /**
 137       * Over-ride the stability to force check to include unstable versions
 138       *
 139       * @param null|string Null to not force stability, 'unstable' or 'stable' to
 140       *                         force the corresponding stability
 141       * @return version_helper
 142       */
 143  	public function force_stability($stability)
 144      {
 145          $this->force_stability = $stability;
 146  
 147          return $this;
 148      }
 149  
 150      /**
 151       * Wrapper for version_compare() that allows using uppercase A and B
 152       * for alpha and beta releases.
 153       *
 154       * See http://www.php.net/manual/en/function.version-compare.php
 155       *
 156       * @param string $version1        First version number
 157       * @param string $version2        Second version number
 158       * @param string $operator        Comparison operator (optional)
 159       *
 160       * @return mixed                Boolean (true, false) if comparison operator is specified.
 161       *                                Integer (-1, 0, 1) otherwise.
 162       */
 163  	public function compare($version1, $version2, $operator = null)
 164      {
 165          return phpbb_version_compare($version1, $version2, $operator);
 166      }
 167  
 168      /**
 169       * Check whether or not a version is "stable"
 170       *
 171       * Stable means only numbers OR a pl release
 172       *
 173       * @param string $version
 174       * @return bool Bool true or false
 175       */
 176  	public function is_stable($version)
 177      {
 178          $matches = false;
 179          preg_match('/^[\d\.]+/', $version, $matches);
 180  
 181          if (empty($matches[0]))
 182          {
 183              return false;
 184          }
 185  
 186          return $this->compare($version, $matches[0], '>=');
 187      }
 188  
 189      /**
 190      * Gets the latest version for the current branch the user is on
 191      *
 192      * @param bool $force_update Ignores cached data. Defaults to false.
 193      * @param bool $force_cache Force the use of the cache. Override $force_update.
 194      * @return string
 195      * @throws \RuntimeException
 196      */
 197  	public function get_latest_on_current_branch($force_update = false, $force_cache = false)
 198      {
 199          $versions = $this->get_versions_matching_stability($force_update, $force_cache);
 200  
 201          $self = $this;
 202          $current_version = $this->current_version;
 203  
 204          // Filter out any versions less than the current version
 205          $versions = array_filter($versions, function($data) use ($self, $current_version) {
 206              return $self->compare($data['current'], $current_version, '>=');
 207          });
 208  
 209          // Get the lowest version from the previous list.
 210          return array_reduce($versions, function($value, $data) use ($self) {
 211              if ($value === null || $self->compare($data['current'], $value, '<'))
 212              {
 213                  return $data['current'];
 214              }
 215  
 216              return $value;
 217          });
 218      }
 219  
 220      /**
 221       * Gets the latest update for the current branch the user is on
 222       * Will suggest versions from newer branches when EoL has been reached
 223       * and/or version from newer branch is needed for having all known security
 224       * issues fixed.
 225       *
 226       * @param bool $force_update Ignores cached data. Defaults to false.
 227       * @param bool $force_cache Force the use of the cache. Override $force_update.
 228       * @return array Version info or empty array if there are no updates
 229       * @throws \RuntimeException
 230       */
 231  	public function get_update_on_branch($force_update = false, $force_cache = false)
 232      {
 233          $versions = $this->get_versions_matching_stability($force_update, $force_cache);
 234  
 235          $self = $this;
 236          $current_version = $this->current_version;
 237  
 238          // Filter out any versions less than the current version
 239          $versions = array_filter($versions, function($data) use ($self, $current_version) {
 240              return $self->compare($data['current'], $current_version, '>=');
 241          });
 242  
 243          // Get the lowest version from the previous list.
 244          $update_info = array_reduce($versions, function($value, $data) use ($self, $current_version) {
 245              if ($value === null && $self->compare($data['current'], $current_version, '>='))
 246              {
 247                  if (!$data['eol'] && (!$data['security'] || $self->compare($data['security'], $data['current'], '<=')))
 248                  {
 249                      return ($self->compare($data['current'], $current_version, '>')) ? $data : array();
 250                  }
 251                  else
 252                  {
 253                      return null;
 254                  }
 255              }
 256  
 257              return $value;
 258          });
 259  
 260          return $update_info === null ? array() : $update_info;
 261      }
 262  
 263      /**
 264       * Gets the latest extension update for the current phpBB branch the user is on
 265       * Will suggest versions from newer branches when EoL has been reached
 266       * and/or version from newer branch is needed for having all known security
 267       * issues fixed.
 268       *
 269       * @param bool $force_update Ignores cached data. Defaults to false.
 270       * @param bool $force_cache Force the use of the cache. Override $force_update.
 271       * @return array Version info or empty array if there are no updates
 272       * @throws \RuntimeException
 273       */
 274  	public function get_ext_update_on_branch($force_update = false, $force_cache = false)
 275      {
 276          $versions = $this->get_versions_matching_stability($force_update, $force_cache);
 277  
 278          $self = $this;
 279          $current_version = $this->current_version;
 280  
 281          // Get current phpBB branch from version, e.g.: 3.2
 282          preg_match('/^(\d+\.\d+).*$/', $this->config['version'], $matches);
 283          $current_branch = $matches[1];
 284  
 285          // Filter out any versions less than the current version
 286          $versions = array_filter($versions, function($data) use ($self, $current_version) {
 287              return $self->compare($data['current'], $current_version, '>=');
 288          });
 289  
 290          // Filter out any phpbb branches less than the current version
 291          $branches = array_filter(array_keys($versions), function($branch) use ($self, $current_branch) {
 292              return $self->compare($branch, $current_branch, '>=');
 293          });
 294          if (!empty($branches))
 295          {
 296              $versions = array_intersect_key($versions, array_flip($branches));
 297          }
 298          else
 299          {
 300              // If branches are empty, it means the current phpBB branch is newer than any branch the
 301              // extension was validated against. Reverse sort the versions array so we get the newest
 302              // validated release available.
 303              krsort($versions);
 304          }
 305  
 306          // Get the first available version from the previous list.
 307          $update_info = array_reduce($versions, function($value, $data) use ($self, $current_version) {
 308              if ($value === null && $self->compare($data['current'], $current_version, '>='))
 309              {
 310                  if (!$data['eol'] && (!$data['security'] || $self->compare($data['security'], $data['current'], '<=')))
 311                  {
 312                      return $self->compare($data['current'], $current_version, '>') ? $data : array();
 313                  }
 314                  else
 315                  {
 316                      return null;
 317                  }
 318              }
 319  
 320              return $value;
 321          });
 322  
 323          return $update_info === null ? array() : $update_info;
 324      }
 325  
 326      /**
 327      * Obtains the latest version information
 328      *
 329      * @param bool $force_update Ignores cached data. Defaults to false.
 330      * @param bool $force_cache Force the use of the cache. Override $force_update.
 331      * @return array
 332      * @throws \RuntimeException
 333      */
 334  	public function get_suggested_updates($force_update = false, $force_cache = false)
 335      {
 336          $versions = $this->get_versions_matching_stability($force_update, $force_cache);
 337  
 338          $self = $this;
 339          $current_version = $this->current_version;
 340  
 341          // Filter out any versions less than or equal to the current version
 342          return array_filter($versions, function($data) use ($self, $current_version) {
 343              return $self->compare($data['current'], $current_version, '>');
 344          });
 345      }
 346  
 347      /**
 348      * Obtains the latest version information matching the stability of the current install
 349      *
 350      * @param bool $force_update Ignores cached data. Defaults to false.
 351      * @param bool $force_cache Force the use of the cache. Override $force_update.
 352      * @return array Version info
 353      * @throws \RuntimeException
 354      */
 355  	public function get_versions_matching_stability($force_update = false, $force_cache = false)
 356      {
 357          $info = $this->get_versions($force_update, $force_cache);
 358  
 359          if ($this->force_stability !== null)
 360          {
 361              return ($this->force_stability === 'unstable') ? $info['unstable'] : $info['stable'];
 362          }
 363  
 364          return ($this->is_stable($this->current_version)) ? $info['stable'] : $info['unstable'];
 365      }
 366  
 367      /**
 368      * Obtains the latest version information
 369      *
 370      * @param bool $force_update Ignores cached data. Defaults to false.
 371      * @param bool $force_cache Force the use of the cache. Override $force_update.
 372      * @return array Version info, includes stable and unstable data
 373      * @throws \RuntimeException
 374      */
 375  	public function get_versions($force_update = false, $force_cache = false)
 376      {
 377          $cache_file = '_versioncheck_' . $this->host . $this->path . $this->file . $this->use_ssl;
 378  
 379          $info = $this->cache->get($cache_file);
 380  
 381          if ($info === false && $force_cache)
 382          {
 383              throw new \RuntimeException($this->user->lang('VERSIONCHECK_FAIL'));
 384          }
 385          else if ($info === false || $force_update)
 386          {
 387              try {
 388                  $info = $this->file_downloader->get($this->host, $this->path, $this->file, $this->use_ssl ? 443 : 80);
 389              }
 390              catch (\phpbb\exception\runtime_exception $exception)
 391              {
 392                  $prepare_parameters = array_merge(array($exception->getMessage()), $exception->get_parameters());
 393                  throw new \RuntimeException(call_user_func_array(array($this->user, 'lang'), $prepare_parameters));
 394              }
 395              $error_string = $this->file_downloader->get_error_string();
 396  
 397              if (!empty($error_string))
 398              {
 399                  throw new \RuntimeException($error_string);
 400              }
 401  
 402              $info = json_decode($info, true);
 403  
 404              // Sanitize any data we retrieve from a server
 405              if (!empty($info))
 406              {
 407                  $json_sanitizer = function (&$value, $key) {
 408                      $type_cast_helper = new \phpbb\request\type_cast_helper();
 409                      $type_cast_helper->set_var($value, $value, gettype($value), true);
 410                  };
 411                  array_walk_recursive($info, $json_sanitizer);
 412              }
 413  
 414              if (empty($info['stable']) && empty($info['unstable']))
 415              {
 416                  $this->user->add_lang('acp/common');
 417  
 418                  throw new \RuntimeException($this->user->lang('VERSIONCHECK_FAIL'));
 419              }
 420  
 421              $info['stable'] = (empty($info['stable'])) ? array() : $info['stable'];
 422              $info['unstable'] = (empty($info['unstable'])) ? $info['stable'] : $info['unstable'];
 423  
 424              $info = $this->validate_versions($info);
 425  
 426              $this->cache->put($cache_file, $info, 86400); // 24 hours
 427          }
 428  
 429          return $info;
 430      }
 431  
 432      /**
 433       * Validate versions info input
 434       *
 435       * @param array $versions_info Decoded json data array. Will be modified
 436       *        and cleaned by this method
 437       *
 438       * @return array Versions info array
 439       */
 440  	public function validate_versions($versions_info)
 441      {
 442          $array_diff = array_diff_key($versions_info, array($this->version_schema));
 443  
 444          // Remove excessive data
 445          if (count($array_diff) > 0)
 446          {
 447              $old_versions_info = $versions_info;
 448              $versions_info = array(
 449                  'stable'    => !empty($old_versions_info['stable']) ? $old_versions_info['stable'] : array(),
 450                  'unstable'    => !empty($old_versions_info['unstable']) ? $old_versions_info['unstable'] : array(),
 451              );
 452              unset($old_versions_info);
 453          }
 454  
 455          foreach ($versions_info as $stability_type => &$versions_data)
 456          {
 457              foreach ($versions_data as $branch => &$version_data)
 458              {
 459                  if (!preg_match('/^[0-9a-z\-\.]+$/i', $branch))
 460                  {
 461                      unset($versions_data[$branch]);
 462                      continue;
 463                  }
 464  
 465                  $stability_diff = array_diff_key($version_data, $this->version_schema[$stability_type]);
 466  
 467                  if (count($stability_diff) > 0)
 468                  {
 469                      $old_version_data = $version_data;
 470                      $version_data = array();
 471                      foreach ($this->version_schema[$stability_type] as $key => $value)
 472                      {
 473                          if (isset($old_version_data[$key]))
 474                          {
 475                              $version_data[$key] = $old_version_data[$key];
 476                          }
 477                      }
 478                      unset($old_version_data);
 479                  }
 480  
 481                  foreach ($version_data as $key => &$value)
 482                  {
 483                      if (!isset($this->version_schema[$stability_type][$key]))
 484                      {
 485                          unset($version_data[$key]);
 486                          throw new \RuntimeException($this->user->lang('VERSIONCHECK_INVALID_ENTRY'));
 487                      }
 488  
 489                      switch ($this->version_schema[$stability_type][$key])
 490                      {
 491                          case 'bool':
 492                              $value = (bool) $value;
 493                          break;
 494  
 495                          case 'url':
 496                              if (!empty($value) && !preg_match('#^' . get_preg_expression('url') . '$#iu', $value) &&
 497                                  !preg_match('#^' . get_preg_expression('www_url') . '$#iu', $value))
 498                              {
 499                                  throw new \RuntimeException($this->user->lang('VERSIONCHECK_INVALID_URL'));
 500                              }
 501                          break;
 502  
 503                          case 'version':
 504                              if (!empty($value) && !preg_match(get_preg_expression('semantic_version'), $value))
 505                              {
 506                                  throw new \RuntimeException($this->user->lang('VERSIONCHECK_INVALID_VERSION'));
 507                              }
 508                          break;
 509  
 510                          default:
 511                              // Shouldn't be possible to trigger this
 512                              throw new \RuntimeException($this->user->lang('VERSIONCHECK_INVALID_ENTRY'));
 513                      }
 514                  }
 515              }
 516          }
 517  
 518          return $versions_info;
 519      }
 520  }


Generated: Thu Jan 11 00:25:41 2018 Cross-referenced by PHPXref 0.7.1