xref: /webtrees/app/Services/UpgradeService.php (revision ebf05b8e693429fda0546b2b2b3cc18a09ae41ee)
10d11ac7eSGreg Roach<?php
20d11ac7eSGreg Roach/**
30d11ac7eSGreg Roach * webtrees: online genealogy
40d11ac7eSGreg Roach * Copyright (C) 2018 webtrees development team
50d11ac7eSGreg Roach * This program is free software: you can redistribute it and/or modify
60d11ac7eSGreg Roach * it under the terms of the GNU General Public License as published by
70d11ac7eSGreg Roach * the Free Software Foundation, either version 3 of the License, or
80d11ac7eSGreg Roach * (at your option) any later version.
90d11ac7eSGreg Roach * This program is distributed in the hope that it will be useful,
100d11ac7eSGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
110d11ac7eSGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
120d11ac7eSGreg Roach * GNU General Public License for more details.
130d11ac7eSGreg Roach * You should have received a copy of the GNU General Public License
140d11ac7eSGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>.
150d11ac7eSGreg Roach */
16e7f56f2aSGreg Roachdeclare(strict_types=1);
17e7f56f2aSGreg Roach
180d11ac7eSGreg Roachnamespace Fisharebest\Webtrees\Services;
190d11ac7eSGreg Roach
200d11ac7eSGreg Roachuse Fisharebest\Webtrees\Database;
210d11ac7eSGreg Roachuse Fisharebest\Webtrees\DebugBar;
220d11ac7eSGreg Roachuse Fisharebest\Webtrees\Site;
230d11ac7eSGreg Roachuse GuzzleHttp\Client;
240d11ac7eSGreg Roachuse GuzzleHttp\Exception\RequestException;
250d11ac7eSGreg Roachuse Symfony\Component\HttpFoundation\Response;
260d11ac7eSGreg Roach
270d11ac7eSGreg Roach/**
280d11ac7eSGreg Roach * Automatic upgrades.
290d11ac7eSGreg Roach */
300d11ac7eSGreg Roachclass UpgradeService
310d11ac7eSGreg Roach{
320d11ac7eSGreg Roach    // Regular expression to match a version string such as "1.7.10" or "2.0.0-alpha.1"
330d11ac7eSGreg Roach    const REGEX_VERSION = '\d+\.\d+\.\d+(-[a-z0-9.-]+)?';
340d11ac7eSGreg Roach
350d11ac7eSGreg Roach    // Only check the webtrees server infrequently.
360d11ac7eSGreg Roach    const CHECK_FOR_UPDATE_INTERVAL = 24 * 60 * 60;
370d11ac7eSGreg Roach
380d11ac7eSGreg Roach    // Fetch information about upgrades from here.
390d11ac7eSGreg Roach    // Note: earlier versions of webtrees used svn.webtrees.net, so we must maintain both URLs.
400d11ac7eSGreg Roach    const UPDATE_URL = 'https://dev.webtrees.net/build/latest-version.txt';
410d11ac7eSGreg Roach
420d11ac7eSGreg Roach    // If the update server doesn't respond after this time, give up.
430d11ac7eSGreg Roach    const HTTP_TIMEOUT = 3.0;
440d11ac7eSGreg Roach
450d11ac7eSGreg Roach    /**
460d11ac7eSGreg Roach     * @return bool
470d11ac7eSGreg Roach     */
480d11ac7eSGreg Roach    public function isUpgradeAvailable(): bool
490d11ac7eSGreg Roach    {
500d11ac7eSGreg Roach        // If the latest version is unavailable, we will have an empty sting which equates to version 0.
510d11ac7eSGreg Roach
520d11ac7eSGreg Roach        return version_compare(WT_VERSION, $this->fetchLatestVersion()) < 0;
530d11ac7eSGreg Roach    }
540d11ac7eSGreg Roach
550d11ac7eSGreg Roach    /**
560d11ac7eSGreg Roach     * What is the latest version of webtrees.
570d11ac7eSGreg Roach     *
580d11ac7eSGreg Roach     * @return string
590d11ac7eSGreg Roach     */
600d11ac7eSGreg Roach    public function latestVersion(): string
610d11ac7eSGreg Roach    {
620d11ac7eSGreg Roach        $latest_version = $this->fetchLatestVersion();
630d11ac7eSGreg Roach
640d11ac7eSGreg Roach        list($version) = explode('|', $latest_version);
650d11ac7eSGreg Roach
660d11ac7eSGreg Roach        return $version;
670d11ac7eSGreg Roach    }
680d11ac7eSGreg Roach
690d11ac7eSGreg Roach    /**
700d11ac7eSGreg Roach     * Where can we download the latest version of webtrees.
710d11ac7eSGreg Roach     *
720d11ac7eSGreg Roach     * @return string
730d11ac7eSGreg Roach     */
740d11ac7eSGreg Roach    public function downloadUrl(): string
750d11ac7eSGreg Roach    {
760d11ac7eSGreg Roach        $latest_version = $this->fetchLatestVersion();
770d11ac7eSGreg Roach
780d11ac7eSGreg Roach        list(, , $url) = explode('|', $latest_version . '||');
790d11ac7eSGreg Roach
800d11ac7eSGreg Roach        return $url;
810d11ac7eSGreg Roach    }
820d11ac7eSGreg Roach
830d11ac7eSGreg Roach    /**
840d11ac7eSGreg Roach     * Check with the webtrees.net server for the latest version of webtrees.
850d11ac7eSGreg Roach     * Fetching the remote file can be slow, so check infrequently, and cache the result.
860d11ac7eSGreg Roach     * Pass the current versions of webtrees, PHP and MySQL, as the response
870d11ac7eSGreg Roach     * may be different for each. The server logs are used to generate
880d11ac7eSGreg Roach     * installation statistics which can be found at http://dev.webtrees.net/statistics.html
890d11ac7eSGreg Roach     *
900d11ac7eSGreg Roach     * @return string
910d11ac7eSGreg Roach     */
920d11ac7eSGreg Roach    private function fetchLatestVersion(): string
930d11ac7eSGreg Roach    {
940d11ac7eSGreg Roach        $last_update_timestamp = (int) Site::getPreference('LATEST_WT_VERSION_TIMESTAMP');
950d11ac7eSGreg Roach
960d11ac7eSGreg Roach        if ($last_update_timestamp < WT_TIMESTAMP - self::CHECK_FOR_UPDATE_INTERVAL) {
970d11ac7eSGreg Roach            try {
980d11ac7eSGreg Roach                $client = new Client([
990d11ac7eSGreg Roach                    'timeout' => self::HTTP_TIMEOUT,
1000d11ac7eSGreg Roach                ]);
1010d11ac7eSGreg Roach
1020d11ac7eSGreg Roach                $response = $client->get(self::UPDATE_URL, [
1030d11ac7eSGreg Roach                    'query' => $this->serverParameters(),
1040d11ac7eSGreg Roach                ]);
1050d11ac7eSGreg Roach
1060d11ac7eSGreg Roach                if ($response->getStatusCode() === Response::HTTP_OK) {
1070d11ac7eSGreg Roach                    Site::setPreference('LATEST_WT_VERSION', $response->getBody()->getContents());
108*ebf05b8eSGreg Roach                    Site::setPreference('LATEST_WT_VERSION_TIMESTAMP', (string) WT_TIMESTAMP);
1090d11ac7eSGreg Roach                }
1100d11ac7eSGreg Roach            } catch (RequestException $ex) {
1110d11ac7eSGreg Roach                DebugBar::addThrowable($ex);
1120d11ac7eSGreg Roach            }
1130d11ac7eSGreg Roach        }
1140d11ac7eSGreg Roach
1150d11ac7eSGreg Roach        return Site::getPreference('LATEST_WT_VERSION');
1160d11ac7eSGreg Roach    }
1170d11ac7eSGreg Roach
1180d11ac7eSGreg Roach    /**
1190d11ac7eSGreg Roach     * The upgrade server needs to know a little about this server.
1200d11ac7eSGreg Roach     */
1218f53f488SRico Sonntag    private function serverParameters(): array
1220d11ac7eSGreg Roach    {
1230d11ac7eSGreg Roach        $mysql_version = Database::prepare("SHOW VARIABLES LIKE 'version'")->fetchOneRow();
1240d11ac7eSGreg Roach
1250d11ac7eSGreg Roach        $operating_system = DIRECTORY_SEPARATOR === '/' ? 'u' : 'w';
1260d11ac7eSGreg Roach
1270d11ac7eSGreg Roach        return [
1280d11ac7eSGreg Roach            'w' => WT_VERSION,
1290d11ac7eSGreg Roach            'p' => PHP_VERSION,
1300d11ac7eSGreg Roach            'm' => $mysql_version->value,
1310d11ac7eSGreg Roach            'o' => $operating_system,
1320d11ac7eSGreg Roach        ];
1330d11ac7eSGreg Roach    }
1340d11ac7eSGreg Roach}
135