有些时候在某一个项目中, 使用了一个第三方包, 但是会发现某一些地方不符合项目需求. 或者需要在包代码里注入一段自己的逻辑. 我们可以选择自己维护一个分支, 或者干脆改vendor文件夹, 并它他加入代码管理. 但对于要修改少量代码时, 这样做貌似不太'优雅'
请先查阅函数spl_autoload_register
文档
其实composer也是利用这个函数来实现自动加载的.
php代码执行时, 如果遇到代码里依赖了一个类, 而这个类在当前进程中不存在时, php 会按照加载器队列顺序调用通过spl_autoload_register
函数注册过的类加载器. 直到某个加载器执行完后, 这个被依赖的类被加载进当前进程空间, 或者所有加载器都执行完这个类仍不存在, 并抛出类不存在的异常.
所以我们也可以利用spl_autoload_register
函数, 注册一个加载器, 并使其在队列里位于composer加载器之前. 当php需要加载我们要改动的第三方代码包里的类时, 我们加载自己改好的php文件, 而不是vendor包里的php文件. 这样就达到了覆盖的目的.
煮个栗子:
我写了一个自动抢饿了么最大外卖红包swoole程序. 大概流程就是, 利用hanson/vbot
包监听微信群里发送的红包链接, 并把链接推送到红包队列, 然后有个进程使用小号轮训饿了么接口, 等红包的下一个就是最大时, 用大号去领, 就领到了, 但是有个问题:
vbot 包有个message handler类, 里面有个listen方法. 这个方法里会调用微信网页版的接口. 当收到微信消息时会调用用户的消息处理器, 并且此方法是while(true)循环.所以一旦执行listen方法, 整个进程是会阻塞在这个方法里的, 就算用swoole的异步定时器也无力回天. 但是我又有个需求, 就是异步发送消息: 当某个红包的最大红包来临时, 可以配置不自动抢, 而是把链接转发到一个'内部群', 由群员抢最大红包. 所以我只好修改一下这个类了, 把while(true)循环改成swoole的定时器
项目目录结构
注意到, overwrite文件夹, 就是我用来放覆盖第三方代码php文件的目录
里面的autoload.php文件内容:
<?php
spl_autoload_register(function ($cls) {
$map = [
'Hanson\Vbot\Core\MessageHandler' => __DIR__ . '/MessageHandler.php',
'Hanson\Vbot\Core\Server' => __DIR__ . '/Server.php',
];
if (isset($map[$cls])) {
echo $cls . ' loaded' . PHP_EOL;
include $map[$cls];
return true;
}
// 注意需要设置prepend参数为true
}, true, true);
项目的composer.json加载了这autoload.php:
{
"require": {
"hanson/vbot": "^2.0",
"symfony/debug": "^4.1"
},
"autoload": {
"psr-4": {
"App\\": "src/"
},
"files": ["src/overwrite/autoload.php"]
}
}
请自行体会