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. |
|
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
version_helper.php
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