一步步编写PHP的Framework(十)

 

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

PS:之前的很多内容我都没有细讲,就比如路由,真正的框架路由肯定不是通过GET方式指定的,而是正则,并且它需要兼容多个Server,多种方式。

我们知道所有的请求都需要经过控制器,所以首先还是说一下控制器。

我们之前已经 说过控制器的概念了,但是这个控制器还是功能太弱了,因为它只是从功能上是控制器,框架并没有为它做任何事情,框架实际上可以实现一些常用的功能,然后用户定义的控制器继承它,这样用户可以少写很多代码的。

之前的控制器是:

1 <?php
2 class IndexController {
3     public function index() {
4         echo 'Hello world';
5     }
6 }

现在假设框架已经实现了控制器的一些基本功能,这个类我们称为Controller.php,那么现在代码就变成了:

1 <?php
2 class IndexController extends Controller {
3     public function index() {
4         echo 'Hello world';
5     }
6 }

这样做有什么好处呢,由于Controller继承了Base,所以IndexController也拥有了Base的功能,这样就不需要用户再编写很多捕获的代码等。

 

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

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

首先是跳转,这个用的太普遍了,比如用户登录的时候,点击登录按钮,进入后台处理的页面,处理完毕之后就需要跳转,那么怎么实现跳转呢?

有几种方式:

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

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

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

由于header调用的时候如果之前页面已经有输出,跳转会失效,所以需要结合meta的refresh一起使用,当然,你也可以直接使用JS的这种方式来实现,只是我不太喜欢这种方式,因为我之前使用这种方式实现跳转的时候出过问题。

现在我们定义跳转这个函数的名字为_redirect,为什么前面加上_呢,这也是我的一个习惯,对于函数,只要不是public,我都使用_作为前缀。可能大家会问了,为什么不设置这个函数为public呢,因为用户编写的控制器也只会继承它而不会直接调用,所以我设置成为了protected。      

1 protected function _redirect(Array $arr) {
2  
3  
4 }

 

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

我们使用的方式可以是这样:

1 $this->_redirect(array(
2         'action' => 'test',
3         'controller' => 'Test',
4         'param1' => '1'
5 ));

 

它代表的意思是跳转到Test这个控制器的test这个Action,并且还传递了一个参数,这个参数名为param1,值为1。

01 protected function _redirect(Array $arr) {
02         $controller= empty($_GET['c']) ? C('defaultController') : trim($_GET['c']); //设置了默认的控制器
03         $action= empty($_GET['a']) ? C('defaultAction') : trim($_GET['a']); //设置了默认的Action
04         array_key_exists('controller',$arr)  $arr['controller'] = $controller;
05         array_key_exists('action',$arr)  $arr['action'] = $action;
06         $str = '/?';
07         foreach($arr as $key => $val) {
08             if(!is_int($key)) {
09                 $str .= ($key . '=' . $val . '&');
10             }
11         }
12         $str = substr($str,0,strlen($str) - 1);
13         Response::redirect($str);
14     }

 

这个就是我刚刚手写的跳转代码,实际上就是把传递的数组拼接一下然后组成一个字符串,这个字符串就可以看成是一个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这样一个文件:

01 <?php
02 class Response extends Base {
03     public static function redirect($url) {
04         if(is_string($url)) {
05             if(!headers_sent()) {
06                 header("Location:" . $url);
07                 exit();
08             } else {
09                 $str = '<meta http-equiv="Refresh" contect="0;url='.$url.'">';
10                 exit($str);
11             }
12         } else {
13             //错误处理
14         }
15     }
16 }

这里的逻辑比较简单,实际上就是判定是否有输出,没有输出那么就直接使用header("Location")进行跳转,如果有输出,那么使用meta的refresh跳转。

 

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

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

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

所以这样我们就可以比较简单的实现转发了:

01 protected function _forward(Array $arr) {
02         $controller= empty($_GET['c']) ? C('defaultController') : trim($_GET['c']); //设置了默认的控制器
03         $action= empty($_GET['a']) ? C('defaultAction') : trim($_GET['a']); //设置了默认的Action
04         if(array_key_exists('controller',$arr)) {
05             $controller = $arr['controller'];
06         }
07         if(array_key_exists('action',$arr)) {
08             $action = $arr['action'];
09         }
10         $controller .= 'Controller';
11         if($controller === get_class()) {
12             if(method_exists($this,$action)) {
13                 $this->$action();
14             } else {
15                 //时间有限,不写逻辑了
16             }
17         } else {
18             if(class_exists($controller)) {
19                 $class = new $controller();
20                 if(method_exists($class,$action)) {
21                     $class->$action();
22                 } else {
23                     //时间有限,不写了
24                 }
25             } else {
26                 //时间有限,不写了
27             }
28         }
29     }

实际上逻辑上就是判定一下要调用的Action是否属于本控制器,如果是本控制器,直接调用$this->$action()即可,否则,需要实例化这个控制器,即$class = new $controller(),然后再调用这个Action。

本来以为半个小时就可以写完,结果写了一个小时了,由于时间超出我的预算,所以代码都是手写的,不知道有不有什么语法错误什么的,反正看看思路就OK了。

时间: 2024-10-14 16:26:54

一步步编写PHP的Framework(十)的相关文章

一步步编写PHP的Framework(十四)

   今天我说一下怎么在框架中over掉这些安全问题.       首先是SQL注入,这个如果你使用的是PDO,我觉得应该没什么问题,如果你使用的还是mysql_*等API,那么你可以在框架中实现bindParameter或者在插入数据库之前进行字符串转义.       前两天把上一篇文章写完之后,Vian在后面留言说到过SQL注入的一个解决方案,就是在在插入DB之前进行'''.addslashes($id).''',它的意思就是首先进行addslashes操作,之后再强制单引号包裹,这样它就是

一步步编写PHP的Framework(十五)

  从今天开始我们开始介绍模型,模型是一个框架中非常重要的一块儿,控制器实际上不能进行数据的处理,这种处理的过程我们全部放在模型这一块儿来做.         如果是Java,即使是没用框架,它的模型这一块儿也挺复杂的,首先编写service接口,然后写实现,然后定义DAO接口,DAO实现,然后定义这几层之间传输数据的Domain,一般我认为它就是一个POJO.         Java这种契约式的编程方式很不错,即使后面的实现类发生改变,接口没有改变,代码的修改的量也不是很多.        

一步步编写PHP的Framework(十六)

  我把模型分成了几块儿,A,B,C,D,E,F,这几块儿分别代表了什么呢?       首先A,这个Base代表的就是整个框架的基类,所有的模型文件都必须继承自这个基类:       由于B的内容需要依赖D,所以先说一下D,D首先有一个ConnectionManager,它负责管理数据库的连接和关闭还有数据库驱动的选择,这个命名自己感觉不太好,先就将就吧,既然它负责数据库的连接和关闭,肯定它就有以下方法: 1 <?php 2 public class ConnectionManager { 3

一步步编写PHP的Framework(十二)

          我上次在讲redirect和forward的时候我就说过,这两个函数要正常使用还需要修改一下Route这个类,至少要将比如域名,控制器名,Action名等存储起来,后面调用redirect,forward的时候可以使用.         现在我们就转到Route.php,原来这个类的代码很简单: 01 <?php 02 class Route extends Base { 03    public static function run() { 04       $contr

一步步编写PHP的Framework(十八)

  上一次我们把模型的驱动类实现了,由于ConnectionManager需要按照配置文件中配置的驱动名实例化驱动类,所以我们先在配置文件中加入驱动名: 01 <?php 02 return array( 03     'defaultController' => 'Index', 04     'defaultAction' => 'index', 05     'debug' => true, 06     'errorReporting' => -1, 07     '

一步步编写PHP的Framework(十三)

  上次讲到控制器怎么样将数据传递到视图,今天我就主要说一下在程序中怎么让代码更"安全",之后就转到讲模型,再讲怎么做视图,最后再讲控制器的功能强化.       我再声明一下,我写这个文章只是让大家对PHP的框架编写有一个基本的了解,由于本人技术有限,这个文章是给PHP初学者学习的,所以高手勿喷,还有就是我现在时间也有限,所以每次可能需要两三天才能写一篇,每篇我写的时间也要控制在一个小时以内,由于边写这个文章边编代码,所以代码中可能会存在很多BUG,见谅!!        如果你是一

一步步编写PHP的Framework(一)

        框架这个概念可能一开始就吓坏了很多PHP的Coder,其实你只要把一个Framework想明白了,其实也没啥,只是别人功能更复杂,细节考虑的更多,代码质量更高.        框架最重要的一个文件为入口文件,基本上相当于一个总控开关,所有的请求都需要经过这个文件: 1 <?php 2 define('APP_PATH',dirname(__FILE__).'/..'); //应用路径 3 include APP_PATH.'/Library/Toper/Core/FrontCon

一步步编写PHP的Framework(八)

  既然前端控制器控制一切,那么我们可以用它来做更多的事情!! 我们知道,在windows里面默认php.ini中display_errors默认开启,而在linux中默认是关闭的,那么这样对于程序的调试时很麻烦的,所以我们可以在配置文件中设置一个debug属性,它代表是否调试打开,如果打开调试,程序出现任何异常,那么会输出很多调试信息.在这儿,我只是很简单的设置一下,真正的框架调试功能肯定不是这样简单的. 先我们实现如果debug开启,那么显示调试信息debug mode:这样一个字符串,并且

一步步编写PHP的Framework(九)

         之前讲到过对于异常的处理机制,最好可以自定义异常,如果debug打开,那么显示具体的调试信息,如果debug关闭,那么将异常记录到日志,然后跳转到错误页,这节课我就主要来讲一下这个异常处理机制.        现在我们定义一个异常的基类,在Toper中为Tp_Exception,在这儿,我就直接叫BaseException吧,那BaseException.php肯定首先是有一个架子的: 1 <?php 2 class BaseException extends Exceptio