PHP中有一些方法,非常的神奇,在PHP手册中被称之为Magic Methods,其中大家比较熟知的应该是__construct和__destruct,这在普通的类的定义中会经常用到,一个是构造函数,一个是析构函数。
除此以外,还有下面一些函数
__call
__callStatic
__get
__set
__isset
__unset
__sleep
__wakeup
__toString
__invoke
__set_state
__clone
在这个系列中我会将这些Magic Methods一个一个加以说明,今天要说明的是__construct,废话不多说了,进入正题吧。
__construct:从php5开始,开发者可以为类定义构造函数了,如果类定义了这个方法,新建该类的对象时会自动调用该方法,在php中是不能通过c++的利用同样函数名,不同函数参数的方式进行构造函数的重载的,比如说下面的代码
12345678910111213141516171819202122232425
class TestClass { protected $a = ''; protected $b = ''; public function __construct($a = null) { echo "Enter constructor 1"; $this->a = $a; } public function __construct($a = null, $b = null) { echo 'Enter constructor 2'; $this->a = $a; $this->b = $b; } }
上面的代码执行会出现下面的错误。
Fatal error: Cannot redeclare TestClass::__construct() in
也就是说无法通过定义两个方法名的方式来进行构造函数的重载,那就没有办法重载构造函数了么,不是,其实php的每一个函数都拥有可变参数。
12345678910111213141516171819202122232425262728293031323334353637
class TestClass { protected $a = ''; protected $b = ''; public function __construct($a = null) { echo "Enter constructor 1\n"; $this->a = $a; $args = func_get_args(); if (count($args) > 1) { $this->b = $args[1]; } } public function __get($key) { return $this->$key; } } $tc1 = new TestClass('test1'); $tc2 = new TestClass('test1', 'test2'); echo "tc1.a=$tc1->a tc1.b=$tc1->b\n"; echo "tc2.a=$tc2->a tc2.b=$tc2->b\n";
运行脚本得到的结果如下:
1234567
Enter constructor 1 Enter constructor 1 tc1.a=test1 tc1.b= tc2.a=test1 tc2.b=test2
这样就实现了构造函数的重载。如果类的定义中没有__construct方法,php5为了向前兼容,会将与类名同名的函数作为构造函数使用,如类:
1234567891011121314151617181920212223242526272829303132333435
class TTClass { protected $a = ''; protected $b = ''; public function TTClass($a = null) { $this->a = $a; $args = func_get_args(); if (count($args) > 1) { $this->b = $args[1]; } } public function __get($key) { return $this->$key; } } $tt1 = new TTClass('tt1a'); $tt2 = new TTClass('tt2a', 'tt2b'); echo "tt1.a=$tt1->a tt1.b=$tt1->b\n"; echo "tt2.a=$tt2->a tt2.b=$tt2->b\n";
输出结果如下:
123
tt1.a=tt1a tt1.b= tt2.a=tt2a tt2.b=tt2b
利用与类同名方法的函数当做构造函数来使用时,需要注意的是重载构造函数时需要与上面__construct重载的方法一样。
如果子类定义了构造函数,那么父类的构造函数不会隐式的自动调用,如果需要在子类初始化是调用父类的构造函数需要在子类的构造函数中加入parent::__construct。如下例
12345678910111213141516171819202122232425262728293031323334353637383940414243
class ParentClass { public function __construct() { echo "Now in ParentClass's constructor.\n"; echo "Parent's Constructor\n"; } } class ChildClassA extends ParentClass { public function __construct() { echo "Now in ChildClassA constructor.\n"; echo "ChildA's Constructor\n"; } } class ChildClassB extends ParentClass { public function __construct() { echo "Now in ChildClassB's constructor.\n"; parent::__construct(); echo "ChildB's Constructor\n"; } } $parent = new ParentClass(); $childA = new ChildClassA(); $childB = new ChildClassB();
脚本执行的结果如下:
123456789101112131415
Now in ParentClass's constructor. Parent's Constructor Now in ChildClassA constructor. ChildA's Constructor Now in ChildClassB's constructor. Now in ParentClass's constructor. Parent's Constructor ChildB's Constructor
上面的结果,ChildClassA的初始化过程中没有任何parent的信息,也就是说没有自动调用父类的构造函数,而在ChildClassB的构造函数中显式调用了父类的构造函数,因此也有了父类构造函数的信息打印出来。从上面的结果可以证明父类的构造函数不会隐式自动被调用,需要编码实现。
至此,__construct相关的内容大致就说完了,也许日后会遇到或者想到什么再对此进行补充吧,今天就到这里了。