我觉得自从使用PHP以来最难以理解的概念就是PHP的类。我除了正在学习MySQL一些基本知识以外,对于其他的数据库引擎没有经验。此前对于OOP面向对象编程我也没有经验,OOP对于我来说也是个新鲜事物,但是通过对其原理以及为何如此强大的理解,使我有了一定的我了解如果任何程序都用OOP来实现的话,无疑会得到一个强大的程序;但是对我而言,如果我只是认为自己了解了其中的机制,而不把这种理解应用到编程实践,OOP对我来说将始终停留在纸上谈兵的层次上。于是,几天前,在利用常规的函数来实现一些功能时,我尝试采用对象来实现,我发现的确这让我的工作更加简单了!于是我将这些经验写出来,希望能够帮助象我一样的人们。
类说穿了就是一些变量(OOP概念中的成员变量,译者注)和一些操作处理这些变量的函数(OOP概念中的成员函数)的集合。它提供了一种在程序中以现实生活的形态来思考问题的方法,换句话说,就是描述了一个对象。反之,一个对象或者实例,就是这个类的真正实现。比如说我们要描述一辆自行车:一个合适的自行车类应该含有如下变量:$pedals(脚蹬),$chain(链条),$front wheel(前轮),$rear wheel(后轮),$brakes(刹车), and $handl_bars(车把)。你可以参考现实生活中对一辆自行车的操作来实现你的脚本。比如可以通过向函数Accelerate()传递一个$Braking_Force,并将这个变量应用于你所定义的实例的其他变量如$front_wheel, $rear_wheel,也可以返回操作的结果,从而实现相应的操作。
看起来很美,但是这些通过常规的函数和变量不是也能够实现吗?不错,如果你的程序中只有一个自行车的实例,为它单独定义一个类的确意思不大,但是如果你需要很多自行车实例的时候呢?但是你程序中的变量传递流会变得相当复杂,你得时时保证每个自行车实例指定的变量都正确的传递给了不同的函数。但是使用对象大大降低了你所传递的变量的数量,因为对象中的处理函数(所谓的方法method,译者著。)自动的认定作用范围就限制在本对象之内,那么它所能够操作的对象也就限制在了对象范围内。而且对象的定义可以非常容易的包含到不同的脚本只用,(在得到可重用性的同时)还从而保证了每一个自行车的在不同的脚本中工作方式都是一致的。
让我们来创建一个实际的类,这个类我几乎用在了我站点的每一个页面中,也许你会从中发现值得参考的东西。
我不知道你是什么情况,但是对我来说,当我写一个动态Web页面的时候,我最痛恨的就是时不时要中断对程序流程的思考,转而思考HTML的合适的表现格式。这样做的结果就是使我最终作出来的页面缺少吸引力,因为我没有那么多的精力去顾及字体、字号、背景、以及文字的颜色。解决方法就是:通过PHP类来实现对文本的格式化,从而实现HTML的输出属性定制。
我把这个类命名为“Style”。其中包含了如下一些对于设置HTML属性至关重要成员变量:
<?php
class Style {
var $text;
var $alink;
var $vlink;
var $link;
var $bgcol;
var $face;
var $size;
var $align;
var $valign;
}
?>
我想你已经对于HTML语法相当熟悉了,以上的变量名就是他们在HTML语法中的名字。接下来我为类Style创建一个名称为Style的成员函数。
<?php
class Style {
function Style ($text= "#000000",$alink= "#AA00AA",$vlink= "#AA00AA",$link= "#3333FF",$bgcol= "#999999",$face= "Book Antiqua",$size=3,$align= "CENTER",$valign= "TOP") {
$this->text=$text;
$this->alink=$alink;
$this->vlink=$vlink;
$this->link=$link;
$this->bgcol=$bgcol;
$this->face=$face;
$this->size=$size;
$this->align=$align;
$this->valign=$valign;
}
}
?>
当你在类中创建一个与类同名的成员函数时,这个函数会在你每次创建一个类的实例的时候被执行,该函数称为构造函数。我利用这个机制,来使实现在每次创建一个对象的时候,自动的为每个HTML属性变量赋予一个缺省值。
<?php $Basic = new Style; ?>
上面的语法"=new ClassName;”就是为类Style声明了一个实例$Basic。
你可以在声明一个类的实例的同时,通过传递参数的形式为类中的某个变量指定不同的值。但是即使你声明只修改其中一个变量,也得通知指定其他成员变量的值。从这一点来说,类和常规函数是相同的。比如说你要为Style类中的text变量指定一个缺省值以外的值,其他的变量你也要指定一遍。不过这还有更简单的方法(实际上这就是通常使用的方法,没有什么新鲜的?译者注),就是通过在类中加一个成员函数来改变其中类中某个变量的值:
<?php
Function Set($varname,$value) {
$this->$varname=$value;
}
?>
然后,我们通过如下的代码就可实现对一个对象实例中指定变量值的修改。
<?php $Basic->Set( 'size', '2'); ?>
你必须使用”->”操作符以声明是一个对象的变量或函数。上面的代码就是告诉代码解释器”运行$Basic中的Set()成员函数”。解释器就会知道,$Basic就是类Style的一个实例,因为我们此前已经声明过了。同样我们可以通过如下代码引用一个实例的变量 (如:$Basic->text)
让我们创建一个表头样式对象,其属性与缺省值有些不同。
<?php
$Theader= new Style;
$Theader->Set( 'text', '#0000FF');
$Theader->Set( 'bgcol', '#000000');
?>
到在这里,够好了吧?现在我的表格头有了蓝色的文字和黑色的北京。我还想要我的表体相对于我的主页而言有些轻灰,文字用黑色的,而表体中的文字要小一些,于是我可以:
<?php
$Tbody=new Style;
$Tbody->Set( 'bgcol', '#AAAAAA');
$Tbody->Set( 'size',2);
?>
相当不错,现在我们还能够用他来作什么呢?问得好!我很高兴你会问这个问题。我们需要在类中再创建几个成员函数来是Style可以用到任何HTML页面对象中。首先我要做的就是,为我的页面设置属性,于是:
<?php
function Body() {
PRINT "<BODY BGCOLOR="$this->bgcol" ".
"TEXT="$this->text" ".
"LINK="$this->link" VLINK="$this->vlink" ".
"ALINK="$this->alink"> ";
}
?>
上述代码,为我站点中的所有页面设置了整体风格。其中还设计到了一个变量”$this”。我们在类中只用这个变量,解释器会自动将其理解为我们在引用当前对象本身的一个成员变量。换句话说,$this变量在应用中与对象名称(如本例中的$Basic)是等义的。你应该注意到我们在此所做的的确要比用常规函数来实现来得简单。我们可以不用向函数传递变量而在函数中引用这个变量。而通常,常规函数是通过几个全程变量数组才能够实现这种机制的。不过需要声明的是,所有对象的变量和成员函数的作用域都是在本对象范围以内的。
试验以下下面的PHP代码(假设你已经包含了Style类,并为其创建了一个实例$Basic,然后页面中已经有了 和 <HEAD></HEAD> 标签)
<?php $Basic->Body(); ?>
这样我们就可以输出一些东西到页面中了,我们可以采用传统的方法,但我想做点不同的….(这个作者故弄玄虚,幽默过头了?译者注)这就是我用的另外一个成员函数TextOut:
<?php
function TextOut($message= " ") {
PRINT " "text">$message ";
}
?>
这个函数需要一个参数$message,$message是你要输出的文字,该函数会以指定的属性输出这些文字。我们可以通过如下方法实现:
<?php
$Basic->TextOut( 'This is my test message');
$Tbody->TextOut( ' -- kinda neat, huh?');
?>
注意,由于这两个函数中间没有输出回车换行<br>,所以结果将输出到同一行上。而我还想要第二部分的字体更小一点,这个我在上面的$Tbody对象中已经声明了,这样比较安全,$Tbody与$Basic唯一的不同还有个$bgcol属性,在这里没有用到。注意到了程序声明中的” ”吗?这是用来在没有参数传递给函数时函数,函数就输出缺省的不间断空格,原因后述。
到目前为止,我们的工作还没有保存。最后的一个例子说明如何通过一种简单的方法,来修改字体的颜色和或大小,即使在一个输出语法的中间,也不需要改动整个类。我们加入如下的代码:
<?php
function TDOut ($message= " ",$colspan=1) {
PRINT "<TD COLSPAN=$colspan BGCOLOR="$this->bgcol" ".
"ALIGN="$this->align" VALIGN="$this->valign">";
$this->TextOut($message);
PRINT "</TD>
";
}
?>
那么,讲解就到此为止!记住我想让我的表格有不同的背景颜色,我可以这样实现:
<TABLE>
<TR>
<?php
$Theader->TDOut( "Name",2);
$Theader->TDOut( "Location",3);
?>
</TR>
<TR>
<?php
$Theader->TDOut( "Last");
$Theader->TDOut( "First");
$Theader->TDOut( "City");
$Theader->TDOut( "State/Province");
$Theader->TDOut( "Country");
?>
</TR>
这里,你可以看到colspan参数是如何起作用的,如果没有声明,将会有个缺省值1。于是在第一个行“Name”中,合并了2列;Location行合并了3列。第二行,所有的列都被合并成了一个。
我们通过如下方式来实现表体:
<TR>
<?php
$Tbody->TDOut( "Kreisler");
$Tbody->TDOut( "Rod");
$Tbody->TDOut( "Cortlandt");
$Tbody->TDOut( "New York");
$Tbody->TDOut( "USA");
?>
</TR>
但看起来似乎仍然有些繁琐,我们是否可以节省更多的步骤?那么采用下面的函数:
<?php
function TROut($message) { /*And NO comments about fish, please! ;) */
PRINT "<TR>
";
$cells=explode( "|",$message);
$iterations=count($cells);
$i=0;
while ($i<$iterations) {
list($message,$span)=explode( ":",$cells[$i]);
if (strlen($message)<1) $message= " ";
if ($span){
$this->TDOut ($message,$span);
}else{
$this->TDOut ($message);
}
$i++;
}
PRINT "</TR>
";
}
?>
WOW!看起来有些繁琐,我们来分解一下:
第 3行根据”|”符号分解一个字符串,并且将分解出来的每个元素都放到数组$cell中。第四行取得数组的中数组元素的个数到$iterations;第6 行开始一个循环处理数组中的每个元素。第7行以”:”作为分隔符分解一个数组元素,将其存储到$message和$span中。第8行检查$message是否有值。如果没有就赋一个缺省值。第九行检查有没有$span(就是合并不合并单元格,通过$cell数据中冒号后还有$span来要确定),如果是这样,第10行调用TDOut函数,传递$message和单元格个数参数;如果没有第12行程序单纯输出$message信息(TDOut函数就用缺省值1来设置$colspan属性)。最后,我们结束一个表格行(通过</tr>)。
或许你看了还有些糊涂(不糊涂才怪),到底如何传递给TROut参数才能包括所有的必要信息?正确地格式就是:celldata[:colspan]|celldata[:colspan]|......celldata[:colspan](用|分隔单元格,单元格中数据后跟:号分隔的合并单元格的数目来指定要扩展本单元格的个数?译者注。)
所以,总结一下我们以前所作的工作,表头和表体可以通过下面的调用来完成:
<TABLE>
<?
$Theader->TROut( "Name:2|Address:3");
$Theader->TROut( "First|Last|City|State/Province|Country");
$Tbody->TROut( "Rod|Kreisler|Cortlandt|New York|USA");
?>
</TABLE>
Wow! 是不是非常的简单?(实际上的确非常简单,但是这个作者写的还不足以让人认识到这一点。也就是说给的例子缺乏说服力,我个人就觉得依靠TROut这行代码,输出一个行还不如样式表来得简单。作者起码用一个BBS的例子来说明OOP在PHP中的用处才有意义。当然了,作为PHP中OOP的入门课程,本文也算生动了,而且各个方面讲得也差不多。)。
如果有单元格中的数据存放在变量中的情况怎么办?只要Join数组就可以了:
<?php
$message=join($arry, "|");
$Tbody->TROut($message);
?>
当然,你不能在一个Join中同时输入列扩展的单元格数,但是你可以再用一个。假设你的数组包含6个元素,第3和第4行分别要扩展2个和3个单元格,你可以通过如下代码实现将:#扩展格数参数指定到$message中
$newarray=$arry;
$newarray[2]=join(list($newarray[2],"2"),":");
$newarray[4]=join(list($newarray[4],"3"),":");
$message=join($newarray,"|");
$Tbody->TROut($message);
显然,你可以加入更多的成员函数。如果有人有更好的想法,可以Email给我,我会封装更多的代码并发表出来。