Added plugins and build tools
This commit is contained in:
4
plugins/README.md
Normal file
4
plugins/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# Plugins
|
||||
|
||||
Install by copying or symlinking into your `.spark/plugins` directory, or whatever
|
||||
directory you have defined to preload from.
|
24
plugins/com.noccy.docker/DockerBuildCommand.php
Normal file
24
plugins/com.noccy.docker/DockerBuildCommand.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Docker;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class DockerBuildCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("docker:build")
|
||||
->setDescription("Build an image");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$config = read_config("docker.json");
|
||||
print_r($config);
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
37
plugins/com.noccy.docker/DockerCompose/Service.php
Normal file
37
plugins/com.noccy.docker/DockerCompose/Service.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Docker\DockerCompose;
|
||||
|
||||
|
||||
class Service
|
||||
{
|
||||
private array $service;
|
||||
|
||||
private array $environment = [];
|
||||
|
||||
private Stack|null $stack;
|
||||
|
||||
public function __construct(array $service, ?Stack $stack)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->stack = $stack;
|
||||
|
||||
foreach ($this->service['environment']??[] as $k=>$v) {
|
||||
if (is_numeric($k)) {
|
||||
[$k, $v] = explode("=", $v, 2);
|
||||
}
|
||||
$this->environment[$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
public function getImage():?String
|
||||
{
|
||||
return $this->service['image']??null;
|
||||
}
|
||||
|
||||
public function getEnvironment(): array
|
||||
{
|
||||
return $this->environment;
|
||||
}
|
||||
|
||||
}
|
42
plugins/com.noccy.docker/DockerCompose/Stack.php
Normal file
42
plugins/com.noccy.docker/DockerCompose/Stack.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Docker\DockerCompose;
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class Stack
|
||||
{
|
||||
|
||||
private array $compose;
|
||||
|
||||
private string $version;
|
||||
|
||||
private array $services;
|
||||
|
||||
public function __construct(string $filename)
|
||||
{
|
||||
$this->compose = Yaml::parseFile($filename);
|
||||
|
||||
$this->version = $this->compose['version']??null;
|
||||
$this->enumServices();
|
||||
}
|
||||
|
||||
private function enumServices()
|
||||
{
|
||||
foreach ($this->compose['services'] as $service=>$config) {
|
||||
$this->services[$service] = new Service($config, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function getServiceNames(): array
|
||||
{
|
||||
return array_keys($this->services);
|
||||
}
|
||||
|
||||
public function getService(string $name): ?Service
|
||||
{
|
||||
return $this->services[$name] ?? null;
|
||||
}
|
||||
|
||||
}
|
||||
|
85
plugins/com.noccy.docker/DockerDbExportCommand.php
Normal file
85
plugins/com.noccy.docker/DockerDbExportCommand.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Docker;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use Symfony\Component\Console\Input\Input;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class DockerDbExportCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("docker:db:export")
|
||||
->setDescription("Export a database")
|
||||
->addOption("mysql", null, InputOption::VALUE_NONE, "Export from a MySQL database")
|
||||
->addOption("dsn", null, InputOption::VALUE_REQUIRED, "Database DSN")
|
||||
->addOption("service", null, InputOption::VALUE_REQUIRED, "Service name in stack")
|
||||
->addOption("database", null, InputOption::VALUE_REQUIRED, "Database name")
|
||||
->addOption("output", "o", InputOption::VALUE_REQUIRED, "Output to file instead of stdout")
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$docker = get_plugin("com.noccy.docker");
|
||||
|
||||
$service = $input->getOption("service");
|
||||
$dsn = $input->getOption("dsn");
|
||||
if ($service) {
|
||||
$stack = $docker->getComposeStack();
|
||||
if ($input->getOption("mysql")) {
|
||||
$dbtype = 'mysql';
|
||||
} else {
|
||||
$services = $stack->getServiceNames();
|
||||
if (!in_array($service, $services)) {
|
||||
$output->writeln("<error>Invalid service {$service}. Valid are ".join(", ", $services)."</>");
|
||||
return Command::INVALID;
|
||||
}
|
||||
$image = $stack->getService($service)->getImage();
|
||||
if (preg_match('/(mysql|mariadb)/i', $image)) {
|
||||
$dbtype = 'mysql';
|
||||
$env = $stack->getService($service)->getEnvironment();
|
||||
$database = $env['MYSQL_DATABASE']??null;
|
||||
if ($dbpass = $env['MYSQL_ROOT_PASSWORD']??null) {
|
||||
$dbuser = 'root';
|
||||
} else {
|
||||
$dbuser = $env['MYSQL_USER']??null;
|
||||
$dbpass = $env['MYSQL_PASSWORD']??null;
|
||||
}
|
||||
} else {
|
||||
$output->writeln("<error>Unable to determine database type from service</>");
|
||||
return Command::INVALID;
|
||||
}
|
||||
}
|
||||
$database = $database ?? $input->getOption("database");
|
||||
if (empty($database)) {
|
||||
$output->writeln("<error>No --database specified</>");
|
||||
return Command::INVALID;
|
||||
}
|
||||
switch ($dbtype) {
|
||||
case 'mysql':
|
||||
$cmd = sprintf("mysqldump -u%s -p%s %s", $dbuser, $dbpass, $database);
|
||||
break;
|
||||
}
|
||||
$this->exportFromService($service, $cmd, $output);
|
||||
} elseif ($dsn) {
|
||||
$url = parse_url($dsn);
|
||||
if (empty($url)) {
|
||||
$output->writeln("<error>Bad database DSN {$dsn}. Should look like mysql://user:pass@host:port/database</>");
|
||||
return Command::INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function exportFromService(string $service, string $command, OutputInterface $output)
|
||||
{
|
||||
$cmd = sprintf("docker-compose exec -T %s %s", $service, $command);
|
||||
passthru($cmd);
|
||||
}
|
||||
}
|
24
plugins/com.noccy.docker/DockerDownCommand.php
Normal file
24
plugins/com.noccy.docker/DockerDownCommand.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Docker;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class DockerDownCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("docker:down")
|
||||
->setDescription("Stop a stack or container");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$config = read_config("docker.json");
|
||||
print_r($config);
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
24
plugins/com.noccy.docker/DockerExecCommand.php
Normal file
24
plugins/com.noccy.docker/DockerExecCommand.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Docker;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class DockerExecCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("docker:exec")
|
||||
->setDescription("Execute scripts in a docker container");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$config = read_config("docker.json");
|
||||
print_r($config);
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
70
plugins/com.noccy.docker/DockerStatusCommand.php
Normal file
70
plugins/com.noccy.docker/DockerStatusCommand.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Docker;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class DockerStatusCommand extends Command
|
||||
{
|
||||
const BULLET="\u{25cf}";
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("docker:status")
|
||||
->setDescription("Show docker status");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
|
||||
exec("docker-compose ps -q", $ids, $ret);
|
||||
if (count($ids) === 0) return Command::SUCCESS;
|
||||
|
||||
exec("docker inspect ".join(" ",$ids), $out, $ret);
|
||||
$json = json_decode(join("", $out));
|
||||
|
||||
$stack = get_plugin('com.noccy.docker')->getComposeStack();
|
||||
|
||||
$table = new Table($output);
|
||||
$table->setStyle("box");
|
||||
$table->setHeaders([ "Name", "Status", "Image", "Ports" ]);
|
||||
foreach ($json as $container) {
|
||||
$startedTs = preg_replace('/(\.([0-9]+)Z)$/', '+0100', $container->State->StartedAt);
|
||||
$s = date_parse($startedTs);
|
||||
$started = mktime($s['hour'], $s['minute'], $s['second'], $s['month'], $s['day'], $s['year']) + 3600;
|
||||
if ($container->State->Dead) {
|
||||
$status = "<fg=red>".self::BULLET."</> ".$container->State->Status;
|
||||
} elseif ($container->State->Restarting) {
|
||||
$status = "<fg=yellow>".self::BULLET."</> ".$container->State->Status;
|
||||
} elseif ($container->State->Running) {
|
||||
$elapsed = time() - $started;
|
||||
if ($elapsed > 60) {
|
||||
$em = floor($elapsed / 60);
|
||||
$es = $elapsed - ($em * 60);
|
||||
if ($em>60) {
|
||||
$eh = floor($em / 60);
|
||||
$em = $em - ($eh * 60);
|
||||
$elapsed = sprintf("%dh%dm%ds", $eh, $em, $es);
|
||||
} else {
|
||||
$elapsed = sprintf("%dm%ds", $em, $es);
|
||||
}
|
||||
} else {
|
||||
$elapsed = sprintf("%ds", $elapsed);
|
||||
}
|
||||
$status = "<fg=green>".self::BULLET."</> ".$container->State->Status." (<fg=green>{$elapsed}</>)";
|
||||
} else {
|
||||
$status = "<fg=red>".self::BULLET."</> ".$container->State->Status;
|
||||
}
|
||||
$ports = $container->Config->ExposedPorts??[];
|
||||
$ports = array_keys((array)$ports);
|
||||
$table->addRow([ $container->Name, $status, $container->Config->Image, join(", ", $ports) ]);
|
||||
}
|
||||
$table->render();
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
24
plugins/com.noccy.docker/DockerUpCommand.php
Normal file
24
plugins/com.noccy.docker/DockerUpCommand.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Docker;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class DockerUpCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("docker:up")
|
||||
->setDescription("Start a stack or container");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$config = read_config("docker.json");
|
||||
print_r($config);
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
54
plugins/com.noccy.docker/sparkplug.php
Normal file
54
plugins/com.noccy.docker/sparkplug.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php // "name":"Docker plugin for SparkPlug", "author":"Noccy"
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Docker;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use SparkPlug\Com\Noccy\Docker\DockerCompose\Stack;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class DockerPlug extends SparkPlug
|
||||
{
|
||||
private array $compose = [];
|
||||
|
||||
private Stack $composeStack;
|
||||
|
||||
public function load()
|
||||
{
|
||||
$config = read_config("docker.json");
|
||||
$docker = $config['docker']??[];
|
||||
|
||||
$hasCompose = array_key_exists('compose', $docker);
|
||||
$hasBuild = array_key_exists('build', $docker);
|
||||
|
||||
if ($hasCompose) {
|
||||
$this->compose = $docker['compose'];
|
||||
}
|
||||
|
||||
if ($hasCompose || $hasBuild) {
|
||||
register_command(new DockerUpCommand);
|
||||
register_command(new DockerDownCommand);
|
||||
register_command(new DockerStatusCommand);
|
||||
}
|
||||
if ($hasBuild) {
|
||||
register_command(new DockerBuildCommand);
|
||||
register_command(new DockerExecCommand);
|
||||
}
|
||||
register_command(new DockerDbExportCommand);
|
||||
}
|
||||
|
||||
public function getComposeStack(): ?Stack
|
||||
{
|
||||
$base = $this->getProjectDirectory();
|
||||
if (empty($this->composeStack)) {
|
||||
$composeFile = $base . "/" . ($this->compose['file']??'docker-compose.yml');
|
||||
$this->composeStack = new Stack($composeFile);
|
||||
}
|
||||
return $this->composeStack;
|
||||
}
|
||||
}
|
||||
|
||||
//if (file_exists(get_environment()->getConfigDirectory()."/maker.json")) {
|
||||
register_plugin("com.noccy.docker", new DockerPlug());
|
||||
//}
|
73
plugins/com.noccy.git/GitIgnoreCommand.php
Normal file
73
plugins/com.noccy.git/GitIgnoreCommand.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Git;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
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 GitIgnoreCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("git:ignore")
|
||||
->setDescription("List, add or remove paths from gits ignorelists")
|
||||
->addOption("local","l",InputOption::VALUE_NONE,"Use the local ignore rather than .gitignore")
|
||||
->addOption("add","a",InputOption::VALUE_NONE,"Add a pattern")
|
||||
->addOption("remove","r",InputOption::VALUE_NONE,"Attempt to remove a pattern")
|
||||
->addArgument("pattern", InputArgument::OPTIONAL, "Pattern to add or remove")
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$local = $input->getOption("local");
|
||||
$root = $this->getEnvironment()->getProjectDirectory();
|
||||
$file = $root . (!$local ? "/.gitignore" : "/.git/info/exclude");
|
||||
$pattern = $input->getArgument("pattern");
|
||||
|
||||
if (empty($pattern)) {
|
||||
if (file_exists($file)) {
|
||||
$ignores = file($file, FILE_IGNORE_NEW_LINES);
|
||||
foreach ($ignores as $ignore) {
|
||||
if (str_starts_with(trim($ignore),'#')) {
|
||||
$output->writeln("<fg=green>".$ignore."</>");
|
||||
} else {
|
||||
$output->writeln("<fg=white>".$ignore."</>");
|
||||
}
|
||||
}
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
$output->writeln("<info>Empty list</>");
|
||||
return Command::SUCCESS;
|
||||
} elseif ($input->getOption("add") && $pattern) {
|
||||
if (file_exists($file)) {
|
||||
$ignores = file($file, FILE_IGNORE_NEW_LINES);
|
||||
} else {
|
||||
$ignores = [];
|
||||
}
|
||||
array_push($ignores, $pattern);
|
||||
file_put_contents($file, join("\n", $ignores));
|
||||
$output->writeln("<info>Updated {$file}</>");
|
||||
return Command::SUCCESS;
|
||||
} elseif ($input->getOption("remove") && $pattern) {
|
||||
if (file_exists($file)) {
|
||||
$ignores = file($file, FILE_IGNORE_NEW_LINES);
|
||||
$ignores = array_filter($ignores, function ($v) use ($pattern) {
|
||||
return $v != $pattern;
|
||||
});
|
||||
$output->writeln("<info>Updated {$file}</>");
|
||||
file_put_contents($file, join("\n", $ignores));
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
$output->writeln("<info>Not updating non-existing file {$file}</>");
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
$output->writeln("<error>Expected no pattern, --add pattern or --remove pattern</>");
|
||||
return Command::INVALID;
|
||||
}
|
||||
}
|
25
plugins/com.noccy.git/sparkplug.php
Normal file
25
plugins/com.noccy.git/sparkplug.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php // "name":"Git plugin for SparkPlug", "author":"Noccy"
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Git;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class GitPlug extends SparkPlug
|
||||
{
|
||||
public function load()
|
||||
{
|
||||
$root = $this->getProjectDirectory();
|
||||
if (!file_exists($root."/.git")) {
|
||||
return;
|
||||
}
|
||||
|
||||
register_command(new GitIgnoreCommand());
|
||||
}
|
||||
}
|
||||
|
||||
//if (file_exists(get_environment()->getConfigDirectory()."/maker.json")) {
|
||||
register_plugin("com.noccy.git", new GitPlug());
|
||||
//}
|
42
plugins/com.noccy.maker/sparkplug.php
Normal file
42
plugins/com.noccy.maker/sparkplug.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php // "name":"Build stuff from stuff", "author":"Noccy"
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Maker;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class MakerPlug extends SparkPlug
|
||||
{
|
||||
public function load()
|
||||
{
|
||||
$config = json_decode(file_get_contents(get_environment()->getConfigDirectory()."/maker.json"), true);
|
||||
foreach ($config as $rule=>$info) {
|
||||
if (str_starts_with($rule, '@')) {
|
||||
$rule = substr($rule, 1);
|
||||
register_command(new class($rule) extends Command {
|
||||
private $rule;
|
||||
public function __construct($rule)
|
||||
{
|
||||
$this->rule = $rule;
|
||||
parent::__construct();
|
||||
}
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("make:{$this->rule}");
|
||||
$this->setDescription("Run the {$this->rule} maker task");
|
||||
}
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (file_exists(get_environment()->getConfigDirectory()."/maker.json")) {
|
||||
register_plugin("com.noccy.maker", new MakerPlug());
|
||||
}
|
36
plugins/com.noccy.pdo/PdoExecCommand.php
Normal file
36
plugins/com.noccy.pdo/PdoExecCommand.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Pdo;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
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 PdoExecCommand extends Command {
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$source = $input->getOption("res");
|
||||
$sourcePdo = get_resource($source)->getPDO();
|
||||
if (!$sourcePdo) {
|
||||
$output->writeln("<error>Invalid resource: {$source}</>");
|
||||
return Command::INVALID;
|
||||
}
|
||||
|
||||
$query = $input->getArgument('query');
|
||||
|
||||
$stmt = $sourcePdo->prepare($query);
|
||||
$stmt->execute();
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
protected function configure() {
|
||||
$this->setName("pdo:exec");
|
||||
$this->setDescription("Run a query without returning data");
|
||||
$this->addOption("res", "r", InputOption::VALUE_REQUIRED, "Resource to query", "db");
|
||||
$this->addArgument("query", InputArgument::REQUIRED, "SQL query to execute");
|
||||
}
|
||||
}
|
||||
|
85
plugins/com.noccy.pdo/PdoQueryCommand.php
Normal file
85
plugins/com.noccy.pdo/PdoQueryCommand.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Pdo;
|
||||
|
||||
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 PdoQueryCommand extends Command {
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$source = $input->getOption("res");
|
||||
$sourcePdo = get_resource($source)->getPDO();
|
||||
if (!$sourcePdo) {
|
||||
$output->writeln("<error>Invalid resource: {$source}</>");
|
||||
return Command::INVALID;
|
||||
}
|
||||
|
||||
$box = $input->getOption('box');
|
||||
$query = $input->getArgument('query');
|
||||
$vert = $input->getOption("vertical");
|
||||
$unserialize = $input->getOption("unserialize");
|
||||
|
||||
$stmt = $sourcePdo->query($query);
|
||||
$stmt->execute();
|
||||
|
||||
$table = new Table($output);
|
||||
$table->setStyle($box?"box":"compact");
|
||||
$hasColumns = false;
|
||||
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||
if (!$hasColumns) {
|
||||
if ($vert) {
|
||||
$table->setHeaders([ "Field", "VarType", "Value" ]);
|
||||
} else {
|
||||
$table->setHeaders(array_keys($row));
|
||||
}
|
||||
$hasColumns = true;
|
||||
} else {
|
||||
if ($vert) {
|
||||
if ($box) {
|
||||
$table->addRow(new TableSeparator());
|
||||
} else {
|
||||
$table->addRow(["","","-----"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($vert) {
|
||||
foreach ($row as $k=>$v) {
|
||||
$vv = $v;
|
||||
if ($unserialize) {
|
||||
$j = @json_decode($v);
|
||||
$p = @unserialize($v);
|
||||
if ($j) {
|
||||
$v = $j;
|
||||
$vv = json_encode($v, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
} elseif ($p) {
|
||||
$v = $p;
|
||||
$vv = json_encode($p, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
}
|
||||
$table->addRow([ $k, gettype($v), $vv ]);
|
||||
}
|
||||
} else {
|
||||
$table->addRow($row);
|
||||
}
|
||||
}
|
||||
$table->render();
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
protected function configure() {
|
||||
$this->setName("pdo:query");
|
||||
$this->setDescription("Run a query against a defined PDO connection");
|
||||
$this->addOption("res", "r", InputOption::VALUE_REQUIRED, "Resource to query", "db");
|
||||
$this->addOption("vertical", "l", InputOption::VALUE_NONE, "Print result as rows instead of columns");
|
||||
$this->addOption("box", null, InputOption::VALUE_NONE, "Use boxed table");
|
||||
$this->addOption("unserialize", "u", InputOption::VALUE_NONE, "Attempt to unserialize serialized data");
|
||||
$this->addArgument("query", InputArgument::REQUIRED, "SQL query to execute");
|
||||
}
|
||||
}
|
||||
|
59
plugins/com.noccy.pdo/PdoResource.php
Normal file
59
plugins/com.noccy.pdo/PdoResource.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Pdo;
|
||||
|
||||
use Spark\Resource\ResourceType;
|
||||
use PDO;
|
||||
|
||||
class PdoResource extends ResourceType
|
||||
{
|
||||
private PDO|null $pdo = null;
|
||||
|
||||
private array $options;
|
||||
|
||||
public function __construct(array $options)
|
||||
{
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
private function createFromURI(string $uri)
|
||||
{
|
||||
$uris = parse_url($uri);
|
||||
$username = $uris['user']??null;
|
||||
$password = $uris['pass']??null;
|
||||
|
||||
switch ($uris['scheme']??null) {
|
||||
case 'mysql':
|
||||
$database = ltrim($uris['path']??null, '/');
|
||||
$dsn = sprintf("mysql:host=%s;port=%d;dbname=%s", $uris['host']??'127.0.0.1', $uris['port']??3306, $database);
|
||||
break;
|
||||
case 'sqlite':
|
||||
$database = $uris['path']??':memory:';
|
||||
$dsn = sprintf("sqlite:%s", $database);
|
||||
break;
|
||||
default:
|
||||
fprintf(STDERR, "error: Unable to create PDO resource from URI, invalid type %s\n", $uris['scheme']??null);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->pdo = new \PDO($dsn, $username, $password);
|
||||
}
|
||||
|
||||
public function getPDO(): ?PDO
|
||||
{
|
||||
if (!$this->pdo) {
|
||||
$this->createFromURI($this->options['uri']);
|
||||
}
|
||||
return $this->pdo;
|
||||
}
|
||||
|
||||
public function info()
|
||||
{
|
||||
return $this->options['uri'];
|
||||
}
|
||||
|
||||
public function createTable(string $name, array $columns, bool $ifNotExists=false)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
16
plugins/com.noccy.pdo/sparkplug.php
Normal file
16
plugins/com.noccy.pdo/sparkplug.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php // "name":"Access databases through PDO", "author":"Noccy"
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Pdo;
|
||||
|
||||
use SparkPlug;
|
||||
|
||||
class PdoPlugin extends SparkPlug {
|
||||
public function load()
|
||||
{
|
||||
register_command(new PdoQueryCommand());
|
||||
register_command(new PdoExecCommand());
|
||||
}
|
||||
}
|
||||
|
||||
register_plugin("com.noccy.pdo", new PdoPlugin);
|
||||
register_resource_type("pdo", PdoResource::class);
|
Reference in New Issue
Block a user