src/PEAR/Command/Install.php
function doInstall($command, $options, $params)
    {
        if (!class_exists('PEAR_PackageFile')) {
            require_once 'PEAR/PackageFile.php';
        }

        if (isset($options['installroot']) && isset($options['packagingroot'])) {
            return $this->raiseError('ERROR: cannot use both --installroot and --packagingroot');
        }

        $reg = &$this->config->getRegistry();
        $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel');
        if (!$reg->channelExists($channel)) {
            return $this->raiseError('Channel "' . $channel . '" does not exist');
        }

        if (empty($this->installer)) {
            $this->installer = &$this->getInstaller($this->ui);
        }

        if ($command == 'upgrade' || $command == 'upgrade-all') {
            // If people run the upgrade command but pass nothing, emulate a upgrade-all
            if ($command == 'upgrade' && empty($params)) {
                return $this->doUpgradeAll($command, $options, $params);
            }
            $options['upgrade'] = true;
        } else {
            $packages = $params;
        }

        $instreg = &$reg; // instreg used to check if package is installed
        if (isset($options['packagingroot']) && !isset($options['upgrade'])) {
            $packrootphp_dir = $this->installer->_prependPath(
                $this->config->get('php_dir', null, 'pear.php.net'),
                $options['packagingroot']);
            $instreg = new PEAR_Registry($packrootphp_dir); // other instreg!

            if ($this->config->get('verbose') > 2) {
                $this->ui->outputData('using package root: ' . $options['packagingroot']);
            }
        }

        $abstractpackages = $otherpackages = array();
        // parse params
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);

        foreach ($params as $param) {
            if (strpos($param, 'http://') === 0) {
                $otherpackages[] = $param;
                continue;
            }

            if (strpos($param, 'channel://') === false && @file_exists($param)) {
                if (isset($options['force'])) {
                    $otherpackages[] = $param;
                    continue;
                }

                $pkg = new PEAR_PackageFile($this->config);
                $pf  = $pkg->fromAnyFile($param, PEAR_VALIDATE_DOWNLOADING);
                if (PEAR::isError($pf)) {
                    $otherpackages[] = $param;
                    continue;
                }

                $exists   = $reg->packageExists($pf->getPackage(), $pf->getChannel());
                $pversion = $reg->packageInfo($pf->getPackage(), 'version', $pf->getChannel());
                $version_compare = version_compare($pf->getVersion(), $pversion, '<=');
                if ($exists && $version_compare) {
                    if ($this->config->get('verbose')) {
                        $this->ui->outputData('Ignoring installed package ' .
                            $reg->parsedPackageNameToString(
                            array('package' => $pf->getPackage(),
                                  'channel' => $pf->getChannel()), true));
                    }
                    continue;
                }
                $otherpackages[] = $param;
                continue;
            }

            $e = $reg->parsePackageName($param, $channel);
            if (PEAR::isError($e)) {
                $otherpackages[] = $param;
            } else {
                $abstractpackages[] = $e;
            }
        }
        PEAR::staticPopErrorHandling();

        // if there are any local package .tgz or remote static url, we can't
        // filter.  The filter only works for abstract packages
        if (count($abstractpackages) && !isset($options['force'])) {
            // when not being forced, only do necessary upgrades/installs
            if (isset($options['upgrade'])) {
                $abstractpackages = $this->_filterUptodatePackages($abstractpackages, $command);
            } else {
                $count = count($abstractpackages);
                foreach ($abstractpackages as $i => $package) {
                    if (isset($package['group'])) {
                        // do not filter out install groups
                        continue;
                    }

                    if ($instreg->packageExists($package['package'], $package['channel'])) {
                        if ($count > 1) {
                            if ($this->config->get('verbose')) {
                                $this->ui->outputData('Ignoring installed package ' .
                                    $reg->parsedPackageNameToString($package, true));
                            }
                            unset($abstractpackages[$i]);
                        } elseif ($count === 1) {
                            // Lets try to upgrade it since it's already installed
                            $options['upgrade'] = true;
                        }
                    }
                }
            }
            $abstractpackages =
                array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
        } elseif (count($abstractpackages)) {
            $abstractpackages =
                array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
        }

        $packages = array_merge($abstractpackages, $otherpackages);
        if (!count($packages)) {
            $c = '';
            if (isset($options['channel'])){
                $c .= ' in channel "' . $options['channel'] . '"';
            }
            $this->ui->outputData('Nothing to ' . $command . $c);
            return true;
        }

        $this->downloader = &$this->getDownloader($this->ui, $options, $this->config);
        $errors = $downloaded = $binaries = array();
        $downloaded = &$this->downloader->download($packages);
        if (PEAR::isError($downloaded)) {
            return $this->raiseError($downloaded);
        }

        $errors = $this->downloader->getErrorMsgs();
        if (count($errors)) {
            $err = array();
            $err['data'] = array();
            foreach ($errors as $error) {
                if ($error !== null) {
                    $err['data'][] = array($error);
                }
            }

            if (!empty($err['data'])) {
                $err['headline'] = 'Install Errors';
                $this->ui->outputData($err);
            }

            if (!count($downloaded)) {
                return $this->raiseError("$command failed");
            }
        }

        $data = array(
            'headline' => 'Packages that would be Installed'
        );

        if (isset($options['pretend'])) {
            foreach ($downloaded as $package) {
                $data['data'][] = array($reg->parsedPackageNameToString($package->getParsedPackage()));
            }
            $this->ui->outputData($data, 'pretend');
            return true;
        }

        $this->installer->setOptions($options);
        $this->installer->sortPackagesForInstall($downloaded);
        if (PEAR::isError($err = $this->installer->setDownloadedPackages($downloaded))) {
            $this->raiseError($err->getMessage());
            return true;
        }

        $binaries = $extrainfo = array();
        foreach ($downloaded as $param) {
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $info = $this->installer->install($param, $options);
            PEAR::staticPopErrorHandling();
            if (PEAR::isError($info)) {
                $oldinfo = $info;
                $pkg = &$param->getPackageFile();
                if ($info->getCode() != PEAR_INSTALLER_NOBINARY) {
                    if (!($info = $pkg->installBinary($this->installer))) {
                        $this->ui->outputData('ERROR: ' .$oldinfo->getMessage());
                        continue;
                    }

                    // we just installed a different package than requested,
                    // let's change the param and info so that the rest of this works
                    $param = $info[0];
                    $info  = $info[1];
                }
            }

            if (!is_array($info)) {
                return $this->raiseError("$command failed");
            }

            if ($param->getPackageType() == 'extsrc' ||
                  $param->getPackageType() == 'extbin' ||
                  $param->getPackageType() == 'zendextsrc' ||
                  $param->getPackageType() == 'zendextbin'
            ) {
                $pkg = &$param->getPackageFile();
                if ($instbin = $pkg->getInstalledBinary()) {
                    $instpkg = &$instreg->getPackage($instbin, $pkg->getChannel());
                } else {
                    $instpkg = &$instreg->getPackage($pkg->getPackage(), $pkg->getChannel());
                }

                foreach ($instpkg->getFilelist() as $name => $atts) {
                    $pinfo = pathinfo($atts['installed_as']);
                    if (!isset($pinfo['extension']) ||
                          in_array($pinfo['extension'], array('c', 'h'))
                    ) {
                        continue; // make sure we don't match php_blah.h
                    }

                    if ((strpos($pinfo['basename'], 'php_') === 0 &&
                          $pinfo['extension'] == 'dll') ||
                          // most unices
                          $pinfo['extension'] == 'so' ||
                          // hp-ux
                          $pinfo['extension'] == 'sl') {
                        $binaries[] = array($atts['installed_as'], $pinfo);
                        break;
                    }
                }

                if (count($binaries)) {
                    foreach ($binaries as $pinfo) {
                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                        $ret = $this->enableExtension(array($pinfo[0]), $param->getPackageType());
                        PEAR::staticPopErrorHandling();
                        if (PEAR::isError($ret)) {
                            $extrainfo[] = $ret->getMessage();
                            if ($param->getPackageType() == 'extsrc' ||
                                  $param->getPackageType() == 'extbin') {
                                $exttype = 'extension';
                            } else {
                                ob_start();
                                phpinfo(INFO_GENERAL);
                                $info = ob_get_contents();
                                ob_end_clean();
                                $debug = function_exists('leak') ? '_debug' : '';
                                $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
                                $exttype = 'zend_extension' . $debug . $ts;
                            }
                            $extrainfo[] = 'You should add "' . $exttype . '=' .
                                $pinfo[1]['basename'] . '" to php.ini';
                        } else {
                            $extrainfo[] = 'Extension ' . $instpkg->getProvidesExtension() .
                                ' enabled in php.ini';
                        }
                    }
                }
            }

            if ($this->config->get('verbose') > 0) {
                $chan = $param->getChannel();
                $label = $reg->parsedPackageNameToString(
                    array(
                        'channel' => $chan,
                        'package' => $param->getPackage(),
                        'version' => $param->getVersion(),
                    ));
                $out = array('data' => "$command ok: $label");
                if (isset($info['release_warnings'])) {
                    $out['release_warnings'] = $info['release_warnings'];
                }
                $this->ui->outputData($out, $command);

                if (!isset($options['register-only']) && !isset($options['offline'])) {
                    if ($this->config->isDefinedLayer('ftp')) {
                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                        $info = $this->installer->ftpInstall($param);
                        PEAR::staticPopErrorHandling();
                        if (PEAR::isError($info)) {
                            $this->ui->outputData($info->getMessage());
                            $this->ui->outputData("remote install failed: $label");
                        } else {
                            $this->ui->outputData("remote install ok: $label");
                        }
                    }
                }
            }

            $deps = $param->getDeps();
            if ($deps) {
                if (isset($deps['group'])) {
                    $groups = $deps['group'];
                    if (!isset($groups[0])) {
                        $groups = array($groups);
                    }

                    foreach ($groups as $group) {
                        if ($group['attribs']['name'] == 'default') {
                            // default group is always installed, unless the user
                            // explicitly chooses to install another group
                            continue;
                        }
                        $extrainfo[] = $param->getPackage() . ': Optional feature ' .
                            $group['attribs']['name'] . ' available (' .
                            $group['attribs']['hint'] . ')';
                    }

                    $extrainfo[] = $param->getPackage() .
                        ': To install optional features use "pear install ' .
                        $reg->parsedPackageNameToString(
                            array('package' => $param->getPackage(),
                                  'channel' => $param->getChannel()), true) .
                              '#featurename"';
                }
            }

            $pkg = &$instreg->getPackage($param->getPackage(), $param->getChannel());
            // $pkg may be NULL if install is a 'fake' install via --packagingroot
            if (is_object($pkg)) {
                $pkg->setConfig($this->config);
                if ($list = $pkg->listPostinstallScripts()) {
                    $pn = $reg->parsedPackageNameToString(array('channel' =>
                       $param->getChannel(), 'package' => $param->getPackage()), true);
                    $extrainfo[] = $pn . ' has post-install scripts:';
                    foreach ($list as $file) {
                        $extrainfo[] = $file;
                    }
                    $extrainfo[] = $param->getPackage() .
                        ': Use "pear run-scripts ' . $pn . '" to finish setup.';
                    $extrainfo[] = 'DO NOT RUN SCRIPTS FROM UNTRUSTED SOURCES';
                }
            }
        }

        if (count($extrainfo)) {
            foreach ($extrainfo as $info) {
                $this->ui->outputData($info);
            }
        }

        return true;
    }
function doInstall($command, $options, $params)
    {
        if (!class_exists('PEAR_PackageFile')) {
            require_once 'PEAR/PackageFile.php';
        }

        if (isset($options['installroot']) && isset($options['packagingroot'])) {
            return $this->raiseError('ERROR: cannot use both --installroot and --packagingroot');
        }

        $reg = &$this->config->getRegistry();
        $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel');
        if (!$reg->channelExists($channel)) {
            return $this->raiseError('Channel "' . $channel . '" does not exist');
        }

        if (empty($this->installer)) {
            $this->installer = &$this->getInstaller($this->ui);
        }

        if ($command == 'upgrade' || $command == 'upgrade-all') {
            // If people run the upgrade command but pass nothing, emulate a upgrade-all
            if ($command == 'upgrade' && empty($params)) {
                return $this->doUpgradeAll($command, $options, $params);
            }
            $options['upgrade'] = true;
        } else {
            $packages = $params;
        }

        $instreg = &$reg; // instreg used to check if package is installed
        if (isset($options['packagingroot']) && !isset($options['upgrade'])) {
            $packrootphp_dir = $this->installer->_prependPath(
                $this->config->get('php_dir', null, 'pear.php.net'),
                $options['packagingroot']);
            $instreg = new PEAR_Registry($packrootphp_dir); // other instreg!

            if ($this->config->get('verbose') > 2) {
                $this->ui->outputData('using package root: ' . $options['packagingroot']);
            }
        }

        $abstractpackages = $otherpackages = array();
        // parse params
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);

        foreach ($params as $param) {
            if (strpos($param, 'http://') === 0) {
                $otherpackages[] = $param;
                continue;
            }

            if (strpos($param, 'channel://') === false && @file_exists($param)) {
                if (isset($options['force'])) {
                    $otherpackages[] = $param;
                    continue;
                }

                $pkg = new PEAR_PackageFile($this->config);
                $pf  = $pkg->fromAnyFile($param, PEAR_VALIDATE_DOWNLOADING);
                if (PEAR::isError($pf)) {
                    $otherpackages[] = $param;
                    continue;
                }

                $exists   = $reg->packageExists($pf->getPackage(), $pf->getChannel());
                $pversion = $reg->packageInfo($pf->getPackage(), 'version', $pf->getChannel());
                $version_compare = version_compare($pf->getVersion(), $pversion, '<=');
                if ($exists && $version_compare) {
                    if ($this->config->get('verbose')) {
                        $this->ui->outputData('Ignoring installed package ' .
                            $reg->parsedPackageNameToString(
                            array('package' => $pf->getPackage(),
                                  'channel' => $pf->getChannel()), true));
                    }
                    continue;
                }
                $otherpackages[] = $param;
                continue;
            }

            $e = $reg->parsePackageName($param, $channel);
            if (PEAR::isError($e)) {
                $otherpackages[] = $param;
            } else {
                $abstractpackages[] = $e;
            }
        }
        PEAR::staticPopErrorHandling();

        // if there are any local package .tgz or remote static url, we can't
        // filter.  The filter only works for abstract packages
        if (count($abstractpackages) && !isset($options['force'])) {
            // when not being forced, only do necessary upgrades/installs
            if (isset($options['upgrade'])) {
                $abstractpackages = $this->_filterUptodatePackages($abstractpackages, $command);
            } else {
                $count = count($abstractpackages);
                foreach ($abstractpackages as $i => $package) {
                    if (isset($package['group'])) {
                        // do not filter out install groups
                        continue;
                    }

                    if ($instreg->packageExists($package['package'], $package['channel'])) {
                        if ($count > 1) {
                            if ($this->config->get('verbose')) {
                                $this->ui->outputData('Ignoring installed package ' .
                                    $reg->parsedPackageNameToString($package, true));
                            }
                            unset($abstractpackages[$i]);
                        } elseif ($count === 1) {
                            // Lets try to upgrade it since it's already installed
                            $options['upgrade'] = true;
                        }
                    }
                }
            }
            $abstractpackages =
                array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
        } elseif (count($abstractpackages)) {
            $abstractpackages =
                array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
        }

        $packages = array_merge($abstractpackages, $otherpackages);
        if (!count($packages)) {
            $c = '';
            if (isset($options['channel'])){
                $c .= ' in channel "' . $options['channel'] . '"';
            }
            $this->ui->outputData('Nothing to ' . $command . $c);
            return true;
        }

        $this->downloader = &$this->getDownloader($this->ui, $options, $this->config);
        $errors = $downloaded = $binaries = array();
        $downloaded = &$this->downloader->download($packages);
        if (PEAR::isError($downloaded)) {
            return $this->raiseError($downloaded);
        }

        $errors = $this->downloader->getErrorMsgs();
        if (count($errors)) {
            $err = array();
            $err['data'] = array();
            foreach ($errors as $error) {
                if ($error !== null) {
                    $err['data'][] = array($error);
                }
            }

            if (!empty($err['data'])) {
                $err['headline'] = 'Install Errors';
                $this->ui->outputData($err);
            }

            if (!count($downloaded)) {
                return $this->raiseError("$command failed");
            }
        }

        $data = array(
            'headline' => 'Packages that would be Installed'
        );

        if (isset($options['pretend'])) {
            foreach ($downloaded as $package) {
                $data['data'][] = array($reg->parsedPackageNameToString($package->getParsedPackage()));
            }
            $this->ui->outputData($data, 'pretend');
            return true;
        }

        $this->installer->setOptions($options);
        $this->installer->sortPackagesForInstall($downloaded);
        if (PEAR::isError($err = $this->installer->setDownloadedPackages($downloaded))) {
            $this->raiseError($err->getMessage());
            return true;
        }

        $binaries = $extrainfo = array();
        foreach ($downloaded as $param) {
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
            $info = $this->installer->install($param, $options);
            PEAR::staticPopErrorHandling();
            if (PEAR::isError($info)) {
                $oldinfo = $info;
                $pkg = &$param->getPackageFile();
                if ($info->getCode() != PEAR_INSTALLER_NOBINARY) {
                    if (!($info = $pkg->installBinary($this->installer))) {
                        $this->ui->outputData('ERROR: ' .$oldinfo->getMessage());
                        continue;
                    }

                    // we just installed a different package than requested,
                    // let's change the param and info so that the rest of this works
                    $param = $info[0];
                    $info  = $info[1];
                }
            }

            if (!is_array($info)) {
                return $this->raiseError("$command failed");
            }

            if ($param->getPackageType() == 'extsrc' ||
                  $param->getPackageType() == 'extbin' ||
                  $param->getPackageType() == 'zendextsrc' ||
                  $param->getPackageType() == 'zendextbin'
            ) {
                $pkg = &$param->getPackageFile();
                if ($instbin = $pkg->getInstalledBinary()) {
                    $instpkg = &$instreg->getPackage($instbin, $pkg->getChannel());
                } else {
                    $instpkg = &$instreg->getPackage($pkg->getPackage(), $pkg->getChannel());
                }

                foreach ($instpkg->getFilelist() as $name => $atts) {
                    $pinfo = pathinfo($atts['installed_as']);
                    if (!isset($pinfo['extension']) ||
                          in_array($pinfo['extension'], array('c', 'h'))
                    ) {
                        continue; // make sure we don't match php_blah.h
                    }

                    if ((strpos($pinfo['basename'], 'php_') === 0 &&
                          $pinfo['extension'] == 'dll') ||
                          // most unices
                          $pinfo['extension'] == 'so' ||
                          // hp-ux
                          $pinfo['extension'] == 'sl') {
                        $binaries[] = array($atts['installed_as'], $pinfo);
                        break;
                    }
                }

                if (count($binaries)) {
                    foreach ($binaries as $pinfo) {
                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                        $ret = $this->enableExtension(array($pinfo[0]), $param->getPackageType());
                        PEAR::staticPopErrorHandling();
                        if (PEAR::isError($ret)) {
                            $extrainfo[] = $ret->getMessage();
                            if ($param->getPackageType() == 'extsrc' ||
                                  $param->getPackageType() == 'extbin') {
                                $exttype = 'extension';
                            } else {
                                ob_start();
                                phpinfo(INFO_GENERAL);
                                $info = ob_get_contents();
                                ob_end_clean();
                                $debug = function_exists('leak') ? '_debug' : '';
                                $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
                                $exttype = 'zend_extension' . $debug . $ts;
                            }
                            $extrainfo[] = 'You should add "' . $exttype . '=' .
                                $pinfo[1]['basename'] . '" to php.ini';
                        } else {
                            $extrainfo[] = 'Extension ' . $instpkg->getProvidesExtension() .
                                ' enabled in php.ini';
                        }
                    }
                }
            }

            if ($this->config->get('verbose') > 0) {
                $chan = $param->getChannel();
                $label = $reg->parsedPackageNameToString(
                    array(
                        'channel' => $chan,
                        'package' => $param->getPackage(),
                        'version' => $param->getVersion(),
                    ));
                $out = array('data' => "$command ok: $label");
                if (isset($info['release_warnings'])) {
                    $out['release_warnings'] = $info['release_warnings'];
                }
                $this->ui->outputData($out, $command);

                if (!isset($options['register-only']) && !isset($options['offline'])) {
                    if ($this->config->isDefinedLayer('ftp')) {
                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
                        $info = $this->installer->ftpInstall($param);
                        PEAR::staticPopErrorHandling();
                        if (PEAR::isError($info)) {
                            $this->ui->outputData($info->getMessage());
                            $this->ui->outputData("remote install failed: $label");
                        } else {
                            $this->ui->outputData("remote install ok: $label");
                        }
                    }
                }
            }

            $deps = $param->getDeps();
            if ($deps) {
                if (isset($deps['group'])) {
                    $groups = $deps['group'];
                    if (!isset($groups[0])) {
                        $groups = array($groups);
                    }

                    foreach ($groups as $group) {
                        if ($group['attribs']['name'] == 'default') {
                            // default group is always installed, unless the user
                            // explicitly chooses to install another group
                            continue;
                        }
                        $extrainfo[] = $param->getPackage() . ': Optional feature ' .
                            $group['attribs']['name'] . ' available (' .
                            $group['attribs']['hint'] . ')';
                    }

                    $extrainfo[] = $param->getPackage() .
                        ': To install optional features use "pear install ' .
                        $reg->parsedPackageNameToString(
                            array('package' => $param->getPackage(),
                                  'channel' => $param->getChannel()), true) .
                              '#featurename"';
                }
            }

            $pkg = &$instreg->getPackage($param->getPackage(), $param->getChannel());
            // $pkg may be NULL if install is a 'fake' install via --packagingroot
            if (is_object($pkg)) {
                $pkg->setConfig($this->config);
                if ($list = $pkg->listPostinstallScripts()) {
                    $pn = $reg->parsedPackageNameToString(array('channel' =>
                       $param->getChannel(), 'package' => $param->getPackage()), true);
                    $extrainfo[] = $pn . ' has post-install scripts:';
                    foreach ($list as $file) {
                        $extrainfo[] = $file;
                    }
                    $extrainfo[] = $param->getPackage() .
                        ': Use "pear run-scripts ' . $pn . '" to finish setup.';
                    $extrainfo[] = 'DO NOT RUN SCRIPTS FROM UNTRUSTED SOURCES';
                }
            }
        }

        if (count($extrainfo)) {
            foreach ($extrainfo as $info) {
                $this->ui->outputData($info);
            }
        }

        return true;
    }
{{{ doInstall()