marwanalsoltany / blend
一款轻量级的PHP任务执行器,注重简洁性。
Requires
- php: ^7.4|^8.0
- ext-json: *
Requires (Dev)
- phpunit/phpunit: ^9.5
README
一款轻量级的PHP任务执行器,注重简洁性。
如果您喜欢这个项目并且想支持其开发,给它一个 ⭐ 会非常感激!
主要特性
- 极快
- 易于配置
- 无依赖
关于Blend
Blend是一款功能丰富且轻量级的PHP任务执行器,注重简洁性。Blend的诞生源于对尝试管理多个具有不同界面和名称、位置、语法、选项/参数的CLI工具和/或脚本的沮丧。使用Blend及其直观且强大的API,您只需做一次,它将像其名称所暗示的那样为您“融合”。Blend将以您喜欢的形式展示这些CLI工具和/或脚本,并让您有机会以任务的形式从单个美丽且富有洞察力的界面访问它们,该界面将在整个过程中协助您、提供反馈和建议。
为了使其尽可能便携和简单,Blend包仅包含一个类(TaskRunner
),这个类为您完成所有魔法。
Blend被设计为灵活,可以以多种方式安装,每种方式都有其优点和缺点。选择最适合您和您需求的方法。查看安装部分以获取更多详细信息。
Blend为什么存在?
Blend最初是为了个人使用而开发的。然而,它已经走得很远,值得发布。它可能不是您所寻找的,所以请仔细查看。
安装
使用Composer
通过Composer使用以下命令安装Blend:
composer require marwanalsoltany/blend
这是安装Blend的推荐方式。使用此安装方法,Blend将被安装得就像任何正常的Composer包一样。您可以使用当前工作目录中的配置文件通过vendor/bin/blend
可执行文件与之交互,或者通过在独立文件中引入它并程序化地提供配置来实现。当然,您也可以全局安装它,使其在系统范围内可访问。
使用PHAR
从发布页面或使用以下命令之一下载Blend PHAR存档:
php -r "copy('https://git.io/JEseO', 'blend');"
php -r "copy('https://github.com/MarwanAlsoltany/blend/releases/latest/download/blend.phar', 'blend');"
或使用以下命令下载特定版本(将vX.X.X
替换为您想要的版本):
php -r "copy('https://github.com/MarwanAlsoltany/blend/releases/download/vX.X.X/blend.phar', 'blend');"
使用此安装方法,您将获得一个便携的PHAR存档,您可以将其放置在任何您想要的地方,甚至将其包含在PATH
中以方便访问。使用此安装方法,您必须提供一个配置文件来配置/自定义Blend。此安装方法存在是为了便携性,其中Blend不受特定项目的限制,且配置文件就足够了。从v1.0.3
开始,PHAR安装方法通过任务update
(在v1.1.0
之前被称为)与其它方法区分开来,该任务将更新您的PHAR到本存储库中可用的最新版本。phar:update
使用安装程序
直接从存储库下载Blend安装程序,或使用以下命令之一:
php -r "copy('https://git.io/JEseR', 'setup');" && php setup
php -r "copy('https://raw.githubusercontent.com/MarwanAlsoltany/blend/master/bin/setup', 'setup');" && php setup
使用此方法,Blend可执行文件和源代码将安装到当前工作目录(以便在配置Blend时利用IDE的Intellisense功能)。使用此安装方法,您可以使用blend
可执行文件或通过在CWD中提供配置文件来程序化地配置Blend。这种安装方法仅适用于旧项目,在这些项目中无法使用Composer,并且需要程序化配置。
配置
可以使用两种可用的配置格式之一来配置Blend
PHP配置 blend.config.php
<?php return [ 'autoload' => null, 'merge' => true, 'executables' => [ 'php' => [ './bin/*', ], ], 'translations' => [ 'abc' => 'xyz', ], 'ansi' => true, 'quiet' => false, 'tasks' => [ 'some:task' => [ 'name' => 'some:task', 'description' => 'Some task', 'executor' => 'shell', 'executable' => 'ls', 'arguments' => '-lash', 'hidden' => false, 'disabled' => false, ], ], ];
JSON配置 blend.config.json
:(推荐)
{ "$schema": "https://raw.githubusercontent.com/MarwanAlsoltany/blend/master/config/schema.json", "autoload": null, "merge": true, "executables": { "php": [ "./bin/*" ] }, "translations": { "abc": "xyz" }, "ansi": true, "quiet": false, "tasks": { "some:task": { "name": "some:task", "description": "Some task", "executor": "shell", "executable": "ls", "arguments": "-lash", "hidden": false, "disabled": false } } }
注意: 请参阅
config/blend.config.php
和config/schema.json
以了解预期的数据类型。请注意,JSON配置有一些限制(例如回调任务),因此请检查这两个文件。
配置加载是如何工作的?
Blend会尝试从当前工作目录加载配置,如果找不到任何内容,它将向上移动一层,并在父目录中查找,依此类推,直到它到达根目录。如果在根目录中也找不到任何内容,Blend将不带配置启动。
事实: 尽管推荐使用JSON配置格式,但PHP配置具有优先级。这意味着,如果在同一目录中找到这两种配置格式,将加载PHP配置而不是JSON配置。这仅仅是因为PHP配置可以执行,因此更强大。
示例
一个基本的Blend可执行文件
#!/usr/bin/env php <?php use MAKS\Blend\TaskRunner as Blend; $blend = new Blend(); $blend->start();
一个更高级的Blend可执行文件
#!/usr/bin/env php <?php use MAKS\Blend\TaskRunner as Blend; $blend = new Blend([ // files in "./php/bin" will be loaded as tasks and get executed using PHP 'php' => [ './php/bin/*', ], // files in "./js/bin" with the JS extension will be loaded as tasks and get executed using Node 'node' => [ './js/bin/*.js', ], ]); // available arguments: $executables, $translations, $config, $ansi, $quiet $blend->setName('My Task Runner'); $blend->setVersion('vX.X.X'); // NOTE: these tasks are for demonstration purposes only // adding a shell task $blend->addShellTask('ls', 'Lists content of CWD or the passed one.', 'ls', '-lash'); // adding a callback task $blend->addCallbackTask('whoami', null, function () { /** @var Blend $this */ $this->say('@task'); // using the @task placeholder to get a string representation of the task object }); $blend->disableTask('whoami'); // preventing the task from being ran $blend->hideTask('whoami'); // preventing the task from being listed // alternatively, you can use the makeTask() method to pass all arguments at once $blend->makeTask([ 'name' => 'runner', 'description' => null, 'executor' => Blend::CALLBACK_TASK, 'executable' => static function ($runner) { // functions that can't be bound, get the runner as the first argument $runner->say('@runner'); }, 'arguments' => null, 'hidden' => true, 'disabled' => true, ]); // extending the task runner with an additional method // NOTE: this implementation is for demonstration purposes only $blend->extend('pipe', function (string ...$tasks) { $results = []; static $previous = null; foreach ($tasks as $task) { $current = $this->getTask($task); // skip if task does not exist if (!$current) { continue; } // pass the result as an argument if the next task is a callback task if ($current->executor === Blend::CALLBACK_TASK) { $current->arguments[] = $previous; $previous = $results[$task] = $this->runTask($task); continue; } // if not a callback task, then it is a shell task (CLI command) // execute the task and cache the result for the next task $this->runTask($task); // with shell tasks, we're interested in the whole result of the task (command) // and not only its exit code, that's why we're using getExecResults() method instead $previous = $results[$task] = $this->getExecResult(); } return $results; }); // now you can use the newly created method to pipe tasks together $blend->addCallbackTask('piped:tasks:run', 'Executes piped tasks.', function () { /** @var Blend $this */ $this->say('Running piped tasks ...'); $this->pipe( 'example:task:1', 'example:task:2', 'example:task:3' // ... ); $this->say('Finished piping!'); }); $blend->sort(); $blend->start();
Blend可执行文件的实际示例(PHP开发服务器)
#!/usr/bin/env php <?php use MAKS\Blend\TaskRunner as Blend; $blend = new Blend(); $cwd = getcwd(); $blend->addCallbackTask( 'server:start', 'Starts a PHP Development Server in CWD', function ($cwd) { /** @var Blend $this */ if (file_exists("{$cwd}/.pid.server")) { $this->say('An already started PHP Development Server has been found.'); return Blend::FAILURE; } $pid = $this->exec("php -S localhost:8000 -t {$cwd}", true); // passing true runs the command asynchronously // you can use $this->getExecResult() method to get all additional info about the executed command. $this->say("Started a PHP Development Server in the background with PID: [{$pid}]"); file_put_contents("{$cwd}/.pid.server", $pid); return Blend::SUCCESS; }, [$cwd] // passing arguments to tasks callback ); $blend->addCallbackTask( 'server:stop', 'Stops a started PHP Development Server in CWD', function ($cwd) { /** @var Blend $this */ if (!file_exists("{$cwd}/.pid.server")) { $this->say('No started PHP Development Server has been found.'); return Blend::FAILURE; } $pid = trim(file_get_contents("{$cwd}/.pid.server")); $this->exec(PHP_OS === 'WINNT' ? "tskill {$pid}" : "kill -15 {$pid}"); $this->say("Stopped PHP Development Server with PID: [{$pid}]"); unlink("{$cwd}/.pid.server"); return Blend::SUCCESS; }, [$cwd] ); $blend->addCallbackTask( 'server:restart', 'Restarts the started PHP Development Server in CWD', function () { /** @var Blend $this */ $this->say('Restarting the PHP Development Server'); $this ->setQuiet(true) // disable output temporarily ->run('server:stop') ->run('server:start') ->setQuiet(false); // enable output again // use the runTask() method instead to get the return value of the called task // return $this->runTask('server:stop') & $this->runTask('server:start'); } ); $blend->addCallbackTask( 'server:cleanup', 'Removes ".pid.server" file from CWD if available', function ($cwd) { /** @var Blend $this */ if (file_exists($file = "{$cwd}/.pid.server")) { if (unlink($file)) { $this->say('Removed ".pid.server" file successfully.'); } else { $this->say('Failed to remove ".pid.server" file!'); return Blend::FAILURE; } } else { $this->say('Nothing to clean up!'); } return Blend::SUCCESS; }, [$cwd] ); $blend->start();
注意: Blend从包含它的可执行文件名称中获取ID(
$argv[0]
)。因此,如果您要将包含它的文件重命名为其他名称,所有Blend输出都将反映这一新更改(帮助信息、建议等)。环境变量和配置文件名称也应与新的名称匹配。
提示:
TaskRunner
类有很好的文档,如果您对Blend API有任何疑问,请参阅其方法中的DocBlocks,您可能会在那里找到答案。
API
以下是Blend的完整API(TaskRunner
类)。
注意: 这里列出了
TaskRunner::class
的完整API(包括私有和受保护的成员),因为您大多通过使用具有对私有作用域访问权限的TaskRunner::extend()
方法来扩展Blend。
常量
属性
公共方法
受保护的方法
私有方法
魔术方法
许可证
Blend是一个开源项目,根据MIT许可证授权。
版权(c)2021 Marwan Al-Soltany。保留所有权利。