CSDN博客

img qiqiy

PHP 的Oop+模板编程初级示例与类库下载

发表于2008/10/1 17:18:00  401人阅读

前言:
对PHP,有很多人不赞成用OOP方式编程,反对使用模板者更众,我曾经也是极力排斥,但通过一段时间的尝试之后,发现OOP+模板虽然在速度上慢了可忽略不计的一点时间,编写起来确实爽,尤其表现在业务逻辑的处理与代码的维护性上。
费话少说,下面我就谈谈我的一点学习心得,希望对初学者有所帮助,也希望高手指正。

系统文件存储结构:
先列出树形列表,再解释。

├─
│ index.php

├─sources //子功能模块(业务逻辑)文件目录
│   index.php
│   list.php
│   details.php
.....
│ 
├─template //模板文件目录
│   index.tpl
.....
│ 
├─cache //缓存文件目录
│   gfdgr5123f4sfd9rwfdyt543fdsf12gf.html
.....
│ 
├─css //CSS 样式表文件目录
│   style.css

├─images //图片目录
│   1.gif
│   .....
│ 
├─include //包括目录
│ │ config.inc.php //系统全局配置文件
│ │ functions.inc.php //系统全局函数
│ │
│ ├─lib //系统基础类
│ │  Application.inc.php
│ │  .....
│ │
│ └─data //数据目录(IP文件)
│ ip.dat
│    .....

├─js //JS文件目录
│   checkForm.js
│   .....

├─admin //后台管理目录
│ │ index.php
│ │
│ ├─sources //子功能模块(业务逻辑)文件目录
│ │   index.php
│ │   list.php
│ │   update.php
│ │ .....
│ │ 
│ └─template //模板文件目录
│     index.tpl
│   .....

└─tmp //上传临时目录


主目录下的index.php,仅提供转向,转到sources目录下的index.php。同样admin下的index.php转向到admin/sources/index.php。你可以直接用header("location:sources/index.php"),也可以输出一段提示再转向,:正在进入,请稍候...<script>location.href="sources/index.php"</script>

为什么要这样做呢?
sources目录下的index.php是一个统一应用程序接口,通过接收$_GET或$_POST的action参数决定执行相应的子功能模块。例如index.php?action=list,即要执行list.php文件;index.php?action=insert即要执行insert.php文件。这样把应用程序接口index.php和其它子功能模块放在同一级目录,使得业务逻辑更方便;另外sources目录与模板文件目录template在同一级目录,使得模板文件被调用时,其中的图片等外部链接仍然有效,不会出现因目录级别不同找不到图片的现象。 
 
程序设计:
首先,我们有一个统一的应用程序接口sources/index.php。我们看一下index.php的内容:

[PHP]
<?
include (dirname(__FILE__)."/../include/lib/Application.inc.php");
$app = new Application();
$app->setRoot(dirname(__FILE__)) ;
$app->setDefaultAction("main");
$app->run();
?>

[/PHP]

上面的代码很简单:
第1行:载入应用程序接口类Application.inc.php。有很多朋友使用include和require时,在多级调用后经常出现找不到文件的问题,如果象上面那样:绝对路径+相对路径,包你永远正确。扯远了。。。^_^
第4行:$app->setRoot(dirname(__FILE__)) ;设置子功能模块根目录。由于index.php与子功能模块文件在同一目录,因此设为dirname(__FILE__)
第5行:$app->setDefaultAction("main");设置默认子功能模块,即没有$_GET['action']和$_POST['action']时执行main.php。
很重要的在第六行$app->run(),开始执行子功能模块。

下面就来看下Application.inc.php中run()是如何运作:
代码如下:

[PHP]
<?
function run()
{
$action = $this->getAction();
$page_class_file = $this->root . $action . '.php';
include_once($page_class_file);
$page = & new CPage($this);
$page->execute();
exit ();
}
?>
[/PHP]

首先我们通过getAction()得到参数action的值,
比如list,当前路径+list+.php就是将要执行的子模块
include_once($page_class_file); 载入list.php
很显然,在list.php中有一个类CPage ,并有一个方法函数execute()作为接口来处理数据并显示该页。
在初始化CPage类时,我们将$app对象用$this作为参数传给了CPage,那么在CPage中就可以使用$app对象中封装的所有方法。因此我们可以在$app中封装多一些的常用方法。比如,可以将数据库的初始化,模板的初始化封装进去。
完整代码如下:
Application.inc.php
[PHP]
<?php
if(!defined("__CLASS_APPLICATION__"))
{
define("__CLASS_APPLICATION__",1);

require(dirname(__FILE__)."/PhpClass.inc.php");


class Application extends PhpClass
{
var $db = null;
var $defaultAction ;
var $root;

//构造函数

function Application($root='')
{
if (!defined(ACTIONSTR))define("ACTIONSTR","action");
if (!empty($root))$this->setRoot($root);
}

//设置子功能模块所在的根目录

function setRoot($root='')
{
if (empty($root))$root = dirname($_SERVER['SCRIPT_FILENAME']);
if (!is_dir($root)) return $this->catchErr("无效的路径");
if ( substr($root,-1) != DIRECTORY_SEPARATOR) $root .= DIRECTORY_SEPARATOR;
$this->root = $root;
}

//设置默认要执行的功能页

function setDefaultAction($name)
{
$this->defaultAction = $name;
}

//执行子功能页

function run()
{
$action = $this->getAction();
$page_class_file = $this->root . $action . '.php';
include_once($page_class_file);
$page = & new CPage($this);
$page->execute();
exit ();
}

//获取参数action的值

function getAction()
{
$_name = (isset($_POST[ACTIONSTR])) ? $_POST[ACTIONSTR] : $_GET[ACTIONSTR];
if (empty($_name)) $_name = $this->defaultAction;
return $_name;
}

//模板

function tpl($templateDir='') {
$this->includeClass('Vant');
if (empty($templateDir)) $templateDir = $this->root . 'templates/'
$tpl = & new Vant($templateDir);
return $tpl;
}

//载入类文件

function includeClass ($fileName, $path = '', $lastName = '.inc.php')
{
if ( substr($path,0,1) != DIRECTORY_SEPARATOR) $path = DIRECTORY_SEPARATOR . $path;
if ( substr($path,0,-1) != DIRECTORY_SEPARATOR) $path .= DIRECTORY_SEPARATOR;
if (empty($fileName) ) return $this->catchErr("无效的class名");

include(dirname(__FILE__) . $path . $fileName . $lastName);
}

//数据库连接

function connectDb($host='',$username='',$password='',$database='')
{
$this->includeClass('DB');

$db = new DB($host,$username,password,database);
$db->connect();
$this->db = & $db;
return $db;
}

}
//end class


}
//end if defined


?>

[/PHP]
作者: lwg888 发布日期: 2004-7-02
上面的代码中有一个方法includeClass(),专门用来包含类与函数等文件,注意,请不要用它来包含配置文件、普通PHP文件等。
Application类还继承了PhpClass类。PhpClass类里面是些什么东些呢?在我所有的类库中,基本上每个类中都要用到一些相同的方法与属性,比如错处理catchErr()等,将它们封装起来,作为所有类的祖类。
PhpClass的完整代码如下:
PhpClass.inc.php
[PHP]
<?php

if(!defined("__CLASS_PHPCLASS__"))
{
define("__CLASS_PHPCLASS__",1);


class PhpClass{

var $debug = false;
//是否显示调式信息

var $debugMsg = '';
//记录调式信息

var $errMsg = '';
//记录第一次的错误信息

var $error_report = true;
//是否输出错误信息

var $error_halt = true;
//出错后是否停止执行


//为成员变量赋值

function setVal($name,$value)
{
$this->$name = $value;
}

//取得成员变量的值

function getVal($name)
{
return $this->$name;
}

//捕获错误

function catchErr($msg)
{
if(empty($this->errMsg)) $this->errMsg = $msg;

if ($this->error_report) echo $this->errMsg;

if ($this->error_halt) exit;

return false;
}

//输出错误

function err($exit = false)
{
if (!empty($this->errMsg))
{
echo $this->errMsg;
if ($exit) exit;
}
}

}
// end class


}
// end if defined

?>
[/PHP]

下面我们回到list.php中去,看子功能模块如何运作
从前面的介绍我们知道了list.php,必有一个类CPage且其中必定有两个函数:
一个构造函数CPage(),一个execute()
通过构造函数的参数将$app对象接收到,那么简单代码如下:

[PHP]
list.php
<?php
class CPage
{
function CPage(& $app)
//$app就是Application对象的实例

{
}

function execute()
{
}
}
?>
[/PHP]

由于很多子功能模块可能要完成一些共同的功能,如显示logo,copyright等,我们可以将这样共同的功能封装在一个类中,命名为Page类,作为所有子功能模块的父类,所有子功能模块都从它继承。
看一下Page.inc.php:

[PHP]
<?
class Page
{
var $app;
var $vars=array();

function Page(&$app)
{
$this->app = & $app;
}
function setCopyRight()
{
$this->vars['copyright'] = "CopyRightwg888";
}
}
?>
[/PHP]

list.php中的CPage类就从Page类继承
代码示例:

[PHP]
<?php
include("Page.inc.php");

class CPage extends Page
{
var $pt;
var $app;

function CPage(& $app)
{
$this->age($app);
}

function execute()
{
$db = $this->app->connectDb();
//利用Application对象中的数据库连接方法

$sql= "select * from table ...";
$this->app->includeClass ('PageTurn');
//载入PageTurn.inc.php翻页类文件

$pt = & new PageTurn(20);
//初始化翻页类,每页显示20条

$pt->query($sql,$db->conn);
//执行SQL语句

$this->pt = & $pt;
$this->show();
//显示网页

}

function show()
{
$tpl=$this->app->tpl();
//建立模板对象

$tpl->set("loop",$this->pt->field);
$tpl->set("copyright",$this->vars['copyright']);
//Page类中的 $vars['copyright']

echo $tpl->parse('list.tpl');
//显示模板

}
}
?>

[/PHP]

上面用到了分页类PageTurn与模板类Vant,大家可以参考下载中的相关类

讲了这么多,我们已经基本上弄清了用OOP+模板方式写程式的方法,下面就来总结它的好与歹。
如果你不喜欢用OOP方式编程,你可以说:类=累。我同意,但不以OOP方式编程,运行速度是会快一些,而你会变的很累,不管是业务逻辑的组织还是今后代码的维护,都不是一件轻松的事。在机器性能日益提高和网络宽频日益普及的今天,速度不是单方面追求的目标,我们要在速度与代码易用性、易编写性之间取得平衡。

从上面的讲解我们可以看出:完全实现了Model模型/逻辑、View视图/界面、Controller控制/流程 的分离。使得结构非常清晰,维护非常方便,模型设计者,界面设计者与流程控制设计者能更好的分工与合作。代码可重用,今后如果要开发一个新的项目,很快就能完成,因为你可能只需叫美工重新设计一下模板,或者稍稍修改一下子功能模块。

还有一点不得不提:
index.php是一个统一应用程序接口,通过$_GRT['action']或$_POST['action'],决定执行哪个子功能模块。当它执行该模块时是将它include进来,因此所有的操作都是在index.php中进行,所呈现的网页都是index.php?action=...,这样使我们控制流程非常方便。比如要显示用户列表,只需调用list.tpl模板,要显示错误信息,只需调用errmsg.tpl模板,甚至不必要使用header("location:..")来转向,不管是显示用户列表还是显示错误信息,所有的$_GRT或$_POST或其它变量都还在,不用象转向时那样用?xx=...传一大堆变量过去,不是有不少人常问“数组能不能传递?”“对象能不能传递?”,

写到这里,一点劣见,望大家指正 。

0 0

相关博文

我的热门文章

img
取 消
img