]> granicus.if.org Git - php/commitdiff
* refactored the command/options code:
authorStig Bakken <ssb@php.net>
Sun, 12 May 2002 21:09:04 +0000 (21:09 +0000)
committerStig Bakken <ssb@php.net>
Sun, 12 May 2002 21:09:04 +0000 (21:09 +0000)
  - now each command class should define a "commands" property with
    documentation, option specs etc.
  - both long and short options are now supported
  - after recent changes to Console_Getopt, you may now have options
    to commands even though the same option is also valid for the pear
    command itself
  - less CLI-centric, better suited to Gtk and Web frontends

pear/PEAR/Command.php
pear/PEAR/Command/Common.php
pear/PEAR/Command/Install.php
pear/PEAR/Installer.php
pear/scripts/pear.in

index 7972f42eb5374372ef7f55c2bf940d502c290a19..6127a9709969cffee7dae98950881f0c4219f2f6 100644 (file)
@@ -27,6 +27,12 @@ require_once "PEAR.php";
  */
 $GLOBALS['_PEAR_Command_commandlist'] = array();
 
+/**
+ * Array of command objects
+ * @var array class => object
+ */
+$GLOBALS['_PEAR_Command_objects'] = array();
+
 /**
  * Which user interface class is being used.
  * @var string class name
@@ -39,12 +45,6 @@ $GLOBALS['_PEAR_Command_uiclass'] = 'PEAR_Frontend_CLI';
  */
 $GLOBALS['_PEAR_Command_uiobject'] = null;
 
-/**
-* The options accepted by the commands
-* @var string the options
-*/
-$GLOBALS['_PEAR_Command_commandopts'] = '';
-
 /**
  * PEAR command class, a simple factory class for administrative
  * commands.
@@ -81,8 +81,8 @@ $GLOBALS['_PEAR_Command_commandopts'] = '';
  * - DON'T OUTPUT ANYTHING! Return text for output instead.
  *
  * - DON'T USE HTML! The text you return will be used from both Gtk,
- *   web and command-line interfaces, so for now keep everything to
- *   plain text.
+ *   web and command-line interfaces, so for now, keep everything to
+ *   plain text.  There may be a common (XML) markup format later.
  *
  * - DON'T USE EXIT OR DIE! Always use pear errors.  From static
  *   classes do PEAR::raiseError(), from other classes do
@@ -93,8 +93,8 @@ class PEAR_Command
     /**
      * Get the right object for executing a command.
      *
-     * @param object Instance of PEAR_Config object
-     * @param string The name of the command
+     * @param string $command The name of the command
+     * @param object $config  Instance of PEAR_Config object
      *
      * @return object the command object or a PEAR error
      *
@@ -105,12 +105,13 @@ class PEAR_Command
         if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
             PEAR_Command::registerCommands();
         }
-        if (isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
-            $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
-            $obj = &new $class(PEAR_Command::getFrontendObject(), $config);
-            return $obj;
+        $class = @$GLOBALS['_PEAR_Command_commandlist'][$command];
+        if (empty($class)) {
+            return PEAR::raiseError("unknown command `$command'");
         }
-        return PEAR::raiseError("unknown command `$command'");
+        $ui = PEAR_Command::getFrontendObject();
+        $obj = &new $class($ui, $config);
+        return $obj;
     }
 
     /**
@@ -120,34 +121,44 @@ class PEAR_Command
      */
     function &getFrontendObject()
     {
-        global $_PEAR_Command_uiclass, $_PEAR_Command_uiobject;
-        if (empty($_PEAR_Command_uiobject)) {
-            $_PEAR_Command_uiobject = &new $_PEAR_Command_uiclass;
+        if (empty($GLOBALS['_PEAR_Command_uiobject'])) {
+            $GLOBALS['_PEAR_Command_uiobject'] = &new $GLOBALS['_PEAR_Command_uiclass'];
         }
-        return $_PEAR_Command_uiobject;
+        return $GLOBALS['_PEAR_Command_uiobject'];
     }
 
     /**
      * Load current frontend class.
      *
-     * @param  string  Name of the frontend
+     * @param string $uiclass Name of class implementing the frontend
      *
-     * @return boolean TRUE if the frontend exists, otherwise FALSE.
+     * @return object the frontend object, or a PEAR error
      */
-    function setFrontendClass($uiclass)
+    function &setFrontendClass($uiclass)
     {
-        $GLOBALS['_PEAR_Command_uiclass'] = $uiclass;
-        $file = str_replace("_", "/", $uiclass) . '.php';
-        include_once $file;
-        return class_exists(strtolower($uiclass));
+        $file = str_replace('_', '/', $uiclass) . '.php';
+        @include_once $file;
+        if (class_exists(strtolower($uiclass))) {
+            $obj = &new $uiclass;
+            // quick test to see if this class implements a few of the most
+            // important frontend methods
+            if (method_exists($obj, 'displayLine') && method_exists($obj, 'userConfirm')) {
+                $GLOBALS['_PEAR_Command_uiobject'] = &$obj;
+                $GLOBALS['_PEAR_Command_uiclass'] = $uiclass;
+                return $obj;
+            } else {
+                return PEAR::raiseError("not a frontend class: $uiclass");
+            }
+        }
+        return PEAR::raiseError("no such class: $uiclass");
     }
 
     /**
      * Set current frontend.
      *
-     * @param  string  Name of the frontend type
+     * @param string $uitype Name of the frontend type (for example "CLI")
      *
-     * @return boolean TRUE if the frontend exists, otherwise FALSE.
+     * @return object the frontend object, or a PEAR error
      */
     function setFrontendType($uitype)
     {
@@ -179,32 +190,28 @@ class PEAR_Command
         }
         $dp = @opendir($dir);
         if (empty($dp)) {
-            return PEAR::raiseError("PEAR_Command::registerCommands: ".
-                                    "opendir($dir) failed");
+            return PEAR::raiseError("registerCommands: opendir($dir) failed");
         }
         if (!$merge) {
             $GLOBALS['_PEAR_Command_commandlist'] = array();
         }
-        $cmdopts = array();
         while ($entry = readdir($dp)) {
-            if ($entry{0} == '.' || substr($entry, -4) != '.php' ||
-                $entry == 'Common.php')
-            {
+            if ($entry{0} == '.' || substr($entry, -4) != '.php' || $entry == 'Common.php') {
                 continue;
             }
             $class = "PEAR_Command_".substr($entry, 0, -4);
             $file = "$dir/$entry";
             include_once $file;
             // List of commands
-            $implements = call_user_func(array($class, "getCommands"));
+            if (empty($GLOBALS['_PEAR_Command_objects'][$class])) {
+                $GLOBALS['_PEAR_Command_objects'][$class] = &new $class($ui, $config);
+            }
+            $implements = $GLOBALS['_PEAR_Command_objects'][$class]->getCommands();
             foreach ($implements as $command => $desc) {
                 $GLOBALS['_PEAR_Command_commandlist'][$command] = $class;
                 $GLOBALS['_PEAR_Command_commanddesc'][$command] = $desc;
             }
-            // List of options accepted
-            $cmdopts = array_merge($cmdopts, call_user_func(array($class, "getOptions")));
         }
-        $GLOBALS['_PEAR_Command_commandopts'] = implode('', $cmdopts);
         return true;
     }
 
@@ -225,19 +232,27 @@ class PEAR_Command
     }
 
     /**
-     * Get the list of currently supported options, and what
-     * classes implement them.
+     * Compiles arguments for getopt.
      *
-     * @return array array option => implementing class
+     * @param string $command     command to get optstring for
+     * @param string $short_args  (reference) short getopt format
+     * @param array  $long_args   (reference) long getopt format
+     *
+     * @return void
      *
      * @access public
      */
-    function getOptions()
+    function getGetoptArgs($command, &$short_args, &$long_args)
     {
         if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
             PEAR_Command::registerCommands();
         }
-        return $GLOBALS['_PEAR_Command_commandopts'];
+        $class = @$GLOBALS['_PEAR_Command_commandlist'][$command];
+        if (empty($class)) {
+            return null;
+        }
+        $obj = &$GLOBALS['_PEAR_Command_objects'][$class];
+        return $obj->getGetoptArgs($command, $short_args, $long_args);
     }
 
     /**
@@ -257,8 +272,7 @@ class PEAR_Command
     /**
      * Get help for command.
      *
-     * @param  string Name of the command for which help should be
-     *                called.
+     * @param string $command Name of the command to return help for
      *
      * @access public
      */
@@ -266,7 +280,8 @@ class PEAR_Command
     {
         $cmds = PEAR_Command::getCommands();
         if (isset($cmds[$command])) {
-            return call_user_func(array($cmds[$command], 'getHelp'), $command);
+            $class = $cmds[$command];
+            return $GLOBALS['_PEAR_Command_objects'][$class]->getHelp($command);
         }
         return false;
     }
index c31c23680da91e6049dd35a17345d96a4ccb60a7..471930fa67ae332aa7657c8dc783d15cebbbcff4 100644 (file)
@@ -13,7 +13,7 @@
 // | obtain it through the world-wide-web, please send a note to          |
 // | license@php.net so we can mail you a copy immediately.               |
 // +----------------------------------------------------------------------+
-// | Author: Stig Bakken <ssb@fast.no>                                    |
+// | Author: Stig Sæther Bakken <ssb@fast.no>                             |
 // +----------------------------------------------------------------------+
 //
 // $Id$
@@ -22,6 +22,8 @@ require_once "PEAR.php";
 
 class PEAR_Command_Common extends PEAR
 {
+    // {{{ properties
+
     /**
      * PEAR_Config object used to pass user system and configuration
      * on when executing commands
@@ -36,6 +38,9 @@ class PEAR_Command_Common extends PEAR
      */
     var $ui;
 
+    // }}}
+    // {{{ constructor
+
     /**
      * PEAR_Command_Common constructor.
      *
@@ -48,15 +53,59 @@ class PEAR_Command_Common extends PEAR
         $this->ui = &$ui;
     }
 
-    function getOptions()
-    {
-        return array();
-    }
+    // }}}
+
+    // {{{ getHelp()
 
     function getHelp($command)
     {
         return array(null, 'No help avaible yet');
     }
+
+    // }}}
+    // {{{ getGetoptArgs()
+
+    function getGetoptArgs($command, &$short_args, &$long_args)
+    {
+        $short_args = "";
+        $long_args = array();
+        if (empty($this->commands[$command])) {
+            return;
+        }
+        reset($this->commands[$command]);
+        while (list($option, $info) = each($this->commands[$command]['options'])) {
+            $larg = $sarg = '';
+            if (isset($info['arg'])) {
+                if ($info['arg']{0} == '(') {
+                    $larg = '==';
+                    $sarg = '::';
+                    $arg = substr($info['arg'], 1, -1);
+                } else {
+                    $larg = '=';
+                    $sarg = ':';
+                    $arg = $info['arg'];
+                }
+            }
+            if (isset($info['shortopt'])) {
+                $short_args .= $info['shortopt'] . $sarg;
+            }
+            $long_args[] = $option . $larg;
+        }
+    }
+
+    // }}}
+    // {{{ run()
+
+    function run($command, $options, $params)
+    {
+        $func = @$this->commands[$command]['function'];
+        if (empty($func)) {
+            return $this->raiseError("unknown command `$command'");
+        }
+        return $this->$func($command, $options, $params);
+    }
+
+    // }}}
 }
 
 ?>
\ No newline at end of file
index 6436ab4208bb3d3a7591f65d0599d3cd14da9f4b..ee6a8fd34976130b127700615f1bdebc66ff6bd4 100644 (file)
 // | obtain it through the world-wide-web, please send a note to          |
 // | license@php.net so we can mail you a copy immediately.               |
 // +----------------------------------------------------------------------+
-// | Author: Stig Bakken <ssb@fast.no>                                    |
+// | Author: Stig Sæther Bakken <ssb@fast.no>                             |
 // +----------------------------------------------------------------------+
 //
 // $Id$
 
 require_once "PEAR/Command/Common.php";
 require_once "PEAR/Installer.php";
+require_once "Console/Getopt.php";
 
 /**
  * PEAR commands for installation or deinstallation/upgrading of
@@ -28,6 +29,109 @@ require_once "PEAR/Installer.php";
  */
 class PEAR_Command_Install extends PEAR_Command_Common
 {
+    // {{{ command definitions
+
+    var $commands = array(
+        'install' => array(
+            'summary' => 'Install Package',
+            'function' => 'doInstall',
+            'options' => array(
+                'force' => array(
+                    'shortopt' => 'f',
+                    'doc' => 'will overwrite newer installed packages',
+                    ),
+                'nodeps' => array(
+                    'shortopt' => 'n',
+                    'doc' => 'ignore dependencies, install anyway',
+                    ),
+                'register-only' => array(
+                    'shortopt' => 'r',
+                    'doc' => 'do not install files, only register the package as installed',
+                    ),
+                'soft' => array(
+                    'shortopt' => 's',
+                    'doc' => 'soft install, fail silently, or upgrade if already installed',
+                    ),
+                'nocompress' => array(
+                    'shortopt' => 'Z',
+                    'doc' => 'request uncompressed files when downloading',
+                    ),
+                ),
+            'doc' => 'Installs one or more PEAR packages.  You can specify a package to
+install in four ways:
+
+"Package-1.0.tgz" : installs from a local file
+
+"http://example.com/Package-1.0.tgz" : installs from
+anywhere on the net.
+
+"package.xml" : installs the package described in
+package.xml.  Useful for testing, or for wrapping a PEAR package in
+another package manager such as RPM.
+
+"Package" : queries your configured server
+({config master_server}) and downloads the newest package with
+the preferred quality/state ({config preferred_state}).
+
+More than one package may be specified at once.  It is ok to mix these
+four ways of specifying packages.
+'),
+        'upgrade' => array(
+            'summary' => 'Upgrade Package',
+            'function' => 'doInstall',
+            'options' => array(
+                'force' => array(
+                    'shortopt' => 'f',
+                    'doc' => 'overwrite newer installed packages',
+                    ),
+                'nodeps' => array(
+                    'shortopt' => 'n',
+                    'doc' => 'ignore dependencies, upgrade anyway',
+                    ),
+                'register-only' => array(
+                    'shortopt' => 'r',
+                    'doc' => 'do not install files, only register the package as upgraded',
+                    ),
+                'nocompress' => array(
+                    'shortopt' => 'Z',
+                    'request uncompressed files when downloading',
+                    ),
+                ),
+            'doc' => 'Upgrades one or more PEAR packages.  See documentation for the
+"install" command for ways to specify a package.
+
+When upgrading, your package will be updated if the provided new
+package has a higher version number (use the -f option if you need to
+upgrade anyway).
+
+More than one package may be specified at once.
+'),
+        'uninstall' => array(
+            'summary' => 'Un-install Package',
+            'function' => 'doUninstall',
+            'options' => array(
+                'nodeps' => array(
+                    'shortopt' => 'n',
+                    'doc' => 'ignore dependencies, uninstall anyway',
+                    ),
+                'register-only' => array(
+                    'shortopt' => 'r',
+                    'doc' => 'do not remove files, only register the packages as not installed',
+                    ),
+                ),
+            'doc' => 'Upgrades one or more PEAR packages.  See documentation for the
+"install" command for ways to specify a package.
+
+When upgrading, your package will be updated if the provided new
+package has a higher version number (use the -f option if you need to
+upgrade anyway).
+
+More than one package may be specified at once.
+'),
+
+    );
+
+    // }}}
     // {{{ constructor
 
     /**
@@ -51,11 +155,16 @@ class PEAR_Command_Install extends PEAR_Command_Common
      */
     function getCommands()
     {
-        return array('install' => 'Install Package',
-                     'uninstall' => 'Uninstall Package',
-                     'upgrade' => 'Upgrade Package');
+        $ret = array();
+        foreach (array_keys($this->commands) as $command) {
+            $ret[$command] = $this->commands[$command]['summary'];
+        }
+        return $ret;
     }
 
+    // }}}
+    // {{{ getHelp()
+
     function getHelp($command)
     {
         switch ($command) {
@@ -83,61 +192,41 @@ class PEAR_Command_Install extends PEAR_Command_Common
         return $ret;
     }
 
-    // }}}
-    // {{{ getOptions()
-
-    function getOptions()
-    {
-        return array('f', 'n', 'r', 's', 'Z');
-    }
-
     // }}}
     // {{{ run()
 
     function run($command, $options, $params)
     {
-        $installer = &new PEAR_Installer($this->ui);
-
+        $this->installer = &new PEAR_Installer($ui);
+//        return parent::run($command, $options, $params);
         $failmsg = '';
-        $opts = array();
-        if (isset($options['f'])) {
-            $opts['force'] = true;
-        }
-        if (isset($options['n'])) {
-            $opts['nodeps'] = true;
-        }
-        if (isset($options['r'])) {
-            $opts['register_only'] = true;
-        }
-        if (isset($options['s'])) {
-            $opts['soft'] = true;
-        }
-        if (isset($options['Z'])) {
-            $opts['nocompress'] = true;
-        }
         switch ($command) {
             case 'upgrade':
-                $opts['upgrade'] = true;
+                $options['upgrade'] = true;
                 // fall through
-            case 'install': {
-                if ($installer->install(@$params[0], $opts, $this->config)) {
-                    $this->ui->displayLine("$command ok");
-                } else {
-                    $failmsg = "$command failed";
+            case 'install':
+                foreach ($params as $pkg) {
+                    $bn = basename($pkg);
+                    $info = $this->installer->install($pkg, $options, $this->config);
+                    if (is_array($info)) {
+                        $label = "$info[package] $info[version]";
+                        $this->ui->displayLine("$command ok: $label");
+                    } else {
+                        $failmsg = "$command failed";
+                    }
                 }
                 break;
-            }
-            case 'uninstall': {
-                if ($installer->uninstall($params[0], $options)) {
-                    $this->ui->displayLine("uninstall ok");
-                } else {
-                    $failmsg = "uninstall failed";
+            case 'uninstall':
+                foreach ($params as $pkg) {
+                    if ($this->installer->uninstall($pkg, $options)) {
+                        $this->ui->displayLine("uninstall ok");
+                    } else {
+                        $failmsg = "uninstall failed";
+                    }
                 }
                 break;
-            }
-            default: {
+            default:
                 return false;
-            }
         }
         if ($failmsg) {
             return $this->raiseError($failmsg);
index 92191ee89422122891f14432b9a5b61160bd923a..0bbea5365a1770c7157288d8ebc56e293b1406d0 100644 (file)
@@ -271,13 +271,13 @@ class PEAR_Installer extends PEAR_Common
      *
      * @param $pkgfile path to the package file
      *
-     * @return bool true if successful, false if not
+     * @return array package info if successful, null if not
      */
 
     function install($pkgfile, $options = array())
     {
         // recognized options:
-        // - register_only : update registry but don't install files
+        // - register-only : update registry but don't install files
         // - upgrade       : upgrade existing install
         // - soft          : fail silently
         //
@@ -379,7 +379,7 @@ class PEAR_Installer extends PEAR_Common
                 if (empty($options['soft'])) {
                     $this->log(0, $error);
                 }
-                return $this->raiseError('Dependencies failed');
+                return $this->raiseError("$pkgname: dependencies failed");
             }
         }
 
@@ -399,7 +399,7 @@ class PEAR_Installer extends PEAR_Common
             if (empty($options['force']) && !version_compare($v2, $v1, 'gt')) {
                 return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)");
             }
-            if (empty($options['register_only'])) {
+            if (empty($options['register-only'])) {
                 // when upgrading, remove old release's files first:
                 if (PEAR::isError($err = $this->_deletePackageFiles($pkgname))) {
                     return $this->raiseError($err);
@@ -411,7 +411,7 @@ class PEAR_Installer extends PEAR_Common
 
         // info from the package it self we want to access from _installFile
         $this->pkginfo = $pkginfo;
-        if (empty($options['register_only'])) {
+        if (empty($options['register-only'])) {
             if (!is_dir($this->config->get('php_dir'))) {
                 return $this->raiseError("no script destination directory\n",
                                          null, PEAR_ERROR_DIE);
@@ -446,7 +446,10 @@ class PEAR_Installer extends PEAR_Common
         } else {
             $ret = $this->registry->updatePackage($pkgname, $this->pkginfo, false);
         }
-        return $ret;
+        if (!$ret) {
+            return null;
+        }
+        return $pkginfo;
     }
 
     // }}}
index f1d7a4e5585cbade9a63ce949166971b68516201..c68e79ef1d396db86df57f607e3906ccd1ef4d48 100644 (file)
@@ -32,12 +32,10 @@ require_once 'Console/Getopt.php';
 
 PEAR_Command::setFrontendType('CLI');
 $all_commands = PEAR_Command::getCommands();
-$cmd_options  = PEAR_Command::getOptions();
-$progname = basename(__FILE__);
 
 $argv = Console_Getopt::readPHPArgv();
-array_shift($argv);
-$options = Console_Getopt::getopt($argv, "c:C:d:D:Gh?sSqu:v" . $cmd_options);
+$progname = basename(array_shift($argv));
+$options = Console_Getopt::getopt($argv, "c:C:d:D:Gh?sSqu:v");
 if (PEAR::isError($options)) {
     usage($options);
 }
@@ -45,9 +43,13 @@ if (PEAR::isError($options)) {
 $opts = $options[0];
 
 $fetype = 'CLI';
-foreach ($opts as $opt) {
-    if ($opt[0] == 'G') {
-        $fetype = 'Gtk';
+if ($progname == 'gpear' || $progname == 'pear-gtk') {
+    $fetype = 'Gtk';
+} else {
+    foreach ($opts as $opt) {
+        if ($opt[0] == 'G') {
+            $fetype = 'Gtk';
+        }
     }
 }
 PEAR_Command::setFrontendType($fetype);
@@ -116,7 +118,7 @@ if ($store_user_config) {
     $config->store('user');
 }
 
-$command = (isset($options[1][0])) ? $options[1][0] : null;
+$command = (isset($options[1][0])) ? array_shift($options[1]) : null;
 
 if (empty($command) && ($store_user_config || $store_system_config)) {
     exit;
@@ -133,9 +135,35 @@ if ($fetype == 'Gtk') {
     if (PEAR::isError($cmd)) {
         die($cmd->getMessage());
     }
+    
+    $short_args = $long_args = array();
+    PEAR_Command::getGetoptArgs($command, $short_args, $long_args);
+    if (PEAR::isError($tmp = Console_Getopt::getopt($params, $short_args, $long_args))) {
+        return $this->raiseError($tmp);
+    }
+    list($tmpopt, $params) = $tmp;
+    $options = array();
+    foreach ($tmpopt as $foo => $tmp2) {
+        list($opt, $value) = $tmp2;
+        if ($value === null) {
+            $value = true;
+        }
+        if (strlen($opt) == 1) {
+            foreach ($this->commands[$command]['options'] as $o => $d) {
+                if (@$d['shortopt'] == $opt) {
+                    $options[$o] = $value;
+                }
+            }
+        } else {
+            if (substr($opt, 0, 2) == '--') {
+                    $options[substr($opt, 2)] = $value;
+                }
+            }
+        }
+
 
-    $cmdargs = array_slice($options[1], 1);
-    $ok = $cmd->run($command, $cmdopts, $cmdargs);
+
+    $ok = $cmd->run($command, $options[1]);
     if ($ok === false) {
         PEAR::raiseError("unknown command `$command'");
     }
@@ -190,6 +218,10 @@ function cmdHelp($command)
         "     -u foo     unset `foo' in the user configuration\n".
         "     -h, -?     display help/usage (this message)\n";
     } elseif ($help = PEAR_Command::getHelp($command)) {
+        if (is_string($help)) {
+            $help = preg_replace('/{config\s+([^\}]+)}/e', "\$config->get('\1')", $help);
+            return "Usage : $help";
+        }
         return "Usage : $progname $command {$help[0]}\n{$help[1]}";
     }
     return "No such command";