Security update for Core, with self-updated composer
[yaffs-website] / vendor / psy / psysh / src / Psy / Shell.php
1 <?php
2
3 /*
4  * This file is part of Psy Shell.
5  *
6  * (c) 2012-2017 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\CodeCleaner\NoReturnValue;
15 use Psy\Exception\BreakException;
16 use Psy\Exception\ErrorException;
17 use Psy\Exception\Exception as PsyException;
18 use Psy\Exception\ThrowUpException;
19 use Psy\Input\ShellInput;
20 use Psy\Input\SilentInput;
21 use Psy\Output\ShellOutput;
22 use Psy\TabCompletion\Matcher;
23 use Psy\VarDumper\PresenterAware;
24 use Symfony\Component\Console\Application;
25 use Symfony\Component\Console\Command\Command as BaseCommand;
26 use Symfony\Component\Console\Formatter\OutputFormatter;
27 use Symfony\Component\Console\Input\ArgvInput;
28 use Symfony\Component\Console\Input\InputArgument;
29 use Symfony\Component\Console\Input\InputDefinition;
30 use Symfony\Component\Console\Input\InputInterface;
31 use Symfony\Component\Console\Input\InputOption;
32 use Symfony\Component\Console\Input\StringInput;
33 use Symfony\Component\Console\Output\OutputInterface;
34
35 /**
36  * The Psy Shell application.
37  *
38  * Usage:
39  *
40  *     $shell = new Shell;
41  *     $shell->run();
42  *
43  * @author Justin Hileman <justin@justinhileman.info>
44  */
45 class Shell extends Application
46 {
47     const VERSION = 'v0.8.17';
48
49     const PROMPT      = '>>> ';
50     const BUFF_PROMPT = '... ';
51     const REPLAY      = '--> ';
52     const RETVAL      = '=> ';
53
54     private $config;
55     private $cleaner;
56     private $output;
57     private $readline;
58     private $inputBuffer;
59     private $code;
60     private $codeBuffer;
61     private $codeBufferOpen;
62     private $context;
63     private $includes;
64     private $loop;
65     private $outputWantsNewline = false;
66     private $completion;
67     private $tabCompletionMatchers = array();
68     private $stdoutBuffer;
69     private $prompt;
70
71     /**
72      * Create a new Psy Shell.
73      *
74      * @param Configuration $config (default: null)
75      */
76     public function __construct(Configuration $config = null)
77     {
78         $this->config       = $config ?: new Configuration();
79         $this->cleaner      = $this->config->getCodeCleaner();
80         $this->loop         = $this->config->getLoop();
81         $this->context      = new Context();
82         $this->includes     = array();
83         $this->readline     = $this->config->getReadline();
84         $this->inputBuffer  = array();
85         $this->stdoutBuffer = '';
86
87         parent::__construct('Psy Shell', self::VERSION);
88
89         $this->config->setShell($this);
90
91         // Register the current shell session's config with \Psy\info
92         \Psy\info($this->config);
93     }
94
95     /**
96      * Check whether the first thing in a backtrace is an include call.
97      *
98      * This is used by the psysh bin to decide whether to start a shell on boot,
99      * or to simply autoload the library.
100      */
101     public static function isIncluded(array $trace)
102     {
103         return isset($trace[0]['function']) &&
104           in_array($trace[0]['function'], array('require', 'include', 'require_once', 'include_once'));
105     }
106
107     /**
108      * Invoke a Psy Shell from the current context.
109      *
110      * @see Psy\debug
111      * @deprecated will be removed in 1.0. Use \Psy\debug instead
112      *
113      * @param array  $vars        Scope variables from the calling context (default: array())
114      * @param object $boundObject Bound object ($this) value for the shell
115      *
116      * @return array Scope variables from the debugger session
117      */
118     public static function debug(array $vars = array(), $boundObject = null)
119     {
120         return \Psy\debug($vars, $boundObject);
121     }
122
123     /**
124      * Adds a command object.
125      *
126      * {@inheritdoc}
127      *
128      * @param BaseCommand $command A Symfony Console Command object
129      *
130      * @return BaseCommand The registered command
131      */
132     public function add(BaseCommand $command)
133     {
134         if ($ret = parent::add($command)) {
135             if ($ret instanceof ContextAware) {
136                 $ret->setContext($this->context);
137             }
138
139             if ($ret instanceof PresenterAware) {
140                 $ret->setPresenter($this->config->getPresenter());
141             }
142         }
143
144         return $ret;
145     }
146
147     /**
148      * Gets the default input definition.
149      *
150      * @return InputDefinition An InputDefinition instance
151      */
152     protected function getDefaultInputDefinition()
153     {
154         return new InputDefinition(array(
155             new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
156             new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message.'),
157         ));
158     }
159
160     /**
161      * Gets the default commands that should always be available.
162      *
163      * @return array An array of default Command instances
164      */
165     protected function getDefaultCommands()
166     {
167         $sudo = new Command\SudoCommand();
168         $sudo->setReadline($this->readline);
169
170         $hist = new Command\HistoryCommand();
171         $hist->setReadline($this->readline);
172
173         return array(
174             new Command\HelpCommand(),
175             new Command\ListCommand(),
176             new Command\DumpCommand(),
177             new Command\DocCommand(),
178             new Command\ShowCommand($this->config->colorMode()),
179             new Command\WtfCommand($this->config->colorMode()),
180             new Command\WhereamiCommand($this->config->colorMode()),
181             new Command\ThrowUpCommand(),
182             new Command\TraceCommand(),
183             new Command\BufferCommand(),
184             new Command\ClearCommand(),
185             new Command\EditCommand($this->config->getRuntimeDir()),
186             // new Command\PsyVersionCommand(),
187             $sudo,
188             $hist,
189             new Command\ExitCommand(),
190         );
191     }
192
193     /**
194      * @return array
195      */
196     protected function getTabCompletionMatchers()
197     {
198         if (empty($this->tabCompletionMatchers)) {
199             $this->tabCompletionMatchers = array(
200                 new Matcher\CommandsMatcher($this->all()),
201                 new Matcher\KeywordsMatcher(),
202                 new Matcher\VariablesMatcher(),
203                 new Matcher\ConstantsMatcher(),
204                 new Matcher\FunctionsMatcher(),
205                 new Matcher\ClassNamesMatcher(),
206                 new Matcher\ClassMethodsMatcher(),
207                 new Matcher\ClassAttributesMatcher(),
208                 new Matcher\ObjectMethodsMatcher(),
209                 new Matcher\ObjectAttributesMatcher(),
210                 new Matcher\ClassMethodDefaultParametersMatcher(),
211                 new Matcher\ObjectMethodDefaultParametersMatcher(),
212                 new Matcher\FunctionDefaultParametersMatcher(),
213             );
214         }
215
216         return $this->tabCompletionMatchers;
217     }
218
219     /**
220      * @param array $matchers
221      */
222     public function addTabCompletionMatchers(array $matchers)
223     {
224         $this->tabCompletionMatchers = array_merge($matchers, $this->getTabCompletionMatchers());
225     }
226
227     /**
228      * Set the Shell output.
229      *
230      * @param OutputInterface $output
231      */
232     public function setOutput(OutputInterface $output)
233     {
234         $this->output = $output;
235     }
236
237     /**
238      * Runs the current application.
239      *
240      * @param InputInterface  $input  An Input instance
241      * @param OutputInterface $output An Output instance
242      *
243      * @return int 0 if everything went fine, or an error code
244      */
245     public function run(InputInterface $input = null, OutputInterface $output = null)
246     {
247         $this->initializeTabCompletion();
248
249         if ($input === null && !isset($_SERVER['argv'])) {
250             $input = new ArgvInput(array());
251         }
252
253         if ($output === null) {
254             $output = $this->config->getOutput();
255         }
256
257         try {
258             return parent::run($input, $output);
259         } catch (\Exception $e) {
260             $this->writeException($e);
261         }
262     }
263
264     /**
265      * Runs the current application.
266      *
267      * @throws Exception if thrown via the `throw-up` command
268      *
269      * @param InputInterface  $input  An Input instance
270      * @param OutputInterface $output An Output instance
271      *
272      * @return int 0 if everything went fine, or an error code
273      */
274     public function doRun(InputInterface $input, OutputInterface $output)
275     {
276         $this->setOutput($output);
277
278         $this->resetCodeBuffer();
279
280         $this->setAutoExit(false);
281         $this->setCatchExceptions(false);
282
283         $this->readline->readHistory();
284
285         // if ($this->config->useReadline()) {
286         //     readline_completion_function(array($this, 'autocomplete'));
287         // }
288
289         $this->output->writeln($this->getHeader());
290         $this->writeVersionInfo();
291         $this->writeStartupMessage();
292
293         try {
294             $this->loop->run($this);
295         } catch (ThrowUpException $e) {
296             throw $e->getPrevious();
297         }
298     }
299
300     /**
301      * Read user input.
302      *
303      * This will continue fetching user input until the code buffer contains
304      * valid code.
305      *
306      * @throws BreakException if user hits Ctrl+D
307      */
308     public function getInput()
309     {
310         $this->codeBufferOpen = false;
311
312         do {
313             // reset output verbosity (in case it was altered by a subcommand)
314             $this->output->setVerbosity(ShellOutput::VERBOSITY_VERBOSE);
315
316             $input = $this->readline();
317
318             /*
319              * Handle Ctrl+D. It behaves differently in different cases:
320              *
321              *   1) In an expression, like a function or "if" block, clear the input buffer
322              *   2) At top-level session, behave like the exit command
323              */
324             if ($input === false) {
325                 $this->output->writeln('');
326
327                 if ($this->hasCode()) {
328                     $this->resetCodeBuffer();
329                 } else {
330                     throw new BreakException('Ctrl+D');
331                 }
332             }
333
334             // handle empty input
335             if (trim($input) === '') {
336                 continue;
337             }
338
339             if ($this->hasCommand($input)) {
340                 $this->readline->addHistory($input);
341                 $this->runCommand($input);
342
343                 continue;
344             }
345
346             $this->addCode($input);
347         } while (!$this->hasValidCode());
348     }
349
350     /**
351      * Pass the beforeLoop callback through to the Loop instance.
352      *
353      * @see Loop::beforeLoop
354      */
355     public function beforeLoop()
356     {
357         $this->loop->beforeLoop();
358     }
359
360     /**
361      * Pass the afterLoop callback through to the Loop instance.
362      *
363      * @see Loop::afterLoop
364      */
365     public function afterLoop()
366     {
367         $this->loop->afterLoop();
368     }
369
370     /**
371      * Set the variables currently in scope.
372      *
373      * @param array $vars
374      */
375     public function setScopeVariables(array $vars)
376     {
377         $this->context->setAll($vars);
378     }
379
380     /**
381      * Return the set of variables currently in scope.
382      *
383      * @param bool $includeBoundObject Pass false to exclude 'this'. If you're
384      *                                 passing the scope variables to `extract`
385      *                                 in PHP 7.1+, you _must_ exclude 'this'
386      *
387      * @return array Associative array of scope variables
388      */
389     public function getScopeVariables($includeBoundObject = true)
390     {
391         $vars = $this->context->getAll();
392
393         if (!$includeBoundObject) {
394             unset($vars['this']);
395         }
396
397         return $vars;
398     }
399
400     /**
401      * Return the set of magic variables currently in scope.
402      *
403      * @param bool $includeBoundObject Pass false to exclude 'this'. If you're
404      *                                 passing the scope variables to `extract`
405      *                                 in PHP 7.1+, you _must_ exclude 'this'
406      *
407      * @return array Associative array of magic scope variables
408      */
409     public function getSpecialScopeVariables($includeBoundObject = true)
410     {
411         $vars = $this->context->getSpecialVariables();
412
413         if (!$includeBoundObject) {
414             unset($vars['this']);
415         }
416
417         return $vars;
418     }
419
420     /**
421      * Get the set of unused command-scope variable names.
422      *
423      * @return array Array of unused variable names
424      */
425     public function getUnusedCommandScopeVariableNames()
426     {
427         return $this->context->getUnusedCommandScopeVariableNames();
428     }
429
430     /**
431      * Get the set of variable names currently in scope.
432      *
433      * @return array Array of variable names
434      */
435     public function getScopeVariableNames()
436     {
437         return array_keys($this->context->getAll());
438     }
439
440     /**
441      * Get a scope variable value by name.
442      *
443      * @param string $name
444      *
445      * @return mixed
446      */
447     public function getScopeVariable($name)
448     {
449         return $this->context->get($name);
450     }
451
452     /**
453      * Set the bound object ($this variable) for the interactive shell.
454      *
455      * @param object|null $boundObject
456      */
457     public function setBoundObject($boundObject)
458     {
459         $this->context->setBoundObject($boundObject);
460     }
461
462     /**
463      * Get the bound object ($this variable) for the interactive shell.
464      *
465      * @return object|null
466      */
467     public function getBoundObject()
468     {
469         return $this->context->getBoundObject();
470     }
471
472     /**
473      * Add includes, to be parsed and executed before running the interactive shell.
474      *
475      * @param array $includes
476      */
477     public function setIncludes(array $includes = array())
478     {
479         $this->includes = $includes;
480     }
481
482     /**
483      * Get PHP files to be parsed and executed before running the interactive shell.
484      *
485      * @return array
486      */
487     public function getIncludes()
488     {
489         return array_merge($this->config->getDefaultIncludes(), $this->includes);
490     }
491
492     /**
493      * Check whether this shell's code buffer contains code.
494      *
495      * @return bool True if the code buffer contains code
496      */
497     public function hasCode()
498     {
499         return !empty($this->codeBuffer);
500     }
501
502     /**
503      * Check whether the code in this shell's code buffer is valid.
504      *
505      * If the code is valid, the code buffer should be flushed and evaluated.
506      *
507      * @return bool True if the code buffer content is valid
508      */
509     protected function hasValidCode()
510     {
511         return !$this->codeBufferOpen && $this->code !== false;
512     }
513
514     /**
515      * Add code to the code buffer.
516      *
517      * @param string $code
518      */
519     public function addCode($code)
520     {
521         try {
522             // Code lines ending in \ keep the buffer open
523             if (substr(rtrim($code), -1) === '\\') {
524                 $this->codeBufferOpen = true;
525                 $code = substr(rtrim($code), 0, -1);
526             } else {
527                 $this->codeBufferOpen = false;
528             }
529
530             $this->codeBuffer[] = $code;
531             $this->code         = $this->cleaner->clean($this->codeBuffer, $this->config->requireSemicolons());
532         } catch (\Exception $e) {
533             // Add failed code blocks to the readline history.
534             $this->addCodeBufferToHistory();
535
536             throw $e;
537         }
538     }
539
540     /**
541      * Get the current code buffer.
542      *
543      * This is useful for commands which manipulate the buffer.
544      *
545      * @return array
546      */
547     public function getCodeBuffer()
548     {
549         return $this->codeBuffer;
550     }
551
552     /**
553      * Run a Psy Shell command given the user input.
554      *
555      * @throws InvalidArgumentException if the input is not a valid command
556      *
557      * @param string $input User input string
558      *
559      * @return mixed Who knows?
560      */
561     protected function runCommand($input)
562     {
563         $command = $this->getCommand($input);
564
565         if (empty($command)) {
566             throw new \InvalidArgumentException('Command not found: ' . $input);
567         }
568
569         $input = new ShellInput(str_replace('\\', '\\\\', rtrim($input, " \t\n\r\0\x0B;")));
570
571         if ($input->hasParameterOption(array('--help', '-h'))) {
572             $helpCommand = $this->get('help');
573             $helpCommand->setCommand($command);
574
575             return $helpCommand->run($input, $this->output);
576         }
577
578         return $command->run($input, $this->output);
579     }
580
581     /**
582      * Reset the current code buffer.
583      *
584      * This should be run after evaluating user input, catching exceptions, or
585      * on demand by commands such as BufferCommand.
586      */
587     public function resetCodeBuffer()
588     {
589         $this->codeBuffer = array();
590         $this->code       = false;
591     }
592
593     /**
594      * Inject input into the input buffer.
595      *
596      * This is useful for commands which want to replay history.
597      *
598      * @param string|array $input
599      * @param bool         $silent
600      */
601     public function addInput($input, $silent = false)
602     {
603         foreach ((array) $input as $line) {
604             $this->inputBuffer[] = $silent ? new SilentInput($line) : $line;
605         }
606     }
607
608     /**
609      * Flush the current (valid) code buffer.
610      *
611      * If the code buffer is valid, resets the code buffer and returns the
612      * current code.
613      *
614      * @return string PHP code buffer contents
615      */
616     public function flushCode()
617     {
618         if ($this->hasValidCode()) {
619             $this->addCodeBufferToHistory();
620             $code = $this->code;
621             $this->resetCodeBuffer();
622
623             return $code;
624         }
625     }
626
627     /**
628      * Filter silent input from code buffer, write the rest to readline history.
629      */
630     private function addCodeBufferToHistory()
631     {
632         $codeBuffer = array_filter($this->codeBuffer, function ($line) {
633             return !$line instanceof SilentInput;
634         });
635
636         $code = implode("\n", $codeBuffer);
637
638         if (trim($code) !== '') {
639             $this->readline->addHistory($code);
640         }
641     }
642
643     /**
644      * Get the current evaluation scope namespace.
645      *
646      * @see CodeCleaner::getNamespace
647      *
648      * @return string Current code namespace
649      */
650     public function getNamespace()
651     {
652         if ($namespace = $this->cleaner->getNamespace()) {
653             return implode('\\', $namespace);
654         }
655     }
656
657     /**
658      * Write a string to stdout.
659      *
660      * This is used by the shell loop for rendering output from evaluated code.
661      *
662      * @param string $out
663      * @param int    $phase Output buffering phase
664      */
665     public function writeStdout($out, $phase = PHP_OUTPUT_HANDLER_END)
666     {
667         $isCleaning = false;
668         if (version_compare(PHP_VERSION, '5.4', '>=')) {
669             $isCleaning = $phase & PHP_OUTPUT_HANDLER_CLEAN;
670         }
671
672         // Incremental flush
673         if ($out !== '' && !$isCleaning) {
674             $this->output->write($out, false, ShellOutput::OUTPUT_RAW);
675             $this->outputWantsNewline = (substr($out, -1) !== "\n");
676             $this->stdoutBuffer .= $out;
677         }
678
679         // Output buffering is done!
680         if ($phase & PHP_OUTPUT_HANDLER_END) {
681             // Write an extra newline if stdout didn't end with one
682             if ($this->outputWantsNewline) {
683                 $this->output->writeln(sprintf('<aside>%s</aside>', $this->config->useUnicode() ? '⏎' : '\\n'));
684                 $this->outputWantsNewline = false;
685             }
686
687             // Save the stdout buffer as $__out
688             if ($this->stdoutBuffer !== '') {
689                 $this->context->setLastStdout($this->stdoutBuffer);
690                 $this->stdoutBuffer = '';
691             }
692         }
693     }
694
695     /**
696      * Write a return value to stdout.
697      *
698      * The return value is formatted or pretty-printed, and rendered in a
699      * visibly distinct manner (in this case, as cyan).
700      *
701      * @see self::presentValue
702      *
703      * @param mixed $ret
704      */
705     public function writeReturnValue($ret)
706     {
707         if ($ret instanceof NoReturnValue) {
708             return;
709         }
710
711         $this->context->setReturnValue($ret);
712         $ret    = $this->presentValue($ret);
713         $indent = str_repeat(' ', strlen(static::RETVAL));
714
715         $this->output->writeln(static::RETVAL . str_replace(PHP_EOL, PHP_EOL . $indent, $ret));
716     }
717
718     /**
719      * Renders a caught Exception.
720      *
721      * Exceptions are formatted according to severity. ErrorExceptions which were
722      * warnings or Strict errors aren't rendered as harshly as real errors.
723      *
724      * Stores $e as the last Exception in the Shell Context.
725      *
726      * @param \Exception      $e      An exception instance
727      * @param OutputInterface $output An OutputInterface instance
728      */
729     public function writeException(\Exception $e)
730     {
731         $this->context->setLastException($e);
732         $this->output->writeln($this->formatException($e));
733         $this->resetCodeBuffer();
734     }
735
736     /**
737      * Helper for formatting an exception for writeException().
738      *
739      * @todo extract this to somewhere it makes more sense
740      *
741      * @param \Exception $e
742      *
743      * @return string
744      */
745     public function formatException(\Exception $e)
746     {
747         $message = $e->getMessage();
748         if (!$e instanceof PsyException) {
749             if ($message === '') {
750                 $message = get_class($e);
751             } else {
752                 $message = sprintf('%s with message \'%s\'', get_class($e), $message);
753             }
754         }
755
756         $severity = ($e instanceof \ErrorException) ? $this->getSeverity($e) : 'error';
757
758         return sprintf('<%s>%s</%s>', $severity, OutputFormatter::escape($message), $severity);
759     }
760
761     /**
762      * Helper for getting an output style for the given ErrorException's level.
763      *
764      * @param \ErrorException $e
765      *
766      * @return string
767      */
768     protected function getSeverity(\ErrorException $e)
769     {
770         $severity = $e->getSeverity();
771         if ($severity & error_reporting()) {
772             switch ($severity) {
773                 case E_WARNING:
774                 case E_NOTICE:
775                 case E_CORE_WARNING:
776                 case E_COMPILE_WARNING:
777                 case E_USER_WARNING:
778                 case E_USER_NOTICE:
779                 case E_STRICT:
780                     return 'warning';
781
782                 default:
783                     return 'error';
784             }
785         } else {
786             // Since this is below the user's reporting threshold, it's always going to be a warning.
787             return 'warning';
788         }
789     }
790
791     /**
792      * Helper for throwing an ErrorException.
793      *
794      * This allows us to:
795      *
796      *     set_error_handler(array($psysh, 'handleError'));
797      *
798      * Unlike ErrorException::throwException, this error handler respects the
799      * current error_reporting level; i.e. it logs warnings and notices, but
800      * doesn't throw an exception unless it's above the current error_reporting
801      * threshold. This should probably only be used in the inner execution loop
802      * of the shell, as most of the time a thrown exception is much more useful.
803      *
804      * If the error type matches the `errorLoggingLevel` config, it will be
805      * logged as well, regardless of the `error_reporting` level.
806      *
807      * @see \Psy\Exception\ErrorException::throwException
808      * @see \Psy\Shell::writeException
809      *
810      * @throws \Psy\Exception\ErrorException depending on the current error_reporting level
811      *
812      * @param int    $errno   Error type
813      * @param string $errstr  Message
814      * @param string $errfile Filename
815      * @param int    $errline Line number
816      */
817     public function handleError($errno, $errstr, $errfile, $errline)
818     {
819         if ($errno & error_reporting()) {
820             ErrorException::throwException($errno, $errstr, $errfile, $errline);
821         } elseif ($errno & $this->config->errorLoggingLevel()) {
822             // log it and continue...
823             $this->writeException(new ErrorException($errstr, 0, $errno, $errfile, $errline));
824         }
825     }
826
827     /**
828      * Format a value for display.
829      *
830      * @see Presenter::present
831      *
832      * @param mixed $val
833      *
834      * @return string Formatted value
835      */
836     protected function presentValue($val)
837     {
838         return $this->config->getPresenter()->present($val);
839     }
840
841     /**
842      * Get a command (if one exists) for the current input string.
843      *
844      * @param string $input
845      *
846      * @return null|BaseCommand
847      */
848     protected function getCommand($input)
849     {
850         $input = new StringInput($input);
851         if ($name = $input->getFirstArgument()) {
852             return $this->get($name);
853         }
854     }
855
856     /**
857      * Check whether a command is set for the current input string.
858      *
859      * @param string $input
860      *
861      * @return bool True if the shell has a command for the given input
862      */
863     protected function hasCommand($input)
864     {
865         $input = new StringInput($input);
866         if ($name = $input->getFirstArgument()) {
867             return $this->has($name);
868         }
869
870         return false;
871     }
872
873     /**
874      * Get the current input prompt.
875      *
876      * @return string
877      */
878     protected function getPrompt()
879     {
880         if ($this->hasCode()) {
881             return static::BUFF_PROMPT;
882         }
883
884         return $this->config->getPrompt() ?: static::PROMPT;
885     }
886
887     /**
888      * Read a line of user input.
889      *
890      * This will return a line from the input buffer (if any exist). Otherwise,
891      * it will ask the user for input.
892      *
893      * If readline is enabled, this delegates to readline. Otherwise, it's an
894      * ugly `fgets` call.
895      *
896      * @return string One line of user input
897      */
898     protected function readline()
899     {
900         if (!empty($this->inputBuffer)) {
901             $line = array_shift($this->inputBuffer);
902             if (!$line instanceof SilentInput) {
903                 $this->output->writeln(sprintf('<aside>%s %s</aside>', static::REPLAY, OutputFormatter::escape($line)));
904             }
905
906             return $line;
907         }
908
909         if ($bracketedPaste = $this->config->useBracketedPaste()) {
910             printf("\e[?2004h"); // Enable bracketed paste
911         }
912
913         $line = $this->readline->readline($this->getPrompt());
914
915         if ($bracketedPaste) {
916             printf("\e[?2004l"); // ... and disable it again
917         }
918
919         return $line;
920     }
921
922     /**
923      * Get the shell output header.
924      *
925      * @return string
926      */
927     protected function getHeader()
928     {
929         return sprintf('<aside>%s by Justin Hileman</aside>', $this->getVersion());
930     }
931
932     /**
933      * Get the current version of Psy Shell.
934      *
935      * @return string
936      */
937     public function getVersion()
938     {
939         $separator = $this->config->useUnicode() ? '—' : '-';
940
941         return sprintf('Psy Shell %s (PHP %s %s %s)', self::VERSION, phpversion(), $separator, php_sapi_name());
942     }
943
944     /**
945      * Get a PHP manual database instance.
946      *
947      * @return \PDO|null
948      */
949     public function getManualDb()
950     {
951         return $this->config->getManualDb();
952     }
953
954     /**
955      * Autocomplete variable names.
956      *
957      * This is used by `readline` for tab completion.
958      *
959      * @param string $text
960      *
961      * @return mixed Array possible completions for the given input, if any
962      */
963     protected function autocomplete($text)
964     {
965         $info = readline_info();
966         // $line = substr($info['line_buffer'], 0, $info['end']);
967
968         // Check whether there's a command for this
969         // $words = explode(' ', $line);
970         // $firstWord = reset($words);
971
972         // check whether this is a variable...
973         $firstChar = substr($info['line_buffer'], max(0, $info['end'] - strlen($text) - 1), 1);
974         if ($firstChar === '$') {
975             return $this->getScopeVariableNames();
976         }
977     }
978
979     /**
980      * Initialize tab completion matchers.
981      *
982      * If tab completion is enabled this adds tab completion matchers to the
983      * auto completer and sets context if needed.
984      */
985     protected function initializeTabCompletion()
986     {
987         // auto completer needs shell to be linked to configuration because of the context aware matchers
988         if ($this->config->getTabCompletion()) {
989             $this->completion = $this->config->getAutoCompleter();
990             $this->addTabCompletionMatchers($this->config->getTabCompletionMatchers());
991             foreach ($this->getTabCompletionMatchers() as $matcher) {
992                 if ($matcher instanceof ContextAware) {
993                     $matcher->setContext($this->context);
994                 }
995                 $this->completion->addMatcher($matcher);
996             }
997             $this->completion->activate();
998         }
999     }
1000
1001     /**
1002      * @todo Implement self-update
1003      * @todo Implement prompt to start update
1004      *
1005      * @return void|string
1006      */
1007     protected function writeVersionInfo()
1008     {
1009         if (PHP_SAPI !== 'cli') {
1010             return;
1011         }
1012
1013         try {
1014             $client = $this->config->getChecker();
1015             if (!$client->isLatest()) {
1016                 $this->output->writeln(sprintf('New version is available (current: %s, latest: %s)', self::VERSION, $client->getLatest()));
1017             }
1018         } catch (\InvalidArgumentException $e) {
1019             $this->output->writeln($e->getMessage());
1020         }
1021     }
1022
1023     /**
1024      * Write a startup message if set.
1025      */
1026     protected function writeStartupMessage()
1027     {
1028         $message = $this->config->getStartupMessage();
1029         if ($message !== null && $message !== '') {
1030             $this->output->writeln($message);
1031         }
1032     }
1033 }