-Copyright (c) 2015, Michael Wallner <mike@iworks.at>.
+Copyright (c) 2015, Michael Wallner <mike@php.net>.
All rights reserved.
Redistribution and use in source and binary forms, with or without
@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
- chmod +x $@
test:
@echo "Running tests ... "
* Creates bin/pharext, invoked through the Makefile
*/
-$pkgname = __DIR__."/../bin/pharext";
-$tmpname = __DIR__."/pharext.phar";
+set_include_path(dirname(__DIR__)."/src");
+spl_autoload_register(function($c) {
+ return include strtr($c, "\\_", "//") . ".php";
+});
-if (file_exists($tmpname)) {
- if (!unlink($tmpname)) {
- fprintf(STDERR, "%s\n", error_get_last()["message"]);
- exit(3);
- }
-}
+require_once __DIR__."/../src/pharext/Version.php";
-$package = new \Phar($tmpname, 0, "pharext.phar");
+$file = (new pharext\Task\PharBuild(null, [
+ "header" => sprintf("pharext v%s (c) Michael Wallner <mike@php.net>", pharext\VERSION),
+ "version" => pharext\VERSION,
+ "name" => "pharext",
+ "date" => date("Y-m-d"),
+ "stub" => "pharext_packager.php",
+ "license" => file_get_contents(__DIR__."/../LICENSE")
+], false))->run();
if (getenv("SIGN")) {
- shell_exec("stty -echo");
- printf("Password: ");
- $password = fgets(STDIN, 1024);
- printf("\n");
- shell_exec("stty echo");
- if (substr($password, -1) == "\n") {
- $password = substr($password, 0, -1);
- }
-
- $pkey = openssl_pkey_get_private("file://".__DIR__."/pharext.key", $password);
- if (!is_resource($pkey)) {
- $this->error("Could not load private key %s/pharext.key", __DIR__);
- exit(3);
- }
- if (!openssl_pkey_export($pkey, $key)) {
- $this->error(null);
- exit(3);
- }
-
- $package->setSignatureAlgorithm(Phar::OPENSSL, $key);
+ $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");
}
-$package->buildFromDirectory(dirname(__DIR__)."/src", "/^.*\.php$/");
-$package->setDefaultStub("pharext_packager.php");
-$package->setStub("#!/usr/bin/php -dphar.readonly=0\n".$package->getStub());
-unset($package);
-
-if (!rename($tmpname, $pkgname)) {
- fprintf(STDERR, "%s\n", error_get_last()["message"]);
- exit(4);
-}
+/* we do not need the extra logic of Task\PharRename */
+rename($file, __DIR__."/../bin/pharext");
use pharext\Cli\Args as CliArgs;
-require_once "pharext/Version.php";
+use Phar;
+
+if (!function_exists("array_column")) {
+ function array_column(array $array, $col, $idx = null) {
+ $result = [];
+ foreach ($array as $el) {
+ if (isset($idx)) {
+ $result[$el[$idx]] = $el[$col];
+ } else {
+ $result[] = $el[$col];
+ }
+ }
+ return $result;
+ }
+}
trait Command
{
return $this->args;
}
+ /**
+ * Retrieve metadata of the currently running phar
+ * @param string $key
+ * @return mixed
+ */
+ public function metadata($key = null) {
+ $running = new Phar(Phar::running(false));
+
+ if ($key === "signature") {
+ $sig = $running->getSignature();
+ return sprintf("%s signature of %s\n%s",
+ $sig["hash_type"],
+ $this->metadata("name"),
+ chunk_split($sig["hash"], 64, "\n"));
+ }
+
+ $metadata = $running->getMetadata();
+ if (isset($key)) {
+ return $metadata[$key];
+ }
+ return $metadata;
+ }
+
/**
* Output pharext vX.Y.Z header
*/
- function header() {
- printf("pharext v%s (c) Michael Wallner <mike@php.net>\n\n",
- \pharext\VERSION);
+ public function header() {
+ if (!headers_sent()) {
+ /* only display header, if we didn't generate any output yet */
+ printf("%s\n\n", $this->metadata("header"));
+ }
}
/**
}
}
+ /**
+ * @inheritdoc
+ * @see \pharext\Command::warn()
+ */
+ public function warn($fmt) {
+ if (!$this->args->quiet) {
+ if (!isset($fmt)) {
+ $fmt = "%s\n";
+ $arg = error_get_last()["message"];
+ } else {
+ $arg = array_slice(func_get_args(), 1);
+ }
+ vfprintf(STDERR, "Warning: $fmt", $arg);
+ }
+ }
+
/**
* @inheritdoc
* @see \pharext\Command::error()
} elseif (is_dir("$dir/$entry")) {
$this->rm("$dir/$entry");
} elseif (!unlink("$dir/$entry")) {
- $this->error(null);
+ $this->warn(null);
}
}
if (!rmdir($dir)) {
- $this->error(null);
+ $this->warn(null);
}
}
}
*/
public function info($fmt);
+ /**
+ * Print warning
+ * @param string $fmt
+ * @param string ...$args
+ */
+ public function warn($fmt);
+
/**
* Print error
* @param string $fmt
* @param string ...$args
*/
public function error($fmt);
-
+
/**
* Execute the command
* @param int $argc command line argument count
--- /dev/null
+<?php
+
+namespace pharext;
+
+class Exception extends \Exception
+{
+ public function __construct($message = null, $code = 0, $previous = null) {
+ if (!isset($message)) {
+ $last_error = error_get_last();
+ $message = $last_error["message"];
+ if (!$code) {
+ $code = $last_error["type"];
+ }
+ }
+ parent::__construct($message, $code, $previous);
+ }
+}
namespace pharext;
+/**
+ * Execute system command
+ */
class ExecCmd
{
/**
public function __construct($command, $verbose = false) {
$this->command = $command;
$this->verbose = $verbose;
-
- /* interrupt output stream */
- if ($verbose) {
- printf("\n");
- }
}
/**
private function suExec($command, &$output, &$status) {
if (!($proc = proc_open($command, [STDIN,["pipe","w"],["pipe","w"]], $pipes))) {
$status = -1;
- throw new \Exception("Failed to run {$command}");
+ throw new Exception("Failed to run {$command}");
}
$stdout = $pipes[1];
$passwd = 0;
/**
* Run the command
* @param array $args
- * @throws \Exception
+ * @throws \pharext\Exception
*/
public function run(array $args = null) {
$exec = escapeshellcmd($this->command);
}
if ($this->status) {
- throw new \Exception("Command {$this->command} failed ({$this->status})");
+ throw new Exception("Command {$this->command} failed ({$this->status})");
}
}
* @return int
*/
public function getStatus() {
- return $status;
+ return $this->status;
}
/**
namespace pharext;
-use Phar;
use pharext\Cli\Args as CliArgs;
use pharext\Cli\Command as CliCommand;
+use Phar;
+use SplObjectStorage;
+
/**
* The extension install command executed by the extension phar
*/
{
use CliCommand;
- /**
- * The temporary directory we should operate in
- * @var string
- */
- private $tmp;
-
- /**
- * The directory we came from
- * @var string
- */
- private $cwd;
-
/**
* Create the command
*/
]);
}
- /**
- * Cleanup temp directory
- */
- public function __destruct() {
- $this->cleanup();
+ private function extract(Phar $phar) {
+ $this->debug("Extracting %s ...\n", basename($phar->getPath()));
+ return (new Task\Extract($phar))->run($this->args->verbose);
+ }
+
+ private function hooks(SplObjectStorage $phars) {
+ $hooks = [];
+ foreach ($phars as $phar) {
+ if (isset($phar["pharext_install.php"])) {
+ $callable = include $phar["pharext_install.php"];
+ if (is_callable($callable)) {
+ $hooks[] = $callable($this);
+ }
+ }
+ }
+ return $hooks;
}
/**
* @see \pharext\Command::run()
*/
public function run($argc, array $argv) {
- $this->cwd = getcwd();
- $this->tmp = new Tempdir(basename(Phar::running(false)));
-
+ $list = new SplObjectStorage();
$phar = new Phar(Phar::running(false));
+ $temp = $this->extract($phar);
+
foreach ($phar as $entry) {
- if (fnmatch("*.ext.phar*", $entry->getBaseName())) {
- $temp = new Tempdir($entry->getBaseName());
- $phar->extractTo($temp, $entry->getFilename(), true);
- $phars[(string) $temp] = new Phar($temp."/".$entry->getFilename());
+ $dep_file = $entry->getBaseName();
+ if (fnmatch("*.ext.phar*", $dep_file)) {
+ $dep_phar = new Phar("$temp/$dep_file");
+ $list[$dep_phar] = $this->extract($dep_phar);
}
}
- $phars[(string) $this->tmp] = $phar;
+ /* the actual ext.phar at last */
+ $list[$phar] = $temp;
- foreach ($phars as $phar) {
- if (isset($phar["pharext_install.php"])) {
- $callable = include $phar["pharext_install.php"];
- if (is_callable($callable)) {
- $recv[] = $callable($this);
- }
- }
- }
-
+ /* installer hooks */
+ $hook = $this->hooks($list);
+
+ /* standard arg stuff */
$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;
}
-
+
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);
- }
+ }
if (!$this->args["quiet"]) {
$this->help($prog);
}
exit(1);
}
-
- if (isset($recv)) {
- foreach ($recv as $r) {
- $r($this);
- }
- }
- foreach ($phars as $temp => $phar) {
- $this->installPackage($phar, $temp);
- }
- }
- /**
- * Prepares, configures, builds and installs the extension
- */
- private function installPackage(Phar $phar, $temp) {
- $this->info("Installing %s ... \n", basename($phar->getAlias()));
- try {
- $phar->extractTo($temp, null, true);
- } catch (\Exception $e) {
- $this->error("%s\n", $e->getMessage());
- exit(3);
+ /* post process hooks */
+ foreach ($hook as $callback) {
+ if (is_callable($callback)) {
+ $callback($this);
+ }
}
- if (!chdir($temp)) {
- $this->error(null);
- exit(4);
+ /* install packages */
+ foreach ($list as $phar) {
+ $this->info("Installing %s ...\n", basename($phar->getPath()));
+ $this->install($list[$phar]);
+ $this->activate($list[$phar]);
+ $this->cleanup($list[$phar]);
+ $this->info("Successfully installed %s!\n", basename($phar->getPath()));
}
-
- $this->build();
- $this->activate();
- $this->cleanup($temp);
}
/**
* Phpize + trinity
*/
- private function build() {
+ private function install($temp) {
try {
// phpize
- $this->info("Runnin phpize ... ");
- $cmd = new ExecCmd($this->php("ize"), $this->args->verbose);
- $cmd->run();
- $this->info("OK\n");
-
+ $this->info("Running phpize ...\n");
+ $phpize = new Task\Phpize($temp, $this->args->prefix, $this->args->{"common-name"});
+ $phpize->run($this->args->verbose);
+
// configure
- $this->info("Running configure ... ");
- $args = ["--with-php-config=". $this->php("-config")];
- if ($this->args->configure) {
- $args = array_merge($args, $this->args->configure);
- }
- $cmd = new ExecCmd("./configure", $this->args->verbose);
- $cmd->run($args);
- $this->info("OK\n");
+ $this->info("Running configure ...\n");
+ $configure = new Task\Configure($temp, $this->args->configure, $this->args->prefix, $this->args{"common-name"});
+ $configure->run($this->args->verbose);
// make
- $this->info("Running make ... ");
- $cmd = new ExecCmd("make", $this->args->verbose);
- if ($this->args->verbose) {
- $cmd->run(["-j3"]);
- } else {
- $cmd->run(["-j3", "-s"]);
- }
- $this->info("OK\n");
-
+ $this->info("Running make ...\n");
+ $make = new Task\Make($temp);
+ $make->run($this->args->verbose);
+
// install
- $this->info("Running make install ... ");
- if (isset($this->args->sudo)) {
- $cmd->setSu($this->args->sudo);
- }
- if ($this->args->verbose) {
- $cmd->run(["install"]);
- } else {
- $cmd->run(["install", "-s"]);
- }
- $this->info("OK\n");
+ $this->info("Running make install ...\n");
+ $sudo = isset($this->args->sudo) ? $this->args->sudo : null;
+ $install = new Task\Make($temp, ["install"], $sudo);
+ $install->run($this->args->verbose);
} catch (\Exception $e) {
$this->error("%s\n", $e->getMessage());
- $this->error("%s\n", $cmd->getOutput());
+ exit(2);
}
}
- /**
- * Perform any cleanups
- */
- private function cleanup($temp = null) {
- if (!isset($temp)) {
- $temp = $this->tmp;
- }
+ private function cleanup($temp) {
if (is_dir($temp)) {
- chdir($this->cwd);
- $this->info("Cleaning up %s ...\n", $temp);
$this->rm($temp);
+ } elseif (file_exists($temp)) {
+ unlink($temp);
}
}
- /**
- * Construct a command from prefix common-name and suffix
- * @param type $suffix
- * @return string
- */
- private function php($suffix) {
- $cmd = $this->args["common-name"] . $suffix;
- if (isset($this->args->prefix)) {
- $cmd = $this->args->prefix . "/bin/" . $cmd;
- }
- return $cmd;
- }
-
- /**
- * Activate extension in php.ini
- */
- private function activate() {
+ private function activate($temp) {
if ($this->args->ini) {
$files = [realpath($this->args->ini)];
} else {
$files[] = php_ini_loaded_file();
}
- $extension = basename(current(glob("modules/*.so")));
- $pattern = preg_quote($extension);
-
- foreach ($files as $index => $file) {
- $temp = new Tempfile("phpini");
- foreach (file($file) as $line) {
- if (preg_match("/^\s*extension\s*=\s*[\"']?{$pattern}[\"']?\s*(;.*)?\$/", $line)) {
- // already there
- $this->info("Extension already activated\n");
- return;
- }
- fwrite($temp->getStream(), $line);
- }
- }
-
- // not found, add extension line to the last process file
- if (isset($temp, $file)) {
- fprintf($temp->getStream(), "extension=%s\n", $extension);
- $temp->closeStream();
+ $sudo = isset($this->args->sudo) ? $this->args->sudo : null;
- $path = $temp->getPathname();
- $stat = stat($file);
-
- try {
- $this->info("Running INI owner transfer ... ");
- $ugid = sprintf("%d:%d", $stat["uid"], $stat["gid"]);
- $cmd = new ExecCmd("chown", $this->args->verbose);
- if (isset($this->args->sudo)) {
- $cmd->setSu($this->args->sudo);
- }
- $cmd->run([$ugid, $path]);
- $this->info("OK\n");
-
- $this->info("Running INI permission transfer ... ");
- $perm = decoct($stat["mode"] & 0777);
- $cmd = new ExecCmd("chmod", $this->args->verbose);
- if (isset($this->args->sudo)) {
- $cmd->setSu($this->args->sudo);
- }
- $cmd->run([$perm, $path]);
- $this->info("OK\n");
-
- $this->info("Running INI activation ... ");
- $cmd = new ExecCmd("mv", $this->args->verbose);
- if (isset($this->args->sudo)) {
- $cmd->setSu($this->args->sudo);
- }
- $cmd->run([$path, $file]);
- $this->info("OK\n");
- } catch (\Exception $e) {
- $this->error("%s\n", $e->getMessage());
- $this->error("%s\n", $cmd->getOutput());
- exit(5);
+ try {
+ $this->info("Running INI activation ...\n");
+ $activate = new Task\Activate($temp, $files, $sudo);
+ if (!$activate->run($this->args->verbose)) {
+ $this->info("Extension already activated ...\n");
}
+ } catch (\Exception $e) {
+ $this->error("%s\n", $e->getMessage());
+ $this->error("%s\n", $output);
+ exit(3);
}
}
}
namespace pharext\Openssl;
+use pharext\Exception;
+
class PrivateKey
{
/**
* Read a private key
* @param string $file
* @param string $password
- * @throws \Exception
+ * @throws \pharext\Exception
*/
function __construct($file, $password) {
/* there appears to be a bug with refcount handling of this
* resource; when the resource is stored as property, it cannot be
- * "coerced to a private key" on openssl_sign() alter in another method
+ * "coerced to a private key" on openssl_sign() later in another method
*/
$key = openssl_pkey_get_private("file://$file", $password);
if (!is_resource($key)) {
- throw new \Exception("Could not load private key");
+ throw new Exception("Could not load private key");
}
openssl_pkey_export($key, $this->key);
$this->pub = openssl_pkey_get_details($key)["key"];
/**
* Export the public key to a file
* @param string $file
- * @throws \Exception
+ * @throws \pharext\Exception
*/
function exportPublicKey($file) {
if (!file_put_contents("$file.tmp", $this->pub) || !rename("$file.tmp", $file)) {
- throw new \Exception(error_get_last()["message"]);
+ throw new Exception;
}
}
}
namespace pharext;
use Phar;
-use PharData;
use pharext\Cli\Args as CliArgs;
use pharext\Cli\Command as CliCommand;
CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG],
[null, "signature", "Dump signature",
CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT],
+ [null, "license", "Show license",
+ CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT],
+ [null, "version", "Show version",
+ CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT],
]);
}
foreach ($this->cleanup as $cleanup) {
if (is_dir($cleanup)) {
$this->rm($cleanup);
- } else {
+ } elseif (file_exists($cleanup)) {
unlink($cleanup);
}
}
$this->help($prog);
exit;
}
- if ($this->args["signature"]) {
- exit($this->signature($prog));
+ 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(2);
}
try {
if ($errs) {
if (!$this->args["quiet"]) {
- if (!headers_sent()) {
- /* only display header, if we didn't generate any output yet */
- $this->header();
- }
+ $this->header();
}
foreach ($errs as $err) {
$this->error("%s\n", $err);
$this->createPackage();
}
-
- /**
- * Dump program signature
- * @param string $prog
- * @return int exit code
- */
- function signature($prog) {
- try {
- $sig = (new Phar(Phar::running(false)))->getSignature();
- printf("%s signature of %s\n%s", $sig["hash_type"], $prog,
- chunk_split($sig["hash"], 64, "\n"));
- return 0;
- } catch (\Exception $e) {
- $this->error("%s\n", $e->getMessage());
- return 2;
- }
- }
-
+
/**
* Download remote source
* @param string $source
* @return string local source
*/
private function download($source) {
- $this->info("Fetching remote source %s ... ", $source);
- if ($this->args["git"]) {
- $local = new Tempdir("gitclone");
- $cmd = new ExecCmd("git", $this->args->verbose);
- $cmd->run(["clone", $source, $local]);
- if (!$this->args->verbose) {
- $this->info("OK\n");
- }
+ $this->info("Fetching remote source %s ...\n", $source);
+
+ if ($this->args->git) {
+ $task = new Task\GitClone($source);
} else {
- $context = stream_context_create([],["notification" => function($notification, $severity, $message, $code, $bytes_cur, $bytes_max) {
- switch ($notification) {
- case STREAM_NOTIFY_CONNECT:
- $this->debug("\n");
- break;
- case STREAM_NOTIFY_PROGRESS:
- if ($bytes_max) {
- $bytes_pct = $bytes_cur/$bytes_max;
- $this->debug("\r %3d%% [%s>%s] ",
- $bytes_pct*100,
- str_repeat("=", round(70*$bytes_pct)),
- str_repeat(" ", round(70*(1-$bytes_pct)))
- );
- }
- break;
- case STREAM_NOTIFY_COMPLETED:
- /* this is not generated, why? */
- break;
- }
- }]);
- if (!$remote = fopen($source, "r", false, $context)) {
- $this->error(null);
- exit(2);
- }
- $local = new Tempfile("remote");
- if (!stream_copy_to_stream($remote, $local->getStream())) {
- $this->error(null);
- exit(2);
- }
- $local->closeStream();
- $this->info("OK\n");
+ $task = new Task\StreamFetch($source, function($bytes_pct) {
+ $this->debug(" %3d%% [%s>%s] \r",
+ floor($bytes_pct*100),
+ str_repeat("=", round(50*$bytes_pct)),
+ str_repeat(" ", round(50*(1-$bytes_pct)))
+ );
+ });
}
-
+ $local = $task->run($this->args->verbose);
+ $this->debug("\n");
+
$this->cleanup[] = $local;
return $local;
}
* @return string extracted directory
*/
private function extract($source) {
- $dest = new Tempdir("local");
- $this->debug("Extracting %s to %s ... ", $source, $dest);
- $archive = new PharData($source);
- $archive->extractTo($dest);
- $this->debug("OK\n");
+ $this->debug("Extracting %s ...\n", $source);
+
+ $task = new Task\Extract($source);
+ $dest = $task->run($this->args->verbose);
+
$this->cleanup[] = $dest;
return $dest;
}
private function localize($source) {
if (!stream_is_local($source)) {
$source = $this->download($source);
+ $this->cleanup[] = $source;
}
+ $source = realpath($source);
if (!is_dir($source)) {
$source = $this->extract($source);
- if ($this->args["pecl"]) {
- $this->debug("Sanitizing PECL dir ... ");
- $dirs = glob("$source/*", GLOB_ONLYDIR);
- $files = array_diff(glob("$source/*"), $dirs);
- $source = current($dirs);
- foreach ($files as $file) {
- rename($file, "$source/" . basename($file));
- }
- $this->debug("OK\n");
+ $this->cleanup[] = $source;
+
+ if ($this->args->pecl) {
+ $this->debug("Sanitizing PECL dir ...\n");
+ $source = (new Task\PeclFixup($source))->run($this->args->verbose);
}
}
return $source;
}
- /**
- * Traverses all pharext source files to bundle
- * @return Generator
- */
- private function bundle() {
- $rdi = new \RecursiveDirectoryIterator(__DIR__);
- $rii = new \RecursiveIteratorIterator($rdi);
- for ($rii->rewind(); $rii->valid(); $rii->next()) {
- yield "pharext/". $rii->getSubPathname() => $rii->key();
-
- }
- }
-
- /**
- * Ask for password on the console
- * @param string $prompt
- * @return string password
- */
- private function askpass($prompt = "Password:") {
- system("stty -echo", $retval);
- if ($retval) {
- $this->error("Could not disable echo on the terminal\n");
- }
- printf("%s ", $prompt);
- $pass = fgets(STDIN, 1024);
- system("stty echo");
- if (substr($pass, -1) == "\n") {
- $pass = substr($pass, 0, -1);
- }
- return $pass;
- }
-
/**
* Creates the extension phar
*/
private function createPackage() {
- $pkguniq = uniqid();
- $pkgtemp = sprintf("%s/%s.phar", sys_get_temp_dir(), $pkguniq);
- $pkgdesc = "{$this->args->name}-{$this->args->release}";
-
- $this->info("Creating phar %s ...%s", $pkgtemp, $this->args->verbose ? "\n" : " ");
try {
- $package = new Phar($pkgtemp);
+ $meta = array_merge($this->metadata(), [
+ "date" => date("Y-m-d"),
+ "name" => $this->args->name,
+ "release" => $this->args->release,
+ "license" => @file_get_contents(current(glob($this->source->getBaseDir()."/LICENSE*"))),
+ "stub" => "pharext_installer.php",
+ ]);
+ $file = (new Task\PharBuild($this->source, $meta))->run();
if ($this->args->sign) {
- $this->info("\nUsing private key to sign phar ... \n");
- $privkey = new Openssl\PrivateKey(realpath($this->args->sign), $this->askpass());
- $privkey->sign($package);
+ $this->info("Using private key to sign phar ...\n");
+ $pass = (new Task\Askpass)->run($this->args->verbose);
+ $sign = new Task\PharSign($file, $this->args->sign, $pass);
+ $pkey = $sign->run($this->args->verbose);
}
- $package->startBuffering();
- $package->buildFromIterator($this->source, $this->source->getBaseDir());
- $package->buildFromIterator($this->bundle(__DIR__));
- $package->addFile(__DIR__."/../pharext_installer.php", "pharext_installer.php");
- $package->setDefaultStub("pharext_installer.php");
- $package->setStub("#!/usr/bin/php -dphar.readonly=1\n".$package->getStub());
- $package->stopBuffering();
+ } catch (\Exception $e) {
+ $this->error("%s\n", $e->getMessage());
+ exit(4);
+ }
+
+ if ($this->args->gzip) {
+ try {
+ $gzip = (new Task\PharCompress($file, Phar::GZ))->run();
+ $move = new Task\PharRename($gzip, $this->args->dest, $this->args->name ."-". $this->args->release);
+ $name = $move->run($this->args->verbose);
- if (!chmod($pkgtemp, 0777)) {
- $this->error(null);
- } elseif ($this->args->verbose) {
- $this->debug("Created executable phar %s\n", $pkgtemp);
- } else {
- $this->info("OK\n");
- }
- if ($this->args->gzip) {
- $this->info("Compressing with gzip ... ");
- try {
- $package->compress(Phar::GZ)
- ->setDefaultStub("pharext_installer.php");
- $this->info("OK\n");
- } catch (\Exception $e) {
- $this->error("%s\n", $e->getMessage());
+ $this->info("Created gzipped phar %s\n", $name);
+
+ if ($this->args->sign) {
+ $sign = new Task\PharSign($name, $this->args->sign, $pass);
+ $sign->run($this->args->verbose)->exportPublicKey($name.".pubkey");
}
+
+ } catch (\Exception $e) {
+ $this->warn("%s\n", $e->getMessage());
}
- if ($this->args->bzip) {
- $this->info("Compressing with bzip ... ");
- try {
- $package->compress(Phar::BZ2)
- ->setDefaultStub("pharext_installer.php");
- $this->info("OK\n");
- } catch (\Exception $e) {
- $this->error("%s\n", $e->getMessage());
+ }
+
+ if ($this->args->bzip) {
+ try {
+ $bzip = (new Task\PharCompress($file, Phar::BZ2))->run();
+ $move = new Task\PharRename($bzip, $this->args->dest, $this->args->name ."-". $this->args->release);
+ $name = $move->run($this->args->verbose);
+
+ $this->info("Created bzipped phar %s\n", $name);
+
+ if ($this->args->sign) {
+ $sign = new Task\PharSign($name, $this->args->sign, $pass);
+ $sign->run($this->args->verbose)->exportPublicKey($name.".pubkey");
}
+
+ } catch (\Exception $e) {
+ $this->warn("%s\n", $e->getMessage());
+ }
+ }
+
+ try {
+ $move = new Task\PharRename($file, $this->args->dest, $this->args->name ."-". $this->args->release);
+ $name = $move->run($this->args->verbose);
+
+ $this->info("Created executable phar %s\n", $name);
+
+ if (isset($pkey)) {
+ $pkey->exportPublicKey($name.".pubkey");
}
- unset($package);
} catch (\Exception $e) {
$this->error("%s\n", $e->getMessage());
exit(4);
}
-
- foreach (glob($pkgtemp."*") as $pkgtemp) {
- $pkgfile = str_replace($pkguniq, "{$pkgdesc}.ext", $pkgtemp);
- $pkgname = $this->args->dest ."/". basename($pkgfile);
- $this->info("Finalizing %s ... ", $pkgname);
- if (!rename($pkgtemp, $pkgname)) {
- $this->error(null);
- exit(5);
- }
- $this->info("OK\n");
- if ($this->args->sign && isset($privkey)) {
- $keyname = $this->args->dest ."/". basename($pkgfile) . ".pubkey";
- $this->info("Public Key %s ... ", $keyname);
- try {
- $privkey->exportPublicKey($keyname);
- $this->info("OK\n");
- } catch (\Exception $e) {
- $this->error("%s", $e->getMessage());
- }
- }
- }
}
}
}
/* there may be symlinks, so no realpath here */
if (!file_exists("$path/$file")) {
- $this->cmd->error("File %s does not exist in %s\n", $file, $path);
+ $this->cmd->warn("File %s does not exist in %s\n", $file, $path);
}
yield "$path/$file";
}
namespace pharext\SourceDir;
use pharext\Command;
+use pharext\Exception;
use pharext\SourceDir;
/**
} elseif (realpath("$path/package.xml")) {
$sxe = simplexml_load_file("$path/package.xml");
} else {
- throw new \Exception("Missing package.xml in $path");
+ throw new Exception("Missing package.xml in $path");
}
$sxe->registerXPathNamespace("pecl", $sxe->getDocNamespaces()[""]);
if (!isset($args->name)) {
$name = (string) $sxe->xpath("/pecl:package/pecl:name")[0];
foreach ($args->parse(2, ["--name", $name]) as $error) {
- $cmd->error("%s\n", $error);
+ $cmd->warn("%s\n", $error);
}
}
if (!isset($args->release)) {
$release = (string) $sxe->xpath("/pecl:package/pecl:version/pecl:release")[0];
foreach ($args->parse(2, ["--release", $release]) as $error) {
- $cmd->error("%s\n", $error);
+ $cmd->warn("%s\n", $error);
}
}
* @return string
*/
private static function loadHook($configure, $dependencies) {
+ require_once "pharext/Version.php";
return include __DIR__."/../../pharext_install.tpl.php";
}
substr($b, strpos(".ext.phar", $b))
);
});
- yield realpath($this->path."/".end($glob));
+ yield end($glob);
} else {
unset($dependencies[$key]);
}
private function generateFiles() {
foreach ($this->generateHooks() as $file => $hook) {
if ($this->cmd->getArgs()->verbose) {
- $this->cmd->info("Packaging %s\n", is_string($hook) ? $hook : $file);
+ $this->cmd->info("Packaging %s\n", is_scalar($hook) ? $hook : $file);
}
yield $file => $hook;
}
foreach ($this->sxe->xpath("//pecl:file") as $file) {
$path = $this->path ."/". $this->dirOf($file) ."/". $file["name"];
if ($this->cmd->getArgs()->verbose) {
- $this->cmd->info("Packaging %s\n", $path);
+ $this->cmd->info("Packaging %s\n", substr($path, strlen($this->path)));
}
if (!($realpath = realpath($path))) {
- $this->cmd->error("File %s does not exist", $path);
+ $this->cmd->warn("File %s does not exist", $path);
}
yield $realpath;
}
namespace pharext\SourceDir;
use pharext\Command;
+use pharext\Exception;
use pharext\SourceDir;
/**
$callable = include "$path/pharext_package.php";
if (!is_callable($callable)) {
- throw new \Exception("Package hook did not return a callable");
+ throw new Exception("Package hook did not return a callable");
}
$this->iter = $callable($cmd, $path);
}
--- /dev/null
+<?php
+
+namespace pharext;
+
+/**
+ * Simple task interface
+ */
+interface Task
+{
+ public function run($verbose = false);
+}
--- /dev/null
+<?php
+
+namespace pharext\Task;
+
+use pharext\Exception;
+use pharext\ExecCmd;
+use pharext\Task;
+use pharext\Tempfile;
+
+/**
+ * PHP INI activation
+ */
+class Activate implements Task
+{
+ /**
+ * @var string
+ */
+ private $cwd;
+
+ /**
+ * @var array
+ */
+ private $inis;
+
+ /**
+ * @var string
+ */
+ private $sudo;
+
+ /**
+ * @param string $cwd working directory
+ * @param array $inis custom INI or list of loaded/scanned INI files
+ * @param string $sudo sudo command
+ * @throws \pharext\Exception
+ */
+ public function __construct($cwd, array $inis, $sudo = null) {
+ $this->cwd = $cwd;
+ $this->sudo = $sudo;
+ if (!$this->inis = $inis) {
+ throw new Exception("No PHP INIs given");
+ }
+ }
+
+ /**
+ * @param bool $verbose
+ * @return boolean false, if extension was already activated
+ */
+ public function run($verbose = false) {
+ $extension = basename(current(glob("{$this->cwd}/modules/*.so")));
+ $pattern = preg_quote($extension);
+
+ foreach ($this->inis as $file) {
+ $temp = new Tempfile("phpini");
+ foreach (file($file) as $line) {
+ if (preg_match("/^\s*extension\s*=\s*[\"']?{$pattern}[\"']?\s*(;.*)?\$/", $line)) {
+ return false;
+ }
+ fwrite($temp->getStream(), $line);
+ }
+ }
+
+ /* not found; append to last processed file, which is the main by default */
+ fprintf($temp->getStream(), "extension=%s\n", $extension);
+ $temp->closeStream();
+
+ $path = $temp->getPathname();
+ $stat = stat($file);
+
+ // owner transfer
+ $ugid = sprintf("%d:%d", $stat["uid"], $stat["gid"]);
+ $cmd = new ExecCmd("chown", $verbose);
+ if (isset($this->sudo)) {
+ $cmd->setSu($this->sudo);
+ }
+ $cmd->run([$ugid, $path]);
+
+ // permission transfer
+ $perm = decoct($stat["mode"] & 0777);
+ $cmd = new ExecCmd("chmod", $verbose);
+ if (isset($this->sudo)) {
+ $cmd->setSu($this->sudo);
+ }
+ $cmd->run([$perm, $path]);
+
+ // rename
+ $cmd = new ExecCmd("mv", $verbose);
+ if (isset($this->sudo)) {
+ $cmd->setSu($this->sudo);
+ }
+ $cmd->run([$path, $file]);
+
+ return true;
+ }
+}
--- /dev/null
+<?php
+
+namespace pharext\Task;
+
+use pharext\Exception;
+use pharext\Task;
+
+/**
+ * Ask password on console
+ */
+class Askpass implements Task
+{
+ /**
+ * @var string
+ */
+ private $prompt;
+
+ /**
+ * @param string $prompt
+ */
+ public function __construct($prompt = "Password:") {
+ $this->prompt = $prompt;
+ }
+
+ /**
+ * @param bool $verbose
+ * @return string
+ */
+ public function run($verbose = false) {
+ system("stty -echo");
+ printf("%s ", $this->prompt);
+ $pass = fgets(STDIN, 1024);
+ printf("\n");
+ system("stty echo");
+ if (substr($pass, -1) == "\n") {
+ $pass = substr($pass, 0, -1);
+ }
+ return $pass;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+namespace pharext\Task;
+
+use pharext\Task;
+
+use RecursiveDirectoryIterator;
+use RecursiveIteratorIterator;
+
+/**
+ * List all library files of pharext to bundle with a phar
+ */
+class BundleGenerator implements Task
+{
+ /**
+ * @param bool $verbose
+ * @return Generator
+ */
+ public function run($verbose = false) {
+ $rdi = new RecursiveDirectoryIterator(dirname(dirname(__DIR__)));
+ $rii = new RecursiveIteratorIterator($rdi);
+ for ($rii->rewind(); $rii->valid(); $rii->next()) {
+ if (!$rii->isDot()) {
+ yield $rii->getSubPathname() => $rii->key();
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+
+namespace pharext\Task;
+
+use pharext\Exception;
+use pharext\ExecCmd;
+use pharext\Task;
+
+/**
+ * Runs extension's configure
+ */
+class Configure implements Task
+{
+ /**
+ * @var array
+ */
+ private $args;
+
+ /**
+ * @var string
+ */
+ private $cwd;
+
+ /**
+ * @param string $cwd working directory
+ * @param array $args configure args
+ * @param string $prefix install prefix, e.g. /usr/local
+ * @param string $common_name PHP programs common name, e.g. php5
+ */
+ public function __construct($cwd, array $args = null, $prefix = null, $common_name = "php") {
+ $this->cwd = $cwd;
+ $cmd = $common_name . "-config";
+ if (isset($prefix)) {
+ $cmd = $prefix . "/bin/" . $cmd;
+ }
+ $this->args = ["--with-php-config=$cmd"];
+ if ($args) {
+ $this->args = array_merge($this->args, $args);
+ }
+ }
+
+ public function run($verbose = false) {
+ $pwd = getcwd();
+ if (!chdir($this->cwd)) {
+ throw new Exception;
+ }
+ try {
+ $cmd = new ExecCmd("./configure", $verbose);
+ $cmd->run($this->args);
+ } finally {
+ chdir($pwd);
+ }
+ }
+}
--- /dev/null
+<?php
+
+namespace pharext\Task;
+
+use pharext\Task;
+use pharext\Tempdir;
+
+use Phar;
+use PharData;
+
+/**
+ * Extract a package archive
+ */
+class Extract implements Task
+{
+ /**
+ * @var Phar(Data)
+ */
+ private $source;
+
+ /**
+ * @param mixed $source archive location
+ */
+ public function __construct($source) {
+ if ($source instanceof Phar || $source instanceof PharData) {
+ $this->source = $source;
+ } else {
+ $this->source = new PharData($source);
+ }
+ }
+
+ /**
+ * @param bool $verbose
+ * @return \pharext\Tempdir
+ */
+ public function run($verbose = false) {
+ $dest = new Tempdir("extract");
+ $this->source->extractTo($dest);
+ return $dest;
+ }
+}
--- /dev/null
+<?php
+
+namespace pharext\Task;
+
+use pharext\ExecCmd;
+use pharext\Task;
+use pharext\Tempdir;
+
+/**
+ * Clone a git repo
+ */
+class GitClone implements Task
+{
+ /**
+ * @var string
+ */
+ private $source;
+
+ /**
+ * @param string $source git repo location
+ */
+ public function __construct($source) {
+ $this->source = $source;
+ }
+
+ /**
+ * @param bool $verbose
+ * @return \pharext\Tempdir
+ */
+ public function run($verbose = false) {
+ $local = new Tempdir("gitclone");
+ $cmd = new ExecCmd("git", $verbose);
+ $cmd->run(["clone", $this->source, $local]);
+ return $local;
+ }
+}
--- /dev/null
+<?php
+
+namespace pharext\Task;
+
+use pharext\ExecCmd;
+use pharext\Exception;
+use pharext\Task;
+
+/**
+ * Run make in the source dir
+ */
+class Make implements Task
+{
+ /**
+ * @var string
+ */
+ private $cwd;
+
+ /**
+ * @var array
+ */
+ private $args;
+
+ /**
+ * @var string
+ */
+ private $sudo;
+
+ /**
+ *
+ * @param string $cwd working directory
+ * @param array $args make's arguments
+ * @param string $sudo sudo command
+ */
+ public function __construct($cwd, array $args = null, $sudo = null) {
+ $this->cwd = $cwd;
+ $this->sudo = $sudo;
+ $this->args = $args;
+ }
+
+ /**
+ *
+ * @param bool $verbose
+ * @throws \pharext\Exception
+ */
+ public function run($verbose = false) {
+ $pwd = getcwd();
+ if (!chdir($this->cwd)) {
+ throw new Exception;
+ }
+ try {
+ $cmd = new ExecCmd("make", $verbose);
+ if (isset($this->sudo)) {
+ $cmd->setSu($this->sudo);
+ }
+ $args = $this->args;
+ if (!$verbose) {
+ $args = array_merge((array) $args, ["-s"]);
+ }
+ $cmd->run($args);
+ } finally {
+ chdir($pwd);
+ }
+ }
+}
--- /dev/null
+<?php
+
+namespace pharext\Task;
+
+use pharext\Exception;
+use pharext\Task;
+
+/**
+ * Fixup package.xml files in an extracted PECL dir
+ */
+class PeclFixup implements Task
+{
+ /**
+ * @var string
+ */
+ private $source;
+
+ /**
+ * @param string $source source directory
+ */
+ public function __construct($source) {
+ $this->source = $source;
+ }
+
+ /**
+ * @param bool $verbose
+ * @return string sanitized source location
+ * @throws \pahrext\Exception
+ */
+ public function run($verbose = false) {
+ $dirs = glob("{$this->source}/*", GLOB_ONLYDIR);
+ $files = array_diff(glob("{$this->source}/*"), $dirs);
+
+ if (count($dirs) !== 1 || !count($files)) {
+ throw new Exception("Does not look like an extracted PECL dir: {$this->source}");
+ }
+
+ $dest = current($dirs);
+
+ foreach ($files as $file) {
+ if (!rename($file, "$dest/" . basename($file))) {
+ throw new Exception;
+ }
+ }
+
+ return $dest;
+ }
+}
--- /dev/null
+<?php
+
+namespace pharext\Task;
+
+use pharext\Exception;
+use pharext\SourceDir;
+use pharext\Task;
+use pharext\Tempname;
+
+use Phar;
+
+/**
+ * Build phar
+ */
+class PharBuild implements Task
+{
+ /**
+ * @var \pharext\SourceDir
+ */
+ private $source;
+
+ /**
+ * @var array
+ */
+ private $meta;
+
+ /**
+ * @var bool
+ */
+ private $readonly;
+
+ /**
+ * @param SourceDir $source extension source directory
+ * @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) {
+ $this->source = $source;
+ $this->meta = $meta;
+ $this->readonly = $readonly;
+ }
+
+ /**
+ * @param bool $verbose
+ * @return \pharext\Tempname
+ * @throws \pharext\Exception
+ */
+ public function run($verbose = false) {
+ /* Phar::compress() and ::convert*() use strtok("."), ugh!
+ * so, be sure to not use any other dots in the filename
+ * except for .phar
+ */
+ $temp = new Tempname("", "-pharext.phar");
+
+ $phar = new Phar($temp);
+ $phar->startBuffering();
+
+ 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());
+ }
+ }
+
+ $phar->buildFromIterator((new Task\BundleGenerator)->run());
+
+ if ($this->source) {
+ $phar->buildFromIterator($this->source, $this->source->getBaseDir());
+ }
+
+ $phar->stopBuffering();
+
+ if (!chmod($temp, fileperms($temp) | 0111)) {
+ throw new Exception;
+ }
+
+ return $temp;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+namespace pharext\Task;
+
+use pharext\Task;
+
+use Phar;
+
+/**
+ * Clone a compressed copy of a phar
+ */
+class PharCompress implements Task
+{
+ /**
+ * @var string
+ */
+ private $file;
+
+ /**
+ * @var Phar
+ */
+ private $package;
+
+ /**
+ * @var int
+ */
+ private $encoding;
+
+ /**
+ * @var string
+ */
+ private $extension;
+
+ /**
+ * @param string $file path to the original phar
+ * @param int $encoding Phar::GZ or Phar::BZ2
+ */
+ public function __construct($file, $encoding) {
+ $this->file = $file;
+ $this->package = new Phar($file);
+ $this->encoding = $encoding;
+
+ switch ($encoding) {
+ case Phar::GZ:
+ $this->extension = ".gz";
+ break;
+ case Phar::BZ2:
+ $this->extension = ".bz2";
+ break;
+ }
+ }
+
+ /**
+ * @param bool $verbose
+ * @return string
+ */
+ public function run($verbose = false) {
+ $phar = $this->package->compress($this->encoding);
+ $meta = $phar->getMetadata();
+ if (isset($meta["stub"])) {
+ /* drop shebang */
+ $phar->setDefaultStub($meta["stub"]);
+ }
+ return $this->file . $this->extension;
+ }
+}
--- /dev/null
+<?php
+
+namespace pharext\Task;
+
+use pharext\Exception;
+use pharext\Task;
+
+/**
+ * Rename the phar archive
+ */
+class PharRename implements Task
+{
+ /**
+ * @var string
+ */
+ private $phar;
+
+ /**
+ * @var string
+ */
+ private $dest;
+
+ /**
+ * @var string
+ */
+ private $name;
+
+ /**
+ * @param string $phar path to phar
+ * @param string $dest destination dir
+ * @param string $name package name
+ */
+ public function __construct($phar, $dest, $name) {
+ $this->phar = $phar;
+ $this->dest = $dest;
+ $this->name = $name;
+ }
+
+ /**
+ * @param bool $verbose
+ * @return string path to renamed phar
+ * @throws \pharext\Exception
+ */
+ public function run($verbose = false) {
+ $extension = substr(strstr($this->phar, "-pharext.phar"), 8);
+ $name = sprintf("%s/%s.ext%s", $this->dest, $this->name, $extension);
+
+ if (!rename($this->phar, $name)) {
+ throw new Exception;
+ }
+
+ return $name;
+ }
+}
--- /dev/null
+<?php
+
+namespace pharext\Task;
+
+use pharext\Openssl;
+use pharext\Task;
+
+use Phar;
+
+/**
+ * Sign the phar with a private key
+ */
+class PharSign implements Task
+{
+ /**
+ * @var Phar
+ */
+ private $phar;
+
+ /**
+ * @var \pharext\Openssl\PrivateKey
+ */
+ private $pkey;
+
+ /**
+ *
+ * @param mixed $phar phar instance or path to phar
+ * @param string $pkey path to private key
+ * @param string $pass password for the private key
+ */
+ public function __construct($phar, $pkey, $pass) {
+ if ($phar instanceof Phar || $phar instanceof PharData) {
+ $this->phar = $phar;
+ } else {
+ $this->phar = new Phar($phar);
+ }
+ $this->pkey = new Openssl\PrivateKey($pkey, $pass);
+ }
+
+ /**
+ * @param bool $verbose
+ * @return \pharext\Openssl\PrivateKey
+ */
+ public function run($verbose = false) {
+ $this->pkey->sign($this->phar);
+ return $this->pkey;
+ }
+}
--- /dev/null
+<?php
+
+namespace pharext\Task;
+
+use pharext\Exception;
+use pharext\ExecCmd;
+use pharext\Task;
+
+/**
+ * Run phpize in the extension source directory
+ */
+class Phpize implements Task
+{
+ /**
+ * @var string
+ */
+ private $phpize;
+
+ /**
+ *
+ * @var string
+ */
+ private $cwd;
+
+ /**
+ * @param string $cwd working directory
+ * @param string $prefix install prefix, e.g. /usr/local
+ * @param string $common_name PHP program common name, e.g. php5
+ */
+ public function __construct($cwd, $prefix = null, $common_name = "php") {
+ $this->cwd = $cwd;
+ $cmd = $common_name . "ize";
+ if (isset($prefix)) {
+ $cmd = $prefix . "/bin/" . $cmd;
+ }
+ $this->phpize = $cmd;
+ }
+
+ /**
+ * @param bool $verbose
+ * @throws \pharext\Exception
+ */
+ public function run($verbose = false) {
+ $pwd = getcwd();
+ if (!chdir($this->cwd)) {
+ throw new Exception;
+ }
+ try {
+ $cmd = new ExecCmd($this->phpize, $verbose);
+ $cmd->run();
+ } finally {
+ chdir($pwd);
+ }
+ }
+}
--- /dev/null
+<?php
+
+namespace pharext\Task;
+
+use pharext\Exception;
+use pharext\Task;
+use pharext\Tempfile;
+
+/**
+ * Fetch a remote archive
+ */
+class StreamFetch implements Task
+{
+ /**
+ * @var string
+ */
+ private $source;
+
+ /**
+ * @var callable
+ */
+ private $progress;
+
+ /**
+ * @param string $source remote file location
+ * @param callable $progress progress callback
+ */
+ public function __construct($source, callable $progress) {
+ $this->source = $source;
+ $this->progress = $progress;
+ }
+
+ private function createStreamContext() {
+ $progress = $this->progress;
+
+ return stream_context_create([],["notification" => function($notification, $severity, $message, $code, $bytes_cur, $bytes_max) use($progress) {
+ switch ($notification) {
+ case STREAM_NOTIFY_CONNECT:
+ $progress(0);
+ break;
+ case STREAM_NOTIFY_PROGRESS:
+ $progress($bytes_max ? $bytes_cur/$bytes_max : .5);
+ break;
+ case STREAM_NOTIFY_COMPLETED:
+ /* this is not generated, why? */
+ $progress(1);
+ break;
+ }
+ }]);
+ }
+
+ /**
+ * @param bool $verbose
+ * @return \pharext\Task\Tempfile
+ * @throws \pharext\Exception
+ */
+ public function run($verbose = false) {
+ $context = $this->createStreamContext();
+
+ if (!$remote = fopen($this->source, "r", false, $context)) {
+ throw new Exception;
+ }
+
+ $local = new Tempfile("remote");
+ if (!stream_copy_to_stream($remote, $local->getStream())) {
+ throw new Exception;
+ }
+ $local->closeStream();
+
+ /* STREAM_NOTIFY_COMPLETED is not generated, see above */
+ call_user_func($this->progress, 1);
+
+ return $local;
+ }
+}
namespace pharext;
+/**
+ * Create a temporary directory
+ */
class Tempdir extends \SplFileInfo
{
- private $dir;
-
+ /**
+ * @param string $prefix prefix to uniqid()
+ * @throws \pharext\Exception
+ */
public function __construct($prefix) {
- $temp = sprintf("%s/%s", sys_get_temp_dir(), uniqid($prefix));
- if (!is_dir($temp)) {
- if (!mkdir($temp, 0700, true)) {
- throw new Exception("Could not create tempdir: ".error_get_last()["message"]);
- }
+ $temp = new Tempname($prefix);
+ if (!is_dir($temp) && !mkdir($temp, 0700, true)) {
+ throw new Exception("Could not create tempdir: ".error_get_last()["message"]);
}
parent::__construct($temp);
}
namespace pharext;
+/**
+ * Create a new temporary file
+ */
class Tempfile extends \SplFileInfo
{
+ /**
+ * @var resource
+ */
private $handle;
-
- function __construct($prefix) {
+
+ /**
+ * @param string $prefix uniqid() prefix
+ * @param string $suffix e.g. file extension
+ * @throws \pharext\Exception
+ */
+ public function __construct($prefix, $suffix = ".tmp") {
$tries = 0;
- /* PharData needs a dot in the filename, sure */
- $temp = sys_get_temp_dir() . "/";
-
$omask = umask(077);
do {
- $path = $temp.uniqid($prefix).".tmp";
+ $path = new Tempname($prefix, $suffix);
$this->handle = fopen($path, "x");
} while (!is_resource($this->handle) && $tries++ < 10);
umask($omask);
if (!is_resource($this->handle)) {
- throw new \Exception("Could not create temporary file");
+ throw new Exception("Could not create temporary file");
}
parent::__construct($path);
}
-
- function __destruct() {
+
+ /**
+ * Unlink the file
+ */
+ public function __destruct() {
@unlink($this->getPathname());
}
-
- function closeStream() {
+
+ /**
+ * Close the stream
+ */
+ public function closeStream() {
fclose($this->handle);
}
- function getStream() {
+ /**
+ * Retrieve the stream resource
+ * @return resource
+ */
+ public function getStream() {
return $this->handle;
}
}
--- /dev/null
+<?php
+
+namespace pharext;
+
+/**
+ * A temporary file/directory name
+ */
+class Tempname
+{
+ /**
+ * @var string
+ */
+ private $name;
+
+ /**
+ * @param string $prefix uniqid() prefix
+ * @param string $suffix e.g. file extension
+ */
+ public function __construct($prefix, $suffix = null) {
+ $this->name = sys_get_temp_dir() . "/" . uniqid($prefix) . $suffix;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString() {
+ return (string) $this->name;
+ }
+}
<?='<?php'?>
/**
- * Generated by pharext v<?=pharext\VERSION?> at <?=date("Y-m-d H:i:i T")?>.
+ * Generated by pharext v<?=pharext\VERSION?> at <?=date("Y-m-d H:i:s T")?>.
*/
namespace pharext;