0%

输出进度条

有时候我们想在命令行执行一些耗时的命令,我们可以利用 symfony 提供的进度条相关的类,来输出一个进度条,显示当前的处理进度。

参考:Symfony ProgressBar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php

namespace App\Commands;

use Illuminate\Console\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\ConsoleOutput;

class Test extends Command
{
protected $signature = 'test';

/**
* Execute the command.
*
* @return void
*/
public function handle()
{
//
$output = new ConsoleOutput();
$progressBar = new ProgressBar($output, 1000);
$progressBar->setFormat(" %elapsed:6s%/%estimated:-6s% 内存消耗: %memory:6s%\n%current%/%max% [%bar%] %percent:3s%%");

foreach (range(1, 1000) as $_) {
usleep(5000);
$progressBar->advance();
}

$progressBar->finish();
echo "\n";
}
}

效果:

x

输出多个进度条:

1
2
3
$progressBar->start();
print "\n";
$progressBar->start();

中间需要打印一个换行符

输出动态变化的变量

有时候我们想在命令执行过程中显示一个动态变化的数,同时又想输出的格式保持不变,我们可以使用 setMessage 方法

1
2
$progressBar->setFormat("已处理数量: %count%");  // 这里是一个占位符,可以和进度条写在一起
$progressBar->setMessage($count, 'count'); // 使用 `$count` 替换输出内容中的 "%$count%"

具体效果类似:

1
已处理数量: 1 (这里的 1 会变更,而不是另起一行输出)

有可能是配置缓存导致的,

运行:

1
php artisan config:clear

可清除配置缓存,配置缓存保存在 bootstrap/cache/config.php,可以直接去那文件夹看看是不是缓存导致的。

还有另外一个缓存文件 bootstrap/cache/services.json,上面的命令只是清除配置,下面的命令可以同时清除这两个缓存文件。

1
php artisan clear-compiled
1
2
3
php artisan config:clear
php artisan clear-compiled // 还不行再运行下面的
php artisan optimize

使用 MacroableBuilder 添加自定义方法

Laravel 中提供了 Macroabletrait,之前一直没有想过可以用上这个东西。

最近才想到可以这么做,源码看这里:https://github.com/laravel/framework/blob/5.6/src/Illuminate/Support/Traits/Macroable.php

目前用的是 5.1 版本,应该 5.1 版本以后的都有这个特性。

当然,你也可以在 Model 基类里面添加自定义方法,但是这样添加的方法 DB 类是用不了的。

但是使用 macro 定义到 Builder 的方法可以同时在 DB 类和 Eloquent Model 中使用(具体不赘述,自己看源码,就是利用 __call__callStatic 这些魔术方法)。

定义

使用方法:调用 Macroablemacro 方法,绑定一个自定义方法到 Builder 类中,如:

1
2
3
4
\Illuminate\Database\Query\Builder\Builder::macro('active', function () {
return $this->where('status', 1);
});

使用

调用方法是(使用 DB 类):

1
DB::table(xxx)->active()->get();

或者(使用 Eloquent Model):

1
\App\Model\User::active()->first();

放在哪里?

至于我们应该把上面那几行放哪里?

个人觉得一个比较好的地方是 Providers 中,在 app/Providers 下面新建一个 Provider,把 macro 调用放到 Providerregister 方法中。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

namespace App\Providers;

use Illuminate\Database\Query\Builder;
use Illuminate\Support\ServiceProvider;

/** @mixin \Illuminate\Database\Eloquent\Builder */
class DatabaseServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
Builder::macro('active', function () {
return $this->where('status', 1);
});
}
}

当然,加了 Providers 之后还要在 config/app.php 中配置这个 Provider

就这样。

还有个问题是,这样添加的方法 ide 无法识别,我们这时候就可以使用 @method 了,如:

1
@method $this active()

可以使用命令把这些 @method 写入到 Builder 头部。

一直以来,想调试框架中的某些东西,如想知道 Elpquentcreate 方法返回值是个什么东西,

以前的话,应该就是在 create 方法调用之后,使用 dd 或者 var_dump 之类的函数打印出来

如:

1
2
3
4
5
public function getTest()
{
$user = \App\User::create(['name' => 'tinker']);
dd($user);
}

这样一来,这个流程似乎有点冗长,因为我们还要打开浏览器查看

有了 tinker,我们就可以直接在命令行运行我们想要测试的代码,如:

tinker

我们可以看到,create 方法返回了一个模型对象。

我们就把这个 tinker 当作可以交互式的 代码执行工具好了,我们在学习 Laravel 的过程中可以在这里直接测试 Laravel 框架中的一些用法,也可以进行调试什么的。

通过控制台的 cookie 信息我们会发现,每次请求之后,关键的 cookie,如PHPSESSIDXSRF-TOKEN 都会发生变化,并且都是很长的一串字符串。

其实这是一个 json 数组,其中包含了 ivvaluemac 三个字段:

cookie

这些字段都是在框架加密解密的时候使用的,加密方法是 openssl_encrypt

openssl_encrypt

openssl 不太了解的可以看下下面的例子:

1
2
3
4
5
6
7
8
$data = 'laravel';
$iv = random_bytes(16);
$key = 'this is key';

$encrypt = openssl_encrypt($data, 'AES-256-CBC', $key, 0, $iv);

var_dump($encrypt);
var_dump(openssl_decrypt($encrypt, 'AES-256-CBC', $key, 0, $iv));

Laravel 中的话,key 就是 .env 配置文件里面的 APP_KEY,除了 key 还有两个变化的参数就是 加密、解密的数据以及 iv

也就是说,如果我们需要加密 cookie 的话,我们至少得保存下 加密后的数据以及 iv

这样看来,mac 字段似乎有点多余,但是我们可以使用该字段来验证数据的合法性:

x

如果验证不通过,Laravel 也就不会对 data 进行解密操作。

虽然每次请求 cookie 都会发生变化,但是实际数据是没有变的,发生变化只是因为用来加密的 iv 变了(使用 random_bytes 方法生成)。

由于 iv 每次都变化,所以需要把 iv 也一同返回给浏览器,加上验证数据合法性的 mac,最后返回的就是下面的数组的 json 编码后在 base64 编码的数据:

1
2
3
4
5
[
'iv' => random_bytes(16), // 16位随机字节串
'value' => 'xxxx...', // 加密后的数据
'mac' => 'xxx...' // 后续请求验证数据合法性的字符串
]