xref: /webtrees/app/Services/UpgradeService.php (revision 71239cb694d278d044f33328daaa60c8ed7431e9)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2018 webtrees development team
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16declare(strict_types=1);
17
18namespace Fisharebest\Webtrees\Services;
19
20use Fisharebest\Webtrees\Database;
21use Fisharebest\Webtrees\DebugBar;
22use Fisharebest\Webtrees\Site;
23use Fisharebest\Webtrees\Webtrees;
24use GuzzleHttp\Client;
25use GuzzleHttp\Exception\RequestException;
26use Symfony\Component\HttpFoundation\Response;
27
28/**
29 * Automatic upgrades.
30 */
31class UpgradeService
32{
33    // Regular expression to match a version string such as "1.7.10" or "2.0.0-alpha.1"
34    const REGEX_VERSION = '\d+\.\d+\.\d+(-[a-z0-9.-]+)?';
35
36    // Only check the webtrees server infrequently.
37    const CHECK_FOR_UPDATE_INTERVAL = 24 * 60 * 60;
38
39    // Fetch information about upgrades from here.
40    // Note: earlier versions of webtrees used svn.webtrees.net, so we must maintain both URLs.
41    const UPDATE_URL = 'https://dev.webtrees.net/build/latest-version.txt';
42
43    // If the update server doesn't respond after this time, give up.
44    const HTTP_TIMEOUT = 3.0;
45
46    /**
47     * @return bool
48     */
49    public function isUpgradeAvailable(): bool
50    {
51        // If the latest version is unavailable, we will have an empty sting which equates to version 0.
52
53        return version_compare(Webtrees::VERSION, $this->fetchLatestVersion()) < 0;
54    }
55
56    /**
57     * What is the latest version of webtrees.
58     *
59     * @return string
60     */
61    public function latestVersion(): string
62    {
63        $latest_version = $this->fetchLatestVersion();
64
65        [$version] = explode('|', $latest_version);
66
67        return $version;
68    }
69
70    /**
71     * Where can we download the latest version of webtrees.
72     *
73     * @return string
74     */
75    public function downloadUrl(): string
76    {
77        $latest_version = $this->fetchLatestVersion();
78
79        [, , $url] = explode('|', $latest_version . '||');
80
81        return $url;
82    }
83
84    /**
85     * Check with the webtrees.net server for the latest version of webtrees.
86     * Fetching the remote file can be slow, so check infrequently, and cache the result.
87     * Pass the current versions of webtrees, PHP and MySQL, as the response
88     * may be different for each. The server logs are used to generate
89     * installation statistics which can be found at http://dev.webtrees.net/statistics.html
90     *
91     * @return string
92     */
93    private function fetchLatestVersion(): string
94    {
95        $last_update_timestamp = (int) Site::getPreference('LATEST_WT_VERSION_TIMESTAMP');
96
97        if ($last_update_timestamp < WT_TIMESTAMP - self::CHECK_FOR_UPDATE_INTERVAL) {
98            try {
99                $client = new Client([
100                    'timeout' => self::HTTP_TIMEOUT,
101                ]);
102
103                $response = $client->get(self::UPDATE_URL, [
104                    'query' => $this->serverParameters(),
105                ]);
106
107                if ($response->getStatusCode() === Response::HTTP_OK) {
108                    Site::setPreference('LATEST_WT_VERSION', $response->getBody()->getContents());
109                    Site::setPreference('LATEST_WT_VERSION_TIMESTAMP', (string) WT_TIMESTAMP);
110                }
111            } catch (RequestException $ex) {
112                DebugBar::addThrowable($ex);
113            }
114        }
115
116        return Site::getPreference('LATEST_WT_VERSION');
117    }
118
119    /**
120     * The upgrade server needs to know a little about this server.
121     */
122    private function serverParameters(): array
123    {
124        $mysql_version = Database::prepare("SHOW VARIABLES LIKE 'version'")->fetchOneRow();
125
126        $operating_system = DIRECTORY_SEPARATOR === '/' ? 'u' : 'w';
127
128        return [
129            'w' => Webtrees::VERSION,
130            'p' => PHP_VERSION,
131            'm' => $mysql_version->value,
132            'o' => $operating_system,
133        ];
134    }
135}
136