Swoole-添加自定义路由实现控制器访问
swoole本身不支持路由功能,默认情况我们实现路由访问可能要这样
$path = $request->server['request_uri']switch ($path){case '/favicon.ico':$response->status(404);$response->end('Not Found');break;case '/abc/def/gg':$response->end("/abc/def/gg");break;default://var_dump($path);$response->end('<h1>Swoole HTTP Server YYDS.</h1>');break;}
时间一长满屏都是这里的路由判断。
为了防止改方案时近视度数蹭蹭涨。。。。。。
我们需要添加添加一个自定义路由。
作为资深代码搬运工,我们老规矩,先从官方文档复制一个httpServer demo放到index.php文件中
//index.php
$http = new Swoole\Http\Server('0.0.0.0', 9501);$http->on('Request', function ($request, $response) {$response->header('Content-Type', 'text/html; charset=utf-8');$response->end('<h1>Hello Swoole. #' . rand(1000, 9999) . '</h1>');
});$http->start();
启动不用我说了,不成功直接关电脑。
因为swoole的重启命令无法重启被加载过的文件,所以这里做一个自动重启的bash,简单粗暴的重启服务
#!/bin/bashDIR_TO_WATCH="."declare -A LAST_MODIFIED
while IFS= read -r file; doLAST_MODIFIED["$file"]=$(stat -c %Y "$file")
done < <(find "$DIR_TO_WATCH" -type f)start_swoole() {php "$DIR_TO_WATCH/index.php" &PID=$!echo "Swoole server started with PID: $PID"
}stop_swoole() {if [ -n "$PID" ]; thenecho "Stopping Swoole server (PID: $PID)..."kill -TERM "$PID"wait "$PID"echo "Swoole server stopped (PID: $PID)"fi
}trap stop_swoole EXIT# swoole
start_swoolewhile true; do# 检查是否有文件被修改MODIFIED=falsewhile IFS= read -r file; doCURRENT_MODIFIED=$(stat -c %Y "$file")if [ "${LAST_MODIFIED["$file"]}" != "$CURRENT_MODIFIED" ]; thenMODIFIED=trueLAST_MODIFIED["$file"]="$CURRENT_MODIFIED"fidone < <(find "$DIR_TO_WATCH" -type f)# 如果有文件被修改,重启 Swoole 服务器if [ "$MODIFIED" = true ]; thenecho "$DIR_TO_WATCH 被修改,服务重启。。。"stop_swoolestart_swoolefisleep 1
done
启动这个bash
现在开始进入正题
首先创建一个Routes的class
然后创建一个存放路由的变量,并引入swoole相关的文件
然后添加一个方法对routes做简单处理
<?phpuse Swoole\Http\Request;
use Swoole\Http\Response;
class Routes
{protected $routes = [];//addRoute对routes做简单处理public function addRoute($method, $uri,callable $action){$this->routes[] = ['method' => strtoupper($method),'pattern' => '/^' . str_replace('/', '\/', $uri) . '$/', //地址替换为正则'handler' => $action];var_dump($this->routes);}}
接下来我们继续在路由类中创建一个分发路由的函数,
解析请求路径和请求方法,再加一个404的页面处理
// 路由分发方法
public function dispatch(Request $request, Response $response): void
{$requestMethod = strtoupper($request->server['request_method'] ?? 'GET');$requestPath = parse_url($request->server['request_uri'] ?? '/', PHP_URL_PATH);/** 此处先留白 **/$response->status(404);$response->end('404 Not Found');}
接下来修改index.php
随便添加两个路由地址,并返回随便什么内容
//index.php
require_once 'Routes.php';
$http = new Swoole\Http\Server('0.0.0.0', 9501);$routes = new Routes();
$routes->addRoute('get', '/',function ($request, $response) {$response->end('<h1>Home. #' . rand(1000, 9999) . '</h1>');
});
$routes->addRoute('get', '/abc/aa',function ($request, $response) {$response->end('<h1>Hello AA. #' . rand(1000, 9999) . '</h1>');
});
$routes->addRoute('get', '/abc/bb',function ($request, $response) {$response->end('<h1>Hello BB. #' . rand(1000, 9999) . '</h1>');
});
$http->on('Request', function ($request, $response)use($routes) {$response->header('Content-Type', 'text/html; charset=utf-8');//注释send//$response->end('<h1>Hello Swoole ++++ asdsf. #' . rand(1000, 9999) . '</h1>');//分发路由$routes->dispatch($request, $response);
});$http->start();
这时候打开浏览器,页面会显示你自己写的404 not fund
but 哈 别急。
路由分发方法主要解析routes数组,并执行routes中的回调函数
所以接下来在上面留白地方遍历routes,
// 路由分发public function dispatch(Request $request, Response $response): void{$requestMethod = strtoupper($request->server['request_method'] ?? 'GET');$requestPath = parse_url($request->server['request_uri'] ?? '/', PHP_URL_PATH);//遍历routesforeach ($this->routes as $route) {if ($route['method'] !== $requestMethod) {continue;}if (preg_match($route['pattern'], $requestPath, $matches)) {// 提取动态参数$params = [];foreach ($matches as $key => $value) {if (is_string($key)) {$params[$key] = $value;}}$route['handler']($request, $response, ...array_values($params));return; // 路由匹配成功就别继续了}}// 404 处理$response->status(404);$response->end('404 Not Found');}
现在等待swoole重启后刷新页面、主页应该显示Home # xxx
继续访问你定义的几个路由地址,页面返回你预期的内容
实际应用过程中,我们定义了路由应该是get(),post(),put()这样的方法
路由定义也不要在index.php
所以接下来我们继续修改Router文件,修改addRoute的可见性和实现方法,支持控制器文件,并添加对应请求方法
修改后的index.php代码
//index.php
require_once 'Routes.php';
require_once 'IndexController.php';
require_once 'route.php';
$http = new Swoole\Http\Server('0.0.0.0', 9501);
/** @var Routes $routes */
$http->on('Request', function ($request, $response)use($routes) {$response->header('Content-Type', 'text/html; charset=utf-8');//注释send//$response->end('<h1>Hello Swoole ++++ asdsf. #' . rand(1000, 9999) . '</h1>');//分发路由$routes->dispatch($request, $response);
});$http->start();
创建路由定义 文件route.php
<?php
//route.php
$routes = new Routes();
$routes->get('/',[IndexController::class,'index']); //添加一个控制器访问$routes->get( '/abc/aa',function ($request, $response) {$response->end('<h1>Hello AA. #' . rand(1000, 9999) . '</h1>');
});
$routes->get( '/abc/bb',function ($request, $response) {$response->end('<h1>Hello BB. #' . rand(1000, 9999) . '</h1>');
});
$routes->get( '/abc/cc',function ($request, $response) {$response->end('<h1>Hello cc. #' . rand(1000, 9999) . '</h1>');
});
修改Routes文件
//Routes
use Swoole\Http\Request;
use Swoole\Http\Response;
class Routes
{protected $routes = [];public static $verbs = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];public function get($uri,$action){$this->addRoute('get', $uri, $action);}public function post($uri,$action){$this->addRoute('post', $uri, $action);}public function put($uri,$action){$this->addRoute('put', $uri, $action);}public function delete($uri, $action){$this->addRoute('delete', $uri, $action);}public function any($uri, $action){foreach(self::$verbs as $verb){$this->addRoute($verb, $uri, $action);}}//修改可见性protected function addRoute($method, $uri, $action){// 将路径中的 {param} 转换为正则表达式$pattern = preg_replace_callback('/\{(\w+)\}/', function ($matches) {return '(?P<'.$matches[1].'>\w+)';}, $uri);if(is_array($action)) //添加对控制器支持{$action = function (Request $request,Response $response, ...$params)use($action){[$class, $method] = $action;$controller = new $class;return $controller->$method($request, $response, ...$params);};}$this->routes[] = ['method' => strtoupper($method),'pattern' => '/^' . str_replace('/', '\/', $pattern) . '$/','handler' => $action];}// 路由分发public function dispatch(Request $request, Response $response): void{$requestMethod = strtoupper($request->server['request_method'] ?? 'GET');$requestPath = parse_url($request->server['request_uri'] ?? '/', PHP_URL_PATH);foreach ($this->routes as $route) {if ($route['method'] !== $requestMethod) {continue;}if (preg_match($route['pattern'], $requestPath, $matches)) {// 提取动态参数$params = [];foreach ($matches as $key => $value) {if (is_string($key)) {$params[$key] = $value;}}$route['handler']($request, $response, ...array_values($params));return; // 路由匹配成功就别继续了}}// 404 处理$response->status(404);$response->end('404 Not Found');}}
添加一个控制器IndexController
<?php
use Swoole\Http\Request;
use Swoole\Http\Response;
class IndexController
{public function index(Request $request, Response $response){$response->end('<h1>INDEX Controller </h1>');}}
浏览器访问首页
显示效果和预期一致,
至于请求参数,那是request的工作