php-spark/plugins/com.noccy.pdo.shell/Shell/PdoShell.php

254 lines
7.1 KiB
PHP

<?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,
'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("<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|.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(" <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>** Selected {$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 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("<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)));
$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();
}
}