2020-08-31 10:11:06 +08:00
< ? php
2022-03-15 18:05:33 +08:00
declare ( strict_types = 1 );
2020-08-31 10:11:06 +08:00
namespace ZM ;
use Doctrine\Common\Annotations\AnnotationReader ;
2021-03-24 23:34:46 +08:00
use Error ;
2020-08-31 10:11:06 +08:00
use Exception ;
2021-07-09 01:38:30 +08:00
use Phar ;
2022-03-15 18:05:33 +08:00
use ReflectionClass ;
use ReflectionException ;
use Swoole\Runtime ;
2021-03-24 23:34:46 +08:00
use Swoole\Server\Port ;
2022-03-15 18:05:33 +08:00
use Swoole\WebSocket\Server ;
2021-06-16 00:17:30 +08:00
use Throwable ;
2022-03-15 18:05:33 +08:00
use ZM\Annotation\Swoole\SwooleHandler ;
2020-08-31 10:11:06 +08:00
use ZM\Config\ZMConfig ;
use ZM\ConnectionManager\ManagerGM ;
2022-03-15 18:05:33 +08:00
use ZM\Console\Console ;
2021-03-02 21:24:31 +08:00
use ZM\Console\TermColor ;
2022-03-13 22:46:22 +08:00
use ZM\Exception\ZMKnownException ;
2020-09-29 15:07:43 +08:00
use ZM\Store\LightCache ;
2020-11-03 21:02:24 +08:00
use ZM\Store\LightCacheInside ;
use ZM\Store\Lock\SpinLock ;
use ZM\Store\ZMAtomic ;
2020-08-31 10:11:06 +08:00
use ZM\Utils\DataProvider ;
2021-03-24 23:34:46 +08:00
use ZM\Utils\Terminal ;
2020-09-29 15:07:43 +08:00
use ZM\Utils\ZMUtil ;
2020-08-31 10:11:06 +08:00
class Framework
{
/**
2022-04-10 00:58:07 +08:00
* 框架运行的参数
*
2020-08-31 10:11:06 +08:00
* @ var array
*/
2020-09-29 15:07:43 +08:00
public static $argv ;
2022-03-15 18:05:33 +08:00
2020-08-31 10:11:06 +08:00
/**
2022-04-10 00:58:07 +08:00
* 通信服务器实例
*
2020-08-31 10:11:06 +08:00
* @ var Server
*/
public static $server ;
2022-03-15 18:05:33 +08:00
2021-03-24 23:34:46 +08:00
/**
2022-04-10 00:58:07 +08:00
* 框架加载的文件
*
2021-03-24 23:34:46 +08:00
* @ var string []
*/
public static $loaded_files = [];
2022-03-15 18:05:33 +08:00
2022-04-10 00:58:07 +08:00
/**
* 是否为单文件模式
*
* @ var bool
*/
2022-03-15 18:05:33 +08:00
public static $instant_mode = false ;
2020-08-31 10:11:06 +08:00
/**
2022-04-11 22:35:10 +08:00
* Swoole 服务端配置
2022-04-10 00:58:07 +08:00
*
* @ var null | array
2020-08-31 10:11:06 +08:00
*/
2022-04-11 22:35:10 +08:00
private $swoole_server_config ;
2022-03-15 18:05:33 +08:00
2021-06-16 00:17:30 +08:00
/**
* @ var array
*/
private $setup_events = [];
2020-08-31 10:11:06 +08:00
2022-04-10 00:58:07 +08:00
/**
* 创建一个新的框架实例
*
* @ param array $args 运行参数
* @ param bool $instant_mode 是否为单文件模式
*/
public function __construct ( array $args = [], bool $instant_mode = false )
2022-03-13 22:46:22 +08:00
{
2021-11-02 16:01:24 +08:00
$tty_width = $this -> getTtyWidth ();
self :: $instant_mode = $instant_mode ;
2020-08-31 10:11:06 +08:00
self :: $argv = $args ;
2022-04-10 00:58:07 +08:00
// 初始化配置
2022-04-11 23:31:49 +08:00
ZMConfig :: setDirectory ( DataProvider :: getSourceRootDir () . '/config' );
2022-03-15 18:05:33 +08:00
ZMConfig :: setEnv ( $args [ 'env' ] ? ? '' );
if ( ZMConfig :: get ( 'global' ) === false ) {
echo zm_internal_errcode ( 'E00007' ) . 'Global config load failed: ' . ZMConfig :: $last_error . " \n Error path: " . DataProvider :: getSourceRootDir () . " \n Please init first! \n See: https://github.com/zhamao-robot/zhamao-framework/issues/37 \n " ;
exit ( 1 );
2020-12-20 18:49:03 +08:00
}
2021-03-16 01:34:17 +08:00
2022-03-20 16:23:07 +08:00
// 定义常量
2022-04-10 00:58:07 +08:00
require_once 'global_defines.php' ;
2021-03-16 01:34:17 +08:00
2022-04-10 00:58:07 +08:00
// 确保目录存在
2022-04-11 23:31:49 +08:00
DataProvider :: createIfNotExists ( ZMConfig :: get ( 'global' , 'zm_data' ));
DataProvider :: createIfNotExists ( ZMConfig :: get ( 'global' , 'config_dir' ));
2022-04-10 00:58:07 +08:00
DataProvider :: createIfNotExists ( ZMConfig :: get ( 'global' , 'crash_dir' ));
// 初始化连接池?
2020-09-29 15:07:43 +08:00
try {
2022-03-15 18:05:33 +08:00
ManagerGM :: init ( ZMConfig :: get ( 'global' , 'swoole' )[ 'max_connection' ] ? ? 2048 , 0.5 , [
2020-09-29 15:07:43 +08:00
[
2022-03-15 18:05:33 +08:00
'key' => 'connect_id' ,
'type' => 'string' ,
'size' => 30 ,
2020-09-29 15:07:43 +08:00
],
[
2022-03-15 18:05:33 +08:00
'key' => 'type' ,
'type' => 'int' ,
],
2020-09-29 15:07:43 +08:00
]);
} catch ( ConnectionManager\TableException $e ) {
2022-03-15 18:05:33 +08:00
echo zm_internal_errcode ( 'E00008' ) . $e -> getMessage () . PHP_EOL ;
exit ( 1 );
2020-09-29 15:07:43 +08:00
}
2022-04-10 00:58:07 +08:00
2020-08-31 10:11:06 +08:00
try {
2022-04-10 00:58:07 +08:00
// 初始化日志
2020-08-31 10:11:06 +08:00
Console :: init (
2022-03-15 18:05:33 +08:00
ZMConfig :: get ( 'global' , 'info_level' ) ? ? 2 ,
2020-08-31 10:11:06 +08:00
self :: $server ,
2022-03-15 18:05:33 +08:00
$args [ 'log-theme' ] ? ? 'default' ,
( $o = ZMConfig :: get ( 'console_color' )) === false ? [] : $o
2020-08-31 10:11:06 +08:00
);
2022-04-10 00:58:07 +08:00
// 是否同步输出到文件
2022-03-15 18:05:33 +08:00
if (( ZMConfig :: get ( 'global' , 'runtime' )[ 'save_console_log_file' ] ? ? false ) !== false ) {
Console :: setOutputFile ( ZMConfig :: get ( 'global' , 'runtime' )[ 'save_console_log_file' ]);
2022-01-08 16:19:43 +08:00
}
2020-08-31 10:11:06 +08:00
2022-04-10 00:58:07 +08:00
// 设置默认时区
2022-03-15 18:05:33 +08:00
$timezone = ZMConfig :: get ( 'global' , 'timezone' ) ? ? 'Asia/Shanghai' ;
2020-08-31 10:11:06 +08:00
date_default_timezone_set ( $timezone );
2022-04-10 00:58:07 +08:00
// 读取 Swoole 配置
2022-04-11 22:35:10 +08:00
$this -> swoole_server_config = ZMConfig :: get ( 'global' , 'swoole' );
$this -> swoole_server_config [ 'log_level' ] = SWOOLE_LOG_DEBUG ;
2022-04-10 00:58:07 +08:00
// 是否启用远程终端
2022-03-15 18:05:33 +08:00
$add_port = ZMConfig :: get ( 'global' , 'remote_terminal' )[ 'status' ] ? ? false ;
2020-08-31 10:11:06 +08:00
2022-04-10 00:58:07 +08:00
// 加载服务器事件
2022-03-29 02:10:09 +08:00
if ( ! $instant_mode ) {
$this -> loadServerEvents ();
}
2021-06-16 00:17:30 +08:00
2022-04-10 00:58:07 +08:00
// 解析命令行参数
2021-03-24 23:34:46 +08:00
$this -> parseCliArgs ( self :: $argv , $add_port );
2021-06-16 00:17:30 +08:00
2022-04-10 00:58:07 +08:00
// 设置默认最长等待时间
2022-04-11 22:35:10 +08:00
if ( ! isset ( $this -> swoole_server_config [ 'max_wait_time' ])) {
$this -> swoole_server_config [ 'max_wait_time' ] = 5 ;
2021-03-29 15:34:24 +08:00
}
2022-04-10 00:58:07 +08:00
// 设置最大 worker 进程数
2022-04-11 22:35:10 +08:00
$worker = $this -> swoole_server_config [ 'worker_num' ] ? ? swoole_cpu_num ();
2022-03-15 18:05:33 +08:00
define ( 'ZM_WORKER_NUM' , $worker );
2021-06-16 00:17:30 +08:00
2022-04-10 00:58:07 +08:00
// 初始化原子计数器
2021-03-24 23:34:46 +08:00
ZMAtomic :: init ();
2021-06-16 00:17:30 +08:00
2022-04-10 00:58:07 +08:00
// 非静默模式下打印启动信息
if ( ! self :: $argv [ 'private-mode' ]) {
$out [ 'working_dir' ] = DataProvider :: getWorkingDir ();
$out [ 'listen' ] = ZMConfig :: get ( 'global' , 'host' ) . ':' . ZMConfig :: get ( 'global' , 'port' );
2022-04-11 22:35:10 +08:00
if ( ! isset ( $this -> swoole_server_config [ 'worker_num' ])) {
2022-04-10 00:58:07 +08:00
if (( ZMConfig :: get ( 'global' , 'runtime' )[ 'swoole_server_mode' ] ? ? SWOOLE_PROCESS ) === SWOOLE_PROCESS ) {
$out [ 'worker' ] = swoole_cpu_num () . ' (auto)' ;
} else {
$out [ 'single_proc_mode' ] = 'true' ;
}
2021-07-09 01:38:30 +08:00
} else {
2022-04-11 22:35:10 +08:00
$out [ 'worker' ] = $this -> swoole_server_config [ 'worker_num' ];
2022-04-10 00:58:07 +08:00
}
$out [ 'environment' ] = ( $args [ 'env' ] ? ? null ) === null ? 'default' : $args [ 'env' ];
$out [ 'log_level' ] = Console :: getLevel ();
$out [ 'version' ] = ZM_VERSION . ( LOAD_MODE === 0 ? ( ' (build ' . ZM_VERSION_ID . ')' ) : '' );
$out [ 'master_pid' ] = posix_getpid ();
if ( APP_VERSION !== 'unknown' ) {
$out [ 'app_version' ] = APP_VERSION ;
}
2022-04-11 22:35:10 +08:00
if ( isset ( $this -> swoole_server_config [ 'task_worker_num' ])) {
$out [ 'task_worker' ] = $this -> swoole_server_config [ 'task_worker_num' ];
2022-04-10 00:58:07 +08:00
}
if (( ZMConfig :: get ( 'global' , 'sql_config' )[ 'sql_host' ] ? ? '' ) !== '' ) {
$conf = ZMConfig :: get ( 'global' , 'sql_config' );
$out [ 'mysql_pool' ] = $conf [ 'sql_database' ] . '@' . $conf [ 'sql_host' ] . ':' . $conf [ 'sql_port' ];
}
if (( ZMConfig :: get ( 'global' , 'mysql_config' )[ 'host' ] ? ? '' ) !== '' ) {
$conf = ZMConfig :: get ( 'global' , 'mysql_config' );
$out [ 'mysql' ] = $conf [ 'dbname' ] . '@' . $conf [ 'host' ] . ':' . $conf [ 'port' ];
}
if ( ZMConfig :: get ( 'global' , 'redis_config' )[ 'host' ] !== '' ) {
$conf = ZMConfig :: get ( 'global' , 'redis_config' );
$out [ 'redis_pool' ] = $conf [ 'host' ] . ':' . $conf [ 'port' ];
}
if ( ZMConfig :: get ( 'global' , 'static_file_server' )[ 'status' ] !== false ) {
$out [ 'static_file_server' ] = 'enabled' ;
}
if ( self :: $argv [ 'show-php-ver' ] !== false ) {
$out [ 'php_version' ] = PHP_VERSION ;
$out [ 'swoole_version' ] = SWOOLE_VERSION ;
2021-07-09 01:38:30 +08:00
}
2021-03-02 21:24:31 +08:00
2022-04-10 00:58:07 +08:00
if ( $add_port ) {
$conf = ZMConfig :: get ( 'global' , 'remote_terminal' );
$out [ 'terminal' ] = $conf [ 'host' ] . ':' . $conf [ 'port' ];
}
2022-03-29 02:10:09 +08:00
self :: printProps ( $out , $tty_width , $args [ 'log-theme' ] === null );
}
2022-04-10 00:58:07 +08:00
// 预览模式则直接提出
2022-03-15 18:05:33 +08:00
if ( $args [ 'preview' ] ? ? false ) {
2021-05-08 10:02:41 +08:00
exit ();
}
2021-06-16 00:17:30 +08:00
2022-04-10 00:58:07 +08:00
// 初始化服务器
2021-07-09 01:38:30 +08:00
self :: $server = new Server (
2022-03-15 18:05:33 +08:00
ZMConfig :: get ( 'global' , 'host' ),
ZMConfig :: get ( 'global' , 'port' ),
ZMConfig :: get ( 'global' , 'runtime' )[ 'swoole_server_mode' ] ? ? SWOOLE_PROCESS
2021-07-09 01:38:30 +08:00
);
2021-03-06 17:22:42 +08:00
2022-04-10 00:58:07 +08:00
// 监听远程终端
2021-03-24 23:34:46 +08:00
if ( $add_port ) {
2022-03-15 18:05:33 +08:00
$conf = ZMConfig :: get ( 'global' , 'remote_terminal' ) ? ? [
'status' => true ,
'host' => '127.0.0.1' ,
'port' => 20002 ,
'token' => '' ,
];
$welcome_msg = Console :: setColor ( 'Welcome! You can use `help` for usage.' , 'green' );
2021-03-24 23:34:46 +08:00
/** @var Port $port */
2022-03-15 18:05:33 +08:00
$port = self :: $server -> listen ( $conf [ 'host' ], $conf [ 'port' ], SWOOLE_SOCK_TCP );
2021-03-24 23:34:46 +08:00
$port -> set ([
2022-03-15 18:05:33 +08:00
'open_http_protocol' => false ,
2021-03-24 23:34:46 +08:00
]);
2022-04-10 00:58:07 +08:00
$port -> on ( 'connect' , function ( \Swoole\Server $serv , $fd ) use ( $welcome_msg , $conf ) {
2022-03-15 18:05:33 +08:00
ManagerGM :: pushConnect ( $fd , 'terminal' );
2022-04-10 00:58:07 +08:00
// 推送欢迎信息
2022-03-15 18:05:33 +08:00
$serv -> send ( $fd , file_get_contents ( working_dir () . '/config/motd.txt' ));
2022-04-10 00:58:07 +08:00
// 要求输入令牌
2022-03-15 18:05:33 +08:00
if ( ! empty ( $conf [ 'token' ])) {
$serv -> send ( $fd , 'Please input token: ' );
2021-03-24 23:34:46 +08:00
} else {
$serv -> send ( $fd , $welcome_msg . " \n >>> " );
}
});
$port -> on ( 'receive' , function ( $serv , $fd , $reactor_id , $data ) use ( $welcome_msg , $conf ) {
ob_start ();
try {
2022-03-15 18:05:33 +08:00
$arr = LightCacheInside :: get ( 'light_array' , 'input_token' ) ? ? [];
2021-03-24 23:34:46 +08:00
if ( empty ( $arr [ $fd ] ? ? '' )) {
2022-03-15 18:05:33 +08:00
if ( $conf [ 'token' ] != '' ) {
2021-03-24 23:34:46 +08:00
$token = trim ( $data );
2022-03-15 18:05:33 +08:00
if ( $token === $conf [ 'token' ]) {
SpinLock :: transaction ( 'input_token' , function () use ( $fd , $token ) {
$arr = LightCacheInside :: get ( 'light_array' , 'input_token' );
2021-03-24 23:34:46 +08:00
$arr [ $fd ] = $token ;
2022-03-15 18:05:33 +08:00
LightCacheInside :: set ( 'light_array' , 'input_token' , $arr );
2021-03-24 23:34:46 +08:00
});
2022-03-15 18:05:33 +08:00
$serv -> send ( $fd , Console :: setColor ( " Auth success!! \n " , 'green' ));
2021-03-24 23:34:46 +08:00
$serv -> send ( $fd , $welcome_msg . " \n >>> " );
} else {
2022-03-15 18:05:33 +08:00
$serv -> send ( $fd , Console :: setColor ( " Auth failed!! \n " , 'red' ));
2021-03-24 23:34:46 +08:00
$serv -> close ( $fd );
}
return ;
}
}
2022-03-15 18:05:33 +08:00
if ( trim ( $data ) == 'exit' || trim ( $data ) == 'q' ) {
$serv -> send ( $fd , Console :: setColor ( " Bye! \n " , 'blue' ));
2021-03-24 23:34:46 +08:00
$serv -> close ( $fd );
return ;
}
Terminal :: executeCommand ( trim ( $data ));
} catch ( Exception $e ) {
2022-03-15 18:05:33 +08:00
$error_msg = $e -> getMessage () . ' at ' . $e -> getFile () . '(' . $e -> getLine () . ')' ;
Console :: error ( zm_internal_errcode ( 'E00009' ) . 'Uncaught exception ' . get_class ( $e ) . ' when calling "open": ' . $error_msg );
2021-03-24 23:34:46 +08:00
Console :: trace ();
} catch ( Error $e ) {
2022-03-15 18:05:33 +08:00
$error_msg = $e -> getMessage () . ' at ' . $e -> getFile () . '(' . $e -> getLine () . ')' ;
Console :: error ( zm_internal_errcode ( 'E00009' ) . 'Uncaught ' . get_class ( $e ) . ' when calling "open": ' . $error_msg );
2021-03-24 23:34:46 +08:00
Console :: trace ();
}
$r = ob_get_clean ();
2022-03-15 18:05:33 +08:00
if ( ! empty ( $r )) {
$serv -> send ( $fd , $r );
}
if ( ! in_array ( trim ( $data ), [ 'r' , 'reload' ])) {
$serv -> send ( $fd , '>>> ' );
}
2021-03-24 23:34:46 +08:00
});
$port -> on ( 'close' , function ( $serv , $fd ) {
ManagerGM :: popConnect ( $fd );
2022-03-20 16:23:07 +08:00
// echo "Client: Close.\n";
2021-03-24 23:34:46 +08:00
});
}
2022-04-10 00:58:07 +08:00
// 设置服务器配置
2022-04-11 22:35:10 +08:00
self :: $server -> set ( $this -> swoole_server_config );
2021-03-13 15:16:10 +08:00
Console :: setServer ( self :: $server );
2022-04-10 00:58:07 +08:00
// 非静默模式下,打印欢迎信息
2022-03-29 02:10:09 +08:00
if ( ! self :: $argv [ 'private-mode' ]) {
self :: printMotd ( $tty_width );
}
2021-03-02 21:24:31 +08:00
2020-08-31 10:11:06 +08:00
global $asd ;
$asd = get_included_files ();
2022-04-10 00:58:07 +08:00
2020-09-29 15:07:43 +08:00
// 注册 Swoole Server 的事件
$this -> registerServerEvents ();
2022-04-10 00:58:07 +08:00
// 初始化缓存
2022-03-15 18:05:33 +08:00
$r = ZMConfig :: get ( 'global' , 'light_cache' ) ? ? [
2022-03-20 16:23:07 +08:00
'size' => 512 , // 最多允许储存的条数( 需要2的倍数)
'max_strlen' => 32768 , // 单行字符串最大长度( 需要2的倍数)
'hash_conflict_proportion' => 0.6 , // Hash冲突率( 越大越好, 但是需要的内存更多)
2022-03-15 18:05:33 +08:00
'persistence_path' => DataProvider :: getDataFolder () . '_cache.json' ,
'auto_save_interval' => 900 ,
];
2020-11-03 21:02:24 +08:00
LightCache :: init ( $r );
LightCacheInside :: init ();
2022-04-10 00:58:07 +08:00
// 初始化自旋锁
2022-03-15 18:05:33 +08:00
SpinLock :: init ( $r [ 'size' ]);
2022-04-10 00:58:07 +08:00
// 注册全局错误处理器
set_error_handler ( static function ( $error_no , $error_msg , $error_file , $error_line ) {
2021-01-03 18:16:35 +08:00
switch ( $error_no ) {
case E_WARNING :
$level_tips = 'PHP Warning: ' ;
break ;
case E_NOTICE :
$level_tips = 'PHP Notice: ' ;
break ;
case E_DEPRECATED :
$level_tips = 'PHP Deprecated: ' ;
break ;
case E_USER_ERROR :
$level_tips = 'User Error: ' ;
break ;
case E_USER_WARNING :
$level_tips = 'User Warning: ' ;
break ;
case E_USER_NOTICE :
$level_tips = 'User Notice: ' ;
break ;
case E_USER_DEPRECATED :
$level_tips = 'User Deprecated: ' ;
break ;
case E_STRICT :
$level_tips = 'PHP Strict: ' ;
break ;
default :
$level_tips = 'Unkonw Type Error: ' ;
break ;
2022-04-10 00:58:07 +08:00
}
2021-01-03 18:16:35 +08:00
$error = $level_tips . $error_msg . ' in ' . $error_file . ' on ' . $error_line ;
2022-04-10 00:58:07 +08:00
Console :: warning ( $error );
// 如果 return false 则错误会继续递交给 PHP 标准错误处理
2021-01-03 18:16:35 +08:00
return true ;
}, E_ALL | E_STRICT );
2020-08-31 10:11:06 +08:00
} catch ( Exception $e ) {
2022-04-10 00:58:07 +08:00
Console :: error ( '框架初始化出现异常,请检查!' );
2022-03-15 18:05:33 +08:00
Console :: error ( zm_internal_errcode ( 'E00010' ) . $e -> getMessage ());
2021-03-06 17:22:42 +08:00
Console :: debug ( $e );
2022-03-15 18:05:33 +08:00
exit ;
2020-08-31 10:11:06 +08:00
}
}
2022-03-13 22:46:22 +08:00
/**
* 将各进程的pid写入文件, 以备后续崩溃及僵尸进程处理使用
2022-04-10 00:58:07 +08:00
*
2022-03-13 22:46:22 +08:00
* @ param int | string $pid
* @ internal
*/
public static function saveProcessState ( int $type , $pid , array $data = [])
{
switch ( $type ) {
case ZM_PROCESS_MASTER :
$file = _zm_pid_dir () . '/master.json' ;
$json = [
'pid' => intval ( $pid ),
'stdout' => $data [ 'stdout' ],
2022-03-15 18:05:33 +08:00
'daemon' => $data [ 'daemon' ],
2022-03-13 22:46:22 +08:00
];
file_put_contents ( $file , json_encode ( $json , JSON_UNESCAPED_UNICODE ));
return ;
case ZM_PROCESS_MANAGER :
$file = _zm_pid_dir () . '/manager.pid' ;
file_put_contents ( $file , strval ( $pid ));
return ;
case ZM_PROCESS_WORKER :
$file = _zm_pid_dir () . '/worker.' . $data [ 'worker_id' ] . '.pid' ;
file_put_contents ( $file , strval ( $pid ));
return ;
case ZM_PROCESS_USER :
$file = _zm_pid_dir () . '/user.' . $data [ 'process_name' ] . '.pid' ;
file_put_contents ( $file , strval ( $pid ));
return ;
case ZM_PROCESS_TASKWORKER :
$file = _zm_pid_dir () . '/taskworker.' . $data [ 'worker_id' ] . '.pid' ;
file_put_contents ( $file , strval ( $pid ));
return ;
}
}
/**
* 用于框架内部获取多进程运行状态的函数
2022-04-10 00:58:07 +08:00
*
2022-03-15 18:05:33 +08:00
* @ param mixed $id_or_name
2022-03-13 22:46:22 +08:00
* @ throws ZMKnownException
2022-03-15 18:05:33 +08:00
* @ return false | int | mixed
2022-03-13 22:46:22 +08:00
* @ internal
*/
public static function getProcessState ( int $type , $id_or_name = null )
{
$file = _zm_pid_dir ();
switch ( $type ) {
case ZM_PROCESS_MASTER :
2022-03-15 18:05:33 +08:00
if ( ! file_exists ( $file . '/master.json' )) {
return false ;
}
2022-03-13 22:46:22 +08:00
$json = json_decode ( file_get_contents ( $file . '/master.json' ), true );
2022-03-15 18:05:33 +08:00
if ( $json !== null ) {
return $json ;
}
return false ;
2022-03-13 22:46:22 +08:00
case ZM_PROCESS_MANAGER :
2022-03-15 18:05:33 +08:00
if ( ! file_exists ( $file . '/manager.pid' )) {
return false ;
}
2022-03-13 22:46:22 +08:00
return intval ( file_get_contents ( $file . '/manager.pid' ));
case ZM_PROCESS_WORKER :
2022-03-15 18:05:33 +08:00
if ( ! is_int ( $id_or_name )) {
throw new ZMKnownException ( 'E99999' , 'worker_id必须为整数' );
}
if ( ! file_exists ( $file . '/worker.' . $id_or_name . '.pid' )) {
return false ;
}
2022-03-13 22:46:22 +08:00
return intval ( file_get_contents ( $file . '/worker.' . $id_or_name . '.pid' ));
case ZM_PROCESS_USER :
2022-03-15 18:05:33 +08:00
if ( ! is_string ( $id_or_name )) {
throw new ZMKnownException ( 'E99999' , 'process_name必须为字符串' );
}
if ( ! file_exists ( $file . '/user.' . $id_or_name . '.pid' )) {
return false ;
}
2022-03-13 22:46:22 +08:00
return intval ( file_get_contents ( $file . '/user.' . $id_or_name . '.pid' ));
case ZM_PROCESS_TASKWORKER :
2022-03-15 18:05:33 +08:00
if ( ! is_int ( $id_or_name )) {
throw new ZMKnownException ( 'E99999' , 'worker_id必须为整数' );
}
if ( ! file_exists ( $file . '/taskworker.' . $id_or_name . '.pid' )) {
return false ;
}
2022-03-13 22:46:22 +08:00
return intval ( file_get_contents ( $file . '/taskworker.' . $id_or_name . '.pid' ));
default :
return false ;
}
}
/**
2022-04-03 01:47:38 +08:00
* @ param null | int | string $id_or_name
2022-03-13 22:46:22 +08:00
* @ throws ZMKnownException
* @ internal
*/
public static function removeProcessState ( int $type , $id_or_name = null )
{
switch ( $type ) {
case ZM_PROCESS_MASTER :
$file = _zm_pid_dir () . '/master.json' ;
2022-03-15 18:05:33 +08:00
if ( file_exists ( $file )) {
unlink ( $file );
}
2022-03-13 22:46:22 +08:00
return ;
case ZM_PROCESS_MANAGER :
$file = _zm_pid_dir () . '/manager.pid' ;
2022-03-15 18:05:33 +08:00
if ( file_exists ( $file )) {
unlink ( $file );
}
2022-03-13 22:46:22 +08:00
return ;
case ZM_PROCESS_WORKER :
2022-03-15 18:05:33 +08:00
if ( ! is_int ( $id_or_name )) {
throw new ZMKnownException ( 'E99999' , 'worker_id必须为整数' );
}
2022-03-13 22:46:22 +08:00
$file = _zm_pid_dir () . '/worker.' . $id_or_name . '.pid' ;
2022-03-15 18:05:33 +08:00
if ( file_exists ( $file )) {
unlink ( $file );
}
2022-03-13 22:46:22 +08:00
return ;
case ZM_PROCESS_USER :
2022-03-15 18:05:33 +08:00
if ( ! is_string ( $id_or_name )) {
throw new ZMKnownException ( 'E99999' , 'process_name必须为字符串' );
}
2022-03-13 22:46:22 +08:00
$file = _zm_pid_dir () . '/user.' . $id_or_name . '.pid' ;
2022-03-15 18:05:33 +08:00
if ( file_exists ( $file )) {
unlink ( $file );
}
2022-03-13 22:46:22 +08:00
return ;
case ZM_PROCESS_TASKWORKER :
2022-03-15 18:05:33 +08:00
if ( ! is_int ( $id_or_name )) {
throw new ZMKnownException ( 'E99999' , 'worker_id必须为整数' );
}
2022-03-13 22:46:22 +08:00
$file = _zm_pid_dir () . '/taskworker.' . $id_or_name . '.pid' ;
2022-03-15 18:05:33 +08:00
if ( file_exists ( $file )) {
unlink ( $file );
}
2022-03-13 22:46:22 +08:00
return ;
}
}
public function start ()
{
2021-06-16 00:17:30 +08:00
try {
self :: $loaded_files = get_included_files ();
self :: $server -> start ();
2022-03-15 18:05:33 +08:00
zm_atomic ( 'server_is_stopped' ) -> set ( 1 );
Console :: log ( 'zhamao-framework is stopped.' );
2021-06-16 00:17:30 +08:00
} catch ( Throwable $e ) {
2022-03-15 18:05:33 +08:00
exit ( zm_internal_errcode ( 'E00011' ) . 'Framework has an uncaught ' . get_class ( $e ) . ': ' . $e -> getMessage () . PHP_EOL );
2021-06-16 00:17:30 +08:00
}
}
2022-03-15 18:05:33 +08:00
public static function printProps ( $out , $tty_width , $colorful = true )
{
2022-04-10 00:58:07 +08:00
$max_border = min ( $tty_width , 65 );
if ( LOAD_MODE === 0 ) {
2022-03-15 18:05:33 +08:00
echo Console :: setColor ( " * Framework started with source mode. \n " , $colorful ? 'yellow' : '' );
}
echo str_pad ( '' , $max_border , '=' ) . PHP_EOL ;
$current_line = 0 ;
$line_width = [];
$line_data = [];
foreach ( $out as $k => $v ) {
start :
if ( ! isset ( $line_width [ $current_line ])) {
$line_width [ $current_line ] = $max_border - 2 ;
}
2022-03-20 16:23:07 +08:00
// Console::info("行宽[$current_line]: ".$line_width[$current_line]);
2022-03-15 18:05:33 +08:00
if ( $max_border >= 57 ) { // 很宽的时候,一行能放两个短行
2022-04-10 00:58:07 +08:00
if ( $line_width [ $current_line ] === ( $max_border - 2 )) { // 空行
2022-03-15 18:05:33 +08:00
self :: writeNoDouble ( $k , $v , $line_data , $line_width , $current_line , $colorful , $max_border );
} else { // 不是空行,已经有东西了
$tmp_line = $k . ': ' . $v ;
2022-03-20 16:23:07 +08:00
// Console::info("[$current_line]即将插入后面的东西[".$tmp_line."]");
2022-03-15 18:05:33 +08:00
if ( strlen ( $tmp_line ) > $line_width [ $current_line ]) { // 地方不够,另起一行
$line_data [ $current_line ] = str_replace ( '| ' , '' , $line_data [ $current_line ]);
++ $current_line ;
goto start ;
} // 地方够,直接写到后面并另起一行
$line_data [ $current_line ] .= $k . ': ' ;
if ( $colorful ) {
$line_data [ $current_line ] .= TermColor :: color8 ( 32 );
}
$line_data [ $current_line ] .= $v ;
if ( $colorful ) {
$line_data [ $current_line ] .= TermColor :: RESET ;
}
++ $current_line ;
}
} else { // 不够宽,直接写单行
self :: writeNoDouble ( $k , $v , $line_data , $line_width , $current_line , $colorful , $max_border );
}
}
foreach ( $line_data as $v ) {
echo $v . PHP_EOL ;
}
echo str_pad ( '' , $max_border , '=' ) . PHP_EOL ;
}
2022-04-11 23:31:49 +08:00
public function getTtyWidth () : int
2022-03-15 18:05:33 +08:00
{
$size = exec ( 'stty size' );
if ( empty ( $size )) {
return 65 ;
}
return ( int ) explode ( ' ' , trim ( $size ))[ 1 ];
}
public static function loadFrameworkState ()
{
if ( ! file_exists ( DataProvider :: getDataFolder () . '.state.json' )) {
return [];
}
$r = json_decode ( file_get_contents ( DataProvider :: getDataFolder () . '.state.json' ), true );
if ( $r === null ) {
$r = [];
}
return $r ;
}
public static function saveFrameworkState ( $data )
{
return file_put_contents ( DataProvider :: getDataFolder () . '.state.json' , json_encode ( $data , 64 | 128 | 256 ));
}
private static function printMotd ( $tty_width )
{
if ( file_exists ( DataProvider :: getSourceRootDir () . '/config/motd.txt' )) {
$motd = file_get_contents ( DataProvider :: getSourceRootDir () . '/config/motd.txt' );
} else {
$motd = file_get_contents ( __DIR__ . '/../../config/motd.txt' );
}
$motd = explode ( " \n " , $motd );
foreach ( $motd as $k => $v ) {
$motd [ $k ] = substr ( $v , 0 , $tty_width );
}
$motd = implode ( " \n " , $motd );
echo $motd ;
}
2021-07-09 01:38:30 +08:00
/**
* @ noinspection PhpIncludeInspection
*/
2022-03-13 22:46:22 +08:00
private function loadServerEvents ()
{
2022-03-15 18:05:33 +08:00
if ( Phar :: running () !== '' ) {
2021-07-04 15:45:30 +08:00
ob_start ();
2022-03-15 18:05:33 +08:00
include_once DataProvider :: getFrameworkRootDir () . '/src/ZM/script_setup_loader.php' ;
2021-07-04 15:45:30 +08:00
$r = ob_get_clean ();
$result_code = 0 ;
} else {
2022-03-15 18:05:33 +08:00
$r = exec ( PHP_BINARY . ' ' . DataProvider :: getFrameworkRootDir () . '/src/ZM/script_setup_loader.php' , $output , $result_code );
2021-07-04 15:45:30 +08:00
}
2021-06-16 00:17:30 +08:00
if ( $result_code !== 0 ) {
2022-03-15 18:05:33 +08:00
Console :: error ( 'Parsing code error!' );
2021-06-16 00:17:30 +08:00
exit ( 1 );
}
$json = json_decode ( $r , true );
if ( ! is_array ( $json )) {
2022-03-15 18:05:33 +08:00
Console :: warning ( zm_internal_errcode ( 'E00012' ) . 'Parsing @SwooleHandler and @OnSetup error!' );
2021-06-16 00:17:30 +08:00
}
$this -> setup_events = $json ;
2020-12-10 16:37:04 +08:00
}
2020-08-31 10:11:06 +08:00
/**
* 从全局配置文件里读取注入系统事件的类
2022-04-10 00:58:07 +08:00
*
2020-08-31 10:11:06 +08:00
* @ throws ReflectionException
* @ throws ReflectionException
*/
2022-03-13 22:46:22 +08:00
private function registerServerEvents ()
{
2021-03-29 15:34:24 +08:00
$reader = new AnnotationReader ();
2022-03-15 18:05:33 +08:00
$all = ZMUtil :: getClassesPsr4 ( FRAMEWORK_ROOT_DIR . '/src/ZM/Event/SwooleEvent/' , 'ZM\\Event\\SwooleEvent' );
2021-03-29 15:34:24 +08:00
foreach ( $all as $v ) {
$class = new $v ();
$reflection_class = new ReflectionClass ( $class );
$anno_class = $reader -> getClassAnnotation ( $reflection_class , SwooleHandler :: class );
if ( $anno_class !== null ) { // 类名形式的注解
2022-03-15 18:05:33 +08:00
$this -> setup_events [ 'event' ][] = [
'class' => $v ,
'method' => 'onCall' ,
'event' => $anno_class -> event ,
2021-05-08 10:02:41 +08:00
];
2021-03-29 15:34:24 +08:00
}
}
2022-03-15 18:05:33 +08:00
foreach (( $this -> setup_events [ 'setup' ] ? ? []) as $v ) {
Console :: debug ( 'Calling @OnSetup: ' . $v [ 'class' ]);
$c = ZMUtil :: getModInstance ( $v [ 'class' ]);
$method = $v [ 'method' ];
$c -> { $method }();
2020-08-31 10:11:06 +08:00
}
2021-05-08 10:02:41 +08:00
2022-03-15 18:05:33 +08:00
foreach ( $this -> setup_events [ 'event' ] as $v ) {
self :: $server -> on ( $v [ 'event' ], function ( ... $param ) use ( $v ) {
ZMUtil :: getModInstance ( $v [ 'class' ]) -> { $v [ 'method' ]}( ... $param );
2021-05-08 10:02:41 +08:00
});
2020-08-31 10:11:06 +08:00
}
}
/**
* 解析命令行的 $argv 参数们
2022-04-10 00:58:07 +08:00
*
2022-04-02 23:37:22 +08:00
* @ param array $args 命令行参数
* @ param bool | string $add_port 是否添加端口号
2020-08-31 10:11:06 +08:00
*/
2022-04-02 23:37:22 +08:00
private function parseCliArgs ( array $args , & $add_port )
2022-03-13 22:46:22 +08:00
{
2020-08-31 10:11:06 +08:00
$coroutine_mode = true ;
global $terminal_id ;
2021-02-21 22:17:34 +08:00
$terminal_id = uuidgen ();
2020-08-31 10:11:06 +08:00
foreach ( $args as $x => $y ) {
2021-03-29 15:34:24 +08:00
if ( $y ) {
switch ( $x ) {
case 'worker-num' :
2021-03-25 17:11:35 +08:00
if ( intval ( $y ) >= 1 && intval ( $y ) <= 1024 ) {
2022-04-11 22:35:10 +08:00
$this -> swoole_server_config [ 'worker_num' ] = intval ( $y );
2021-03-25 17:11:35 +08:00
} else {
2022-04-11 22:35:10 +08:00
Console :: warning ( zm_internal_errcode ( 'E00013' ) . 'Invalid worker num! Turn to default value (' . ( $this -> swoole_server_config [ 'worker_num' ] ? ? swoole_cpu_num ()) . ')' );
2021-03-25 17:11:35 +08:00
}
2021-03-29 15:34:24 +08:00
break ;
case 'task-worker-num' :
2021-03-25 17:11:35 +08:00
if ( intval ( $y ) >= 1 && intval ( $y ) <= 1024 ) {
2022-04-11 22:35:10 +08:00
$this -> swoole_server_config [ 'task_worker_num' ] = intval ( $y );
$this -> swoole_server_config [ 'task_enable_coroutine' ] = true ;
2021-03-25 17:11:35 +08:00
} else {
2022-03-15 18:05:33 +08:00
Console :: warning ( zm_internal_errcode ( 'E00013' ) . 'Invalid worker num! Turn to default value (0)' );
2021-03-25 17:11:35 +08:00
}
2021-03-29 15:34:24 +08:00
break ;
case 'disable-coroutine' :
2020-09-29 15:07:43 +08:00
$coroutine_mode = false ;
2021-03-29 15:34:24 +08:00
break ;
case 'debug-mode' :
2022-03-15 18:05:33 +08:00
self :: $argv [ 'disable-safe-exit' ] = true ;
2020-08-31 10:11:06 +08:00
$coroutine_mode = false ;
$terminal_id = null ;
2022-03-15 18:05:33 +08:00
self :: $argv [ 'watch' ] = true ;
echo " * You are in debug mode, do not use in production! \n " ;
2021-03-29 15:34:24 +08:00
break ;
case 'daemon' :
2022-04-11 22:35:10 +08:00
$this -> swoole_server_config [ 'daemonize' ] = 1 ;
2022-03-15 18:05:33 +08:00
Console :: $theme = 'no-color' ;
2022-04-11 22:35:10 +08:00
Console :: log ( '已启用守护进程,输出重定向到 ' . $this -> swoole_server_config [ 'log_file' ]);
2020-08-31 10:11:06 +08:00
$terminal_id = null ;
2021-03-29 15:34:24 +08:00
break ;
case 'disable-console-input' :
case 'no-interaction' :
$terminal_id = null ;
break ;
case 'log-error' :
Console :: setLevel ( 0 );
break ;
case 'log-warning' :
Console :: setLevel ( 1 );
break ;
case 'log-info' :
Console :: setLevel ( 2 );
break ;
case 'log-verbose' :
case 'verbose' :
Console :: setLevel ( 3 );
break ;
case 'log-debug' :
Console :: setLevel ( 4 );
break ;
case 'log-theme' :
2020-08-31 10:11:06 +08:00
Console :: $theme = $y ;
2021-03-29 15:34:24 +08:00
break ;
case 'remote-terminal' :
2021-03-27 16:30:15 +08:00
$add_port = true ;
2021-03-29 15:34:24 +08:00
break ;
case 'show-php-ver' :
default :
2022-03-20 16:23:07 +08:00
// Console::info("Calculating ".$x);
// dump($y);
2021-03-29 15:34:24 +08:00
break ;
}
2020-08-31 10:11:06 +08:00
}
}
2022-03-15 18:05:33 +08:00
$global_hook = ZMConfig :: get ( 'global' , 'runtime' )[ 'swoole_coroutine_hook_flags' ] ? ? ( SWOOLE_HOOK_ALL & ( ~ SWOOLE_HOOK_CURL ));
if ( $coroutine_mode ) {
Runtime :: enableCoroutine ( true , $global_hook );
} else {
Runtime :: enableCoroutine ( false , SWOOLE_HOOK_ALL );
}
2020-08-31 10:11:06 +08:00
}
2022-03-13 22:46:22 +08:00
private static function writeNoDouble ( $k , $v , & $line_data , & $line_width , & $current_line , $colorful , $max_border )
{
2022-03-15 18:05:33 +08:00
$tmp_line = $k . ': ' . $v ;
2022-03-20 16:23:07 +08:00
// Console::info("写入[".$tmp_line."]");
if ( strlen ( $tmp_line ) > $line_width [ $current_line ]) { // 输出的内容太多了,以至于一行都放不下一个,要折行
2022-03-15 18:05:33 +08:00
$title_strlen = strlen ( $k . ': ' );
2021-03-02 21:24:31 +08:00
$content_len = $line_width [ $current_line ] - $title_strlen ;
2022-03-15 18:05:33 +08:00
$line_data [ $current_line ] = ' ' . $k . ': ' ;
if ( $colorful ) {
$line_data [ $current_line ] .= TermColor :: color8 ( 32 );
}
2021-03-02 21:24:31 +08:00
$line_data [ $current_line ] .= substr ( $v , 0 , $content_len );
2022-03-15 18:05:33 +08:00
if ( $colorful ) {
$line_data [ $current_line ] .= TermColor :: RESET ;
}
2021-03-02 21:24:31 +08:00
$rest = substr ( $v , $content_len );
++ $current_line ; // 带标题的第一行满了,折到第二行
do {
2022-03-15 18:05:33 +08:00
if ( $colorful ) {
$line_data [ $current_line ] = TermColor :: color8 ( 32 );
}
$line_data [ $current_line ] .= ' ' . substr ( $rest , 0 , $max_border - 2 );
if ( $colorful ) {
$line_data [ $current_line ] .= TermColor :: RESET ;
}
2021-03-02 21:24:31 +08:00
$rest = substr ( $rest , $max_border - 2 );
++ $current_line ;
} while ( $rest > $max_border - 2 ); // 循环,直到放完
} else { // 不需要折行
2022-03-20 16:23:07 +08:00
// Console::info("不需要折行");
2022-03-15 18:05:33 +08:00
$line_data [ $current_line ] = ' ' . $k . ': ' ;
if ( $colorful ) {
$line_data [ $current_line ] .= TermColor :: color8 ( 32 );
}
2021-03-02 21:24:31 +08:00
$line_data [ $current_line ] .= $v ;
2022-03-15 18:05:33 +08:00
if ( $colorful ) {
$line_data [ $current_line ] .= TermColor :: RESET ;
}
2021-03-02 21:24:31 +08:00
if ( $max_border >= 57 ) {
if ( strlen ( $tmp_line ) >= intval (( $max_border - 2 ) / 2 )) { // 不需要折行,直接输出一个转下一行
2022-03-20 16:23:07 +08:00
// Console::info("不需要折行,直接输出一个转下一行");
2021-03-02 21:24:31 +08:00
++ $current_line ;
} else { // 输出很小,写到前面并分片
2022-03-20 16:23:07 +08:00
// Console::info("输出很小,写到前面并分片");
2021-03-02 21:24:31 +08:00
$space = intval ( $max_border / 2 ) - 2 - strlen ( $tmp_line );
2022-03-15 18:05:33 +08:00
$line_data [ $current_line ] .= str_pad ( '' , $space );
$line_data [ $current_line ] .= '| ' ; // 添加分片
2021-03-02 21:24:31 +08:00
$line_width [ $current_line ] -= ( strlen ( $tmp_line ) + 3 + $space );
}
} else {
++ $current_line ;
}
}
}
2020-08-31 10:11:06 +08:00
}