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