PageRenderTime 57ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 1ms

/vendor/psy/psysh/src/Psy/Configuration.php

https://gitlab.com/PragmaticLinux/Laravel
PHP | 999 lines | 486 code | 104 blank | 409 comment | 57 complexity | c321465efcc6562fcef364775dcc1b56 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of Psy Shell
  4. *
  5. * (c) 2012-2014 Justin Hileman
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Psy;
  11. use Psy\Exception\RuntimeException;
  12. use Psy\ExecutionLoop\ForkingLoop;
  13. use Psy\ExecutionLoop\Loop;
  14. use Psy\Output\OutputPager;
  15. use Psy\Output\ShellOutput;
  16. use Psy\Presenter\PresenterManager;
  17. use Psy\Readline\GNUReadline;
  18. use Psy\Readline\Libedit;
  19. use Psy\Readline\Readline;
  20. use Psy\Readline\Transient;
  21. use Psy\TabCompletion\AutoCompleter;
  22. use XdgBaseDir\Xdg;
  23. /**
  24. * The Psy Shell configuration.
  25. */
  26. class Configuration
  27. {
  28. private static $AVAILABLE_OPTIONS = array(
  29. 'defaultIncludes', 'useReadline', 'usePcntl', 'codeCleaner', 'pager',
  30. 'loop', 'configDir', 'dataDir', 'runtimeDir', 'manualDbFile',
  31. 'requireSemicolons', 'historySize', 'eraseDuplicates', 'tabCompletion',
  32. 'tabCompletionMatchers',
  33. );
  34. private $defaultIncludes;
  35. private $configDir;
  36. private $dataDir;
  37. private $runtimeDir;
  38. private $configFile;
  39. private $historyFile;
  40. private $historySize;
  41. private $eraseDuplicates;
  42. private $manualDbFile;
  43. private $hasReadline;
  44. private $useReadline;
  45. private $hasPcntl;
  46. private $usePcntl;
  47. private $newCommands = array();
  48. private $requireSemicolons = false;
  49. private $tabCompletion;
  50. private $tabCompletionMatchers = array();
  51. // services
  52. private $readline;
  53. private $output;
  54. private $shell;
  55. private $cleaner;
  56. private $pager;
  57. private $loop;
  58. private $manualDb;
  59. private $presenters;
  60. private $completer;
  61. /**
  62. * Construct a Configuration instance.
  63. *
  64. * Optionally, supply an array of configuration values to load.
  65. *
  66. * @param array $config Optional array of configuration values.
  67. */
  68. public function __construct(array $config = array())
  69. {
  70. // explicit configFile option
  71. if (isset($config['configFile'])) {
  72. $this->configFile = $config['configFile'];
  73. } elseif ($configFile = getenv('PSYSH_CONFIG')) {
  74. $this->configFile = $configFile;
  75. }
  76. // legacy baseDir option
  77. if (isset($config['baseDir'])) {
  78. $msg = "The 'baseDir' configuration option is deprecated. " .
  79. "Please specify 'configDir' and 'dataDir' options instead.";
  80. trigger_error($msg, E_USER_DEPRECATED);
  81. $this->setConfigDir($config['baseDir']);
  82. $this->setDataDir($config['baseDir']);
  83. }
  84. unset($config['configFile'], $config['baseDir']);
  85. // go go gadget, config!
  86. $this->loadConfig($config);
  87. $this->init();
  88. }
  89. /**
  90. * Initialize the configuration.
  91. *
  92. * This checks for the presence of Readline and Pcntl extensions.
  93. *
  94. * If a config file is available, it will be loaded and merged with the current config.
  95. */
  96. public function init()
  97. {
  98. // feature detection
  99. $this->hasReadline = function_exists('readline');
  100. $this->hasPcntl = function_exists('pcntl_signal') && function_exists('posix_getpid');
  101. if ($configFile = $this->getConfigFile()) {
  102. $this->loadConfigFile($configFile);
  103. }
  104. }
  105. /**
  106. * Get the current PsySH config file.
  107. *
  108. * If a `configFile` option was passed to the Configuration constructor,
  109. * this file will be returned. If not, all possible config directories will
  110. * be searched, and the first `config.php` or `rc.php` file which exists
  111. * will be returned.
  112. *
  113. * If you're trying to decide where to put your config file, pick
  114. *
  115. * ~/.config/psysh/config.php
  116. *
  117. * @return string
  118. */
  119. public function getConfigFile()
  120. {
  121. if (isset($this->configFile)) {
  122. return $this->configFile;
  123. }
  124. foreach ($this->getConfigDirs() as $dir) {
  125. $file = $dir . '/config.php';
  126. if (@is_file($file)) {
  127. return $this->configFile = $file;
  128. }
  129. $file = $dir . '/rc.php';
  130. if (@is_file($file)) {
  131. return $this->configFile = $file;
  132. }
  133. }
  134. }
  135. /**
  136. * Helper function to get the proper home directory.
  137. *
  138. * @return string
  139. */
  140. private function getPsyHome()
  141. {
  142. if ($home = getenv('HOME')) {
  143. return $home . '/.psysh';
  144. }
  145. if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
  146. // Check the old default
  147. $oldHome = strtr(getenv('HOMEDRIVE') . '/' . getenv('HOMEPATH') . '/.psysh', '\\', '/');
  148. if ($appData = getenv('APPDATA')) {
  149. $home = strtr($appData, '\\', '/') . '/PsySH';
  150. if (is_dir($oldHome) && !is_dir($home)) {
  151. $msg = sprintf(
  152. "Config directory found at '%s'. Please move it to '%s'.",
  153. strtr($oldHome, '/', '\\'),
  154. strtr($home, '/', '\\')
  155. );
  156. trigger_error($msg, E_USER_DEPRECATED);
  157. return $oldHome;
  158. }
  159. return $home;
  160. }
  161. }
  162. }
  163. /**
  164. * Get potential config directory paths.
  165. *
  166. * If a `configDir` option was explicitly set, returns an array containing
  167. * just that directory.
  168. *
  169. * Otherwise, it returns `~/.psysh` and all XDG Base Directory config directories:
  170. *
  171. * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
  172. *
  173. * @return string[]
  174. */
  175. protected function getConfigDirs()
  176. {
  177. if (isset($this->configDir)) {
  178. return array($this->configDir);
  179. }
  180. $xdg = new Xdg();
  181. $dirs = array_map(function ($dir) {
  182. return $dir . '/psysh';
  183. }, $xdg->getConfigDirs());
  184. if ($home = $this->getPsyHome()) {
  185. array_unshift($dirs, $home);
  186. }
  187. return $dirs;
  188. }
  189. /**
  190. * Get potential data directory paths.
  191. *
  192. * If a `dataDir` option was explicitly set, returns an array containing
  193. * just that directory.
  194. *
  195. * Otherwise, it returns `~/.psysh` and all XDG Base Directory data directories:
  196. *
  197. * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
  198. *
  199. * @return string[]
  200. */
  201. protected function getDataDirs()
  202. {
  203. if (isset($this->dataDir)) {
  204. return array($this->dataDir);
  205. }
  206. $xdg = new Xdg();
  207. $dirs = array_map(function ($dir) {
  208. return $dir . '/psysh';
  209. }, $xdg->getDataDirs());
  210. if ($home = $this->getPsyHome()) {
  211. array_unshift($dirs, $home);
  212. }
  213. return $dirs;
  214. }
  215. /**
  216. * Load configuration values from an array of options.
  217. *
  218. * @param array $options
  219. */
  220. public function loadConfig(array $options)
  221. {
  222. foreach (self::$AVAILABLE_OPTIONS as $option) {
  223. if (isset($options[$option])) {
  224. $method = 'set' . ucfirst($option);
  225. $this->$method($options[$option]);
  226. }
  227. }
  228. foreach (array('commands', 'tabCompletionMatchers', 'presenters') as $option) {
  229. if (isset($options[$option])) {
  230. $method = 'add' . ucfirst($option);
  231. $this->$method($options[$option]);
  232. }
  233. }
  234. }
  235. /**
  236. * Load a configuration file (default: `$HOME/.config/psysh/config.php`).
  237. *
  238. * This configuration instance will be available to the config file as $config.
  239. * The config file may directly manipulate the configuration, or may return
  240. * an array of options which will be merged with the current configuration.
  241. *
  242. * @throws \InvalidArgumentException if the config file returns a non-array result.
  243. *
  244. * @param string $file
  245. */
  246. public function loadConfigFile($file)
  247. {
  248. $__psysh_config_file__ = $file;
  249. $load = function ($config) use ($__psysh_config_file__) {
  250. $result = require $__psysh_config_file__;
  251. if ($result !== 1) {
  252. return $result;
  253. }
  254. };
  255. $result = $load($this);
  256. if (!empty($result)) {
  257. if (is_array($result)) {
  258. $this->loadConfig($result);
  259. } else {
  260. throw new \InvalidArgumentException('Psy Shell configuration must return an array of options');
  261. }
  262. }
  263. }
  264. /**
  265. * Set files to be included by default at the start of each shell session.
  266. *
  267. * @param array $includes
  268. */
  269. public function setDefaultIncludes(array $includes = array())
  270. {
  271. $this->defaultIncludes = $includes;
  272. }
  273. /**
  274. * Get files to be included by default at the start of each shell session.
  275. *
  276. * @return array
  277. */
  278. public function getDefaultIncludes()
  279. {
  280. return $this->defaultIncludes ?: array();
  281. }
  282. /**
  283. * Set the shell's config directory location.
  284. *
  285. * @param string $dir
  286. */
  287. public function setConfigDir($dir)
  288. {
  289. $this->configDir = (string) $dir;
  290. }
  291. /**
  292. * Get the current configuration directory, if any is explicitly set.
  293. *
  294. * @return string
  295. */
  296. public function getConfigDir()
  297. {
  298. return $this->configDir;
  299. }
  300. /**
  301. * Set the shell's data directory location.
  302. *
  303. * @param string $dir
  304. */
  305. public function setDataDir($dir)
  306. {
  307. $this->dataDir = (string) $dir;
  308. }
  309. /**
  310. * Get the current data directory, if any is explicitly set.
  311. *
  312. * @return string
  313. */
  314. public function getDataDir()
  315. {
  316. return $this->dataDir;
  317. }
  318. /**
  319. * Set the shell's temporary directory location.
  320. *
  321. * @param string $dir
  322. */
  323. public function setRuntimeDir($dir)
  324. {
  325. $this->runtimeDir = (string) $dir;
  326. }
  327. /**
  328. * Get the shell's temporary directory location.
  329. *
  330. * Defaults to `/psysh` inside the system's temp dir unless explicitly
  331. * overridden.
  332. *
  333. * @return string
  334. */
  335. public function getRuntimeDir()
  336. {
  337. if (!isset($this->runtimeDir)) {
  338. $xdg = new Xdg();
  339. $this->runtimeDir = $xdg->getRuntimeDir() . '/psysh';
  340. }
  341. if (!is_dir($this->runtimeDir)) {
  342. mkdir($this->runtimeDir, 0700, true);
  343. }
  344. return $this->runtimeDir;
  345. }
  346. /**
  347. * @deprecated Use setRuntimeDir() instead.
  348. *
  349. * @param string $dir
  350. */
  351. public function setTempDir($dir)
  352. {
  353. trigger_error("'setTempDir' is deprecated. Use 'setRuntimeDir' instead.", E_USER_DEPRECATED);
  354. return $this->setRuntimeDir($dir);
  355. }
  356. /**
  357. * @deprecated Use getRuntimeDir() instead.
  358. *
  359. * @return string
  360. */
  361. public function getTempDir()
  362. {
  363. trigger_error("'getTempDir' is deprecated. Use 'getRuntimeDir' instead.", E_USER_DEPRECATED);
  364. return $this->getRuntimeDir();
  365. }
  366. /**
  367. * Set the readline history file path.
  368. *
  369. * @param string $file
  370. */
  371. public function setHistoryFile($file)
  372. {
  373. $this->historyFile = (string) $file;
  374. }
  375. /**
  376. * Get the readline history file path.
  377. *
  378. * Defaults to `/history` inside the shell's base config dir unless
  379. * explicitly overridden.
  380. *
  381. * @return string
  382. */
  383. public function getHistoryFile()
  384. {
  385. if (isset($this->historyFile)) {
  386. return $this->historyFile;
  387. }
  388. foreach ($this->getConfigDirs() as $dir) {
  389. $file = $dir . '/psysh_history';
  390. if (@is_file($file)) {
  391. return $this->historyFile = $file;
  392. }
  393. $file = $dir . '/history';
  394. if (@is_file($file)) {
  395. return $this->historyFile = $file;
  396. }
  397. }
  398. // fallback: create our own
  399. if (isset($this->configDir)) {
  400. $dir = $this->configDir;
  401. } else {
  402. $xdg = new Xdg();
  403. $dir = $xdg->getHomeConfigDir();
  404. }
  405. if (!is_dir($dir)) {
  406. mkdir($dir, 0700, true);
  407. }
  408. $file = $dir . '/psysh_history';
  409. return $this->historyFile = $file;
  410. }
  411. /**
  412. * Set the readline max history size.
  413. *
  414. * @param int $value
  415. */
  416. public function setHistorySize($value)
  417. {
  418. $this->historySize = (int) $value;
  419. }
  420. /**
  421. * Get the readline max history size.
  422. *
  423. * @return int
  424. */
  425. public function getHistorySize()
  426. {
  427. return $this->historySize;
  428. }
  429. /**
  430. * Sets whether readline erases old duplicate history entries.
  431. *
  432. * @param bool $value
  433. */
  434. public function setEraseDuplicates($value)
  435. {
  436. $this->eraseDuplicates = (bool) $value;
  437. }
  438. /**
  439. * Get whether readline erases old duplicate history entries.
  440. *
  441. * @return bool
  442. */
  443. public function getEraseDuplicates()
  444. {
  445. return $this->eraseDuplicates;
  446. }
  447. /**
  448. * Get a temporary file of type $type for process $pid.
  449. *
  450. * The file will be created inside the current temporary directory.
  451. *
  452. * @see self::getRuntimeDir
  453. *
  454. * @param string $type
  455. * @param int $pid
  456. *
  457. * @return string Temporary file name
  458. */
  459. public function getTempFile($type, $pid)
  460. {
  461. return tempnam($this->getRuntimeDir(), $type . '_' . $pid . '_');
  462. }
  463. /**
  464. * Get a filename suitable for a FIFO pipe of $type for process $pid.
  465. *
  466. * The pipe will be created inside the current temporary directory.
  467. *
  468. * @param string $type
  469. * @param id $pid
  470. *
  471. * @return string Pipe name
  472. */
  473. public function getPipe($type, $pid)
  474. {
  475. return sprintf('%s/%s_%s', $this->getRuntimeDir(), $type, $pid);
  476. }
  477. /**
  478. * Check whether this PHP instance has Readline available.
  479. *
  480. * @return bool True if Readline is available.
  481. */
  482. public function hasReadline()
  483. {
  484. return $this->hasReadline;
  485. }
  486. /**
  487. * Enable or disable Readline usage.
  488. *
  489. * @param bool $useReadline
  490. */
  491. public function setUseReadline($useReadline)
  492. {
  493. $this->useReadline = (bool) $useReadline;
  494. }
  495. /**
  496. * Check whether to use Readline.
  497. *
  498. * If `setUseReadline` as been set to true, but Readline is not actually
  499. * available, this will return false.
  500. *
  501. * @return bool True if the current Shell should use Readline.
  502. */
  503. public function useReadline()
  504. {
  505. return isset($this->useReadline) ? ($this->hasReadline && $this->useReadline) : $this->hasReadline;
  506. }
  507. /**
  508. * Set the Psy Shell readline service.
  509. *
  510. * @param Readline $readline
  511. */
  512. public function setReadline(Readline $readline)
  513. {
  514. $this->readline = $readline;
  515. }
  516. /**
  517. * Get the Psy Shell readline service.
  518. *
  519. * By default, this service uses (in order of preference):
  520. *
  521. * * GNU Readline
  522. * * Libedit
  523. * * A transient array-based readline emulation.
  524. *
  525. * @return Readline
  526. */
  527. public function getReadline()
  528. {
  529. if (!isset($this->readline)) {
  530. $className = $this->getReadlineClass();
  531. $this->readline = new $className(
  532. $this->getHistoryFile(),
  533. $this->getHistorySize(),
  534. $this->getEraseDuplicates()
  535. );
  536. }
  537. return $this->readline;
  538. }
  539. /**
  540. * Get the appropriate Readline implementation class name.
  541. *
  542. * @see self::getReadline
  543. *
  544. * @return string
  545. */
  546. private function getReadlineClass()
  547. {
  548. if ($this->useReadline()) {
  549. if (GNUReadline::isSupported()) {
  550. return 'Psy\Readline\GNUReadline';
  551. } elseif (Libedit::isSupported()) {
  552. return 'Psy\Readline\Libedit';
  553. }
  554. }
  555. return 'Psy\Readline\Transient';
  556. }
  557. /**
  558. * Check whether this PHP instance has Pcntl available.
  559. *
  560. * @return bool True if Pcntl is available.
  561. */
  562. public function hasPcntl()
  563. {
  564. return $this->hasPcntl;
  565. }
  566. /**
  567. * Enable or disable Pcntl usage.
  568. *
  569. * @param bool $usePcntl
  570. */
  571. public function setUsePcntl($usePcntl)
  572. {
  573. $this->usePcntl = (bool) $usePcntl;
  574. }
  575. /**
  576. * Check whether to use Pcntl.
  577. *
  578. * If `setUsePcntl` has been set to true, but Pcntl is not actually
  579. * available, this will return false.
  580. *
  581. * @return bool True if the current Shell should use Pcntl.
  582. */
  583. public function usePcntl()
  584. {
  585. return isset($this->usePcntl) ? ($this->hasPcntl && $this->usePcntl) : $this->hasPcntl;
  586. }
  587. /**
  588. * Enable or disable strict requirement of semicolons.
  589. *
  590. * @see self::requireSemicolons()
  591. *
  592. * @param bool $requireSemicolons
  593. */
  594. public function setRequireSemicolons($requireSemicolons)
  595. {
  596. $this->requireSemicolons = (bool) $requireSemicolons;
  597. }
  598. /**
  599. * Check whether to require semicolons on all statements.
  600. *
  601. * By default, PsySH will automatically insert semicolons at the end of
  602. * statements if they're missing. To strictly require semicolons, set
  603. * `requireSemicolons` to true.
  604. *
  605. * @return bool
  606. */
  607. public function requireSemicolons()
  608. {
  609. return $this->requireSemicolons;
  610. }
  611. /**
  612. * Set a CodeCleaner service instance.
  613. *
  614. * @param CodeCleaner $cleaner
  615. */
  616. public function setCodeCleaner(CodeCleaner $cleaner)
  617. {
  618. $this->cleaner = $cleaner;
  619. }
  620. /**
  621. * Get a CodeCleaner service instance.
  622. *
  623. * If none has been explicitly defined, this will create a new instance.
  624. *
  625. * @return CodeCleaner
  626. */
  627. public function getCodeCleaner()
  628. {
  629. if (!isset($this->cleaner)) {
  630. $this->cleaner = new CodeCleaner();
  631. }
  632. return $this->cleaner;
  633. }
  634. /**
  635. * Enable or disable tab completion.
  636. *
  637. * @param bool $tabCompletion
  638. */
  639. public function setTabCompletion($tabCompletion)
  640. {
  641. $this->tabCompletion = (bool) $tabCompletion;
  642. }
  643. /**
  644. * Check whether to use tab completion.
  645. *
  646. * If `setTabCompletion` has been set to true, but readline is not actually
  647. * available, this will return false.
  648. *
  649. * @return bool True if the current Shell should use tab completion.
  650. */
  651. public function getTabCompletion()
  652. {
  653. return isset($this->tabCompletion) ? ($this->hasReadline && $this->tabCompletion) : $this->hasReadline;
  654. }
  655. /**
  656. * Set the Shell Output service.
  657. *
  658. * @param ShellOutput $output
  659. */
  660. public function setOutput(ShellOutput $output)
  661. {
  662. $this->output = $output;
  663. }
  664. /**
  665. * Get a Shell Output service instance.
  666. *
  667. * If none has been explicitly provided, this will create a new instance
  668. * with VERBOSITY_NORMAL and the output page supplied by self::getPager
  669. *
  670. * @see self::getPager
  671. *
  672. * @return ShellOutput
  673. */
  674. public function getOutput()
  675. {
  676. if (!isset($this->output)) {
  677. $this->output = new ShellOutput(ShellOutput::VERBOSITY_NORMAL, null, null, $this->getPager());
  678. }
  679. return $this->output;
  680. }
  681. /**
  682. * Set the OutputPager service.
  683. *
  684. * If a string is supplied, a ProcOutputPager will be used which shells out
  685. * to the specified command.
  686. *
  687. * @throws \InvalidArgumentException if $pager is not a string or OutputPager instance.
  688. *
  689. * @param string|OutputPager $pager
  690. */
  691. public function setPager($pager)
  692. {
  693. if ($pager && !is_string($pager) && !$pager instanceof OutputPager) {
  694. throw new \InvalidArgumentException('Unexpected pager instance.');
  695. }
  696. $this->pager = $pager;
  697. }
  698. /**
  699. * Get an OutputPager instance or a command for an external Proc pager.
  700. *
  701. * If no Pager has been explicitly provided, and Pcntl is available, this
  702. * will default to `cli.pager` ini value, falling back to `which less`.
  703. *
  704. * @return string|OutputPager
  705. */
  706. public function getPager()
  707. {
  708. if (!isset($this->pager) && $this->usePcntl()) {
  709. if ($pager = ini_get('cli.pager')) {
  710. // use the default pager (5.4+)
  711. $this->pager = $pager;
  712. } elseif ($less = exec('which less 2>/dev/null')) {
  713. // check for the presence of less...
  714. $this->pager = $less . ' -R -S -F -X';
  715. }
  716. }
  717. return $this->pager;
  718. }
  719. /**
  720. * Set the Shell evaluation Loop service.
  721. *
  722. * @param Loop $loop
  723. */
  724. public function setLoop(Loop $loop)
  725. {
  726. $this->loop = $loop;
  727. }
  728. /**
  729. * Get a Shell evaluation Loop service instance.
  730. *
  731. * If none has been explicitly defined, this will create a new instance.
  732. * If Pcntl is available and enabled, the new instance will be a ForkingLoop.
  733. *
  734. * @return Loop
  735. */
  736. public function getLoop()
  737. {
  738. if (!isset($this->loop)) {
  739. if ($this->usePcntl()) {
  740. $this->loop = new ForkingLoop($this);
  741. } else {
  742. $this->loop = new Loop($this);
  743. }
  744. }
  745. return $this->loop;
  746. }
  747. /**
  748. * Get an AutoCompleter service instance.
  749. *
  750. * @return AutoCompleter
  751. */
  752. public function getAutoCompleter()
  753. {
  754. if (!isset($this->completer)) {
  755. $this->completer = new AutoCompleter();
  756. }
  757. return $this->completer;
  758. }
  759. /**
  760. * Get user specified tab completion matchers for the AutoCompleter.
  761. *
  762. * @return array
  763. */
  764. public function getTabCompletionMatchers()
  765. {
  766. return $this->tabCompletionMatchers;
  767. }
  768. /**
  769. * Add additional tab completion matchers to the AutoCompleter.
  770. *
  771. * @param array $matchers
  772. */
  773. public function addTabCompletionMatchers(array $matchers)
  774. {
  775. $this->tabCompletionMatchers = array_merge($this->tabCompletionMatchers, $matchers);
  776. if (isset($this->shell)) {
  777. $this->shell->addTabCompletionMatchers($this->tabCompletionMatchers);
  778. }
  779. }
  780. /**
  781. * Add commands to the Shell.
  782. *
  783. * This will buffer new commands in the event that the Shell has not yet
  784. * been instantiated. This allows the user to specify commands in their
  785. * config rc file, despite the fact that their file is needed in the Shell
  786. * constructor.
  787. *
  788. * @param array $commands
  789. */
  790. public function addCommands(array $commands)
  791. {
  792. $this->newCommands = array_merge($this->newCommands, $commands);
  793. if (isset($this->shell)) {
  794. $this->doAddCommands();
  795. }
  796. }
  797. /**
  798. * Internal method for adding commands. This will set any new commands once
  799. * a Shell is available.
  800. */
  801. private function doAddCommands()
  802. {
  803. if (!empty($this->newCommands)) {
  804. $this->shell->addCommands($this->newCommands);
  805. $this->newCommands = array();
  806. }
  807. }
  808. /**
  809. * Set the Shell backreference and add any new commands to the Shell.
  810. *
  811. * @param Shell $shell
  812. */
  813. public function setShell(Shell $shell)
  814. {
  815. $this->shell = $shell;
  816. $this->doAddCommands();
  817. }
  818. /**
  819. * Set the PHP manual database file.
  820. *
  821. * This file should be an SQLite database generated from the phpdoc source
  822. * with the `bin/build_manual` script.
  823. *
  824. * @param string $filename
  825. */
  826. public function setManualDbFile($filename)
  827. {
  828. $this->manualDbFile = (string) $filename;
  829. }
  830. /**
  831. * Get the current PHP manual database file.
  832. *
  833. * @return string Default: '~/.local/share/psysh/php_manual.sqlite'
  834. */
  835. public function getManualDbFile()
  836. {
  837. if (isset($this->manualDbFile)) {
  838. return $this->manualDbFile;
  839. }
  840. foreach ($this->getDataDirs() as $dir) {
  841. $file = $dir . '/php_manual.sqlite';
  842. if (@is_file($file)) {
  843. return $this->manualDbFile = $file;
  844. }
  845. }
  846. }
  847. /**
  848. * Get a PHP manual database connection.
  849. *
  850. * @return PDO
  851. */
  852. public function getManualDb()
  853. {
  854. if (!isset($this->manualDb)) {
  855. $dbFile = $this->getManualDbFile();
  856. if (is_file($dbFile)) {
  857. try {
  858. $this->manualDb = new \PDO('sqlite:' . $dbFile);
  859. } catch (\PDOException $e) {
  860. if ($e->getMessage() === 'could not find driver') {
  861. throw new RuntimeException('SQLite PDO driver not found', 0, $e);
  862. } else {
  863. throw $e;
  864. }
  865. }
  866. }
  867. }
  868. return $this->manualDb;
  869. }
  870. /**
  871. * Add an array of Presenters.
  872. *
  873. * @param array $presenters
  874. */
  875. public function addPresenters(array $presenters)
  876. {
  877. $manager = $this->getPresenterManager();
  878. foreach ($presenters as $presenter) {
  879. $manager->addPresenter($presenter);
  880. }
  881. }
  882. /**
  883. * Get the PresenterManager service.
  884. *
  885. * @return PresenterManager
  886. */
  887. public function getPresenterManager()
  888. {
  889. if (!isset($this->presenters)) {
  890. $this->presenters = new PresenterManager();
  891. }
  892. return $this->presenters;
  893. }
  894. }