Verzeichnisstruktur phpBB-3.3.16


Veröffentlicht
27.04.2026

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

version_helper.php

Zuletzt modifiziert: 01.05.2026, 11:25 - Dateigröße: 15.30 KiB


001  <?php
002  /**
003  *
004  * This file is part of the phpBB Forum Software package.
005  *
006  * @copyright (c) phpBB Limited <https://www.phpbb.com>
007  * @license GNU General Public License, version 2 (GPL-2.0)
008  *
009  * For full copyright and license information, please see
010  * the docs/CREDITS.txt file.
011  *
012  */
013   
014  namespace phpbb;
015   
016  use phpbb\exception\version_check_exception;
017   
018  /**
019   * Class to handle version checking and comparison
020   */
021  class version_helper
022  {
023      /**
024       * @var string Host
025       */
026      protected $host = 'version.phpbb.com';
027   
028      /**
029       * @var string Path to file
030       */
031      protected $path = '/phpbb';
032   
033      /**
034       * @var string File name
035       */
036      protected $file = 'versions.json';
037   
038      /**
039       * @var bool Use SSL or not
040       */
041      protected $use_ssl = false;
042   
043      /**
044       * @var string Current version installed
045       */
046      protected $current_version;
047   
048      /**
049       * @var null|string Null to not force stability, 'unstable' or 'stable' to
050       *                    force the corresponding stability
051       */
052      protected $force_stability;
053   
054      /** @var \phpbb\cache\service */
055      protected $cache;
056   
057      /** @var \phpbb\config\config */
058      protected $config;
059   
060      /** @var \phpbb\file_downloader */
061      protected $file_downloader;
062   
063      protected $version_schema = array(
064          'stable' => array(
065              'current'        => 'version',
066              'download'        => 'url',
067              'announcement'    => 'url',
068              'eol'            => 'url',
069              'security'        => 'bool',
070          ),
071          'unstable' => array(
072              'current'        => 'version',
073              'download'        => 'url',
074              'announcement'    => 'url',
075              'eol'            => 'url',
076              'security'        => 'bool',
077          ),
078      );
079   
080      /**
081       * Constructor
082       *
083       * @param \phpbb\cache\service $cache
084       * @param \phpbb\config\config $config
085       * @param \phpbb\file_downloader $file_downloader
086       */
087      public function __construct(\phpbb\cache\service $cache, \phpbb\config\config $config, \phpbb\file_downloader $file_downloader)
088      {
089          $this->cache = $cache;
090          $this->config = $config;
091          $this->file_downloader = $file_downloader;
092   
093          if (defined('PHPBB_QA'))
094          {
095              $this->force_stability = 'unstable';
096          }
097   
098          $this->current_version = $this->config['version'];
099      }
100   
101      /**
102       * Set location to the file
103       *
104       * @param string $host Host (e.g. version.phpbb.com)
105       * @param string $path Path to file (e.g. /phpbb)
106       * @param string $file File name (Default: versions.json)
107       * @param bool $use_ssl Use SSL or not (Default: false)
108       * @return version_helper
109       */
110      public function set_file_location($host, $path, $file = 'versions.json', $use_ssl = false)
111      {
112          $this->host = $host;
113          $this->path = $path;
114          $this->file = $file;
115          $this->use_ssl = $use_ssl;
116   
117          return $this;
118      }
119   
120      /**
121       * Set current version
122       *
123       * @param string $version The current version
124       * @return version_helper
125       */
126      public function set_current_version($version)
127      {
128          $this->current_version = $version;
129   
130          return $this;
131      }
132   
133      /**
134       * Over-ride the stability to force check to include unstable versions
135       *
136       * @param null|string $stability Null to not force stability, 'unstable' or 'stable' to
137       *                         force the corresponding stability
138       * @return version_helper
139       */
140      public function force_stability($stability)
141      {
142          $this->force_stability = $stability;
143   
144          return $this;
145      }
146   
147      /**
148       * Wrapper for version_compare() that allows using uppercase A and B
149       * for alpha and beta releases.
150       *
151       * See http://www.php.net/manual/en/function.version-compare.php
152       *
153       * @param string $version1        First version number
154       * @param string $version2        Second version number
155       * @param string $operator        Comparison operator (optional)
156       *
157       * @return mixed                Boolean (true, false) if comparison operator is specified.
158       *                                Integer (-1, 0, 1) otherwise.
159       */
160      public function compare($version1, $version2, $operator = null)
161      {
162          return phpbb_version_compare($version1, $version2, $operator);
163      }
164   
165      /**
166       * Check whether or not a version is "stable"
167       *
168       * Stable means only numbers OR a pl release
169       *
170       * @param string $version
171       * @return bool Bool true or false
172       */
173      public function is_stable(string $version): bool
174      {
175          $matches = false;
176          preg_match('/^[\d.]+/', $version, $matches);
177   
178          if (empty($matches[0]))
179          {
180              return false;
181          }
182   
183          return $this->compare($version, $matches[0], '>=');
184      }
185   
186      /**
187       * Get branch for version, e.g. 3.2 for version 3.2.5
188       *
189       * @param string $version Version to get branch from
190       * @return string Branch for version or empty string if version is not of expected format
191       */
192      protected function get_branch(string $version): string
193      {
194          $matches = [];
195          preg_match('/^(\d+\.\d+).*$/', $version, $matches);
196   
197          return $matches[1] ?? '';
198      }
199   
200      /**
201      * Gets the latest version for the current branch the user is on
202      *
203      * @param bool $force_update Ignores cached data. Defaults to false.
204      * @param bool $force_cache Force the use of the cache. Override $force_update.
205      * @return string
206      * @throws version_check_exception
207      */
208      public function get_latest_on_current_branch(bool $force_update = false, bool $force_cache = false): string
209      {
210          $versions = $this->get_versions_matching_stability($force_update, $force_cache);
211   
212          $current_version = $this->current_version;
213   
214          // Get the branch information for the current version
215          $current_branch = $this->get_branch($current_version);
216          if (isset($versions[$current_branch]) && empty($versions[$current_branch]['eol']))
217          {
218              return $versions[$current_branch]['current'];
219          }
220   
221          // Sort versions in version ascending order so we can loop from the lowest version to the highest version.
222          uksort($versions, function($version1, $version2) {
223              return $this->compare($version1, $version2, '>');
224          });
225   
226          // Find next available version from versions info.
227          // Will suggest newer branches when EoL has been reached for the current branch, and/or version from newer branch
228          // is needed for having all known security issues fixed ('security' > latest on branch).
229          foreach ($versions as $branch => $data)
230          {
231              if ($this->compare($branch, $current_branch, '>=') && empty($data['eol'])
232                  && (empty($data['security']) || $this->compare($data['security'], $data['current'], '<=')))
233              {
234                  return $data['current'];
235              }
236          }
237   
238          return '';
239      }
240   
241      /**
242       * Gets the latest update for the current branch the user is on
243       * Will suggest versions from newer branches when EoL has been reached
244       * and/or version from newer branch is needed for having all known security
245       * issues fixed.
246       *
247       * @param bool $force_update Ignores cached data. Defaults to false.
248       * @param bool $force_cache Force the use of the cache. Override $force_update.
249       * @return array Version info or empty array if there are no updates
250       * @throws \RuntimeException
251       */
252      public function get_update_on_branch(bool $force_update = false, bool $force_cache = false): array
253      {
254          $versions = $this->get_versions_matching_stability($force_update, $force_cache);
255   
256          $current_version = $this->current_version;
257   
258          // Use current branch information if it exists
259          $current_branch = $this->get_branch($current_version);
260          $current_branch_data = $versions[$current_branch] ?? null;
261          if ($current_branch_data && empty($current_branch_data['eol'])
262              && (empty($current_branch_data['security']) || $this->compare($current_branch_data['security'], $current_version, '<=')))
263          {
264              return ($this->compare($current_branch_data['current'], $current_version, '>')) ? $current_branch_data : [];
265          }
266   
267          // Sort versions in version ascending order so we can loop from the lowest version to the highest version.
268          uksort($versions, function($version1, $version2) {
269              return $this->compare($version1, $version2, '>');
270          });
271   
272          foreach ($versions as $branch => $data)
273          {
274              if ($this->compare($branch, $current_branch, '>=') && empty($data['eol'])
275                  && (empty($data['security']) || $this->compare($data['security'], $data['current'], '<=')))
276              {
277                  if ($this->compare($data['current'], $current_version, '>'))
278                  {
279                      return $data;
280                  }
281                  else
282                  {
283                      break;
284                  }
285              }
286          }
287   
288          return [];
289      }
290   
291      /**
292       * Gets the latest extension update for the current phpBB branch the user is on
293       * Will suggest versions from newer branches when EoL has been reached
294       * and/or version from newer branch is needed for having all known security
295       * issues fixed.
296       *
297       * @param bool $force_update Ignores cached data. Defaults to false.
298       * @param bool $force_cache Force the use of the cache. Override $force_update.
299       * @return array Version info or empty array if there are no updates
300       * @throws \RuntimeException
301       */
302      public function get_ext_update_on_branch($force_update = false, $force_cache = false)
303      {
304          $versions = $this->get_versions_matching_stability($force_update, $force_cache);
305   
306          $self = $this;
307          $current_version = $this->current_version;
308   
309          $current_branch = $this->get_branch($this->config['version']);
310   
311          // Filter out any versions less than the current version
312          $versions = array_filter($versions, function($data) use ($self, $current_version) {
313              return $self->compare($data['current'], $current_version, '>=');
314          });
315   
316          // Filter out any phpbb branches less than the current version
317          $branches = array_filter(array_keys($versions), function($branch) use ($self, $current_branch) {
318              return $self->compare($branch, $current_branch, '>=');
319          });
320          if (!empty($branches))
321          {
322              $versions = array_intersect_key($versions, array_flip($branches));
323          }
324          else
325          {
326              // If branches are empty, it means the current phpBB branch is newer than any branch the
327              // extension was validated against. Reverse sort the versions array so we get the newest
328              // validated release available.
329              krsort($versions);
330          }
331   
332          // Get the first available version from the previous list.
333          $update_info = array_reduce($versions, function($value, $data) use ($self, $current_version) {
334              if ($value === null && $self->compare($data['current'], $current_version, '>='))
335              {
336                  if (!$data['eol'] && (!$data['security'] || $self->compare($data['security'], $data['current'], '<=')))
337                  {
338                      return $self->compare($data['current'], $current_version, '>') ? $data : array();
339                  }
340                  else
341                  {
342                      return null;
343                  }
344              }
345   
346              return $value;
347          });
348   
349          return $update_info === null ? array() : $update_info;
350      }
351   
352      /**
353      * Obtains the latest version information
354      *
355      * @param bool $force_update Ignores cached data. Defaults to false.
356      * @param bool $force_cache Force the use of the cache. Override $force_update.
357      * @return array
358      * @throws version_check_exception
359      */
360      public function get_suggested_updates($force_update = false, $force_cache = false)
361      {
362          $versions = $this->get_versions_matching_stability($force_update, $force_cache);
363   
364          $self = $this;
365          $current_version = $this->current_version;
366   
367          // Filter out any versions less than or equal to the current version
368          return array_filter($versions, function($data) use ($self, $current_version) {
369              return $self->compare($data['current'], $current_version, '>');
370          });
371      }
372   
373      /**
374      * Obtains the latest version information matching the stability of the current install
375      *
376      * @param bool $force_update Ignores cached data. Defaults to false.
377      * @param bool $force_cache Force the use of the cache. Override $force_update.
378      * @return array Version info
379      * @throws version_check_exception
380      */
381      public function get_versions_matching_stability($force_update = false, $force_cache = false)
382      {
383          $info = $this->get_versions($force_update, $force_cache);
384   
385          if ($this->force_stability !== null)
386          {
387              return ($this->force_stability === 'unstable') ? $info['unstable'] : $info['stable'];
388          }
389   
390          return ($this->is_stable($this->current_version)) ? $info['stable'] : $info['unstable'];
391      }
392   
393      /**
394      * Obtains the latest version information
395      *
396      * @param bool $force_update Ignores cached data. Defaults to false.
397      * @param bool $force_cache Force the use of the cache. Override $force_update.
398      * @return array Version info, includes stable and unstable data
399      * @throws version_check_exception
400      */
401      public function get_versions($force_update = false, $force_cache = false)
402      {
403          $cache_file = '_versioncheck_' . $this->host . $this->path . $this->file . $this->use_ssl;
404   
405          $info = $this->cache->get($cache_file);
406   
407          if ($info === false && $force_cache)
408          {
409              throw new version_check_exception('VERSIONCHECK_FAIL');
410          }
411          else if ($info === false || $force_update)
412          {
413              $info = $this->file_downloader->get($this->host, $this->path, $this->file, $this->use_ssl ? 443 : 80, 30);
414              $error_string = $this->file_downloader->get_error_string();
415   
416              if (!empty($error_string))
417              {
418                  throw new version_check_exception($error_string);
419              }
420   
421              $info = json_decode($info, true);
422   
423              // Sanitize any data we retrieve from a server
424              if (!empty($info))
425              {
426                  $json_sanitizer = function (&$value, $key) {
427                      $type_cast_helper = new \phpbb\request\type_cast_helper();
428                      $type_cast_helper->set_var($value, $value, gettype($value), true);
429                  };
430                  array_walk_recursive($info, $json_sanitizer);
431              }
432   
433              if (empty($info['stable']) && empty($info['unstable']))
434              {
435                  throw new version_check_exception('VERSIONCHECK_FAIL');
436              }
437   
438              $info['stable'] = (empty($info['stable'])) ? array() : $info['stable'];
439              $info['unstable'] = (empty($info['unstable'])) ? $info['stable'] : $info['unstable'];
440   
441              $info = $this->validate_versions($info);
442   
443              $this->cache->put($cache_file, $info, 86400); // 24 hours
444          }
445   
446          return $info;
447      }
448   
449      /**
450       * Validate versions info input
451       *
452       * @param array $versions_info Decoded json data array. Will be modified
453       *        and cleaned by this method
454       *
455       * @return array Versions info array
456       * @throws version_check_exception
457       */
458      public function validate_versions($versions_info)
459      {
460          $array_diff = array_diff_key($versions_info, array($this->version_schema));
461   
462          // Remove excessive data
463          if (count($array_diff) > 0)
464          {
465              $old_versions_info = $versions_info;
466              $versions_info = array(
467                  'stable'    => !empty($old_versions_info['stable']) ? $old_versions_info['stable'] : array(),
468                  'unstable'    => !empty($old_versions_info['unstable']) ? $old_versions_info['unstable'] : array(),
469              );
470              unset($old_versions_info);
471          }
472   
473          foreach ($versions_info as $stability_type => &$versions_data)
474          {
475              foreach ($versions_data as $branch => &$version_data)
476              {
477                  if (!preg_match('/^[0-9a-z\-\.]+$/i', $branch))
478                  {
479                      unset($versions_data[$branch]);
480                      continue;
481                  }
482   
483                  $stability_diff = array_diff_key($version_data, $this->version_schema[$stability_type]);
484   
485                  if (count($stability_diff) > 0)
486                  {
487                      $old_version_data = $version_data;
488                      $version_data = array();
489                      foreach ($this->version_schema[$stability_type] as $key => $value)
490                      {
491                          if (isset($old_version_data[$key]))
492                          {
493                              $version_data[$key] = $old_version_data[$key];
494                          }
495                      }
496                      unset($old_version_data);
497                  }
498   
499                  foreach ($version_data as $key => &$value)
500                  {
501                      if (!isset($this->version_schema[$stability_type][$key]))
502                      {
503                          unset($version_data[$key]);
504                          throw new version_check_exception('VERSIONCHECK_INVALID_ENTRY');
505                      }
506   
507                      switch ($this->version_schema[$stability_type][$key])
508                      {
509                          case 'bool':
510                              $value = (bool) $value;
511                          break;
512   
513                          case 'url':
514                              if (!empty($value) && !preg_match('#^' . get_preg_expression('url') . '$#iu', $value) &&
515                                  !preg_match('#^' . get_preg_expression('www_url') . '$#iu', $value))
516                              {
517                                  throw new version_check_exception('VERSIONCHECK_INVALID_URL');
518                              }
519                          break;
520   
521                          case 'version':
522                              if (!empty($value) && !preg_match(get_preg_expression('semantic_version'), $value))
523                              {
524                                  throw new version_check_exception('VERSIONCHECK_INVALID_VERSION');
525                              }
526                          break;
527   
528                          default:
529                              // Shouldn't be possible to trigger this
530                              throw new version_check_exception('VERSIONCHECK_INVALID_ENTRY');
531                      }
532                  }
533              }
534          }
535   
536          return $versions_info;
537      }
538  }
539