herrera-io / box
Requires
- php: >=5.3.3
- ext-phar: *
- phine/path: ~1.0
- tedivm/jshrink: ~1.0
Requires (Dev)
- herrera-io/annotations: ~1.0
- herrera-io/phpunit-test-case: 1.*
- mikey179/vfsstream: 1.1.0
- phpseclib/phpseclib: ~0.3
- phpunit/phpunit: 3.7.*
Suggests
- herrera-io/annotations: For compacting annotated docblocks.
- phpseclib/phpseclib: For verifying OpenSSL signed phars without the phar extension.
README
Box 是基于 Phar
类的库。它旨在简化创建新 phar 和修改现有 phar 的过程。功能包括压缩源文件、更好的自定义存根生成和更好的 OpenSSL 签名处理。
示例
use Herrera\Box\Box; use Herrera\Box\StubGenerator; $box = Box::create('test.phar'); $box->buildFromDirectory('/path/to/dir'); $box->getPhar()->setStub( StubGenerator::create() ->index('path/to/script.php') ->generate() );
安装
将其添加到您的 Composer 依赖项列表中
$ composer require herrera-io/box=1.*
使用
Box 库包含许多功能,其中一些设计为可以独立使用。这样做是为了使 phar 构建器更好地控制 phar 构建过程。
压缩源文件
Box 使用源文件 "压缩器"。压缩器简单来说就是一个类,它会检查给定的文件是否受支持,然后操纵其内容以使其变小。我将在 最后构建 Phars 中介绍如何实际使用它们。
创建压缩器类有两种方式:实现 CompactorInterface
接口或扩展 Compactor
抽象类。
实现 CompactorInterface
CompactorInterface
接口只需要你在你的类中实现两个方法:compact($contents)
和 support($file)
。参数 $contents
是源文件的内容,参数 $file
是文件的完整路径。你如何确定哪些文件类型受支持完全取决于你自己。
在这个例子中,这个自定义压缩器将只修改以 .php
结尾的文件,并从每一行的末尾删除空白字符
namespace Example\Compactor; use Herrera\Box\Compactor\CompactorInterface; /** * My example compactor. */ class RemoveWhitespace implements CompactorInterface { /** * Seek and destroy (whitespaces). * * @param string $source The source code. * * @return string The compacted source code. */ public function compact($source) { return preg_replace('/[ \t]+$/m', '', $source); } /** * Make sure we support it. * * @param string $file The file path. * * @return boolean Returns TRUE if supported, FALSE if not. */ public function supports($file) { return ('php' === pathinfo($file, PATHINFO_EXTENSION)); } }
扩展 Compactor
包含了一个处理文件类型检查的抽象压缩器类。你只需要提供受支持的默认文件扩展列表。这些扩展可以在必要时由使用它们的开发者覆盖。
这个例子是之前示例压缩器的一个变体
namespace Example\Compactor; use Herrera\Box\Compactor\Compactor; /** * My example compactor. */ class RemoveWhitespace extends Compactor { /** * The default supported file extensions. * * @var array */ protected $extensions = array('php'); /** * Seek and destroy (whitespaces). * * @param string $source The source code. * * @return string The compacted source code. */ public function compact($source) { return preg_replace('/[ \t]+$/m', '', $source); } }
开发者可以通过调用 Compactor->setExtensions()
方法来修改支持的文件扩展名
$example = new Example\Compactor\RemoveWhitespace(); $example->setExtensions( array( 'inc', 'php' ) );
捆绑压缩器
该库捆绑了两个压缩器以方便使用。
压缩 JavaScript
JavaScript
压缩器会压缩 JavaScript 文件,但需要 tedivm/jshrink
包才能工作。当你安装 Box 库时,它会包括这个包。
use Herrera\Box\Compactor\Javascript; $compactor = new Javascript();
压缩JSON
JSON压缩器非常易于使用,因为没有配置选项。但是,需要安装json
扩展才能使用它。所有额外的空格都将从.json
文件中删除。
use Herrera\Box\Compactor\Json; $compactor = new Json();
压缩PHP
PHP压缩器将从.php
文件中删除所有注释和空白。被删除的注释将与原始注释相同的行数一起删除。这样做是为了在phar中发生错误时保留报告的行号。
use Herrera\Box\Compactor\Php; $compactor = new Php();
如果您使用Doctrine格式的注释,还可以在PHP压缩器中利用一个特殊功能。为了压缩注释并保留注释,您需要安装herrera-io/annotations
库并创建一个Tokenizer
实例。
use Herrera\Annotations\Tokenizer; $compactor->setTokenizer(new Tokenizer());
行数和注释数据都将保留。
管理签名
Phar
类提供了一种简单的方法来提取和验证phar的签名。只需实例化该类即可验证指定的phar。然而,执行这些任务需要phar
扩展。Box库包括了一种在不使用扩展的情况下提取和验证签名的方法。
use Herrera\Box\Exception\SignatureException; use Herrera\Box\Signature; $sig = new Signature('/path/to/my.phar'); // same output as Phar->getSignature() $signature = $sig->get(); try { // TRUE if passed, FALSE if failed $result = $sig->verify(); } catch (SignatureException $exception) { // the signature could not be verified }
Signature::create()
方法是对Signature::__construct()
的别名,允许使用上述示例的简写版本。
if (Signature::create('/path/to/my.phar')->verify()) { // do the do }
在没有扩展可用的情况下验证phar的能力,在本质上更为谨慎。在没有扩展的敏感环境中,开发人员或系统管理员在将phar部署到系统之前,可能想要验证其完整性。
提取Phar
除了能够在没有扩展的情况下验证phar的签名之外,您还可以提取其内容。此功能主要设计为作为自定义stub的一部分嵌入,但它也可以用于提取任何phar。
use Herrera\Box\Extract; $extract = new Extract('/path/to/my.phar', 65538); $extract->go('/path/to/extract/dir');
构造函数的第一个参数是现有phar的路径。第二个是stub的长度。第二个参数是必需的,以便Extract
类知道phar的清单开始的位置。通常,当使用默认stub时,该值由Phar
类生成。
如果该值未知,可以使用Extract::findStubLength()
方法调用Extract
类来做出最佳猜测。如果stub长度被错误地猜测,Extract
类将在提取过程中抛出异常。
默认情况下,Extract->go()
方法将创建一个临时目录路径,并将phar的内容提取到该目录。示例中指定的目录路径是可选的。
为了减少开销,如果目标目录中存在特殊文件,则Extract
类不会重新提取phar。这用于加快没有使用phar扩展执行的phar的执行过程。
请注意,如果phar中的任何文件使用gzip或bzip2进行了压缩,则需要相应的扩展进行解压缩。如果未安装所需的扩展,则Extract
类将抛出异常。
生成stub
如果项目适用,可以使用 Box 库生成自定义的桩。您将控制桩中的以下功能
- 设置别名。
- 设置“横幅”注释(例如版权声明)。
- 嵌入
Extract
类以支持自提取。 - 设置 CLI 索引脚本。
- 启用使用
Phar::interceptFileFuncs()
。 - 设置文件 MIME 类型。
- 将服务器变量列表设置为“mung”。
- 设置 404 脚本。
- 设置“shebang”行。
- 选择使用
Phar::webPhar()
而不是Phar::mapPhar()
。
以下是一个使用所有设置的桩生成示例
use Herrera\Box\StubGenerator; $generator = new StubGenerator(); $banner = <<<BANNER Copyright (c) 2013 Some Dude Some license text here. BANNER; $mimetypes = array( 'phps' => Phar::PHP ); $rewrite = <<<REWRITE function rewrite_url(\$uri) { return \$rewritten; } REWRITE; $stub = $generator ->alias('test.phar') ->banner($banner) ->extract(true) ->index('bin/cli.php') ->intercept(true) ->mimetypes($mimetypes) ->mung(array('REQUEST_URI')) ->notFound('lib/404.php') ->rewrite($rewrite) ->shebang('/home/dude/.local/php/bin/php') ->web(true) ->generate();
生成的桩如下
<?php /** * Copyright (c) 2013 Some Dude * * Some license text here. */ define('BOX_EXTRACT_PATTERN_DEFAULT', '__HALT' . '_COMPILER(); ?>'); define('BOX_EXTRACT_PATTERN_OPEN', "__HALT" . "_COMPILER(); ?>\r\n"); if (class_exists('Phar')) { Phar::webPhar('test.phar', 'bin/cli.php', 'lib/404.php', array ( 'phps' => 0, ), 'function rewrite_url($uri) { return $rewritten; }'); Phar::interceptFileFuncs(); Phar::mungServer(array ( 0 => 'REQUEST_URI', )); } else { $extract = new Extract(__FILE__, Extract::findStubLength(__FILE__)); $dir = $extract->go(); set_include_path($dir . PATH_SEPARATOR . get_include_path()); require "$dir/bin/cli.php"; } // ... snip ... __HALT_COMPILER();
为了简洁起见,嵌入的
Extract
类被替换为 "... 省略 ..."。
示例桩可能对你需要的来说有点过头。通过不使用 extract()
方法,你可以轻松地从桩中删除几百行代码,减小其大小,但你将失去在没有 phar
扩展的环境中执行 Phar 的能力。
最后,构建 Phar
所有这些功能都很棒,但它们在 Box
类中使用时更好。设计 Box
类是为了在(希望)简单的用户界面中自动集成所有这些功能。
有两种方式可以实例化该类
user Herrera\Box\Box; // use an existing Phar instance $box = new Box($phar); // or create one $box = Box::create('/path/to/my.phar');
请注意,
Box::create()
方法接受与Phar::__construct()
方法相同的参数。
注册压缩器
无论您是使用捆绑的压缩器还是您自己的压缩器,您都需要调用 Box->addCompactor()
方法将您的类注册到 Box
中。使用 Box
添加到 Phar 的所有文件都将自动通过支持的压缩器进行处理。
use Herrera\Box\Compactor\Json; use Herrera\Box\Compactor\Php; $box->addCompactor(new Json()); $box->addCompactor(new Php()); $box->addCompactor($custom);
使用占位符值
Box
类提供搜索和替换添加文件中占位符值的能力。请记住,任何替换都仅支持标量值。
$box->setValues( array( '@match@' => 'replace' ) );
使用上述值进行匹配,以下代码
$myCode = 'This @match@ is now "replace".';
将作为以下内容添加到 Phar 中
$myCode = 'This replace is now "replace".';
添加文件
要实际使用已注册的压缩器和设置占位符值替换,您需要使用 Box
类的添加文件方法。这些方法与 Phar
类的方法相同,但会自动应用适当的压缩器和替换
Box->addFile()
Box->addFromString()
Box->buildFromDirectory()
Box->buildFromIterator()
请注意,如果您需要添加不进行任何更改的文件(例如二进制文件),您可能需要使用 Phar
实例直接添加该文件。
$phar = $box->getPhar(); $phar->addFile('...');
设置桩
Box
类提供了一种简单的方法来添加从文件中获取的桩,并同时在桩上应用占位符替换
$box->setStubUsingFile('/path/to/stub.php', true);
第二个参数表示应该在桩上执行替换。如果您将其省略,它将默认为 false
,这意味着不会执行替换,桩将按原样使用。
私钥签名
Box
类提供了两种使用私钥签名phar文件的方法。然而,无论使用哪种方法,都需要有openssl
扩展可用。
使用字符串
如果您已经将私钥作为字符串变量加载,则可以使用Box->sign()
方法。
$box->sign($key, $pass);
如果私钥是用密码生成的,则需要提供密码。
使用文件
您还可以使用文件中包含的私钥来签名phar文件。
$box->signUsingFile($file, $pass);