webman剛出來的時候我閱讀過webman的代碼,也閱讀過其他框架的代碼,然后我自己擼了一個框架casualman,最早單純只是為了玩,后面我把他運用在了公司部分項目的生產(chǎn)環(huán)境,已經(jīng)穩(wěn)定運行了半年以上了。
在這個過程中,結(jié)合我自己的一些個人習慣和我看到的其他框架的一些好的地方,我發(fā)現(xiàn)webman的代碼在一些地方可以被建議:
入口文件start.php比較凌亂臃腫
其實本著可以用就沒問題的思路,我覺得代碼寫成什么樣,只要沒有影響閱讀,其實不用過分追求所謂的優(yōu)雅和美感,但是吧,我比較強迫癥,我個人還是有這么一個建議,覺得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()做了一些進程啟動、事件綁定、協(xié)議綁定、端口綁定等工作;每種進程基礎(chǔ)都有onStart、onReload、onStop三個方法,如果是監(jiān)聽類型的進程還會有onBufferDrain、onBufferFull、onMessage、onConnect、onClose、onError六個方法,以上兩種類型的進程分別用AbstractProcess::classs和ListenerInterface的實現(xiàn)來區(qū)分,以下代碼有體現(xiàn):
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()做了一些基礎(chǔ)內(nèi)容的初始化操作:
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();//執(zhí)行回調(diào)
}
$this->_app = make(Application::class, self::$name, self::$version);
foreach (self::commands() as $command){
$this->_app->add(new $command);
}
return $this->_app;
}
上述代碼種Application::class實際上是實現(xiàn)了Symfony/Console組件的入口,為什么workerman已經(jīng)有了自己的命令,還需要引入這個組件呢?實際上我考慮到在很多場景下除了workerman提供的基礎(chǔ)命令外,開發(fā)者也需要一些自定義的命令來執(zhí)行一些腳本或者處理器,比如初始化ORM、清除文件緩存、執(zhí)行額外進程等,既然要使用,那我想著索性引入Symfony/Console組件統(tǒng)一管理,并且實現(xiàn)映射workerman自身的命令作為基礎(chǔ)命令
非常感謝你的建議。
同感,start.php 確實相比其它入口文件代碼有點多,不過隨著版本不斷迭代start.php這部分也在慢慢在整理。后面版本會做到條理清晰代碼精簡。
你的 casualman 項目很帥氣!