webman刚出来的时候我阅读过webman的代码,也阅读过其他框架的代码,然后我自己撸了一个框架casualman,最早单纯只是为了玩,后面我把他运用在了公司部分项目的生产环境,已经稳定运行了半年以上了。
在这个过程中,结合我自己的一些个人习惯和我看到的其他框架的一些好的地方,我发现webman的代码在一些地方可以被建议:
入口文件start.php比较凌乱臃肿
其实本着可以用就没问题的思路,我觉得代码写成什么样,只要没有影响阅读,其实不用过分追求所谓的优雅和美感,但是吧,我比较强迫症,我个人还是有这么一个建议,觉得webman的入口文件可以精简干练;我自己的框架在入口文件是这么写的:
declare(strict_types=1);
ini_set('date.timezone','Asia/Shanghai');
ini_set('memory_limit', '256M');
define('ROOT_PATH' , dirname(__DIR__));
require_once ROOT_PATH . '/vendor/autoload.php';
require_once ROOT_PATH . '/helpers.php';
use Kernel\ApplicationFactory;
use Utils\Tools;
try{
(new ApplicationFactory())('3Y-CLEARING-CENTER','2.0.0', function(){
// 中间件
Co()->get(\Kernel\Middlewares::class)->init('3Y-CLEARING-CENTER');
// 注册错误收集
set_error_handler(function (...$params){
Tools::log('error_handler', $params, runtime_path());
},DEBUG ? E_ALL : E_WARNING|E_ERROR);
})->run();
}catch(Throwable $throwable){
dump($throwable);
exit("{$throwable->getMessage()}|{$throwable->getCode()}" . PHP_EOL);
}
其中,类ApplicationFactory 分别利用application()和__invoke()做了初始化的工作;
application()做了一些进程启动、事件绑定、协议绑定、端口绑定等工作;每种进程基础都有onStart、onReload、onStop三个方法,如果是监听类型的进程还会有onBufferDrain、onBufferFull、onMessage、onConnect、onClose、onError六个方法,以上两种类型的进程分别用AbstractProcess::classs和ListenerInterface的实现来区分,以下代码有体现:
public static function application(?string $app = null, bool $skip = false){
$process = Config::get('process');
if($app !== null and !isset($process[$app])){
exit('Not found the app' . PHP_EOL);
}
if(!$skip){
try {
foreach ($process as $name => $config){
if($app !== null and $app !== $name){
continue;
}
$handle = make($config['handler']);
if($handle instanceof AbstractProcess){
$handle = ($handle)();
$handle->name = $name ?? 'unknown';
$handle->count = isset($config['count']) ? $config['count'] : 1;
$handle->reloadable = isset($config['reloadable']) ? $config['reloadable'] : true;
}
if(
$handle instanceof ListenerInterface and
$handle instanceof AbstractProcess and
isset($config['listen'])
){
$handle->setSocketName($config['listen']);
$handle->reusePort = isset($config['reusePort']) ? $config['reusePort'] : true;
$handle->transport = isset($config['transport']) ? $config['transport'] : 'tcp';
$handle->protocol = isset($config['protocol']) ? $config['protocol'] : null;
}
}
}catch (\Throwable $throwable){
exit($throwable->getMessage());
}
}
AbstractProcess::runAll();
}
__invoke()做了一些基础内容的初始化操作:
public function __invoke(?string $name = null, ?string $version = null, ?callable $func = null) : Application
{
self::$name = $name ?? self::$name;
self::$version = $version ?? self::$version;
$this->_env();//env初始化
$this->_config();//config目录初始化
$this->_log();//log初始化
if($func){
$func();//执行回调
}
$this->_app = make(Application::class, self::$name, self::$version);
foreach (self::commands() as $command){
$this->_app->add(new $command);
}
return $this->_app;
}
上述代码种Application::class实际上是实现了Symfony/Console组件的入口,为什么workerman已经有了自己的命令,还需要引入这个组件呢?实际上我考虑到在很多场景下除了workerman提供的基础命令外,开发者也需要一些自定义的命令来执行一些脚本或者处理器,比如初始化ORM、清除文件缓存、执行额外进程等,既然要使用,那我想着索性引入Symfony/Console组件统一管理,并且实现映射workerman自身的命令作为基础命令
非常感谢你的建议。
同感,start.php 确实相比其它入口文件代码有点多,不过随着版本不断迭代start.php这部分也在慢慢在整理。后面版本会做到条理清晰代码精简。
你的 casualman 项目很帅气!
哈哈~3Q3Q~希望workerman及生态越来越好!