'table', 'table.maxwidth' => 40, 'table.style' => 'box', ]; public function __construct(OutputInterface $output) { $this->output = $output; $this->options = $this->defaultOptions; } private function promptForCommand() { $prompt = sprintf("PDO:[%s%s]> ", $this->resource, $this->db?"":"?"); $input = readline($prompt); return $input; } private function parseCommand(string $input): array { $parsed = str_getcsv($input, ' ', '"'); $parsed = array_map([$this,"expand"], $parsed); $command = array_shift($parsed); return [ strtolower($command), $parsed ]; } private function expand(string $string): string { return $string; } public function run() { if ($this->running == true) return; $this->running = true; while ($this->running) { $input = $this->promptForCommand(); if (!trim($input)) { continue; } readline_add_history($input); if (str_starts_with($input, ".")) { [$cmd,$args] = $this->parseCommand($input); $this->handleCommand($cmd, $args); } else { $this->doQuery($input, []); } } } public function runCommands(array $commands) { foreach ($commands as $input) { if (str_starts_with($input, ".")) { [$cmd,$args] = $this->parseCommand($input); $this->handleCommand($cmd, $args); } else { $this->doQuery($input, []); } } } private function handleCommand(string $command, array $args) { switch ($command) { case '.select': $this->doSelectCommand($args); break; case '.set': $this->doSetCommand($args); break; case '.var': $this->doVarCommand($args); break; case '.query': $this->doQueryCommand($args); break; case '.help': $this->doHelpCommand($args); break; case '.quit': case '.exit': $this->running = false; break;; default: $this->output->writeln("Bad command. Try .help"); } } private function doHelpCommand(array $args) { $cmds = [ '.help' => "Show this help", '.select RES' => "Select the database resource to query", '.set [KEY [VALUE]]' => "Set a configuration value", '.var [NAME [VALUE]]' => "Set a variable, or show variable value", '.exit|.quit' => "Exit the shell", 'SQL' => "Run SQL against the database", '.query SQL [PARAM..]' => "Escape and run a query using ? as placeholder", ]; foreach ($cmds as $cmd=>$info) { $this->output->writeln(" {$cmd} - {$info}"); } } private function doSetCommand(array $args) { $varname = array_shift($args); $value = array_shift($args); if (empty($varname)) { foreach ($this->options as $var=>$value) { $this->output->writeln("{$var}: ".var_export($value,true).""); } return; } if ($value !== null) { if (!array_key_exists($varname, $this->options)) { $this->output->writeln("No such option {$varname}"); return; } $this->options[$varname] = $args; } else { $this->output->writeln(var_export($this->options[$varname]??null,true)); } } private function doSelectCommand(array $args) { $name = array_shift($args); $res = get_resource($name); if ($res instanceof PdoResource) { $this->db = $res; $this->resource = $name; $this->output->writeln("** Selected {$name}"); } else { $this->output->writeln("Invalid resource {$name}"); } } private function doVarCommand(array $args) { $varname = array_shift($args); if (empty($varname)) { foreach ($this->vars as $var=>$value) { $this->output->writeln("{$var}=".var_export($value,true).""); } return; } if (count($args) > 1) { $this->vars[$varname] = $args; } elseif (count($args) > 0) { $this->vars[$varname] = array_shift($args); } else { $this->output->writeln(var_export($this->vars[$varname]??null,true)); } } private function doQueryCommand(array $args) { $query = array_shift($args); $this->doQuery($query, $args); } private function doQuery(string $query, array $params=[]) { if (!$query) { return; } if (!$this->db) { $this->output->writeln("No database resource selected"); return; } $pdo = $this->db->getPDO(); try { $stmt = $pdo->prepare($query); $stmt->execute($params); $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); } catch (\PDOException $e) { $this->output->writeln("{$e->getMessage()}"); return; } switch ($this->options['output']) { case 'table': $this->dumpQueryTable($res); break; case 'vertical': $this->dumpQueryVertical($res); break; default: print_r($res); } } private function dumpQueryTable(array $res) { if (count($res) == 0) return; $table = new Table($this->output); $table->setHeaders(array_keys(reset($res))); $table->setStyle($this->options['table.style']); $max = $this->options['table.maxwidth']; foreach ($res as $row) { $table->addRow(array_map(function ($v) use ($max) { return strlen($v)>$max ? substr($v, 0, $max)."..." : $v; }, $row)); } $table->render(); } }