怎么一步步编写简单的PHP的Framework(十)

      以前讲了这么多,实际上都只是为框架搭建了一个架子而已,框架里面尚未什么东西, 从今天开始,我就开始从Controller,Model,View这几块儿来分别介绍一下。 php

       PS:以前的不少内容我都没有细讲,就好比路由,真正的框架路由确定不是经过GET方式指定的,而是正则,而且它须要兼容多个Server,多种方式。 数组

      咱们知道全部的请求都须要通过控制器,因此首先仍是说一下控制器。 服务器

       咱们以前已经 说过控制器的概念了,可是这个控制器仍是功能太弱了,由于它只是从功能上是控制器,框架并无为它作任何事情,框架实际上能够实现一些经常使用的功能,而后用户定义的控制器继承它,这样用户能够少写不少代码的。 框架

       以前的控制器是: 函数


<?php
class IndexController {
	public function index() {
		echo 'Hello world';
	}
}
       如今假设框架已经实现了控制器的一些基本功能,这个类咱们称为Controller.php,那么如今代码就变成了:



<?php
class IndexController extends Controller {
	public function index() {
		echo 'Hello world';
	}
}
         这样作有什么好处呢,因为Controller继承了Base,因此IndexController也拥有了Base的功能,这样就不须要用户再编写不少捕获的代码等。


          固然,这样作也有缺点,测试IndexController的时候比较麻烦。 测试

          今天我先说一下控制器比较基础的两个功能,跳转和转发。 ui

           首先是跳转,这个用的太广泛了,好比用户登陆的时候,点击登陆按钮,进入后台处理的页面,处理完毕以后就须要跳转,那么怎么实现跳转呢? this

           有几种方式: url

           第一种:<script>location.href = "XXXX";</script>在JS中实现跳转; code

           第二种:header("Location:url");具体使用可查看PHP手册;

           第三种:在HTML的meta中设置refresh;

           因为header调用的时候若是以前页面已经有输出,跳转会失效,因此须要结合meta的refresh一块儿使用,固然,你也能够直接使用JS的这种方式来实现,只是我不太喜欢这种方式,由于我以前使用这种方式实现跳转的时候出过问题。

           如今咱们定义跳转这个函数的名字为_redirect,为何前面加上_呢,这也是个人一个习惯,对于函数,只要不是public,我都使用_做为前缀。可能你们会问了,为何不设置这个函数为public呢,由于用户编写的控制器也只会继承它而不会直接调用,因此我设置成为了protected。

              

protected function _redirect(Array $arr) {


}


         暂时能够将功能弄得弱一点,假设跳转的参数经过数组传递过来,那么咱们可使用类型提示(Array $arr)这种方式来搞定,若是传递的参数不是数组,那么直接会报错。

         咱们使用的方式能够是这样:


$this->_redirect(array(
        'action' => 'test',
        'controller' => 'Test',
        'param1' => '1'
));
           它表明的意思是跳转到Test这个控制器的test这个Action,而且还传递了一个参数,这个参数名为param1,值为1。



protected function _redirect(Array $arr) {
		$controller = empty($_GET['c']) ? C('defaultController') : trim($_GET['c']); //设置了默认的控制器
		$action = empty($_GET['a']) ? C('defaultAction') : trim($_GET['a']); //设置了默认的Action
		array_key_exists('controller',$arr) || $arr['controller'] = $controller;
		array_key_exists('action',$arr) || $arr['action'] = $action;
		$str = '/?';
		foreach($arr as $key => $val) {
			if(!is_int($key)) {
				$str .= ($key . '=' . $val . '&');
			}
		}
		$str = substr($str,0,strlen($str) - 1);
		Response::redirect($str);
	}


            这个就是我刚刚手写的跳转代码,实际上就是把传递的数组拼接一下而后组成一个字符串,这个字符串就能够当作是一个URL,因为如今没有对Route.php进行更多的处理,对于localhost/demo2/index.php?controller=a这种URL它跳转就会出错,暂时只支持localhost/index.php?controller=a这种URL,还有$controller和$action的获取和Route.php中的代码重复了,这些都须要在后面真正实现路由的时候再讲解,暂时就这么看看吧,虽然我本身都感受这样的代码很恶心。

            可能你们都注意到了,当这个函数拼接到URL以后,是直接调用了Response的redirect方法,这是为何呢?

             第一:有可能在真正应用中,咱们直接在控制器中调用$this->_redirect知足不了咱们的需求,这个时候咱们就须要直接调用Response::redirect,好比跳转到百度首页就只能调用Response::redirect("http://www.baidu.com");

             第二:从逻辑上,跳转是一个服务器对客户端的响应,因此须要写在Response中,具体的可参照Java。

             那么咱们又必须新建一个Response.php这样一个文件:


<?php
class Response extends Base {
	public static function redirect($url) {
		if(is_string($url)) {
			if(!headers_sent()) {
				header("Location:" . $url);
				exit();
			} else {
				$str = '<meta http-equiv="Refresh" contect="0;url='.$url.'">';
				exit($str);
			}
		} else {
			//错误处理
		}
	}
}
             这里的逻辑比较简单,实际上就是断定是否有输出,没有输出那么就直接使用header("Location")进行跳转,若是有输出,那么使用meta的refresh跳转。


             注意:实际上还能够在这个跳转上开发更多的功能,可是因为我只是大概讲一下,因此更多的内容就不写了,有兴趣的人能够去Toper上面看看。

             这样,一个比较简单的跳转就完成了,那么怎么实现转发呢?

              能够简单这样理解,转发实际上就是再调用了一下某一个Controller的某一个Action。

               因此这样咱们就能够比较简单的实现转发了:


protected function _forward(Array $arr) {
		$controller = empty($_GET['c']) ? C('defaultController') : trim($_GET['c']); //设置了默认的控制器
		$action = empty($_GET['a']) ? C('defaultAction') : trim($_GET['a']); //设置了默认的Action
		if(array_key_exists('controller',$arr)) {
			$controller = $arr['controller'];
		}
		if(array_key_exists('action',$arr)) {
			$action = $arr['action'];
		}
		$controller .= 'Controller';
		if($controller === get_class()) {
			if(method_exists($this,$action)) {
				$this->$action();
			} else {
				//时间有限,不写逻辑了
			}
		} else {
			if(class_exists($controller)) {
				$class = new $controller();
				if(method_exists($class,$action)) {
					$class->$action();
				} else {
					//时间有限,不写了
				}
			} else {
				//时间有限,不写了
			}
		}
	}
                实际上逻辑上就是断定一下要调用的Action是否属于本控制器,若是是本控制器,直接调用$this->$action()便可,不然,须要实例化这个控制器,即$class = new $controller(),而后再调用这个Action。


                原本觉得半个小时就能够写完,结果写了一个小时了,因为时间超出个人预算,因此代码都是手写的,不知道有不有什么语法错误什么的,反正看看思路就OK了。

                代码点此下载

相关文章
相关标签/搜索