edbef1fcde81c0dc1ab29cab48e680a69465fb63
[yaffs-website] / vendor / psy / psysh / src / functions.php
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\VersionUpdater\GitHubChecker;
15 use Symfony\Component\Console\Input\ArgvInput;
16 use Symfony\Component\Console\Input\InputArgument;
17 use Symfony\Component\Console\Input\InputDefinition;
18 use Symfony\Component\Console\Input\InputOption;
19 use XdgBaseDir\Xdg;
20
21 if (!function_exists('Psy\sh')) {
22     /**
23      * Command to return the eval-able code to startup PsySH.
24      *
25      *     eval(\Psy\sh());
26      *
27      * @return string
28      */
29     function sh()
30     {
31         return 'extract(\Psy\debug(get_defined_vars(), isset($this) ? $this : null));';
32     }
33 }
34
35 if (!function_exists('Psy\debug')) {
36     /**
37      * Invoke a Psy Shell from the current context.
38      *
39      * For example:
40      *
41      *     foreach ($items as $item) {
42      *         \Psy\debug(get_defined_vars());
43      *     }
44      *
45      * If you would like your shell interaction to affect the state of the
46      * current context, you can extract() the values returned from this call:
47      *
48      *     foreach ($items as $item) {
49      *         extract(\Psy\debug(get_defined_vars()));
50      *         var_dump($item); // will be whatever you set $item to in Psy Shell
51      *     }
52      *
53      * Optionally, supply an object as the `$boundObject` parameter. This
54      * determines the value `$this` will have in the shell, and sets up class
55      * scope so that private and protected members are accessible:
56      *
57      *     class Foo {
58      *         function bar() {
59      *             \Psy\debug(get_defined_vars(), $this);
60      *         }
61      *     }
62      *
63      * @param array  $vars        Scope variables from the calling context (default: array())
64      * @param object $boundObject Bound object ($this) value for the shell
65      *
66      * @return array Scope variables from the debugger session
67      */
68     function debug(array $vars = [], $boundObject = null)
69     {
70         echo PHP_EOL;
71
72         $sh = new Shell();
73         $sh->setScopeVariables($vars);
74
75         // Show a couple of lines of call context for the debug session.
76         //
77         // @todo come up with a better way of doing this which doesn't involve injecting input :-P
78         if ($sh->has('whereami')) {
79             $sh->addInput('whereami -n2', true);
80         }
81
82         if ($boundObject !== null) {
83             $sh->setBoundObject($boundObject);
84         }
85
86         $sh->run();
87
88         return $sh->getScopeVariables(false);
89     }
90 }
91
92 if (!function_exists('Psy\info')) {
93     /**
94      * Get a bunch of debugging info about the current PsySH environment and
95      * configuration.
96      *
97      * If a Configuration param is passed, that configuration is stored and
98      * used for the current shell session, and no debugging info is returned.
99      *
100      * @param Configuration|null $config
101      *
102      * @return array|null
103      */
104     function info(Configuration $config = null)
105     {
106         static $lastConfig;
107         if ($config !== null) {
108             $lastConfig = $config;
109
110             return;
111         }
112
113         $xdg = new Xdg();
114         $home = rtrim(str_replace('\\', '/', $xdg->getHomeDir()), '/');
115         $homePattern = '#^' . preg_quote($home, '#') . '/#';
116
117         $prettyPath = function ($path) use ($homePattern) {
118             if (is_string($path)) {
119                 return preg_replace($homePattern, '~/', $path);
120             } else {
121                 return $path;
122             }
123         };
124
125         $config = $lastConfig ?: new Configuration();
126
127         $core = [
128             'PsySH version'       => Shell::VERSION,
129             'PHP version'         => PHP_VERSION,
130             'OS'                  => PHP_OS,
131             'default includes'    => $config->getDefaultIncludes(),
132             'require semicolons'  => $config->requireSemicolons(),
133             'error logging level' => $config->errorLoggingLevel(),
134             'config file'         => [
135                 'default config file' => $prettyPath($config->getConfigFile()),
136                 'local config file'   => $prettyPath($config->getLocalConfigFile()),
137                 'PSYSH_CONFIG env'    => $prettyPath(getenv('PSYSH_CONFIG')),
138             ],
139             // 'config dir'  => $config->getConfigDir(),
140             // 'data dir'    => $config->getDataDir(),
141             // 'runtime dir' => $config->getRuntimeDir(),
142         ];
143
144         // Use an explicit, fresh update check here, rather than relying on whatever is in $config.
145         $checker = new GitHubChecker();
146         $updateAvailable = null;
147         $latest = null;
148         try {
149             $updateAvailable = !$checker->isLatest();
150             $latest = $checker->getLatest();
151         } catch (\Exception $e) {
152         }
153
154         $updates = [
155             'update available'       => $updateAvailable,
156             'latest release version' => $latest,
157             'update check interval'  => $config->getUpdateCheck(),
158             'update cache file'      => $prettyPath($config->getUpdateCheckCacheFile()),
159         ];
160
161         if ($config->hasReadline()) {
162             $info = readline_info();
163
164             $readline = [
165                 'readline available' => true,
166                 'readline enabled'   => $config->useReadline(),
167                 'readline service'   => get_class($config->getReadline()),
168             ];
169
170             if (isset($info['library_version'])) {
171                 $readline['readline library'] = $info['library_version'];
172             }
173
174             if (isset($info['readline_name']) && $info['readline_name'] !== '') {
175                 $readline['readline name'] = $info['readline_name'];
176             }
177         } else {
178             $readline = [
179                 'readline available' => false,
180             ];
181         }
182
183         $pcntl = [
184             'pcntl available' => function_exists('pcntl_signal'),
185             'posix available' => function_exists('posix_getpid'),
186         ];
187
188         $disabledFuncs = array_map('trim', explode(',', ini_get('disable_functions')));
189         if (in_array('pcntl_signal', $disabledFuncs) || in_array('pcntl_fork', $disabledFuncs)) {
190             $pcntl['pcntl disabled'] = true;
191         }
192
193         $history = [
194             'history file'     => $prettyPath($config->getHistoryFile()),
195             'history size'     => $config->getHistorySize(),
196             'erase duplicates' => $config->getEraseDuplicates(),
197         ];
198
199         $docs = [
200             'manual db file'   => $prettyPath($config->getManualDbFile()),
201             'sqlite available' => true,
202         ];
203
204         try {
205             if ($db = $config->getManualDb()) {
206                 if ($q = $db->query('SELECT * FROM meta;')) {
207                     $q->setFetchMode(\PDO::FETCH_KEY_PAIR);
208                     $meta = $q->fetchAll();
209
210                     foreach ($meta as $key => $val) {
211                         switch ($key) {
212                             case 'built_at':
213                                 $d = new \DateTime('@' . $val);
214                                 $val = $d->format(\DateTime::RFC2822);
215                                 break;
216                         }
217                         $key = 'db ' . str_replace('_', ' ', $key);
218                         $docs[$key] = $val;
219                     }
220                 } else {
221                     $docs['db schema'] = '0.1.0';
222                 }
223             }
224         } catch (Exception\RuntimeException $e) {
225             if ($e->getMessage() === 'SQLite PDO driver not found') {
226                 $docs['sqlite available'] = false;
227             } else {
228                 throw $e;
229             }
230         }
231
232         $autocomplete = [
233             'tab completion enabled' => $config->useTabCompletion(),
234             'custom matchers'        => array_map('get_class', $config->getTabCompletionMatchers()),
235             'bracketed paste'        => $config->useBracketedPaste(),
236         ];
237
238         // Shenanigans, but totally justified.
239         if ($shell = Sudo::fetchProperty($config, 'shell')) {
240             $core['loop listeners'] = array_map('get_class', Sudo::fetchProperty($shell, 'loopListeners'));
241             $core['commands']       = array_map('get_class', $shell->all());
242
243             $autocomplete['custom matchers'] = array_map('get_class', Sudo::fetchProperty($shell, 'matchers'));
244         }
245
246         // @todo Show Presenter / custom casters.
247
248         return array_merge($core, compact('updates', 'pcntl', 'readline', 'history', 'docs', 'autocomplete'));
249     }
250 }
251
252 if (!function_exists('Psy\bin')) {
253     /**
254      * `psysh` command line executable.
255      *
256      * @return \Closure
257      */
258     function bin()
259     {
260         return function () {
261             $usageException = null;
262
263             $input = new ArgvInput();
264             try {
265                 $input->bind(new InputDefinition([
266                     new InputOption('help',     'h',  InputOption::VALUE_NONE),
267                     new InputOption('config',   'c',  InputOption::VALUE_REQUIRED),
268                     new InputOption('version',  'v',  InputOption::VALUE_NONE),
269                     new InputOption('cwd',      null, InputOption::VALUE_REQUIRED),
270                     new InputOption('color',    null, InputOption::VALUE_NONE),
271                     new InputOption('no-color', null, InputOption::VALUE_NONE),
272
273                     new InputArgument('include', InputArgument::IS_ARRAY),
274                 ]));
275             } catch (\RuntimeException $e) {
276                 $usageException = $e;
277             }
278
279             $config = [];
280
281             // Handle --config
282             if ($configFile = $input->getOption('config')) {
283                 $config['configFile'] = $configFile;
284             }
285
286             // Handle --color and --no-color
287             if ($input->getOption('color') && $input->getOption('no-color')) {
288                 $usageException = new \RuntimeException('Using both "--color" and "--no-color" options is invalid');
289             } elseif ($input->getOption('color')) {
290                 $config['colorMode'] = Configuration::COLOR_MODE_FORCED;
291             } elseif ($input->getOption('no-color')) {
292                 $config['colorMode'] = Configuration::COLOR_MODE_DISABLED;
293             }
294
295             $shell = new Shell(new Configuration($config));
296
297             // Handle --help
298             if ($usageException !== null || $input->getOption('help')) {
299                 if ($usageException !== null) {
300                     echo $usageException->getMessage() . PHP_EOL . PHP_EOL;
301                 }
302
303                 $version = $shell->getVersion();
304                 $name    = basename(reset($_SERVER['argv']));
305                 echo <<<EOL
306 $version
307
308 Usage:
309   $name [--version] [--help] [files...]
310
311 Options:
312   --help     -h Display this help message.
313   --config   -c Use an alternate PsySH config file location.
314   --cwd         Use an alternate working directory.
315   --version  -v Display the PsySH version.
316   --color       Force colors in output.
317   --no-color    Disable colors in output.
318
319 EOL;
320                 exit($usageException === null ? 0 : 1);
321             }
322
323             // Handle --version
324             if ($input->getOption('version')) {
325                 echo $shell->getVersion() . PHP_EOL;
326                 exit(0);
327             }
328
329             // Pass additional arguments to Shell as 'includes'
330             $shell->setIncludes($input->getArgument('include'));
331
332             try {
333                 // And go!
334                 $shell->run();
335             } catch (\Exception $e) {
336                 echo $e->getMessage() . PHP_EOL;
337
338                 // @todo this triggers the "exited unexpectedly" logic in the
339                 // ForkingLoop, so we can't exit(1) after starting the shell...
340                 // fix this :)
341
342                 // exit(1);
343             }
344         };
345     }
346 }