6 use pharext\Cli\Args
as CliArgs
;
7 use pharext\Cli\Command
as CliCommand
;
10 * The extension packaging command executed by bin/pharext
12 class Packager
implements Command
17 * Extension source directory
18 * @var pharext\SourceDir
26 private $cleanup = [];
31 public function __construct() {
32 $this->args
= new CliArgs([
33 ["h", "help", "Display this help",
34 CliArgs
::OPTIONAL|CliArgs
::SINGLE|CliArgs
::NOARG|CliArgs
::HALT
],
35 ["v", "verbose", "More output",
36 CliArgs
::OPTIONAL|CliArgs
::SINGLE|CliArgs
::NOARG
],
37 ["q", "quiet", "Less output",
38 CliArgs
::OPTIONAL|CliArgs
::SINGLE|CliArgs
::NOARG
],
39 ["n", "name", "Extension name",
40 CliArgs
::REQUIRED|CliArgs
::SINGLE|CliArgs
::REQARG
],
41 ["r", "release", "Extension release version",
42 CliArgs
::REQUIRED|CliArgs
::SINGLE|CliArgs
::REQARG
],
43 ["s", "source", "Extension source directory",
44 CliArgs
::REQUIRED|CliArgs
::SINGLE|CliArgs
::REQARG
],
45 ["g", "git", "Use `git ls-tree` to determine file list",
46 CliArgs
::OPTIONAL|CliArgs
::SINGLE|CliArgs
::NOARG
],
47 ["p", "pecl", "Use PECL package.xml to determine file list, name and release",
48 CliArgs
::OPTIONAL|CliArgs
::SINGLE|CliArgs
::NOARG
],
49 ["d", "dest", "Destination directory",
50 CliArgs
::OPTIONAL|CliArgs
::SINGLE|CliArgs
::REQARG
,
52 ["z", "gzip", "Create additional PHAR compressed with gzip",
53 CliArgs
::OPTIONAL|CliArgs
::SINGLE|CliArgs
::NOARG
],
54 ["Z", "bzip", "Create additional PHAR compressed with bzip",
55 CliArgs
::OPTIONAL|CliArgs
::SINGLE|CliArgs
::NOARG
],
56 ["S", "sign", "Sign the PHAR with a private key",
57 CliArgs
::OPTIONAL|CliArgs
::SINGLE|CliArgs
::REQARG
],
58 ["E", "zend", "Mark as Zend Extension",
59 CliArgs
::OPTIONAL|CliARgs
::SINGLE|CliArgs
::NOARG
],
60 [null, "signature", "Show pharext signature",
61 CliArgs
::OPTIONAL|CliArgs
::SINGLE|CliArgs
::NOARG|CliArgs
::HALT
],
62 [null, "license", "Show pharext license",
63 CliArgs
::OPTIONAL|CliArgs
::SINGLE|CliArgs
::NOARG|CliArgs
::HALT
],
64 [null, "version", "Show pharext version",
65 CliArgs
::OPTIONAL|CliArgs
::SINGLE|CliArgs
::NOARG|CliArgs
::HALT
],
72 function __destruct() {
73 foreach ($this->cleanup
as $cleanup) {
74 if (is_dir($cleanup)) {
76 } elseif (file_exists($cleanup)) {
84 * @see \pharext\Command::run()
86 public function run($argc, array $argv) {
88 $prog = array_shift($argv);
89 foreach ($this->args
->parse(--$argc, $argv) as $error) {
93 if ($this->args
["help"]) {
99 foreach (["signature", "license", "version"] as $opt) {
100 if ($this->args
[$opt]) {
101 printf("%s\n", $this->metadata($opt));
105 } catch (\Exception
$e) {
106 $this->error("%s\n", $e->getMessage());
111 /* source needs to be evaluated before CliArgs validation,
112 * so e.g. name and version can be overriden and CliArgs
113 * does not complain about missing arguments
115 if ($this->args
["source"]) {
116 $source = $this->localize($this->args
["source"]);
117 if ($this->args
["pecl"]) {
118 $this->source
= new SourceDir\
Pecl($this, $source);
119 } elseif ($this->args
["git"]) {
120 $this->source
= new SourceDir\
Git($this, $source);
122 $this->source
= new SourceDir\
Pharext($this, $source);
125 } catch (\Exception
$e) {
126 $errs[] = $e->getMessage();
129 foreach ($this->args
->validate() as $error) {
134 if (!$this->args
["quiet"]) {
137 foreach ($errs as $err) {
138 $this->error("%s\n", $err);
141 if (!$this->args
["quiet"]) {
147 $this->createPackage();
151 * Download remote source
152 * @param string $source
153 * @return string local source
155 private function download($source) {
156 $this->info("Fetching remote source %s ...\n", $source);
158 if ($this->args
->git
) {
159 $task = new Task\
GitClone($source);
161 $task = new Task\
StreamFetch($source, function($bytes_pct) {
162 $this->debug(" %3d%% [%s>%s] \r",
163 floor($bytes_pct*100),
164 str_repeat("=", round(50*$bytes_pct)),
165 str_repeat(" ", round(50*(1-$bytes_pct)))
169 $local = $task->run($this->args
->verbose
);
172 $this->cleanup
[] = $local;
177 * Extract local archive
178 * @param stirng $source
179 * @return string extracted directory
181 private function extract($source) {
182 $this->debug("Extracting %s ...\n", $source);
184 $task = new Task\
Extract($source);
185 $dest = $task->run($this->args
->verbose
);
187 $this->cleanup
[] = $dest;
192 * Localize a possibly remote source
193 * @param string $source
194 * @return string local source directory
196 private function localize($source) {
197 if (!stream_is_local($source)) {
198 $source = $this->download($source);
199 $this->cleanup
[] = $source;
201 $source = realpath($source);
202 if (!is_dir($source)) {
203 $source = $this->extract($source);
204 $this->cleanup
[] = $source;
206 if ($this->args
->pecl
) {
207 $this->debug("Sanitizing PECL dir ...\n");
208 $source = (new Task\
PeclFixup($source))->run($this->args
->verbose
);
215 * Creates the extension phar
217 private function createPackage() {
219 $meta = array_merge($this->metadata(), [
220 "date" => date("Y-m-d"),
221 "name" => $this->args
->name
,
222 "release" => $this->args
->release
,
223 "license" => @file_get_contents
(current(glob($this->source
->getBaseDir()."/LICENSE*"))),
224 "stub" => "pharext_installer.php",
225 "type" => $this->args
->zend ?
"zend_extension" : "extension",
227 $file = (new Task\
PharBuild($this->source
, $meta))->run();
229 if ($this->args
->sign
) {
230 $this->info("Using private key to sign phar ...\n");
231 $pass = (new Task\Askpass
)->run($this->args
->verbose
);
232 $sign = new Task\
PharSign($file, $this->args
->sign
, $pass);
233 $pkey = $sign->run($this->args
->verbose
);
236 } catch (\Exception
$e) {
237 $this->error("%s\n", $e->getMessage());
241 if ($this->args
->gzip
) {
243 $gzip = (new Task\
PharCompress($file, Phar
::GZ
))->run();
244 $move = new Task\
PharRename($gzip, $this->args
->dest
, $this->args
->name
."-". $this->args
->release
);
245 $name = $move->run($this->args
->verbose
);
247 $this->info("Created gzipped phar %s\n", $name);
249 if ($this->args
->sign
) {
250 $sign = new Task\
PharSign($name, $this->args
->sign
, $pass);
251 $sign->run($this->args
->verbose
)->exportPublicKey($name.".pubkey");
254 } catch (\Exception
$e) {
255 $this->warn("%s\n", $e->getMessage());
259 if ($this->args
->bzip
) {
261 $bzip = (new Task\
PharCompress($file, Phar
::BZ2
))->run();
262 $move = new Task\
PharRename($bzip, $this->args
->dest
, $this->args
->name
."-". $this->args
->release
);
263 $name = $move->run($this->args
->verbose
);
265 $this->info("Created bzipped phar %s\n", $name);
267 if ($this->args
->sign
) {
268 $sign = new Task\
PharSign($name, $this->args
->sign
, $pass);
269 $sign->run($this->args
->verbose
)->exportPublicKey($name.".pubkey");
272 } catch (\Exception
$e) {
273 $this->warn("%s\n", $e->getMessage());
278 $move = new Task\
PharRename($file, $this->args
->dest
, $this->args
->name
."-". $this->args
->release
);
279 $name = $move->run($this->args
->verbose
);
281 $this->info("Created executable phar %s\n", $name);
284 $pkey->exportPublicKey($name.".pubkey");
287 } catch (\Exception
$e) {
288 $this->error("%s\n", $e->getMessage());