发信人: jackyz()
整理人: dalasthunder(2002-07-25 06:31:28), 站内信件
|
面向对象的PHP
---------------------------------------------------------------------
作者:Mark Williams
译者:limodou
概述
OK,你可能已经听说过面向对象,你认为你已经满脑子都是它了。你所交谈 过的每一个人都说面向对象是开发者的真正出路。你应该怎么去做?对你来说它 里面有些什么?并且用PHP到底能做些什么?
广泛地说,所有上面所说的都是真的,但是我猜如果你对OO(面向对象)不熟 悉,你想要看一个具体的例子----一个理想的什么东西,而且这个例子可以应用 在你自已的页面上。好吧,让我们开始吧。
让我们先看一下我们真正想要处理的。[图1]
基本上,我们有三个信息栏 (F1 Teams(F1车队), Drivers Championship(赛 手冠军) 和 Constructors
Championship(车队冠军)).
F1 Teams
相当简单,这是一个信息栏,其中有一个无序的列表。每一项都是到车队网 站的链接。
Drivers Championship(赛手冠军)
这是一个信息栏,显示了在F1赛手优胜记录中当前的点数。在这个信息栏中 没有链接。
Constructors Championship(车队冠军)
象赛手优胜记录栏一样,这是一个信息栏,显示了车队优生记录中当前的点 数。
现在,在你发言之前,我知道你可能不想知道现在F1冠军赛的细节,但是它 的确适合作为一个演示用例。在每天的最后,数据被简单地从数据库中取出(在 这个例子中我使用MySQL ),所以你可以使用你喜欢的任何数据。要点就是这些 栏是"伟大的"门户类型工具,用来显示大量的集中的数据。不管你是不是它们的 爱好者,它们就是这样工作的。
如何创建他们?
我的脑子里有好几种可以创建它们的方法:
将大量的代码放在一个web页面中。如果你只想让信息框显示在一个页面中,这个 方法没什么错,但是一旦你开始拆分到交叉页面时,其上的维护工作就变得乏味 了。 将信息框建成SSI(Server Side Include,服务器方包含)文件。这样做仍然 没有什么问题;那么你可以在任意数量的页面中包括信息框,通过引用包含文件 。 将信息框建成对象。开始时,需要多花点时问去建立,但是它有利于生成可移 值代码(我所说的是代码从站点到站点,不只是页面到页面!)。另外,我们创 建了数据源和信息框布局两个接口,这就意味着我们拥有了对数据和布局更简单 地控制。
你可能已经猜到,我们要使用OO方法。其它的要点是什么呢?
那么,让我们从头开始
建立需求。根据这个练习的目的,我定义需求如下:
*.数据源必须可变及可控的。
*.布局大小必须可变及可控的。
*.颜色必须是可变及可定制的。
*.字体外观必须是可控的。
有很多的方法可以用来满足这些要求,对于后面的两个,使用CSS 可能更容 易,这就是我们要做的。对于前两个,这就是故事的开始:
为了建立这个信息框我们将使用七个独立的文件:
index.phtml
mysqldb.obj
infobox.obj
linkbox.obj
resultbox.obj
constants.inc
main.css
标准
所有的.obj文件都是类定义。对每个类或子类我都使用了一个文件。.phtml 文件是我们将插入信息框的文件。.inc文件是一个一般的SSI文件,并且.css文件 是一个真正的样式表文件。
开发过程
象通常的作法,我们首先定义我们的全局常量,然后我们将转移到我们的数 据模式上面(和数据库),跟着是我们的数据类,然后是信息框类,并且最后, 我们将创建.phtml文件,它将所有东西拼装在一起组成一个可以工作的例子---- 或者至少,这就是此时的计划!
常量
这是一个PHP 文件,我创建它用来定义在整个站点上要用的常量。适当地使 用它,就能从一个地方改变站点的外观,而不是不得不在多个页面中对变量进行 修改。下面是我们的constants.inc例子的内容:
<?php
// 数据库常量
$HOST="localhost";
$DB="testing";
$WEBUSER="rOOt";
$WEBPASSWORD="";
// 颜色常量
$COLOR_PRIMARY="#037B0B";
$COLOR_SECONDARY="#FFFFC0";
$COLOR_TERTIARY="#ECED81";
// 常量值
$TRUE=1;
$FALSE=0;
// 应用特别设置
$TITLE="Object Orientation Demonstration";
$ADMINEMAIL="[email protected]";
// CSS插件值
$CSSBOXTITLE="boxtitle";
?>
数据库模式
没有具体的环境,这个模式应该是不真实的数据库模式[图2],但是它是为这 个例子的目的工作的:
如你所见,news数据在一个表中,有自已的要求,同时race,driver,team 和points的详细数据放在一些有关联的表中。如果你不理解这个模式,就不必多 说了,在继续深入之前,你应该去读一下关于RDBMS(译者:关系数据库管理系统 )介绍的资料。
mysqldb类
那么,现在已经定义了RDBMS,并且知道了数据存储在什么地方。是该"玩"面 向对象的时候了!!!
如果你玩PHP 已经有一段时间了,你应该相当清楚有很多可用的函数用来同 许多的数据库进行交互。从根本上说,这个类将作为一些MySQL 函数的包装---- 当然,如果你正在使用Postgres编程(或Oracle,或其它的RDBMS)你可以根据这 些进行修改。
代码如下:
<?php
class mysqldb {
//建立对象
var $host;
var $db;
var $dbuser;
var $dbpassword;
var $sql;
var $numberrows;
var $dbopenstatus;
var $dbconnection;
/*
使用这些函数得到或设置这个对象的变量的值。 这是好的OO习惯,因为
它意味着数据类型检查可以被完成并且引发相应的错误。
*/
// Get和Set属性
function gethost() {
return $this->dbhost;
}
function sethost($req_host) {
$this->dbhost = $req_host;
}
function getdb() {
return $this->db;
}
function setdb($req_db) {
$this->db = $req_db;
}
function getdbuser() {
return $this->dbuser;
}
function setdbuser($req_user) {
$this->dbuser = $req_user;
}
function getdbpassword() {
return $this->dbpassword;
}
function setdbpassword($req_password) {
$this->dbpassword = $req_password;
}
function getsql() {
return $this->sql;
}
function setsql($req_sql) {
$this->sql = $req_sql;
}
function getnumberrows() {
return $this->numberrows;
}
function setnumberrows($req_numberresults) {
$this->numberesults = $req_numberresults;
}
function setdbconnection($req_dbconnection) {
$this->dbconnection = $req_connection;
}
function getdbconnection() {
return $this->dbconnection;
}
/*
这是对象的构造函数。在这个例子中我设置了对象属性的大部分值,使用了在
constants.inc中定义的全局变量的值。通过这样做,对于特别的操作我只需
要改变这些属性的值,这样我们就不需要处理整个例子了。
*/
function mysqldb() {
global $HOST, $DB, $WEBUSER, $WEBPASSWORD;
global $TRUE, $FALSE;
$this->sethost($HOST);
$this->setdb($DB);
$this->setdbuser($WEBUSER);
$this->setdbpassword($WEBPASSWORD);
$this->setdbconnection($FALSE);
}
/*
这些是对象的方法。它们提供了打开一个数据库连接,关闭一个连接和执行一个
SELECT查询的功能。当然,这些可以被扩展,以便允许Insert,Update和Delete
等等。
*/
function opendbconnection() {
global $TRUE, $FALSE;
$this->dbconnection = mysql_connect( "$this->dbhost", "$this- >dbuser", "$this->dbuserpassword");
if ($this->dbconnection == $TRUE) {
$this->db = mysql_select_db( "$this->db");
$this->setdbconnection($TRUE);
} else {
$this->setdbconnection($FALSE);
return false;
}
return true;
}
function closedbconnection() {
if ($this->dbconnection = $TRUE) {
mysql_close($this->dbconnection);
}
}
function selectquery() {
global $TRUE, $FALSE;
if ($this->dbconnection == $FALSE) {
$this->opendbconnection();
}
$this->qry = mysql_query($this->sql);
if (!$this->qry) {
return false;
} else {
$this->numberrows = mysql_num_rows($this->qry);
if ($this->numberrows > 0) {
for($x = 0; $x > $this->numberrows; $x++) {
$this->result[$x] = mysql_fetch_row($this->qry);
}
} else {
echo( "[Error:] Retrieving data");
return false;
}
return true;
}
}
}
?>
上面就是数据类。现在我们需要建立处理实际布局(信息栏)的类!
通用信息类
在这一部分,我们将要创建三个类。不错,是三个类。一个用于管理数据, 两个子类用于绘制信息栏。先从管理数据类开始(把它叫做genericinfo)。这是 代码:
<?php
class genericinfo {
//创建对象,为变量保留内存空间
var $outerwidth;
var $outerbordercolor;
var $outerborderwidth;
var $titlebgcolor;
var $innerwidth;
var $innerbgcolor;
// 文本的变量
var $title;
// 样式变量
var $cssboxtitle;
/*
使用这些函数,用来得到或设置这个对象变量的值。这是好的OO习惯,因为 它意 味着数据类型检查可以被完成并且引发相应的错误。
*/
function setouterwidth($req_outerwidth) {
$this->outerwidth = $req_outerwidth;
}
function getouterwidth() {
return $this->getouterwidth;
}
function setouterbordercolor($req_outerbordercolor) {
$this->outerbordercolor = $req_outerbordercolor;
}
function getouterbordercolor() {
return $this->outerbordercolor;
}
function setouterborderwidth($req_outerborderwidth) {
$this->outerborderwidth = $req_outerborderwidth;
}
function getouterborderwidth() {
return $this->outerborderwidth;
}
function settitlebgcolor($req_titlebgcolor) {
$this->titlebgcolor = $req_titlebgcolor;
}
function gettitlebgcolor() {
return $this->titlebgcolor;
}
function setinnerwidth($req_innerwidth) {
$this->innerwidth = $req_innerwidth;
}
function getinnerwidth() {
return $this->innerwidth;
}
function setinnerbgcolor($req_innerbgcolor) {
$this->innerbgcolor = $req_innerbgcolor;
}
function getinnerbgcolor() {
return $this->innerbgcolor;
}
function settitle($req_title) {
$this->title = $req_title;
}
function gettitle() {
return $this->title;
}
function setcssboxtitle($req_cssboxtitle) {
$this->cssboxtitle = $req_cssboxtitle;
}
function getcssboxtitle() {
return $this->cssboxtitle;
}
/*
这是对象的构造函数。在这个例子中我设置了对象属性的大部分值,使 用了在 constants.inc中定义的全局变量的值。通过这样做,对于特别的操作我 只需 要改变这些属性的值,这样我们就不需要处理整个例子了。
*/
function genericinfo() {
global $COLOR_PRIMARY, $COLOR_SECONDARY, $COLOR_TERTIARY;
global $CSSBOXTITLE;
$this->setouterwidth(150);
$this->setouterbordercolor($COLOR_TERTIARY);
$this->setouterborderwidth(1);
$this->settitlebgcolor($COLOR_PRIMARY);
$this->setinnerwidth(146);
$this->setinnerbgcolor($COLOR_SECONDARY);
if (isset($CSSBOXTITLE)) {
$this->setcssboxtitle($CSSBOXTITLE);
}
}
// 方法
}
?>
你注意到类中没有一个方法,对此不要怀疑。为什么?噢,我猜在一个实际 的环境中,你可能会很好的选择把这三个类组合成一个,根据描述信息改变方法 。然而,这是一个使用了类和子类(继承下来的)的现实的例子,并且我选择子 类的原因是我可以给信息栏增加更多的"滋味"(例如,图角),我可以创建更深 层的子类,增加正确代码的可移植性。
linkbox类
这个类将使用它所提供的数据,并且用它来为我们生成链接框。根本上说, 是将一个多维的数组解析成为链接框。这就意味着数组将由数据片段组成,每个 片段有两个部分--一部分为你想让用户看见的,一部分为实际的URL。
<?php
class linkbox extends genericinfo {
/*
创建对象。你将会注意到,我们没有为变量保留内存空间。在这个情况下,
不需要。
*/
/*
这是linkbox的构造函数。它所做的唯一一件事就是调用父类的构造函数。为什么 ?
就是,在PHP管理了部分的面向对象的功能的同时。但是有一点失败的地方(在此 刻)
就是子类中的构造函数。那么,为了保证子类使用父类的构造函数被实例化,我 简单
地调用了父类的构造函数。当然如果我接着想要覆盖任何值,我能够很容易地改 变它。
*/
function linkbox() {
$this->genericinfo();
}
/*
这是类中仅有的方法。相当简单,如你所见,它绘制表格,放置所要求的数据在
适当的位置。
*/
function drawlinkbox() {
echo( "<TABLE BORDER=\"$this->outerborderwidth\" CELLPADDING=\ "0\" CELLSPACING=\"0\" WIDTH=\"$this->outerwidth\" BORDERCOLOR=\"$this ->outerbordercolor\" BGCOLOR=\"$this->titlebgcolor\">");
echo( "<TR>");
echo( "<TD>");
if (isset($this->cssboxtitle)) {
echo( "<DIV CLASS=\"" . $this->getcssboxtitle() . "\">");
echo($this->title);
echo( "</DIV>");
} else {
echo($this->title);
}
echo( "</TD>");
echo( "</TR>");
echo( "<TR>");
echo( "<TD>");
echo( "<TABLE BORDER=\"0\" CELLPADDING=\"0\" CELLSPACING=\"0\" WIDTH=\"$this->innerwidth\" BGCOLOR=\"$this->innerbgcolor\">");
echo( "<TR>");
echo( "<TD>");
echo( "");
for ($x = 0; $x < count($this->data); $x++) {
echo( "- <A HREF=\"" . $this->data[$x][1] . "\">" . $th
is->data[$x][0] . " ");
}
echo( " ");
echo( "</TD>");
echo( "</TR>");
echo( "</TABLE>");
echo( "</TD>");
echo( "</TR>");
echo( "</TABLE>");
}
}
?>
resultbox Class
这个与linkbox类没有什么不一样的,除了没有HREF包含在其中。我们创建了 一个两列的表格,并且将分析后的数据放在其中。
<?php
class resultbox extends genericinfo {
/*
创建对象。你将会注意到,我们没有为变量保留内存空间。在这个情况下,
不需要。
*/
//constructor
function resultbox() {
$this->genericinfo();
}
/*
这是类中仅有的方法。相当简单,如你所见,它绘制表格,放置所要求 的数据在
适当的位置。
*/
function drawresultbox() {
echo( "<TABLE BORDER=\"$this->outerborderwidth\" CELLPADDING=\ "0\" CELLSPACING=\"0\" WIDTH=\"$this->outerwidth\" BORDERCOLOR=\"$this ->outerbordercolor\" BGCOLOR=\"$this->titlebgcolor\">");
echo( "<TR>");
echo( "<TD>");
if (isset($this->cssboxtitle)) {
echo( "<DIV CLASS=\"" . $this->getcssboxtitle() . "\">");
echo($this->title);
echo( "</DIV>");
} else {
echo($this->title);
}
echo( "</TD>");
echo( "</TR>");
echo( "<TR>");
echo( "<TD>");
echo( "<TABLE BORDER=\"0\" CELLPADDING=\"0\" CELLSPACING=\"0\" WIDTH=\"$this->innerwidth\" BGCOLOR=\"$this->innerbgcolor\">");
for ($x = 0; $x < count($this->data); $x++) {
echo( "<TR>");
echo( "<TD>");
echo($this->data[$x][0]);
echo( "</TD>");
echo( "<TD>");
echo($this->data[$x][1]);
echo( "</TD>");
echo( "</TR>");
}
echo( "</TABLE>");
echo( "</TD>");
echo( "</TR>");
echo( "</TABLE>");
}
}
?>
Index.phtml
这些东西是如何组合在一起的呢?好,最好的演示就是创建我们将用之表现 给用户的内容的页面,名为index.phtml
1: <?
2: include "constants.inc";
3: include "mysqldb.obj";
4: include "genericinfo.obj";
5: include "linkbox.obj";
6: include "resultbox.obj"
7: ?>
8: <HTML>
9: <HEAD>
10: <TITLE>
11: <? echo($TITLE); ?>
12: </TITLE>
13: <LINK TYPE="text/css" REL="stylesheet" HREF="main.css">
14: </HEAD>
15:
16: <BODY BGCOLOR="#FFFFFF">
17:
18: <TABLE BORDER="0" CELLPADDING="10" CELLSPACING="10">
19: <TR VALIGN="top">
20: <TD>
21: <?
22:
23: $db0 = new mysqldb();
24: $db0->setsql("SELECT tem_team, tem_url FROM team ORDER BY tem_id");
25: if ($db0->selectquery()) {
26: $lnk = new linkbox();
27: $lnk->settitle("F1 Teams");
28: $lnk->data = $db0->result;
29: $lnk->drawlinkbox();
30: } else {
31: echo("[Error:] Unable to connect");
32: }
33:
34: ?>
35: </TD>
36: <TD>
37: <?
38:
39: $db1 = new mysqldb();
40: $db1->setsql(" SELECT
41: CONCAT(\"\",UPPER(driver.drv_surname), \ " \", driver.drv_forename),
42: SUM(points.pts_teampoints) as totdriverpoin ts
43: FROM points
44: LEFT JOIN driver ON points.drv_id = driver. drv_id
45: GROUP BY driver.drv_surname, driver.drv_forenam e
46: HAVING totdriverpoints <> 0
47: ORDER BY totdriverpoints DESC");
48:
49: if ($db1->selectquery()) {
50: $rst = new resultbox();
51: $rst->setouterwidth(175);
52: $rst->setinnerwidth(171);
53: $rst->settitle("F1 Drivers Championship");
54: $rst->data = $db1->result;
55: $rst->drawresultbox();
56: } else {
57: echo("[Error:] Unable to connect");
58: }
59:
60: ?>
61: </TD>
62: <TD>
63: <?
64:
65: $db2 = new mysqldb();
66: $db2->setsql(" SELECT
67: team.tem_team,
68: SUM(points.pts_teampoints) as totteampoints
69: FROM points
70: LEFT JOIN team ON points.tem_id = team.tem_ id
71: GROUP BY team.tem_team
72: HAVING totteampoints > 0
73: ORDER BY totteampoints DESC");
74:
75: if ($db2->selectquery()) {
76: $rst = new resultbox();
77: $rst->setouterwidth(175);
78: $rst->setinnerwidth(171);
79: $rst->settitle("F1 Constructor's Championship");
80: $rst->data = $db2->result;
81: $rst->drawresultbox();
82: } else {
83: echo("[Error:] Unable to connect");
84: }
85:
86: ?>
87: </TD>
88: </TR>
89: </TABLE>
90: </BODY>
91: </HTML>
解释:
1 - 7行 这些语句将我们在前面所写的文件放在一起,从而使它们的内容有 效。注意,constants.inc文件应该是第一个被包含的文件。如果不这样,它就不 是全局的了。
8 - 20行 这里我们使用了HTML,用来建立页面并且引入样式表。
21 - 34行 返回到PHP,开始使用对象。我们要创建的第一项是F1车队栏。
23 实例化mysqldb类,创建一个mysqldb类的对象;通过$db0来引用。这样就 要执行mysqldb类的构造函数,通过在constants.inc中的全局常量设置mysqldb类 的变量初始值。
24行 调用setsql($req_sel)函数来设置$db0对象的sql属性。
25行 这行很有意思。基本上,如果对mysqldb对象里的selectquery()的调用 失败(或返回false),执行跳到30行。
假设25行返回true,我们就得到了数据!不太坏吧?现在让我们继续绘制信 息栏。
26行 实例化linkbox()类,创建一个linkbox类的对象,用$lnk来引用它。这 样将要执行linkbox类的构造函数,它将依次调用父类(genericinfo)的构造函 数。
27行 设置linkbox的标题,通过调用父类的settitle($req_title)方法。
28行 要确保赋给$lnk的数据是有效的,应该与$db0对象的当前数据相同。
29行 调用linkbox类的drawlinkbox()函数。因为$lnk现在处理所以的数据, 它就能够完全绘制出linkbox来。在drawlinkbox函数中要注意的一点是在第16行 到22行。如果设置了$CSSBOXTITLE全局常量,那么样式表将被引用,否则就不会 。
就这样!你现在已经在屏幕上画出了新的信息栏。
35 - 36行 回到HTML代码来结束单元格并且打开一个新的单元格。
从现在开始,你应该能够看完本页代码的剩余部分,看一下其它的对象是如 何被画出来的。
main.css
最后,是样式表,它将被用在整个工作中!不要再多说费话了 ;-)
1: <STYLE>
2: .bugresolver {
3:
4: }
5: TD {
6: font-family: verdana, arial, courier;
7: font-size: 10;
8: }
9:
10: P {
11: font-family: verdana, arial, courier;
12: font-size: 20;
13: }
14:
15: A {
16: font-family: verdana, arial, courier;
17: font-size: 12;
18: color: #000084;
19: text-decoration: none;
20: font-weight: bold;
21: text-align: right;
22: }
23:
24: A:hover {
25: font-family: verdana, arial, courier;
26: font-size: 12;
27: color: #990000;
28: background-color: #DCDADA;
29: text-decoration: none;
30: font-weight: bold;
31: text-align: right;
32: }
33:
34: UL {
35: margin-left: 25;
36: }
37: .boxtitle {
38: font-family: verdana, arial, courier;
39: font-size: 14;
40: color: #FFFFFF;
41: font-weight: bold;
42: text-align: center;
43: }
44:
45: .lnkBox {
46: font-family: verdana, arial, courier;
47: font-size: 10;
48: color: #000084;
49: margin-left: 5px;
50: text-align: left;
51: }
52: </STYLE>
例子/下载
对于那些想下载这个例子,并且自已试一下的来说,有一个ZIP文件[http:/ /phprecord.e-chome.net/docs/mark20000727.zip]
对于有OO经验的人
我很清楚在代码中有很多的漏洞。例如,我没有完成在设置或返回属性值时 对数据的检查。然而,记住这个练习的目的是给出人们一个摸拟现实的关于在PH P 中的面向对象的处理与函数使用的例子(人们可以学习它)。
对于没有OO经验的人
这是一个模拟现实的关于在PHP中的面向对象的处理与函数使用的例子。在代 码中有很多的漏洞。那并不是说代码是错误的,但是它的功能是向你演示如何用 OO来工作。
如果你正在寻求更多的指导,想要发展这些代码用在你的站点上,你应该重 新看一下很多在这些页面中的get和set函数,确保你开发的处理数据的例程被处 理;确保它拥有正确的数据类型(例如在需要的时它可为整数,在需要时它可为 SQL 语句)。记住,这些类所处理的数据是从已经存在的数据库来的,所以我知 道数据是有效的。
要知道使用这些信息栏的更多的例子,请参观e-sphere.net, f1circle.com 和 markaw.com。
关于作者
此时,Mark Williams,是一个专业的在欧洲排名为前10名的保险公司之一的 高级技术顾问,正从事着大量的因特网/电子商务的工作,包括WAP技术,视频会 议,Windows 2000,Linux等等...
为了放松,Mark是一个F1赛车的爱好者,每个星期日(在赛季中)你总会发现 他在了解最新的消息。
去markaw.com,e-sphere.net 和 f1circle.com可以了解更多的信息。
---------------------------------------------------------------------
作者:Mark Williams
原文:http://www.PHPBuilder.com
译者:limodou
来自:http://phprecord.126.com
说明:
这篇文章是 limodou 同学的译作,非常精彩,相信对于那些想要学习OOP的编程 思想,提高PHP技术水平的人很有价值。感谢 limodou 为广大 PHP 的爱好者们提 供源源不断的精彩文章!遗憾的是转贴过程中丢失了一些图形,没有这些图形, 文章失色不少。:( ,如果你感兴趣,可以去[http://phprecord.126.com]看看 “原汁原味”的版本。
-- ※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 61.141.205.77]
|
|