Bookmark and Share

Lee's 程序人生

HTML CSS Javascript XML AJAX ATLAS C# C++ 数据结构 软件工程 设计模式 asp.net Java 数字图象处理 Sql 数据库
  博客园  :: 首页  :: 新随笔  :: 联系 :: 管理

基于MVC模型开发属于自己的个人博客

Posted on 2010-02-05 18:56  analyzer  阅读(565)  评论(0编辑  收藏  举报

0.前言


本文会从搭建WAMP环境开始,描述如何开发自己的个人博客。虽然目前Wordpress已经非常完善、功能非常强大,但对于喜欢简洁的朋友来说似乎有点臃肿。对于刚刚看完PHP语法,苦于没有项目实践的朋友,本文也是一个非常好的台阶。

目前PHP在Web开发方面已经非常流行。可是还有一部分开发人员对PHP的应用还是停留在ASP的阶段。直接将PHP代码和HTML代码混合在同一个文件中。这对于网站的后期维护是非常苦难的,甚至2、3个月后自己写的代码就不认得了。目前PHP有许多框架可以解决以上的问题,例如轻量级的ThinkPHP等、重量级的Zend Framework。对于一些小型的项目,框架可能显得有点“牛刀”。这时,我们就需要自己实现一个简单的MVC框架。这样即可简化后期代码的维护,也可避免框架的学习成本。对于你经常使用的东西,还可以抽象出来,做一个属于自己的框架。这也是本文编写的一个目的。

另:本文会介绍如何搭建开发环境,但每位读者的机器环境不尽相同。只能起指导的作用,具体的问题还是需要google。


1.搭建开发环境

1.1.Apache的安装

Apache的安装比较简单,可以直接从官方网站下载安装。安装过程比较简单,一路按Next就可以了(请根据自己的情况调整安装路径,下文假设Apache安装在D:\prg\Apache Software Foundation\Apache2.2)。

1.2.MySQL的安装

MySQL 同样提供msi的安装包,目前GA的版本是5.1.42,同样可以在mysql官网上找到。安装的过程同样也是一路按Next即可,基本没什么大问题,下文假设MySQL安装在D:\prg\MySQL\MySQL Server 5.0。

1.3.数据库管理工具

在其他文章中会推荐适用phpMyAdmin作为MySQL的管理工具。但就我个人而已,不太喜欢Web方式的数据库管理工具。都已经是本机了,为什么不用C/S的工具呢?我觉得有一个工具比较好用的,叫Navicat MySQL。虽然是一个商业工具,但对个人有免费版,一般功能都有了。安装过程也是比较简单,不作详细叙述。

1.4.PHP5配置

PHP和Apache的配置方式有很多种,我以前使用的方式需要往system32文件夹扔一些文件。这种方法非常不好,再重装以后都需要重新配置,很麻烦。这里介绍一个环境变量的配置方法。

解压PHP,本文假设PHP5解压在D:\prg\PHP5中。

设置环境变量:右击我的电脑->属性->高级->环境变量,找到一个名为Path的环境变量。在最后添加PHP5的路径,以分号(;)分割每个路径。

设置Apache的httpd.conf文件:打开Apache目录下conf\httpd.conf文件:

找到最后一行LoadModule,加入一下配置。

## Add php module handle
LoadModule php5_module "D:/prg/PHP5/php5apache2_2.dll"
AddType application/x-httpd-php .php
# configure the path to php.ini
PHPIniDir "D:/prg/PHP5"

保存,并重启Apache服务器。注意,路径要跟自己安装路径相对应。PHPIniDir是指配置文件的路径。

从PHP5目录中复制php.ini-recommended到配置文件的路径,并改名为php.ini。打开php.ini,找到extension_dir,将其设置成你ext文件夹所在的路径(ext文件夹在PHP目录内)。找到php_gd2.dll、php_mysql.dll、php_mysqli.dll,去掉前面的注释,重启Apache服务器。如果不出意外,Apache服务器正常启动。写一php文件,名为test.php,内容如下:

<?php phpinfo(); ?>

放到Apache目录下的htdocs。

2.MVC模型简介

模型(Model)代表应用程序的数据(data)和用于控制访问和修改这些数据的业务规则(business rule)。通常模型被用来作为对现实世界中一个处理过程的软件近似。

视图(View)被用来组织模型的内容,它从模型那里获得数据并指定这些数据如何表现。

控制器(Controller)定义了应用程序的行为;它负责对来自视图的用户要求进行解释,并把这些要求映射成相应的行为,这些行为由模型负责实现。

示意图如下:

 

以上介绍的内容来自J2EE的文档。MVC模型最初是来自SmallTalk社区,后来被引入到Java社区。传统的MVC模型是用于桌面程序的开发。上图说明了,与用户接触的只有视图。用户对视图的操作传递到控制器,控制器经过一系列的业务计算,将数据传到底层的模型层。模型层接受数据后,通知视图更新,这过程是异步的。这里有一个大学时代开发的备忘录,是基于MVC模型开发的,可以点这里下载。显然,对于Web来说,传统的MVC并不太合适,我们无法主动通知视图(HTML,浏览器)进行更新。经过稍作调整后,Web的MVC应该是如下:

3.实现一个控制器:

对于PHP的MVC来说,最重要的是一个控制器。实现一个好的控制器,对于程序扩展有非常好的作用。下面是一个控制器的抽象:

01//define section
02abstract class BaseController
03{
04    /*
05        入口名,仅用于记录该控制器由哪个入口进入
06        @var string
07    */
08    protected $mEntrance;
09    /*
10        记录控制器开始时间
11        @var integer
12    */
13    protected $mStartTime;
14    /*
15        记录控制器结束时间
16        @var integer
17    */
18    protected $mEndTime;
19    /*
20        控制器对应的视图
21        @var object
22    */
23    protected $mrView;
24    /*
25        数据库操作对象
26        @var object
27    */
28    protected $mrDbo;
29    /*
30        控制器的动作
31        @var string
32    */
33    protected $mAction;
34    /*
35        控制器的工作模式
36        @var string
37    */
38    protected $mMode;
39    /*
40        页面变量
41        @var array
42    */
43    protected $mPageVar;
44    /*
45        函数接口
46    */
47    abstract public function Start();
48    abstract public function doAction();
49    abstract public function End();
50}

控制器最重要是三个函数:Start、doAction、End。Start是用作公共部分的处理,例如字符编码、输出的文件格式、Zip压缩等;doAction是根据传递过来action做相应的动作,例如穿过来的action是index,则显示首页。End()则是作一些请求收尾工作和输出处理的内容。为什么要分开这三部分呢?除了逻辑上清晰以外,还可以方便我们以后实现插件机制。如:我们可以再Start之前做一个钩子:before_start,插件注册一个函数到这个钩子,就可以做一些初始化的工作。以下是博客首页控制器的部分代码:

01//define section
02class IndexController extends BaseController
03{
04    /*
05        构造函数
06    */
07    public function __construct()
08    {
09        $this->mEntrance = 'front';
10         
11        $this->mrDbo = &new DBO();
12        $this->mrDbo->Connect();
13         
14        $this->mrView = &new SmartTemplate();
15        $this->mrView->template_dir = (realpath(rootpath.'/tpl/Frontpage/'.blog_template_name.'/'));
16        $this->mrView->temp_dir = realpath(rootpath.'/temp/');
17        $this->mrVIew->cache_dir = realpath(rootpath.'/cache/smarttemplate').'/';
18         
19        $this->mAction = isset($_GET['action']) ? $_GET['action'] : 'index';
20        $this->mMode = isset($_GET['mode']) ? $_GET['mode'] : 'normal';
21         
22        $this->mPageVar['param']['action'] = $this->mAction;
23        $this->mPageVar['param']['mode'] = $this->mMode;
24         
25        $this->mPageVar['param']['param1'] =  isset($_GET['param1']) ? $_GET['param1'] : null;
26        $this->mPageVar['param']['param2'] =  isset($_GET['param2']) ? $_GET['param2'] : null;
27        //TODO:这里添加一个动态的菜单,让插件能修改首页的菜单项
28    }
29    /*
30        控制器开始函数
31    */
32    public function Start()
33    {
34        $this->mStartTime = microtime(true);
35        session_start();
36        //启动缓冲区压缩
37        if (system_gzip) {
38            ob_start("ob_gzhandler");
39        }
40        else {
41            ob_start();//关闭缓冲区压缩
42        }
43        //错误报告
44        if (system_debug) {//如果处于调试模式,则报告所有的错误
45            error_reporting(E_ALL);
46        }
47        else {//只显示严重的错误
48            error_reporting(E_ERROR | E_WARNING | E_PARSE);
49        }
50        date_default_timezone_set(system_timezone);//设置系统默认时区
51        header('content-Type: text/html; charset=UTF-8');//UTF-8编码
52    }
53    /*
54        控制器主体函数
55    */
56    public function doAction()
57    {
58        switch($this->mAction)
59        {
60            case 'index'://首页和日志暂时相等
61            case 'blog':
62                $this->doBlog();
63                break;
64            case 'category':
65                $this->doCategory();
66                break;
67            case 'ccommit':
68                $this->doCommentCommit();
69                break;
70            case 'photo':
71                $this->doPhoto();
72                break;
73            case 'resume':
74                $this->doResume();
75                break;
76            default:
77                $this->Message("错误:未实现的action");
78                break;
79        }
80    }
81    /*
82        控制器结束函数
83    */
84    public function End()
85    {
86        $this->mEndTime = microtime(true);
87        $run_time $this->mEndTime - $this->mStartTime;
88        if(system_debug) {
89            $this->mrView->assign('run_time',$run_time);
90            $this->mrView->assign('query_times',$this->mrDbo->GetQueryCount());
91        }
92        if(null != $this->mrView->get_templatefile()) {
93            $this->mrView->output();
94        }
95        ob_flush();
96    }

希望从上面的代码,大家能够更加明白这个控制的工作方式。在这里还想说一下,什么是单入口点。所谓单入口,就是所有程序只能从一个文件中访问。通过不同GET参数实现访问不同页面。这样做的好处是可以限制用户的访问,保证系统的安全。具体实现的方式如下:

1.定义一个入口文件(这里命名为index.php),编写一下代码,定义一个常量:define('rootpath',dirname(__FILE__));//indicate the programme path,this would be use in other php file.

2.再其他的PHP文件中做一下判断:

1if(!defined('rootpath')) {//can't direct access this file
2    die("Access forbidden.");
3}

这样就可以限制代码用户对其他页面的访问。下面是一个具体的入口点实现方式,大家可以参照一下:

01//define section
02define('rootpath',dirname(__FILE__));//indicate the programme path,this would be use in other php file.
03//var initialize section
04$entrance    = isset($_GET['go']) ? $_GET['go'] : 'front';//get entrance,if can't get any entrance default is index page
05$controller    = null;
06 
07//将请求传送到相应控制器
08//若控制器较多,调度比较复杂,应该实现一个调度器dispatcher
09//TODO:实现一个调度器
10 
11switch($entrance)
12{
13    case 'front'://if is a front page,create IndexController
14        include_once rootpath.'/includes/core/controller/IndexController.php';
15        $controller new IndexController();
16        break;
17    case 'admin'://admin,create AdminController
18        include_once rootpath.'/includes/core/controller/AdminController.php';
19        $controller new AdminController();
20        break;
21    case 'image'://course image use different header,so we need a new controller
22        include_once rootpath.'/includes/core/controller/ImageController.php';
23        $controller new ImageController();
24        break;
25    default://not implements controller,print error message
26        die("未实现的控制器,请求页面失败");
27        break;
28}
29 
30//start page handling
31$controller->Start();
32$controller->doAction();
33$controller->End();
34//EOF

如果你还需要URL路由器等高级功能,也可以在这个文件进行。再次体现的程序的扩展性。

4.视图层

传统的Web程序,视图层都是采用HTML(这里不讲述AJAX等方式的视图)。HTML的渲染,我们可以采用模板引擎。PHP的模板引擎非常的多,著名的有Smarty,更有直接用PHP 作为模板引擎的(例如:国内流行的SaBlog-X)速度的确很快,不过要以复杂性的提高作为代价。我在这里采用的是smarttemplate(目前好像已经改名为QuickSkin)。Smarttemplate的速度非常快,采用缓存编译的方式。一次编译后,结果会被缓存。缓存后的速度和直接适用PHP作为模板引擎的速度相当,是两者折中的一个方案。

具体的使用方法,请参考官网的文档。里面有一些例子,可以让你快速入门。

我要啦免费统计