第19章 类与对象(PHP5)之四:类常量(Class Constants)
可以在每个基类中定义常量使它保持不变。在你不使用$符号去声明或使用它时,常量不同于普通变量。就象静态成员,常量值不能通过对象的实例来访问(而应使用$object::constant). 常量值必须是一个常量表达式,而不是一个变量,一个类的成员,一个数学表达式或函数调用的结果。
例子 19-15. 定义并使用一个常量
<?php
class MyClass
{ const constant = 'constant value';
function showConstant() { echo self::constant."\n"; }
}
echo MyClass::constant."\n";
$class = new MyClass();
$class->showConstant();// echo $class::constant; is not allowed
?>
第19章 类与对象(PHP5)之五:抽象类(Class Abstraction)
PHP 5中引入了抽象类和抽象方法。不允许创建一个已经定义为abstract的类的一个实例。任何至少包含一个抽象方法的类也必须是抽象的。被定义为抽象的方法仅仅是声明方法的一个信号,并不能定义它们的实现。
当从一个抽象类继承时,在父类中所有抽象方法的标记的声明必须通过子类定义;另外,这些方法必须用定义相同的访问属性。例如,如果方法被定义为protected类型,执行函数必须定义为protected或public.
例子 19-16. 抽象类例子
<?php
abstract class AbstractClass
{ // Force Extending class to define this method
abstract protected function getValue();
abstract protected function prefixValue($prefix);
public function printOut() // Common method
{
print $this->getValue()."\n";
}
}
class ConcreteClass1 extends AbstractClass
{ protected function getValue()
{
return "ConcreteClass1";
}
public function prefixValue($prefix)
{
return "{$prefix}ConcreteClass1";
}
}
class ConcreteClass2 extends AbstractClass
{ public function getValue()
{
return "ConcreteClass2";
}
public function prefixValue($prefix)
{
return"{$prefix}ConcreteClass2";
}
}
$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') ."\n";
$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') ."\n";
?>
上例将输出:
ConcreteClass1
FOO_ConcreteClass1
ConcreteClass2
FOO_ConcreteClass2旧代码拥有非用户自定义的命名为abstract的类或函数将要运行如果没有被修改。
PHP面向对象、类经典教程
作者:木林森| 文章来源:www.phpchina.com | 点击数:1664 |更新时间:2007-2-1 19:50:12
第19章 类与对象(PHP5)之六:对象接口(Object Interfaces)
对象接口允许你创建一个指定类的方法的执行代码,而不必说明这些方法是如何被操作(处理)的。
接口被用来定义接口关键字的使用,同样作为一个标准类,但没有任何方法有它们内容的定义。
在接口中所有的方法必须声明为public,这是接口的特性。
implements (执行,实现)
为了实现一个接口,使用了implements操作。在接口中所有的方法必须在一个类的内部实现;疏忽这些将导致一个致命错误。如果渴望通过使用一个逗号分开每个接口,类可以实现多个接口。
例子 19-17. 接口实例
<?php
// Declare the interface 'iTemplate'
interface iTemplate
{ public function setVariable($name, $var);
public function getHtml($template);
}
// Implement the interface . This will work
class Template implements iTemplate
{ private $vars = array();
public function setVariable($name,$var){ $this->vars[$name]=$var; }
public function getHtml($template)
{ foreach($this->vars as $name => $value)
{ $template = str_replace('{'.$name.'}',$value,$template);
}
return $template;
}
}
//This will not work Fatal error:Class BadTemplate contains 1 abstract
//methos and must therefore be declared abstract (iTemplate::getHtml)
class BadTemplate implements iTemplate
{ private $vars = array();
public function setVariable($name,$var){ $this->vars[$name] = $var; }
}
?>
第19章 类与对象(PHP5)之七:重载(Overloading)
方法调用和成员访问都能通过__call,__get和__set方法被加载。这些方法将只有当你试图访问不包括成员或方法的对象或继承对象时触发。不是所有的重载方法都必须被定义为static.
从PHP 5.1.0开始也可以通过__isset()和__unset()方法逐个重载isset()和unset()函数。
成员函数重载(Member overloading)
void __set ( string name, mixed value )
mixed __get ( string name )
bool __isset ( string name )
void __unset ( string name )
类成员可以通过定义这些指定的特定方法加载类中的自定义代码。$name参数被用来命名变量,这些变量必须是被设置的或返回的。__set()方法的$value参数指定设置$name的对象的值。
例子 19-18.使用__get,__set,__isset和__unset重载的例子
<?php
class Setter
{ public $n;
private $x = array("a" => 1, "b" => 2, "c" => 3);
private function __get($nm)
{ echo "Getting [$nm]\n";
if (isset($this->x[$nm]))
{ $r = $this->x[$nm];
print "Returning: $r\n";
return $r;
} else { echo "Nothing!\n"; }
}
private function __set($nm, $val)
{ echo "Setting [$nm] to $val\n";
if (isset($this->x[$nm]))
{ $this->x[$nm] = $val;
echo "OK!\n";
} else { echo "Not OK!\n"; }
}
private function __isset($nm)
{ echo "Checking if $nm is set\n";
return isset($this->x[$nm]);
}
private function __unset($nm)
{ echo "Unsetting $nm\n";
unset($this->x[$nm]);
}
}
$foo = new Setter();
$foo->n = 1;
$foo->a = 100;
$foo->a++;
$foo->z++;
var_dump(isset($foo->a)); //true
unset($foo->a);
var_dump(isset($foo->a)); //false
// this doesn't pass through the __isset() method
because 'n' is a public property
var_dump(isset($foo->n));
var_dump($foo);
?>
上例将输出: Setting [a] to 100
OK!
Getting [a]
Returning: 100
Setting [a] to 101
OK!
Getting [z]
Nothing!
Setting [z] to 1
Not OK!
Checking if a is set
bool(true)
Unsetting a
Checking if a is set
bool(false)
bool(true)
object(Setter)#1 (2) {
["n"]=>int(1)["x:private"]=>array(2) {
["b"]=>
int(2)
["c"]=>
int(3)
}
}
方法重载(Method overloading)
mixed __call ( string name, array arguments )
类方法可以通过定义这些指定的特定方法加载类中的自定义代码。$name参数被用来命名那些被请求的函数名。Arguments在定义为array的函数的$arguments参数中将被传递。从__call()方法返回的值将被返回方法的访问者。
例子 19-19.使用__call重载的实例
<?php
class Caller
{ private $x = array(1, 2, 3);
public function __call($m, $a)
{ print "Method $m called:\n";
var_dump($a);
return $this->x;
}
}
$foo = new Caller();
$a = $foo->test(1, "2", 3.4, true);
var_dump($a);
?>
上例将输出: Method test called:
array(4) {
[0]=>
int(1)
[1]=>
string(1) "2"
[2]=>
float(3.4)
[3]=>
bool(true)
}
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}第19章 类与对象(PHP5)之八:对象迭代(Object Iteration)
PHP5提供一个为对象定义通过一连串的消息被重述的途径成为可能,例如使用一个foreach语句。默认地,所有可见的属性将用来迭代(反复)。
例子 19-20. 简单的对象迭代(Simple Object Iteration) <?php
class MyClass
{ public $var1 = 'value 1';
public $var2 = 'value 2';
public $var3 = 'value 3';
protected $protected = 'protected var';
private $private = 'private var';
function iterateVisible()
{ echo "MyClass::iterateVisible:\n";
foreach($this as $key => $value){ print "$key => $value\n"; }
}
}
$class = new MyClass();
foreach($class as $key => $value){ print "$key => $value\n"; }
echo "\n";
$class->iterateVisible();
?>
如输出显示,foreach重述能通过全部可见变量被访问。更进一步,你可以实现一个PHP5的指定的内在接口迭代器(Iterator)。允许对象描述什么是对象重述和对象如何被重述的。
第19章 类与对象(PHP5)之九:模式(Patterns)
模式是最好的实践和设计的描述方法。它给普通的编程问题展示一个可变的解决方案。
工厂模式(Factory)
工厂模式允许在运行的时间实例化一个对象。自从工厂模式命名以来,制造一个对象是可能的。
例子 19-23.工厂方法 (Factory Method)
<?php
class Example
{ public static function factory($type)//The factory method
{ if (include_once 'Drivers/'.$type.'.php')
{ $classname = 'Driver_' . $type;
return new $classname;
}else{ throw new Exception ('Driver not found'); }
}
}
?>
在类中允许定义一个驱动器在不工作时被加载的方法。如果类例子是一个数据库抽象类,可以象如下这样加载一个MySQL和SQLite驱动
<?php
$mysql = Example::factory('MySQL'); // Load a MySQL Driver
$sqlite = Example::factory('SQLite'); // Load a SQLite Driver
?> 单独模式(Singleton)
单模式适用于需要一个类的单独接口的情况。最普通的例子是数据库连接。这个模式的实现
允许程序员构造一个通过许多其他对象轻松地访问的单独接口。
例子 19-24.单模式函数(Singleton Function)
<?php
class Example
{ // Hold an instance of the class
private static $instance;
private function __construct()//A private constructor;prevents direct creation of object
{ echo 'I am constructed'; }
public static function singleton()// The singleton method
{ if (!isset(self::$instance))
{ $c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}
// Example method
public function bark() { echo 'Woof!'; }
// Prevent users to clone the instance
public function __clone(){ trigger_error('Clone is not allowed.',E_USER_ERROR); }
}
?>
允许类实例的一个单独接口被重新获得。
<?php
$test = new Example; // This would fail because the constructor is private
$test = Example::singleton();// This will always retrieve a single instance of the class
$test->bark();
$test_clone = clone($test); // This will issue an E_USER_ERROR.
?>
<?php
class MyIterator implements Iterator
{ private $var = array();
public function __construct($array)
{ if (is_array($array)){ $this->var = $array; }
}
public function rewind()
{ echo "rewinding\n";
reset($this->var);
}
public function current()
{ $var = current($this->var);
echo "current: $var\n";
return $var;
}
public function key()
{ $var = key($this->var);
echo "key: $var\n";
return $var;
}
public function next()
{ $var = next($this->var);
echo "next: $var\n";
return $var;
}
public function valid()
{ $var = $this->current() !== false;
echo "valid: {$var}\n";
return $var;
}
}
$values = array(1,2,3);
$it = new MyIterator($values);
foreach ($it as $a => $b) { print "$a: $b\n"; }
?>
上例将输出:
rewinding
current: 1
valid: 1
current: 1
key: 0
0: 1
next: 2
current: 2
valid: 1
current: 2
key: 1
1: 2
next: 3
current: 3
valid: 1
current: 3
key: 2
2: 3
next:
current:
valid: 在定义类时你也可以不必通过简单实现PHP5的IteratorAggregate接口定义所有的迭代器函数。
例19-22.IteratorAggregate对象迭代实现
<?php
class MyCollection implements IteratorAggregate
{ private $items = array();
private $count = 0;
// Required definition of interface IteratorAggregate
public function getIterator()
{ return new MyIterator($this->items);
}
public function add($value)
{
$this->items[$this->count++] = $value;
}
}
$coll = new MyCollection();
$coll->add('value 1');
$coll->add('value 2');
$coll->add('value 3');
foreach ($coll as $key => $val){ echo "key/value: [$key -> $val]\n\n"; }
?>
上例将输出: rewinding
current: value 1
valid: 1
current: value 1
key: 0
key/value: [0 -> value 1]
next: value 2
current: value 2
valid: 1
current: value 2
key: 1
key/value: [1 -> value 2]
next: value 3
current: value 3
valid: 1
current: value 3
key: 2
key/value: [2 -> value 3]
next:
current:
valid:
第19章 类与对象(PHP5) 之十:魔法方法(Magic Methods)
函数名__construct, __destruct (注意构造函数和析构函数), __call, __get, __set, __isset, __unset (注意重载), __sleep, __wakeup, __toString, __set_state, __clone and __autoload是PHP类里边的魔法函数.
函数名 __construct, __destruct(注意构造函数和析构函数), __call, __get, __set, __isset, __unset (see 注意重载), __sleep, __wakeup, __toString, __set_state, __clone and __autoload在PHP类里是魔术的.在任何类里你不能用这些名字给函数命名除非你想与它们的魔术功能性相关联。
注意: PHP储量将所有用__开始的函数名作为魔术函数。推荐地,在PHP里不能用__做函数名除非你想用文件证明是魔术函数。
__sleep()和__wakeup()
serialize() 检查类中是否有魔术名称 __sleep 的函数。如果这样,该函数将在任何序列化之前运行。它可以清除对象并应该返回一个包含有该对象中应被序列化的所有变量名的数组。
使用 __sleep 的目的是关闭对象可能具有的任何数据库连接,提交等待中的数据或进行类似的清除任务。此外,如果有非常大的对象而并不需要完全储存下来时此函数也很有用。
相反地,unserialize() 检查具有魔术名称 __wakeup 的函数的存在。如果存在,此函数可以重建对象可能具有的任何资源。
使用 __wakeup 的目的是重建在序列化中可能丢失的任何数据库连接以及处理其它重新初始化的任务。
例子 19-25. Sleep and wakeup
<?php
class Connection
{ protected $link;
private $server, $username, $password, $db;
public function __construct($server, $username, $password, $db)
{ $this->server = $server;
$this->username = $username;
$this->password = $password;
$this->db = $db;
$this->connect();
}
private function connect()
{ $this->link = mysql_connect($this->server, $this->username, $this->password);
mysql_select_db($this->db, $this->link);
}
public function __sleep() { mysql_close($this->link); }
public function __wakeup() { $this->connect(); }
}
?>
__toString
__toString方法允许一个类决定当它被修改为string类型时是如何起作用的。
例子 19-26.Simple example
<?php
class TestClass// Declare a simple class
{ public $foo;
public function __construct($foo){ $this->foo = $foo; }
public function __toString() { return $this->foo; }
}
$class = new TestClass('Hello');
echo $class;
?>
上例将输出:Hello
__toString方法将只在使用echo()和print()直接地组合时被调用是一个有效的注释方法。
例子 19-27. Cases where __toString is called
<?php
echo $class; //__toString called
echo 'text',$class; //__toString called (still a normal parameter for echo)
echo 'text'.$class; // __toString not called (concatenation operator used first)
echo (string)$class; //__toString not called (cast to string first)
echo "text $class";//__toString not called (cast to string first)
?>
__set_state
从PHP 5.1.0开始static方法是通过var_export()函数来访问类接口的。这个方法的唯一参数是一个包含属性出口的以为array(‘property’=value,…)形式的数组
第19章:类与对象(PHP5)之十一:最终关键字(Final Keyword)
PHP5引入了最终关键字,防止子类使用final从一个重要的方法做定义的前缀。如果类本身已经被定义为final,类将不能被扩展。
例子 19-28.Final方法实例
<?php
class BaseClass
{ public function test()
{
echo "BaseClass::test() called\n";
}
final public function moreTesting()
{
echo"BaseClass::moreTesting() called\n";
}
}
class ChildClass extends BaseClass
{
public function moreTesting()
{
echo "ChildClass::moreTesting() called\n";
}
}
//Results in Fatal error:Cannot override final method BaseClass::moreTesting()
?>
例子 19-29. Final 类实例
<?php
final class BaseClass
{ public function test()
{
echo "BaseClass::test() called\n";
}
//Here it doesn't matter if you specify the function as final or not
final public function moreTesting()
{
echo"BaseClass::moreTesting() called\n";
}
}
class ChildClass extends BaseClass { }
//Results in Fatal error:Class ChildClass may not inherit from final class (BaseClass)
?>
19章 类与对象(PHP5)之十二:对象克隆(Object cloning)
通过完全地复制属性创建一个对象的拷贝不是通常想要的行为。需求的一个好的实例适合于拷贝构造函数,
如果有一个对象描述一个GTK窗口和对象保存这个GTK窗口的资源,当你创建一个副本,你或许想创建一个相同的属性新窗口使用和保存新对象资源的新窗口。另一个例子是当你复制父对象时如果保存一个引用给另一个对象,你想创建其他类的一个新实例来分开拷贝所属的复制品。一个对象的拷贝是使用clone关键字来创建的(如果可能的话可以调用对象的__clone()方法),一个对象的__clone()方法不能被直接声明。
$copy_of_object = clone $object;
当一个对象被克隆时,PHP5将执行一个所有对象的属性的浅拷贝。任何对其它变量引用的属性将只保留引用。如果一个__clone()方法被定义,然后重新创建一个对象的克隆方法来允许任何必需的属性当它需要被改变时调用。
例子 19-30. 克隆一个对象
<?php
class SubObject
{ static $instances = 0;
public $instance;
public function __construct(){ $this->instance=++self::$instances; }
public function __clone() { $this->instance=++self::$instances; }
}
class MyCloneable
{ public $object1;
public $object2;
function __clone()
{
$this->object1=clone($this->object1);//Force a copy of this->object,otherwise it will point to same object.
}
}
$obj = new MyCloneable();
$obj->object1 = new SubObject();
$obj->object2 = new SubObject();
$obj2 = clone $obj;
print("Original Object:\n");
print_r($obj);
print("Cloned Object:\n");
print_r($obj2);
?>
上例将输出:
Original Object:
MyCloneable Object
(
[object1] => SubObject Object
(
[instance] => 1
)
[object2] => SubObject Object
(
[instance] => 2
)
)
Cloned Object:
MyCloneable Object
(
[object1] => SubObject Object
(
[instance] => 3
)
[object2] => SubObject Object
(
[instance] => 2
)
)