From 4af08c149c8b66e9fe8b9b0fb9f276e0745377d1 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Tue, 25 Aug 2015 17:13:22 +0200 Subject: [PATCH] support for running .ext.phars without ext/phar --- Makefile | 5 + bin/pharext | Bin 74847 -> 89746 bytes build/create-phar.php | 5 +- src/pharext/Archive.php | 257 ++++++++++++++++++++++++++++++ src/pharext/Cli/Command.php | 7 +- src/pharext/Installer.php | 10 +- src/pharext/Packager.php | 3 +- src/pharext/Task/Extract.php | 6 +- src/pharext/Task/PharBuild.php | 21 ++- src/pharext/Task/PharCompress.php | 8 +- src/pharext_installer.php | 29 +++- src/pharext_packager.php | 28 +++- 12 files changed, 355 insertions(+), 24 deletions(-) create mode 100644 src/pharext/Archive.php diff --git a/Makefile b/Makefile index 161d2de..887b82d 100644 --- a/Makefile +++ b/Makefile @@ -28,4 +28,9 @@ release: cp build/Metadata.php.in src/pharext/Metadata.php && \ git ci -am "back to dev" +archive-test: bin/pharext + ./bin/pharext -vpgs ../apfd.git + -../php-5.5/sapi/cli/php ./apfd-1.0.1.ext.phar + -./apfd-1.0.1.ext.phar + .PHONY: all clean test release diff --git a/bin/pharext b/bin/pharext index 7640c6dff29c0b68c23dd6bfd4acd8e6fae23539..321039ee1d5d6cace5c5162a40bef6c0a884a922 100755 GIT binary patch delta 19828 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 delta 7805 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 diff --git a/build/create-phar.php b/build/create-phar.php index d2804a8..26ac6e9 100644 --- a/build/create-phar.php +++ b/build/create-phar.php @@ -4,14 +4,13 @@ * Creates bin/pharext, invoked through the Makefile */ -set_include_path(dirname(__DIR__)."/src"); +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, pharext\Metadata::all() + [ +$file = (new pharext\Task\PharBuild(null, __DIR__."/../src/pharext_packager.php", pharext\Metadata::all() + [ "name" => "pharext", - "stub" => "pharext_packager.php", "license" => file_get_contents(__DIR__."/../LICENSE") ], false))->run(); diff --git a/src/pharext/Archive.php b/src/pharext/Archive.php new file mode 100644 index 0000000..6a9b4a4 --- /dev/null +++ b/src/pharext/Archive.php @@ -0,0 +1,257 @@ + 16, + self::SIG_SHA1 => 20, + self::SIG_SHA256 => 32, + self::SIG_SHA512 => 64, + self::SIG_OPENSSL=> 0 + ]; + + private static $sigalg = [ + self::SIG_MD5 => "md5", + self::SIG_SHA1 => "sha1", + self::SIG_SHA256 => "sha256", + self::SIG_SHA512 => "sha512", + self::SIG_OPENSSL=> "openssl" + ]; + + private static $sigtyp = [ + self::SIG_MD5 => "MD5", + self::SIG_SHA1 => "SHA-1", + self::SIG_SHA256 => "SHA-256", + self::SIG_SHA512 => "SHA-512", + self::SIG_OPENSSL=> "OpenSSL", + ]; + + const PERM_FILE_MASK = 0x01ff; + const COMP_FILE_MASK = 0xf000; + const COMP_GZ_FILE = 0x1000; + const COMP_BZ2_FILE = 0x2000; + + const COMP_PHAR_MASK= 0xf000; + const COMP_PHAR_GZ = 0x1000; + const COMP_PHAR_BZ2 = 0x2000; + + private $file; + private $fd; + private $stub; + private $manifest; + private $signature; + private $extracted; + + function __construct($file = null) { + if (strlen($file)) { + $this->open($file); + } + } + + function open($file) { + if (!$this->fd = @fopen($this->file = $file, "r")) { + throw new Exception; + } + $this->stub = $this->readStub(); + $this->manifest = $this->readManifest(); + $this->signature = $this->readSignature(); + } + + function extract() { + return $this->extracted ?: $this->extractTo(new Tempdir("archive")); + } + + function extractTo($dir) { + if ((string) $this->extracted == (string) $dir) { + return $this->extracted; + } + foreach ($this->manifest["entries"] as $file => $entry) { + fseek($this->fd, $this->manifest["offset"]+$entry["offset"]); + $path = $dir."/$file"; + $dirn = dirname($path); + if (!is_dir($dirn) && !@mkdir($dirn, 0777, true)) { + throw new Exception; + } + if (!$fd = @fopen($path, "w")) { + throw new Exception; + } + switch ($entry["flags"] & self::COMP_FILE_MASK) { + case self::COMP_GZ_FILE: + if (!@stream_filter_append($fd, "zlib.inflate")) { + throw new Exception; + } + break; + case self::COMP_BZ2_FILE: + if (!@stream_filter_append($fd, "bz2.decompress")) { + throw new Exception; + } + break; + } + if ($entry["osize"] != ($copied = stream_copy_to_stream($this->fd, $fd, $entry["csize"]))) { + throw new Exception("Copied '$copied' of '$file', expected '{$entry["osize"]}' from '{$entry["csize"]}"); + } + fclose($fd); + + $crc = hexdec(hash_file("crc32b", $path)); + if ($crc !== $entry["crc32"]) { + throw new Exception("CRC mismatch of '$file': '$crc' != '{$entry["crc32"]}"); + } + + chmod($path, $entry["flags"] & self::PERM_FILE_MASK); + touch($path, $entry["stamp"]); + } + return $this->extracted = $dir; + } + + function offsetExists($o) { + return isset($this->entries[$o]); + } + + function offsetGet($o) { + $this->extract(); + return new \SplFileInfo($this->extracted."/$o"); + } + + function offsetSet($o, $v) { + throw new Exception("Archive is read-only"); + } + + function offsetUnset($o) { + throw new Exception("Archive is read-only"); + } + + function getSignature() { + /* compatible with Phar::getSignature() */ + return [ + "hash_type" => self::$sigtyp[$this->signature["flags"]], + "hash" => strtoupper(bin2hex($this->signature["hash"])), + ]; + } + + function getPath() { + /* compatible with Phar::getPath() */ + return new \SplFileInfo($this->file); + } + + function getMetadata($key = null) { + if (isset($key)) { + return $this->manifest["meta"][$key]; + } + return $this->manifest["meta"]; + } + + private function readStub() { + $stub = ""; + while (!feof($this->fd)) { + $line = fgets($this->fd); + $stub .= $line; + if (false !== stripos($line, self::HALT_COMPILER)) { + /* check for '?>' on a separate line */ + if ('?>' === fread($this->fd, 2)) { + $stub .= '?>' . fgets($this->fd); + } else { + fseek($this->fd, -2, SEEK_CUR); + } + break; + } + } + return $stub; + } + + private function readManifest() { + $current = ftell($this->fd); + $header = unpack("Vlen/Vnum/napi/Vflags", fread($this->fd, 14)); + if (($alias = current(unpack("V", fread($this->fd, 4))))) { + $alias = fread($this->fd, $alias); + } + if (($meta = current(unpack("V", fread($this->fd, 4))))) { + $meta = unserialize(fread($this->fd, $meta)); + } + $entries = []; + for ($i = 0; $i < $header["num"]; ++$i) { + $this->readEntry($entries); + } + $offset = ftell($this->fd); + if (($length = $offset - $current - 4) != $header["len"]) { + throw new Exception("Manifest length read was '$length', expected '{$header["len"]}'"); + } + return $header + compact("alias", "meta", "entries", "offset"); + } + + private function readEntry(array &$entries) { + if (!count($entries)) { + $offset = 0; + } else { + $last = end($entries); + $offset = $last["offset"] + $last["csize"]; + } + if (($file = current(unpack("V", fread($this->fd, 4))))) { + $file = fread($this->fd, $file); + } + if ($file === 0 || !strlen($file)) { + throw new Exception("Empty file name encountered at offset '$offset'"); + } + $header = unpack("Vosize/Vstamp/Vcsize/Vcrc32/Vflags", fread($this->fd, 20)); + if (($meta = current(unpack("V", fread($this->fd, 4))))) { + $meta = unserialize(fread($this->fd, $meta)); + } else { + $meta = []; + } + $entries[$file] = $header + compact("meta", "offset"); + } + + private function readSignature() { + fseek($this->fd, -8, SEEK_END); + $sig = unpack("Vflags/Z4magic", fread($this->fd, 8)); + $end = ftell($this->fd); + + if ($sig["magic"] !== "GBMB") { + throw new Exception("Invalid signature magic value '{$sig["magic"]}"); + } + + switch ($sig["flags"]) { + case self::SIG_OPENSSL: + fseek($this->fd, -12, SEEK_END); + if (($hash = current(unpack("V", fread($this->fd, 4))))) { + $offset = 4 + $hash; + fseek($this->fd, -$offset, SEEK_CUR); + $hash = fread($this->fd, $hash); + fseek($this->fd, 0, SEEK_SET); + $valid = openssl_verify(fread($this->fd, $end - $offset - 8), + $hash, file_get_contents($this->file.".pubkey")) === 1; + } + break; + + case self::SIG_MD5: + case self::SIG_SHA1: + case self::SIG_SHA256: + case self::SIG_SHA512: + $offset = 8 + self::$siglen[$sig["flags"]]; + fseek($this->fd, -$offset, SEEK_END); + $hash = fread($this->fd, self::$siglen[$sig["flags"]]); + $algo = hash_init(self::$sigalg[$sig["flags"]]); + fseek($this->fd, 0, SEEK_SET); + hash_update_stream($algo, $this->fd, $end - $offset); + $valid = hash_final($algo, true) === $hash; + break; + + default: + throw new Exception("Invalid signature type '{$sig["flags"]}"); + } + + return $sig + compact("hash", "valid"); + } +} diff --git a/src/pharext/Cli/Command.php b/src/pharext/Cli/Command.php index b733885..bc0afbb 100644 --- a/src/pharext/Cli/Command.php +++ b/src/pharext/Cli/Command.php @@ -2,6 +2,7 @@ namespace pharext\Cli; +use pharext\Archive; use pharext\Cli\Args as CliArgs; use Phar; @@ -42,7 +43,11 @@ trait Command * @return mixed */ public function metadata($key = null) { - $running = new Phar(Phar::running(false)); + if (extension_loaded("Phar")) { + $running = new Phar(Phar::running(false)); + } else { + $running = new Archive(PHAREXT_PHAR); + } if ($key === "signature") { $sig = $running->getSignature(); diff --git a/src/pharext/Installer.php b/src/pharext/Installer.php index 29fe271..0171eae 100644 --- a/src/pharext/Installer.php +++ b/src/pharext/Installer.php @@ -68,7 +68,7 @@ class Installer implements Command } } - private function extract(Phar $phar) { + private function extract($phar) { $temp = (new Task\Extract($phar))->run($this->verbosity()); $this->cleanup[] = new Task\Cleanup($temp); return $temp; @@ -90,13 +90,17 @@ class Installer implements Command private function load() { $list = new SplObjectStorage(); - $phar = new Phar(Phar::running(false)); + $phar = extension_loaded("Phar") + ? new Phar(Phar::running(false)) + : new Archive(PHAREXT_PHAR); $temp = $this->extract($phar); foreach ($phar as $entry) { $dep_file = $entry->getBaseName(); if (fnmatch("*.ext.phar*", $dep_file)) { - $dep_phar = new Phar("$temp/$dep_file"); + $dep_phar = extension_loaded("Phar") + ? new Phar("$temp/$dep_file") + : new Archive("$temp/$dep_file"); $list[$dep_phar] = $this->extract($dep_phar); } } diff --git a/src/pharext/Packager.php b/src/pharext/Packager.php index bdb83bb..10542e4 100644 --- a/src/pharext/Packager.php +++ b/src/pharext/Packager.php @@ -247,10 +247,9 @@ class Packager implements Command "name" => $this->args->name, "release" => $this->args->release, "license" => $this->source->getLicense(), - "stub" => "pharext_installer.php", "type" => $this->args->zend ? "zend_extension" : "extension", ]); - $file = (new Task\PharBuild($this->source, $meta))->run($this->verbosity()); + $file = (new Task\PharBuild($this->source, __DIR__."/../pharext_installer.php", $meta))->run($this->verbosity()); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EBUILD); diff --git a/src/pharext/Task/Extract.php b/src/pharext/Task/Extract.php index d68b426..3aa3f85 100644 --- a/src/pharext/Task/Extract.php +++ b/src/pharext/Task/Extract.php @@ -2,6 +2,7 @@ namespace pharext\Task; +use pharext\Archive; use pharext\Task; use pharext\Tempdir; @@ -22,7 +23,7 @@ class Extract implements Task * @param mixed $source archive location */ public function __construct($source) { - if ($source instanceof Phar || $source instanceof PharData) { + if ($source instanceof Phar || $source instanceof PharData || $source instanceof Archive) { $this->source = $source; } else { $this->source = new PharData($source); @@ -37,6 +38,9 @@ class Extract implements Task if ($verbose) { printf("Extracting %s ...\n", basename($this->source->getPath())); } + if ($this->source instanceof Archive) { + return $this->source->extract(); + } $dest = new Tempdir("extract"); $this->source->extractTo($dest); return $dest; diff --git a/src/pharext/Task/PharBuild.php b/src/pharext/Task/PharBuild.php index 03ac42c..25dd7a4 100644 --- a/src/pharext/Task/PharBuild.php +++ b/src/pharext/Task/PharBuild.php @@ -19,6 +19,11 @@ class PharBuild implements Task */ private $source; + /** + * @var string + */ + private $stub; + /** * @var array */ @@ -31,11 +36,13 @@ class PharBuild implements Task /** * @param SourceDir $source extension source directory + * @param string $stub path to phar stub * @param array $meta phar meta data * @param bool $readonly whether the stub has -dphar.readonly=1 set */ - public function __construct(SourceDir $source = null, array $meta = null, $readonly = true) { + public function __construct(SourceDir $source = null, $stub, array $meta = null, $readonly = true) { $this->source = $source; + $this->stub = $stub; $this->meta = $meta; $this->readonly = $readonly; } @@ -57,12 +64,12 @@ class PharBuild implements Task if ($this->meta) { $phar->setMetadata($this->meta); - if (isset($this->meta["stub"])) { - $phar->setDefaultStub($this->meta["stub"]); - $phar->setStub("#!/usr/bin/php -dphar.readonly=" . - intval($this->readonly) ."\n". - $phar->getStub()); - } + } + 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); } $phar->buildFromIterator((new Task\BundleGenerator)->run()); diff --git a/src/pharext/Task/PharCompress.php b/src/pharext/Task/PharCompress.php index ea0607f..78a9349 100644 --- a/src/pharext/Task/PharCompress.php +++ b/src/pharext/Task/PharCompress.php @@ -58,12 +58,10 @@ class PharCompress implements Task if ($verbose) { printf("Compressing %s ...\n", basename($this->package->getPath())); } + /* stop shebang */ + $stub = $this->package->getStub(); $phar = $this->package->compress($this->encoding); - $meta = $phar->getMetadata(); - if (isset($meta["stub"])) { - /* drop shebang */ - $phar->setDefaultStub($meta["stub"]); - } + $phar->setStub(substr($stub, strpos($stub, "\n")+1)); return $this->file . $this->extension; } } diff --git a/src/pharext_installer.php b/src/pharext_installer.php index 89e44a3..19386f2 100644 --- a/src/pharext_installer.php +++ b/src/pharext_installer.php @@ -1,11 +1,38 @@ +#!/usr/bin/env php +#include +#include +#include +#include + +namespace pharext; + +if (extension_loaded("Phar")) { + \Phar::interceptFileFuncs(); + \Phar::mapPhar(); + $phardir = "phar://".__FILE__; +} else { + $archive = new Archive(__FILE__); + $phardir = $archive->extract(); +} + +set_include_path("$phardir:". get_include_path()); + +$installer = new Installer(); $installer->run($argc, $argv); + +__HALT_COMPILER(); diff --git a/src/pharext_packager.php b/src/pharext_packager.php index f85c2f1..bdce719 100644 --- a/src/pharext_packager.php +++ b/src/pharext_packager.php @@ -1,10 +1,36 @@ +#!/usr/bin/env php -dphar.readonly=0 run($argc, $argv); + +__HALT_COMPILER(); -- 2.30.2