xref: /webtrees/app/Services/UpgradeService.php (revision 8b67c11a1199191915b4af08a3841e7ce9d528b6)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2019 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\Site;
22use Fisharebest\Webtrees\Webtrees;
23use GuzzleHttp\Client;
24use GuzzleHttp\Exception\RequestException;
25use Symfony\Component\HttpFoundation\Response;
26
27/**
28 * Automatic upgrades.
29 */
30class UpgradeService
31{
32    // Regular expression to match a version string such as "1.7.10" or "2.0.0-alpha.1"
33    private const REGEX_VERSION = '\d+\.\d+\.\d+(-[a-z0-9.-]+)?';
34
35    // Only check the webtrees server infrequently.
36    private const CHECK_FOR_UPDATE_INTERVAL = 24 * 60 * 60;
37
38    // Fetch information about upgrades from here.
39    // Note: earlier versions of webtrees used svn.webtrees.net, so we must maintain both URLs.
40    private const UPDATE_URL = 'https://dev.webtrees.net/build/latest-version.txt';
41
42    // If the update server doesn't respond after this time, give up.
43    private const HTTP_TIMEOUT = 3.0;
44
45    /**
46     * @return bool
47     */
48    public function isUpgradeAvailable(): bool
49    {
50        // If the latest version is unavailable, we will have an empty sting which equates to version 0.
51
52        return version_compare(Webtrees::VERSION, $this->fetchLatestVersion()) < 0;
53    }
54
55    /**
56     * What is the latest version of webtrees.
57     *
58     * @return string
59     */
60    public function latestVersion(): string
61    {
62        $latest_version = $this->fetchLatestVersion();
63
64        [$version] = explode('|', $latest_version);
65
66        return $version;
67    }
68
69    /**
70     * Where can we download the latest version of webtrees.
71     *
72     * @return string
73     */
74    public function downloadUrl(): string
75    {
76        $latest_version = $this->fetchLatestVersion();
77
78        [, , $url] = explode('|', $latest_version . '||');
79
80        return $url;
81    }
82
83    /**
84     * Check with the webtrees.net server for the latest version of webtrees.
85     * Fetching the remote file can be slow, so check infrequently, and cache the result.
86     * Pass the current versions of webtrees, PHP and MySQL, as the response
87     * may be different for each. The server logs are used to generate
88     * installation statistics which can be found at http://dev.webtrees.net/statistics.html
89     *
90     * @return string
91     */
92    private function fetchLatestVersion(): string
93    {
94        $last_update_timestamp = (int) Site::getPreference('LATEST_WT_VERSION_TIMESTAMP');
95
96        if ($last_update_timestamp < WT_TIMESTAMP - self::CHECK_FOR_UPDATE_INTERVAL) {
97            try {
98                $client = new Client([
99                    'timeout' => self::HTTP_TIMEOUT,
100                ]);
101
102                $response = $client->get(self::UPDATE_URL, [
103                    'query' => $this->serverParameters(),
104                ]);
105
106                if ($response->getStatusCode() === Response::HTTP_OK) {
107                    Site::setPreference('LATEST_WT_VERSION', $response->getBody()->getContents());
108                    Site::setPreference('LATEST_WT_VERSION_TIMESTAMP', (string) WT_TIMESTAMP);
109                }
110            } catch (RequestException $ex) {
111                // Can't connect to the server?
112                // Use the existing information about latest versions.
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        $operating_system = DIRECTORY_SEPARATOR === '/' ? 'u' : 'w';
125
126        return [
127            'w' => Webtrees::VERSION,
128            'p' => PHP_VERSION,
129            'o' => $operating_system,
130        ];
131    }
132}
133