From 16753e189234da92a0f1f6b77f75797ad8845e6b Mon Sep 17 00:00:00 2001 From: Christopher Vagnetoft Date: Thu, 16 Dec 2021 02:39:52 +0100 Subject: [PATCH] Added PDO shell plugin --- plugins/README.md | 2 + .../com.noccy.pdo.shell/PdoShellCommand.php | 29 +++ .../com.noccy.pdo.shell/Shell/PdoShell.php | 225 ++++++++++++++++++ plugins/com.noccy.pdo.shell/sparkplug.php | 14 ++ 4 files changed, 270 insertions(+) create mode 100644 plugins/com.noccy.pdo.shell/PdoShellCommand.php create mode 100644 plugins/com.noccy.pdo.shell/Shell/PdoShell.php create mode 100644 plugins/com.noccy.pdo.shell/sparkplug.php diff --git a/plugins/README.md b/plugins/README.md index 2e4113b..4492ad0 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -10,6 +10,8 @@ directory you have defined to preload from. - `com.noccy.pdo`: Interact with PDO from the command line. Registers a custom resource type, so database connections can be defined in advance to be available to scripts and more. +- `com.noccy.pdo.shell`: An interactive shell for querying PDO resouces. No more + forgetting passwords or complex commands to dive into a SQL shell! - `com.noccy.watcher`: A plugin too watch files and invoke scripts or commands when a modification is detected. This can be used to compile scss/less or to generate other resources as files are changed. diff --git a/plugins/com.noccy.pdo.shell/PdoShellCommand.php b/plugins/com.noccy.pdo.shell/PdoShellCommand.php new file mode 100644 index 0000000..a981424 --- /dev/null +++ b/plugins/com.noccy.pdo.shell/PdoShellCommand.php @@ -0,0 +1,29 @@ +setName("pdo:shell"); + $this->setDescription("Launch an interactive PDO shell"); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $shell = new Shell\PdoShell($output); + $shell->run(); + + return Command::SUCCESS; + } + +} + diff --git a/plugins/com.noccy.pdo.shell/Shell/PdoShell.php b/plugins/com.noccy.pdo.shell/Shell/PdoShell.php new file mode 100644 index 0000000..a825e2d --- /dev/null +++ b/plugins/com.noccy.pdo.shell/Shell/PdoShell.php @@ -0,0 +1,225 @@ + 'table', + 'table.maxwidth' => 40, + ]; + + 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, []); + } + } + } + + 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 '.help': + $this->doHelpCommand($args); + break; + 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' => "Exit the shell", + 'SQL' => "Run SQL against the database", + ]; + 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("Seleced {$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 doQuery(string $query, array $params=[]) + { + 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))); + + $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(); + } + +} + diff --git a/plugins/com.noccy.pdo.shell/sparkplug.php b/plugins/com.noccy.pdo.shell/sparkplug.php new file mode 100644 index 0000000..4c9f06f --- /dev/null +++ b/plugins/com.noccy.pdo.shell/sparkplug.php @@ -0,0 +1,14 @@ +