自制PHP框架之路由与控制器

前端之家收集整理的这篇文章主要介绍了自制PHP框架之路由与控制器前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

我们为什么要使用路由?原因1:一个更漂亮的URI

1.URI的改进

刚刚开始学PHP时,我们一定写过blog.PHP?id=1之类的URI,使用GET方式获取参数。这样的URI有两个缺点,一是容易被sql注射攻击,二是维护性可读性差,大家可以比较下面两种URI哪一种更具备可读性。

PHP;"> www.mysite.com/blog.PHP?id=1

上面URI是我们初学PHP最常用的。

PHP;"> www.mysite.com/blog/1

这种URI是目前最流行的URI,举个例子,比如很多读书类,电影类网站,都使用了这样的URI,这样的URI要比index.PHP?a=1&b=2&c=3&d=4....要简洁很多。

2.实现方法

在WEB项目的根目录下写一个.htaccess文件

PHP;"> RewriteEngine On

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

重写规则,让域名后面的字符串直接做为一个参数传入index.PHP,这样index.PHP就成为了你整个WEB应用的中心,定义了“请求和响应的映射”。

原因2:单一入口机制的易维护性

1.路由数组

一个PHP初学者,刚开始做项目,项目做着做着规模做大了,常常这个PHP页面给另一个PHP页面用GET方法传值,有时传的值还不止一个,时间一久,你的WEB项目,N个PHP页面宛如一个复杂的蜘蛛网,让你难以维护。一旦有修改,会涉及很多PHP文件,工作量很大。

MVC的单一入口机制可以解决维护难的问题,路由就是一套映射,可以让你一个URI对应一个方法

''=>'IndexController@Index','blog'=>'BlogController@Show','blog/{id}/{name}'=>'BlogController@Show',];

2.获取参数

PHP;"> $path=$_SERVER['PATH_INFO'];

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

echo $path.PHP_EOL;

我们在浏览器里输入:www.mysite.com/blog/1后,path变量为/blog/1。使用ltrim函数删除左边的斜杠,然后使用explode把字符串拆解成数组。

PHP;"> $path_arr=explode('/',$path);

核心代码如下:

$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系统中最常见的,如果不用路由机制,你可能要没完没了的重复写重定向或者错误页面显示或者跳转代码,有了路由,只需要一句话就可以完成。

原因3:减少资源的消耗

MVC采用了控制器(controller)来响应请求(request),每次请求来时,应该在指定的一个PHP文件中初始化这个控制器,而不是分别在不同的PHP文件中做初始化工作,这样可以减少资源的消耗。

是不是一定要用控制器?方案1:不用控制器

我们现在路由数组里添加一项,value不是一个字符串,而是一个匿名函数(Closure)

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

]; 

这里的route[f]是一个匿名函数,并不是一个控制器类的方法,所以,我们要把上一节路由代码做一下修改

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';

}

方案2:使用控制器

每一次都require一个html页面是一件很不优雅的事情,所以我们写一个render函数

PHP;"> 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]);

}

}

?>

用不用控制器,取决于你的业务复杂度。个人建议使用控制器,但是对于业务很简单的页面跳转或检查,可以直接写在一个匿名函数里。

控制器里写些什么?

我们也许写过这样的代码

PHP;"> class IndexController extends Controller{

public function Index($content){

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

}

}

这样把界面的代码嵌入的写法是非常难以维护的,也是很多开发人员(包括我)最厌恶的写法,因为这种写法并没有做好界面与业务逻辑的分离,所以我们需要使用视图。

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

  <div>

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

  </div>

<?php } ?>

每一次调用控制器的某个方法时,render函数都会把参数以关联数组的形式传入,做到“业务逻辑”和“表现”的浅层次分离,但是这种分离还不是最好的,因为前端开发人员仍然需要面对甚至处理PHP代码,后端开发人员也有和前端人员沟通的成本,所以后面某一节,会再谈一种更好的分离方式。

猜你在找的PHP相关文章