padraic/phar-updater

该软件包已被废弃,不再维护。未建议替代包。

使PHAR自我更新变得简单且安全。

v1.0.6 2018-03-30 12:52 UTC

README

Build Status Scrutinizer Code Quality StyleCI Total Downloads

您有一个要分发的PHAR文件,包含自我更新命令已成为一种潮流。您真的需要编写它吗?在这里,Humbug中央的十名小兵(所有十名)已经为您编写了一个。

目录

简介

padraic/phar-updater 软件包具有以下功能:

  • 完全支持SSL/TLS验证。
  • 支持OpenSSL phar签名。
  • 简单的API,要么更新,要么抛出异常。
  • 支持SHA-1版本同步和Github发布作为更新策略。

除了下面的详细文档之外,您可以在Humbug的自我更新命令中找到该软件包的使用方法,作为基于Symfony Console的PHAR的一部分,您可以自由重用。

安装

composer require padraic/phar-updater

该软件包使用PHP流进行远程请求,因此需要启用openssl扩展和allow_url_open设置。curl支持将在以后实现。

使用方法

默认更新策略使用当前远程PHAR的版本文件的SHA-1哈希,当版本更改时将更新本地PHAR。还有一个Github策略,它跟踪Github发布,您可以上传新的PHAR文件以发布。

基本的SHA-1策略

创建您的自我更新命令,或者甚至是当前PHAR以外的其他PHAR的更新命令,并包含此命令。

/**
 * The simplest usage assumes the currently running phar is to be updated and
 * that it has been signed with a private key (using OpenSSL).
 *
 * The first constructor parameter is the path to a phar if you are not updating
 * the currently running phar.
 */

use Humbug\SelfUpdate\Updater;

$updater = new Updater();
$updater->getStrategy()->setPharUrl('https://example.com/current.phar');
$updater->getStrategy()->setVersionUrl('https://example.com/current.version');
try {
    $result = $updater->update();
    echo $result ? "Updated!\n" : "No update needed!\n";
} catch (\Exception $e) {
    echo "Well, something happened! Either an oopsie or something involving hackers.\n";
    exit(1);
}

如果您不使用OpenSSL签名PHAR

/**
 * The second parameter to the constructor must be false if your phars are
 * not signed using OpenSSL.
 */

use Humbug\SelfUpdate\Updater;

$updater = new Updater(null, false);
$updater->getStrategy()->setPharUrl('https://example.com/current.phar');
$updater->getStrategy()->setVersionUrl('https://example.com/current.version');
try {
    $result = $updater->update();
    echo $result ? "Updated!\n" : "No update needed!\n";
} catch (\Exception $e) {
    echo "Well, something happened! Either an oopsie or something involving hackers.\n";
    exit(1);
}

如果您需要版本信息

use Humbug\SelfUpdate\Updater;

$updater = new Updater();
$updater->getStrategy()->setPharUrl('https://example.com/current.phar');
$updater->getStrategy()->setVersionUrl('https://example.com/current.version');
try {
    $result = $updater->update();
    if ($result) {
        $new = $updater->getNewVersion();
        $old = $updater->getOldVersion();
        printf(
            'Updated from SHA-1 %s to SHA-1 %s', $old, $new
        );
    } else {
        echo "No update needed!\n";
    }
} catch (\Exception $e) {
    echo "Well, something happened! Either an oopsie or something involving hackers.\n";
    exit(1);
}

有关如何设置SHA-1策略的概述,请参阅更新策略部分。它是维护开发或夜间版本PHAR的简单选择,这些PHAR发布到特定的编号版本。

Github发布策略

除了开发或夜间PHAR之外,如果您在Github上发布编号版本(即标签),您可以上传额外的文件(如PHAR)以包含在Github发布中。

/**
 * Other than somewhat different setters for the strategy, all other operations
 * are identical.
 */

use Humbug\SelfUpdate\Updater;

$updater = new Updater();
$updater->setStrategy(Updater::STRATEGY_GITHUB);
$updater->getStrategy()->setPackageName('myvendor/myapp');
$updater->getStrategy()->setPharName('myapp.phar');
$updater->getStrategy()->setCurrentLocalVersion('v1.0.1');
try {
    $result = $updater->update();
    echo $result ? "Updated!\n" : "No update needed!\n";
} catch (\Exception $e) {
    echo "Well, something happened! Either an oopsie or something involving hackers.\n";
    exit(1);
}

软件包名称是指Packagist使用的名称,而PHAR名称是假设在版本之间恒定的PHAR文件名。

当前发布版本与本地phar的关联由实现来提供。这需要在phar中存储,并且应与GitHub使用的版本字符串相匹配。这可以遵循任何具有可识别的前缀和后缀的标准实践,例如v1.0.31.0.31.11.3rc1.3.2pl2

如果您希望更新到非稳定版本,例如用户想要根据开发轨道更新时,您可以设置GitHub策略的不稳定标志。默认情况下,这设置为stable或常量形式\Humbug\SelfUpdate\Strategy\GithubStrategy::STABLE

$updater->getStrategy()->setStability('unstable');

如果您想忽略稳定性,只更新到最新版本

$updater->getStrategy()->setStability('any');

回滚支持

更新器会自动将原始phar的备份复制到myname-old.phar。您可以使用这种约定轻松触发回滚

use Humbug\SelfUpdate\Updater;

/**
 * Same constructor parameters as you would use for updating. Here, just defaults.
 */
$updater = new Updater();
try {
    $result = $updater->rollback();
    if (!$result) {
        echo "Failure!\n";
        exit(1);
    }
    echo "Success!\n";
} catch (\Exception $e) {
    echo "Well, something happened! Either an oopsie or something involving hackers.\n";
    exit(1);
}

由于用户可能有不同的命名和定位备份的要求,您可以在更新当前phar时显式管理备份应写入的精确路径,或使用setBackupPath()函数在触发回滚之前读取,或使用setRestorePath()。这些将代替简单的内置约定。

构造函数参数

更新器的构造函数相当简单。三种基本变化是

/**
 * Default: Update currently running phar which has been signed.
 */
$updater = new Updater;
/**
 * Update currently running phar which has NOT been signed.
 */
$updater = new Updater(null, false);
/**
 * Use a strategy other than the default SHA Hash.
 */
$updater = new Updater(null, false, Updater::STRATEGY_GITHUB);
/**
 * Update a different phar which has NOT been signed.
 */
$updater = new Updater('/path/to/impersonatephil.phar', false);

检查更新

您可以通过使用hasUpdate方法告诉用户有哪些更新可用,跨越任何稳定性轨道。这将获取最新远程版本,并将其与当前版本进行比较,并返回一个简单的true/false结果,即它仅在本地版本相同或根本不存在该稳定性级别的远程版本时为false。您可以通过新版本是一个字符串来区分这两种false状态,其中确实存在版本,但如果不存在则返回false

use Humbug\SelfUpdate\Updater;

/**
 * Configuration is identical in every way for actual updates. You can run this
 * across multiple configuration variants to get recent stable, unstable, and dev
 * versions available.
 *
 * This would configure update for an unsigned phar (second constructor must be
 * false in this case).
 */
$updater = new Updater(null, false);
$updater->setStrategy(Updater::STRATEGY_GITHUB);
$updater->getStrategy()->setPackageName('myvendor/myapp');
$updater->getStrategy()->setPharName('myapp.phar');
$updater->getStrategy()->setCurrentLocalVersion('v1.0.1');

try {
    $result = $updater->hasUpdate();
    if ($result) {
        printf(
            'The current stable build available remotely is: %s',
            $updater->getNewVersion()
        );
    } elseif (false === $updater->getNewVersion()) {
        echo "There are no stable builds available.\n";
    } else {
        echo "You have the current stable build installed.\n";
    }
} catch (\Exception $e) {
    echo "Well, something happened! Either an oopsie or something involving hackers.\n";
    exit(1);
}

避免更新后文件包含

更新正在运行的phar变得更加复杂,因为一旦替换,尝试从较旧的phar中加载该进程内的文件很可能导致内部phar损坏错误。例如,如果您使用Symfony Console并为您自己的命令创建了一个事件调度器,某些事件类的延迟加载将产生这种影响。

解决方案是禁用或删除您self-update命令的调度器。

通常,当编写您的self-update CLI命令时,要么预先加载更新前可能需要的任何类,要么如果它们不是必需的则禁用它们的加载。

自定义更新策略

所有更新策略都围绕检查更新和下载更新展开。替换本地文件和备份的实际工作由单独处理。要创建自定义策略,您可以实现Humbug\SelfUpdate\Strategy\StrategyInterface,并在构造后传递您实现的新实例。

$updater = new Updater(null, false);
$updater->setStrategyObject(new MyStrategy);

类似的setStrategy()方法仅用于传递与内部策略匹配的标志。

更新策略

SHA-1哈希同步

phar-updater包仅(将会改变!)支持一种更新策略,即根据远程可用的当前phar文件的SHA-1哈希值来更新phar。这假设只有两个到三个远程文件存在。

  • myname.phar
  • myname.version
  • myname.phar.pubkey(可选)

myname.phar是最近构建的phar。

myname.version包含最近构建的phar的SHA-1哈希值,其中哈希值是文件中的第一个字符串(如果不是唯一的字符串)。您可以使用以下bash命令轻松生成:

sha1sum myname.phar > myname.version

请记住,对于您想要分发的每个新的phar构建,都要重新生成版本文件。使用sha1sum会在哈希值之后添加额外的数据,但这没关系,因为哈希值是文件中的第一个字符串,这是唯一的要求。

如果您使用非常推荐的OpenSSL签名,您也可以将公钥放在网上作为myname.phar.pubkey,用于phar的初始安装。但是,请注意,phar-updater本身永远不会下载此密钥,永远不会在您的文件系统中替换此密钥,也永远不会安装无法由本地缓存的公钥验证签名的phar。

如果您需要出于任何原因更换密钥,用户将需要手动下载新的phar以及新的密钥。虽然这听起来有些极端,但通常不允许在用户不知情的情况下任意更改密钥并不是一个好主意。OpenSSL签名没有类似于中央权威或浏览器受信任证书存储的机制,可以安全地自动化此类密钥更改。

Github Releases

在Github上标记新版本时,这些版本会创建并托管为Github Releases,允许您附加更改日志和附加文件下载。使用此Github功能允许您将新的phar附加到版本中,将其与在Packagist上发布的版本字符串相关联。

利用这种架构,Github更新phar的策略可以比较现有的本地phar版本与远程版本,并从Github更新到最新稳定(或不稳定)版本。

目前假设phar文件在所有版本中具有相同的名称,即仅是一个简单的名称,如myapp.phar,文件名中不包含版本信息。您也可以以相同的方式上传您的phar的公钥。使用已建立的约定,即phar名称后附加.pubkey,例如myapp.phar将与myapp.phar.pubkey匹配。

您可以在这里了解更多关于Github版本的信息。

虽然您可以起草一个版本,但每当您创建一个新的git标签时,Github版本都会自动创建。如果您使用git标签,您可以去对应的版本,点击编辑按钮并附加文件。建议在标记后尽快这样做,以限制新版本存在而没有附加更新phar的时间窗口。