Added PDO shell plugin

This commit is contained in:
Chris 2021-12-16 02:39:52 +01:00
parent 1125ccb82d
commit 16753e1892
4 changed files with 270 additions and 0 deletions

View File

@ -10,6 +10,8 @@ directory you have defined to preload from.
- `com.noccy.pdo`: Interact with PDO from the command line. Registers a custom - `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 resource type, so database connections can be defined in advance to be available
to scripts and more. 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 - `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 a modification is detected. This can be used to compile scss/less or to generate
other resources as files are changed. other resources as files are changed.

View File

@ -0,0 +1,29 @@
<?php
namespace SparkPlug\Com\Noccy\Pdo\Shell;
use Spark\Commands\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Helper\TableSeparator;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class PdoShellCommand extends Command {
protected function configure() {
$this->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;
}
}

View File

@ -0,0 +1,225 @@
<?php
namespace SparkPlug\Com\Noccy\Pdo\Shell\Shell;
use SparkPlug\Com\Noccy\Pdo\PdoResource;
use Spark\Commands\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Helper\TableSeparator;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class PdoShell {
private OutputInterface $output;
private string $resource = "db";
private ?PdoResource $db = null;
private array $vars = [];
private bool $running = false;
private array $options = [];
private array $defaultOptions = [
'output' => '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("<error>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(" <options=bold>{$cmd}</> - <info>{$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("<info>{$var}</>: <comment>".var_export($value,true)."</>");
}
return;
}
if ($value !== null) {
if (!array_key_exists($varname, $this->options)) {
$this->output->writeln("<error>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("<fg=green>Seleced {$name}</>");
} else {
$this->output->writeln("<error>Invalid resource {$name}</>");
}
}
private function doVarCommand(array $args)
{
$varname = array_shift($args);
if (empty($varname)) {
foreach ($this->vars as $var=>$value) {
$this->output->writeln("<info>{$var}</>=<comment>".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("<error>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("<error>{$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();
}
}

View File

@ -0,0 +1,14 @@
<?php // "name":"Interactive PDO Shell", "author":"Noccy"
namespace SparkPlug\Com\Noccy\Pdo\Shell;
use SparkPlug;
class PdoShellPlugin extends SparkPlug {
public function load()
{
register_command(new PdoShellCommand());
}
}
register_plugin("com.noccy.pdo.shell", new PdoShellPlugin);