2 // vim: foldmethod=marker
6 * @author ICHII Takashi <ichii386@schweetheart.jp>
7 * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
12 require_once 'PEAR.php';
13 require_once 'PEAR/Config.php';
14 require_once 'PEAR/Command.php';
15 require_once 'PEAR/PackageFile.php';
17 // {{{ Ethna_PearWrapper
19 * wrapper class for PEAR_Command
20 * This class should be instantiated in ethna handler.
22 * @author ICHII Takashi <ichii386@schweetheart.jp>
26 class Ethna_PearWrapper
33 /** @protected string channel url of ethna repositry */
36 /** @protected string target, 'master' or 'local' */
39 /** @protected object controller object collesponding to the target */
40 protected $target_ctl;
42 /** @protected object PEAR_Config PEAR_Config object */
45 /** @protected object PEAR_Registry PEAR_Registry object */
48 /** @protected object PEAR_Frontend PEAR_Frontend(_CLI) object */
51 /** @protected array options for pearcmd */
57 // {{{ constructor, initializer
59 * Ethna_PearWrapper constructor
63 public function __construct()
65 $this->channel = null;
67 $this->registry = null;
70 $this->target_ctl = null;
74 * setup PEAR_Config and so on.
76 * @param string $target whether 'master' or 'local'
77 * @param string|null $app_dir local application directory.
78 * @param string|null $channel channel for the package repository.
79 * @return true|Ethna_Error
81 public function init($target, $app_dir = null, $channel = null)
84 if ($target == 'master') {
85 $this->target = 'master';
87 // default target is 'local'.
88 $this->target = 'local';
91 // setup PEAR_Frontend
92 PEAR_Command::setFrontendType('CLI');
93 $this->ui = PEAR_Command::getFrontendObject();
95 // set PEAR's error handling
96 // TODO: if PEAR/Command/Install.php is newer than 1.117, displayError goes well.
97 PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array(&$this->ui, 'displayFatalError'));
100 $master_setting = Ethna_Handle::getMasterSetting('repositry');
101 if ($channel !== null) {
102 $this->channel = $channel;
103 } else if (isset($master_setting["channel_{$target}"])) {
104 $this->channel = $master_setting["channel_{$target}"];
106 $this->channel = 'pear.ethna.jp';
109 // set target controller
110 if ($target == 'master') {
111 $this->target_ctl = Ethna_Handle::getEthnaController();
113 $this->target_ctl = Ethna_Handle::getAppController($app_dir);
115 if (Ethna::isError($this->target_ctl)) {
116 return $this->target_ctl;
120 if ($target == 'master') {
121 $ret = $this->_setMasterConfig();
123 $ret = $this->_setLocalConfig();
125 if (Ethna::isError($ret)) {
128 $this->ui->setConfig($this->config);
130 // setup PEAR_Registry
131 $this->registry = $this->config->getRegistry();
139 * @return true|Ethna_Error
142 private function _setMasterConfig()
147 $this->config = PEAR_Config::singleton();
150 $reg = $this->config->getRegistry();
151 if ($reg->channelExists($this->channel) == false) {
152 $ret = $this->doChannelDiscover();
153 if (Ethna::isError($ret)) {
164 * @return true|Ethna_Error
167 protected function _setLocalConfig()
172 $base = $this->target_ctl->getBaseDir();
173 $bin = $this->target_ctl->getDirectory('bin');
174 $tmp = $this->target_ctl->getDirectory('tmp');
176 'php_dir' => "{$base}/skel",
177 'bin_dir' => "{$bin}",
178 'cache_dir' => "{$tmp}/.pear/cache",
179 'download_dir' => "{$tmp}/.pear/download",
180 'temp_dir' => "{$tmp}/.pear/temp",
181 'doc_dir' => "{$tmp}/.pear/doc",
182 'ext_dir' => "{$tmp}/.pear/ext",
183 'data_dir' => "{$tmp}/.pear/data",
184 'test_dir' => "{$tmp}/.pear/test",
188 foreach ($dirs as $key => $dir) {
189 if (is_dir($dir) == false) {
190 Ethna_Util::mkdir($dir, 0755);
194 $pearrc = "{$base}/skel/.pearrc";
195 $this->config = PEAR_Config::singleton($pearrc);
197 // read local .pearrc if exists.
198 if (is_file($pearrc) && is_readable($pearrc)) {
199 $this->config->readConfigFile($pearrc);
202 // set dirs to config
203 foreach ($dirs as $key => $dir) {
204 $this->config->set($key, $dir);
208 $reg = $this->config->getRegistry();
209 if ($reg->channelExists($this->channel) == false) {
210 $ret = $this->doChannelDiscover();
211 if (Ethna::isError($ret)) {
215 $this->config->set('default_channel', $this->channel);
217 // write local .pearrc
218 $this->config->writeConfigFile();
228 * @return true|Ethna_Error
230 public function doClearCache()
233 $r = $this->_run('clear-cache', array(), array());
234 if (PEAR::isError($r)) {
241 // {{{ doChannelDiscover
243 * do channel-discover
245 * @return true|Ethna_Error
247 public function doChannelDiscover()
250 $r = $this->_run('channel-discover', array(), array($this->channel));
251 if (PEAR::isError($r)) {
258 // {{{ isChannelExists
260 * whether channel discovered or not
264 public function isChannelExists()
266 return $this->registry->channelExists($this->channel);
270 // {{{ doChannelUpdate
274 * @return true|Ethna_Error
276 public function doChannelUpdate()
279 if ($this->isChannelExists() == false) {
280 $r = $this->doChannelDiscover();
281 if (PEAR::isError($r)) {
285 $r = $this->_run('channel-update', array(), array($this->channel));
286 if (PEAR::isError($r)) {
293 // {{{ _doInstallOrUpgrade
297 * @param string $command 'install' or 'upgrade'
298 * @param string $package package string
299 * @return true|Ethna_Error
302 private function _doInstallOrUpgrade($command, $package)
305 $r = $this->_run($command, array(), array($package));
306 if (PEAR::isError($r)) {
317 * @param string $pkg_name package name.
318 * @param string $state package state.
319 * @return true|Ethna_Error
321 public function doInstall($pkg_name, $state = null)
323 $pkg = "{$this->channel}/{$pkg_name}";
324 if ($state !== null) {
325 $pkg = "{$pkg}-{$state}";
327 $r = $this->_doInstallOrUpgrade('install', $pkg);
332 // {{{ doInstallFromTgz
334 * do install from local tgz file
336 * @param string $pkg_file package filename
337 * @return true|Ethna_Error
339 public function doInstallFromTgz($pkg_file)
341 $r = $this->_doInstallOrUpgrade('install', $pkg_file);
350 * @param string $pkg_name package name.
351 * @param string $state package state.
352 * @return true|Ethna_Error
354 public function doUpgrade($pkg_name, $state = null)
356 $pkg = "{$this->channel}/{$pkg_name}";
357 if ($state !== null) {
358 $pkg = "{$pkg}-{$state}";
360 $r = $this->_doInstallOrUpgrade('upgrade', $pkg);
365 // {{{ doUpgradeFromTgz
367 * do upgrade from local tgz file
369 * @param string $pkg_file package filename
370 * @return true|Ethna_Error
372 public function doUpgradeFromTgz($pkg_file)
374 $r = $this->_doInstallOrUpgrade('upgrade', $pkg_file);
381 * check package installed
383 * @param string $package package name
386 public function isInstalled($package)
388 return $this->registry->packageExists($package, $this->channel);
394 * get package version
396 * @param string $package package name
397 * @return string version string
399 public function getVersion($package)
401 $pobj = $this->registry->getPackage($package, $this->channel);
402 return $pobj->getVersion();
408 * get package version
410 * @param string $package package name
411 * @return string version string
413 public function getState($package)
415 $pobj = $this->registry->getPackage($package, $this->channel);
416 return $pobj->getState();
422 * do uninstall (packages installed with ethna command)
424 * @return true|Ethna_Error
426 public function doUninstall($package)
429 if ($this->isInstalled($package) == false) {
430 return Ethna::raiseNotice("{$this->channel}/{$package} is not installed.");
432 $r = $this->_run('uninstall', array(), array("{$this->channel}/{$package}"));
433 if (PEAR::isError($r)) {
436 if ($this->isInstalled($package)) {
437 return Ethna::raiseNotice("uninstall failed: {$this->channel}/{$package}");
443 // {{{ getPackageNameFromTgz
445 * get package info from tar/tgz file.
447 * @param string $filename package file name.
448 * @return string package name
452 public function getPackageNameFromTgz($filename)
454 $config = PEAR_Config::singleton();
455 $packagefile = new PEAR_PackageFile($config);
456 $info = $packagefile->fromTgzFile($filename, PEAR_VALIDATE_NORMAL);
457 if (PEAR::isError($info)) {
460 $info_array = $info->toArray();
461 return $info_array['name'];
465 // {{{ getCanonicalPackageName
467 * get canonical package name (case sensitive)
469 * @param string $package package name.
470 * @return string canonical name
473 public function getCanonicalPackageName($package)
475 if ($this->isInstalled($package) == false) {
476 return Ethna::raiseNotice("{$this->channel}/{$package} is not installed.");
478 $pobj = $this->registry->getPackage($package, $this->channel);
479 $cname = $pobj->getName();
484 // {{{ getInstalledPackageList
486 * get installed package list
488 * @return array installed package list
491 public function getInstalledPackageList()
494 foreach ($this->registry->listPackages($this->channel) as $pkg) {
495 $ret[] = $this->getCanonicalPackageName($pkg);
503 * do info (packages installed with ethna command)
505 * @param string $package package name.
506 * @return true|Ethna_Error
508 public function doInfo($package)
510 return $this->_run('info', array(), array("{$this->channel}/{$package}"));
516 * do info (packages installable with ethna command)
518 * @param string $package package name.
519 * @return true|Ethna_Error
521 public function doRemoteInfo($package)
523 return $this->_run('remote-info', array(), array("{$this->channel}/{$package}"));
531 * @return true|Ethna_Error
533 public function doUpgradeAll()
535 return $this->_run('upgrade-all', array('channel' => "{$this->channel}"), array());
541 * do list (packages installed with ethna command)
543 * @return true|Ethna_Error
545 public function doList()
547 return $this->_run('list', array('channel' => $this->channel), array());
553 * do remote-list (packages installable with ethna command)
555 * @return true|Ethna_Error
557 public function doRemoteList()
559 return $this->_run('remote-list', array('channel' => $this->channel), array());
567 * @param string $command command name
568 * @param array $options options
569 * @param array $params parameters
570 * @return true|Ethna_Error
572 * @see PEAR_Command_Common::run, etc.
574 protected function _run($command, $options, $params)
576 if ($this->config === null) {
577 return Ethna::raiseError('configuration not initialized.');
582 $cmd = PEAR_Command::factory($command, $this->config);
583 if (PEAR::isError($cmd)) {
587 // pear command options
588 if (is_array($this->_pearopt) && count($this->_pearopt) > 0) {
589 $pearopts = $this->_getPearOpt($cmd, $command, $this->_pearopt);
590 $options = array_merge($pearopts, $options);
593 $ret = $cmd->run($command, $options, $params);
594 if (PEAR::isError($ret)) {
602 * provide yes-or-no dialog.
607 public function confirmDialog($message, $default = 'yes')
609 $ret = $this->ui->userConfirm($message);
614 * provide table layout
616 * @param array $headline headline
617 * @param array $rows rows which have the same size as headline's.
620 public function displayTable($caption, $headline, $rows)
623 foreach (array_keys($headline) as $k) {
624 $headline[$k] = sprintf('% -8s', $headline[$k]);
627 $data = array('caption' => $caption,
629 'headline' => $headline,
631 $this->ui->outputData($data);
638 public function setPearOpt($pearopt)
640 $this->_pearopt = $pearopt;
647 private function _getPearOpt(&$cmd_obj, $cmd_str, $opt_array)
649 $short_args = $long_args = null;
650 PEAR_Command::getGetOptArgs($cmd_str, $short_args, $long_args);
651 $opt = new Ethna_Getopt();
652 $opt_arg = $opt->getopt($opt_array, $short_args, $long_args);
653 if (Ethna::isError($opt_arg)) return array();
655 foreach ($opt_arg[0] as $tmp) {
656 list($opt, $val) = $tmp;
657 if ($val === null) $val = true;
658 if (strlen($opt) == 1) {
659 $cmd_opts = $cmd_obj->getOptions($cmd_str);
660 foreach ($cmd_opts as $o => $d) {
661 if (isset($d['shortopt']) && $d['shortopt'] == $opt) {
666 if (substr($opt, 0, 2) == '--') $opts[substr($opt, 2)] = $val;