Updates the pharext code within an .ext.phar without the real need of re-packaging.
Usage:
$ ./bin/pharext.update [-hvq] [--] <path ...>
-h|--help Display this help
-v|--verbose More output
-q|--quiet Less output
--signature Show pharext signature
--license Show pharext license
--version Show pharext version
-- path Path to .ext.phar to update (REQUIRED) (MULTIPLE)
*.phar
*.phar.gz
*.phar.bz2
-build/pharext.key
+build/*.key
*.phar*
*.pubkey
# build bin/pharext
#
-all: bin/pharext
+all: bin/pharext bin/pharext.update
-bin/pharext: src/* src/pharext/* src/pharext/*/* src/pharext/*/*/*
+bin/%: build/%.php src/* src/pharext/* src/pharext/*/* src/pharext/*/*/*
@echo "Linting changed source files ... "
@for file in $?; do php -l $$file | sed -ne '/^No syntax errors/!p' && exit $${PIPESTATUS[0]}; done
- @echo "Creating bin/pharext ... "
- php -d phar.readonly=0 build/create-phar.php
+ @echo "Creating $@ ... "
+ php -d phar.readonly=0 $<
test:
@echo "Running tests ... "
--- /dev/null
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0tzV2ptX5/W1qRb+jMzo
+2AMOoih9nHw6Psd53iKVuNidLtriX/ujqJdcoHuQhn+t5AZkiEJm4susZvRGWiub
+0VNzhq4k+whsJXOAkdoqWwyxd0KmcL3mWnvb52sVOj+pLn0NkKI7gwJ56pP/lHNA
+Zd73LMNUUuw/bT2po4jts4UqVuRr50xU7SVUbQxZ/nbwKi1T62+7gehRBKScskAL
+Wuya8KCTRMcskRUiOEQc9FRSxaQL4ULyPO3sYwxRhWtuDDdSjiMZUzIRDjl1SbS2
+n+mipwyi7DBLv15qfXucq5fRmUUKBEEI9ggihCtRiW49A1SJK2Bra4V1PjqZhhyq
+6QIDAQAB
+-----END PUBLIC KEY-----
+++ /dev/null
-<?php
-
-/**
- * Creates bin/pharext, invoked through the Makefile
- */
-
-set_include_path(dirname(__DIR__)."/src:".get_include_path());
-spl_autoload_register(function($c) {
- return include strtr($c, "\\_", "//") . ".php";
-});
-
-$file = (new pharext\Task\PharBuild(null, __DIR__."/../src/pharext_packager.php", pharext\Metadata::all() + [
- "name" => "pharext",
- "license" => file_get_contents(__DIR__."/../LICENSE")
-], false))->run();
-
-if (getenv("SIGN")) {
- $pass = (new pharext\Task\Askpass)->run();
- $sign = new pharext\Task\PharSign($file, __DIR__."/pharext.key", $pass);
- $pkey = $sign->run();
- $pkey->exportPublicKey(__DIR__."/../bin/pharext.pubkey");
-}
-
-/* we do not need the extra logic of Task\PharRename */
-rename($file, __DIR__."/../bin/pharext");
--- /dev/null
+<?php
+
+/**
+ * Creates bin/pharext, invoked through the Makefile
+ */
+
+set_include_path(dirname(__DIR__)."/src:".get_include_path());
+spl_autoload_register(function($c) {
+ return include strtr($c, "\\_", "//") . ".php";
+});
+
+$file = (new pharext\Task\PharBuild(null, __DIR__."/../src/pharext_packager.php", pharext\Metadata::all() + [
+ "name" => "pharext",
+ "license" => file_get_contents(__DIR__."/../LICENSE")
+], false))->run();
+
+if (getenv("SIGN")) {
+ $pass = (new pharext\Task\Askpass)->run();
+ $sign = new pharext\Task\PharSign($file, __DIR__."/pharext.key", $pass);
+ $pkey = $sign->run();
+ $pkey->exportPublicKey(__DIR__."/../bin/pharext.pubkey");
+}
+
+/* we do not need the extra logic of Task\PharRename */
+rename($file, __DIR__."/../bin/pharext");
--- /dev/null
+<?php
+
+/**
+ * Creates bin/pharext.update, invoked through the Makefile
+ */
+
+set_include_path(dirname(__DIR__)."/src:".get_include_path());
+spl_autoload_register(function($c) {
+ return include strtr($c, "\\_", "//") . ".php";
+});
+
+$file = (new pharext\Task\PharBuild(null, __DIR__."/../src/pharext_updater.php", pharext\Metadata::all() + [
+ "name" => "pharext.update",
+ "license" => file_get_contents(__DIR__."/../LICENSE")
+], false))->run();
+
+if (getenv("SIGN")) {
+ $pass = (new pharext\Task\Askpass)->run();
+ $sign = new pharext\Task\PharSign($file, __DIR__."/pharext.update.key", $pass);
+ $pkey = $sign->run();
+ $pkey->exportPublicKey(__DIR__."/../bin/pharext.update.pubkey");
+}
+
+/* we do not need the extra logic of Task\PharRename */
+rename($file, __DIR__."/../bin/pharext.update");
"keywords": ["ext", "extension", "phar", "package", "install"],
"type": "project",
"license": "BSD-2-Clause",
- "bin": ["bin/pharext", "bin/pharext.pubkey"]
+ "bin": [
+ "bin/pharext",
+ "bin/pharext.pubkey",
+ "bin/pharext.update",
+ "bin/pharext.update.key"
+ ]
}
class Installer implements Command
{
use CliCommand;
-
+
/**
* Cleanups
* @var array
*/
private $cleanup = [];
-
+
/**
* Create the command
*/
public function __construct() {
$this->args = new CliArgs([
- ["h", "help", "Display help",
+ ["h", "help", "Display help",
CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT],
["v", "verbose", "More output",
CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG],
["p", "prefix", "PHP installation prefix if phpize is not in \$PATH, e.g. /opt/php7",
CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG],
["n", "common-name", "PHP common program name, e.g. php5 or zts-php",
- CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG,
+ CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG,
"php"],
["c", "configure", "Additional extension configure flags, e.g. -c --with-flag",
CliArgs::OPTIONAL|CliArgs::MULTI|CliArgs::REQARG],
CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT],
]);
}
-
+
/**
* Perform cleaniup
*/
$cleanup->run();
}
}
-
+
private function extract($phar) {
$temp = (new Task\Extract($phar))->run($this->verbosity());
$this->cleanup[] = new Task\Cleanup($temp);
return $temp;
}
-
+
private function hooks(SplObjectStorage $phars) {
$hook = [];
foreach ($phars as $phar) {
private function load() {
$list = new SplObjectStorage();
- $phar = extension_loaded("Phar")
+ $phar = extension_loaded("Phar")
? new Phar(Phar::running(false))
: new Archive(PHAREXT_PHAR);
$temp = $this->extract($phar);
$list[$dep_phar] = $this->extract($dep_phar);
}
}
-
+
/* the actual ext.phar at last */
$list[$phar] = $temp;
return $list;
exit(self::EINSTALL);
}
}
-
+
/**
* Phpize + trinity
*/
$phpize->run($this->verbosity());
// configure
- $configure = new Task\Configure($temp, $this->args->configure, $this->args->prefix, $this->args{"common-name"});
+ $configure = new Task\Configure($temp, $this->args->configure, $this->args->prefix, $this->args->{"common-name"});
$configure->run($this->verbosity());
// make
$sudo = isset($this->args->sudo) ? $this->args->sudo : null;
$type = $this->metadata("type") ?: "extension";
-
+
$activate = new Task\Activate($temp, $files, $type, $this->args->prefix, $this->args{"common-name"}, $sudo);
if (!$activate->run($this->verbosity())) {
$this->info("Extension already activated ...\n");
if ($this->meta) {
$phar->setMetadata($this->meta);
}
- if (is_file($this->stub)) {
- $stub = preg_replace_callback('/^#include <([^>]+)>/m', function($includes) {
- return file_get_contents($includes[1], true, null, 5);
- }, file_get_contents($this->stub));
- $phar->setStub($stub);
+ if ($this->stub) {
+ (new PharStub($phar, $this->stub))->run($verbose);
}
$phar->buildFromIterator((new Task\BundleGenerator)->run());
--- /dev/null
+<?php
+
+namespace pharext\Task;
+
+use Phar;
+use pharext\Exception;
+use pharext\Task;
+
+/**
+ * Set the phar's stub
+ */
+class PharStub implements Task
+{
+ /**
+ * @var \Phar
+ */
+ private $phar;
+
+ /**
+ * @var string
+ */
+ private $stub;
+
+ /**
+ * @param \Phar $phar
+ * @param string $stub file path to the stub
+ * @throws \pharext\Exception
+ */
+ function __construct(Phar $phar, $stub) {
+ $this->phar = $phar;
+ if (!file_exists($this->stub = $stub)) {
+ throw new Exception("File '$stub' does not exist");
+ }
+ }
+
+ /**
+ * @param bool $verbose
+ */
+ function run($verbose = false) {
+ if ($verbose) {
+ printf("Using stub '%s'...\n", basename($this->stub));
+ }
+ $stub = preg_replace_callback('/^#include <([^>]+)>/m', function($includes) {
+ return file_get_contents($includes[1], true, null, 5);
+ }, file_get_contents($this->stub));
+ if ($this->phar->isCompressed() && substr($stub, 0, 2) === "#!") {
+ $stub = substr($stub, strpos($stub, "\n")+1);
+ }
+ $this->phar->setStub($stub);
+ }
+}
--- /dev/null
+<?php
+
+namespace pharext;
+
+use Phar;
+use PharFileInfo;
+use RecursiveIteratorIterator;
+use SplFileInfo;
+
+class Updater implements Command
+{
+ use Cli\Command;
+
+ /**
+ * Create the command
+ */
+ public function __construct() {
+ $this->args = new Cli\Args([
+ ["h", "help", "Display this help",
+ Cli\Args::OPTIONAL|Cli\Args::SINGLE|Cli\Args::NOARG|Cli\Args::HALT],
+ ["v", "verbose", "More output",
+ Cli\Args::OPTIONAL|Cli\Args::SINGLE|Cli\Args::NOARG],
+ ["q", "quiet", "Less output",
+ Cli\Args::OPTIONAL|Cli\Args::SINGLE|Cli\Args::NOARG],
+ [null, "signature", "Show pharext signature",
+ Cli\Args::OPTIONAL|Cli\Args::SINGLE|Cli\Args::NOARG|Cli\Args::HALT],
+ [null, "license", "Show pharext license",
+ Cli\Args::OPTIONAL|Cli\Args::SINGLE|Cli\Args::NOARG|Cli\Args::HALT],
+ [null, "version", "Show pharext version",
+ Cli\Args::OPTIONAL|Cli\Args::SINGLE|Cli\Args::NOARG|Cli\Args::HALT],
+ [0, "path", "Path to .ext.phar to update",
+ Cli\Args::REQUIRED|Cli\Args::MULTI],
+ ]);
+ }
+
+ /**
+ * @inheritdoc
+ * @see \pharext\Command::run()
+ */
+ public function run($argc, array $argv) {
+ $errs = [];
+ $prog = array_shift($argv);
+ foreach ($this->args->parse(--$argc, $argv) as $error) {
+ $errs[] = $error;
+ }
+
+ if ($this->args["help"]) {
+ $this->header();
+ $this->help($prog);
+ exit;
+ }
+
+ try {
+ foreach (["signature", "license", "version"] as $opt) {
+ if ($this->args[$opt]) {
+ printf("%s\n", $this->metadata($opt));
+ exit;
+ }
+ }
+ } catch (\Exception $e) {
+ $this->error("%s\n", $e->getMessage());
+ exit(self::EARGS);
+ }
+
+
+ foreach ($this->args->validate() as $error) {
+ $errs[] = $error;
+ }
+
+ if ($errs) {
+ if (!$this->args["quiet"]) {
+ $this->header();
+ }
+ foreach ($errs as $err) {
+ $this->error("%s\n", $err);
+ }
+ printf("\n");
+ if (!$this->args["quiet"]) {
+ $this->help($prog);
+ }
+ exit(self::EARGS);
+ }
+
+ foreach ($this->args[0] as $file) {
+ if (file_exists($file)) {
+ $this->updatePackage(new SplFileInfo($file));
+ } else {
+ $this->error("File '%s' does not exist\n", $file);
+ exit(self::EARGS);
+ }
+ }
+ }
+
+ private function replacePharext($temp) {
+ $phar = new Phar($temp, Phar::CURRENT_AS_SELF);
+ $phar->startBuffering();
+
+ // replace current pharext files
+ $core = (new Task\BundleGenerator)->run($this->verbosity());
+ $phar->buildFromIterator($core);
+ $stub = __DIR__."/../pharext_installer.php";
+ (new Task\PharStub($phar, $stub))->run($this->verbosity());
+
+ // check dependencies
+ foreach ($phar as $info) {
+ if (fnmatch("*.ext.phar*", $info->getBasename())) {
+ $this->updatePackage($info, $phar);
+ }
+ }
+
+ $phar->stopBuffering();
+ }
+
+ private function updatePackage(SplFileInfo $file, Phar $phar = null) {
+ $this->info("Updating pharext core in '%s'...\n", basename($file));
+
+ $temp = new Tempname("update", substr(strstr($file, ".ext.phar"), 4));
+
+ if (!copy($file->getPathname(), $temp)) {
+ throw new Exception;
+ }
+
+ $this->replacePharext($temp);
+
+ if ($phar) {
+ $phar->addFile($temp, $file);
+ } elseif (!rename($temp, $file->getPathname())) {
+ throw new Exception;
+ }
+ }
+}
--- /dev/null
+#!/usr/bin/php -dphar.readonly=0
+<?php
+
+/**
+ * The installer updater stub for extension phars
+ */
+
+namespace pharext;
+
+spl_autoload_register(function($c) {
+ return include strtr($c, "\\_", "//") . ".php";
+});
+
+set_include_path('phar://' . __FILE__ .":". get_include_path());
+
+if (!extension_loaded("Phar")) {
+ fprintf(STDERR, "ERROR: Phar extension not loaded\n\n");
+ fprintf(STDERR, "\tPlease load the phar extension in your php.ini\n".
+ "\tor rebuild PHP with the --enable-phar flag.\n\n");
+ exit(1);
+}
+
+if (ini_get("phar.readonly")) {
+ fprintf(STDERR, "ERROR: Phar is configured read-only\n\n");
+ fprintf(STDERR, "\tPlease specify phar.readonly=0 in your php.ini\n".
+ "\tor run this command with php -dphar.readonly=0\n\n");
+ exit(1);
+}
+
+\Phar::interceptFileFuncs();
+\Phar::mapPhar();
+
+$updater = new Updater();
+$updater->run($argc, $argv);
+
+__HALT_COMPILER();