comparison vendor/psy/psysh/src/Shell.php @ 17:129ea1e6d783

Update, including to Drupal core 8.6.10
author Chris Cannam
date Thu, 28 Feb 2019 13:21:36 +0000
parents c2387f117808
children
comparison
equal deleted inserted replaced
16:c2387f117808 17:129ea1e6d783
45 * 45 *
46 * @author Justin Hileman <justin@justinhileman.info> 46 * @author Justin Hileman <justin@justinhileman.info>
47 */ 47 */
48 class Shell extends Application 48 class Shell extends Application
49 { 49 {
50 const VERSION = 'v0.9.6'; 50 const VERSION = 'v0.9.9';
51 51
52 const PROMPT = '>>> '; 52 const PROMPT = '>>> ';
53 const BUFF_PROMPT = '... '; 53 const BUFF_PROMPT = '... ';
54 const REPLAY = '--> '; 54 const REPLAY = '--> ';
55 const RETVAL = '=> '; 55 const RETVAL = '=> ';
71 private $prompt; 71 private $prompt;
72 private $loopListeners; 72 private $loopListeners;
73 private $autoCompleter; 73 private $autoCompleter;
74 private $matchers = []; 74 private $matchers = [];
75 private $commandsMatcher; 75 private $commandsMatcher;
76 private $lastExecSuccess = true;
76 77
77 /** 78 /**
78 * Create a new Psy Shell. 79 * Create a new Psy Shell.
79 * 80 *
80 * @param Configuration $config (default: null) 81 * @param Configuration $config (default: null)
107 * or to simply autoload the library. 108 * or to simply autoload the library.
108 */ 109 */
109 public static function isIncluded(array $trace) 110 public static function isIncluded(array $trace)
110 { 111 {
111 return isset($trace[0]['function']) && 112 return isset($trace[0]['function']) &&
112 in_array($trace[0]['function'], ['require', 'include', 'require_once', 'include_once']); 113 \in_array($trace[0]['function'], ['require', 'include', 'require_once', 'include_once']);
113 } 114 }
114 115
115 /** 116 /**
116 * Invoke a Psy Shell from the current context. 117 * Invoke a Psy Shell from the current context.
117 * 118 *
232 /** 233 /**
233 * @deprecated Nothing should use this anymore 234 * @deprecated Nothing should use this anymore
234 */ 235 */
235 protected function getTabCompletionMatchers() 236 protected function getTabCompletionMatchers()
236 { 237 {
237 @trigger_error('getTabCompletionMatchers is no longer used', E_USER_DEPRECATED); 238 @\trigger_error('getTabCompletionMatchers is no longer used', E_USER_DEPRECATED);
238 } 239 }
239 240
240 /** 241 /**
241 * Gets the default command loop listeners. 242 * Gets the default command loop listeners.
242 * 243 *
262 * 263 *
263 * @param array $matchers 264 * @param array $matchers
264 */ 265 */
265 public function addMatchers(array $matchers) 266 public function addMatchers(array $matchers)
266 { 267 {
267 $this->matchers = array_merge($this->matchers, $matchers); 268 $this->matchers = \array_merge($this->matchers, $matchers);
268 269
269 if (isset($this->autoCompleter)) { 270 if (isset($this->autoCompleter)) {
270 $this->addMatchersToAutoCompleter($matchers); 271 $this->addMatchersToAutoCompleter($matchers);
271 } 272 }
272 } 273 }
390 throw new BreakException('Ctrl+D'); 391 throw new BreakException('Ctrl+D');
391 } 392 }
392 } 393 }
393 394
394 // handle empty input 395 // handle empty input
395 if (trim($input) === '' && !$this->codeBufferOpen) { 396 if (\trim($input) === '' && !$this->codeBufferOpen) {
396 continue; 397 continue;
397 } 398 }
398 399
399 $input = $this->onInput($input); 400 $input = $this->onInput($input);
400 401
422 if (!$this->hasCode()) { 423 if (!$this->hasCode()) {
423 return; 424 return;
424 } 425 }
425 426
426 $code = $this->codeBuffer; 427 $code = $this->codeBuffer;
427 array_push($code, $input); 428 \array_push($code, $input);
428 $tokens = @token_get_all('<?php ' . implode("\n", $code)); 429 $tokens = @\token_get_all('<?php ' . \implode("\n", $code));
429 $last = array_pop($tokens); 430 $last = \array_pop($tokens);
430 431
431 return $last === '"' || $last === '`' || 432 return $last === '"' || $last === '`' ||
432 (is_array($last) && in_array($last[0], [T_ENCAPSED_AND_WHITESPACE, T_START_HEREDOC, T_COMMENT])); 433 (\is_array($last) && \in_array($last[0], [T_ENCAPSED_AND_WHITESPACE, T_START_HEREDOC, T_COMMENT]));
433 } 434 }
434 435
435 /** 436 /**
436 * Run execution loop listeners before the shell session. 437 * Run execution loop listeners before the shell session.
437 */ 438 */
557 558
558 return $vars; 559 return $vars;
559 } 560 }
560 561
561 /** 562 /**
563 * Return the set of variables currently in scope which differ from the
564 * values passed as $currentVars.
565 *
566 * This is used inside the Execution Loop Closure to pick up scope variable
567 * changes made by commands while the loop is running.
568 *
569 * @param array $currentVars
570 *
571 * @return array Associative array of scope variables which differ from $currentVars
572 */
573 public function getScopeVariablesDiff(array $currentVars)
574 {
575 $newVars = [];
576
577 foreach ($this->getScopeVariables(false) as $key => $value) {
578 if (!array_key_exists($key, $currentVars) || $currentVars[$key] !== $value) {
579 $newVars[$key] = $value;
580 }
581 }
582
583 return $newVars;
584 }
585
586 /**
562 * Get the set of unused command-scope variable names. 587 * Get the set of unused command-scope variable names.
563 * 588 *
564 * @return array Array of unused variable names 589 * @return array Array of unused variable names
565 */ 590 */
566 public function getUnusedCommandScopeVariableNames() 591 public function getUnusedCommandScopeVariableNames()
573 * 598 *
574 * @return array Array of variable names 599 * @return array Array of variable names
575 */ 600 */
576 public function getScopeVariableNames() 601 public function getScopeVariableNames()
577 { 602 {
578 return array_keys($this->context->getAll()); 603 return \array_keys($this->context->getAll());
579 } 604 }
580 605
581 /** 606 /**
582 * Get a scope variable value by name. 607 * Get a scope variable value by name.
583 * 608 *
645 * 670 *
646 * @return array 671 * @return array
647 */ 672 */
648 public function getIncludes() 673 public function getIncludes()
649 { 674 {
650 return array_merge($this->config->getDefaultIncludes(), $this->includes); 675 return \array_merge($this->config->getDefaultIncludes(), $this->includes);
651 } 676 }
652 677
653 /** 678 /**
654 * Check whether this shell's code buffer contains code. 679 * Check whether this shell's code buffer contains code.
655 * 680 *
680 */ 705 */
681 public function addCode($code, $silent = false) 706 public function addCode($code, $silent = false)
682 { 707 {
683 try { 708 try {
684 // Code lines ending in \ keep the buffer open 709 // Code lines ending in \ keep the buffer open
685 if (substr(rtrim($code), -1) === '\\') { 710 if (\substr(\rtrim($code), -1) === '\\') {
686 $this->codeBufferOpen = true; 711 $this->codeBufferOpen = true;
687 $code = substr(rtrim($code), 0, -1); 712 $code = \substr(\rtrim($code), 0, -1);
688 } else { 713 } else {
689 $this->codeBufferOpen = false; 714 $this->codeBufferOpen = false;
690 } 715 }
691 716
692 $this->codeBuffer[] = $silent ? new SilentInput($code) : $code; 717 $this->codeBuffer[] = $silent ? new SilentInput($code) : $code;
764 789
765 if (empty($command)) { 790 if (empty($command)) {
766 throw new \InvalidArgumentException('Command not found: ' . $input); 791 throw new \InvalidArgumentException('Command not found: ' . $input);
767 } 792 }
768 793
769 $input = new ShellInput(str_replace('\\', '\\\\', rtrim($input, " \t\n\r\0\x0B;"))); 794 $input = new ShellInput(\str_replace('\\', '\\\\', \rtrim($input, " \t\n\r\0\x0B;")));
770 795
771 if ($input->hasParameterOption(['--help', '-h'])) { 796 if ($input->hasParameterOption(['--help', '-h'])) {
772 $helpCommand = $this->get('help'); 797 $helpCommand = $this->get('help');
773 $helpCommand->setCommand($command); 798 $helpCommand->setCommand($command);
774 799
833 858
834 if (empty($this->codeStack)) { 859 if (empty($this->codeStack)) {
835 return; 860 return;
836 } 861 }
837 862
838 list($codeBuffer, $codeBufferOpen, $code) = array_pop($this->codeStack); 863 list($codeBuffer, $codeBufferOpen, $code) = \array_pop($this->codeStack);
839 864
840 $this->codeBuffer = $codeBuffer; 865 $this->codeBuffer = $codeBuffer;
841 $this->codeBufferOpen = $codeBufferOpen; 866 $this->codeBufferOpen = $codeBufferOpen;
842 $this->code = $code; 867 $this->code = $code;
843 } 868 }
859 if ($line instanceof SilentInput) { 884 if ($line instanceof SilentInput) {
860 return; 885 return;
861 } 886 }
862 887
863 // Skip empty lines and lines starting with a space 888 // Skip empty lines and lines starting with a space
864 if (trim($line) !== '' && substr($line, 0, 1) !== ' ') { 889 if (\trim($line) !== '' && \substr($line, 0, 1) !== ' ') {
865 $this->readline->addHistory($line); 890 $this->readline->addHistory($line);
866 } 891 }
867 } 892 }
868 893
869 /** 894 /**
870 * Filter silent input from code buffer, write the rest to readline history. 895 * Filter silent input from code buffer, write the rest to readline history.
871 */ 896 */
872 private function addCodeBufferToHistory() 897 private function addCodeBufferToHistory()
873 { 898 {
874 $codeBuffer = array_filter($this->codeBuffer, function ($line) { 899 $codeBuffer = \array_filter($this->codeBuffer, function ($line) {
875 return !$line instanceof SilentInput; 900 return !$line instanceof SilentInput;
876 }); 901 });
877 902
878 $this->addHistory(implode("\n", $codeBuffer)); 903 $this->addHistory(\implode("\n", $codeBuffer));
879 } 904 }
880 905
881 /** 906 /**
882 * Get the current evaluation scope namespace. 907 * Get the current evaluation scope namespace.
883 * 908 *
886 * @return string Current code namespace 911 * @return string Current code namespace
887 */ 912 */
888 public function getNamespace() 913 public function getNamespace()
889 { 914 {
890 if ($namespace = $this->cleaner->getNamespace()) { 915 if ($namespace = $this->cleaner->getNamespace()) {
891 return implode('\\', $namespace); 916 return \implode('\\', $namespace);
892 } 917 }
893 } 918 }
894 919
895 /** 920 /**
896 * Write a string to stdout. 921 * Write a string to stdout.
905 $isCleaning = $phase & PHP_OUTPUT_HANDLER_CLEAN; 930 $isCleaning = $phase & PHP_OUTPUT_HANDLER_CLEAN;
906 931
907 // Incremental flush 932 // Incremental flush
908 if ($out !== '' && !$isCleaning) { 933 if ($out !== '' && !$isCleaning) {
909 $this->output->write($out, false, ShellOutput::OUTPUT_RAW); 934 $this->output->write($out, false, ShellOutput::OUTPUT_RAW);
910 $this->outputWantsNewline = (substr($out, -1) !== "\n"); 935 $this->outputWantsNewline = (\substr($out, -1) !== "\n");
911 $this->stdoutBuffer .= $out; 936 $this->stdoutBuffer .= $out;
912 } 937 }
913 938
914 // Output buffering is done! 939 // Output buffering is done!
915 if ($phase & PHP_OUTPUT_HANDLER_END) { 940 if ($phase & PHP_OUTPUT_HANDLER_END) {
916 // Write an extra newline if stdout didn't end with one 941 // Write an extra newline if stdout didn't end with one
917 if ($this->outputWantsNewline) { 942 if ($this->outputWantsNewline) {
918 $this->output->writeln(sprintf('<aside>%s</aside>', $this->config->useUnicode() ? '⏎' : '\\n')); 943 $this->output->writeln(\sprintf('<aside>%s</aside>', $this->config->useUnicode() ? '⏎' : '\\n'));
919 $this->outputWantsNewline = false; 944 $this->outputWantsNewline = false;
920 } 945 }
921 946
922 // Save the stdout buffer as $__out 947 // Save the stdout buffer as $__out
923 if ($this->stdoutBuffer !== '') { 948 if ($this->stdoutBuffer !== '') {
937 * 962 *
938 * @param mixed $ret 963 * @param mixed $ret
939 */ 964 */
940 public function writeReturnValue($ret) 965 public function writeReturnValue($ret)
941 { 966 {
967 $this->lastExecSuccess = true;
968
942 if ($ret instanceof NoReturnValue) { 969 if ($ret instanceof NoReturnValue) {
943 return; 970 return;
944 } 971 }
945 972
946 $this->context->setReturnValue($ret); 973 $this->context->setReturnValue($ret);
947 $ret = $this->presentValue($ret); 974 $ret = $this->presentValue($ret);
948 $indent = str_repeat(' ', strlen(static::RETVAL)); 975 $indent = \str_repeat(' ', \strlen(static::RETVAL));
949 976
950 $this->output->writeln(static::RETVAL . str_replace(PHP_EOL, PHP_EOL . $indent, $ret)); 977 $this->output->writeln(static::RETVAL . \str_replace(PHP_EOL, PHP_EOL . $indent, $ret));
951 } 978 }
952 979
953 /** 980 /**
954 * Renders a caught Exception. 981 * Renders a caught Exception.
955 * 982 *
960 * 987 *
961 * @param \Exception $e An exception instance 988 * @param \Exception $e An exception instance
962 */ 989 */
963 public function writeException(\Exception $e) 990 public function writeException(\Exception $e)
964 { 991 {
992 $this->lastExecSuccess = false;
965 $this->context->setLastException($e); 993 $this->context->setLastException($e);
966 $this->output->writeln($this->formatException($e)); 994 $this->output->writeln($this->formatException($e));
967 $this->resetCodeBuffer(); 995 $this->resetCodeBuffer();
968 } 996 }
969 997
970 /** 998 /**
999 * Check whether the last exec was successful.
1000 *
1001 * Returns true if a return value was logged rather than an exception.
1002 *
1003 * @return bool
1004 */
1005 public function getLastExecSuccess()
1006 {
1007 return $this->lastExecSuccess;
1008 }
1009
1010 /**
971 * Helper for formatting an exception for writeException(). 1011 * Helper for formatting an exception for writeException().
972 * 1012 *
973 * @todo extract this to somewhere it makes more sense 1013 * @todo extract this to somewhere it makes more sense
974 * 1014 *
975 * @param \Exception $e 1015 * @param \Exception $e
979 public function formatException(\Exception $e) 1019 public function formatException(\Exception $e)
980 { 1020 {
981 $message = $e->getMessage(); 1021 $message = $e->getMessage();
982 if (!$e instanceof PsyException) { 1022 if (!$e instanceof PsyException) {
983 if ($message === '') { 1023 if ($message === '') {
984 $message = get_class($e); 1024 $message = \get_class($e);
985 } else { 1025 } else {
986 $message = sprintf('%s with message \'%s\'', get_class($e), $message); 1026 $message = \sprintf('%s with message \'%s\'', \get_class($e), $message);
987 } 1027 }
988 } 1028 }
989 1029
990 $message = preg_replace( 1030 $message = \preg_replace(
991 "#(\\w:)?(/\\w+)*/src/Execution(?:Loop)?Closure.php\(\d+\) : eval\(\)'d code#", 1031 "#(\\w:)?(/\\w+)*/src/Execution(?:Loop)?Closure.php\(\d+\) : eval\(\)'d code#",
992 "eval()'d code", 1032 "eval()'d code",
993 str_replace('\\', '/', $message) 1033 \str_replace('\\', '/', $message)
994 ); 1034 );
995 1035
996 $message = str_replace(" in eval()'d code", ' in Psy Shell code', $message); 1036 $message = \str_replace(" in eval()'d code", ' in Psy Shell code', $message);
997 1037
998 $severity = ($e instanceof \ErrorException) ? $this->getSeverity($e) : 'error'; 1038 $severity = ($e instanceof \ErrorException) ? $this->getSeverity($e) : 'error';
999 1039
1000 return sprintf('<%s>%s</%s>', $severity, OutputFormatter::escape($message), $severity); 1040 return \sprintf('<%s>%s</%s>', $severity, OutputFormatter::escape($message), $severity);
1001 } 1041 }
1002 1042
1003 /** 1043 /**
1004 * Helper for getting an output style for the given ErrorException's level. 1044 * Helper for getting an output style for the given ErrorException's level.
1005 * 1045 *
1008 * @return string 1048 * @return string
1009 */ 1049 */
1010 protected function getSeverity(\ErrorException $e) 1050 protected function getSeverity(\ErrorException $e)
1011 { 1051 {
1012 $severity = $e->getSeverity(); 1052 $severity = $e->getSeverity();
1013 if ($severity & error_reporting()) { 1053 if ($severity & \error_reporting()) {
1014 switch ($severity) { 1054 switch ($severity) {
1015 case E_WARNING: 1055 case E_WARNING:
1016 case E_NOTICE: 1056 case E_NOTICE:
1017 case E_CORE_WARNING: 1057 case E_CORE_WARNING:
1018 case E_COMPILE_WARNING: 1058 case E_COMPILE_WARNING:
1084 * @param string $errfile Filename 1124 * @param string $errfile Filename
1085 * @param int $errline Line number 1125 * @param int $errline Line number
1086 */ 1126 */
1087 public function handleError($errno, $errstr, $errfile, $errline) 1127 public function handleError($errno, $errstr, $errfile, $errline)
1088 { 1128 {
1089 if ($errno & error_reporting()) { 1129 if ($errno & \error_reporting()) {
1090 ErrorException::throwException($errno, $errstr, $errfile, $errline); 1130 ErrorException::throwException($errno, $errstr, $errfile, $errline);
1091 } elseif ($errno & $this->config->errorLoggingLevel()) { 1131 } elseif ($errno & $this->config->errorLoggingLevel()) {
1092 // log it and continue... 1132 // log it and continue...
1093 $this->writeException(new ErrorException($errstr, 0, $errno, $errfile, $errline)); 1133 $this->writeException(new ErrorException($errstr, 0, $errno, $errfile, $errline));
1094 } 1134 }
1130 * 1170 *
1131 * @return bool True if the shell has a command for the given input 1171 * @return bool True if the shell has a command for the given input
1132 */ 1172 */
1133 protected function hasCommand($input) 1173 protected function hasCommand($input)
1134 { 1174 {
1135 if (preg_match('/([^\s]+?)(?:\s|$)/A', ltrim($input), $match)) { 1175 if (\preg_match('/([^\s]+?)(?:\s|$)/A', \ltrim($input), $match)) {
1136 return $this->has($match[1]); 1176 return $this->has($match[1]);
1137 } 1177 }
1138 1178
1139 return false; 1179 return false;
1140 } 1180 }
1165 * @return string One line of user input 1205 * @return string One line of user input
1166 */ 1206 */
1167 protected function readline() 1207 protected function readline()
1168 { 1208 {
1169 if (!empty($this->inputBuffer)) { 1209 if (!empty($this->inputBuffer)) {
1170 $line = array_shift($this->inputBuffer); 1210 $line = \array_shift($this->inputBuffer);
1171 if (!$line instanceof SilentInput) { 1211 if (!$line instanceof SilentInput) {
1172 $this->output->writeln(sprintf('<aside>%s %s</aside>', static::REPLAY, OutputFormatter::escape($line))); 1212 $this->output->writeln(\sprintf('<aside>%s %s</aside>', static::REPLAY, OutputFormatter::escape($line)));
1173 } 1213 }
1174 1214
1175 return $line; 1215 return $line;
1176 } 1216 }
1177 1217
1178 if ($bracketedPaste = $this->config->useBracketedPaste()) { 1218 if ($bracketedPaste = $this->config->useBracketedPaste()) {
1179 printf("\e[?2004h"); // Enable bracketed paste 1219 \printf("\e[?2004h"); // Enable bracketed paste
1180 } 1220 }
1181 1221
1182 $line = $this->readline->readline($this->getPrompt()); 1222 $line = $this->readline->readline($this->getPrompt());
1183 1223
1184 if ($bracketedPaste) { 1224 if ($bracketedPaste) {
1185 printf("\e[?2004l"); // ... and disable it again 1225 \printf("\e[?2004l"); // ... and disable it again
1186 } 1226 }
1187 1227
1188 return $line; 1228 return $line;
1189 } 1229 }
1190 1230
1193 * 1233 *
1194 * @return string 1234 * @return string
1195 */ 1235 */
1196 protected function getHeader() 1236 protected function getHeader()
1197 { 1237 {
1198 return sprintf('<aside>%s by Justin Hileman</aside>', $this->getVersion()); 1238 return \sprintf('<aside>%s by Justin Hileman</aside>', $this->getVersion());
1199 } 1239 }
1200 1240
1201 /** 1241 /**
1202 * Get the current version of Psy Shell. 1242 * Get the current version of Psy Shell.
1203 * 1243 *
1205 */ 1245 */
1206 public function getVersion() 1246 public function getVersion()
1207 { 1247 {
1208 $separator = $this->config->useUnicode() ? '—' : '-'; 1248 $separator = $this->config->useUnicode() ? '—' : '-';
1209 1249
1210 return sprintf('Psy Shell %s (PHP %s %s %s)', self::VERSION, phpversion(), $separator, php_sapi_name()); 1250 return \sprintf('Psy Shell %s (PHP %s %s %s)', self::VERSION, PHP_VERSION, $separator, PHP_SAPI);
1211 } 1251 }
1212 1252
1213 /** 1253 /**
1214 * Get a PHP manual database instance. 1254 * Get a PHP manual database instance.
1215 * 1255 *
1223 /** 1263 /**
1224 * @deprecated Tab completion is provided by the AutoCompleter service 1264 * @deprecated Tab completion is provided by the AutoCompleter service
1225 */ 1265 */
1226 protected function autocomplete($text) 1266 protected function autocomplete($text)
1227 { 1267 {
1228 @trigger_error('Tab completion is provided by the AutoCompleter service', E_USER_DEPRECATED); 1268 @\trigger_error('Tab completion is provided by the AutoCompleter service', E_USER_DEPRECATED);
1229 } 1269 }
1230 1270
1231 /** 1271 /**
1232 * Initialize tab completion matchers. 1272 * Initialize tab completion matchers.
1233 * 1273 *
1278 } 1318 }
1279 1319
1280 try { 1320 try {
1281 $client = $this->config->getChecker(); 1321 $client = $this->config->getChecker();
1282 if (!$client->isLatest()) { 1322 if (!$client->isLatest()) {
1283 $this->output->writeln(sprintf('New version is available (current: %s, latest: %s)', self::VERSION, $client->getLatest())); 1323 $this->output->writeln(\sprintf('New version is available (current: %s, latest: %s)', self::VERSION, $client->getLatest()));
1284 } 1324 }
1285 } catch (\InvalidArgumentException $e) { 1325 } catch (\InvalidArgumentException $e) {
1286 $this->output->writeln($e->getMessage()); 1326 $this->output->writeln($e->getMessage());
1287 } 1327 }
1288 } 1328 }