EasySwoole核心Core分析(一):initialize方法

上篇我们分析了Command的原理,这次接着流程走到Core类.

CommandRunner的run方法中执行了 Core::getInstance()->initialize(); 初始化操作.我们本篇就对该方法一探究竟.

__construct构造方法中定义了几个常量

defined('SWOOLE_VERSION') or define('SWOOLE_VERSION',intval(phpversion('swoole')));
defined('EASYSWOOLE_ROOT') or define('EASYSWOOLE_ROOT', realpath(getcwd()));
defined('EASYSWOOLE_SERVER') or define('EASYSWOOLE_SERVER',1);
defined('EASYSWOOLE_WEB_SERVER') or define('EASYSWOOLE_WEB_SERVER',2);
defined('EASYSWOOLE_WEB_SOCKET_SERVER') or define('EASYSWOOLE_WEB_SOCKET_SERVER',3);
defined('EASYSWOOLE_REDIS_SERVER') or define('EASYSWOOLE_REDIS_SERVER',4);

initialize方法中,首先检测EasySwooleEvent类是否存在

//检查全局文件是否存在.
$file = EASYSWOOLE_ROOT . '/EasySwooleEvent.php';
if(file_exists($file)){
    require_once $file;
    try{
        // 判断载入的类是否实现了Event这个接口
        $ref = new \ReflectionClass('EasySwoole\EasySwoole\EasySwooleEvent');
        if(!$ref->implementsInterface(Event::class)){
            die('global file for EasySwooleEvent is not compatible for EasySwoole\EasySwoole\EasySwooleEvent');
        }
        unset($ref);
    }catch (\Throwable $throwable){
        die($throwable->getMessage());
    }
}else{
    die('global event file missing');
}

然后去初始化一些操作

//先加载配置文件
$this->loadEnv();
//执行框架初始化事件
EasySwooleEvent::initialize();
//临时文件和Log目录初始化
$this->sysDirectoryInit();
//注册错误回调
$this->registerErrorHandler();

加载配置就是将根目录下produce.phpdev.php保存进Config里.之后可以通过 Config::getInstance()->getConf()获取.(Config的实现会在之后文章中细讲,现在只需要知道他可以存取数据,是一个KV存储系统)

EasySwooleEvent::initialize()这一步则是调用之前引用进来的全局类的initialize方法,上篇我们说到可以将自己自定义Command命令声明在这.

$this->sysDirectoryInit() 初始化 临时文件 和 日志 目录.在配置项中框架默认为null,

'TEMP_DIR' => null,
'LOG_DIR' => null

方法中判断为null时,临时文件和日志放在根目录下的Temp和Log目录中.

$tempDir = Config::getInstance()->getConf('TEMP_DIR');
if(empty($tempDir)){
    $tempDir = EASYSWOOLE_ROOT.'/Temp';
    Config::getInstance()->setConf('TEMP_DIR',$tempDir); // 保存新的临时目录
}else{
    $tempDir = rtrim($tempDir,'/');
}

if(!is_dir($tempDir)){
    File::createDirectory($tempDir); // 创建目录
}
defined('EASYSWOOLE_TEMP_DIR') or define('EASYSWOOLE_TEMP_DIR',$tempDir);

同理 日志目录的处理方式也是一样.不多赘述了.最后设置默认文件地址 pid.pidswoole.log

初始化的最后最后一步就是注册全局异常处理registerErrorHandler

首先显示所有错误

ini_set("display_errors", "On");
error_reporting(E_ALL | E_STRICT);

然后初始化日志系统

$logger = Di::getInstance()->get(SysConst::LOGGER_HANDLER); // SysConst 里定义了框架的一些常量
// 判断该类是否实现LoggerInterface接口
if(!$logger instanceof LoggerInterface){
	// 没有实现或者未定义则使用框架默认的日志系统
	$logger = new DefaultLogger(EASYSWOOLE_LOG_DIR);
}
Logger::getInstance($logger); // 实例化Logger类

Logger提供了logconsole方法.日志操作类需要实现LoggerInterface接口

namespace EasySwoole\Log;
interface LoggerInterface
{
    const LOG_LEVEL_INFO = 1;
    const LOG_LEVEL_NOTICE = 2;
    const LOG_LEVEL_WARNING = 3;
    const LOG_LEVEL_ERROR = 4;

    function log(?string $msg,int $logLevel = self::LOG_LEVEL_INFO,string $category = 'DEBUG'):string ;
    function console(?string $msg,int $logLevel = self::LOG_LEVEL_INFO,string $category = 'DEBUG');
}

log方法的本质就是file_put_contents将日志写入文件

function log(?string $msg,int $logLevel = self::LOG_LEVEL_INFO,string $category = 'DEBUG'):string
{
    $date = date('Y-m-d H:i:s');
    $levelStr = $this->levelMap($logLevel);
    $filePath = $this->logDir."/log.log";
    $str = "[{$date}][{$category}][{$levelStr}] : [{$msg}]\n";
    file_put_contents($filePath,"{$str}",FILE_APPEND|LOCK_EX);
    return $str;
}

console是将日志输出在终端

function console(?string $msg,int $logLevel = self::LOG_LEVEL_INFO,string $category = 'DEBUG')
{
    $date = date('Y-m-d H:i:s');
    $levelStr = $this->levelMap($logLevel);
    $temp =  $this->colorString("[{$date}][{$category}][{$levelStr}] : [{$msg}]",$logLevel)."\n";
    fwrite(STDOUT,$temp);
}

同理我们如果想实现一些个性化的日志需求可以自定义一个日志类,然后在EasySwooleEventinitialize方法中加上Di::getInstance()->set(SysConst::LOGGER_HANDLER, 自定义的日志类)实现自己的日志系统

接着又初始化了Trigger

//初始化追追踪器
$trigger = Di::getInstance()->get(SysConst::TRIGGER_HANDLER);
// 同样判断是否实现TriggerInterface接口
if(!$trigger instanceof TriggerInterface){
    // 使用默认的触发器(用于主动触发错误或者异常而不中断程序继续执行)
    $trigger = new DefaultTrigger(Logger::getInstance());
}
Trigger::getInstance($trigger); // 实例化Trigger类

最后注册 set_error_handlerregister_shutdown_function 方法

set_error_handler 设定用户自定义的错误处理函数. 方便用户自己定义来处理运行中的错误.

register_shutdown_function 脚本执行完成时回调

//在没有配置自定义错误处理器的情况下,转化为trigger处理
$errorHandler = Di::getInstance()->get(SysConst::ERROR_HANDLER);
//判断是否可调用
if(!is_callable($errorHandler)){
    $errorHandler = function($errorCode, $description, $file = null, $line = null){
        $l = new Location();
        $l->setFile($file);
        $l->setLine($line);
        // Trigger功能: 主动触发错误或者异常而不中断程序继续执行
        Trigger::getInstance()->error($description,$errorCode,$l);
    };
}
// 注册全局异常处理
set_error_handler($errorHandler);

$func = Di::getInstance()->get(SysConst::SHUTDOWN_FUNCTION);
if(!is_callable($func)){
    $func = function (){
        // 判断是否有异常
        $error = error_get_last();
        if(!empty($error)){
            $l = new Location();
            $l->setFile($error['file']);
            $l->setLine($error['line']);
            // Trigger功能: 主动触发错误或者异常而不中断程序继续执行
            Trigger::getInstance()->error($error['message'],$error['type'],$l);
        }
    };
}
// 终止前回调该方法
register_shutdown_function($func);

Location只负责记录两个参数 linefile.顾名思义他记录着某段函数的位置.

到此我们的initialize方法分析完毕.当你阅读完本篇后可能会对Trigger产生困惑.这是啥? 不要急,马上我们专门介绍一下Trigger.