PHP框架之路由與控制器詳解

路由與控制器之間是什麼關係?爲什麼使用路由?下面是由本站小編爲大家整理的PHP框架之路由與控制器,喜歡的可以收藏一下!瞭解更多詳情資訊,請關注應屆畢業生考試網!

PHP框架之路由與控制器詳解

  我們爲什麼要使用路由?

  原因1:一個更漂亮的URI

的改進

剛剛開始學PHP時,我們一定寫過之類的URI,使用GET方式獲取參數。這樣的URI有兩個缺點,一是容易被SQL注射攻擊,二是維護性可讀性差,大家可以比較下面兩種URI哪一種更具備可讀性。

上面URI是我們初學PHP最常用的。

這種URI是目前最流行的URI,舉個例子,比如很多讀書類,電影類網站,都使用了這樣的URI,這樣的URI要比要簡潔很多。

2.實現方法

在WEB項目的根目錄下寫一個cess文件

RewriteEngine On

RewriteRule ^([a-zA-Z0-9/]*)$ $1

重寫規則,讓域名後面的字符串直接做爲一個參數傳入,這樣就成爲了你整個WEB應用的中心,定義了“請求和響應的映射”。

  原因2:單一入口機制的易維護性

1.路由數組

一個PHP初學者,剛開始做項目,項目做着做着規模做大了,常常這個PHP頁面給另一個PHP頁面用GET方法傳值,有時傳的值還不止一個,時間一久,你的WEB項目,N個PHP頁面宛如一個複雜的蜘蛛網,讓你難以維護。一旦有修改,會涉及很多PHP文件,工作量很大。

MVC的'單一入口機制可以解決維護難的問題,路由就是一套映射,可以讓你一個URI對應一個方法。

$route=[

''=>'IndexController@Index',

'blog'=>'BlogController@Show',

'blog/{id}/{name}'=>'BlogController@Show',

];

2.獲取參數

$path=$_SERVER['PATH_INFO'];

$path=ltrim($path,'/');

echo $_EOL;

我們在瀏覽器裏輸入:後,path變量爲/blog/1。使用ltrim函數刪除左邊的斜槓,然後使用explode把字符串拆解成數組。

$path_arr=explode('/', $path);

  核心代碼如下:

if(isset($_SERVER['PATH_INFO'])){

$path=$_SERVER['PATH_INFO'];

$path=ltrim($path,'/');

$path_arr=explode('/', $path);

}

if(isset($path_arr[0])){

$key=$path_arr[0];

unset($path_arr[0]);

}

else{

$key='';

}

if(isset($path_arr[1])){

$parameters=array_values($path_arr);

}

if(isset($route[$key])){

$arr=explode('@', $route[$key]);

$controller=new $arr[0];

$action=$arr[1];

if(isset($parameters)){

$controller->$action($parameters);

}

else{

$controller->$action();

}

}

else{

require '';

}

unset函數可以銷燬數組中key和value,但是並不會重建索引,所以path_arr[0]是要調用的控制器類和方法名,path_arr[1]或者path_arr[1..N]就作爲傳入方法的參數。

重定向和錯誤頁面是WEB系統中最常見的,如果不用路由機制,你可能要沒完沒了的重複寫重定向或者錯誤頁面的顯示或者跳轉代碼,有了路由,只需要一句話就可以完成。

  原因3:減少資源的消耗

MVC採用了控制器(controller)來響應請求(request),每次請求來時,應該在指定的一個PHP文件中初始化這個控制器,而不是分別在不同的PHP文件中做初始化工作,這樣可以減少資源的消耗。

是不是一定要用控制器?

方案1:不用控制器

我們現在路由數組裏添加一項,value不是一個字符串,而是一個匿名函數(Closure)

$route=[

''=>'Index',

'blog'=>'BlogController@Show',

'blog/{id}/{name}'=>'BlogController@Show',

'f'=>function(){echo 'hello';}

];

這裏的route[f]是一個匿名函數,並不是一個控制器類的方法,所以,我們要把上一節路由代碼做一下修改:

if(isset($route[$key])){

if($route[$key] instanceof Closure){

$route[$key]();

}

else{

$arr=explode('@', $route[$key]);

$controller=new $arr[0];

$action=$arr[1];

if(isset($parameters)){

$controller->$action($parameters);

}

else{

$controller->$action();

}

}

}

else{

require '';

}

方案2:使用控制器

每一次都require一個html頁面是一件很不優雅的事情,所以我們寫一個render函數

function render($path,array $args){

extract($args);

require($path);

}

我們知道每個URI對應了一個方法,但是我們常常遇到這樣的問題:

<?php

class Controller{

public function __call($method,$args){

echo 'has not this function'.$method;

}

}

class IndexController extends Controller{

public function Index(){

echo __CLASS__;

for($i=1;$i<=20;++$i){

$data[$i]='content';

}

render('',['data'=>$data]);

}

}

class BlogController extends Controller{

public function Show(){

echo __CLASS__;

for($i=1;$i<=10;++$i){

$data[$i]='blog';

}

render('',['data'=>$data]);

}

}

?>

用不用控制器,取決於你的業務複雜度。個人建議使用控制器,但是對於業務很簡單的頁面跳轉或檢查,可以直接寫在一個匿名函數裏。

控制器裏寫些什麼?

我們也許寫過這樣的代碼:

class IndexController extends Controller{

public function Index($content){

return '<html><head></head><body>'.$content.'</body></html>';

}

}

這樣把界面的代碼嵌入的寫法是非常難以維護的,也是很多開發人員(包括我)最厭惡的寫法,因爲這種寫法並沒有做好界面與業務邏輯的分離,所以我們需要使用視圖。

<html>

<head>

</head>

<body>

<?php foreach($data as $key=>$value){ ?>

<p>

<?php echo $key.':'.$value; ?>

</p>

<?php } ?>

</body>

</html>

每一次調用控制器的某個方法時,render函數都會把參數以關聯數組的形式傳入,做到“業務邏輯”和“表現”的淺層次分離,但是這種分離還不是最好的,因爲前端開發人員仍然需要面對甚至處理PHP代碼,後端開發人員也有和前端人員溝通的成本,所以後面某一節,會再談一種更好的分離方式。