Initial commit
This commit is contained in:
		
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					/composer.lock
 | 
				
			||||||
 | 
					/vendor
 | 
				
			||||||
 | 
					/.phpunit.cache
 | 
				
			||||||
 | 
					/*.phar
 | 
				
			||||||
 | 
					/.spark
 | 
				
			||||||
 | 
					/.spark.json
 | 
				
			||||||
							
								
								
									
										72
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					# Spark: Ignite your development workflow
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Spark is a utility to help with everything from various mundane tasks to complex
 | 
				
			||||||
 | 
					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'`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Using Spark
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Spark expects a configuration file to either be found at `./.spark.json` or
 | 
				
			||||||
 | 
					`./.spark/spark.json` relative to the project root. The `./.spark` directory
 | 
				
			||||||
 | 
					will always be used for auxillary configuration, so the placement is fully up
 | 
				
			||||||
 | 
					to you.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					On its own it doesn't do much except provide a command interface to its inside.
 | 
				
			||||||
 | 
					The magic can be found in preloading:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*spark.json*
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "preload": [ "./.spark/plugins/*", "./.spark/autoload.php" ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The preloader will go over each of the defined rules and attempt to load them
 | 
				
			||||||
 | 
					in one of two ways, if applicable:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Files with a `.php`-extension will be loaded directly.
 | 
				
			||||||
 | 
					2. Directories having a `sparkplug.php` file will be loaded as plugins.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The advantages of writing your extensions as flat files:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Simple interface
 | 
				
			||||||
 | 
					- Quickly register resources for other parts of Spark
 | 
				
			||||||
 | 
					- All code evaluated on load (can be a caveat!)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The advantage of writing your extensions as plugins:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Object-oriented interface
 | 
				
			||||||
 | 
					- Delayed evaluation of code, ensuring dependencies are loaded
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Scripts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Using scripts is the simplest way to leverage Spark:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*spark.json*
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  ...
 | 
				
			||||||
 | 
					  "scripts": {
 | 
				
			||||||
 | 
					    "hello": "./.spark/hello.php",
 | 
				
			||||||
 | 
					    "world": "echo 'World'",
 | 
				
			||||||
 | 
					    "greet": [
 | 
				
			||||||
 | 
					      "@hello",
 | 
				
			||||||
 | 
					      "@world"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`.php`-files are executed in-process, and as such have access to any registered
 | 
				
			||||||
 | 
					resources, resource types and plugins.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Resources
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Resources are wrappers around database connections and such, providing a cleaner
 | 
				
			||||||
 | 
					interface to its innards.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										7
									
								
								bin/spark
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										7
									
								
								bin/spark
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env php
 | 
				
			||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require_once __DIR__."/../vendor/autoload.php";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$app = new Spark\SparkApplication();
 | 
				
			||||||
 | 
					$app->run();
 | 
				
			||||||
							
								
								
									
										40
									
								
								composer.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								composer.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "name": "noccylabs/spark",
 | 
				
			||||||
 | 
					    "description": "Energize your development workflow",
 | 
				
			||||||
 | 
					    "type": "application",
 | 
				
			||||||
 | 
					    "license": "MIT",
 | 
				
			||||||
 | 
					    "authors": [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "Christopher Vagnetoft",
 | 
				
			||||||
 | 
					            "email": "cvagnetoft@gmail.com"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "bin": [
 | 
				
			||||||
 | 
					        "bin/spark"
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "autoload": {
 | 
				
			||||||
 | 
					        "psr-4": {
 | 
				
			||||||
 | 
					            "Spark\\": "src/",
 | 
				
			||||||
 | 
					            "": "runtime/"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "files": [
 | 
				
			||||||
 | 
					            "runtime/functions.php"
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "extra": {
 | 
				
			||||||
 | 
					        "phar": {
 | 
				
			||||||
 | 
					            "output": "spark.phar"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "require": {
 | 
				
			||||||
 | 
					        "php": "^8.0",
 | 
				
			||||||
 | 
					        "ext-zip": "^1.19",
 | 
				
			||||||
 | 
					        "ext-xml": "^8.0",
 | 
				
			||||||
 | 
					        "symfony/console": "^6.0",
 | 
				
			||||||
 | 
					        "symfony/expression-language": "^6.0",
 | 
				
			||||||
 | 
					        "symfony/finder": "^6.0",
 | 
				
			||||||
 | 
					        "symfony/process": "^6.0",
 | 
				
			||||||
 | 
					        "psr/log": "^3.0",
 | 
				
			||||||
 | 
					        "symfony/var-dumper": "^6.0"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										6
									
								
								runtime/SparkPlug.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								runtime/SparkPlug.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					abstract class SparkPlug
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    abstract public function load();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										83
									
								
								runtime/functions.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								runtime/functions.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Spark\Commands\Command;
 | 
				
			||||||
 | 
					use Spark\Environment\Environment;
 | 
				
			||||||
 | 
					use Spark\Resource\ResourceType;
 | 
				
			||||||
 | 
					use Spark\SparkApplication;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$HELPERS = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function object(...$data) {
 | 
				
			||||||
 | 
					    return (object)$data;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function register_helper(string $name, callable $helper) {
 | 
				
			||||||
 | 
					    global $HELPERS;
 | 
				
			||||||
 | 
					    $HELPERS[$name] = $helper;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function helper(string $name, ...$args) {
 | 
				
			||||||
 | 
					    global $HELPERS;
 | 
				
			||||||
 | 
					    if (!array_key_exists($name, $HELPERS)) {
 | 
				
			||||||
 | 
					        fprintf(STDERR, "error: No helper %s registered", $name);
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    $helper = $HELPERS[$name];
 | 
				
			||||||
 | 
					    return $helper(...$args);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function register_plugin(string $name, SparkPlug $plugin) {
 | 
				
			||||||
 | 
					    $refl = new ReflectionClass($plugin);
 | 
				
			||||||
 | 
					    $psr4 = object(
 | 
				
			||||||
 | 
					        namespace: $refl->getNamespaceName()."\\",
 | 
				
			||||||
 | 
					        path: dirname($refl->getFileName())."/"
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    spl_autoload_register(function ($class) use ($psr4) {
 | 
				
			||||||
 | 
					        if (str_starts_with($class, $psr4->namespace)) {
 | 
				
			||||||
 | 
					            $part = substr($class, strlen($psr4->namespace));
 | 
				
			||||||
 | 
					            $file = $psr4->path . strtr($part, "\\", DIRECTORY_SEPARATOR).".php";
 | 
				
			||||||
 | 
					            if (file_exists($file)) {
 | 
				
			||||||
 | 
					                require_once $file;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    SparkApplication::$instance->getPluginManager()->registerPlugin($name, $plugin);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function get_plugin(string $name) {
 | 
				
			||||||
 | 
					    return SparkApplication::$instance->getPluginManager()->getPlugin($name);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function register_command(Command $command) {
 | 
				
			||||||
 | 
					    SparkApplication::$instance->add($command);    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function get_environment(): Environment {
 | 
				
			||||||
 | 
					    return SparkApplication::$instance->getEnvironment();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function register_resource_type(string $name, string $type) {
 | 
				
			||||||
 | 
					    SparkApplication::$instance->getResourceManager()->registerResourceType($name, $type);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function create_resource(string $name, string $type, ...$options) {
 | 
				
			||||||
 | 
					    return SparkApplication::$instance->getResourceManager()->createNamedResource($name, $type, $options);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function get_resource(string $name) {
 | 
				
			||||||
 | 
					    return SparkApplication::$instance->getResourceManager()->getNamedResource($name);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function resource(string $name) {
 | 
				
			||||||
 | 
					    return get_resource($name);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function read_config($file=null) {
 | 
				
			||||||
 | 
					    if (!$file) return;
 | 
				
			||||||
 | 
					    $abs = get_environment()->getConfigDirectory() . "/" . $file;
 | 
				
			||||||
 | 
					    if (!file_exists($abs)) {
 | 
				
			||||||
 | 
					        //fprintf(STDERR, "warning: Can't read config file %s\n", $abs);
 | 
				
			||||||
 | 
					        return [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return (array)json_decode(file_get_contents($abs), true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										38
									
								
								src/Commands/Command.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/Commands/Command.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Spark\Commands;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Flare\SparkApplication;
 | 
				
			||||||
 | 
					use Spark\Environment\Environment;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Application;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Command\Command as CommandCommand;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					abstract class Command extends CommandCommand
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public function getEnvironment(): Environment
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /** @var SparkApplication */
 | 
				
			||||||
 | 
					        $app = $this->getApplication();
 | 
				
			||||||
 | 
					        if (!$app) return null;
 | 
				
			||||||
 | 
					        return $app->getEnvironment();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function loadEnvironment(string $path)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /** @var SparkApplication */
 | 
				
			||||||
 | 
					        $app = $this->getApplication();
 | 
				
			||||||
 | 
					        $app->loadEnvironment($path);        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function createEnvironment(string|null $path=null)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (empty($path)) {
 | 
				
			||||||
 | 
					            $path = getcwd();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        /** @var SparkApplication */
 | 
				
			||||||
 | 
					        $app = $this->getApplication();
 | 
				
			||||||
 | 
					        $app->createEnvironment($path);
 | 
				
			||||||
 | 
					        $app->loadEnvironment($path);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										41
									
								
								src/Commands/ReplCommand.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/Commands/ReplCommand.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Spark\Commands;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Attribute\AsCommand;
 | 
				
			||||||
 | 
					use Spark\Commands\Command;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Input\InputArgument;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Input\InputInterface;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Input\InputOption;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Output\OutputInterface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[AsCommand(name:'repl', description:'Interactive REPL for PHP')]
 | 
				
			||||||
 | 
					class ReplCommand extends Command
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    protected function configure()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->addOption("list", null, InputOption::VALUE_NONE, "List the available scripts");
 | 
				
			||||||
 | 
					        $this->addArgument("script", InputArgument::OPTIONAL, "The script too run (see --list)");
 | 
				
			||||||
 | 
					        $this->addArgument("args", InputArgument::OPTIONAL|InputArgument::IS_ARRAY, "Arguments to the script");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected function execute(InputInterface $input, OutputInterface $output)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $env = $this->getEnvironment();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while (true) {
 | 
				
			||||||
 | 
					            $cmd = readline("repl> ");
 | 
				
			||||||
 | 
					            if ($cmd) readline_add_history($cmd);
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                $ret = @eval("return {$cmd};");
 | 
				
			||||||
 | 
					                //$output->writeln(json_encode($ret,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
 | 
				
			||||||
 | 
					                //$output->writeln("<info>".var_export($ret,true)."</>");
 | 
				
			||||||
 | 
					                dump($ret);
 | 
				
			||||||
 | 
					            } catch (\Throwable $t) {
 | 
				
			||||||
 | 
					                $output->writeln("<error>".$t->getMessage()."</>");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Command::SUCCESS;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										57
									
								
								src/Commands/ResourcesCommand.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/Commands/ResourcesCommand.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Spark\Commands;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Spark\SparkApplication;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Attribute\AsCommand;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Helper\Table;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Input\InputInterface;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Input\InputOption;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Output\OutputInterface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[AsCommand(name:'resources', description:'List defined resources')]
 | 
				
			||||||
 | 
					class ResourcesCommand extends Command
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    protected function configure()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected function execute(InputInterface $input, OutputInterface $output)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $env = $this->getEnvironment();
 | 
				
			||||||
 | 
					        $app = $this->getApplication();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /** @var SparkApplication $app */
 | 
				
			||||||
 | 
					        $resources = $app->getResourceManager();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        $types = $resources->getAllResourceTypes();
 | 
				
			||||||
 | 
					        $named = $resources->getAllNamedResources();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $output->writeln("<options=bold>Resource Types:</>");
 | 
				
			||||||
 | 
					        $table = new Table($output);
 | 
				
			||||||
 | 
					        $table->setStyle("compact");
 | 
				
			||||||
 | 
					        $table->setHeaders([ "Type", "Class" ]);
 | 
				
			||||||
 | 
					        $table->setColumnWidth(0, 10);
 | 
				
			||||||
 | 
					        $map = [];
 | 
				
			||||||
 | 
					        foreach ($types as $type=>$class) {
 | 
				
			||||||
 | 
					            $map[$class] = $type;
 | 
				
			||||||
 | 
					            $table->addRow([ $type, $class ]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $table->render();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $output->writeln("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $output->writeln("<options=bold>Named Resources:</>");
 | 
				
			||||||
 | 
					        $table = new Table($output);
 | 
				
			||||||
 | 
					        $table->setStyle("compact");
 | 
				
			||||||
 | 
					        $table->setHeaders([ "Name", "Type", "Info" ]);
 | 
				
			||||||
 | 
					        $table->setColumnWidth(0, 10);
 | 
				
			||||||
 | 
					        $table->setColumnWidth(1, 6);
 | 
				
			||||||
 | 
					        foreach ($named as $name=>$class) {
 | 
				
			||||||
 | 
					            $table->addRow([ $name, $map[get_class($class)], $class->info() ]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $table->render();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Command::SUCCESS;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										35
									
								
								src/Commands/RunCommand.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/Commands/RunCommand.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Spark\Commands;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Attribute\AsCommand;
 | 
				
			||||||
 | 
					use Spark\Commands\Command;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Input\InputArgument;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Input\InputInterface;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Input\InputOption;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Output\OutputInterface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[AsCommand(name:'run', description:'Run a script')]
 | 
				
			||||||
 | 
					class RunCommand extends Command
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    protected function configure()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->addOption("list", null, InputOption::VALUE_NONE, "List the available scripts");
 | 
				
			||||||
 | 
					        $this->addArgument("script", InputArgument::OPTIONAL, "The script too run (see --list)");
 | 
				
			||||||
 | 
					        $this->addArgument("args", InputArgument::OPTIONAL|InputArgument::IS_ARRAY, "Arguments to the script");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected function execute(InputInterface $input, OutputInterface $output)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $env = $this->getEnvironment();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($input->getOption("list")) {
 | 
				
			||||||
 | 
					            $output->writeln($env->getDefinedScripts());
 | 
				
			||||||
 | 
					            return Command::SUCCESS;
 | 
				
			||||||
 | 
					        } elseif ($script = $input->getArgument('script')) {
 | 
				
			||||||
 | 
					            $env->runScript($script, $input->getArgument('args'), $input, $output);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Command::SUCCESS;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										135
									
								
								src/Environment/Environment.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								src/Environment/Environment.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,135 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Spark\Environment;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Psr\Log\LoggerAwareInterface;
 | 
				
			||||||
 | 
					use Psr\Log\LoggerAwareTrait;
 | 
				
			||||||
 | 
					use Spark\SparkApplication;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Input\InputInterface;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Output\OutputInterface;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Style\SymfonyStyle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Environment
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private bool $loaded = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private array $config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct(array $config)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->config = $config;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getProjectDirectory(): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->config['project_dir'];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getConfigDirectory(): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->config['config_dir'];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getDefinedScripts(): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return array_keys($this->config['scripts']??[]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function runScript(string $name, array $args, InputInterface $input, OutputInterface $output)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $script = $this->config['scripts'][$name];
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (is_string($script)) {
 | 
				
			||||||
 | 
					            // 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;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                include $base . "/" . $script;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                passthru($script);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function loadEnvironment()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($this->loaded) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!array_key_exists('project_dir', $this->config)) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // $this->logger->info("Loading environment...");
 | 
				
			||||||
 | 
					        $preloads = [];
 | 
				
			||||||
 | 
					        $root = $this->config['project_dir'];
 | 
				
			||||||
 | 
					        foreach ((array)$this->config['preload']??[] as $preload) {
 | 
				
			||||||
 | 
					            if (str_contains($preload,'*')) {
 | 
				
			||||||
 | 
					                array_push($preloads, ...glob($root."/".$preload));
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                array_push($preloads, $preload);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        foreach ($preloads as $item) {
 | 
				
			||||||
 | 
					            if (!str_starts_with($item, "/")) {
 | 
				
			||||||
 | 
					                $item = $this->getProjectDirectory() . "/" . $item;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (is_file($item)) {
 | 
				
			||||||
 | 
					                // $this->logger->debug("Preloading file {$item}");
 | 
				
			||||||
 | 
					                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());
 | 
				
			||||||
 | 
					                    //$this->logger->error("Error preloading {$item}: {$t->getMessage()} in {$t->getFile()} on line {$t->getLine()}");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } elseif (is_dir($item) && file_exists($item."/sparkplug.php")) {
 | 
				
			||||||
 | 
					                //$this->logger->debug("Preloading plugin {$item}");
 | 
				
			||||||
 | 
					                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());
 | 
				
			||||||
 | 
					                    //$this->logger->error("Error preloading plugin {$item}: {$t->getMessage()} in {$t->getFile()} on line {$t->getLine()}");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                fprintf(STDERR, "warning: Could not preload %s\n", $item);
 | 
				
			||||||
 | 
					                //$this->logger->warning("Could not preload {$item}");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        SparkApplication::$instance->getPluginManager()->initializePlugins();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static function createFromDirectory(string|null $directory=null): Environment
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $directory = $directory ?? getcwd();
 | 
				
			||||||
 | 
					        $candidates = [ $directory . "/.spark.json", $directory . "/.spark/spark.json" ];
 | 
				
			||||||
 | 
					        $config = [];
 | 
				
			||||||
 | 
					        while ($candidate = array_shift($candidates)) {
 | 
				
			||||||
 | 
					            if (!file_exists($candidate)) { continue; }
 | 
				
			||||||
 | 
					            $json = file_get_contents($candidate);
 | 
				
			||||||
 | 
					            if (!$json || json_last_error()) {
 | 
				
			||||||
 | 
					                throw new \RuntimeException("Error parsing {$candidate}: ".json_last_error_msg());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $config = json_decode($json, true);
 | 
				
			||||||
 | 
					            $config['project_dir'] = realpath($directory);
 | 
				
			||||||
 | 
					            $config['config_dir'] = realpath($directory)."/.spark";
 | 
				
			||||||
 | 
					            $config['config_file'] = $candidate;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $env = new Environment($config);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $env;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										27
									
								
								src/Plugin/PluginManager.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/Plugin/PluginManager.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Spark\Plugin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use SparkPlug;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PluginManager
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    private $plugins = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function registerPlugin(string $name, SparkPlug $plugin)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->plugins[$name] = $plugin;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function initializePlugins()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        foreach ($this->plugins as $plugin) {
 | 
				
			||||||
 | 
					            $plugin->load();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getPlugin(string $name)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->plugins[$name];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										46
									
								
								src/Resource/ResourceManager.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/Resource/ResourceManager.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Spark\Resource;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use ReflectionClass;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ResourceManager
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    private $resourceTypes = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private $namedResources = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function registerResourceType(string $name, string $type)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $refl = new ReflectionClass($type);
 | 
				
			||||||
 | 
					        $this->resourceTypes[$name] = $type;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function createResource(string $type, array $options)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $resource = new $this->resourceTypes[$type]($options);
 | 
				
			||||||
 | 
					        return $resource;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function createNamedResource(string $name, string $type, array $options)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $resource = $this->createResource($type, $options);
 | 
				
			||||||
 | 
					        $this->namedResources[$name] = $resource;
 | 
				
			||||||
 | 
					        return $resource;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getNamedResource(string $name)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->namedResources[$name] ?? null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getAllNamedResources(): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->namedResources;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getAllResourceTypes(): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->resourceTypes;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										8
									
								
								src/Resource/ResourceType.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/Resource/ResourceType.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Spark\Resource;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					abstract class ResourceType
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    abstract public function info();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										73
									
								
								src/SparkApplication.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/SparkApplication.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Spark;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Psr\Log\LoggerInterface;
 | 
				
			||||||
 | 
					use Spark\Environment\Environment;
 | 
				
			||||||
 | 
					use Spark\Plugin\PluginManager;
 | 
				
			||||||
 | 
					use Spark\Resource\ResourceManager;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Application;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Input\InputInterface;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Logger\ConsoleLogger;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Output\OutputInterface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SparkApplication extends Application
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public static SparkApplication $instance;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private Environment $environment;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private ResourceManager $resourceManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private PluginManager $pluginManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private LoggerInterface $logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        parent::__construct("Spark", "dev");
 | 
				
			||||||
 | 
					        self::$instance = $this;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->resourceManager = new ResourceManager();
 | 
				
			||||||
 | 
					        $this->pluginManager = new PluginManager();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->environment = Environment::createFromDirectory();
 | 
				
			||||||
 | 
					        $this->environment->loadEnvironment();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->add(new Commands\RunCommand());
 | 
				
			||||||
 | 
					        $this->add(new Commands\ResourcesCommand());
 | 
				
			||||||
 | 
					        $this->add(new Commands\ReplCommand());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getPluginManager(): PluginManager
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->pluginManager;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getEnvironment(): Environment
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // if (empty($this->environment)) {
 | 
				
			||||||
 | 
					        //     $this->environment = Environment::createFromDirectory();
 | 
				
			||||||
 | 
					        //     $this->environment->setLogger($this->logger);
 | 
				
			||||||
 | 
					        // }
 | 
				
			||||||
 | 
					        return $this->environment;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getResourceManager(): ResourceManager
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->resourceManager;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getLogger(): LoggerInterface
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->logger;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function doRun(InputInterface $input, OutputInterface $output)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->logger = new ConsoleLogger($output);
 | 
				
			||||||
 | 
					        parent::doRun($input, $output);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user