类是简单的用户定义类型。在面向对象的语言中,类就是创建类的对象或实例(功能副本)的模版;类描述了其所属的任何对象的共同特点;类的目的是封装对象的定义和行为,对最终用户隐藏它的具体实现,并使最终用户按照文档说明和期望的方式来使用类对象。封装使程序更小,更易于管理,这是因为对象已经包含了处理它们所需的逻辑。类还有自动加载的功能,这样就可以把脚本分成更小、更易于管理的代码块。
在看一个简单的PHP类之前,我们先来介绍一些术语。
类成员或类属性:是一个变量,是类的数据部分。
类方法:在类中定义的函数。
现在我们要定义一个在二维平面中的点类,使用了笛卡儿坐标定义(如代码清单1-1所示)。由于设计这个类纯粹是为了教学目的,而且它有几个严重的缺陷,所以不建议你把它作为代码开发基础。
代码清单1-1 二维平面
<?php
class Point {
public $x;
public $y;
function __construct($x,$y) {
$this->x=$x;
$this->y=$y;
}
function get_x() {
return($this->x);
}
function get_y() {
return($this->y);
}
function dist($p) {
return(sqrt( pow($this->x-$p->get_x(),2)+
pow($this->y-$p->get_y(),2)));
}
} // 结束类的定义
$p1=new Point(2,3);
$p2=new Point(3,4);
echo $p1->dist($p2),"\n";
$p2->x=5;
echo $p1->dist($p2),"\n";
?>
这个类并不简单,有相当多的地方需要分析和修改。首先,正如前面所说的那样,这个类描述了平面里的一个点,并通过笛卡儿坐标$x和$y来定义。这里用到了关键词public,随后我们会讲解它。还有一个构造方法__construct,当使用new操作符在内存中创建Point类的一个新对象(或实例)时,它就会被调用。换句话说,当执行$p1=new Point(2,3)这一行时,方法__construct就会被自动引用和执行,并且类名后面圆括号中的参数,会被传递给__construct方法使用。
__construct方法中引用了$this变量。$this变量指向的是面向对象的类的实例本身。它始终指向当前对象这个焦点,等价于面向对象中的“我”。在几乎所有的面向对象的语言中,都有这个变量的变体,在一些语言中甚至叫做“self”。
类的构造函数就是对给定的类对象进行初始化(或实例化)时执行的方法。在这个代码示例中,它用来给坐标赋初值。坐标(变量$x和$y)是Point类的成员。此外,还定义了其他几个方法——两个get方法和一个dist方法,dist方法用来计算两点之间的距离。
接下来我们来看看关键字public。声明为public的成员,允许全局访问成员变量的数据。在我们的脚本中,有一行是$p2->x=5;,这说明一个点的x坐标被直接操作了。像这样的访问是无法控制的,即使在最简单的情况下也是相当糟糕的。好的实践是通过get和set方法来读或写类的成员,这样就可以控制类的成员。换句话说,使用get和set方法,就可以控制数据成员的值。对public成员来说,get和set函数是多余的,因为可以直接设置成员变量,就像$p2->x=5。不管怎样,使用public成员,成员变量的值是不可控的。可以为每一个成员直接编写set和get函数,但是PHP提供了所谓的“魔术方法”,用来取代必须为每一个成员写两个函数的方法。
使用private和protected关键词,可以更好地保护类的成员变量。这两个关键字的确切含义将在下一节中解释。值得注意的是,public是默认的可见性。如果没有指定成员或方法的可见性,它的默认属性就是public。写做
class C {
$member;
function method() {...}
….
}
完全等价于写成:
class C {
public $member;
pubic function method() {…}
….
}
与public的类成员相比,private的类成员或方法只能在相同类的方法中可见。只要不是类的方法就无法访问类的任何私有成员,也包括任何其他的私有方法。如果点类的成员$x和$y前面的关键词public被替换成关键词private,并且试图直接访问的话,结果将会是:
PHP Fatal error: Cannot access private property Point::$x in script2.1 on line 25
换句话说,在第25行,我们简单的小技巧$p2->x=5将不再起作用。而构造函数没有任何问题,包括get_x()和get_y()同样没有任何问题,因为它们都是类的成员。这是好事,因为这样将不能直接操纵类对象;用类来实现它们本不应该实现的行为,有可能从根本上改变它们的行为。总之,类是自足的,它就像一个可以控制入口的高速公路——有受限的入口和出口坡道。
现在共有和私有成员已经讲明白了,但是保护成员和方法又如何理解呢?保护成员和方法只能被成员从属的类的方法访问,以及把它们从属的类做为基类的子类的方法所访问。本书将在接下来的部分深入讲解它们。