henzeb/laravel-console-facade

与控制台交互的便捷 facade

v1.19.0 2024-03-11 07:55 UTC

README

Build Status Latest Version on Packagist Total Downloads Test Coverage License

此包允许您从不在命令类内部的任何地方管理控制台输出。

由于我的应用程序要求逻辑不直接在命令类内部,我发现自己不得不在构造函数中添加输出,创建了丑陋且不可重用的代码。这简化了我的过程,现在也简化了您的过程。

安装

只需使用以下命令安装。

composer require henzeb/laravel-console-facade

用法

底层使用 InteractsWithIO trait,因此您可以通过 facade 执行在命令内部可以执行的所有输出操作。

use Henzeb\Console\Facades\Console;
class MyClass {
   
    public function writeMessage(): void
    {   
        Console::ask('Would you like to be able to do this?');
        Console::info('This message was brought to you by Henzeb');
    }
}

控制台助手

除了使用 Console facade,您还可以使用 console 助手方法。

use function Henzeb\Console\Functions\console;

console('hello'); // outputs hello
console()->info('hello'); // outputs hello
console()->ask('Want an answer?'); // asks you a question

注意:在整个文档中使用了 facade,但一切都可以通过助手方法访问。

Laravel 组件工厂

Laravel 发布了他们命令的新样式,并为此使用了一个特殊的 Factory。使用此方法,您可以在自己的类中使用它们。

use Henzeb\Console\Facades\Console;
class MyClass {
   
    public function writeMessage(): void
    {   
        Console::components()->ask('Would you like to be able to do this?');
        Console::components()->info('This message was brought to you by Henzeb');
        Console::components()->bulletList(['this one', 'Another one']);
    }
}

部分管理

该 facade 还允许您管理和使用部分。在部分内部,您只能使用 InteractsWithIO 中的输出方法,如 tableprogressbarinfo,这意味着不能执行提问。

use Henzeb\Console\Facades\Console;
class MyClass {
   
    public function useSection(): void
    {   
        Console::section()->table(['header'=>'title'], [[]]);
        Console::section('section2')->withProgressBar(100, fn()=>true);
        Console::section('section1')->components()->bulletList(['this one', 'Another one']);
        Console::section('section1')->clear();
        Console::section('section3')->info('This message was brought to you by Henzeb');
    }
}

当您不传递名称时,每次调用部分时都会分配一个 uniqid。您可以通过以下方式检索此部分的名称

$section = Console::section();
$section->name(); //returns string similar to 64350abe27355
$section = Console::section('section1');
$section->name(); //returns section1

延迟渲染

延迟渲染在您必须从头开始重建事物时非常有用,例如一个需要大量时间的表格。使用此方法,所有内容首先生成,然后再输出到控制台。

use Henzeb\Console\Facades\Console;

use Henzeb\Console\Output\ConsoleSectionOutput; 

class MyClass {

    public function renderWhenReady(): void
    {   
        Console::section()->render(
            function(ConsoleSectionOutput $section){
                $section->table(['header'=>'title'], [[]]);
            }
        );
        
        Console::section(
            'section2', 
            function(ConsoleSectionOutput $section){
                $section->table(['header'=>'title'], [[]]);
            }
        );
        
    }
}

替换

当涉及到重复渲染时,Symfony 的默认 overwrite 方法有点慢。如果您发现您的控制台应用程序闪烁,replace 是一个好的选择。

注意:renderwatchtail 以及 section 上的回调方法都在底层使用 replace

监视

watch 是一个模仿 Linux 中 watch 命令的方法。默认情况下,它将每 2 秒执行一次给定的回调。

Console::watch(
    function (ConsoleSectionOutput $output) {
        $output->info(now()->toDateTimeString());
    },
);

您也可以指定刷新率以加快或减慢循环。

Console::watch(
    function (ConsoleSectionOutput $output) {
        $output->info(now()->toDateTimeString());
    },
    1
);

您还可以指定部分的名称。这样,您就可以在例如 trap 信号中操作部分。

Console::watch(
    function (ConsoleSectionOutput $output) {
        $output->info(now()->toDateTimeString());
    },
    sectionName: 'yourName'
);

尾部

tail 可以用来“滚动”添加的行。就像 Linux 命令一样,tail 显示您最后 n 行。

Console::tail(); // returns a scrollable section with 10 lines
Console::tail(5); // returns a scrollable section with 5 lines
Console::tail(10, 'mySection'); // returns a scrollable mySection section

Console::section('mySection')->tail(10); 
Console::section('mySection')->tail(10)->tail(5);//downgrades height to 5

Console::section('mySection')->setMaxHeight(10); // uses Symfony's implementation
Console::tail()->setMaxHeight(15); // upgrades height to 15

在 Symfony 的 ConsoleSectionOutput 中已经存在这样做的方法,但存在许多问题。此实现修复了这些问题,并允许您轻松使用任何输出,如进度条和表格。

您仍然可以在常规部分中使用 Symfony 的实现。

退出

退出允许您在代码的任何地方调用退出,同时使其易于测试。

Console::exit();
Console::exit(1);

退出钩子

您还可以添加在调用 exit 时执行的钩子。请注意,它不会将它们注册为关闭函数。

Console::onExit(
    function(int $exitcode) {
        Console::info('exited with code '.$exitcode);
    }
);

Console::onExit(
    function() {
        Console::info('exited with code 123');
    },
    123
);

捕获

就像Laravel一样,有一个trap方法来注册信号。在底层,这并不是使用Laravel和Symfony为向后兼容性而创建的逻辑,但它是相似的。更多信息请参见#43933

在以下场景中,当给SIGINT信号时,三个都会运行,当给SIGTERM信号时,第二个也会运行。第一个处理程序返回true。这意味着在所有处理程序执行后,会给出一个退出信号。

Console::trap(
    function () {
        print('first handler');
        return true;
    },
    SIGINT
);

Console::trap(
    function () {
        print('second handler');
        var_dump(func_get_args());
        return false;
    },
    SIGINT,
    SIGTERM
);

Console::trap(
    function () {
        print('third handler');
    },
    SIGINT
);

重新捕获

trap允许你捕获新的信号处理程序。这在你想要能够连续按两次CTRL+C时很有用。在下面的例子中,下次收到信号时,应用将强制退出。

Console::trap(
    function () {
        print('first handler');
        
        Console::trap(
            function () {
                print('second handler');
                return true;
            }, 
            SIGINT
        );
    },
    SIGINT
);

提示:当处理程序已经通过常规方式或通过Laravel的实现注册时,你可以使用pcntl_signal_get_handler将其传递给trap

注意:这之前是onSignal,但我已经废弃了这个方法,因为Laravel正在使用trap

取消捕获

和Laravel一样,有一个取消捕获的方法。这个方法会像Laravel实现那样自动调用,所以你可以在你的命令中使用Artisan::call而不执行错误的信号处理程序。

Console::untrap();

合并选项和参数

在某些情况下,你可能想要合并选项或参数,比如恢复一个带有存储在缓存中的特定选项或参数的进程,或者重新配置一个正在运行的守护进程。

Console::mergeOptions(['env'=>'production']);

Console::mergeArguments(['yourArgument'=>true]);

当选项或参数通过命令行设置时,该值将优先。

optionGiven和argumentGiven

在Laravel的Command中,确定用户是否指定了选项或参数可能会变得相当混乱。具有可选参数的选项在设置或未设置时都返回null。当你设置默认值时,你可以弄清楚它,但这并不真正友好,感觉像是黑客式而非整洁的代码。

以下方法告诉你用户是否已将选项或参数添加到命令行

// artisan your:command --check --test=false
Console::optionGiven('check');   // returns true
Console::optionGiven('test');    // returns true
Console::optionGiven('verify');  // returns false

// artisan your:command verify
Console::argumentGiven('check');    //returns false
Console::argumentGiven('verify');   //returns true

验证

无论你构建的是将要分发的控制台应用,还是只想确保没有人能破坏你的应用,你都需要使用验证。Laravel控制台外观使得这变得非常容易。

假设你想要验证以下签名的输入

{id?} {--name=} {--age=*} {--birth=}

configure方法中,你只需定义以下内容

Console::validateWith(
    [
        'id' => 'bail|int|exists:users',
        '--name'=>'string|min:2',
        '--age.*' => 'bail|int|between:0,150',
        '--birth' => 'bail|prohibits:--age|date'
    ]
);

当运行你的命令时,验证将自动执行。

在底层,这使用了Laravel的验证引擎,因此你可以使用验证引擎接受的任何内容。

注意事项:当你想要验证选项与可能未传递的参数或选项时,你可能需要编写自己的Rule或闭包。

消息

由于翻译主要基于来自HTTP请求的输入,你可能想要给出不同的翻译。就像使用Laravel的验证引擎一样,添加一个第二个数组。

Console::validateWith(
    [
        'id' => 'bail|int|exists:users',
        '--name' => 'string|min:2',
        '--age.*' => 'bail|exclude_with:--birth|int|between:0,150',
        '--birth' => 'bail|prohibits:--age|date',
    ],
    [
        'exists' => 'User with given id does not exist!'
        'prohibits' => 'Cannot be used together with :other'
    ]   
);

属性名称

Laravel允许你重命名属性。

Console::validateWith(
    [
        'id' => 'bail|int|exists:users',
        '--name' => 'string|min:2'
    ],
    attributes: [
        'id' => 'user id',
        '--name' => 'name'
    ]
);

值名称

就像属性一样,你也可以给某些值命名。

下面我们看到了一个例子,其中接受的标志必须在指定任何性别时是true的形式。

Console::validateWith(
    [
        '--gender' =>'required|in:x,f,m',
        '--accepted' => 'accepted_if:--gender,x,--gender,f,--gender,m',
    ],
    valueNames: [
        '--gender' => [
            'm' => 'male',
            'f' => 'female', 
            'x' => 'other gender'
        ]       
    ]
);

在验证之前的回调

当你需要在执行之前访问Validator实例时,你可以使用beforeValidation方法。

Console::beforeValidation(
    function(Illuminate\Validation\Validator $validator){
        // your logic
    }
);

基于闭包的命令

当运行用Artisan::command()定义的ClosureCommands时,它不会自动验证。相反,你可以做以下操作

Artisan::command(
    'your:command {id?} {--name=} {--age=*} {--birth=}',
    function () {

        Console::validateWith(
            [
                //
            ]
        );

        Console::validate();
        
        //
    }
);

详细程度

控制台为你提供了在应用内部处理详细程度的易于使用的界面。

Console::verbose('verbose'); // only prints `verbose` when -v or higher is passed.
Console::veryVerbose('very verbose'); // only prints `very verbose` when -vv or higher is passed.
Console::debug('debug'); // only prints `debug` when -vvv is passed.

这些方法使用以下样式进行着色,如果您愿意,可以覆盖它们。

高级详细程度

除了简单的线条外,控制台还允许您使用任何可用的输出方法。

Console::verbose()->info('info'); // only prints `info` when -v or higher is passed.

Console::debug()->ask('debug?'); // only asks when -vvv is passed, returns null otherwise

Console::veryVerbose()->ask('very verbose?', 'no'); // only asks when -vv or higher is passed, returns 'no' otherwise

Console::verbose()->withProgressbar(2, fn() => true); // only shows the progressbar when -v or higher is passed

注意:由于详细程度,进度条未显示,但给定的可调用函数仍然被执行。另外请注意,当详细程度不匹配时,监视将完全不会运行。

详细程度和部分

详细程度接口也支持部分。

Console::section('mySection')->debug('debug'); // only prints `debug` when -vvv is passed.
Console::section('mySection')->debug()->info('info');// only prints `info` when -vvv is passed.

Console::debug()->section('mySection')->info('info');// only prints `info` when -vvv is passed.

注意:请注意,详细部分与非详细部分相同。这意味着您不能只清除部分内的详细输出,因为这将会清除整个部分。

静音

静音是一个基于布尔值隐藏元素(如进度条)的方便方法。

Console::silence(false)->info('test'); // prints test
Console::silence(true)->info('test'); // prints nothing
Console::silence(false)->debug('test'); // prints test when -vvv is passed.
Console::silence(true)->debug('test'); // prints nothing, even when -vvv is passed.

Console::section('section')->silence(true)->info('test'); // prints nothing
Console::section('section')->silence(false)->info('test'); // prints test

Console::silence(true)->withProgressBar(5, fn()=>true); // runs the callback, but won't show progress
Console::silence(false)->withProgressBar(5, fn()=>true); // runs the callback, and shows progress

您甚至可以链式静音,这样输出只有在提供参数组合时才会显示

Console::silence(false)->silence(false); // shows output
Console::silence(true)->silence(false); // shows no output
Console::silence(false)->silence(true); // shows no output
Console::silence(true)->silence(true); // shows no output

取消静音

取消静音是静音的直接对立面。

Console::unsilence(true)->info('test'); // prints test
Console::unsilence(false)->info('test'); // prints nothing
Console::unsilence(true)->debug('test'); // prints test when -vvv is passed.
Console::unsilence(false)->debug('test'); // prints nothing, even when -vvv is passed.

Console::section('section')->unsilence(false)->info('test'); // prints nothing
Console::section('section')->unsilence(true)->info('test'); // prints test

Console::unsilence(false)->withProgressBar(5, fn()=>true); // runs the callback, but won't show progress
Console::unsilence(true)->withProgressBar(5, fn()=>true); // runs the callback, and shows progress

注意:您可以用静音做的一切,都可以用取消静音来做。您甚至可以在链式命令中混合它们。

控制台外观和Henzeb\Console\Output\ConsoleSectionOutput可以使用Laravel的Macroable特性进行宏操作。

Console::macro(...)
Henzeb\Console\Output\ConsoleSectionOutput::macro(...)

请参阅文档

条件

您可以在外观以及内部部分中使用whenunless,就像您习惯的那样。请参阅文档

测试

除了常用的外观测试选项外,我还添加了一些方便的方法,用于在测试中使用。

Console::shouldExit();
Console::shouldNotExit();
Console::shouldExitWith(int $seconds);
Console::shouldSleep();
Console::shouldNotSleep();
Console::shouldSleepWith(int $seconds);
Console::watchShouldLoop(int $times, int $sleep = null);

测试此包

composer test

变更日志

请参阅变更日志以获取有关最近更改的更多信息。

贡献

请参阅贡献指南以获取详细信息。

安全

如果您发现任何与安全相关的问题,请通过电子邮件henzeberkheij@gmail.com联系,而不是使用问题跟踪器。

致谢

许可证

GNU AGPLv。请参阅许可证文件以获取更多信息。