1.URI的改進php
剛剛開始學PHP時,咱們必定寫過blog.php?id=1之類的URI,使用GET方式獲取參數。這樣的URI有兩個缺點,一是容易被SQL注射攻擊,二是維護性可讀性差,你們能夠比較下面兩種URI哪種更具有可讀性。html
www.mysite.com/blog.php?id=1
上面URI是咱們初學PHP最經常使用的。前端
www.mysite.com/blog/1
這種URI是目前最流行的URI,舉個例子,好比不少讀書類,電影類網站,都使用了這樣的URI,這樣的URI要比index.php?a=1&b=2&c=3&d=4....要簡潔不少。後端
2.實現方法數組
在WEB項目的根目錄下寫一個.htaccess文件瀏覽器
RewriteEngine On RewriteRule ^([a-zA-Z0-9/]*)$ index.php/$1
重寫規則,讓域名後面的字符串直接作爲一個參數傳入index.php,這樣index.php就成爲了你整個WEB應用的中心,定義了「請求和響應的映射」。bash
1.路由數組函數
一個PHP初學者,剛開始作項目,項目作着作着規模作大了,經常這個PHP頁面給另外一個PHP頁面用GET方法傳值,有時傳的值還不止一個,時間一久,你的WEB項目,N個PHP頁面宛如一個複雜的蜘蛛網,讓你難以維護。一旦有修改,會涉及不少PHP文件,工做量很大。網站
MVC的單一入口機制能夠解決維護難的問題,路由就是一套映射,可讓你一個URI對應一個方法。ui
$route=[ ''=>'IndexController@Index', 'blog'=>'BlogController@Show', 'blog/{id}/{name}'=>'BlogController@Show', ];
2.獲取參數
$path=$_SERVER['PATH_INFO']; $path=ltrim($path,'/'); echo $path.PHP_EOL;
咱們在瀏覽器裏輸入:www.mysite.com/blog/1後,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 'error.html.php'; }
unset函數能夠銷燬數組中key和value,可是並不會重建索引,因此path_arr[0]是要調用的控制器類和方法名,path_arr[1]或者path_arr[1..N]就做爲傳入方法的參數。
重定向和錯誤頁面是WEB系統中最多見的,若是不用路由機制,你可能要沒完沒了的重複寫重定向或者錯誤頁面的顯示或者跳轉代碼,有了路由,只須要一句話就能夠完成。
MVC採用了控制器(controller)來響應請求(request),每次請求來時,應該在指定的一個PHP文件中初始化這個控制器,而不是分別在不一樣的PHP文件中作初始化工做,這樣能夠減小資源的消耗。
咱們如今路由數組裏添加一項,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 'error.html.php'; }
每一次都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('template.html.php',['data'=>$data]); } } class BlogController extends Controller{ public function Show(){ echo __CLASS__; for($i=1;$i<=10;++$i){ $data[$i]='blog'; } render('template.html.php',['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){ ?> <div> <?php echo $key.':'.$value; ?> </div> <?php } ?> </body> </html>
每一次調用控制器的某個方法時,render函數都會把參數以關聯數組的形式傳入,作到「業務邏輯」和「表現」的淺層次分離,可是這種分離還不是最好的,由於前端開發人員仍然須要面對甚至處理PHP代碼,後端開發人員也有和前端人員溝通的成本,因此後面某一節,會再談一種更好的分離方式。