PageRenderTime 47ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/class/PearWrapper.php

http://github.com/ethna/ethna
PHP | 676 lines | 332 code | 67 blank | 277 comment | 56 complexity | b6a5bfb69ee136a34d10651cdb613781 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. // vim: foldmethod=marker
  3. /**
  4. * PearWrapper.php
  5. *
  6. * @author ICHII Takashi <ichii386@schweetheart.jp>
  7. * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
  8. * @package Ethna
  9. * @version $Id: 0d5ac26a60682608f56c627841bb2f745d99a3c1 $
  10. */
  11. require_once 'PEAR.php';
  12. require_once 'PEAR/Config.php';
  13. require_once 'PEAR/Command.php';
  14. require_once 'PEAR/PackageFile.php';
  15. // {{{ Ethna_PearWrapper
  16. /**
  17. * wrapper class for PEAR_Command
  18. * This class should be instantiated in ethna handler.
  19. *
  20. * @author ICHII Takashi <ichii386@schweetheart.jp>
  21. * @access public
  22. * @package Ethna
  23. */
  24. class Ethna_PearWrapper
  25. {
  26. // {{{ properties
  27. /**#@+
  28. * @access private
  29. */
  30. /** @protected string channel url of ethna repositry */
  31. protected $channel;
  32. /** @protected string target, 'master' or 'local' */
  33. protected $target;
  34. /** @protected object controller object collesponding to the target */
  35. protected $target_ctl;
  36. /** @protected object PEAR_Config PEAR_Config object */
  37. protected $config;
  38. /** @protected object PEAR_Registry PEAR_Registry object */
  39. protected $registry;
  40. /** @protected object PEAR_Frontend PEAR_Frontend(_CLI) object */
  41. protected $ui;
  42. /** @protected array options for pearcmd */
  43. protected $_pearopt;
  44. /**#@-*/
  45. // }}}
  46. // {{{ constructor, initializer
  47. /**
  48. * Ethna_PearWrapper constructor
  49. *
  50. * @access public
  51. */
  52. public function __construct()
  53. {
  54. $this->channel = null;
  55. $this->config = null;
  56. $this->registry = null;
  57. $this->ui = null;
  58. $this->target = null;
  59. $this->target_ctl = null;
  60. }
  61. /**
  62. * setup PEAR_Config and so on.
  63. *
  64. * @param string $target whether 'master' or 'local'
  65. * @param string|null $app_dir local application directory.
  66. * @param string|null $channel channel for the package repository.
  67. * @return true|Ethna_Error
  68. */
  69. public function init($target, $app_dir = null, $channel = null)
  70. {
  71. $true = true;
  72. if ($target == 'master') {
  73. $this->target = 'master';
  74. } else {
  75. // default target is 'local'.
  76. $this->target = 'local';
  77. }
  78. // setup PEAR_Frontend
  79. PEAR_Command::setFrontendType('CLI');
  80. $this->ui = PEAR_Command::getFrontendObject();
  81. // set PEAR's error handling
  82. // TODO: if PEAR/Command/Install.php is newer than 1.117, displayError goes well.
  83. PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array($this->ui, 'displayFatalError'));
  84. // set channel
  85. $master_setting = Ethna_Handle::getMasterSetting('repositry');
  86. if ($channel !== null) {
  87. $this->channel = $channel;
  88. } else if (isset($master_setting["channel_{$target}"])) {
  89. $this->channel = $master_setting["channel_{$target}"];
  90. } else {
  91. $this->channel = 'pear.ethna.jp';
  92. }
  93. // set target controller
  94. if ($target == 'master') {
  95. $this->target_ctl = Ethna_Handle::getEthnaController();
  96. } else {
  97. $this->target_ctl = Ethna_Handle::getAppController($app_dir);
  98. }
  99. if (Ethna::isError($this->target_ctl)) {
  100. return $this->target_ctl;
  101. }
  102. // setup PEAR_Config
  103. if ($target == 'master') {
  104. $ret = $this->_setMasterConfig();
  105. } else {
  106. $ret = $this->_setLocalConfig();
  107. }
  108. if (Ethna::isError($ret)) {
  109. return $ret;
  110. }
  111. $this->ui->setConfig($this->config);
  112. // setup PEAR_Registry
  113. $this->registry = $this->config->getRegistry();
  114. return $true;
  115. }
  116. /**
  117. * config for master.
  118. *
  119. * @return true|Ethna_Error
  120. * @access private
  121. */
  122. private function _setMasterConfig()
  123. {
  124. $true = true;
  125. // setup config
  126. $this->config = PEAR_Config::singleton();
  127. // setup channel
  128. $reg = $this->config->getRegistry();
  129. if ($reg->channelExists($this->channel) == false) {
  130. $ret = $this->doChannelDiscover();
  131. if (Ethna::isError($ret)) {
  132. return $ret;
  133. }
  134. }
  135. return $true;
  136. }
  137. /**
  138. * config for local.
  139. *
  140. * @return true|Ethna_Error
  141. * @access protected
  142. */
  143. protected function _setLocalConfig()
  144. {
  145. $true = true;
  146. // determine dirs
  147. $base = $this->target_ctl->getBaseDir();
  148. $bin = $this->target_ctl->getDirectory('bin');
  149. $tmp = $this->target_ctl->getDirectory('tmp');
  150. $dirs = array(
  151. 'php_dir' => "{$base}/skel",
  152. 'bin_dir' => "{$bin}",
  153. 'cache_dir' => "{$tmp}/.pear/cache",
  154. 'download_dir' => "{$tmp}/.pear/download",
  155. 'temp_dir' => "{$tmp}/.pear/temp",
  156. 'doc_dir' => "{$tmp}/.pear/doc",
  157. 'ext_dir' => "{$tmp}/.pear/ext",
  158. 'data_dir' => "{$tmp}/.pear/data",
  159. 'test_dir' => "{$tmp}/.pear/test",
  160. );
  161. // mkdir
  162. foreach ($dirs as $key => $dir) {
  163. if (is_dir($dir) == false) {
  164. Ethna_Util::mkdir($dir, 0755);
  165. }
  166. }
  167. $pearrc = "{$base}/skel/.pearrc";
  168. $this->config = PEAR_Config::singleton($pearrc);
  169. // read local .pearrc if exists.
  170. if (is_file($pearrc) && is_readable($pearrc)) {
  171. $this->config->readConfigFile($pearrc);
  172. }
  173. // set dirs to config
  174. foreach ($dirs as $key => $dir) {
  175. $this->config->set($key, $dir);
  176. }
  177. // setup channel
  178. $reg = $this->config->getRegistry();
  179. if ($reg->channelExists($this->channel) == false) {
  180. $ret = $this->doChannelDiscover();
  181. if (Ethna::isError($ret)) {
  182. return $ret;
  183. }
  184. }
  185. $this->config->set('default_channel', $this->channel);
  186. // write local .pearrc
  187. $this->config->writeConfigFile();
  188. return $true;
  189. }
  190. // }}}
  191. // {{{ doClearCache
  192. /**
  193. * do clear-cache
  194. *
  195. * @return true|Ethna_Error
  196. */
  197. public function doClearCache()
  198. {
  199. $true = true;
  200. $r = $this->_run('clear-cache', array(), array());
  201. if (PEAR::isError($r)) {
  202. return $r;
  203. }
  204. return $true;
  205. }
  206. // }}}
  207. // {{{ doChannelDiscover
  208. /**
  209. * do channel-discover
  210. *
  211. * @return true|Ethna_Error
  212. */
  213. public function doChannelDiscover()
  214. {
  215. $true = true;
  216. $r = $this->_run('channel-discover', array(), array($this->channel));
  217. if (PEAR::isError($r)) {
  218. return $r;
  219. }
  220. return $true;
  221. }
  222. // }}}
  223. // {{{ isChannelExists
  224. /**
  225. * whether channel discovered or not
  226. *
  227. * @return bool
  228. */
  229. public function isChannelExists()
  230. {
  231. return $this->registry->channelExists($this->channel);
  232. }
  233. // }}}
  234. // {{{ doChannelUpdate
  235. /**
  236. * do channel-update
  237. *
  238. * @return true|Ethna_Error
  239. */
  240. public function doChannelUpdate()
  241. {
  242. $true = true;
  243. if ($this->isChannelExists() == false) {
  244. $r = $this->doChannelDiscover();
  245. if (PEAR::isError($r)) {
  246. return $r;
  247. }
  248. }
  249. $r = $this->_run('channel-update', array(), array($this->channel));
  250. if (PEAR::isError($r)) {
  251. return $r;
  252. }
  253. return $true;
  254. }
  255. // }}}
  256. // {{{ _doInstallOrUpgrade
  257. /**
  258. * do install
  259. *
  260. * @param string $command 'install' or 'upgrade'
  261. * @param string $package package string
  262. * @return true|Ethna_Error
  263. * @access private
  264. */
  265. private function _doInstallOrUpgrade($command, $package)
  266. {
  267. $true = true;
  268. $r = $this->_run($command, array(), array($package));
  269. if (PEAR::isError($r)) {
  270. return $r;
  271. }
  272. return $true;
  273. }
  274. // }}}
  275. // {{{ doInstall
  276. /**
  277. * do install
  278. *
  279. * @param string $pkg_name package name.
  280. * @param string $state package state.
  281. * @return true|Ethna_Error
  282. */
  283. public function doInstall($pkg_name, $state = null)
  284. {
  285. $pkg = "{$this->channel}/{$pkg_name}";
  286. if ($state !== null) {
  287. $pkg = "{$pkg}-{$state}";
  288. }
  289. $r = $this->_doInstallOrUpgrade('install', $pkg);
  290. return $r;
  291. }
  292. // }}}
  293. // {{{ doInstallFromTgz
  294. /**
  295. * do install from local tgz file
  296. *
  297. * @param string $pkg_file package filename
  298. * @return true|Ethna_Error
  299. */
  300. public function doInstallFromTgz($pkg_file)
  301. {
  302. $r = $this->_doInstallOrUpgrade('install', $pkg_file);
  303. return $r;
  304. }
  305. // }}}
  306. // {{{ doUpgrade
  307. /**
  308. * do upgrade
  309. *
  310. * @param string $pkg_name package name.
  311. * @param string $state package state.
  312. * @return true|Ethna_Error
  313. */
  314. public function doUpgrade($pkg_name, $state = null)
  315. {
  316. $pkg = "{$this->channel}/{$pkg_name}";
  317. if ($state !== null) {
  318. $pkg = "{$pkg}-{$state}";
  319. }
  320. $r = $this->_doInstallOrUpgrade('upgrade', $pkg);
  321. return $r;
  322. }
  323. // }}}
  324. // {{{ doUpgradeFromTgz
  325. /**
  326. * do upgrade from local tgz file
  327. *
  328. * @param string $pkg_file package filename
  329. * @return true|Ethna_Error
  330. */
  331. public function doUpgradeFromTgz($pkg_file)
  332. {
  333. $r = $this->_doInstallOrUpgrade('upgrade', $pkg_file);
  334. return $r;
  335. }
  336. // }}}
  337. // {{{ isInstalled
  338. /**
  339. * check package installed
  340. *
  341. * @param string $package package name
  342. * @return bool
  343. */
  344. public function isInstalled($package)
  345. {
  346. return $this->registry->packageExists($package, $this->channel);
  347. }
  348. // }}}
  349. // {{{ getVersion
  350. /**
  351. * get package version
  352. *
  353. * @param string $package package name
  354. * @return string version string
  355. */
  356. public function getVersion($package)
  357. {
  358. $pobj = $this->registry->getPackage($package, $this->channel);
  359. return $pobj->getVersion();
  360. }
  361. // }}}
  362. // {{{ getState
  363. /**
  364. * get package version
  365. *
  366. * @param string $package package name
  367. * @return string version string
  368. */
  369. public function getState($package)
  370. {
  371. $pobj = $this->registry->getPackage($package, $this->channel);
  372. return $pobj->getState();
  373. }
  374. // }}}
  375. // {{{ doUninstall
  376. /**
  377. * do uninstall (packages installed with ethna command)
  378. *
  379. * @return true|Ethna_Error
  380. */
  381. public function doUninstall($package)
  382. {
  383. $true = true;
  384. if ($this->isInstalled($package) == false) {
  385. return Ethna::raiseNotice("{$this->channel}/{$package} is not installed.");
  386. }
  387. $r = $this->_run('uninstall', array(), array("{$this->channel}/{$package}"));
  388. if (PEAR::isError($r)) {
  389. return $r;
  390. }
  391. if ($this->isInstalled($package)) {
  392. return Ethna::raiseNotice("uninstall failed: {$this->channel}/{$package}");
  393. }
  394. return $true;
  395. }
  396. // }}}
  397. // {{{ getPackageNameFromTgz
  398. /**
  399. * get package info from tar/tgz file.
  400. *
  401. * @param string $filename package file name.
  402. * @return string package name
  403. * @access public
  404. * @static
  405. */
  406. public function getPackageNameFromTgz($filename)
  407. {
  408. $config = PEAR_Config::singleton();
  409. $packagefile = new PEAR_PackageFile($config);
  410. $info = $packagefile->fromTgzFile($filename, PEAR_VALIDATE_NORMAL);
  411. if (PEAR::isError($info)) {
  412. return $info;
  413. }
  414. $info_array = $info->toArray();
  415. return $info_array['name'];
  416. }
  417. // }}}
  418. // {{{ getCanonicalPackageName
  419. /**
  420. * get canonical package name (case sensitive)
  421. *
  422. * @param string $package package name.
  423. * @return string canonical name
  424. * @access public
  425. */
  426. public function getCanonicalPackageName($package)
  427. {
  428. if ($this->isInstalled($package) == false) {
  429. return Ethna::raiseNotice("{$this->channel}/{$package} is not installed.");
  430. }
  431. $pobj = $this->registry->getPackage($package, $this->channel);
  432. $cname = $pobj->getName();
  433. return $cname;
  434. }
  435. // }}}
  436. // {{{ getInstalledPackageList
  437. /**
  438. * get installed package list
  439. *
  440. * @return array installed package list
  441. * @access public
  442. */
  443. public function getInstalledPackageList()
  444. {
  445. $ret = array();
  446. foreach ($this->registry->listPackages($this->channel) as $pkg) {
  447. $ret[] = $this->getCanonicalPackageName($pkg);
  448. }
  449. return $ret;
  450. }
  451. // }}}
  452. // {{{ doInfo
  453. /**
  454. * do info (packages installed with ethna command)
  455. *
  456. * @param string $package package name.
  457. * @return true|Ethna_Error
  458. */
  459. public function doInfo($package)
  460. {
  461. return $this->_run('info', array(), array("{$this->channel}/{$package}"));
  462. }
  463. // }}}
  464. // {{{ doRemoteInfo
  465. /**
  466. * do info (packages installable with ethna command)
  467. *
  468. * @param string $package package name.
  469. * @return true|Ethna_Error
  470. */
  471. public function doRemoteInfo($package)
  472. {
  473. return $this->_run('remote-info', array(), array("{$this->channel}/{$package}"));
  474. }
  475. // }}}
  476. // {{{ doUpgradeAll
  477. /**
  478. * do upgrade-all
  479. *
  480. * @return true|Ethna_Error
  481. */
  482. public function doUpgradeAll()
  483. {
  484. return $this->_run('upgrade-all', array('channel' => "{$this->channel}"), array());
  485. }
  486. // }}}
  487. // {{{ doList
  488. /**
  489. * do list (packages installed with ethna command)
  490. *
  491. * @return true|Ethna_Error
  492. */
  493. public function doList()
  494. {
  495. return $this->_run('list', array('channel' => $this->channel), array());
  496. }
  497. // }}}
  498. // {{{ doRemoteList
  499. /**
  500. * do remote-list (packages installable with ethna command)
  501. *
  502. * @return true|Ethna_Error
  503. */
  504. public function doRemoteList()
  505. {
  506. return $this->_run('remote-list', array('channel' => $this->channel), array());
  507. }
  508. // }}}
  509. // {{{ subroutines.
  510. /**
  511. * run PEAR_Command.
  512. *
  513. * @param string $command command name
  514. * @param array $options options
  515. * @param array $params parameters
  516. * @return true|Ethna_Error
  517. * @access private
  518. * @see PEAR_Command_Common::run, etc.
  519. */
  520. protected function _run($command, $options, $params)
  521. {
  522. if ($this->config === null) {
  523. return Ethna::raiseError('configuration not initialized.');
  524. }
  525. $true = true;
  526. $cmd = PEAR_Command::factory($command, $this->config);
  527. if (PEAR::isError($cmd)) {
  528. return $cmd;
  529. }
  530. // pear command options
  531. if (is_array($this->_pearopt) && count($this->_pearopt) > 0) {
  532. $pearopts = $this->_getPearOpt($cmd, $command, $this->_pearopt);
  533. $options = array_merge($pearopts, $options);
  534. }
  535. $ret = $cmd->run($command, $options, $params);
  536. if (PEAR::isError($ret)) {
  537. return $ret;
  538. }
  539. return $true;
  540. }
  541. /**
  542. * provide yes-or-no dialog.
  543. *
  544. * @return bool
  545. * @access public
  546. */
  547. public function confirmDialog($message, $default = 'yes')
  548. {
  549. $ret = $this->ui->userConfirm($message);
  550. return $ret;
  551. }
  552. /**
  553. * provide table layout
  554. *
  555. * @param array $headline headline
  556. * @param array $rows rows which have the same size as headline's.
  557. * @access public
  558. */
  559. public function displayTable($caption, $headline, $rows)
  560. {
  561. // spacing
  562. foreach (array_keys($headline) as $k) {
  563. $headline[$k] = sprintf('% -8s', $headline[$k]);
  564. }
  565. $data = array('caption' => $caption,
  566. 'border' => true,
  567. 'headline' => $headline,
  568. 'data' => $rows);
  569. $this->ui->outputData($data);
  570. }
  571. /**
  572. * (experimental)
  573. * @access public
  574. */
  575. public function setPearOpt($pearopt)
  576. {
  577. $this->_pearopt = $pearopt;
  578. }
  579. /**
  580. * (experimental)
  581. * @return array
  582. */
  583. private function _getPearOpt($cmd_obj, $cmd_str, $opt_array)
  584. {
  585. $short_args = $long_args = null;
  586. PEAR_Command::getGetOptArgs($cmd_str, $short_args, $long_args);
  587. $opt = new Ethna_Getopt();
  588. $opt_arg = $opt->getopt($opt_array, $short_args, $long_args);
  589. if (Ethna::isError($opt_arg)) return array();
  590. $opts = array();
  591. foreach ($opt_arg[0] as $tmp) {
  592. list($opt, $val) = $tmp;
  593. if ($val === null) $val = true;
  594. if (strlen($opt) == 1) {
  595. $cmd_opts = $cmd_obj->getOptions($cmd_str);
  596. foreach ($cmd_opts as $o => $d) {
  597. if (isset($d['shortopt']) && $d['shortopt'] == $opt) {
  598. $opts[$o] = $val;
  599. }
  600. }
  601. } else {
  602. if (substr($opt, 0, 2) == '--') $opts[substr($opt, 2)] = $val;
  603. }
  604. }
  605. return $opts;
  606. }
  607. // }}}
  608. }
  609. // }}}