「七天自制PHP框架」第一天:路由与控制器

我们为什么要使用路由?

原因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=[
	‘‘=>‘[email protected]‘,
	‘blog‘=>‘[email protected]‘,
	‘blog/{id}/{name}‘=>‘[email protected]‘,
];

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‘=>‘[email protected]‘,
    ‘blog/{id}/{name}‘=>‘[email protected]‘,
    ‘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:使用控制器

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

时间: 2024-10-12 11:46:54

「七天自制PHP框架」第一天:路由与控制器的相关文章

「七天自制PHP框架」第二天:模型与数据库

往期回顾:「七天自制PHP框架」第一天:路由与控制器,点击此处 什么是模型? 我们的WEB系统一定会和各种数据打交道,实际开发过程中,往往一个类对应了关系数据库的一张或多张数据表,这里就会出现两个问题. 1.类和数据表,一方修改会导致另一方的修改,只要数据表结构不定下来,业务逻辑的开发几乎没法开工 2.获取数据时会牵涉很多SQL语句的拼接,如果数据结构变动,这些SQL需要改写 假如要开发一个博客系统,我们先设计两个Model和两张数据表 第一张数据表,表名是post,存储了博客文章,数据如下:

「七天自制PHP框架」第三天:PHP实现的设计模式

往期回顾:「七天自制PHP框架」第二天:模型与数据库,点击此处 原文地址:http://www.cnblogs.com/sweng/p/6624845.html,欢迎关注:编程老头 为什么要使用设计模式? 设计模式,我的理解是为了达到"可复用"这个目标,而设计的一套相互协作的类. 感兴趣的读者可以阅读<Design Patterns: Elements of Reusable Object-Oriented Software>,四位作者(Gang of Four)在书中列举

「C语言回顾之旅」第一篇:指针详解

说明: 最近学校课程开设了<数据结构>的课程,无疑,数据结构的重要性在IT生涯的进阶路上是不可置疑的,也常说,数据结构是专业性与非专业性的分界线.所以无论以后走的是什么方向,毕竟是读计算机专业的,所以必须学好数据结构的.虽然目前我给自己定的方向是走运维/系统架构方向的,可有句话说得好,不懂开发的运维注定会被淘汰,在IT这一行,要让自己变得更加强大.最近也一直在学Python,感觉还不错,学数据结构相信对自己也肯定有好处的,对一些较为底层的知识有些了解和理解,这样才能走得更远! 无疑C语言就很重

SAP 携合作伙伴建立「工业4.0开放联盟」

开放式生态系统推动工业制造工厂的数字化转型▲▲▲? 联盟支持开放式生态系统,采用「工业4.0开放联盟框架」实现互操作性 ? 开放.互操作的合作方法将为不同规模的企业提供发展良机 ? 工业 4.0 开放联盟旨在让智慧工厂内 80% 的机器都采用统一标准 在2019汉诺威工业博览会上,SAP 联合6家欧洲机械工程.工业自动化和软件行业的企业倍福 (Beckhoff).恩德斯豪斯(Endress+Hauser).赫优讯 (Hilscher).易福门(ifm).库卡 (KUKA)及莫迪维克(Multiv

谈谈「七个好习惯」

<高效能人士的七个习惯>The Seven Habits of Highly Effective People是美国管理大师史蒂芬·柯维的1989年的著作.风靡企业界,号称「世界500强企业必备培训课程」. 台达(之前工作的公司)的CEO日理万机,但每年还是会抽时间从台北飞到东莞给员工上这堂培训课,可见是这本书并不是普通的「鸡汤」. 对习惯2 -「以终为始」的曲折理解 最开始的印象是在台达厂房内的楼梯——有些楼层的楼梯,中间七级,每级用一块不锈钢板钉着一个「习惯」,这样在上楼梯的时候会不自觉看

「视频直播技术详解」系列之四:推流和传输

关于直播的技术文章不少,成体系的不多.我们将用七篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面.深入地了解视频直播技术,更好地技术选型. 在上一期中,我们介绍了讲解编码和封装. 本篇是<解密视频直播技术>系列之四:推流和传输.推流是直播的第一公里,直播的推流对这个直播链路影响非常大,如果推流的网络不稳定,无论我们如何做优化,观众的体验都会很糟糕.所以也是我们排查问题的第一步,如何系统地解决这类问题需要我们对相关理论有基础的认识. 本系列文章大纲如下: (一

【翻译】西川善司「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,前篇(1)

http://www.4gamer.net/games/216/G021678/20140703095/ 新连载「实验做出的游戏图形」,是聚焦在特定游戏的图形上, 对它的结构和使用的技术解说为主旨.之前笔者连载的「西川善司的3D游戏入迷」,覆盖范围都很广,而与特定游戏强关联的技术解说,会在今后的新连载中处理. 作为纪念的第一回选择的,是Arc System Works开发的,2014年2月在街机上运作的格斗游戏「GUILTY GEAR Xrd -SIGN-」 全3D图形的GUILTY GEAR

「Mobile Testing Summit China 2016」 中国移动互联网测试大会-议题征集

时至北京盛夏,一场由 TesterHome 主办的关于移动互联网测试技术的盛会正在紧锣密鼓的筹备中.只要你关注软件质量,热爱测试,期待学习,都欢迎你加入这次移动测试技术大会中和我们一起分享经验.探讨话题,结识业界朋友. 「Mobile Testing Summit China 2016」中国移动互联网测试大会 大会定位:专注移动互联网测试技术的分享会,关注移动互联网质量的有志之士的集会. 大会主旨:秉承着务实.能落地.有深度.高质量.重分享的原则与广大测试工程师做最新最实用的分享与交流,以推广新

自制MVC框架CRUD操作、列表、分页显示插件介绍

这里涉及到的操作都是引用自Stephen.DALService数据层.数据访问层实现方式在后文中我会仔细的说明,先说明一下数据操作集成的插件. 1).InsertAttribute 用于插入记录. 状态返回值:假定hashtable传递变量名的是context ,那么返回值可通过context[InsertAttribute.ValueKey]得到,推荐返回的是插入的主键ID值,当然这个是由数据层设定的. 有以下属性可进行设置: 属性名 作用 默认值 选项说明 其它说明 Key 映射路径.格式如