#!/usr/bin/php -dphar.readonly=0 2, 'c' => 'text/plain', 'cc' => 'text/plain', 'cpp' => 'text/plain', 'c++' => 'text/plain', 'dtd' => 'text/plain', 'h' => 'text/plain', 'log' => 'text/plain', 'rng' => 'text/plain', 'txt' => 'text/plain', 'xsd' => 'text/plain', 'php' => 1, 'inc' => 1, 'avi' => 'video/avi', 'bmp' => 'image/bmp', 'css' => 'text/css', 'gif' => 'image/gif', 'htm' => 'text/html', 'html' => 'text/html', 'htmls' => 'text/html', 'ico' => 'image/x-ico', 'jpe' => 'image/jpeg', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'js' => 'application/x-javascript', 'midi' => 'audio/midi', 'mid' => 'audio/midi', 'mod' => 'audio/mod', 'mov' => 'movie/quicktime', 'mp3' => 'audio/mp3', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'pdf' => 'application/pdf', 'png' => 'image/png', 'swf' => 'application/shockwave-flash', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'wav' => 'audio/wav', 'xbm' => 'image/xbm', 'xml' => 'text/xml', ); header("Cache-Control: no-cache, must-revalidate"); header("Pragma: no-cache"); $basename = basename(__FILE__); if (!strpos($_SERVER['REQUEST_URI'], $basename)) { chdir(Extract_Phar::$temp); include $web; return; } $pt = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], $basename) + strlen($basename)); if (!$pt || $pt == '/') { $pt = $web; header('HTTP/1.1 301 Moved Permanently'); header('Location: ' . $_SERVER['REQUEST_URI'] . '/' . $pt); exit; } $a = realpath(Extract_Phar::$temp . DIRECTORY_SEPARATOR . $pt); if (!$a || strlen(dirname($a)) < strlen(Extract_Phar::$temp)) { header('HTTP/1.0 404 Not Found'); echo "\n \n File Not Found<title>\n </head>\n <body>\n <h1>404 - File ", $pt, " Not Found</h1>\n </body>\n</html>"; exit; } $b = pathinfo($a); if (!isset($b['extension'])) { header('Content-Type: text/plain'); header('Content-Length: ' . filesize($a)); readfile($a); exit; } if (isset($mimes[$b['extension']])) { if ($mimes[$b['extension']] === 1) { include $a; exit; } if ($mimes[$b['extension']] === 2) { highlight_file($a); exit; } header('Content-Type: ' .$mimes[$b['extension']]); header('Content-Length: ' . filesize($a)); readfile($a); exit; } } class Extract_Phar { static $temp; static $origdir; const GZ = 0x1000; const BZ2 = 0x2000; const MASK = 0x3000; const START = 'pharext_packager.php'; const LEN = 6696; static function go($return = false) { $fp = fopen(__FILE__, 'rb'); fseek($fp, self::LEN); $L = unpack('V', $a = (binary)fread($fp, 4)); $m = (binary)''; do { $read = 8192; if ($L[1] - strlen($m) < 8192) { $read = $L[1] - strlen($m); } $last = (binary)fread($fp, $read); $m .= $last; } while (strlen($last) && strlen($m) < $L[1]); if (strlen($m) < $L[1]) { die('ERROR: manifest length read was "' . strlen($m) .'" should be "' . $L[1] . '"'); } $info = self::_unpack($m); $f = $info['c']; if ($f & self::GZ) { if (!function_exists('gzinflate')) { die('Error: zlib extension is not enabled -' . ' gzinflate() function needed for zlib-compressed .phars'); } } if ($f & self::BZ2) { if (!function_exists('bzdecompress')) { die('Error: bzip2 extension is not enabled -' . ' bzdecompress() function needed for bz2-compressed .phars'); } } $temp = self::tmpdir(); if (!$temp || !is_writable($temp)) { $sessionpath = session_save_path(); if (strpos ($sessionpath, ";") !== false) $sessionpath = substr ($sessionpath, strpos ($sessionpath, ";")+1); if (!file_exists($sessionpath) || !is_dir($sessionpath)) { die('Could not locate temporary directory to extract phar'); } $temp = $sessionpath; } $temp .= '/pharextract/'.basename(__FILE__, '.phar'); self::$temp = $temp; self::$origdir = getcwd(); @mkdir($temp, 0777, true); $temp = realpath($temp); if (!file_exists($temp . DIRECTORY_SEPARATOR . md5_file(__FILE__))) { self::_removeTmpFiles($temp, getcwd()); @mkdir($temp, 0777, true); @file_put_contents($temp . '/' . md5_file(__FILE__), ''); foreach ($info['m'] as $path => $file) { $a = !file_exists(dirname($temp . '/' . $path)); @mkdir(dirname($temp . '/' . $path), 0777, true); clearstatcache(); if ($path[strlen($path) - 1] == '/') { @mkdir($temp . '/' . $path, 0777); } else { file_put_contents($temp . '/' . $path, self::extractFile($path, $file, $fp)); @chmod($temp . '/' . $path, 0666); } } } chdir($temp); if (!$return) { include self::START; } } static function tmpdir() { if (strpos(PHP_OS, 'WIN') !== false) { if ($var = getenv('TMP') ? getenv('TMP') : getenv('TEMP')) { return $var; } if (is_dir('/temp') || mkdir('/temp')) { return realpath('/temp'); } return false; } if ($var = getenv('TMPDIR')) { return $var; } return realpath('/tmp'); } static function _unpack($m) { $info = unpack('V', substr($m, 0, 4)); $l = unpack('V', substr($m, 10, 4)); $m = substr($m, 14 + $l[1]); $s = unpack('V', substr($m, 0, 4)); $o = 0; $start = 4 + $s[1]; $ret['c'] = 0; for ($i = 0; $i < $info[1]; $i++) { $len = unpack('V', substr($m, $start, 4)); $start += 4; $savepath = substr($m, $start, $len[1]); $start += $len[1]; $ret['m'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($m, $start, 24))); $ret['m'][$savepath][3] = sprintf('%u', $ret['m'][$savepath][3] & 0xffffffff); $ret['m'][$savepath][7] = $o; $o += $ret['m'][$savepath][2]; $start += 24 + $ret['m'][$savepath][5]; $ret['c'] |= $ret['m'][$savepath][4] & self::MASK; } return $ret; } static function extractFile($path, $entry, $fp) { $data = ''; $c = $entry[2]; while ($c) { if ($c < 8192) { $data .= @fread($fp, $c); $c = 0; } else { $c -= 8192; $data .= @fread($fp, 8192); } } if ($entry[4] & self::GZ) { $data = gzinflate($data); } elseif ($entry[4] & self::BZ2) { $data = bzdecompress($data); } if (strlen($data) != $entry[0]) { die("Invalid internal .phar file (size error " . strlen($data) . " != " . $stat[7] . ")"); } if ($entry[3] != sprintf("%u", crc32((binary)$data) & 0xffffffff)) { die("Invalid internal .phar file (checksum error)"); } return $data; } static function _removeTmpFiles($temp, $origdir) { chdir($temp); foreach (glob('*') as $f) { if (file_exists($f)) { is_dir($f) ? @rmdir($f) : @unlink($f); if (file_exists($f) && is_dir($f)) { self::_removeTmpFiles($f, getcwd()); } } } @rmdir($temp); clearstatcache(); chdir($origdir); } } Extract_Phar::go(); __HALT_COMPILER(); ?> ��������� ���pharext.phar�������pharext/FilteredSourceDir.phpu��|Tu��7���������pharext/Version.php@���|T@���C뵶���������pharext/PharextSourceDir.php��|T��AtFY���������pharext/CliCommand.php ��|T ��]5r���������pharext/Installer.php��|T�� /���������pharext/PeclSourceDir.php ��|T ��{���������pharext/Command.php;��|T;�����������pharext/SourceDir.php��|T�����������pharext/CliArgs.php#��|T#��9KN���������pharext/Packager.php��|T��O���������pharext/GitSourceDir.php��|T��P���������pharext_installer.php���|T���׶���������pharext_install.tpl.php��|T��q"���������pharext_packager.php���|T����`ƶ������<?php namespace pharext; /** * Generic filtered source directory */ class FilteredSourceDir extends \FilterIterator implements SourceDir { /** * The Packager command * @var pharext\Command */ private $cmd; /** * Base directory * @var string */ private $path; /** * Exclude filters * @var array */ private $filter = [".git/*", ".hg/*"]; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct(Command $cmd, $path) { $this->cmd = $cmd; $this->path = $path; parent::__construct( new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($path, \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::SKIP_DOTS ) ) ); foreach ([".gitignore", ".hgignore"] as $ignore) { if (file_exists("$path/$ignore")) { $this->filter = array_merge($this->filter, array_map(function($pat) { $pat = trim($pat); if (substr($pat, -1) == '/') { $pat .= '*'; } return $pat; }, file("$path/$ignore", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES )) ); } } } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * Implements FilterIterator * @see FilterIterator::accept() */ public function accept() { $fn = $this->key(); if (is_dir($fn)) { if ($this->cmd->getArgs()->verbose) { $this->info("Excluding %s\n", $fn); } return false; } $pl = strlen($this->path) + 1; $pn = substr($this->key(), $pl); foreach ($this->filter as $pat) { if (fnmatch($pat, $pn)) { if ($this->cmd->getArgs()->verbose) { $this->info("Excluding %s\n", $pn); } return false; } } if ($this->cmd->getArgs()->verbose) { $this->info("Packaging %s\n", $pn); } return true; } }<?php namespace pharext; const VERSION = "@PHAREXT_VERSION@"; <?php namespace pharext; class PharextSourceDir implements \IteratorAggregate, SourceDir { private $cmd; private $path; private $iter; public function __construct(Command $cmd, $path) { $this->cmd = $cmd; $this->path = $path; $callable = include "$path/pharext_package.php"; if (!is_callable($callable)) { throw new \Exception("Package hook did not return a callable"); } $this->iter = $callable($cmd, $path); } public function getBaseDir() { return $this->path; } public function getIterator() { if (!is_callable($this->iter)) { throw new \Exception("Package hook callback did not return a callable"); } return call_user_func($this->iter, $this->cmd, $this->path); } }<?php namespace pharext; require_once __DIR__."/Version.php"; trait CliCommand { /** * Command line arguments * @var pharext\CliArgs */ private $args; /** * @inheritdoc * @see \pharext\Command::getArgs() */ public function getArgs() { return $this->args; } /** * Output pharext vX.Y.Z header */ function header() { printf("pharext v%s (c) Michael Wallner <mike@php.net>\n\n", VERSION); } /** * @inheritdoc * @see \pharext\Command::info() */ public function info($fmt) { if (!$this->args->quiet) { vprintf($fmt, array_slice(func_get_args(), 1)); } } /** * @inheritdoc * @see \pharext\Command::error() */ public function error($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, "ERROR: $fmt", $arg); } } /** * Output command line help message * @param string $prog */ public function help($prog) { printf("Usage:\n\n \$ %s", $prog); $flags = []; $required = []; $optional = []; foreach ($this->args->getSpec() as $spec) { if ($spec[3] & CliArgs::REQARG) { if ($spec[3] & CliArgs::REQUIRED) { $required[] = $spec; } else { $optional[] = $spec; } } else { $flags[] = $spec; } } if ($flags) { printf(" [-%s]", implode("", array_column($flags, 0))); } foreach ($required as $req) { printf(" -%s <arg>", $req[0]); } if ($optional) { printf(" [-%s <arg>]", implode("|-", array_column($optional, 0))); } printf("\n\n"); $spc = $this->args->getSpec(); $max = max(array_map("strlen", array_column($spc, 1))); $max += $max % 8 + 2; foreach ($spc as $spec) { if (isset($spec[0])) { printf(" -%s|", $spec[0]); } else { printf(" "); } printf("--%s ", $spec[1]); if ($spec[3] & CliArgs::REQARG) { printf("<arg> "); } elseif ($spec[3] & CliArgs::OPTARG) { printf("[<arg>]"); } else { printf(" "); } printf("%s%s", str_repeat(" ", $max-strlen($spec[1])+3*!isset($spec[0])), $spec[2]); if ($spec[3] & CliArgs::REQUIRED) { printf(" (REQUIRED)"); } if (isset($spec[4])) { printf(" [%s]", $spec[4]); } printf("\n"); } printf("\n"); } /** * Create temporary file/directory name * @param string $prefix * @param string $suffix */ private function tempname($prefix, $suffix = null) { if (!isset($suffix)) { $suffix = uniqid(); } return sprintf("%s/%s.%s", sys_get_temp_dir(), $prefix, $suffix); } }<?php namespace pharext; use Phar; /** * The extension install command executed by the extension phar */ class Installer implements Command { 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 */ public function __construct() { $this->args = new CliArgs([ ["h", "help", "Display help", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ["v", "verbose", "More output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["q", "quiet", "Less 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, "php"], ["c", "configure", "Additional extension configure flags, e.g. -c --with-flag", CliArgs::OPTIONAL|CliArgs::MULTI|CliArgs::REQARG], ["s", "sudo", "Installation might need increased privileges", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::OPTARG, "sudo -S %s"] ]); } /** * Cleanup temp directory */ public function __destruct() { $this->cleanup(); } /** * @inheritdoc * @see \pharext\Command::run() */ public function run($argc, array $argv) { if (($hook = stream_resolve_include_path("pharext_install.php"))) { $callable = include $hook; if (is_callable($callable)) { $recv = $callable($this); } } $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)) { $recv($this); } $this->installPackage(); } /** * Prepares, configures, builds and installs the extension */ private function installPackage() { $this->extract(); if (!chdir($this->tmp)) { $this->error(null); exit(4); } $this->exec("phpize", $this->php("ize")); $this->exec("configure", "./configure --with-php-config=". $this->php("-config") . " ". implode(" ", (array) $this->args->configure)); $this->exec("make", $this->args->verbose ? "make -j3" : "make -sj3"); $this->exec("install", $this->args->verbose ? "make install" : "make -s install", true); $this->cleanup(); $this->info("Don't forget to activiate the extension in your php.ini!\n"); } /** * Perform any cleanups */ private function cleanup() { if (is_dir($this->tmp)) { chdir($this->cwd); $this->info("Cleaning up %s ...\n", $this->tmp); $this->rm($this->tmp); } } /** * Extract the phar to a temporary directory */ private function extract() { if (!$file = Phar::running(false)) { $this->error("Did your run the ext.phar?\n"); exit(3); } $temp = $this->tempname(basename($file)); if (!is_dir($temp)) { if (!mkdir($temp, 0750, true)) { $this->error(null); exit(3); } } $this->tmp = $temp; $this->cwd = getcwd(); try { $phar = new Phar($file); $phar->extractTo($temp, null, true); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(3); } } /** * rm -r * @param string $dir */ private function rm($dir) { foreach (scandir($dir) as $entry) { if ($entry === "." || $entry === "..") { continue; } elseif (is_dir("$dir/$entry")) { $this->rm("$dir/$entry"); } elseif (!unlink("$dir/$entry")) { $this->error(null); } } if (!rmdir($dir)) { $this->error(null); } } /** * Execute a program with escalated privileges handling interactive password prompt * @param string $command * @param string $output * @return int */ private function sudo($command, &$output) { if (!($proc = proc_open($command, [STDIN,["pipe","w"],["pipe","w"]], $pipes))) { return -1; } $stdout = $pipes[1]; $passwd = 0; while (!feof($stdout)) { $R = [$stdout]; $W = []; $E = []; if (!stream_select($R, $W, $E, null)) { continue; } $data = fread($stdout, 0x1000); /* only check a few times */ if ($passwd++ < 10) { if (stristr($data, "password")) { printf("\n%s", $data); } } $output .= $data; } return proc_close($proc); } /** * Execute a system command * @param string $name pretty name * @param string $command full command * @param bool $sudo whether the command may need escalated privileges */ private function exec($name, $command, $sudo = false) { $this->info("Running %s ...%s", $this->args->verbose ? $command : $name, $this->args->verbose ? "\n" : " "); if ($sudo && isset($this->args->sudo)) { $retval = $this->sudo(sprintf($this->args->sudo." 2>&1", $command), $output); } elseif ($this->args->verbose) { passthru($command ." 2>&1", $retval); } else { exec($command ." 2>&1", $output, $retval); $output = implode("\n", $output); } if ($retval) { $this->error("Command %s failed with (%s)\n", $command, $retval); if (isset($output) && !$this->args->quiet) { printf("%s\n", $output); } exit(2); } $this->info("OK\n"); } /** * 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; } } <?php namespace pharext; /** * A PECL extension source directory containing a v2 package.xml */ class PeclSourceDir implements \IteratorAggregate, SourceDir { /** * The Packager command * @var pharext\Packager */ private $cmd; /** * The package.xml * @var SimpleXmlElement */ private $sxe; /** * The base directory * @var string */ private $path; /** * Installer hook * @var string */ private $hook; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct(Command $cmd, $path) { $sxe = simplexml_load_file("$path/package.xml"); $sxe->registerXPathNamespace("pecl", $sxe->getDocNamespaces()[""]); $args = $cmd->getArgs(); 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); } } 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); } } if (($configure = $sxe->xpath("/pecl:package/pecl:extsrcrelease/pecl:configureoption"))) { $this->hook = tmpfile(); ob_start(function($s) { fwrite($this->hook, $s); return null; }); call_user_func(function() use ($configure) { include __DIR__."/../pharext_install.tpl.php"; }); ob_end_flush(); } $this->cmd = $cmd; $this->sxe = $sxe; $this->path = $path; } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * Compute the path of a file by parent dir nodes * @param \SimpleXMLElement $ele * @return string */ private function dirOf($ele) { $path = ""; while (($ele = current($ele->xpath(".."))) && $ele->getName() == "dir") { $path = trim($ele["name"], "/") ."/". $path ; } return trim($path, "/"); } /** * Generate a list of files from the package.xml * @return Generator */ private function generateFiles() { if ($this->hook) { rewind($this->hook); yield "pharext_install.php" => $this->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); } if (!($realpath = realpath($path))) { $this->cmd->error("File %s does not exist", $path); } yield $realpath; } } /** * Implements IteratorAggregate * @see IteratorAggregate::getIterator() */ public function getIterator() { return $this->generateFiles(); } } <?php namespace pharext; /** * Command interface */ interface Command { /** * Retrieve command line arguments * @return pharext\CliArgs */ public function getArgs(); /** * Print info * @param string $fmt * @param string ...$args */ public function info($fmt); /** * Print error * @param string $fmt * @param string ...$args */ public function error($fmt); /** * Execute the command * @param int $argc command line argument count * @param array $argv command line argument list */ public function run($argc, array $argv); } <?php namespace pharext; /** * Source directory interface */ interface SourceDir extends \Traversable { /** * Read the source directory * * Note: Best practices are for others, but if you want to follow them, do * not put constructors in interfaces. Keep your complaints, I warned you. * * @param Command $cmd * @param string $path */ public function __construct(Command $cmd, $path); /** * Retrieve the base directory * @return string */ public function getBaseDir(); } <?php namespace pharext; /** * Command line arguments */ class CliArgs implements \ArrayAccess { /** * Optional option */ const OPTIONAL = 0x000; /** * Required Option */ const REQUIRED = 0x001; /** * Only one value, even when used multiple times */ const SINGLE = 0x000; /** * Aggregate an array, when used multiple times */ const MULTI = 0x010; /** * Option takes no argument */ const NOARG = 0x000; /** * Option requires an argument */ const REQARG = 0x100; /** * Option takes an optional argument */ const OPTARG = 0x200; /** * Option halts processing */ const HALT = 0x10000000; /** * Original option spec * @var array */ private $orig = []; /** * Compiled spec * @var array */ private $spec = []; /** * Parsed args * @var array */ private $args = []; /** * Compile the original spec * @param array $spec */ public function __construct(array $spec = null) { $this->compile($spec); } /** * Compile the original spec * @param array $spec * @return pharext\CliArgs self */ public function compile(array $spec = null) { $this->orig = array_merge($this->orig, $spec); foreach ((array) $spec as $arg) { if (isset($arg[0])) { $this->spec["-".$arg[0]] = $arg; } $this->spec["--".$arg[1]] = $arg; } return $this; } /** * Get original spec * @return array */ public function getSpec() { return $this->orig; } /** * Get compiled spec * @return array */ public function getCompiledSpec() { return $this->spec; } /** * Parse command line arguments according to the compiled spec. * * The Generator yields any parsing errors. * Parsing will stop when all arguments are processed or the first option * flagged CliArgs::HALT was encountered. * * @param int $argc * @param array $argv * @return Generator */ public function parse($argc, array $argv) { for ($i = 0; $i < $argc; ++$i) { $o = $argv[$i]; if ($o{0} === '-' && strlen($o) > 2 && $o{1} !== '-') { // multiple short opts, .e.g -vps $argc += strlen($o) - 2; array_splice($argv, $i, 1, array_map(function($s) { return "-$s"; }, str_split(substr($o, 1)))); $o = $argv[$i]; } elseif ($o{0} === '-' && strlen($o) > 2 && $o{1} === '-' && 0 < ($eq = strpos($o, "="))) { $argc++; array_splice($argv, $i, 1, [ substr($o, 0, $eq++), substr($o, $eq) ]); $o = $argv[$i]; } if (!isset($this->spec[$o])) { yield sprintf("Unknown option %s", $o); } elseif (!$this->optAcceptsArg($o)) { $this[$o] = true; } elseif ($i+1 < $argc && !isset($this->spec[$argv[$i+1]])) { $this[$o] = $argv[++$i]; } elseif ($this->optRequiresArg($o)) { yield sprintf("Option --%s requires an argument", $this->optLongName($o)); } else { // OPTARG $this[$o] = $this->optDefaultArg($o); } if ($this->optHalts($o)) { return; } } } /** * Validate that all required options were given. * * The Generator yields any validation errors. * * @return Generator */ public function validate() { $required = array_filter($this->orig, function($spec) { return $spec[3] & self::REQUIRED; }); foreach ($required as $req) { if (!isset($this[$req[0]])) { yield sprintf("Option --%s is required", $req[1]); } } } /** * Retreive the default argument of an option * @param string $o * @return mixed */ private function optDefaultArg($o) { $o = $this->opt($o); if (isset($this->spec[$o][4])) { return $this->spec[$o][4]; } return null; } /** * Retrieve the help message of an option * @param string $o * @return string */ private function optHelp($o) { $o = $this->opt($o); if (isset($this->spec[$o][2])) { return $this->spec[$o][2]; } return ""; } /** * Retrieve option's flags * @param string $o * @return int */ private function optFlags($o) { $o = $this->opt($o); if (isset($this->spec[$o])) { return $this->spec[$o][3]; } return null; } /** * Check whether an option is flagged for halting argument processing * @param string $o * @return boolean */ private function optHalts($o) { return $this->optFlags($o) & self::HALT; } /** * Check whether an option needs an argument * @param string $o * @return boolean */ private function optRequiresArg($o) { return $this->optFlags($o) & self::REQARG; } /** * Check wether an option accepts any argument * @param string $o * @return boolean */ private function optAcceptsArg($o) { return $this->optFlags($o) & 0xf00; } /** * Check whether an option can be used more than once * @param string $o * @return boolean */ private function optIsMulti($o) { return $this->optFlags($o) & self::MULTI; } /** * Retreive the long name of an option * @param string $o * @return string */ private function optLongName($o) { $o = $this->opt($o); return $this->spec[$o][1]; } /** * Retreive the short name of an option * @param string $o * @return string */ private function optShortName($o) { $o = $this->opt($o); return $this->spec[$o][0]; } /** * Retreive the canonical name (--long-name) of an option * @param string $o * @return string */ private function opt($o) { if ($o{0} !== '-') { if (strlen($o) > 1) { $o = "-$o"; } $o = "-$o"; } return $o; } /**@+ * Implements ArrayAccess and virtual properties */ function offsetExists($o) { $o = $this->opt($o); return isset($this->args[$o]); } function __isset($o) { return $this->offsetExists($o); } function offsetGet($o) { $o = $this->opt($o); if (isset($this->args[$o])) { return $this->args[$o]; } return $this->optDefaultArg($o); } function __get($o) { return $this->offsetGet($o); } function offsetSet($o, $v) { $osn = $this->optShortName($o); $oln = $this->optLongName($o); if ($this->optIsMulti($o)) { if (isset($osn)) { $this->args["-$osn"][] = $v; } $this->args["--$oln"][] = $v; } else { if (isset($osn)) { $this->args["-$osn"] = $v; } $this->args["--$oln"] = $v; } } function __set($o, $v) { $this->offsetSet($o, $v); } function offsetUnset($o) { unset($this->args["-".$this->optShortName($o)]); unset($this->args["--".$this->optLongName($o)]); } function __unset($o) { $this->offsetUnset($o); } /**@-*/ } <?php namespace pharext; use Phar; /** * The extension packaging command executed by bin/pharext */ class Packager implements Command { use CliCommand; /** * Extension source directory * @var pharext\SourceDir */ private $source; /** * Create the command */ public function __construct() { $this->args = new CliArgs([ ["h", "help", "Display this help", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ["v", "verbose", "More output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["q", "quiet", "Less output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["n", "name", "Extension name", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["r", "release", "Extension release version", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["s", "source", "Extension source directory", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["g", "git", "Use `git ls-files` instead of the standard ignore filter", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["p", "pecl", "Use PECL package.xml instead of the standard ignore filter", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["d", "dest", "Destination directory", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG, "."], ["z", "gzip", "Create additional PHAR compressed with gzip", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["Z", "bzip", "Create additional PHAR compressed with bzip", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ]); } /** * @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; } if ($this->args["source"]) { if ($this->args["pecl"]) { $this->source = new PeclSourceDir($this, $this->args["source"]); } elseif ($this->args["git"]) { $this->source = new GitSourceDir($this, $this->args["source"]); } elseif (realpath($this->args["source"]."/pharext_package.php")) { $this->source = new PharextSourceDir($this, $this->args["source"]); } else { $this->source = new FilteredSourceDir($this, $this->args["source"]); } } 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); } $this->createPackage(); } /** * Traverses all pharext source files to bundle * @return Generator */ private function bundle() { foreach (scandir(__DIR__) as $entry) { if (fnmatch("*.php", $entry)) { yield "pharext/$entry" => __DIR__."/$entry"; } } } /** * Creates the extension phar */ private function createPackage() { $pkguniq = uniqid(); $pkgtemp = $this->tempname($pkguniq, "phar"); $pkgdesc = "{$this->args->name}-{$this->args->release}"; $this->info("Creating phar %s ...%s", $pkgtemp, $this->args->verbose ? "\n" : " "); try { $package = new Phar($pkgtemp, 0, "ext.phar"); $package->startBuffering(); $package->buildFromIterator($this->source, $this->source->getBaseDir()); $package->buildFromIterator($this->bundle()); $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(); if (!chmod($pkgtemp, 0777)) { $this->error(null); } elseif ($this->args->verbose) { $this->info("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); $this->info("OK\n"); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); } } if ($this->args->bzip) { $this->info("Compressing with bzip ... "); try { $package->compress(Phar::BZ2); $this->info("OK\n"); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); } } 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"); } } } <?php namespace pharext; /** * Extension source directory which is a git repo */ class GitSourceDir implements \IteratorAggregate, SourceDir { /** * The Packager command * @var pharext\Command */ private $cmd; /** * Base directory * @var string */ private $path; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct(Command $cmd, $path) { $this->cmd = $cmd; $this->path = $path; } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * Generate a list of files by `git ls-files` * @return Generator */ private function generateFiles() { $pwd = getcwd(); chdir($this->path); if (($pipe = popen("git ls-files", "r"))) { while (!feof($pipe)) { if (strlen($file = trim(fgets($pipe)))) { if ($this->cmd->getArgs()->verbose) { $this->cmd->info("Packaging %s\n", $file); } if (!($realpath = realpath($file))) { $this->cmd->error("File %s does not exist\n", $file); } yield $realpath; } } pclose($pipe); } chdir($pwd); } /** * Implements IteratorAggregate * @see IteratorAggregate::getIterator() */ public function getIterator() { return $this->generateFiles(); } } <?php /** * The installer sub-stub for extension phars */ function __autoload($c) { return include strtr($c, "\\_", "//") . ".php"; } $installer = new pharext\Installer(); $installer->run($argc, $argv); <?='<?php'?> /** * Generated by pharext v<?=pharext\VERSION?> at <?=date("Y-m-d H:i:i T")?>. */ namespace pharext; return function(Installer $installer) { $args = $installer->getArgs(); <?php foreach ($configure as $cfg) : ?> $args->compile([[ null, "<?=$cfg["name"]?>", "<?=ucfirst($cfg["prompt"])?>", CliArgs::OPTARG, <?php if (strlen($cfg["default"])) : ?> "<?=$cfg["default"]?>" <?php else : ?> NULL <?php endif; ?> ]]); <?php endforeach; ?> return function(Installer $installer) { $args = $installer->getArgs(); <?php foreach ($configure as $cfg) : ?> if (isset($args["<?=$cfg["name"]?>"])) { $args->configure = "--<?=$cfg["name"]?>=".$args["<?=$cfg["name"]?>"]; } <?php endforeach; ?> }; }; <?php /** * The packager sub-stub for bin/pharext */ function __autoload($c) { return include strtr($c, "\\_", "//") . ".php"; } $packager = new pharext\Packager(); $packager->run($argc, $argv); \N{l  G�a���GBMB