xref: /webtrees/app/Services/UpgradeService.php (revision 65e02381e632b9777b3346fdd802519b781016a7)
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;
238d0ebef0SGreg Roachuse Fisharebest\Webtrees\Webtrees;
240d11ac7eSGreg Roachuse GuzzleHttp\Client;
250d11ac7eSGreg Roachuse GuzzleHttp\Exception\RequestException;
260d11ac7eSGreg Roachuse Symfony\Component\HttpFoundation\Response;
270d11ac7eSGreg Roach
280d11ac7eSGreg Roach/**
290d11ac7eSGreg Roach * Automatic upgrades.
300d11ac7eSGreg Roach */
310d11ac7eSGreg Roachclass UpgradeService
320d11ac7eSGreg Roach{
330d11ac7eSGreg Roach    // Regular expression to match a version string such as "1.7.10" or "2.0.0-alpha.1"
340d11ac7eSGreg Roach    const REGEX_VERSION = '\d+\.\d+\.\d+(-[a-z0-9.-]+)?';
350d11ac7eSGreg Roach
360d11ac7eSGreg Roach    // Only check the webtrees server infrequently.
370d11ac7eSGreg Roach    const CHECK_FOR_UPDATE_INTERVAL = 24 * 60 * 60;
380d11ac7eSGreg Roach
390d11ac7eSGreg Roach    // Fetch information about upgrades from here.
400d11ac7eSGreg Roach    // Note: earlier versions of webtrees used svn.webtrees.net, so we must maintain both URLs.
410d11ac7eSGreg Roach    const UPDATE_URL = 'https://dev.webtrees.net/build/latest-version.txt';
420d11ac7eSGreg Roach
430d11ac7eSGreg Roach    // If the update server doesn't respond after this time, give up.
440d11ac7eSGreg Roach    const HTTP_TIMEOUT = 3.0;
450d11ac7eSGreg Roach
460d11ac7eSGreg Roach    /**
470d11ac7eSGreg Roach     * @return bool
480d11ac7eSGreg Roach     */
490d11ac7eSGreg Roach    public function isUpgradeAvailable(): bool
500d11ac7eSGreg Roach    {
510d11ac7eSGreg Roach        // If the latest version is unavailable, we will have an empty sting which equates to version 0.
520d11ac7eSGreg Roach
538d0ebef0SGreg Roach        return version_compare(Webtrees::VERSION, $this->fetchLatestVersion()) < 0;
540d11ac7eSGreg Roach    }
550d11ac7eSGreg Roach
560d11ac7eSGreg Roach    /**
570d11ac7eSGreg Roach     * What is the latest version of webtrees.
580d11ac7eSGreg Roach     *
590d11ac7eSGreg Roach     * @return string
600d11ac7eSGreg Roach     */
610d11ac7eSGreg Roach    public function latestVersion(): string
620d11ac7eSGreg Roach    {
630d11ac7eSGreg Roach        $latest_version = $this->fetchLatestVersion();
640d11ac7eSGreg Roach
65*65e02381SGreg Roach        [$version] = explode('|', $latest_version);
660d11ac7eSGreg Roach
670d11ac7eSGreg Roach        return $version;
680d11ac7eSGreg Roach    }
690d11ac7eSGreg Roach
700d11ac7eSGreg Roach    /**
710d11ac7eSGreg Roach     * Where can we download the latest version of webtrees.
720d11ac7eSGreg Roach     *
730d11ac7eSGreg Roach     * @return string
740d11ac7eSGreg Roach     */
750d11ac7eSGreg Roach    public function downloadUrl(): string
760d11ac7eSGreg Roach    {
770d11ac7eSGreg Roach        $latest_version = $this->fetchLatestVersion();
780d11ac7eSGreg Roach
79*65e02381SGreg Roach        [, , $url] = explode('|', $latest_version . '||');
800d11ac7eSGreg Roach
810d11ac7eSGreg Roach        return $url;
820d11ac7eSGreg Roach    }
830d11ac7eSGreg Roach
840d11ac7eSGreg Roach    /**
850d11ac7eSGreg Roach     * Check with the webtrees.net server for the latest version of webtrees.
860d11ac7eSGreg Roach     * Fetching the remote file can be slow, so check infrequently, and cache the result.
870d11ac7eSGreg Roach     * Pass the current versions of webtrees, PHP and MySQL, as the response
880d11ac7eSGreg Roach     * may be different for each. The server logs are used to generate
890d11ac7eSGreg Roach     * installation statistics which can be found at http://dev.webtrees.net/statistics.html
900d11ac7eSGreg Roach     *
910d11ac7eSGreg Roach     * @return string
920d11ac7eSGreg Roach     */
930d11ac7eSGreg Roach    private function fetchLatestVersion(): string
940d11ac7eSGreg Roach    {
950d11ac7eSGreg Roach        $last_update_timestamp = (int) Site::getPreference('LATEST_WT_VERSION_TIMESTAMP');
960d11ac7eSGreg Roach
970d11ac7eSGreg Roach        if ($last_update_timestamp < WT_TIMESTAMP - self::CHECK_FOR_UPDATE_INTERVAL) {
980d11ac7eSGreg Roach            try {
990d11ac7eSGreg Roach                $client = new Client([
1000d11ac7eSGreg Roach                    'timeout' => self::HTTP_TIMEOUT,
1010d11ac7eSGreg Roach                ]);
1020d11ac7eSGreg Roach
1030d11ac7eSGreg Roach                $response = $client->get(self::UPDATE_URL, [
1040d11ac7eSGreg Roach                    'query' => $this->serverParameters(),
1050d11ac7eSGreg Roach                ]);
1060d11ac7eSGreg Roach
1070d11ac7eSGreg Roach                if ($response->getStatusCode() === Response::HTTP_OK) {
1080d11ac7eSGreg Roach                    Site::setPreference('LATEST_WT_VERSION', $response->getBody()->getContents());
109ebf05b8eSGreg Roach                    Site::setPreference('LATEST_WT_VERSION_TIMESTAMP', (string) WT_TIMESTAMP);
1100d11ac7eSGreg Roach                }
1110d11ac7eSGreg Roach            } catch (RequestException $ex) {
1120d11ac7eSGreg Roach                DebugBar::addThrowable($ex);
1130d11ac7eSGreg Roach            }
1140d11ac7eSGreg Roach        }
1150d11ac7eSGreg Roach
1160d11ac7eSGreg Roach        return Site::getPreference('LATEST_WT_VERSION');
1170d11ac7eSGreg Roach    }
1180d11ac7eSGreg Roach
1190d11ac7eSGreg Roach    /**
1200d11ac7eSGreg Roach     * The upgrade server needs to know a little about this server.
1210d11ac7eSGreg Roach     */
1228f53f488SRico Sonntag    private function serverParameters(): array
1230d11ac7eSGreg Roach    {
1240d11ac7eSGreg Roach        $mysql_version = Database::prepare("SHOW VARIABLES LIKE 'version'")->fetchOneRow();
1250d11ac7eSGreg Roach
1260d11ac7eSGreg Roach        $operating_system = DIRECTORY_SEPARATOR === '/' ? 'u' : 'w';
1270d11ac7eSGreg Roach
1280d11ac7eSGreg Roach        return [
1298d0ebef0SGreg Roach            'w' => Webtrees::VERSION,
1300d11ac7eSGreg Roach            'p' => PHP_VERSION,
1310d11ac7eSGreg Roach            'm' => $mysql_version->value,
1320d11ac7eSGreg Roach            'o' => $operating_system,
1330d11ac7eSGreg Roach        ];
1340d11ac7eSGreg Roach    }
1350d11ac7eSGreg Roach}
136