add bin/pharext.update
authorMichael Wallner <mike@php.net>
Wed, 2 Sep 2015 15:01:37 +0000 (17:01 +0200)
committerMichael Wallner <mike@php.net>
Wed, 2 Sep 2015 15:08:11 +0000 (17:08 +0200)
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)

14 files changed:
.gitignore
Makefile
bin/pharext
bin/pharext.update [new file with mode: 0755]
bin/pharext.update.pubkey [new file with mode: 0644]
build/create-phar.php [deleted file]
build/pharext.php [new file with mode: 0644]
build/pharext.update.php [new file with mode: 0644]
composer.json
src/pharext/Installer.php
src/pharext/Task/PharBuild.php
src/pharext/Task/PharStub.php [new file with mode: 0644]
src/pharext/Updater.php [new file with mode: 0644]
src/pharext_updater.php [new file with mode: 0644]

index a660ac2d946787485d019c09514eccbdde5df3f3..e5416372137084f8c7e2af79e8bd49d2e29c83b8 100644 (file)
@@ -7,6 +7,6 @@ nbproject/
 *.phar
 *.phar.gz
 *.phar.bz2
-build/pharext.key
+build/*.key
 *.phar*
 *.pubkey
index 887b82d974f9a534bc7718cdb0fa9b3c86507f9a..4f0045a04607f91a7d4d3691c43625604d45af50 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,13 +2,13 @@
 # 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 ... "
index b464635584534dc679a2dcced3f27219c49fe2b5..766c44b56dc65f2553d2e97ba1de90aeca1936ab 100755 (executable)
Binary files a/bin/pharext and b/bin/pharext differ
diff --git a/bin/pharext.update b/bin/pharext.update
new file mode 100755 (executable)
index 0000000..a568e9d
Binary files /dev/null and b/bin/pharext.update differ
diff --git a/bin/pharext.update.pubkey b/bin/pharext.update.pubkey
new file mode 100644 (file)
index 0000000..7690045
--- /dev/null
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0tzV2ptX5/W1qRb+jMzo
+2AMOoih9nHw6Psd53iKVuNidLtriX/ujqJdcoHuQhn+t5AZkiEJm4susZvRGWiub
+0VNzhq4k+whsJXOAkdoqWwyxd0KmcL3mWnvb52sVOj+pLn0NkKI7gwJ56pP/lHNA
+Zd73LMNUUuw/bT2po4jts4UqVuRr50xU7SVUbQxZ/nbwKi1T62+7gehRBKScskAL
+Wuya8KCTRMcskRUiOEQc9FRSxaQL4ULyPO3sYwxRhWtuDDdSjiMZUzIRDjl1SbS2
+n+mipwyi7DBLv15qfXucq5fRmUUKBEEI9ggihCtRiW49A1SJK2Bra4V1PjqZhhyq
+6QIDAQAB
+-----END PUBLIC KEY-----
diff --git a/build/create-phar.php b/build/create-phar.php
deleted file mode 100644 (file)
index 26ac6e9..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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");
diff --git a/build/pharext.php b/build/pharext.php
new file mode 100644 (file)
index 0000000..26ac6e9
--- /dev/null
@@ -0,0 +1,25 @@
+<?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");
diff --git a/build/pharext.update.php b/build/pharext.update.php
new file mode 100644 (file)
index 0000000..d5f8fb9
--- /dev/null
@@ -0,0 +1,25 @@
+<?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");
index b4aea135e463a9c24ad7c7baf1e216573ac56159..78ca9cc31f41301151c9e1650b41f5a4e60e53e3 100644 (file)
@@ -4,5 +4,10 @@
        "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"
+       ]
 }
index 0171eaeb6ce695bd7211ce8faa998ca6f2e17f67..b5dba6d9e0565f4e70bbab6422f308aee878aa67 100644 (file)
@@ -14,19 +14,19 @@ use SplObjectStorage;
 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],
@@ -35,7 +35,7 @@ class Installer implements Command
                        ["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],
@@ -58,7 +58,7 @@ class Installer implements Command
                                CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT],
                ]);
        }
-       
+
        /**
         * Perform cleaniup
         */
@@ -67,13 +67,13 @@ class Installer implements Command
                        $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) {
@@ -90,7 +90,7 @@ class Installer implements Command
 
        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);
@@ -104,7 +104,7 @@ class Installer implements Command
                                $list[$dep_phar] = $this->extract($dep_phar);
                        }
                }
-               
+
                /* the actual ext.phar at last */
                $list[$phar] = $temp;
                return $list;
@@ -189,7 +189,7 @@ class Installer implements Command
                        exit(self::EINSTALL);
                }
        }
-       
+
        /**
         * Phpize + trinity
         */
@@ -199,7 +199,7 @@ class Installer implements Command
                $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
@@ -222,7 +222,7 @@ class Installer implements Command
 
                $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");
index 25dd7a42a911ba4f6e7195e5654e8985030f2b1e..d2c06be36d54aa5f41e92a10d1361e60297fd794 100644 (file)
@@ -65,11 +65,8 @@ class PharBuild implements Task
                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());
diff --git a/src/pharext/Task/PharStub.php b/src/pharext/Task/PharStub.php
new file mode 100644 (file)
index 0000000..bbaaf73
--- /dev/null
@@ -0,0 +1,51 @@
+<?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);
+       }
+}
diff --git a/src/pharext/Updater.php b/src/pharext/Updater.php
new file mode 100644 (file)
index 0000000..bee5ed6
--- /dev/null
@@ -0,0 +1,131 @@
+<?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;
+               }
+       }
+}
diff --git a/src/pharext_updater.php b/src/pharext_updater.php
new file mode 100644 (file)
index 0000000..94167c2
--- /dev/null
@@ -0,0 +1,36 @@
+#!/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();