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

咱们为何要使用路由?

缘由1:一个更漂亮的URI

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

缘由2:单一入口机制的易维护性

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

缘由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:使用控制器

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

相关文章
相关标签/搜索