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


Posted in PHP onMay 07, 2017

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

1.URI的改进

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

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应用的中心,定义了“请求和响应的映射”。

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

原因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 'error.html.php';

}

方案2:使用控制器

自制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代码,后端开发人员也有和前端人员沟通的成本,所以后面某一节,会再谈一种更好的分离方式。

PHP 相关文章推荐
PHP注释实例技巧
Oct 03 PHP
PHP 错误之引号中使用变量
May 04 PHP
PHP url 加密解密函数代码
Aug 26 PHP
PHP使用正则表达式清除超链接文本
Nov 12 PHP
PHP按行读取文件时删除换行符的3种方法
May 04 PHP
php中error与exception的区别及应用
Jul 28 PHP
php实现的美国50个州选择列表实例
Apr 20 PHP
PHP 下载文件时如何自动添加bom头及解释BOM头和去掉bom头的方法
Jan 04 PHP
php pdo oracle中文乱码的快速解决方法
May 16 PHP
php+js实现百度地图多点标注的方法
Nov 30 PHP
PHP实现深度优先搜索算法(DFS,Depth First Search)详解
Sep 16 PHP
Laravel 5.4.36中session没有保存成功问题的解决
Feb 19 PHP
PHP-CGI远程代码执行漏洞分析与防范
May 07 #PHP
PHP关键特性之命名空间实例详解
May 06 #PHP
PHP 中使用explode()函数切割字符串为数组的示例
May 06 #PHP
Thinkphp 空操作、空控制器、命名空间(详解)
May 05 #PHP
thinkPHP实现的联动菜单功能详解
May 05 #PHP
thinkPHP实现的省市区三级联动功能示例
May 05 #PHP
php str_replace替换指定次数的方法详解
May 05 #PHP
You might like
无需重新编译php加入ftp扩展的解决方法
2013/02/07 PHP
PHP date()函数警告: It is not safe to rely on the system解决方法
2014/08/20 PHP
thinkphp实现上一篇与下一篇的方法
2014/12/08 PHP
ThinkPHP5+Layui实现图片上传加预览功能
2018/08/17 PHP
Smarty缓存机制实例详解【三种缓存方式】
2019/07/20 PHP
PHP Redis扩展无法加载的问题解决方法
2019/08/22 PHP
让innerHTML的脚本也可以运行起来
2006/07/01 Javascript
JS中==与===操作符的比较
2009/03/21 Javascript
js触发asp.net的Button的Onclick事件应用
2013/02/02 Javascript
jQuery中replaceWith()方法用法实例
2014/12/25 Javascript
简介JavaScript中toTimeString()方法的使用
2015/06/12 Javascript
使用JQuery实现智能表单验证功能
2016/03/08 Javascript
详谈表单格式化插件jquery.serializeJSON
2017/06/23 jQuery
微信小程序上传多图到服务器并获取返回的路径
2019/05/05 Javascript
vue项目中运用webpack动态配置打包多种环境域名的方法
2019/06/24 Javascript
微信小程序获取地理位置及经纬度授权代码实例
2019/09/18 Javascript
JS中this的4种绑定规则详解
2020/02/04 Javascript
[01:10:49]Secret vs VGJ.S 2018国际邀请赛淘汰赛BO3 第二场 8.24
2018/08/25 DOTA
基于Python实现的百度贴吧网络爬虫实例
2015/04/17 Python
使用Python制作获取网站目录的图形化程序
2015/05/04 Python
使用Python监视指定目录下文件变更的方法
2018/10/15 Python
解决Python一行输出不显示的问题
2018/12/03 Python
python中字典按键或键值排序的实现代码
2019/08/27 Python
解决Django中修改js css文件但浏览器无法及时与之改变的问题
2019/08/31 Python
Python进阶之使用selenium爬取淘宝商品信息功能示例
2019/09/16 Python
python二维键值数组生成转json的例子
2019/12/06 Python
在python中使用pyspark读写Hive数据操作
2020/06/06 Python
python中return不返回值的问题解析
2020/07/22 Python
百货商场楼层班组长竞聘书
2014/03/31 职场文书
毕业生实习证明
2014/09/19 职场文书
工作所在部门证明
2014/09/21 职场文书
班子四风对照检查材料思想汇报
2014/09/29 职场文书
部门2014年度工作总结
2014/11/12 职场文书
公司表扬信格式
2015/05/04 职场文书
DE1107机评
2022/04/05 无线电
Windows7下FTP搭建图文教程
2022/08/05 Servers