diff --git a/.spark/build/package.sh b/.spark/build/package.sh index 92f8df8..e3f1ed0 100755 --- a/.spark/build/package.sh +++ b/.spark/build/package.sh @@ -1,5 +1,10 @@ #!/bin/bash +if [ ! -f spark.phar ]; then + echo "error: No spark.phar has been built yet?" + exit 1 +fi + VERSION="$(git describe --tags)" PATH="$PWD/tools:$PATH" @@ -16,9 +21,6 @@ echo " DESTINATION=$DESTINATION" echo "* Preparing release direcory" mkdir -p $DESTINATION -echo "* Building phar archive" -pharlite &>/dev/null - echo "* Copying files to release directory" cp spark.phar $DESTINATION/spark.phar cp README.md $DESTINATION/README.md diff --git a/.spark/build/phar.sh b/.spark/build/phar.sh new file mode 100755 index 0000000..6264489 --- /dev/null +++ b/.spark/build/phar.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +echo "* Building phar archive" +pharlite &>/dev/null + diff --git a/.spark/build/update-version.sh b/.spark/build/update-version.sh index 9a3dd7b..99197a8 100755 --- a/.spark/build/update-version.sh +++ b/.spark/build/update-version.sh @@ -1,5 +1,13 @@ #!/bin/bash +if [ ! -d .git ]; then + if [ ! -f src/version ]; then + echo -e " src/version + echo -e "* Version: \e[1munknown\e[0m" + fi + exit 0 +fi + VERSION="$(git describe --tags)" if [ -z "$VERSION" ]; then diff --git a/.spark/spark.json b/.spark/spark.json index f3f4ea7..a1b9918 100644 --- a/.spark/spark.json +++ b/.spark/spark.json @@ -4,14 +4,18 @@ ".spark/local/*" ], "scripts": { - "version": [ + "check-version": [ ".spark/build/update-version.sh" ], "package": [ ".spark/build/package.sh" ], + "phar": [ + ".spark/build/phar.sh" + ], "build": [ - "@version", + "@check-version", + "@phar", "@package" ] } diff --git a/README.md b/README.md index c92cb38..d7ac922 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,14 @@ database migrations and project deployment. ## Installation -~~Download `spark.phar` and make it executable. If desired, alias `spark=spark.phar`.~~ -~~You may also want to alias `sparksh='spark repl'`.~~ +System Requirements: -Download the latest release from [dev.noccylabs.info](https://dev.noccylabs.info/noccy/php-spark/releases) +* PHP 8.0 or later (php-cli) +* Linux or other POSIX compatible OS. Probably. May work on MacOS! + +### From dist package + +Download the latest dist release from [dev.noccylabs.info](https://dev.noccylabs.info/noccy/php-spark/releases) and extract it into a directory somewhere, such as `/tmp`: ``` @@ -28,6 +32,43 @@ the `sparkplug`, `sparker`, `sparkres` and `sparksh` aliases. You can then install any new plugins into `~/opt/spark/plugins` and enable them in your projects with `sparkplug --enable the.plugin.name`. +### From installer + +Download the latest installer release (the one that ends in `.run`) +from [dev.noccylabs.info](https://dev.noccylabs.info/noccy/php-spark/releases) +and make it executable: + +``` +$ chmod +x spark-0.1.0-dist.run +$ ./spark-0.1.0-dist.run +``` + +Follow the instructions, select Yes when prompted to proceed with the installation. + +### From source + +Download the latest source release from [dev.noccylabs.info](https://dev.noccylabs.info/noccy/php-spark/releases) +and extract it into a directory somewhere, such as `~/src/spark`. You can then build spark, using spark: + +``` +$ unzip -d ~/src/spark spark-0.1.0-src.zip +$ cd ~/src/spark +$ bin/spark run build +``` + +You can now install `spark.phar` where desired, and place the `plugins` directory in +a good place. You want to add the following to your `.bashrc` or similar: + +``` +export SPARK_PLUGINS="" +# If you don't want to rename the .phar for some reason. Skip otherwise! +alias spark=spark.phar +# Useful aliases +alias sparksh=spark repl +alias sparkplug=spark plugins +alias sparkpipe=spark pipe +``` + ## Using Spark ### The easy way diff --git a/plugins/com.noccy.pdo.shell/PdoShellCommand.php b/plugins/com.noccy.pdo.shell/PdoShellCommand.php index a981424..b37f5b7 100644 --- a/plugins/com.noccy.pdo.shell/PdoShellCommand.php +++ b/plugins/com.noccy.pdo.shell/PdoShellCommand.php @@ -15,12 +15,29 @@ class PdoShellCommand extends Command { protected function configure() { $this->setName("pdo:shell"); $this->setDescription("Launch an interactive PDO shell"); + + $this->addOption("db", null, InputOption::VALUE_REQUIRED, "Select database resource", "db"); + $this->addOption("read", "r", InputOption::VALUE_REQUIRED, "Read commands to execute from file"); } protected function execute(InputInterface $input, OutputInterface $output) { $shell = new Shell\PdoShell($output); - $shell->run(); + + $db = $input->getOption("db"); + $read = $input->getOption("read"); + + if ($read) { + if (!file_exists($read)) { + $output->writeln("File not found: {$read}"); + return Command::FAILURE; + } + $file = file($read, FILE_IGNORE_NEW_LINES); + $shell->runCommands($file); + } else { + $shell->runCommands([ ".select {$db}" ]); + $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 index a825e2d..c70778f 100644 --- a/plugins/com.noccy.pdo.shell/Shell/PdoShell.php +++ b/plugins/com.noccy.pdo.shell/Shell/PdoShell.php @@ -28,6 +28,7 @@ class PdoShell { private array $defaultOptions = [ 'output' => 'table', 'table.maxwidth' => 40, + 'table.style' => 'box', ]; public function __construct(OutputInterface $output) @@ -38,7 +39,7 @@ class PdoShell { private function promptForCommand() { - $prompt = sprintf("PDO:[%s%s]> ", $this->resource, $this->db?"":"*"); + $prompt = sprintf("PDO:[%s%s]> ", $this->resource, $this->db?"":"?"); $input = readline($prompt); return $input; @@ -81,6 +82,18 @@ class PdoShell { } } + 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) { @@ -94,9 +107,14 @@ class PdoShell { 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;; @@ -112,8 +130,9 @@ class PdoShell { '.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", + '.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}"); @@ -150,7 +169,7 @@ class PdoShell { if ($res instanceof PdoResource) { $this->db = $res; $this->resource = $name; - $this->output->writeln("Seleced {$name}"); + $this->output->writeln("** Selected {$name}"); } else { $this->output->writeln("Invalid resource {$name}"); } @@ -176,8 +195,17 @@ class PdoShell { } } + 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; @@ -210,7 +238,7 @@ class PdoShell { 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) { diff --git a/src/Environment/Environment.php b/src/Environment/Environment.php index 821e6ef..6e77d5f 100644 --- a/src/Environment/Environment.php +++ b/src/Environment/Environment.php @@ -47,47 +47,6 @@ class Environment return $runner; } - /* - public function runScript(string $name, array $args, InputInterface $input, OutputInterface $output) - { - $script = $this->config['scripts'][$name]??$name; - - if (is_string($script)) { - $this->execScript($script, $args, $output); - } elseif (is_array($script)) { - foreach ($script as $row) { - $a = str_getcsv($row, ' ', "'"); - $c = array_shift($a); - if (str_starts_with($c, '@')) { - $c = ($this->config['scripts'][substr($c,1)])??$c; - $this->runScript($c, $a, $input, $output); - } else { - $this->execScript($c, $a, $output); - } - } - } - } - - private function execScript(string $script, array $args, OutputInterface $output) - { - // call script directly - if (str_ends_with($script, '.php')) { - $GLOBALS['args'] = $args; - $GLOBALS['output'] = $output; - $base = $this->getProjectDirectory(); - if (!file_exists($base ."/". $script)) { - fprintf(STDERR, "error: Could not find script file %s\n", $base."/".$script); - return; - } - //echo "# ".$base."/".$script."\n"; - include $base . "/" . $script; - } else { - //echo "$ {$script}\n"; - passthru($script); - } - } - */ - public function loadEnvironment() { if ($this->loaded) { @@ -135,7 +94,7 @@ class Environment try { include_once($item); } catch (\Throwable $t) { - fprintf(STDERR, "error: Error preloading %s: %s in %s on line %d\n", $item, $t->getMessage(), $t->getFile(), $t->getLine()); + fprintf(STDERR, "warning: Error preloading %s: %s in %s on line %d\n", $item, $t->getMessage(), $t->getFile(), $t->getLine()); //$this->logger->error("Error preloading {$item}: {$t->getMessage()} in {$t->getFile()} on line {$t->getLine()}"); } } elseif (is_dir($item) && file_exists($item."/sparkplug.php")) { @@ -143,7 +102,7 @@ class Environment try { include_once($item."/sparkplug.php"); } catch (\Throwable $t) { - fprintf(STDERR, "error: Error preloading plugin %s: %s in %s on line %d\n", $item, $t->getMessage(), $t->getFile(), $t->getLine()); + fprintf(STDERR, "warning: Error preloading plugin %s: %s in %s on line %d\n", $item, $t->getMessage(), $t->getFile(), $t->getLine()); //$this->logger->error("Error preloading plugin {$item}: {$t->getMessage()} in {$t->getFile()} on line {$t->getLine()}"); } } else { @@ -174,9 +133,9 @@ class Environment } } - public static function createFromDirectory(string|null $directory=null, bool $parents=false): Environment + public static function createFromDirectory(string|null $directory=null, bool $parents=false): ?Environment { - $directory = $directory ?? getcwd(); + $directory = realpath($directory) ?? getcwd(); if ($parents) { $check = $directory; @@ -190,7 +149,7 @@ class Environment } } - $candidates = [ $directory . "/.spark.json", $directory . "/.spark/spark.json" ]; + $candidates = [ $directory . "/.spark.json", $directory . "/spark.json", $directory . "/.spark/spark.json" ]; $config = []; while ($candidate = array_shift($candidates)) { if (!file_exists($candidate)) { continue; } @@ -205,6 +164,9 @@ class Environment break; } + if (!$config) { + return null; + } $env = new Environment($config); return $env;