discuz! q 源码浅析 | laravel china 社区-380玩彩网官网入口
前言
简单分析了一下discuz! q 的源码,纯属个人学习,不对的地方望指正哈。
用到的laravel组件
在vendor/discuz/core/composer.json中可以看到
- “illuminate/container”: ioc服务容器
- “illuminate/database”: 数据库
- “illuminate/bus”: 事件总线
- “illuminate/events”: 事件
- “illuminate/config”: 配置服务
- “illuminate/view”: 视图
- “illuminate/session”: session
- 路由没有用laravel,而是”nikic/fast-route”
- 中间件没有用laravel,而是”laminas/laminas-stratigility”,遵循 psr-7 http 规范
- 处理请求用的”laminas/laminas-httphandlerrunner”,遵循 psr-15 http 处理器规范
入口文件分析
在public/index.php中
- $app = new discuz\foundation\application(dirname(__dir__)); //实例化一个容器,并绑定了事件服务和别名绑定
- $app->make(discuz\http\server::class)->listen(); //实例化server,并处理请求(重心在这里)
discuz\http\server::class 分析
public function listen()
{
// 这里绑定了核心的服务类,包括apiserviceprovider、webserviceprovider,这两个provider会加载routes/下的路由配置和注册中间件
$this->siteboot();
$pipe = new middlewarepipe();
//new requesthandler里用到了apiserviceprovider、webserviceprovider注册的中间件
$pipe->pipe(new requesthandler([
'/api' => 'discuz.api.middleware',
'/' => 'discuz.web.middleware'
], $this->app));
$psr17factory = new psr17factory();
$request = (new serverrequestcreator($psr17factory, $psr17factory, $psr17factory, $psr17factory))->fromglobals();
$this->app->instance('request', $request);
$this->app->alias('request', serverrequestinterface::class);
// 上面的实例化的中间件对象和request对象都注入到requesthandlerrunner
$runner = new requesthandlerrunner(
$pipe,
new sapiemitter,
function () use ($request) {
return $request;
},
function (throwable $e) {
$generator = new errorresponsegenerator;
return $generator($e, new serverrequest, new response);
}
);
// run方法有两个重要步骤:1. 处理请求$this->handler->handle($request);2. 收尾工作$this->emitter->emit($response);
$runner->run();
}
$this->handler->handle($request) 在 laminas\stratigility\middlewarepipe
- 主要是中间件的处理流程,利用splqueue的先进先出队列来实现,在apiserviceprovider、webserviceprovider中注册了中间件,enqueue进splqueue队列,然后一个一个的dequeue出队列并处理请求。
- 最后一个中间件是dispatchroute::class,它则是用来根据请求的url找到对应的控制器开始业务处理。
- ps:中间件的处理不再像laravel那样用到array_reduce的特性,我写了一个简单的demo模拟了下:
class next
{
private $fallbackhandler;
private $queue;
public function __construct($queue, $fallbackhandler)
{
$this->queue = clone $queue;
$this->fallbackhandler = $fallbackhandler;
}
public function handle($request)
{
if ($this->queue->isempty()) {
$this->queue = null;
$fallbackhandler = $this->fallbackhandler;
return $fallbackhandler($request);
}
$middleware = $this->queue->dequeue();
$next = clone $this;
$this->queue = null;
return $middleware($request, $next);
}
}
class splqueuepipeline
{
private $pipeline;
public function __construct()
{
$this->pipeline = new splqueue();
}
public function __clone()
{
$this->pipeline = clone $this->pipeline;
}
public function handle($request)
{
return $this->process($request, function ($r) {
return $r;
});
}
public function process($request, $handler)
{
return (new next($this->pipeline, $handler))->handle($request);
}
public function pipe($middleware)
{
$this->pipeline->enqueue($middleware);
}
}
$pipe = new splqueuepipeline();
$pipe->pipe(function ($request, $next) {
var_dump("pipe 1");
return $next->handle($request);
});
$pipe->pipe(function ($request, $next) {
var_dump("pipe 2");
return $next->handle($request);
});
$rs = $pipe->handle("request");
var_dump($rs);
疑问:app\api\middleware目录下的中间件是如何生效的
- app\listeners\addapimiddleware 将 app\api\middleware 目录的中间件进行注册。
- app\providers\eventserviceprovider 注册了 discuz\api\events\configmiddleware 事件的监听,listener 就是 app\listeners\addapimiddleware。
- app\providers\eventserviceprovider 定义在配置文件的 providers 数组内。而这些provider 的注册在上面提到的$this->siteboot();中完成。
- 那 configmiddleware 事件又是怎么产生的呢?在上面提到的 apiserviceprovider 中,注册完系统中间件后会触发 configmiddleware 事件。这个流程我模拟了一个简单的demo如下:
class eventdispatcher
{
private $bindings = [];
public function listen($eventclass, $listenerclass)
{
if (empty($this->bindings[$eventclass])) {
$this->bindings[$eventclass] = [];
}
$this->bindings[$eventclass][] = $listenerclass;
}
public function dispatch($obj)
{
$eventclass = get_class($obj);
if (!empty($this->bindings[$eventclass])) {
foreach ($this->bindings[$eventclass] as $listenerclass) {
$reflector = new reflectionclass($listenerclass);
$instance = $reflector->newinstance();
$method = $reflector->getmethod('handle');
$method->invoke($instance, $obj);
}
}
}
}
class aevent
{
public $pipe;
public function __construct($obj)
{
$this->pipe = $obj;
}
}
class alistener
{
public function handle(aevent $obj)
{
$obj->pipe->arr[] = "pipe 2";
}
}
$obj = new stdclass();
$obj->arr = ['pipe 1'];
$eventdispatcher = new eventdispatcher();
$eventdispatcher->listen(aevent::class, alistener::class);
$eventdispatcher->dispatch(new aevent($obj));
print_r($obj->arr);
本作品采用《cc 协议》,转载必须注明作者和本文链接
discuz q
里用到了很多腾讯云的api
,基本都是收费的,可以理解为腾讯云深度定制。没找到前端未打包的源码,不好二开
@showcj 前端有开源的代码的
还是没用伪静态呀
@showcj
@cooper @showcj
那是旧版了,后台管理还在用,新的前台用的 uni-app
请问大佬,后台接口得路由时怎么配置得?
感谢大佬。刚入职就是要二开,看了你的文章帮助很大,再次感谢!!!
现在还有哪些类似discuzq这样的论坛门户比较好用来快速开发