<?php
namespace sR;
use tool\CliChain;
use tool\CliSr;
use tool\WebChain;
class Router
{
const MethodCli = 'cli'; const CliUnfind = ':unfind'; protected static $routerRuleDick = []; protected static $queryString = ''; protected static $queryRawString = '';
protected static $queryParam = [];
static function listen(){
if(Adapter::isCli()){
new CliChain();
}else{
new WebChain();
}
if(!Adapter::isCli()){
self::webListener();
}else{
self::cliListener();
}
$setTrack = Adapter::getAppConfig()->value('track');
if($setTrack){
Log::info('用时: '.Adapter::getRtime().'s');
}
}
protected static function matchTheWebRule($rule){
$matched = false;
if($rule == self::$queryString){
$matched = true;
} else{
static $qStrQue = null;
if(!$qStrQue){
$qStrQue = explode('/', self::$queryString);
}
$rStrQue = explode('/', $rule);
if(count($qStrQue) == count($rStrQue)){
$param = [];
$allMatched = true;
foreach ($rStrQue as $i => $v){
$qv = $qStrQue[$i];
if(substr($v, 0, 1) == '{' && substr($v, -1) == '}'){
$key = substr($v, 1, -1);
$param[$key] = $qv;
}else if($v != $qv){
$allMatched = false;
break;
}
}
if($allMatched){
$matched = $allMatched;
self::$queryParam = $param;
}
}
}
return $matched;
}
protected static function getStdRuleStr($rule){
$rule = preg_replace('/\s/', '', $rule);
if($rule && '/' == substr($rule, 0, 1)){
$rule = substr($rule, 1);
}
if($rule && '/' == substr($rule, -1)){
$rule = substr($rule, 0, -1);
}
return $rule;
}
protected static function webListener(){
$app = Adapter::getAppConfig();
$queryString = $app->value('web.rewrite_key');
$queryString = ($_GET[$queryString] ?? null);
$queryString = ($queryString? self::getStdRuleStr($queryString) : null);
self::$queryString = $queryString;
self::$queryRawString = $queryString;
$method = Request::method();
$ruleDick = self::$routerRuleDick[$method] ?? [];
$findPathMk = false;
$rdata = null; foreach ($ruleDick as $rules){
$rtdata = self::matchTheWebRule($rules['path']);
if($rtdata){
if($rules['callback'] ?? false){
$param = self::$queryParam;
$param = empty($param)? false: array_values($param);
if(is_array($param)){
$rdata = call_user_func($rules['callback'], ...$param);
}else{
$rdata = call_user_func($rules['callback']);
}
$findPathMk = true;
}
break;
}
}
if($findPathMk == false){
$findPathMk = self::webAutoRouter();
if($findPathMk && $findPathMk !== true){
$rdata = $findPathMk;
$findPathMk = true;
}
}
if(false === $findPathMk){
$unfindRouter = (self::$routerRuleDick[self::CliUnfind] ?? false);
if(is_callable($unfindRouter)){
$rdata = call_user_func($unfindRouter);
}
}
if(is_array($rdata)){
Response::json($rdata);
}
}
protected static function webAutoRouter(){
$findMk = false;
$app = Adapter::getAppConfig();
if($app->value('auto_router')){
$clsRs = $app->value('web');
$command = Cli::getCommand() ?? $clsRs['default_ctrl'];
if($command){
$nsPref = $app->value('web.ns_pref');
foreach ($nsPref as $v){
$cls = $v. ucfirst($command);
if(class_exists($cls)){
$instance = new $cls();
$action = Cli::getAction() ?? $clsRs['default_method'];;
if(method_exists($instance, $action)){
call_user_func([$instance, $action]);
}
$findMk = true;
break;
}
}
}
}
return $findMk;
}
protected static function cliAutoRouter(){
$findMk = false;
$app = Adapter::getAppConfig();
if($app->value('auto_router')){
$clsRs = $app->value('cli');
$command = Cli::getCommand() ?? $clsRs['default_ctrl'];
if($command){
$nsPref = $app->value('cli.ns_pref');
foreach ($nsPref as $v){
$cls = $v. ucfirst($command);
if(class_exists($cls)){
$instance = new $cls();
$action = Cli::getAction() ?? $clsRs['default_method'];;
if(method_exists($instance, $action)){
call_user_func([$instance, $action]);
}
$findMk = true;
break;
}
}
}
}
return $findMk;
}
protected static function cliListener(){
$ruleDick = self::$routerRuleDick[self::MethodCli] ?? [];
$matchedRouterMk = false;
foreach ($ruleDick as $data){
$name = $data['name'] ?? false;
$name = self::getStdRuleStr($name);
$matched = self::matchTheCliRule($name);
if($matched){
$args = ($matched['args'] ?? false);
if(!empty($args) && is_array($args)){
$args = array_values($args);
call_user_func($data['callback'], ...$args);
}else{
call_user_func($data['callback']);
}
$matchedRouterMk = true;
break;
}
}
if($matchedRouterMk == false){
$matchedRouterMk = self::cliAutoRouter();
}
if($matchedRouterMk == false){
$app = Adapter::getAppConfig();
if($app->value('cli.sr')){
$cliSr = new CliSr();
$matchedRouterMk = $cliSr->isMatched();
}
}
if($matchedRouterMk == false){
$unfindRouter = (self::$routerRuleDick[self::CliUnfind] ?? false);
if(is_callable($unfindRouter)){
call_user_func($unfindRouter);
}
}
}
protected static function matchTheCliRule($name){
$matched = null;
$args = [];
$ruleQue = explode('/', $name);
$cmdQueue = Cli::getCmdQueue();
if(!empty($cmdQueue) && count($ruleQue) == count($cmdQueue)){
$ruleMatched = true;
foreach ($ruleQue as $i => $v){
if(substr($v, 0, 1) == '{' && substr($v, -1) == '}'){
$key = substr($v, 1, -1);
$args[$key] = $cmdQueue[$i];
}elseif ($v !== $cmdQueue[$i]){
$ruleMatched = false;
break;
}
}
if($ruleMatched){
return [
'args' => $args
];
}
}
return $matched;
}
static function get($path, $callback){
if(!isset(self::$routerRuleDick['get'])){
self::$routerRuleDick['get'] = [];
}
$path = self::getStdRuleStr($path);
self::$routerRuleDick['get'][] = ['path'=>$path, 'callback'=>$callback];
}
static function post($path, $callback){
if(!isset(self::$routerRuleDick['post'])){
self::$routerRuleDick['post'] = [];
}
$path = self::getStdRuleStr($path);
self::$routerRuleDick['post'][] = ['path'=>$path, 'callback'=>$callback];
}
static function unfind($callback){
self::$routerRuleDick[self::CliUnfind] = $callback;
}
static function getPath(){
return self::$queryRawString;
}
static function cli($name, $callback){
$cli = self::MethodCli;
if(!isset(self::$routerRuleDick[$cli])){
self::$routerRuleDick[$cli] = [];
}
self::$routerRuleDick[$cli][] = ['name'=>$name, 'callback'=>$callback];
}
static function match($method, $rule, $callback){
$method = preg_replace('/\s/', '', $method);
foreach (explode('|', $method) as $mth){
if(empty($mth)){
continue;
}
if(!isset(self::$routerRuleDick[$mth])){
self::$routerRuleDick[$mth] = [];
}
$data = ['callback'=>$callback];
if($mth == self::MethodCli){
$data['name'] = $rule;
}else{
$data['path'] = $rule;
}
self::$routerRuleDick[$mth][] = $data;
}
}
}