Chris@0: setScopeVariables($vars); Chris@0: Chris@0: // Show a couple of lines of call context for the debug session. Chris@0: // Chris@0: // @todo come up with a better way of doing this which doesn't involve injecting input :-P Chris@0: if ($sh->has('whereami')) { Chris@0: $sh->addInput('whereami -n2', true); Chris@0: } Chris@0: Chris@0: if ($boundObject !== null) { Chris@0: $sh->setBoundObject($boundObject); Chris@0: } Chris@0: Chris@0: $sh->run(); Chris@0: Chris@0: return $sh->getScopeVariables(false); Chris@0: } Chris@0: } Chris@0: Chris@0: if (!function_exists('Psy\info')) { Chris@0: /** Chris@0: * Get a bunch of debugging info about the current PsySH environment and Chris@0: * configuration. Chris@0: * Chris@0: * If a Configuration param is passed, that configuration is stored and Chris@0: * used for the current shell session, and no debugging info is returned. Chris@0: * Chris@0: * @param Configuration|null $config Chris@0: * Chris@0: * @return array|null Chris@0: */ Chris@0: function info(Configuration $config = null) Chris@0: { Chris@0: static $lastConfig; Chris@0: if ($config !== null) { Chris@0: $lastConfig = $config; Chris@0: Chris@0: return; Chris@0: } Chris@0: Chris@0: $xdg = new Xdg(); Chris@0: $home = rtrim(str_replace('\\', '/', $xdg->getHomeDir()), '/'); Chris@0: $homePattern = '#^' . preg_quote($home, '#') . '/#'; Chris@0: Chris@0: $prettyPath = function ($path) use ($homePattern) { Chris@0: if (is_string($path)) { Chris@0: return preg_replace($homePattern, '~/', $path); Chris@0: } else { Chris@0: return $path; Chris@0: } Chris@0: }; Chris@0: Chris@0: $config = $lastConfig ?: new Configuration(); Chris@0: Chris@0: $core = array( Chris@0: 'PsySH version' => Shell::VERSION, Chris@0: 'PHP version' => PHP_VERSION, Chris@0: 'default includes' => $config->getDefaultIncludes(), Chris@0: 'require semicolons' => $config->requireSemicolons(), Chris@0: 'error logging level' => $config->errorLoggingLevel(), Chris@0: 'config file' => array( Chris@0: 'default config file' => $prettyPath($config->getConfigFile()), Chris@0: 'local config file' => $prettyPath($config->getLocalConfigFile()), Chris@0: 'PSYSH_CONFIG env' => $prettyPath(getenv('PSYSH_CONFIG')), Chris@0: ), Chris@0: // 'config dir' => $config->getConfigDir(), Chris@0: // 'data dir' => $config->getDataDir(), Chris@0: // 'runtime dir' => $config->getRuntimeDir(), Chris@0: ); Chris@0: Chris@0: // Use an explicit, fresh update check here, rather than relying on whatever is in $config. Chris@0: $checker = new GitHubChecker(); Chris@0: $updateAvailable = null; Chris@0: $latest = null; Chris@0: try { Chris@0: $updateAvailable = !$checker->isLatest(); Chris@0: $latest = $checker->getLatest(); Chris@0: } catch (\Exception $e) { Chris@0: } Chris@0: Chris@0: $updates = array( Chris@0: 'update available' => $updateAvailable, Chris@0: 'latest release version' => $latest, Chris@0: 'update check interval' => $config->getUpdateCheck(), Chris@0: 'update cache file' => $prettyPath($config->getUpdateCheckCacheFile()), Chris@0: ); Chris@0: Chris@0: if ($config->hasReadline()) { Chris@0: $info = readline_info(); Chris@0: Chris@0: $readline = array( Chris@0: 'readline available' => true, Chris@0: 'readline enabled' => $config->useReadline(), Chris@0: 'readline service' => get_class($config->getReadline()), Chris@0: ); Chris@0: Chris@0: if (isset($info['library_version'])) { Chris@0: $readline['readline library'] = $info['library_version']; Chris@0: } Chris@0: Chris@0: if (isset($info['readline_name']) && $info['readline_name'] !== '') { Chris@0: $readline['readline name'] = $info['readline_name']; Chris@0: } Chris@0: } else { Chris@0: $readline = array( Chris@0: 'readline available' => false, Chris@0: ); Chris@0: } Chris@0: Chris@0: $pcntl = array( Chris@0: 'pcntl available' => function_exists('pcntl_signal'), Chris@0: 'posix available' => function_exists('posix_getpid'), Chris@0: ); Chris@0: Chris@0: $disabledFuncs = array_map('trim', explode(',', ini_get('disable_functions'))); Chris@0: if (in_array('pcntl_signal', $disabledFuncs) || in_array('pcntl_fork', $disabledFuncs)) { Chris@0: $pcntl['pcntl disabled'] = true; Chris@0: } Chris@0: Chris@0: $history = array( Chris@0: 'history file' => $prettyPath($config->getHistoryFile()), Chris@0: 'history size' => $config->getHistorySize(), Chris@0: 'erase duplicates' => $config->getEraseDuplicates(), Chris@0: ); Chris@0: Chris@0: $docs = array( Chris@0: 'manual db file' => $prettyPath($config->getManualDbFile()), Chris@0: 'sqlite available' => true, Chris@0: ); Chris@0: Chris@0: try { Chris@0: if ($db = $config->getManualDb()) { Chris@0: if ($q = $db->query('SELECT * FROM meta;')) { Chris@0: $q->setFetchMode(\PDO::FETCH_KEY_PAIR); Chris@0: $meta = $q->fetchAll(); Chris@0: Chris@0: foreach ($meta as $key => $val) { Chris@0: switch ($key) { Chris@0: case 'built_at': Chris@0: $d = new \DateTime('@' . $val); Chris@0: $val = $d->format(\DateTime::RFC2822); Chris@0: break; Chris@0: } Chris@0: $key = 'db ' . str_replace('_', ' ', $key); Chris@0: $docs[$key] = $val; Chris@0: } Chris@0: } else { Chris@0: $docs['db schema'] = '0.1.0'; Chris@0: } Chris@0: } Chris@0: } catch (Exception\RuntimeException $e) { Chris@0: if ($e->getMessage() === 'SQLite PDO driver not found') { Chris@0: $docs['sqlite available'] = false; Chris@0: } else { Chris@0: throw $e; Chris@0: } Chris@0: } Chris@0: Chris@0: $autocomplete = array( Chris@0: 'tab completion enabled' => $config->getTabCompletion(), Chris@0: 'custom matchers' => array_map('get_class', $config->getTabCompletionMatchers()), Chris@0: 'bracketed paste' => $config->useBracketedPaste(), Chris@0: ); Chris@0: Chris@0: return array_merge($core, compact('updates', 'pcntl', 'readline', 'history', 'docs', 'autocomplete')); Chris@0: } Chris@0: } Chris@0: Chris@0: if (!function_exists('Psy\bin')) { Chris@0: /** Chris@0: * `psysh` command line executable. Chris@0: * Chris@0: * @return Closure Chris@0: */ Chris@0: function bin() Chris@0: { Chris@0: return function () { Chris@0: $usageException = null; Chris@0: Chris@0: $input = new ArgvInput(); Chris@0: try { Chris@0: $input->bind(new InputDefinition(array( Chris@0: new InputOption('help', 'h', InputOption::VALUE_NONE), Chris@0: new InputOption('config', 'c', InputOption::VALUE_REQUIRED), Chris@0: new InputOption('version', 'v', InputOption::VALUE_NONE), Chris@0: new InputOption('cwd', null, InputOption::VALUE_REQUIRED), Chris@0: new InputOption('color', null, InputOption::VALUE_NONE), Chris@0: new InputOption('no-color', null, InputOption::VALUE_NONE), Chris@0: Chris@0: new InputArgument('include', InputArgument::IS_ARRAY), Chris@0: ))); Chris@0: } catch (\RuntimeException $e) { Chris@0: $usageException = $e; Chris@0: } Chris@0: Chris@0: $config = array(); Chris@0: Chris@0: // Handle --config Chris@0: if ($configFile = $input->getOption('config')) { Chris@0: $config['configFile'] = $configFile; Chris@0: } Chris@0: Chris@0: // Handle --color and --no-color Chris@0: if ($input->getOption('color') && $input->getOption('no-color')) { Chris@0: $usageException = new \RuntimeException('Using both "--color" and "--no-color" options is invalid.'); Chris@0: } elseif ($input->getOption('color')) { Chris@0: $config['colorMode'] = Configuration::COLOR_MODE_FORCED; Chris@0: } elseif ($input->getOption('no-color')) { Chris@0: $config['colorMode'] = Configuration::COLOR_MODE_DISABLED; Chris@0: } Chris@0: Chris@0: $shell = new Shell(new Configuration($config)); Chris@0: Chris@0: // Handle --help Chris@0: if ($usageException !== null || $input->getOption('help')) { Chris@0: if ($usageException !== null) { Chris@0: echo $usageException->getMessage() . PHP_EOL . PHP_EOL; Chris@0: } Chris@0: Chris@0: $version = $shell->getVersion(); Chris@0: $name = basename(reset($_SERVER['argv'])); Chris@0: echo <<getOption('version')) { Chris@0: echo $shell->getVersion() . PHP_EOL; Chris@0: exit(0); Chris@0: } Chris@0: Chris@0: // Pass additional arguments to Shell as 'includes' Chris@0: $shell->setIncludes($input->getArgument('include')); Chris@0: Chris@0: try { Chris@0: // And go! Chris@0: $shell->run(); Chris@0: } catch (Exception $e) { Chris@0: echo $e->getMessage() . PHP_EOL; Chris@0: Chris@0: // @todo this triggers the "exited unexpectedly" logic in the Chris@0: // ForkingLoop, so we can't exit(1) after starting the shell... Chris@0: // fix this :) Chris@0: Chris@0: // exit(1); Chris@0: } Chris@0: }; Chris@0: } Chris@0: }