Laravel v5.4.18 中的一个提交,导致的 BUG,因为添加了错误的单测,导致没办法轻易修改,这里提醒大家,使用时需要谨慎,以免采坑。
我们先写一段没有 extra 数据的代码,进行测试
DB::enableQueryLog();
$model = UserExt::query()->find(101);
$model->increment('count');
$model->save();
dump(DB::getQueryLog());
通过测试得知,以上代码只会生成两段 SQL,分别是
select * from `user_ext` where `user_ext`.`id` = ? limit 1
update `user_ext` set `count` = `count` + 1, `user_ext`.`updated_at` = ? where `id` = ?
然后让我们修改测试代码
DB::enableQueryLog();
$model = UserExt::query()->find(101);
$model->increment('count', 1, [
'str' => uniqid()
]);
$model->save();
dump(DB::getQueryLog());
这时,会生成以下三段 SQL
select * from `user_ext` where `user_ext`.`id` = ? limit 1
update `user_ext` set `count` = `count` + 1, `str` = ?, `user_ext`.`updated_at` = ? where `id` = ?
update `user_ext` set `str` = ?, `user_ext`.`updated_at` = ? where `id` = ?
且第二段和第三段 SQL 中,str 的值是一致的。这个问题的主要原因,便是 extra 里的数据不会被同步到 original 中,就导致第二次 save 计算 dirty 的时候,出现了 BUG 。
经过第一个 BUG 的重现,那么第二个问题也就很容易想到了,就是 getChanges 方法。
让我们继续编写代码测试
$model = UserExt::query()->find(101);
$model->increment('count');
dump($model->getChanges());
以上代码会输出以下数据,可见还是符合预期的
array:1 [▼
"count" => 4
]
让我们继续修改代码,在 increment
前增加一次赋值
DB::enableQueryLog();
$model = UserExt::query()->find(101);
$model->str = uniqid();
$model->increment('count');
dump($model->getChanges());
dump(DB::getQueryLog());
会得到以下输出
array:2 [▼
"count" => 7
"str" => "5febf2dc798ed"
]
看似没有问题,但让我们检查一下 SQL
select * from `user_ext` where `user_ext`.`id` = ? limit 1
update `user_ext` set `count` = `count` + 1, `user_ext`.`updated_at` = ? where `id` = ?
却发现,并没有修改 str 的数据,那显然 getChanges 与预期不符。
实际上,increment 在设计上,并没有想要修改前面 setter 的数据,但这种情况下,我们 getChanges 便也不能把 str 算进来。
让我们继续修改代码
DB::enableQueryLog();
$model = UserExt::query()->find(101);
$model->str = uniqid();
$model->increment('count');
dump($model->getChanges());
$model->save();
dump($model->getChanges());
dump(DB::getQueryLog());
两次 getChanges 输出如下
array:2 [▼
"count" => 9
"str" => "5febf3d6418e8"
]
array:2 [▼
"str" => "5febf3d6418e8"
"updated_at" => "2020-12-30 03:28:22"
]
可见两次 getChanges 中,str 的值是一致的。
save 的时候会把 updated_at 算进来,而
increment
的时候是不会算updated_at
,这里至少行为一致,可以作为后续的优化项。
输出的 SQL 如下
select * from `user_ext` where `user_ext`.`id` = ? limit 1
update `user_ext` set `count` = `count` + 1, `str` = ?, `user_ext`.`updated_at` = ? where `id` = ?
update `user_ext` set `str` = ?, `user_ext`.`updated_at` = ? where `id` = ?
Hyperf 是基于 Swoole 4.5+
实现的高性能、高灵活性的 PHP 协程框架,内置协程服务器及大量常用的组件,性能较传统基于 PHP-FPM
的框架有质的提升,提供超高性能的同时,也保持着极其灵活的可扩展性,标准组件均基于 PSR 标准 实现,基于强大的依赖注入设计,保证了绝大部分组件或类都是 可替换
与 可复用
的。
框架组件库除了常见的协程版的 MySQL 客户端
、Redis 客户端
,还为您准备了协程版的 Eloquent ORM
、WebSocket 服务端及客户端
、JSON RPC 服务端及客户端
、GRPC 服务端及客户端
、OpenTracing(Zipkin, Jaeger) 客户端
、Guzzle HTTP 客户端
、Elasticsearch 客户端
、Consul 、Nacos 服务中心
、ETCD 客户端
、AMQP 组件
、Nats 组件
、Apollo 、ETCD 、Zookeeper 、Nacos 和阿里云 ACM 的配置中心
、基于令牌桶算法的限流器
、通用连接池
、熔断器
、Swagger 文档生成
、Swoole Tracker
、Blade 、Smarty 、Twig 、Plates 和 ThinkTemplate 视图引擎
、Snowflake 全局 ID 生成器
、Prometheus 服务监控
等组件,省去了自己实现对应协程版本的麻烦。
Hyperf 还提供了 基于 PSR-11 的依赖注入容器
、注解
、AOP 面向切面编程
、基于 PSR-15 的中间件
、自定义进程
、基于 PSR-14 的事件管理器
、Redis/RabbitMQ 消息队列
、自动模型缓存
、基于 PSR-16 的缓存
、Crontab 秒级定时任务
、Session
、i18n 国际化
、Validation 表单验证
等非常便捷的功能,满足丰富的技术场景和业务场景,开箱即用。