xref: /PHP-8.0/ext/phar/phar/pharcommand.inc (revision f8d79582)
1<?php
2
3/**
4 * @file pharcommand.inc
5 * @ingroup Phar
6 * @brief class CLICommand
7 * @author  Marcus Boerger
8 * @date    2007 - 2008
9 *
10 * Phar Command
11 */
12// {{{ class PharCommand extends CLICommand
13/**
14 * PharCommand class
15 *
16 * This class handles the handling of the phar
17 * commands. It will be used from command line/console
18 * in order to retrieve and execute phar functions.
19 *
20 * @ingroup Phar
21 * @brief   Phar console command implementation
22 * @author  Marcus Boerger
23 * @version 1.0
24 */
25class PharCommand extends CLICommand
26{
27    // {{{ public function cli_get_SP2
28    public function cli_get_SP2($l1, $arg_inf)
29    {
30        return str_repeat(' ', $l1 + 2 + 4 + 9);
31    }
32    // }}}
33    // {{{ public function cli_get_SP3
34    /**
35     * Cli Get SP3
36     *
37     * @param string $l1      Eleven
38     * @param string $l2      Twelve
39     * @param string $arg_inf
40     * @return string  The repeated string.
41     */
42    function cli_get_SP3($l1, $l2, $arg_inf)
43    {
44        return str_repeat(' ', $l1 + 2 + 4 + 9 + 2 + $l2 + 2);
45    }
46    // }}}
47    // {{{ static function phar_args
48    /**
49     * Phar arguments
50     *
51     * This function contains all the phar commands
52     *
53     * @param  string $which    Which argument is chosen.
54     * @param  string $phartype The type of phar, specific file to work on
55     * @return unknown
56     */
57    static function phar_args($which, $phartype)
58    {
59        $phar_args = array(
60            'a' => array(
61                'typ' => 'alias',
62                'val' => NULL,
63                'inf' => '<alias>  Provide an alias name for the phar file.'
64            ),
65            'b' => array(
66                'typ' => 'any',
67                'val' => NULL,
68                'inf' => '<bang>   Hash-bang line to start the archive (e.g. #!/usr/bin/php). The hash '
69                         .'         mark itself \'#!\' and the newline character are optional.'
70            ),
71            'c' => array(
72                'typ' => 'compalg',
73                'val' => NULL,
74                'inf' => '<algo>   Compression algorithm.',
75                'select' => array(
76                    '0'    => 'No compression',
77                    'none' => 'No compression',
78                    'auto' => 'Automatically select compression algorithm'
79                )
80            ),
81            'e' => array(
82                'typ' => 'entry',
83                'val' => NULL,
84                'inf' => '<entry>  Name of entry to work on (must include PHAR internal directory name if any).'
85            ),
86            'f' => array(
87                'typ' => $phartype,
88                'val' => NULL,
89                'inf' => '<file>   Specifies the phar file to work on.'
90            ),
91            'h' => array(
92                'typ' => 'select',
93                'val' => NULL,
94                'inf' => '<method> Selects the hash algorithm.',
95                'select' => array('md5' => 'MD5','sha1' => 'SHA1')
96            ),
97            'i' => array(
98                'typ' => 'regex',
99                'val' => NULL,
100                'inf' => '<regex>  Specifies a regular expression for input files.'
101            ),
102            'k' => array(
103                'typ' => 'any',
104                'val' => NULL,
105                'inf' => '<index>  Subscription index to work on.',
106            ),
107            'l' => array(
108                'typ' => 'int',
109                'val' => 0,
110                'inf' => '<level>  Number of preceding subdirectories to strip from file entries',
111            ),
112            'm' => array(
113                'typ' => 'any',
114                'val' => NULL,
115                'inf' => '<meta>   Meta data to store with entry (serialized php data).'
116            ),
117            'p' => array(
118                'typ' => 'loader',
119                'val' => NULL,
120                'inf' => '<loader> Location of PHP_Archive class file (pear list-files PHP_Archive).'
121                         .'You can use \'0\' or \'1\' to locate it automatically using the mentioned '
122                         .'pear command. When using \'0\' the command does not error out when the '
123                         .'class file cannot be located. This switch also adds some code around the '
124                         .'stub so that class PHP_Archive gets registered as phar:// stream wrapper '
125                         .'if necessary. And finally this switch will add the file phar.inc from '
126                         .'this package and load it to ensure class Phar is present.'
127                         ,
128            ),
129            's' => array(
130                'typ' => 'file',
131                'val' => NULL,
132                'inf' => '<stub>   Select the stub file.'
133            ),
134            'x' => array(
135                'typ' => 'regex',
136                'val' => NULL,
137                'inf' => '<regex>  Regular expression for input files to exclude.'
138            ),
139            'y' => array(
140                'typ' => 'privkey',
141                'val' => NULL,
142                'inf' => '<key>    Private key for OpenSSL signing.',
143            ),
144        );
145
146        if (extension_loaded('zlib')) {
147            $phar_args['c']['select']['gz']    = 'GZip compression';
148            $phar_args['c']['select']['gzip']  = 'GZip compression';
149        }
150
151        if (extension_loaded('bz2')) {
152            $phar_args['c']['select']['bz2']   = 'BZip2 compression';
153            $phar_args['c']['select']['bzip2'] = 'BZip2 compression';
154        }
155
156        $hash_avail = Phar::getSupportedSignatures();
157        $hash_optional = array('SHA-256' => 'SHA256',
158                               'SHA-512' => 'SHA512',
159                               'OpenSSL' => 'OpenSSL');
160        if (!in_array('OpenSSL', $hash_avail)) {
161            unset($phar_args['y']);
162        }
163
164        foreach($hash_optional as $key => $name) {
165            if (in_array($key, $hash_avail)) {
166                $phar_args['h']['select'][strtolower($name)] = $name;
167            }
168        }
169
170        $args = array();
171
172        foreach($phar_args as $lkey => $cfg) {
173            $ukey     = strtoupper($lkey);
174            $required = strpos($which, $ukey) !== false;
175            $optional = strpos($which, $lkey) !== false;
176
177            if ($required || $optional) {
178                $args[$lkey] = $cfg;
179                $args[$lkey]['required'] = $required;
180            }
181        }
182        return $args;
183    }
184    // }}}
185    // {{{ static function strEndsWith
186    /**
187     * String Ends With
188     *
189     * Whether a string ends with another needle.
190     *
191     * @param string $haystack  The haystack
192     * @param string $needle    The needle.
193     * @return mixed false if doesn't end with anything, the string
194     *               substr'ed if the string ends with the needle.
195     */
196    static function strEndsWith($haystack, $needle)
197    {
198        return substr($haystack, -strlen($needle)) == $needle;
199    }
200    // }}}
201    // {{{ static function cli_arg_typ_loader
202    /**
203     * Argument type loader
204     *
205     * @param string $arg   Either 'auto', 'optional' or an filename that
206     *                      contains class PHP_Archive
207     * @param  string $cfg  Configuration to pass to a new file
208     * @param  string $key  The key
209     * @return string $arg  The argument.
210     */
211    static function cli_arg_typ_loader($arg, $cfg, $key)
212    {
213        if (($arg == '0' || $arg == '1') && !file_exists($arg) && substr(PHP_OS, 0, 3) != 'WIN') {
214            $found = NULL;
215            $apiver = false;
216            $path = explode(PATH_SEPARATOR, $_ENV['PATH']);
217            $pear = false;
218            foreach ($path as $component) {
219                if (file_exists($component . DIRECTORY_SEPARATOR . 'pear')
220                    && is_executable($component . DIRECTORY_SEPARATOR . 'pear')) {
221                    $pear = true;
222                    break;
223                }
224            }
225            if ($pear) {
226                $apiver = `pear -q info PHP_Archive 2>/dev/null|grep 'API Version'`;
227                $apiver = trim(substr($apiver, strlen('API Version')));
228            }
229            if ($apiver) {
230                self::notice("PEAR package PHP_Archive: API Version: $apiver.\n");
231                $files  = explode("\n", `pear list-files PHP_Archive`);
232                $phpdir = `pear config-get php_dir 2>/dev/null`;
233                $phpdir = trim($phpdir);
234                self::notice("PEAR package PHP_Archive: $phpdir.\n");
235                if (is_dir($phpdir)) {
236                    foreach($files as $ent) {
237                        $matches = NULL;
238                        if (preg_match(",^php[ \t]+([^ \t].*[\\\\/]PHP[\\\\/]Archive\.php)$,", $ent, $matches)) {
239                            $sub = $matches[1];
240                            if (strpos($sub, $phpdir) !== 0) {
241                                $found = NULL;
242                                break;
243                            }
244                            $found = $sub;
245                            break;
246                        }
247                    }
248                } else {
249                    self::notice("PEAR package PHP_Archive: corrupt or inaccessible base dir: $php_dir.\n");
250                }
251            }
252            if (isset($found)) {
253                self::notice("PEAR package PHP_Archive: $found.\n");
254            } else {
255                $msg = "PEAR package PHP_Archive not installed: generated phar will require PHP's phar extension be enabled.\n";
256                if ($arg == '0') {
257                    self::notice($msg);
258                } else {
259                    self::error($msg);
260                }
261            }
262            $arg = $found;
263        }
264        return self::cli_arg_typ_file($arg);
265    }
266    // }}}
267    // {{{ static function cli_arg_typ_pharnew
268    /**
269     * Argument type new phar
270     *
271     * @param  string $arg  The new phar component.
272     * @param  string $cfg  Configuration to pass to a new file
273     * @param  string $key  The key
274     * @return string $arg  The new argument file.
275     */
276    static function cli_arg_typ_pharnew($arg, $cfg, $key)
277    {
278        $arg = self::cli_arg_typ_filenew($arg, $cfg, $key);
279        if (!Phar::isValidPharFilename($arg)) {
280            self::error("Phar files must have file extension '.phar', '.phar.php', '.phar.bz2' or '.phar.gz'.\n");
281        }
282        return $arg;
283    }
284    // }}}
285    // {{{ static function cli_arg_typ_pharfile
286    /**
287     * Argument type existing Phar file
288     *
289     * Return filename of an existing Phar.
290     *
291     * @param  string $arg      The file in the phar to open.
292     * @param  string $cfg      The configuration information
293     * @param  string $key      The key information.
294     * @return string $pharfile The name of the loaded Phar file.
295     * @note The Phar will be loaded
296     */
297    static function cli_arg_typ_pharfile($arg, $cfg, $key)
298    {
299        try {
300            $pharfile = self::cli_arg_typ_file($arg, $cfg, $key);
301
302            if (!Phar::loadPhar($pharfile)) {
303                self::error("Unable to open phar '$arg'\n");
304            }
305
306            return $pharfile;
307        } catch(Exception $e) {
308            self::error("Exception while opening phar '$arg':\n" . $e->getMessage() . "\n");
309        }
310    }
311    // }}}
312    // {{{ static function cli_arg_typ_pharurl
313    /**
314     * Argument type Phar url-like
315     *
316     * Check the argument as cli_arg_Typ_phar and return its name prefixed
317     * with phar://
318     *
319     * Ex:
320     * <code>
321     *  $arg = 'pharchive.phar/file.php';
322     *  cli_arg_typ_pharurl($arg)
323     * </code>
324     *
325     * @param  string $arg The url-like phar archive to retrieve.
326     * @return string The phar file-archive.
327     */
328    static function cli_arg_typ_pharurl($arg, $cfg, $key)
329    {
330        return 'phar://' . self::cli_arg_typ_pharfile($arg, $cfg, $key);
331    }
332    // }}}
333    // {{{ static function cli_arg_typ_phar
334    /**
335     * Cli argument type phar
336     *
337     * @param  string $arg  The phar archive to use.
338     * @return object new Phar of the passed argument.
339     */
340    static function cli_arg_typ_phar($arg, $cfg, $key)
341    {
342        try {
343            return new Phar(self::cli_arg_typ_pharfile($arg, $cfg, $key));
344        } catch(Exception $e) {
345            self::error("Exception while opening phar '$argv':\n" . $e->getMessage() . "\n");
346        }
347    }
348    // }}}
349    // {{{ static function cli_arg_typ_entry
350    /**
351     * Argument type Entry name
352     *
353     * @param  string $arg The argument (the entry)
354     * @return string $arg The entry itself.
355     */
356    static function cli_arg_typ_entry($arg, $cfg, $key)
357    {
358        // no further check atm, maybe check for no '/' at beginning
359        return $arg;
360    }
361    // }}}
362    // {{{ static function cli_arg_typ_compalg
363    /**
364     * Argument type compression algorithm
365     *
366     * @param  string $arg  The phar selection
367     * @param  string $cfg  The config option.
368     * @param  string $key  The key information.
369     * @return string $arg  The selected algorithm
370     */
371    static function cli_arg_typ_compalg($arg, $cfg, $key)
372    {
373        $arg = self::cli_arg_typ_select($arg, $cfg, $key);
374
375        switch($arg) {
376            case 'auto':
377                if (extension_loaded('zlib')) {
378                    $arg = 'gz';
379                } elseif (extension_loaded('bz2')) {
380                    $arg = 'bz2';
381                } else {
382                    $arg = '0';
383                }
384                break;
385        }
386        return $arg;
387    }
388    // }}}
389    // {{{ static function cli_arg_typ_privkey
390    /**
391     * Argument type private key (for OpenSSL signing)
392     *
393     * @param  string $arg  The phar selection
394     * @param  string $cfg  The config option.
395     * @param  string $key  The key information.
396     * @return string $arg  The private key.
397     */
398    static function cli_arg_typ_privkey($arg, $cfg, $key)
399    {
400        $arg = self::cli_arg_typ_filecont($arg, $cfg, $key);
401
402        $hash_avail = Phar::getSupportedSignatures();
403        if ($arg && !in_array('OpenSSL', $hash_avail))
404        {
405            self::error("Cannot specify private key without OpenSSL support.\n");
406        }
407        return $arg;
408    }
409    // }}}
410    // {{{ static function phar_check_hash
411    /**
412     * Check whether hash method is valid.
413     *
414     * @return Hash constant to be used.
415     */
416    function phar_check_hash($hash, $privkey)
417    {
418        switch($hash) {
419            case 'md5':
420                return Phar::MD5;
421            case 'sha1':
422                return Phar::SHA1;
423            case 'sha256':
424                return Phar::SHA256;
425            case 'sha512':
426                return Phar::SHA512;
427            case 'openssl':
428                if (!$privkey) {
429                    self::error("Cannot use OpenSSL signing without key.\n");
430                }
431                return Phar::OPENSSL;
432        }
433    }
434    // }}}
435    // {{{ static function cli_cmd_inf_pack
436    /**
437     * Information pack
438     *
439     * @return string A description about packing files into a Phar archive.
440     */
441    static function cli_cmd_inf_pack()
442    {
443        return "Pack files into a PHAR archive.\n" .
444               "When using -s <stub>, then the stub file is being " .
445               "excluded from the list of input files/dirs." .
446               "To create an archive that contains PEAR class PHP_Archive " .
447               "then point -p argument to PHP/Archive.php.\n";
448    }
449    // }}}
450    // {{{ static function cli_cmd_arg_pack
451    /**
452     * Pack a new phar infos
453     *
454     * @return array  $args  The arguments for a new Phar archive.
455     */
456    static function cli_cmd_arg_pack()
457    {
458        $args = self::phar_args('abcFhilpsxy', 'pharnew');
459
460        $args[''] = array(
461            'typ'     => 'any',
462            'val'      => NULL,
463            'required' => 1,
464            'inf'      => '         Any number of input files and directories. If -i is in use then ONLY files and matching the given regular expression are being packed. If -x is given then files matching that regular expression are NOT being packed.',
465        );
466
467        return $args;
468    }
469    // }}}
470    // {{{ function phar_set_stub_begin
471    /**
472     * Set the stub
473     */
474    public function phar_set_stub_begin(Phar $phar, $stub, $loader = NULL, $hashbang = NULL)
475    {
476        if (isset($stub)) {
477            $c = file_get_contents($stub);
478
479            if (substr($c, 0, 2) == '#!') {
480                if (strpos($c, "\n") !== false) {
481                    if (!isset($hashbang)) {
482                        $hashbang = substr($c, 0, strpos($c, "\n") + 1);
483                    }
484                    $c = substr($c, strpos($c, "\n") + 1);
485                } else {
486                    if (!isset($hashbang)) {
487                        $hashbang = $c;
488                    }
489                    $c = NULL;
490                }
491            }
492
493            if (isset($hashbang)) {
494                if (substr($hashbang, 0, 2) != '#!') {
495                    $hashbang = '#!' . $hashbang;
496                }
497                if (substr($hashbang, -1) != "\n") {
498                    $hashbang .= "\n";
499                }
500            } else {
501                $hashbang = "";
502            }
503
504            if (isset($loader)) {
505                $s = "<?php if (!class_exists('PHP_Archive')) {\n?>";
506                if (is_file($loader)) {
507                    $s .= file_get_contents($loader);
508                }
509                $s .= "<?php\n";
510                $s .= "}\n";
511                $s .= "if (!in_array('phar', stream_get_wrappers())) {\n";
512                $s .= "\tstream_wrapper_register('phar', 'PHP_Archive');\n";
513                $s .= "}\n";
514                $s .= "if (!class_exists('Phar',0)) {\n";
515                $s .= "\tinclude 'phar://'.__FILE__.'/phar.inc';\n";
516                $s .= "}\n";
517                $s .= '?>';
518                $s .= $c;
519
520                $phar->setStub($hashbang . $s);
521            } else {
522                $phar->setStub($hashbang . $c);
523            }
524            return new SplFileInfo($stub);
525        }
526        return NULL;
527    }
528    // }}}
529    // {{{ function phar_set_stub_end
530    /**
531     * Set stub end
532     */
533    public function phar_set_stub_end(Phar $phar, $stub, $loader = NULL)
534    {
535        if (isset($stub) && isset($loader)) {
536            if (substr(__FILE__, -15) == 'pharcommand.inc') {
537                self::phar_add_file($phar, 0, 'phar.inc', 'phar://'.__FILE__.'/phar.inc', NULL);
538            } else {
539                self::phar_add_file($phar, 0, 'phar.inc', dirname(__FILE__).'/phar/phar.inc', NULL);
540            }
541        }
542    }
543    // }}}
544    // {{{ function cli_cmd_run_pack
545    /**
546     * Pack a new Phar
547     *
548     * This function will try to pack a new Phar archive.
549     *
550     * @see Exit to make sure that we are done.
551     */
552    public function cli_cmd_run_pack()
553    {
554        if (ini_get('phar.readonly')) {
555            self::error("Creating phar files is disabled by ini setting 'phar.readonly'.\n");
556        }
557
558        if (!Phar::canWrite()) {
559            self::error("Creating phar files is disabled, Phar::canWrite() returned false.\n");
560        }
561
562        $alias    = $this->args['a']['val'];
563        $hashbang = $this->args['b']['val'];
564        $archive  = $this->args['f']['val'];
565        $hash     = $this->args['h']['val'];
566        $privkey  = $this->args['y']['val'] ?? null;
567        $regex    = $this->args['i']['val'];
568        $level    = $this->args['l']['val'];
569        $loader   = $this->args['p']['val'];
570        $stub     = $this->args['s']['val'];
571        $invregex = $this->args['x']['val'];
572        $input    = $this->args['']['val'];
573
574        $hash = self::phar_check_hash($hash, $privkey);
575
576        $phar  = new Phar($archive, 0, $alias);
577
578        $phar->startBuffering();
579
580        $stub = $this->phar_set_stub_begin($phar, $stub, $loader, $hashbang);
581
582        if (!is_array($input)) {
583            $this->phar_add($phar, $level, $input, $regex, $invregex, $stub, NULL, isset($loader));
584        } else {
585            foreach($input as $i) {
586                $this->phar_add($phar, $level, $i, $regex, $invregex, $stub, NULL, isset($loader));
587            }
588        }
589
590        $this->phar_set_stub_end($phar, $stub, $loader);
591
592        switch($this->args['c']['val']) {
593            case 'gz':
594            case 'gzip':
595                $phar->compressFiles(Phar::GZ);
596                break;
597            case 'bz2':
598            case 'bzip2':
599                $phar->compressFiles(Phar::BZ2);
600                break;
601            default:
602                $phar->decompressFiles();
603                break;
604        }
605
606        if ($hash) {
607            $phar->setSignatureAlgorithm($hash, $privkey);
608        }
609
610        $phar->stopBuffering();
611        exit(0);
612    }
613    // }}}
614    // {{{ static function phar_add
615    /**
616     * Add files to a phar archive.
617     *
618     * This function will take a directory and iterate through
619     * it and get the files to insert into the Phar archive.
620     *
621     * @param Phar        $phar      The phar object.
622     * @param string      $input     The input directory
623     * @param string      $regex     The regex used in RegexIterator.
624     * @param string      $invregex  The InvertedRegexIterator expression.
625     * @param SplFileInfo $stub Stub file object
626     * @param mixed       $compress  Compression algorithm or NULL
627     * @param boolean     $noloader  Whether to prevent adding the loader
628     */
629    static function phar_add(Phar $phar, $level, $input, $regex, $invregex, SplFileInfo $stub = NULL, $compress = NULL, $noloader = false)
630    {
631        if ($input && is_file($input) && !is_dir($input)) {
632            return self::phar_add_file($phar, $level, $input, $input, $compress);
633        }
634        $dir   = new RecursiveDirectoryIterator($input);
635        $dir   = new RecursiveIteratorIterator($dir);
636
637        if (isset($regex)) {
638            $dir = new RegexIterator($dir, $regex);
639        }
640
641        if (isset($invregex)) {
642            $dir = new InvertedRegexIterator($dir, $invregex);
643        }
644
645        try {
646            foreach($dir as $file) {
647                if ((empty($stub) || $file->getRealPath() != $stub->getRealPath()) && !is_dir($file)) {
648                    self::phar_add_file($phar, $level, $dir->getSubPathName(), $file, $compress, $noloader);
649                }
650            }
651        } catch(Exception $e) {
652            self::error("Unable to complete operation on file '$file'\n" . $e->getMessage() . "\n");
653        }
654    }
655    // }}}
656    // {{{ static function phar_add_file
657    /**
658     * Add a phar file
659     *
660     * This function adds a file to a phar archive.
661     *
662     * @param Phar    $phar      The phar object
663     * @param string  $level     The level of the file.
664     * @param string  $entry     The entry point
665     * @param string  $file      The file to add to the archive
666     * @param string  $compress  The compression scheme for the file.
667     * @param boolean $noloader  Whether to prevent adding the loader
668     */
669    static function phar_add_file(Phar $phar, $level, $entry, $file, $compress, $noloader = false)
670    {
671        $entry = str_replace('//', '/', $entry);
672        while($level-- > 0 && ($p = strpos($entry, '/')) !== false) {
673            $entry = substr($entry, $p+1);
674        }
675
676    if ($noloader && $entry == 'phar.inc') {
677        return;
678    }
679
680        echo "$entry\n";
681
682        $phar[$entry] = file_get_contents($file);
683        switch($compress) {
684            case 'gz':
685            case 'gzip':
686                $phar[$entry]->compress(Phar::GZ);
687                break;
688            case 'bz2':
689            case 'bzip2':
690                $phar[$entry]->compress(Phar::BZ2);
691                break;
692            case '0':
693                $phar[$entry]->decompress();
694                break;
695            default:
696                break;
697        }
698    }
699    // }}}
700    // {{{ public function phar_dir_echo
701    /**
702     * Echo directory
703     *
704     * @param string $pn
705     * @param unknown_type $f
706     */
707    public function phar_dir_echo($pn, $f)
708    {
709        echo "$f\n";
710    }
711    // }}}
712    // {{{ public function phar_dir_operation
713    /**
714     * Directory operations
715     *
716     * Phar directory operations.
717     *
718     * @param RecursiveIteratorIterator $dir  The recursiveIteratorIterator object.
719     * @param string                    $func Function to call on the iterations
720     * @param array                     $args Function arguments.
721     */
722    public function phar_dir_operation(RecursiveIteratorIterator $dir, $func, array $args = array())
723    {
724        $regex   = $this->args['i']['val'];
725        $invregex= $this->args['x']['val'];
726
727        if (isset($regex)) {
728            $dir = new RegexIterator($dir, $regex);
729        }
730
731        if (isset($invregex)) {
732            $dir = new InvertedRegexIterator($dir, $invregex);
733        }
734
735        $any = false;
736        foreach($dir as $pn => $f) {
737            $any = true;
738            call_user_func($func, $pn, $f, $args);
739        }
740        return $any;
741    }
742    // {{{ static function cli_cmd_inf_list
743    /**
744     * Cli Command Info List
745     *
746     * @return string What inf does
747     */
748    static function cli_cmd_inf_list()
749    {
750        return "List contents of a PHAR archive.";
751    }
752    // }}}
753    // {{{ static function cli_cmd_arg_list
754    /**
755     * Cli Command Argument List
756     *
757     * @return arguments list
758     */
759    static function cli_cmd_arg_list()
760    {
761        return self::phar_args('Fix', 'pharurl');
762    }
763    // }}}
764    // {{{ public function cli_cmd_run_list
765    /**
766     * Cli Command Run List
767     *
768     * @see $this->phar_dir_operation
769     */
770    public function cli_cmd_run_list()
771    {
772        $this->phar_dir_operation(
773            new DirectoryTreeIterator(
774                $this->args['f']['val']),
775                array($this, 'phar_dir_echo')
776            );
777    }
778    // }}}
779    // {{{ static function cli_command_inf_tree
780    /**
781     * Cli Command Inf Tree
782     *
783     * @return string  The description of a directory tree for a Phar archive.
784     */
785    static function cli_cmd_inf_tree()
786    {
787        return "Get a directory tree for a PHAR archive.";
788    }
789    // }}}
790    // {{{ static function cli_cmd_arg_tree
791    /**
792     * Cli Command Argument Tree
793     *
794     * @return string Arguments in URL format.
795     */
796    static function cli_cmd_arg_tree()
797    {
798        return self::phar_args('Fix', 'pharurl');
799    }
800    // }}}
801    // {{{ public function cli_cmd_run_tree
802    /**
803     * Cli Command Run Tree
804     *
805     * Set the phar_dir_operation with a directorygraphiterator.
806     *
807     * @see DirectoryGraphIterator
808     * @see $this->phar_dir_operation
809     *
810     */
811    public function cli_cmd_run_tree()
812    {
813        $a = $this->phar_dir_operation(
814            new DirectoryGraphIterator(
815                $this->args['f']['val']),
816                array($this, 'phar_dir_echo')
817            );
818        if (!$a) {
819            echo "|-<root directory>\n";
820        }
821    }
822    // }}}
823    // {{{ cli_cmd_inf_extract
824    /**
825     * Cli Command Inf Extract
826     *
827     * @return string The description of the command extra to a directory.
828     */
829    static function cli_cmd_inf_extract()
830    {
831        return "Extract a PHAR package to a directory.";
832    }
833    // }}}
834    // {{{ static function cli_cmd_arg_extract
835    /**
836     * Cli Command Arguments Extract
837     *
838     * The arguments for the extract function.
839     *
840     * @return array  The arguments for the extraction.
841     */
842    static function cli_cmd_arg_extract()
843    {
844        $args = self::phar_args('Fix', 'phar');
845
846        $args[''] = array(
847            'type' => 'dir',
848            'val' => '.',
849            'inf' => '         Directory to extract to (defaults to \'.\').',
850        );
851
852        return $args;
853    }
854    // }}}
855    // {{{ public function cli_cmd_run_extract
856    /**
857     * Run Extract
858     *
859     * Run the extraction of a phar Archive.
860     *
861     * @see $this->phar_dir_operation
862     */
863    public function cli_cmd_run_extract()
864    {
865        $dir = $this->args['']['val'];
866
867        if (is_array($dir)) {
868            if (count($dir) != 1) {
869                self::error("Only one target directory allowed.\n");
870            } else {
871                $dir = $dir[0];
872            }
873        }
874
875        $phar = $this->args['f']['val'];
876        $base = $phar->getPathname();
877        $bend = strpos($base, '.phar');
878        $bend = strpos($base, '/', $bend);
879        $base = substr($base, 0, $bend + 1);
880        $blen = strlen($base);
881
882        $this->phar_dir_operation(
883            new RecursiveIteratorIterator($phar),
884            array($this, 'phar_dir_extract'),
885            array($blen, $dir)
886        );
887    }
888    // }}}
889    // {{{ public function phar_dir_extract
890    /**
891     * Extract to a directory
892     *
893     * This function will extract the content of a Phar
894     * to a directory and create new files and directories
895     * depending on the permissions on that folder.
896     *
897     * @param string $pn
898     * @param string $f     The file name
899     * @param array $args   The directory and Blen information
900     */
901    public function phar_dir_extract($pn, $f, $args)
902    {
903        $blen   = $args[0];
904        $dir    = $args[1];
905        $sub    = substr($pn, $blen);
906        $target = $dir . '/' . $sub;
907
908        if (!file_exists(dirname($target))) {
909            @mkdir(dirname($target), 0777, true);
910        }
911        if (!file_exists(dirname($target))) {
912            self::error("Operation could not be completed\n");
913        }
914
915        echo "$sub";
916
917        if (!@copy($f, $target)) {
918            echo " ...error\n";
919        } else {
920            echo " ...ok\n";
921        }
922    }
923    // }}}
924    // {{{ static function cli_cmd_inf_delete
925    /**
926     * Delete an entry from a phar information.
927     *
928     * @return string The information
929     */
930    static function cli_cmd_inf_delete()
931    {
932        return 'Delete entry from a PHAR archive';
933    }
934    // }}}
935    // {{{ static function cli_cmd_arg_delete
936    /**
937     * The cli command argument for deleting.
938     *
939     * @return array information about the arguments to use.
940     */
941    static function cli_cmd_arg_delete()
942    {
943        return self::phar_args('FE', 'phar');
944    }
945    // }}}
946    // {{{ public function cli_cmd_run_delete
947    /**
948     * Deleting execution
949     *
950     * Execute the deleting of the file from the phar archive.
951     */
952    public function cli_cmd_run_delete()
953    {
954        $phar  = $this->args['f']['val'];
955        $entry = $this->args['e']['val'];
956
957        $phar->startBuffering();
958        unset($phar[$entry]);
959        $phar->stopBuffering();
960    }
961    // }}}
962    // {{{ static function cli_cmd_inf_add
963    /**
964     * Client comment add file information
965     *
966     * @return string The description of the feature
967     */
968    static function cli_cmd_inf_add()
969    {
970        return "Add entries to a PHAR package.";
971    }
972    // }}}
973    // {{{ static function cli_cmd_arg_add
974    /**
975     * Add a file arguments
976     */
977    static function cli_cmd_arg_add()
978    {
979        $args = self::phar_args('acFilx', 'phar');
980        $args[''] = array(
981            'type'     => 'any',
982            'val'      => NULL,
983            'required' => 1,
984            'inf'      => '         Any number of input files and directories. If -i is in use then ONLY files and matching the given regular expression are being packed. If -x is given then files matching that regular expression are NOT being packed.',
985        );
986        return $args;
987    }
988    // }}}
989    // {{{ public functio cli_cmd_run_add
990    /**
991     * Add a file
992     *
993     * Run the action of adding a file to
994     * a phar archive.
995     */
996    public function cli_cmd_run_add()
997    {
998        $compress= $this->args['c']['val'];
999        $phar    = $this->args['f']['val'];
1000        $regex   = $this->args['i']['val'];
1001        $level   = $this->args['l']['val'];
1002        $invregex= $this->args['x']['val'];
1003        $input   = $this->args['']['val'];
1004
1005        $phar->startBuffering();
1006
1007        if (!is_array($input)) {
1008            $this->phar_add($phar, $level, $input, $regex, $invregex, NULL, $compress);
1009        } else {
1010            foreach($input as $i) {
1011                $this->phar_add($phar, $level, $i, $regex, $invregex, NULL, $compress);
1012            }
1013        }
1014        $phar->stopBuffering();
1015        exit(0);
1016    }
1017    // }}}
1018    // {{{ public function cli_cmd_inf_stub_set
1019    /**
1020     * Set the stup of a phar file.
1021     *
1022     * @return string The stub set description.
1023     */
1024    public function cli_cmd_inf_stub_set()
1025    {
1026        return "Set the stub of a PHAR file. " .
1027               "If no input file is specified as stub then stdin is being used.";
1028    }
1029    // }}}
1030    // {{{ public function cli_cmd_arg_stub_set
1031    /**
1032     * Set the argument stub
1033     *
1034     * @return string arguments for a stub
1035     */
1036    public function cli_cmd_arg_stub_set()
1037    {
1038        $args = self::phar_args('bFps', 'phar');
1039        $args['s']['val'] = 'php://stdin';
1040        return $args;
1041    }
1042    // }}}
1043    // {{{ public function cli_cmd_run_stub_set
1044    /**
1045     * Cli Command run stub set
1046     *
1047     * @see   $phar->setStub()
1048     */
1049    public function cli_cmd_run_stub_set()
1050    {
1051        $hashbang = $this->args['b']['val'];
1052        $phar     = $this->args['f']['val'];
1053        $stub     = $this->args['s']['val'];
1054        $loader   = $this->args['p']['val'];
1055
1056        $this->phar_set_stub_begin($phar, $stub, $loader, $hashbang);
1057        $this->phar_set_stub_end($phar, $stub, $loader);
1058    }
1059    // }}}
1060    // {{{ public function cli_cmd_inf_stub_get
1061    /**
1062     * Get the command stub infos.
1063     *
1064     * @return string a description of the stub of a Phar file.
1065     */
1066    public function cli_cmd_inf_stub_get()
1067    {
1068        return "Get the stub of a PHAR file. " .
1069               "If no output file is specified as stub then stdout is being used.";
1070    }
1071    // }}}
1072    // {{{ public function cli_cmd_arg_stub_get
1073    /**
1074     * Get the argument stub
1075     *
1076     * @return array $args The arguments passed to the stub.
1077     */
1078    public function cli_cmd_arg_stub_get()
1079    {
1080        $args = self::phar_args('Fs', 'phar');
1081        $args['s']['val'] = 'php://stdin';
1082        return $args;
1083    }
1084    // }}}
1085    // {{{ public function cli_cmd_run_stub_get
1086    /**
1087     * Cli Command Run Stub
1088     *
1089     * Get arguments and store them into a stub.
1090     *
1091     * @param arguments $args
1092     * @see   $this->args
1093     */
1094    public function cli_cmd_run_stub_get($args)
1095    {
1096        $phar = $this->args['f']['val'];
1097        $stub = $this->args['s']['val'];
1098
1099        file_put_contents($stub, $phar->getStub());
1100    }
1101    // }}}
1102    // {{{ public function cli_cmd_inf_compress
1103    /**
1104     * Cli Command Inf Compress
1105     *
1106     * Cli Command compress information
1107     *
1108     * @return string A description of the command.
1109     */
1110    public function cli_cmd_inf_compress()
1111    {
1112        return "Compress or uncompress all files or a selected entry.";
1113    }
1114    // }}}
1115    // {{{ public function cli_cmd_arg_cmpress
1116    /**
1117     * Cli Command Arg Compress
1118     *
1119     * @return array The arguments for compress
1120     */
1121    public function cli_cmd_arg_compress()
1122    {
1123        return self::phar_args('FCe', 'phar');
1124    }
1125    // }}}
1126    // {{{ public function cli_cmd_run_compress
1127    /**
1128     * Cli Command Run Compress
1129     *
1130     * @see $this->args
1131     */
1132    public function cli_cmd_run_compress()
1133    {
1134        $phar  = $this->args['f']['val'];
1135        $entry = $this->args['e']['val'];
1136
1137        switch($this->args['c']['val']) {
1138            case 'gz':
1139            case 'gzip':
1140                if (isset($entry)) {
1141                    $phar[$entry]->compress(Phar::GZ);
1142                } else {
1143                    $phar->compressFiles(Phar::GZ);
1144                }
1145                break;
1146            case 'bz2':
1147            case 'bzip2':
1148                if (isset($entry)) {
1149                    $phar[$entry]->compress(Phar::BZ2);
1150                } else {
1151                    $phar->compressFiles(Phar::BZ2);
1152                }
1153                break;
1154            default:
1155                if (isset($entry)) {
1156                    $phar[$entry]->decompress();
1157                } else {
1158                    $phar->decompressFiles();
1159                }
1160                break;
1161        }
1162    }
1163    // }}}
1164    // {{{ public function cli_cmd_inf_sign
1165    /**
1166     * Cli Command Info Signature
1167     *
1168     * @return string A description of the signature arguments.
1169     */
1170    public function cli_cmd_inf_sign()
1171    {
1172        return "Set signature hash algorithm.";
1173    }
1174    // }}}
1175    // {{{ public function cli_cmd_arg_sign
1176    /**
1177     * Cli Command Argument Sign
1178     *
1179     * @return array Arguments for Signature
1180     */
1181    public function cli_cmd_arg_sign()
1182    {
1183        return self::phar_args('FHy', 'phar');
1184    }
1185    // }}}
1186    // {{{ public function cli_cmd_run_sign
1187    /**
1188     * Cli Command Run Signature
1189     *
1190     * @see $phar->setSignaturealgorithm
1191     */
1192    public function cli_cmd_run_sign()
1193    {
1194        $phar     = $this->args['f']['val'];
1195        $hash     = $this->args['h']['val'];
1196        $privkey  = $this->args['y']['val'];
1197
1198        $hash = self::phar_check_hash($hash, $privkey);
1199
1200        $phar->setSignatureAlgorithm($hash, $privkey);
1201    }
1202    // }}}
1203    // {{{ public function cli_cmd_inf_meta_set
1204    /**
1205     * Cli Command Inf Meta Set
1206     *
1207     * @return string A description
1208     */
1209    public function cli_cmd_inf_meta_set()
1210    {
1211        return "Set meta data of a PHAR entry or a PHAR package using serialized input. " .
1212               "If no input file is specified for meta data then stdin is being used." .
1213               "You can also specify a particular index using -k. In that case the metadata is " .
1214               "expected to be an array and the value of the given index is being set. If " .
1215               "the metadata is not present or empty a new array will be created. If the " .
1216               "metadata is present and a flat value then the return value is 1. Also using -k " .
1217               "the input is been taken directly rather then being serialized.";
1218    }
1219    // }}}
1220    // {{{ public function cli_cmd_arg_meta_set
1221    /**
1222     * Cli Command Argument Meta Set
1223     *
1224     * @return array  The arguments for meta set
1225     */
1226    public function cli_cmd_arg_meta_set()
1227    {
1228        return self::phar_args('FekM', 'phar');
1229    }
1230    // }}}
1231    // {{{ public function cli_cmd_run_met_set
1232    /**
1233     * Cli Command Run Metaset
1234     *
1235     * @see $phar->startBuffering
1236     * @see $phar->setMetadata
1237     * @see $phar->stopBuffering
1238     */
1239    public function cli_cmd_run_meta_set()
1240    {
1241        $phar  = $this->args['f']['val'];
1242        $entry = $this->args['e']['val'];
1243        $index = $this->args['k']['val'];
1244        $meta  = $this->args['m']['val'];
1245
1246        $phar->startBuffering();
1247
1248        if (isset($index)) {
1249            if (isset($entry)) {
1250                if ($phar[$entry]->hasMetadata()) {
1251                    $old = $phar[$entry]->getMetadata();
1252                } else {
1253                    $old = array();
1254                }
1255            } else {
1256                if ($phar->hasMetadata()) {
1257                    $old = $phar->getMetadata();
1258                } else {
1259                    $old = array();
1260                }
1261            }
1262
1263            if (!is_array($old)) {
1264                self::error('Metadata is a flat value while an index operation was issued.');
1265            }
1266
1267            $old[$index] = $meta;
1268            $meta = $old;
1269        } else {
1270            $meta = unserialize($meta);
1271        }
1272
1273        if (isset($entry)) {
1274            $phar[$entry]->setMetadata($meta);
1275        } else {
1276            $phar->setMetadata($meta);
1277        }
1278        $phar->stopBuffering();
1279    }
1280    // }}}
1281    // {{{ public function cli_cmd_inf_met_get
1282    /**
1283     * Cli Command Inf Metaget
1284     *
1285     * @return string A description of the metaget arguments
1286     */
1287    public function cli_cmd_inf_meta_get()
1288    {
1289        return "Get meta information of a PHAR entry or a PHAR package in serialized from. " .
1290               "If no output file is specified for meta data then stdout is being used.\n" .
1291               "You can also specify a particular index using -k. In that case the metadata is " .
1292               "expected to be an array and the value of the given index is returned using echo " .
1293               "rather than using serialize. If that index does not exist or no meta data is " .
1294               "present then the return value is 1.";
1295    }
1296    // }}}
1297    // {{{ public function cli_cmd_arg_meta_get
1298    /**
1299     * Cli Command arg metaget
1300     *
1301     * @return array  The arguments for meta get.
1302     */
1303    public function cli_cmd_arg_meta_get()
1304    {
1305        return self::phar_args('Fek', 'phar');
1306    }
1307    // }}}
1308    // {{{ public function cli_cmd_run_meta_get
1309    /**
1310     * Cli Command Run Metaget
1311     *
1312     * @see $this->args
1313     * @see $phar[$x]->hasMetadata()
1314     * @see $phar->getMetadata()
1315     */
1316    public function cli_cmd_run_meta_get()
1317    {
1318        $phar  = $this->args['f']['val'];
1319        $entry = $this->args['e']['val'];
1320        $index = $this->args['k']['val'];
1321
1322        if (isset($entry)) {
1323            if (!$phar[$entry]->hasMetadata()) {
1324                echo "No Metadata\n";
1325                exit(1);
1326            }
1327            echo serialize($phar[$entry]->getMetadata());
1328        } else {
1329            if (!$phar->hasMetadata()) {
1330                echo "No Metadata\n";
1331                exit(1);
1332            }
1333            $meta = $phar->getMetadata();
1334        }
1335
1336        if (isset($index)) {
1337            if (isset($index)) {
1338                if (isset($meta[$index])) {
1339                    echo $meta[$index];
1340                    exit(0);
1341                } else {
1342                    echo "No Metadata\n";
1343                    exit(1);
1344                }
1345            } else {
1346                echo serialize($meta);
1347            }
1348        }
1349    }
1350    // }}}
1351    // {{{ public function cli_cmd_inf_meta_del
1352    /**
1353     * Cli Command Inf Metadel
1354     *
1355     * @return string A description of the metadel function
1356     */
1357    public function cli_cmd_inf_meta_del()
1358    {
1359        return "Delete meta information of a PHAR entry or a PHAR package.\n" .
1360               "If -k is given then the metadata is expected to be an array " .
1361               "and the given index is being deleted.\n" .
1362               "If something was deleted the return value is 0 otherwise it is 1.";
1363    }
1364    // }}}
1365    // {{{ public function cli_cmd_arg_meta_del
1366    /**
1367     * CliC ommand Arg Metadelete
1368     *
1369     * @return array The arguments for metadel
1370     */
1371    public function cli_cmd_arg_meta_del()
1372    {
1373        return self::phar_args('Fek', 'phar');
1374    }
1375    // }}}
1376    // {{{ public function cli_cmd_run_meta_del
1377    /**
1378     * Cli Command Run MetaDel
1379     *
1380     * @see $phar[$x]->delMetadata()
1381     * @see $phar->delMetadata()
1382     */
1383    public function cli_cmd_run_meta_del()
1384    {
1385        $phar  = $this->args['f']['val'];
1386        $entry = $this->args['e']['val'];
1387        $index = $this->args['k']['val'];
1388
1389        if (isset($entry)) {
1390            if (isset($index)) {
1391                if (!$phar[$entry]->hasMetadata()) {
1392                    exit(1);
1393                }
1394                $meta = $phar[$entry]->getMetadata();
1395
1396                // @todo add error message here.
1397                if (!is_array($meta)) {
1398                    exit(1);
1399                }
1400
1401                unset($meta[$index]);
1402                $phar[$entry]->setMetadata($meta);
1403            } else {
1404                exit($phar[$entry]->delMetadata() ? 0 : 1);
1405            }
1406        } else {
1407            if (isset($index)) {
1408                if (!$phar->hasMetadata()) {
1409                    exit(1);
1410                }
1411
1412                $meta = $phar->getMetadata();
1413
1414                // @todo Add error message
1415                if (!is_array($meta)) {
1416                    exit(1);
1417                }
1418
1419                unset($meta[$index]);
1420                $phar->setMetadata($meta);
1421            } else {
1422                exit($phar->delMetadata() ? 0 : 1);
1423            }
1424        }
1425    }
1426    // }}}
1427    // {{{ public function cli_cmd_inf_info
1428    /**
1429     * CLi Command Inf Info
1430     *
1431     * @return string A description about the info commands.
1432     */
1433    public function cli_cmd_inf_info()
1434    {
1435        return "Get information about a PHAR package.\n" .
1436               "By using -k it is possible to return a single value.";
1437    }
1438    // }}}
1439    // {{{ public function cli_cmd_arg_info
1440    /**
1441     * Cli Command Arg Infos
1442     *
1443     * @return array The arguments for info command.
1444     */
1445    public function cli_cmd_arg_info()
1446    {
1447        return self::phar_args('Fk', 'phar');
1448    }
1449    // }}}
1450    // {{{ public function cli_cmd_run_info
1451    /**
1452     * Cli Command Run Info
1453     *
1454     * @param args $args
1455     */
1456    public function cli_cmd_run_info()
1457    {
1458        $phar  = $this->args['f']['val'];
1459        $index = $this->args['k']['val'];
1460
1461        $hash  = $phar->getSignature();
1462        $infos = array();
1463
1464        if ($phar->getAlias()) {
1465            $infos['Alias'] = $phar->getAlias();
1466        }
1467
1468        if (!$hash) {
1469            $infos['Hash-type'] = 'NONE';
1470        } else {
1471            $infos['Hash-type'] = $hash['hash_type'];
1472            $infos['Hash'] = $hash['hash'];
1473        }
1474
1475        $csize   = 0;
1476        $usize   = 0;
1477        $count   = 0;
1478        $ccount  = 0;
1479        $ucount  = 0;
1480        $mcount  = 0;
1481        $compalg = array('GZ'=>0, 'BZ2'=>0);
1482
1483        foreach(new RecursiveIteratorIterator($phar) as $ent) {
1484            $count++;
1485            if ($ent->isCompressed()) {
1486                $ccount++;
1487                $csize += $ent->getCompressedSize();
1488                if ($ent->isCompressed(Phar::GZ)) {
1489                    $compalg['GZ']++;
1490                } elseif ($ent->isCompressed(Phar::BZ2)) {
1491                    $compalg['BZ2']++;
1492                }
1493            } else {
1494                $ucount++;
1495                $csize += $ent->getSize();
1496            }
1497
1498            $usize += $ent->getSize();
1499
1500            if ($ent->hasMetadata()) {
1501                $mcount++;
1502            }
1503        }
1504
1505        $infos['Entries']            = $count;
1506        $infos['Uncompressed-files'] = $ucount;
1507        $infos['Compressed-files']   = $ccount;
1508        $infos['Compressed-gz']      = $compalg['GZ'];
1509        $infos['Compressed-bz2']     = $compalg['BZ2'];
1510        $infos['Uncompressed-size']  = $usize;
1511        $infos['Compressed-size']    = $csize;
1512        $infos['Compression-ratio']  = sprintf('%.3g%%', $usize ? ($csize * 100) / $usize : 100);
1513        $infos['Metadata-global']    = $phar->hasMetadata() * 1;
1514        $infos['Metadata-files']     = $mcount;
1515        $infos['Stub-size']          = strlen($phar->getStub());
1516
1517        if (isset($index)) {
1518            if (!isset($infos[$index])) {
1519                self::error("Requested value does not exist.\n");
1520            }
1521
1522            echo $infos[$index];
1523            exit(0);
1524        }
1525
1526        $l = 0;
1527        foreach($infos as $which => $val) {
1528            $l = max(strlen($which), $l);
1529        }
1530
1531        foreach($infos as $which => $val) {
1532            echo $which . ':' . str_repeat(' ', $l + 1 - strlen($which)) . $val . "\n";
1533        }
1534    }
1535    // }}}
1536    // {{{ public function cli_cmd_inf_version
1537    /**
1538     * CLi Command Inf Version
1539     *
1540     * @return string A description about the info commands.
1541     */
1542    public function cli_cmd_inf_version()
1543    {
1544        return "Get information about the PHAR environment and the tool version.";
1545    }
1546    // }}}
1547    // {{{ public function cli_cmd_arg_version
1548    /**
1549     * Cli Command Arg Version
1550     *
1551     * @return array The arguments for version command.
1552     */
1553    public function cli_cmd_arg_version()
1554    {
1555        return self::phar_args('', NULL);
1556    }
1557    // }}}
1558    // {{{ public function cli_cmd_run_info
1559    /**
1560     * Cli Command Run Info
1561     *
1562     * @param args $args
1563     */
1564    public function cli_cmd_run_version()
1565    {
1566        $use_ext = extension_loaded('phar');
1567        $version = array(
1568            'PHP Version' => phpversion(),
1569            'phar.phar version' => '$Id: 1fb41dbb6a75a1e713adb31d692758175b35b1a3 $',
1570            'Phar EXT version' => $use_ext ? phpversion('phar') : 'Not available',
1571            'Phar API version' => Phar::apiVersion(),
1572            'Phar-based phar archives' => true,
1573            'Tar-based phar archives' => $use_ext,
1574            'ZIP-based phar archives' => $use_ext,
1575            'gzip compression' => extension_loaded('zlib'),
1576            'bzip2 compression' => extension_loaded('bz2'),
1577            'supported signatures' => $use_ext ? join(', ', Phar::getSupportedSignatures()) : '',
1578            );
1579        $klen = 0;
1580        foreach($version as $k => $v)
1581        {
1582            $klen = max($klen, strlen($k));
1583        }
1584        ++$klen;
1585        foreach($version as $k => $v) {
1586            if (is_bool($v)) {
1587                $v = $v ? 'enabled' : 'disabled';
1588            }
1589            printf("%-${klen}s  %s\n", $k.':', $v);
1590        }
1591    }
1592    // }}}
1593}
1594// }}}
1595?>
1596