Multiple fixes
* PDO shell improvements: .query command, -r and --db on command line to read commands from file or preselect database. * Updated build scripts and readme
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								.spark/build/phar.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										5
									
								
								.spark/build/phar.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
echo "* Building phar archive"
 | 
			
		||||
pharlite &>/dev/null
 | 
			
		||||
 | 
			
		||||
@@ -1,5 +1,13 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
if [ ! -d .git ]; then
 | 
			
		||||
    if [ ! -f src/version ]; then
 | 
			
		||||
        echo -e "<?php define(\"APP_VERSION\", \"unknown\");" > src/version
 | 
			
		||||
        echo -e "* Version: \e[1munknown\e[0m"
 | 
			
		||||
    fi
 | 
			
		||||
    exit 0
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
VERSION="$(git describe --tags)"
 | 
			
		||||
 | 
			
		||||
if [ -z "$VERSION" ]; then
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
    ]
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								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="<path-to-plugins-dir>"
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
        $db = $input->getOption("db");
 | 
			
		||||
        $read = $input->getOption("read");
 | 
			
		||||
 | 
			
		||||
        if ($read) {
 | 
			
		||||
            if (!file_exists($read)) {
 | 
			
		||||
                $output->writeln("<error>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;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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("  <options=bold>{$cmd}</> - <info>{$info}</>");
 | 
			
		||||
@@ -150,7 +169,7 @@ class PdoShell {
 | 
			
		||||
        if ($res instanceof PdoResource) {
 | 
			
		||||
            $this->db = $res;
 | 
			
		||||
            $this->resource = $name;
 | 
			
		||||
            $this->output->writeln("<fg=green>Seleced {$name}</>");
 | 
			
		||||
            $this->output->writeln("<fg=green>** Selected {$name}</>");
 | 
			
		||||
        } else {
 | 
			
		||||
            $this->output->writeln("<error>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("<error>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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user