PHP框架之简单的路由器
路由的功能就是分发请求到不同的控制器,基于的原理就是正则匹配。接下来呢,我们实现一个简单的路由器,实现的能力是对于静态的路由(没占位符的),正确调用callback。
对于有占位符的路由,正确调用callback时传入占位符参数,譬如对于路由:/user/{id},当请求为/user/23时,传入参数$args结构为
[ 'id' => '23' ]
大致思路
我们需要把每个路由的信息管理起来:http方法($method),路由字符串($route),回调($callback),因此需要一个addRoute方法,另外提供短方法get,post(就是把$method写好)
对于/user/{id}这样的有占位符的路由字符串,把占位符要提取出来,然后占位符部分变成正则字符串
实现
Route.php类
<?phpnamespace SalamanderRoute;class Route { /** @var string */ public $httpMethod; /** @var string */ public $regex; /** @var array */ public $variables; /** @var mixed */ public $handler; /** * Constructs a route (value object). * * @param string $httpMethod * @param mixed $handler * @param string $regex * @param array $variables */ public function __construct($httpMethod, $handler, $regex, $variables) { $this->httpMethod = $httpMethod; $this->handler = $handler; $this->regex = $regex; $this->variables = $variables; } /** * Tests whether this route matches the given string. * * @param string $str * * @return bool */ public function matches($str) { $regex = '~^' . $this->regex . '$~'; return (bool) preg_match($regex, $str); } }
Dispatcher.php
<?php/** * User: salamander * Date: 2017/11/12 * Time: 13:43 */namespace SalamanderRoute;class Dispatcher { /** @var mixed[][] */ protected $staticRoutes = []; /** @var Route[][] */ private $methodToRegexToRoutesMap = []; const NOT_FOUND = 0; const FOUND = 1; const METHOD_NOT_ALLOWED = 2; /** * 提取占位符 * @param $route * @return array */ private function parse($route) { $regex = '~^(?:/[a-zA-Z0-9_]*|/\{([a-zA-Z0-9_]+?)\})+/?$~'; if(preg_match($regex, $route, $matches)) { // 去掉full match array_shift($matches); return [ preg_replace('~{[a-zA-Z0-9_]+?}~', '([a-zA-Z0-9_]+)', $route), $matches, ]; } throw new \LogicException('register route failed, pattern is illegal'); } /** * 注册路由 * @param $httpMethod string | string[] * @param $route * @param $handler */ public function addRoute($httpMethod, $route, $handler) { $routeData = $this->parse($route); foreach ((array) $httpMethod as $method) { if ($this->isStaticRoute($routeData)) { $this->addStaticRoute($httpMethod, $routeData, $handler); } else { $this->addVariableRoute($httpMethod, $routeData, $handler); } } } private function isStaticRoute($routeData) { return count($routeData[1]) === 0; } private function addStaticRoute($httpMethod, $routeData, $handler) { $routeStr = $routeData[0]; if (isset($this->staticRoutes[$httpMethod][$routeStr])) { throw new \LogicException(sprintf( 'Cannot register two routes matching "%s" for method "%s"', $routeStr, $httpMethod )); } if (isset($this->methodToRegexToRoutesMap[$httpMethod])) { foreach ($this->methodToRegexToRoutesMap[$httpMethod] as $route) { if ($route->matches($routeStr)) { throw new \LogicException(sprintf( 'Static route "%s" is shadowed by previously defined variable route "%s" for method "%s"', $routeStr, $route->regex, $httpMethod )); } } } $this->staticRoutes[$httpMethod][$routeStr] = $handler; } private function addVariableRoute($httpMethod, $routeData, $handler) { list($regex, $variables) = $routeData; if (isset($this->methodToRegexToRoutesMap[$httpMethod][$regex])) { throw new \LogicException(sprintf( 'Cannot register two routes matching "%s" for method "%s"', $regex, $httpMethod )); } $this->methodToRegexToRoutesMap[$httpMethod][$regex] = new Route( $httpMethod, $handler, $regex, $variables ); } public function get($route, $handler) { $this->addRoute('GET', $route, $handler); } public function post($route, $handler) { $this->addRoute('POST', $route, $handler); } public function put($route, $handler) { $this->addRoute('PUT', $route, $handler); } public function delete($route, $handler) { $this->addRoute('DELETE', $route, $handler); } public function patch($route, $handler) { $this->addRoute('PATCH', $route, $handler); } public function head($route, $handler) { $this->addRoute('HEAD', $route, $handler); } /** * 分发 * @param $httpMethod * @param $uri */ public function dispatch($httpMethod, $uri) { $staticRoutes = array_keys($this->staticRoutes[$httpMethod]); foreach ($staticRoutes as $staticRoute) { if($staticRoute === $uri) { return [self::FOUND, $this->staticRoutes[$httpMethod][$staticRoute], []]; } } $routeLookup = []; $index = 1; $regexes = array_keys($this->methodToRegexToRoutesMap[$httpMethod]); foreach ($regexes as $regex) { $routeLookup[$index] = [ $this->methodToRegexToRoutesMap[$httpMethod][$regex]->handler, $this->methodToRegexToRoutesMap[$httpMethod][$regex]->variables, ]; $index += count($this->methodToRegexToRoutesMap[$httpMethod][$regex]->variables); } $regexCombined = '~^(?:' . implode('|', $regexes) . ')$~'; if(!preg_match($regexCombined, $uri, $matches)) { return [self::NOT_FOUND]; } for ($i = 1; '' === $matches[$i]; ++$i); list($handler, $varNames) = $routeLookup[$i]; $vars = []; foreach ($varNames as $varName) { $vars[$varName] = $matches[$i++]; } return [self::FOUND, $handler, $vars]; } }
配置
nginx.conf重写到index.php
location / { try_files $uri $uri/ /index.php$is_args$args; # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } } composer.json自动载入 { "name": "salmander/route", "require": {}, "autoload": { "psr-4": { "SalamanderRoute\\": "SalamanderRoute/" } }}
composer.json自动载入
{ "name": "salmander/route", "require": {}, "autoload": { "psr-4": { "SalamanderRoute\\": "SalamanderRoute/" } }
最终使用
index.php
<?phpinclude_once 'vendor/autoload.php';use SalamanderRoute\Dispatcher; $dispatcher = new Dispatcher(); $dispatcher->get('/', function () { echo 'hello world'; }); $dispatcher->get('/user/{id}', function ($args) { echo "user {$args['id']} visit"; });// Fetch method and URI from somewhere$httpMethod = $_SERVER['REQUEST_METHOD']; $uri = $_SERVER['REQUEST_URI'];// 去掉查询字符串if (false !== $pos = strpos($uri, '?')) { $uri = substr($uri, 0, $pos); } $routeInfo = $dispatcher->dispatch($httpMethod, $uri);switch ($routeInfo[0]) { case Dispatcher::NOT_FOUND: echo '404 not found'; break; case Dispatcher::FOUND: $handler = $routeInfo[1]; $vars = $routeInfo[2]; $handler($vars); break; }
看了上面的这个案例大家应该对PHP实现简单路由器,有更清楚的认识吧,后期我们还会继续推出相关文章,大家有任何问题都可以踊跃发言,
相关推荐:
Atas ialah kandungan terperinci PHP框架之简单的路由器. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

AI Hentai Generator
Menjana ai hentai secara percuma.

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas



PHP 8.4 membawa beberapa ciri baharu, peningkatan keselamatan dan peningkatan prestasi dengan jumlah penamatan dan penyingkiran ciri yang sihat. Panduan ini menerangkan cara memasang PHP 8.4 atau naik taraf kepada PHP 8.4 pada Ubuntu, Debian, atau terbitan mereka

Untuk bekerja dengan tarikh dan masa dalam cakephp4, kami akan menggunakan kelas FrozenTime yang tersedia.

CakePHP ialah rangka kerja sumber terbuka untuk PHP. Ia bertujuan untuk menjadikan pembangunan, penggunaan dan penyelenggaraan aplikasi lebih mudah. CakePHP adalah berdasarkan seni bina seperti MVC yang berkuasa dan mudah difahami. Model, Pandangan dan Pengawal gu

Untuk mengusahakan muat naik fail, kami akan menggunakan pembantu borang. Di sini, adalah contoh untuk muat naik fail.

Pengesah boleh dibuat dengan menambah dua baris berikut dalam pengawal.

Kod Visual Studio, juga dikenali sebagai Kod VS, ialah editor kod sumber percuma — atau persekitaran pembangunan bersepadu (IDE) — tersedia untuk semua sistem pengendalian utama. Dengan koleksi sambungan yang besar untuk banyak bahasa pengaturcaraan, Kod VS boleh menjadi c

CakePHP ialah rangka kerja MVC sumber terbuka. Ia menjadikan pembangunan, penggunaan dan penyelenggaraan aplikasi lebih mudah. CakePHP mempunyai beberapa perpustakaan untuk mengurangkan beban tugas yang paling biasa.

Tutorial ini menunjukkan cara memproses dokumen XML dengan cekap menggunakan PHP. XML (bahasa markup extensible) adalah bahasa markup berasaskan teks yang serba boleh yang direka untuk pembacaan manusia dan parsing mesin. Ia biasanya digunakan untuk penyimpanan data
