<?php class Bird { function __construct($name="No-name", $breed="unknown", $price=15) { $this->name = $name; $this->breed = $breed; $this->price = $price; } function setName($name) { $this->name = $name; } function setBreed($breed) { $this->breed = $breed; } function setPrice($price) { $this->price = $price < 0 ? 0 : $price; } function getName() { return $this->name; } function getBreed() { return $this->breed; } function getPrice() { return $this->price; } function display() { printf("<p>%s is a %s and costs \$%.2f</p>\n", $this->name, $this->breed, $this->price); } } ?>
下面来创建Bird的一个新实例。假设在写这个例子之前,这个宠物商店已经卖掉了鹦鹉Polynesia,所以,这一次来创建一个喜鹊。首先,调用构造函数,并指定一些合理的值:
$magpie = new Bird("Malaysia", "magpie", 7.5); $magpie->display();
由于邻居家的猫一直虎视眈眈要除掉这只喜鹊(这可能意味着需要花钱将它放生),试着使用setPrice()将这个喜鹊的价格设置为一个负数:
$magpie->setPrice(-14.95); $magpie->display();
setPrice()方法不允许将价格设置为小于0的值:
Malaysia is a magpie and costs $0.00
不过,还是有可能绕过这个限制,有可能只是无意,也有可能是一个特别诡异而且憎恨喜鹊的猫科黑客故意所为:
$magpie->price = -14.95; $magpie->display();
可以看到,输出如下:
Malaysia is a magpie and costs $-14.95
如何制止这种情况发生呢?办法是利用PHP的一个特性,研究过Java的人应该对这个特性很熟悉,但在PHP 5中这是一个新增的特性,即可见性(visibility)。利用可见性,可以通过3个关键字控制类成员的访问:
既然了解了可见性,解决前面遇到的问题就很简单了。只需将以下代码插入到Bird类中构造函数定义的前面即可:
private $name; private $breed; private $price;
在浏览器中重新加载这个例子,你会看到下面的结果:
Malaysia is a magpie and costs $7.50 Malaysia is a magpie and costs $0.00 Fatal error: Cannot access private property Bird::$price in /home/www/php5/bird-7.php on line 60
将实例变量声明为private,要求你(或者使用Bird类的其他人)只能通过所定义的设置方法来设置其属性,这样就可以确保一定遵循对这些属性值所做的限制。
所有类成员的可见性都默认为public,这一点当然没什么问题,而且(不同于Java或C++)并不要求专门声明公共变量,不过为所有变量声明可见性仍不失为一个好的做法。一方面,从组织的角度看这样很有好处(例如,如果你习惯提前声明所有变量,那么以后就会发现很可能偶尔需要重用其中的某个变量);另一方面,要想使用私有和保护变量,唯一的办法就是进行显式声明。
使用静态成员和self关键字
有时,你可能希望在类的上下文中访问一个变量或方法,而不是在对象(类实例)中访问。为此,可以使用static关键字,这是PHP 5新增的一个关键字。
举个例子,为上例中的Bird类增加一个静态属性和一个静态方法。用什么顺序来声明关系不大,不过我们倾向于先列出类的所有静态成员,所以下面把新代码直接插入到类声明开始处大括号的后面。
public static $type = "bird"; public static function fly($direction="around") { printf("<p>The bird is flying %s</p>\n", $direction); }
注意,静态成员与其他类成员的可见性是一样的,如果未作声明,它们的可见性也默认为public。可以把static关键字放在可见性关键字的前面,也可以放在其后面,不过一般约定是先声明可见性。静态方法可以有参数,可以返回值,而且可以有默认参数,从这一点上看,它与其他方法没有什么不同。不过,静态方法和静态属性并非与类的具体实例关联,而是与类本身关联。可以使用类名和::操作符在调用代码中引用这些静态成员。例如:
printf("<p>The Bird class represents a %s</p>\n", Bird::$type); Bird::fly(); Bird::fly("south");
要从类的一个实例访问静态成员,必须另外做点工作。下面对display()方法稍做修改来说明这一点:
<?php class Bird { public static $type = "bird"; public static function fly($direction="around") { printf("<p>The bird is flying %s</p>\n", $direction); } private $name; private $breed; private $price; public function __construct($name="No-name", $breed="unknown", $price=15) { $this->name = $name; $this->breed = $breed; $this->price = $price; } function setName($name) { $this->name = $name; } function setBreed($breed) { $this->breed = $breed; } function setPrice($price) { $this->price = $price < 0 ? 0 : $price; } function getName() { return $this->name; } function getBreed() { return $this->breed; } function getPrice() { return $this->price; } public function display() { printf("<p>The %s named '%s' is a %s, and costs \$%.2f</p>\n", self::$type, $this->name, $this->breed, $this->price); } } ?>
下面创建Bird的一个新实例,看看做上述修改后会有什么作用。代码如下:
$sam = new Bird("Toucan Sam", "toucan"); $sam->display();
以下是修改了display()方法后的输出:
The bird named 'Toucan Sam' is a toucan and costs $15.00
如果查看这个新版本的display()方法,你可能会注意到一个新的关键字self,这个关键字指示类本身。注意不要把self与this混淆,这一点很重要:this的含义是“当前对象”或“类的当前实例”,而self表示“当前类”或“当前对象所属的类”,二者的区别如下。
self关键字有以下要求:
this关键字的要求如下: