284 lines
10 KiB
PHP
284 lines
10 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Copyright since 2007 PrestaShop SA and Contributors
|
|
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
|
*
|
|
* NOTICE OF LICENSE
|
|
*
|
|
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
|
|
* that is bundled with this package in the file LICENSE.md.
|
|
* It is also available through the world-wide-web at this URL:
|
|
* https://opensource.org/licenses/AFL-3.0
|
|
* If you did not receive a copy of the license and are unable to
|
|
* obtain it through the world-wide-web, please send an email
|
|
* to license@prestashop.com so we can send you a copy immediately.
|
|
*
|
|
* DISCLAIMER
|
|
*
|
|
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
|
|
* versions in the future. If you wish to customize PrestaShop for your
|
|
* needs please refer to https://devdocs.prestashop.com/ for more information.
|
|
*
|
|
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
|
* @copyright Since 2007 PrestaShop SA and Contributors
|
|
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
|
|
*/
|
|
|
|
namespace PrestaShop\Module\AutoUpgrade;
|
|
|
|
use PrestaShop\Module\AutoUpgrade\Xml\FileLoader;
|
|
use SimpleXMLElement;
|
|
use Symfony\Component\Filesystem\Exception\IOException;
|
|
use Symfony\Component\Filesystem\Filesystem;
|
|
|
|
class Upgrader
|
|
{
|
|
const DEFAULT_CHECK_VERSION_DELAY_HOURS = 12;
|
|
const DEFAULT_CHANNEL = 'minor';
|
|
const DEFAULT_FILENAME = 'prestashop.zip';
|
|
|
|
const ADDONS_API_URL = 'api.addons.prestashop.com';
|
|
|
|
/**
|
|
* @var bool contains true if last version is not installed
|
|
*/
|
|
private $need_upgrade = false;
|
|
|
|
/** @var string */
|
|
public $version_name;
|
|
/** @var ?string */
|
|
public $version_num;
|
|
/**
|
|
* @var string contains the url to download the file
|
|
*/
|
|
public $link;
|
|
/** @var string */
|
|
public $autoupgrade_last_version;
|
|
/** @var string */
|
|
public $autoupgrade_module_link;
|
|
/** @var string */
|
|
public $changelog;
|
|
/** @var bool */
|
|
public $available;
|
|
/** @var string */
|
|
public $md5;
|
|
|
|
/** @var string */
|
|
public $channel = '';
|
|
/** @var string */
|
|
public $branch = '';
|
|
|
|
/** @var string */
|
|
protected $currentPsVersion;
|
|
/**
|
|
* @var FileLoader
|
|
*/
|
|
protected $fileLoader;
|
|
|
|
public function __construct(string $version, FileLoader $fileLoader)
|
|
{
|
|
$this->currentPsVersion = $version;
|
|
$this->fileLoader = $fileLoader;
|
|
}
|
|
|
|
/**
|
|
* downloadLast download the last version of PrestaShop and save it in $dest/$filename.
|
|
*
|
|
* @param string $dest directory where to save the file
|
|
* @param string $filename new filename
|
|
*
|
|
* @TODO ftp if copy is not possible (safe_mode for example)
|
|
*/
|
|
public function downloadLast(string $dest, string $filename = 'prestashop.zip'): bool
|
|
{
|
|
if (empty($this->link)) {
|
|
$this->checkPSVersion();
|
|
}
|
|
|
|
$destPath = realpath($dest) . DIRECTORY_SEPARATOR . $filename;
|
|
|
|
try {
|
|
$filesystem = new Filesystem();
|
|
$filesystem->copy($this->link, $destPath);
|
|
} catch (IOException $e) {
|
|
// If the Symfony filesystem failed, we can try with
|
|
// the legacy method which uses curl.
|
|
Tools14::copy($this->link, $destPath);
|
|
}
|
|
|
|
return is_file($destPath);
|
|
}
|
|
|
|
public function isLastVersion(): bool
|
|
{
|
|
if (empty($this->link)) {
|
|
$this->checkPSVersion();
|
|
}
|
|
|
|
return !$this->need_upgrade;
|
|
}
|
|
|
|
/**
|
|
* checkPSVersion ask to prestashop.com if there is a new version. return an array if yes, false otherwise.
|
|
*
|
|
* @param bool $refresh if set to true, will force to download channel.xml
|
|
* @param string[] $array_no_major array of channels which will return only the immediate next version number
|
|
*
|
|
* @return array{'name':string,'link':string}|false
|
|
*/
|
|
public function checkPSVersion(bool $refresh = false, array $array_no_major = ['minor'])
|
|
{
|
|
// if we use the autoupgrade process, we will never refresh it
|
|
// except if no check has been done before
|
|
$feed = $this->fileLoader->getXmlChannel($refresh);
|
|
|
|
// channel hierarchy :
|
|
// if you follow private, you follow stable release
|
|
// if you follow rc, you also follow stable
|
|
// if you follow beta, you also follow rc
|
|
// et caetera
|
|
$followed_channels = [];
|
|
$followed_channels[] = $this->channel;
|
|
switch ($this->channel) {
|
|
case 'alpha':
|
|
$followed_channels[] = 'beta';
|
|
// no break
|
|
case 'beta':
|
|
$followed_channels[] = 'rc';
|
|
// no break
|
|
case 'rc':
|
|
$followed_channels[] = 'stable';
|
|
// no break
|
|
case 'minor':
|
|
case 'major':
|
|
case 'private':
|
|
$followed_channels[] = 'stable';
|
|
}
|
|
|
|
if ($feed) {
|
|
$this->autoupgrade_last_version = (string) $feed->autoupgrade->last_version;
|
|
$this->autoupgrade_module_link = (string) $feed->autoupgrade->download->link;
|
|
|
|
foreach ($feed->channel as $channel) {
|
|
$channel_available = (string) $channel['available'];
|
|
|
|
$channel_name = (string) $channel['name'];
|
|
// stable means major and minor
|
|
// boolean algebra
|
|
// skip if one of theses props are true:
|
|
// - "stable" in xml, "minor" or "major" in configuration
|
|
// - channel in xml is not channel in configuration
|
|
if (!(in_array($channel_name, $followed_channels))) {
|
|
continue;
|
|
}
|
|
// now we are on the correct channel (minor, major, ...)
|
|
foreach ($channel as $branch) {
|
|
// branch name = which version
|
|
$branch_name = (string) $branch['name'];
|
|
// if channel is "minor" in configuration, do not allow something else than current branch
|
|
// otherwise, allow superior or equal
|
|
if (
|
|
(in_array($this->channel, $followed_channels)
|
|
&& version_compare($branch_name, $this->branch, '>='))
|
|
) {
|
|
// skip if $branch->num is inferior to a previous one, skip it
|
|
if ($this->version_num !== null && version_compare((string) $branch->num, $this->version_num, '<')) {
|
|
continue;
|
|
}
|
|
// also skip if previous loop found an available upgrade and current is not
|
|
if ($this->available && !($channel_available && (string) $branch['available'])) {
|
|
continue;
|
|
}
|
|
// also skip if chosen channel is minor, and xml branch name is superior to current
|
|
if (in_array($this->channel, $array_no_major) && version_compare($branch_name, $this->branch, '>')) {
|
|
continue;
|
|
}
|
|
$this->version_name = (string) $branch->name;
|
|
$this->version_num = (string) $branch->num;
|
|
$this->link = (string) $branch->download->link;
|
|
$this->md5 = (string) $branch->download->md5;
|
|
$this->changelog = (string) $branch->changelog;
|
|
if (extension_loaded('openssl')) {
|
|
$this->link = str_replace('http://', 'https://', $this->link);
|
|
$this->changelog = str_replace('http://', 'https://', $this->changelog);
|
|
}
|
|
$this->available = $channel_available && (string) $branch['available'];
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
// retro-compatibility :
|
|
// return array(name,link) if you don't use the last version
|
|
// false otherwise
|
|
if ($this->version_num !== null && version_compare($this->currentPsVersion, $this->version_num, '<')) {
|
|
$this->need_upgrade = true;
|
|
|
|
return ['name' => $this->version_name, 'link' => $this->link];
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* delete the file /config/xml/$version.xml if exists.
|
|
*/
|
|
public function clearXmlMd5File(string $version): bool
|
|
{
|
|
if (file_exists(_PS_ROOT_DIR_ . '/config/xml/' . $version . '.xml')) {
|
|
return unlink(_PS_ROOT_DIR_ . '/config/xml/' . $version . '.xml');
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* use the addons api to get xml files.
|
|
*
|
|
* @param string $postData
|
|
*
|
|
* @return SimpleXMLElement|false
|
|
*/
|
|
public function getApiAddons(string $xml_localfile, string $postData, bool $refresh = false)
|
|
{
|
|
if (!is_dir(_PS_ROOT_DIR_ . '/config/xml')) {
|
|
if (is_file(_PS_ROOT_DIR_ . '/config/xml')) {
|
|
unlink(_PS_ROOT_DIR_ . '/config/xml');
|
|
}
|
|
mkdir(_PS_ROOT_DIR_ . '/config/xml');
|
|
}
|
|
if ($refresh || !file_exists($xml_localfile) || @filemtime($xml_localfile) < (time() - (3600 * self::DEFAULT_CHECK_VERSION_DELAY_HOURS))) {
|
|
$protocolsList = ['https://' => 443, 'http://' => 80];
|
|
if (!extension_loaded('openssl')) {
|
|
unset($protocolsList['https://']);
|
|
}
|
|
// Make the request
|
|
$opts = [
|
|
'http' => [
|
|
'method' => 'POST',
|
|
'content' => $postData,
|
|
'header' => 'Content-type: application/x-www-form-urlencoded',
|
|
'timeout' => 10,
|
|
], ];
|
|
$context = stream_context_create($opts);
|
|
$xml = false;
|
|
foreach ($protocolsList as $protocol => $port) {
|
|
$xml_string = Tools14::file_get_contents($protocol . self::ADDONS_API_URL, false, $context);
|
|
if ($xml_string) {
|
|
$xml = @simplexml_load_string($xml_string);
|
|
break;
|
|
}
|
|
}
|
|
if ($xml !== false) {
|
|
file_put_contents($xml_localfile, $xml_string);
|
|
}
|
|
} else {
|
|
$xml = @simplexml_load_file($xml_localfile);
|
|
}
|
|
|
|
return $xml;
|
|
}
|
|
}
|