Mercurial > hg > cmmr2012-drupal-site
comparison vendor/psy/psysh/src/Configuration.php @ 0:c75dbcec494b
Initial commit from drush-created site
author | Chris Cannam |
---|---|
date | Thu, 05 Jul 2018 14:24:15 +0000 |
parents | |
children | a9cd425dd02b |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c75dbcec494b |
---|---|
1 <?php | |
2 | |
3 /* | |
4 * This file is part of Psy Shell. | |
5 * | |
6 * (c) 2012-2018 Justin Hileman | |
7 * | |
8 * For the full copyright and license information, please view the LICENSE | |
9 * file that was distributed with this source code. | |
10 */ | |
11 | |
12 namespace Psy; | |
13 | |
14 use Psy\Exception\DeprecatedException; | |
15 use Psy\Exception\RuntimeException; | |
16 use Psy\Output\OutputPager; | |
17 use Psy\Output\ShellOutput; | |
18 use Psy\Readline\GNUReadline; | |
19 use Psy\Readline\HoaConsole; | |
20 use Psy\Readline\Libedit; | |
21 use Psy\Readline\Readline; | |
22 use Psy\Readline\Transient; | |
23 use Psy\TabCompletion\AutoCompleter; | |
24 use Psy\VarDumper\Presenter; | |
25 use Psy\VersionUpdater\Checker; | |
26 use Psy\VersionUpdater\GitHubChecker; | |
27 use Psy\VersionUpdater\IntervalChecker; | |
28 use Psy\VersionUpdater\NoopChecker; | |
29 | |
30 /** | |
31 * The Psy Shell configuration. | |
32 */ | |
33 class Configuration | |
34 { | |
35 const COLOR_MODE_AUTO = 'auto'; | |
36 const COLOR_MODE_FORCED = 'forced'; | |
37 const COLOR_MODE_DISABLED = 'disabled'; | |
38 | |
39 private static $AVAILABLE_OPTIONS = [ | |
40 'codeCleaner', | |
41 'colorMode', | |
42 'configDir', | |
43 'dataDir', | |
44 'defaultIncludes', | |
45 'eraseDuplicates', | |
46 'errorLoggingLevel', | |
47 'forceArrayIndexes', | |
48 'historySize', | |
49 'manualDbFile', | |
50 'pager', | |
51 'prompt', | |
52 'requireSemicolons', | |
53 'runtimeDir', | |
54 'startupMessage', | |
55 'updateCheck', | |
56 'useBracketedPaste', | |
57 'usePcntl', | |
58 'useReadline', | |
59 'useTabCompletion', | |
60 'useUnicode', | |
61 'warnOnMultipleConfigs', | |
62 ]; | |
63 | |
64 private $defaultIncludes; | |
65 private $configDir; | |
66 private $dataDir; | |
67 private $runtimeDir; | |
68 private $configFile; | |
69 /** @var string|false */ | |
70 private $historyFile; | |
71 private $historySize; | |
72 private $eraseDuplicates; | |
73 private $manualDbFile; | |
74 private $hasReadline; | |
75 private $useReadline; | |
76 private $useBracketedPaste; | |
77 private $hasPcntl; | |
78 private $usePcntl; | |
79 private $newCommands = []; | |
80 private $requireSemicolons = false; | |
81 private $useUnicode; | |
82 private $useTabCompletion; | |
83 private $newMatchers = []; | |
84 private $errorLoggingLevel = E_ALL; | |
85 private $warnOnMultipleConfigs = false; | |
86 private $colorMode; | |
87 private $updateCheck; | |
88 private $startupMessage; | |
89 private $forceArrayIndexes = false; | |
90 | |
91 // services | |
92 private $readline; | |
93 private $output; | |
94 private $shell; | |
95 private $cleaner; | |
96 private $pager; | |
97 private $manualDb; | |
98 private $presenter; | |
99 private $autoCompleter; | |
100 private $checker; | |
101 private $prompt; | |
102 | |
103 /** | |
104 * Construct a Configuration instance. | |
105 * | |
106 * Optionally, supply an array of configuration values to load. | |
107 * | |
108 * @param array $config Optional array of configuration values | |
109 */ | |
110 public function __construct(array $config = []) | |
111 { | |
112 $this->setColorMode(self::COLOR_MODE_AUTO); | |
113 | |
114 // explicit configFile option | |
115 if (isset($config['configFile'])) { | |
116 $this->configFile = $config['configFile']; | |
117 } elseif ($configFile = getenv('PSYSH_CONFIG')) { | |
118 $this->configFile = $configFile; | |
119 } | |
120 | |
121 // legacy baseDir option | |
122 if (isset($config['baseDir'])) { | |
123 $msg = "The 'baseDir' configuration option is deprecated; " . | |
124 "please specify 'configDir' and 'dataDir' options instead"; | |
125 throw new DeprecatedException($msg); | |
126 } | |
127 | |
128 unset($config['configFile'], $config['baseDir']); | |
129 | |
130 // go go gadget, config! | |
131 $this->loadConfig($config); | |
132 $this->init(); | |
133 } | |
134 | |
135 /** | |
136 * Initialize the configuration. | |
137 * | |
138 * This checks for the presence of Readline and Pcntl extensions. | |
139 * | |
140 * If a config file is available, it will be loaded and merged with the current config. | |
141 * | |
142 * If no custom config file was specified and a local project config file | |
143 * is available, it will be loaded and merged with the current config. | |
144 */ | |
145 public function init() | |
146 { | |
147 // feature detection | |
148 $this->hasReadline = function_exists('readline'); | |
149 $this->hasPcntl = function_exists('pcntl_signal') && function_exists('posix_getpid'); | |
150 | |
151 if ($configFile = $this->getConfigFile()) { | |
152 $this->loadConfigFile($configFile); | |
153 } | |
154 | |
155 if (!$this->configFile && $localConfig = $this->getLocalConfigFile()) { | |
156 $this->loadConfigFile($localConfig); | |
157 } | |
158 } | |
159 | |
160 /** | |
161 * Get the current PsySH config file. | |
162 * | |
163 * If a `configFile` option was passed to the Configuration constructor, | |
164 * this file will be returned. If not, all possible config directories will | |
165 * be searched, and the first `config.php` or `rc.php` file which exists | |
166 * will be returned. | |
167 * | |
168 * If you're trying to decide where to put your config file, pick | |
169 * | |
170 * ~/.config/psysh/config.php | |
171 * | |
172 * @return string | |
173 */ | |
174 public function getConfigFile() | |
175 { | |
176 if (isset($this->configFile)) { | |
177 return $this->configFile; | |
178 } | |
179 | |
180 $files = ConfigPaths::getConfigFiles(['config.php', 'rc.php'], $this->configDir); | |
181 | |
182 if (!empty($files)) { | |
183 if ($this->warnOnMultipleConfigs && count($files) > 1) { | |
184 $msg = sprintf('Multiple configuration files found: %s. Using %s', implode($files, ', '), $files[0]); | |
185 trigger_error($msg, E_USER_NOTICE); | |
186 } | |
187 | |
188 return $files[0]; | |
189 } | |
190 } | |
191 | |
192 /** | |
193 * Get the local PsySH config file. | |
194 * | |
195 * Searches for a project specific config file `.psysh.php` in the current | |
196 * working directory. | |
197 * | |
198 * @return string | |
199 */ | |
200 public function getLocalConfigFile() | |
201 { | |
202 $localConfig = getcwd() . '/.psysh.php'; | |
203 | |
204 if (@is_file($localConfig)) { | |
205 return $localConfig; | |
206 } | |
207 } | |
208 | |
209 /** | |
210 * Load configuration values from an array of options. | |
211 * | |
212 * @param array $options | |
213 */ | |
214 public function loadConfig(array $options) | |
215 { | |
216 foreach (self::$AVAILABLE_OPTIONS as $option) { | |
217 if (isset($options[$option])) { | |
218 $method = 'set' . ucfirst($option); | |
219 $this->$method($options[$option]); | |
220 } | |
221 } | |
222 | |
223 // legacy `tabCompletion` option | |
224 if (isset($options['tabCompletion'])) { | |
225 $msg = '`tabCompletion` is deprecated; use `useTabCompletion` instead.'; | |
226 @trigger_error($msg, E_USER_DEPRECATED); | |
227 | |
228 $this->setUseTabCompletion($options['tabCompletion']); | |
229 } | |
230 | |
231 foreach (['commands', 'matchers', 'casters'] as $option) { | |
232 if (isset($options[$option])) { | |
233 $method = 'add' . ucfirst($option); | |
234 $this->$method($options[$option]); | |
235 } | |
236 } | |
237 | |
238 // legacy `tabCompletionMatchers` option | |
239 if (isset($options['tabCompletionMatchers'])) { | |
240 $msg = '`tabCompletionMatchers` is deprecated; use `matchers` instead.'; | |
241 @trigger_error($msg, E_USER_DEPRECATED); | |
242 | |
243 $this->addMatchers($options['tabCompletionMatchers']); | |
244 } | |
245 } | |
246 | |
247 /** | |
248 * Load a configuration file (default: `$HOME/.config/psysh/config.php`). | |
249 * | |
250 * This configuration instance will be available to the config file as $config. | |
251 * The config file may directly manipulate the configuration, or may return | |
252 * an array of options which will be merged with the current configuration. | |
253 * | |
254 * @throws \InvalidArgumentException if the config file returns a non-array result | |
255 * | |
256 * @param string $file | |
257 */ | |
258 public function loadConfigFile($file) | |
259 { | |
260 $__psysh_config_file__ = $file; | |
261 $load = function ($config) use ($__psysh_config_file__) { | |
262 $result = require $__psysh_config_file__; | |
263 if ($result !== 1) { | |
264 return $result; | |
265 } | |
266 }; | |
267 $result = $load($this); | |
268 | |
269 if (!empty($result)) { | |
270 if (is_array($result)) { | |
271 $this->loadConfig($result); | |
272 } else { | |
273 throw new \InvalidArgumentException('Psy Shell configuration must return an array of options'); | |
274 } | |
275 } | |
276 } | |
277 | |
278 /** | |
279 * Set files to be included by default at the start of each shell session. | |
280 * | |
281 * @param array $includes | |
282 */ | |
283 public function setDefaultIncludes(array $includes = []) | |
284 { | |
285 $this->defaultIncludes = $includes; | |
286 } | |
287 | |
288 /** | |
289 * Get files to be included by default at the start of each shell session. | |
290 * | |
291 * @return array | |
292 */ | |
293 public function getDefaultIncludes() | |
294 { | |
295 return $this->defaultIncludes ?: []; | |
296 } | |
297 | |
298 /** | |
299 * Set the shell's config directory location. | |
300 * | |
301 * @param string $dir | |
302 */ | |
303 public function setConfigDir($dir) | |
304 { | |
305 $this->configDir = (string) $dir; | |
306 } | |
307 | |
308 /** | |
309 * Get the current configuration directory, if any is explicitly set. | |
310 * | |
311 * @return string | |
312 */ | |
313 public function getConfigDir() | |
314 { | |
315 return $this->configDir; | |
316 } | |
317 | |
318 /** | |
319 * Set the shell's data directory location. | |
320 * | |
321 * @param string $dir | |
322 */ | |
323 public function setDataDir($dir) | |
324 { | |
325 $this->dataDir = (string) $dir; | |
326 } | |
327 | |
328 /** | |
329 * Get the current data directory, if any is explicitly set. | |
330 * | |
331 * @return string | |
332 */ | |
333 public function getDataDir() | |
334 { | |
335 return $this->dataDir; | |
336 } | |
337 | |
338 /** | |
339 * Set the shell's temporary directory location. | |
340 * | |
341 * @param string $dir | |
342 */ | |
343 public function setRuntimeDir($dir) | |
344 { | |
345 $this->runtimeDir = (string) $dir; | |
346 } | |
347 | |
348 /** | |
349 * Get the shell's temporary directory location. | |
350 * | |
351 * Defaults to `/psysh` inside the system's temp dir unless explicitly | |
352 * overridden. | |
353 * | |
354 * @return string | |
355 */ | |
356 public function getRuntimeDir() | |
357 { | |
358 if (!isset($this->runtimeDir)) { | |
359 $this->runtimeDir = ConfigPaths::getRuntimeDir(); | |
360 } | |
361 | |
362 if (!is_dir($this->runtimeDir)) { | |
363 mkdir($this->runtimeDir, 0700, true); | |
364 } | |
365 | |
366 return $this->runtimeDir; | |
367 } | |
368 | |
369 /** | |
370 * Set the readline history file path. | |
371 * | |
372 * @param string $file | |
373 */ | |
374 public function setHistoryFile($file) | |
375 { | |
376 $this->historyFile = ConfigPaths::touchFileWithMkdir($file); | |
377 } | |
378 | |
379 /** | |
380 * Get the readline history file path. | |
381 * | |
382 * Defaults to `/history` inside the shell's base config dir unless | |
383 * explicitly overridden. | |
384 * | |
385 * @return string | |
386 */ | |
387 public function getHistoryFile() | |
388 { | |
389 if (isset($this->historyFile)) { | |
390 return $this->historyFile; | |
391 } | |
392 | |
393 $files = ConfigPaths::getConfigFiles(['psysh_history', 'history'], $this->configDir); | |
394 | |
395 if (!empty($files)) { | |
396 if ($this->warnOnMultipleConfigs && count($files) > 1) { | |
397 $msg = sprintf('Multiple history files found: %s. Using %s', implode($files, ', '), $files[0]); | |
398 trigger_error($msg, E_USER_NOTICE); | |
399 } | |
400 | |
401 $this->setHistoryFile($files[0]); | |
402 } else { | |
403 // fallback: create our own history file | |
404 $dir = $this->configDir ?: ConfigPaths::getCurrentConfigDir(); | |
405 $this->setHistoryFile($dir . '/psysh_history'); | |
406 } | |
407 | |
408 return $this->historyFile; | |
409 } | |
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 /** | |
422 * Get the readline max history size. | |
423 * | |
424 * @return int | |
425 */ | |
426 public function getHistorySize() | |
427 { | |
428 return $this->historySize; | |
429 } | |
430 | |
431 /** | |
432 * Sets whether readline erases old duplicate history entries. | |
433 * | |
434 * @param bool $value | |
435 */ | |
436 public function setEraseDuplicates($value) | |
437 { | |
438 $this->eraseDuplicates = (bool) $value; | |
439 } | |
440 | |
441 /** | |
442 * Get whether readline erases old duplicate history entries. | |
443 * | |
444 * @return bool | |
445 */ | |
446 public function getEraseDuplicates() | |
447 { | |
448 return $this->eraseDuplicates; | |
449 } | |
450 | |
451 /** | |
452 * Get a temporary file of type $type for process $pid. | |
453 * | |
454 * The file will be created inside the current temporary directory. | |
455 * | |
456 * @see self::getRuntimeDir | |
457 * | |
458 * @param string $type | |
459 * @param int $pid | |
460 * | |
461 * @return string Temporary file name | |
462 */ | |
463 public function getTempFile($type, $pid) | |
464 { | |
465 return tempnam($this->getRuntimeDir(), $type . '_' . $pid . '_'); | |
466 } | |
467 | |
468 /** | |
469 * Get a filename suitable for a FIFO pipe of $type for process $pid. | |
470 * | |
471 * The pipe will be created inside the current temporary directory. | |
472 * | |
473 * @param string $type | |
474 * @param int $pid | |
475 * | |
476 * @return string Pipe name | |
477 */ | |
478 public function getPipe($type, $pid) | |
479 { | |
480 return sprintf('%s/%s_%s', $this->getRuntimeDir(), $type, $pid); | |
481 } | |
482 | |
483 /** | |
484 * Check whether this PHP instance has Readline available. | |
485 * | |
486 * @return bool True if Readline is available | |
487 */ | |
488 public function hasReadline() | |
489 { | |
490 return $this->hasReadline; | |
491 } | |
492 | |
493 /** | |
494 * Enable or disable Readline usage. | |
495 * | |
496 * @param bool $useReadline | |
497 */ | |
498 public function setUseReadline($useReadline) | |
499 { | |
500 $this->useReadline = (bool) $useReadline; | |
501 } | |
502 | |
503 /** | |
504 * Check whether to use Readline. | |
505 * | |
506 * If `setUseReadline` as been set to true, but Readline is not actually | |
507 * available, this will return false. | |
508 * | |
509 * @return bool True if the current Shell should use Readline | |
510 */ | |
511 public function useReadline() | |
512 { | |
513 return isset($this->useReadline) ? ($this->hasReadline && $this->useReadline) : $this->hasReadline; | |
514 } | |
515 | |
516 /** | |
517 * Set the Psy Shell readline service. | |
518 * | |
519 * @param Readline $readline | |
520 */ | |
521 public function setReadline(Readline $readline) | |
522 { | |
523 $this->readline = $readline; | |
524 } | |
525 | |
526 /** | |
527 * Get the Psy Shell readline service. | |
528 * | |
529 * By default, this service uses (in order of preference): | |
530 * | |
531 * * GNU Readline | |
532 * * Libedit | |
533 * * A transient array-based readline emulation. | |
534 * | |
535 * @return Readline | |
536 */ | |
537 public function getReadline() | |
538 { | |
539 if (!isset($this->readline)) { | |
540 $className = $this->getReadlineClass(); | |
541 $this->readline = new $className( | |
542 $this->getHistoryFile(), | |
543 $this->getHistorySize(), | |
544 $this->getEraseDuplicates() | |
545 ); | |
546 } | |
547 | |
548 return $this->readline; | |
549 } | |
550 | |
551 /** | |
552 * Get the appropriate Readline implementation class name. | |
553 * | |
554 * @see self::getReadline | |
555 * | |
556 * @return string | |
557 */ | |
558 private function getReadlineClass() | |
559 { | |
560 if ($this->useReadline()) { | |
561 if (GNUReadline::isSupported()) { | |
562 return 'Psy\Readline\GNUReadline'; | |
563 } elseif (Libedit::isSupported()) { | |
564 return 'Psy\Readline\Libedit'; | |
565 } elseif (HoaConsole::isSupported()) { | |
566 return 'Psy\Readline\HoaConsole'; | |
567 } | |
568 } | |
569 | |
570 return 'Psy\Readline\Transient'; | |
571 } | |
572 | |
573 /** | |
574 * Enable or disable bracketed paste. | |
575 * | |
576 * Note that this only works with readline (not libedit) integration for now. | |
577 * | |
578 * @param bool $useBracketedPaste | |
579 */ | |
580 public function setUseBracketedPaste($useBracketedPaste) | |
581 { | |
582 $this->useBracketedPaste = (bool) $useBracketedPaste; | |
583 } | |
584 | |
585 /** | |
586 * Check whether to use bracketed paste with readline. | |
587 * | |
588 * When this works, it's magical. Tabs in pastes don't try to autcomplete. | |
589 * Newlines in paste don't execute code until you get to the end. It makes | |
590 * readline act like you'd expect when pasting. | |
591 * | |
592 * But it often (usually?) does not work. And when it doesn't, it just spews | |
593 * escape codes all over the place and generally makes things ugly :( | |
594 * | |
595 * If `useBracketedPaste` has been set to true, but the current readline | |
596 * implementation is anything besides GNU readline, this will return false. | |
597 * | |
598 * @return bool True if the shell should use bracketed paste | |
599 */ | |
600 public function useBracketedPaste() | |
601 { | |
602 // For now, only the GNU readline implementation supports bracketed paste. | |
603 $supported = ($this->getReadlineClass() === 'Psy\Readline\GNUReadline'); | |
604 | |
605 return $supported && $this->useBracketedPaste; | |
606 | |
607 // @todo mebbe turn this on by default some day? | |
608 // return isset($this->useBracketedPaste) ? ($supported && $this->useBracketedPaste) : $supported; | |
609 } | |
610 | |
611 /** | |
612 * Check whether this PHP instance has Pcntl available. | |
613 * | |
614 * @return bool True if Pcntl is available | |
615 */ | |
616 public function hasPcntl() | |
617 { | |
618 return $this->hasPcntl; | |
619 } | |
620 | |
621 /** | |
622 * Enable or disable Pcntl usage. | |
623 * | |
624 * @param bool $usePcntl | |
625 */ | |
626 public function setUsePcntl($usePcntl) | |
627 { | |
628 $this->usePcntl = (bool) $usePcntl; | |
629 } | |
630 | |
631 /** | |
632 * Check whether to use Pcntl. | |
633 * | |
634 * If `setUsePcntl` has been set to true, but Pcntl is not actually | |
635 * available, this will return false. | |
636 * | |
637 * @return bool True if the current Shell should use Pcntl | |
638 */ | |
639 public function usePcntl() | |
640 { | |
641 return isset($this->usePcntl) ? ($this->hasPcntl && $this->usePcntl) : $this->hasPcntl; | |
642 } | |
643 | |
644 /** | |
645 * Enable or disable strict requirement of semicolons. | |
646 * | |
647 * @see self::requireSemicolons() | |
648 * | |
649 * @param bool $requireSemicolons | |
650 */ | |
651 public function setRequireSemicolons($requireSemicolons) | |
652 { | |
653 $this->requireSemicolons = (bool) $requireSemicolons; | |
654 } | |
655 | |
656 /** | |
657 * Check whether to require semicolons on all statements. | |
658 * | |
659 * By default, PsySH will automatically insert semicolons at the end of | |
660 * statements if they're missing. To strictly require semicolons, set | |
661 * `requireSemicolons` to true. | |
662 * | |
663 * @return bool | |
664 */ | |
665 public function requireSemicolons() | |
666 { | |
667 return $this->requireSemicolons; | |
668 } | |
669 | |
670 /** | |
671 * Enable or disable Unicode in PsySH specific output. | |
672 * | |
673 * Note that this does not disable Unicode output in general, it just makes | |
674 * it so PsySH won't output any itself. | |
675 * | |
676 * @param bool $useUnicode | |
677 */ | |
678 public function setUseUnicode($useUnicode) | |
679 { | |
680 $this->useUnicode = (bool) $useUnicode; | |
681 } | |
682 | |
683 /** | |
684 * Check whether to use Unicode in PsySH specific output. | |
685 * | |
686 * Note that this does not disable Unicode output in general, it just makes | |
687 * it so PsySH won't output any itself. | |
688 * | |
689 * @return bool | |
690 */ | |
691 public function useUnicode() | |
692 { | |
693 if (isset($this->useUnicode)) { | |
694 return $this->useUnicode; | |
695 } | |
696 | |
697 // @todo detect `chsh` != 65001 on Windows and return false | |
698 return true; | |
699 } | |
700 | |
701 /** | |
702 * Set the error logging level. | |
703 * | |
704 * @see self::errorLoggingLevel | |
705 * | |
706 * @param bool $errorLoggingLevel | |
707 */ | |
708 public function setErrorLoggingLevel($errorLoggingLevel) | |
709 { | |
710 $this->errorLoggingLevel = (E_ALL | E_STRICT) & $errorLoggingLevel; | |
711 } | |
712 | |
713 /** | |
714 * Get the current error logging level. | |
715 * | |
716 * By default, PsySH will automatically log all errors, regardless of the | |
717 * current `error_reporting` level. Additionally, if the `error_reporting` | |
718 * level warrants, an ErrorException will be thrown. | |
719 * | |
720 * Set `errorLoggingLevel` to 0 to prevent logging non-thrown errors. Set it | |
721 * to any valid error_reporting value to log only errors which match that | |
722 * level. | |
723 * | |
724 * http://php.net/manual/en/function.error-reporting.php | |
725 * | |
726 * @return int | |
727 */ | |
728 public function errorLoggingLevel() | |
729 { | |
730 return $this->errorLoggingLevel; | |
731 } | |
732 | |
733 /** | |
734 * Set a CodeCleaner service instance. | |
735 * | |
736 * @param CodeCleaner $cleaner | |
737 */ | |
738 public function setCodeCleaner(CodeCleaner $cleaner) | |
739 { | |
740 $this->cleaner = $cleaner; | |
741 } | |
742 | |
743 /** | |
744 * Get a CodeCleaner service instance. | |
745 * | |
746 * If none has been explicitly defined, this will create a new instance. | |
747 * | |
748 * @return CodeCleaner | |
749 */ | |
750 public function getCodeCleaner() | |
751 { | |
752 if (!isset($this->cleaner)) { | |
753 $this->cleaner = new CodeCleaner(); | |
754 } | |
755 | |
756 return $this->cleaner; | |
757 } | |
758 | |
759 /** | |
760 * Enable or disable tab completion. | |
761 * | |
762 * @param bool $useTabCompletion | |
763 */ | |
764 public function setUseTabCompletion($useTabCompletion) | |
765 { | |
766 $this->useTabCompletion = (bool) $useTabCompletion; | |
767 } | |
768 | |
769 /** | |
770 * @deprecated Call `setUseTabCompletion` instead | |
771 * | |
772 * @param bool $useTabCompletion | |
773 */ | |
774 public function setTabCompletion($useTabCompletion) | |
775 { | |
776 $this->setUseTabCompletion($useTabCompletion); | |
777 } | |
778 | |
779 /** | |
780 * Check whether to use tab completion. | |
781 * | |
782 * If `setUseTabCompletion` has been set to true, but readline is not | |
783 * actually available, this will return false. | |
784 * | |
785 * @return bool True if the current Shell should use tab completion | |
786 */ | |
787 public function useTabCompletion() | |
788 { | |
789 return isset($this->useTabCompletion) ? ($this->hasReadline && $this->useTabCompletion) : $this->hasReadline; | |
790 } | |
791 | |
792 /** | |
793 * @deprecated Call `useTabCompletion` instead | |
794 * | |
795 * @return bool | |
796 */ | |
797 public function getTabCompletion() | |
798 { | |
799 return $this->useTabCompletion(); | |
800 } | |
801 | |
802 /** | |
803 * Set the Shell Output service. | |
804 * | |
805 * @param ShellOutput $output | |
806 */ | |
807 public function setOutput(ShellOutput $output) | |
808 { | |
809 $this->output = $output; | |
810 } | |
811 | |
812 /** | |
813 * Get a Shell Output service instance. | |
814 * | |
815 * If none has been explicitly provided, this will create a new instance | |
816 * with VERBOSITY_NORMAL and the output page supplied by self::getPager | |
817 * | |
818 * @see self::getPager | |
819 * | |
820 * @return ShellOutput | |
821 */ | |
822 public function getOutput() | |
823 { | |
824 if (!isset($this->output)) { | |
825 $this->output = new ShellOutput( | |
826 ShellOutput::VERBOSITY_NORMAL, | |
827 $this->getOutputDecorated(), | |
828 null, | |
829 $this->getPager() | |
830 ); | |
831 } | |
832 | |
833 return $this->output; | |
834 } | |
835 | |
836 /** | |
837 * Get the decoration (i.e. color) setting for the Shell Output service. | |
838 * | |
839 * @return null|bool 3-state boolean corresponding to the current color mode | |
840 */ | |
841 public function getOutputDecorated() | |
842 { | |
843 if ($this->colorMode() === self::COLOR_MODE_AUTO) { | |
844 return; | |
845 } elseif ($this->colorMode() === self::COLOR_MODE_FORCED) { | |
846 return true; | |
847 } elseif ($this->colorMode() === self::COLOR_MODE_DISABLED) { | |
848 return false; | |
849 } | |
850 } | |
851 | |
852 /** | |
853 * Set the OutputPager service. | |
854 * | |
855 * If a string is supplied, a ProcOutputPager will be used which shells out | |
856 * to the specified command. | |
857 * | |
858 * @throws \InvalidArgumentException if $pager is not a string or OutputPager instance | |
859 * | |
860 * @param string|OutputPager $pager | |
861 */ | |
862 public function setPager($pager) | |
863 { | |
864 if ($pager && !is_string($pager) && !$pager instanceof OutputPager) { | |
865 throw new \InvalidArgumentException('Unexpected pager instance'); | |
866 } | |
867 | |
868 $this->pager = $pager; | |
869 } | |
870 | |
871 /** | |
872 * Get an OutputPager instance or a command for an external Proc pager. | |
873 * | |
874 * If no Pager has been explicitly provided, and Pcntl is available, this | |
875 * will default to `cli.pager` ini value, falling back to `which less`. | |
876 * | |
877 * @return string|OutputPager | |
878 */ | |
879 public function getPager() | |
880 { | |
881 if (!isset($this->pager) && $this->usePcntl()) { | |
882 if ($pager = ini_get('cli.pager')) { | |
883 // use the default pager | |
884 $this->pager = $pager; | |
885 } elseif ($less = exec('which less 2>/dev/null')) { | |
886 // check for the presence of less... | |
887 $this->pager = $less . ' -R -S -F -X'; | |
888 } | |
889 } | |
890 | |
891 return $this->pager; | |
892 } | |
893 | |
894 /** | |
895 * Set the Shell AutoCompleter service. | |
896 * | |
897 * @param AutoCompleter $autoCompleter | |
898 */ | |
899 public function setAutoCompleter(AutoCompleter $autoCompleter) | |
900 { | |
901 $this->autoCompleter = $autoCompleter; | |
902 } | |
903 | |
904 /** | |
905 * Get an AutoCompleter service instance. | |
906 * | |
907 * @return AutoCompleter | |
908 */ | |
909 public function getAutoCompleter() | |
910 { | |
911 if (!isset($this->autoCompleter)) { | |
912 $this->autoCompleter = new AutoCompleter(); | |
913 } | |
914 | |
915 return $this->autoCompleter; | |
916 } | |
917 | |
918 /** | |
919 * @deprecated Nothing should be using this anymore | |
920 * | |
921 * @return array | |
922 */ | |
923 public function getTabCompletionMatchers() | |
924 { | |
925 return []; | |
926 } | |
927 | |
928 /** | |
929 * Add tab completion matchers to the AutoCompleter. | |
930 * | |
931 * This will buffer new matchers in the event that the Shell has not yet | |
932 * been instantiated. This allows the user to specify matchers in their | |
933 * config rc file, despite the fact that their file is needed in the Shell | |
934 * constructor. | |
935 * | |
936 * @param array $matchers | |
937 */ | |
938 public function addMatchers(array $matchers) | |
939 { | |
940 $this->newMatchers = array_merge($this->newMatchers, $matchers); | |
941 if (isset($this->shell)) { | |
942 $this->doAddMatchers(); | |
943 } | |
944 } | |
945 | |
946 /** | |
947 * Internal method for adding tab completion matchers. This will set any new | |
948 * matchers once a Shell is available. | |
949 */ | |
950 private function doAddMatchers() | |
951 { | |
952 if (!empty($this->newMatchers)) { | |
953 $this->shell->addMatchers($this->newMatchers); | |
954 $this->newMatchers = []; | |
955 } | |
956 } | |
957 | |
958 /** | |
959 * @deprecated Use `addMatchers` instead | |
960 * | |
961 * @param array $matchers | |
962 */ | |
963 public function addTabCompletionMatchers(array $matchers) | |
964 { | |
965 $this->addMatchers($matchers); | |
966 } | |
967 | |
968 /** | |
969 * Add commands to the Shell. | |
970 * | |
971 * This will buffer new commands in the event that the Shell has not yet | |
972 * been instantiated. This allows the user to specify commands in their | |
973 * config rc file, despite the fact that their file is needed in the Shell | |
974 * constructor. | |
975 * | |
976 * @param array $commands | |
977 */ | |
978 public function addCommands(array $commands) | |
979 { | |
980 $this->newCommands = array_merge($this->newCommands, $commands); | |
981 if (isset($this->shell)) { | |
982 $this->doAddCommands(); | |
983 } | |
984 } | |
985 | |
986 /** | |
987 * Internal method for adding commands. This will set any new commands once | |
988 * a Shell is available. | |
989 */ | |
990 private function doAddCommands() | |
991 { | |
992 if (!empty($this->newCommands)) { | |
993 $this->shell->addCommands($this->newCommands); | |
994 $this->newCommands = []; | |
995 } | |
996 } | |
997 | |
998 /** | |
999 * Set the Shell backreference and add any new commands to the Shell. | |
1000 * | |
1001 * @param Shell $shell | |
1002 */ | |
1003 public function setShell(Shell $shell) | |
1004 { | |
1005 $this->shell = $shell; | |
1006 $this->doAddCommands(); | |
1007 $this->doAddMatchers(); | |
1008 } | |
1009 | |
1010 /** | |
1011 * Set the PHP manual database file. | |
1012 * | |
1013 * This file should be an SQLite database generated from the phpdoc source | |
1014 * with the `bin/build_manual` script. | |
1015 * | |
1016 * @param string $filename | |
1017 */ | |
1018 public function setManualDbFile($filename) | |
1019 { | |
1020 $this->manualDbFile = (string) $filename; | |
1021 } | |
1022 | |
1023 /** | |
1024 * Get the current PHP manual database file. | |
1025 * | |
1026 * @return string Default: '~/.local/share/psysh/php_manual.sqlite' | |
1027 */ | |
1028 public function getManualDbFile() | |
1029 { | |
1030 if (isset($this->manualDbFile)) { | |
1031 return $this->manualDbFile; | |
1032 } | |
1033 | |
1034 $files = ConfigPaths::getDataFiles(['php_manual.sqlite'], $this->dataDir); | |
1035 if (!empty($files)) { | |
1036 if ($this->warnOnMultipleConfigs && count($files) > 1) { | |
1037 $msg = sprintf('Multiple manual database files found: %s. Using %s', implode($files, ', '), $files[0]); | |
1038 trigger_error($msg, E_USER_NOTICE); | |
1039 } | |
1040 | |
1041 return $this->manualDbFile = $files[0]; | |
1042 } | |
1043 } | |
1044 | |
1045 /** | |
1046 * Get a PHP manual database connection. | |
1047 * | |
1048 * @return \PDO | |
1049 */ | |
1050 public function getManualDb() | |
1051 { | |
1052 if (!isset($this->manualDb)) { | |
1053 $dbFile = $this->getManualDbFile(); | |
1054 if (is_file($dbFile)) { | |
1055 try { | |
1056 $this->manualDb = new \PDO('sqlite:' . $dbFile); | |
1057 } catch (\PDOException $e) { | |
1058 if ($e->getMessage() === 'could not find driver') { | |
1059 throw new RuntimeException('SQLite PDO driver not found', 0, $e); | |
1060 } else { | |
1061 throw $e; | |
1062 } | |
1063 } | |
1064 } | |
1065 } | |
1066 | |
1067 return $this->manualDb; | |
1068 } | |
1069 | |
1070 /** | |
1071 * Add an array of casters definitions. | |
1072 * | |
1073 * @param array $casters | |
1074 */ | |
1075 public function addCasters(array $casters) | |
1076 { | |
1077 $this->getPresenter()->addCasters($casters); | |
1078 } | |
1079 | |
1080 /** | |
1081 * Get the Presenter service. | |
1082 * | |
1083 * @return Presenter | |
1084 */ | |
1085 public function getPresenter() | |
1086 { | |
1087 if (!isset($this->presenter)) { | |
1088 $this->presenter = new Presenter($this->getOutput()->getFormatter(), $this->forceArrayIndexes()); | |
1089 } | |
1090 | |
1091 return $this->presenter; | |
1092 } | |
1093 | |
1094 /** | |
1095 * Enable or disable warnings on multiple configuration or data files. | |
1096 * | |
1097 * @see self::warnOnMultipleConfigs() | |
1098 * | |
1099 * @param bool $warnOnMultipleConfigs | |
1100 */ | |
1101 public function setWarnOnMultipleConfigs($warnOnMultipleConfigs) | |
1102 { | |
1103 $this->warnOnMultipleConfigs = (bool) $warnOnMultipleConfigs; | |
1104 } | |
1105 | |
1106 /** | |
1107 * Check whether to warn on multiple configuration or data files. | |
1108 * | |
1109 * By default, PsySH will use the file with highest precedence, and will | |
1110 * silently ignore all others. With this enabled, a warning will be emitted | |
1111 * (but not an exception thrown) if multiple configuration or data files | |
1112 * are found. | |
1113 * | |
1114 * This will default to true in a future release, but is false for now. | |
1115 * | |
1116 * @return bool | |
1117 */ | |
1118 public function warnOnMultipleConfigs() | |
1119 { | |
1120 return $this->warnOnMultipleConfigs; | |
1121 } | |
1122 | |
1123 /** | |
1124 * Set the current color mode. | |
1125 * | |
1126 * @param string $colorMode | |
1127 */ | |
1128 public function setColorMode($colorMode) | |
1129 { | |
1130 $validColorModes = [ | |
1131 self::COLOR_MODE_AUTO, | |
1132 self::COLOR_MODE_FORCED, | |
1133 self::COLOR_MODE_DISABLED, | |
1134 ]; | |
1135 | |
1136 if (in_array($colorMode, $validColorModes)) { | |
1137 $this->colorMode = $colorMode; | |
1138 } else { | |
1139 throw new \InvalidArgumentException('invalid color mode: ' . $colorMode); | |
1140 } | |
1141 } | |
1142 | |
1143 /** | |
1144 * Get the current color mode. | |
1145 * | |
1146 * @return string | |
1147 */ | |
1148 public function colorMode() | |
1149 { | |
1150 return $this->colorMode; | |
1151 } | |
1152 | |
1153 /** | |
1154 * Set an update checker service instance. | |
1155 * | |
1156 * @param Checker $checker | |
1157 */ | |
1158 public function setChecker(Checker $checker) | |
1159 { | |
1160 $this->checker = $checker; | |
1161 } | |
1162 | |
1163 /** | |
1164 * Get an update checker service instance. | |
1165 * | |
1166 * If none has been explicitly defined, this will create a new instance. | |
1167 * | |
1168 * @return Checker | |
1169 */ | |
1170 public function getChecker() | |
1171 { | |
1172 if (!isset($this->checker)) { | |
1173 $interval = $this->getUpdateCheck(); | |
1174 switch ($interval) { | |
1175 case Checker::ALWAYS: | |
1176 $this->checker = new GitHubChecker(); | |
1177 break; | |
1178 | |
1179 case Checker::DAILY: | |
1180 case Checker::WEEKLY: | |
1181 case Checker::MONTHLY: | |
1182 $checkFile = $this->getUpdateCheckCacheFile(); | |
1183 if ($checkFile === false) { | |
1184 $this->checker = new NoopChecker(); | |
1185 } else { | |
1186 $this->checker = new IntervalChecker($checkFile, $interval); | |
1187 } | |
1188 break; | |
1189 | |
1190 case Checker::NEVER: | |
1191 $this->checker = new NoopChecker(); | |
1192 break; | |
1193 } | |
1194 } | |
1195 | |
1196 return $this->checker; | |
1197 } | |
1198 | |
1199 /** | |
1200 * Get the current update check interval. | |
1201 * | |
1202 * One of 'always', 'daily', 'weekly', 'monthly' or 'never'. If none is | |
1203 * explicitly set, default to 'weekly'. | |
1204 * | |
1205 * @return string | |
1206 */ | |
1207 public function getUpdateCheck() | |
1208 { | |
1209 return isset($this->updateCheck) ? $this->updateCheck : Checker::WEEKLY; | |
1210 } | |
1211 | |
1212 /** | |
1213 * Set the update check interval. | |
1214 * | |
1215 * @throws \InvalidArgumentDescription if the update check interval is unknown | |
1216 * | |
1217 * @param string $interval | |
1218 */ | |
1219 public function setUpdateCheck($interval) | |
1220 { | |
1221 $validIntervals = [ | |
1222 Checker::ALWAYS, | |
1223 Checker::DAILY, | |
1224 Checker::WEEKLY, | |
1225 Checker::MONTHLY, | |
1226 Checker::NEVER, | |
1227 ]; | |
1228 | |
1229 if (!in_array($interval, $validIntervals)) { | |
1230 throw new \InvalidArgumentException('invalid update check interval: ' . $interval); | |
1231 } | |
1232 | |
1233 $this->updateCheck = $interval; | |
1234 } | |
1235 | |
1236 /** | |
1237 * Get a cache file path for the update checker. | |
1238 * | |
1239 * @return string|false Return false if config file/directory is not writable | |
1240 */ | |
1241 public function getUpdateCheckCacheFile() | |
1242 { | |
1243 $dir = $this->configDir ?: ConfigPaths::getCurrentConfigDir(); | |
1244 | |
1245 return ConfigPaths::touchFileWithMkdir($dir . '/update_check.json'); | |
1246 } | |
1247 | |
1248 /** | |
1249 * Set the startup message. | |
1250 * | |
1251 * @param string $message | |
1252 */ | |
1253 public function setStartupMessage($message) | |
1254 { | |
1255 $this->startupMessage = $message; | |
1256 } | |
1257 | |
1258 /** | |
1259 * Get the startup message. | |
1260 * | |
1261 * @return string|null | |
1262 */ | |
1263 public function getStartupMessage() | |
1264 { | |
1265 return $this->startupMessage; | |
1266 } | |
1267 | |
1268 /** | |
1269 * Set the prompt. | |
1270 * | |
1271 * @param string $prompt | |
1272 */ | |
1273 public function setPrompt($prompt) | |
1274 { | |
1275 $this->prompt = $prompt; | |
1276 } | |
1277 | |
1278 /** | |
1279 * Get the prompt. | |
1280 * | |
1281 * @return string | |
1282 */ | |
1283 public function getPrompt() | |
1284 { | |
1285 return $this->prompt; | |
1286 } | |
1287 | |
1288 /** | |
1289 * Get the force array indexes. | |
1290 * | |
1291 * @return bool | |
1292 */ | |
1293 public function forceArrayIndexes() | |
1294 { | |
1295 return $this->forceArrayIndexes; | |
1296 } | |
1297 | |
1298 /** | |
1299 * Set the force array indexes. | |
1300 * | |
1301 * @param bool $forceArrayIndexes | |
1302 */ | |
1303 public function setForceArrayIndexes($forceArrayIndexes) | |
1304 { | |
1305 $this->forceArrayIndexes = $forceArrayIndexes; | |
1306 } | |
1307 } |