PHP对象的使用
对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。一个对象由一组属性和对这组属性进行操作的一组服务组成。
类与对象的关系就如模具和铸件的关系,类的实例化结果就是对象,而对一类对象的抽象就是类
对象继承
PHP 的对象模型使用了继承。继承将会影响到类与类,对象与对象之间的关系。
比如,当扩展一个类,子类就会继承父类所有公有的和受保护的方法。除非子类覆盖了父类的方法,被继承的方法都会保留其原有功能。
除非使用了自动加载,否则一个类必须在使用之前被定义。如果一个类扩展了另一个,则父类必须在子类之前被声明。此规则适用于类继承其它类与接口。
继承示例
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<?phpclass foo{ public function printItem($string) { echo 'Foo: ' . $string . PHP_EOL; } public function printPHP() { echo 'PHP is great.' . PHP_EOL; }} class bar extends foo{ public function printItem($string) { echo 'Bar: ' . $string . PHP_EOL; }} $foo = new foo();$bar = new bar();$foo->printItem('baz'); // Output: 'Foo: baz'$foo->printPHP(); // Output: 'PHP is great' $bar->printItem('baz'); // Output: 'Bar: baz'$bar->printPHP(); // Output: 'PHP is great' ?> |
对象接口
使用接口(interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。
接口是通过 interface 关键字来定义的,就像定义一个标准的类一样,但其中定义所有的方法都是空的。
接口中定义的所有方法都必须是公有,这是接口的特性。
实现(implements)
要实现一个接口,使用 implements 操作符。类中必须实现接口中定义的所有方法,否则会报一个致命错误。类可以实现多个接口,用逗号来分隔多个接口的名称。
实现多个接口时,接口中的方法不能有重名。
接口也可以继承,通过使用 extends 操作符。
类要实现接口,必须使用和接口中所定义的方法完全一致的方式。否则会导致致命错误。
接口中也可以定义常量。接口常量和类常量的使用完全相同,但是不能被子类或子接口所覆盖。
接口示例
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
<?php// 声明一个'iTemplate'接口interface iTemplate{ public function setVariable($name, $var); public function getHtml($template);}// 实现接口// 下面的写法是正确的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; }} // 下面的写法是错误的,会报错,因为没有实现 getHtml():// Fatal error: Class BadTemplate contains 1 abstract methods// and must therefore be declared abstract (iTemplate::getHtml)class BadTemplate implements iTemplate{ private $vars = array(); public function setVariable($name, $var) { $this->vars[$name] = $var; }}?> |
可扩充的接口
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
<?phpinterface a{ public function foo();} interface b extends a{ public function baz(Baz $baz);} // 正确写法class c implements b{ public function foo() { } public function baz(Baz $baz) { }} // 错误写法会导致一个致命错误class d implements b{ public function foo() { } public function baz(Foo $foo) { }}?> |
继承多个接口
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<?phpinterface a{ public function foo();} interface b{ public function bar();} interface c extends a, b{ public function baz();} class d implements c{ public function foo() { } public function bar() { } public function baz() { }}?> |
使用接口常量
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?phpinterface a{ const b = 'Interface constant';} // 输出接口常量echo a::b; // 错误写法,因为常量不能被覆盖。接口常量的概念和类常量是一样的。class b implements a{ const b = 'Class constant';}?> |
接口加上类型约束,提供了一种很好的方式来确保某个对象包含有某些方法。
遍历对象
PHP 5 提供了一种定义对象的方法使其可以通过单元列表来遍历,例如用 foreach 语句。默认情况下,所有可见属性都将被用于遍历。
简单的对象遍历
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?phpclass 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(); ?> |
以上例程会输出:
var1 => value 1
var2 => value 2
var3 => value 3
MyClass::iterateVisible:
var1 => value 1
var2 => value 2
var3 => value 3
protected => protected var
private => private var
如上所示,foreach 遍历了所有其能够访问的可见属性。
更进一步,可以实现 Iterator 接口。可以让对象自行决定如何遍历以及每次遍历时那些值可用。
实现 Iterator 接口的对象遍历
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
<?phpclass 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:
可以用 IteratorAggregate 接口以替代实现所有的 Iterator 方法。IteratorAggregate 只需要实现一个方法 IteratorAggregate::getIterator(),其应返回一个实现了 Iterator 的类的实例。
通过实现 IteratorAggregate 来遍历对象
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<?phpclass 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:
对象复制
在多数情况下,我们并不需要完全复制一个对象来获得其中属性。但有一个情况下确实需要:如果你有一个 GTK 窗口对象,该对象持有窗口相关的资源。你可能会想复制一个新的窗口,保持所有属性与原来的窗口相同,但必须是一个新的对象(因为如果不是新的对象,那么一个窗口中的改变就会影响到另一个窗口)。还有一种情况:如果对象 A 中保存着对象 B 的引用,当你复制对象 A 时,你想其中使用的对象不再是对象 B 而是 B 的一个副本,那么你必须得到对象 A 的一个副本。
对象复制可以通过 clone 关键字来完成(如果可能,这将调用对象的 __clone() 方法)。对象中的 __clone() 方法不能被直接调用。
$copy_of_object = clone $object;
当对象被复制后,PHP 5 会对对象的所有属性执行一个浅复制(shallow copy)。所有的引用属性 仍然会是一个指向原来的变量的引用。
void __clone ( void )
当复制完成时,如果定义了 __clone() 方法,则新创建的对象(复制生成的对象)中的 __clone() 方法会被调用,可用于修改属性的值(如果有必要的话)。
复制一个对象
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
<?phpclass 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->object, 否则仍然指向同一个对象 $this->object1 = clone $this->object1; }} $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
)
)
对象比较
PHP 5 中的对象比较要比 PHP 4 中复杂,所期望的结果更符合一个面向对象语言。
当使用比较运算符(==)比较两个对象变量时,比较的原则是:如果两个对象的属性和属性值 都相等,而且两个对象是同一个类的实例,那么这两个对象变量相等。
而如果使用全等运算符(===),这两个对象变量一定要指向某个类的同一个实例(即同一个对象)。
通过下面的示例可以理解以上原则。
PHP 5 的对象比较
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
<?phpfunction bool2str($bool){ if ($bool === false) { return 'FALSE'; } else { return 'TRUE'; }} function compareObjects(&$o1, &$o2){ echo 'o1 == o2 : ' . bool2str($o1 == $o2) . "\n"; echo 'o1 != o2 : ' . bool2str($o1 != $o2) . "\n"; echo 'o1 === o2 : ' . bool2str($o1 === $o2) . "\n"; echo 'o1 !== o2 : ' . bool2str($o1 !== $o2) . "\n";} class Flag{ public $flag; function Flag($flag = true) { $this->flag = $flag; }} class OtherFlag{ public $flag; function OtherFlag($flag = true) { $this->flag = $flag; }} $o = new Flag();$p = new Flag();$q = $o;$r = new OtherFlag(); echo "Two instances of the same class\n";compareObjects($o, $p); echo "\nTwo references to the same instance\n";compareObjects($o, $q); echo "\nInstances of two different classes\n";compareObjects($o, $r);?> |
以上例程会输出:
Two instances of the same class
o1 == o2 : TRUE
o1 != o2 : FALSE
o1 === o2 : FALSE
o1 !== o2 : TRUE
Two references to the same instance
o1 == o2 : TRUE
o1 != o2 : FALSE
o1 === o2 : TRUE
o1 !== o2 : FALSE
Instances of two different classes
o1 == o2 : FALSE
o1 != o2 : TRUE
o1 === o2 : FALSE
o1 !== o2 : TRUE
对象和引用
在php5 的对象编程经常提到的一个关键点是“默认情况下对象是通过引用传递的”。但其实这不是完全正确的。下面通过一些例子来说明。
php的引用是别名,就是两个不同的变量名字指向相同的内容。在php5,一个对象变量已经不再保存整个对象的值。只是保存一个标识符来访问真正的对象内容。 当对象作为参数传递,作为结果返回,或者赋值给另外一个变量,另外一个变量跟原来的不是引用的关系,只是他们都保存着同一个标识符的拷贝,这个标识符指向同一个对象的真正内容。
引用和对象
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?phpclass A { public $foo = 1;} $a = new A;$b = $a; // $a ,$b都是同一个标识符的拷贝 // ($a) = ($b) = <id>$b->foo = 2;echo $a->foo."\n"; $c = new A;$d = &$c; // $c ,$d是引用 // ($c,$d) = <id> $d->foo = 2;echo $c->foo."\n"; $e = new A; function foo($obj) { // ($obj) = ($e) = <id> $obj->foo = 2;} foo($e);echo $e->foo."\n";?> |
以上例程会输出:
2
2
2
序列化对象 - 在会话中存放对象
所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。unserialize()函数能够重新把字符串变回php原来的值。 序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。
为了能够unserialize()一个对象,这个对象的类必须已经定义过。如果序列化类A的一个对象,将会返回一个跟类A相关,而且包含了对象所有变量值的字符串。 如果要想在另外一个文件中解序列化一个对象,这个对象的类必须在解序列化之前定义,可以通过包含一个定义该类的文件或使用函数spl_autoload_register()来实现。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?php// classa.inc: class A { public $one = 1; public function show_one() { echo $this->one; } } // page1.php: include("classa.inc"); $a = new A; $s = serialize($a); // 把变量$s保存起来以便文件page2.php能够读到 file_put_contents('store', $s); // page2.php: // 要正确了解序列化,必须包含下面一个文件 include("classa.inc"); $s = file_get_contents('store'); $a = unserialize($s); // 现在可以使用对象$a里面的函数 show_one() $a->show_one();?> |
当一个应用程序使用函数session_register()来保存对象到会话中时,在每个页面结束的时候这些对象都会自动序列化,而在每个页面开始的时候又自动解序列化。 所以一旦对象被保存在会话中,整个应用程序的页面都能使用这些对象。但是,session_register()在php5.4.0之后被移除了。
在应用程序中序列化对象以便在之后使用,强烈推荐在整个应用程序都包含对象的类的定义。 不然有可能出现在解序列化对象的时候,没有找到该对象的类的定义,从而把没有方法的类__PHP_Incomplete_Class_Name作为该对象的类,导致返回一个没有用的对象。
所以在上面的例子中,当运行session_register("a"),把变量$a放在会话里之后,需要在每个页面都包含文件classa.inc,而不是只有文件page1.php和page2.php。


相关推荐
深度学习 -- 损失函数
深度残差网络(Deep Residual Networks (ResNets))
深度学习 -- 激活函数
神经网络训练 -- 调整学习速率
生成对抗网络(GAN)改进与发展
生成对抗网络(GAN)优点与缺点
生成对抗网络(GAN)的训练
生成对抗网络(GAN)基本原理
生成模型与判别模型