PageRenderTime 43ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

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

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