__construct() 构造函数
php中构造方法是对象创建完成后第一个被对象自动调用的方法。在每个类中都有一个构造方法,如果没有显示地声明它,那么类中都会默认存在一个没有参数且内容为空的构造方法。
1.构造方法的作用:
通常构造方法被用来执行一些有用的初始化任务,如对成员属性在创建对象时赋予初始值。
2.构造方法的在类中的声明格式:
function __constrct([参数列表]){
方法体 //通常用来对成员属性进行初始化赋值
}
3.在类中声明构造方法需要注意的事项:
1).在同一个类中只能声明一个构造方法,原因是,PHP不支持构造函数重载。 2).构造方法名称是以两个下画线开始的__construct()
4.代码示例:
class Constructs
{
private $name;
private $age;
private $sex;
public function __construct($name,$age,$sex)
{
$this->name = $name;
$this->sex = $sex;
$this->age = $age;
}
public function info(){
return '我是'.$this->name.' , 今年 '.$this->age.' 岁了 , 我是'.$this->sex.'孩子喔。';
}
}
$info = new Constructs('小洪帽','15','男');
print_r($info->info());
__destruct() 析构函数
通过上面的讲解,现在我们已经知道了什么叫构造方法。那么与构造方法对应的就是析构方法。
析构方法允许在销毁一个类之前执行的一些操作或完成一些功能,比如说关闭文件、释放结果集等。
析构方法是PHP5才引进的新内容。
析造方法的声明格式与构造方法 __construct()
比较类似,也是以两个下划线开始的方法 __destruct()
,这种析构方法名称也是固定的。
1.析构方法的声明格式:
public function __destruct(){
//注意:析构函数不能带有任何参数。
}
2.析构方法的作用:
一般来说,析构方法在PHP中并不是很常用,它属类中可选择的一部分,通常用来完成一些在对象销毁前的清理任务。
3.代码示例:
class Constructs
{
private $name;
private $age;
private $sex;
public function __construct($name,$age,$sex)
{
$this->name = $name;
$this->sex = $sex;
$this->age = $age;
}
/**
* [__destruct 析构方法]
*/
public function __destruct(){
echo '我们下次再见,Bey~';
}
public function info(){
return '我是'.$this->name.' , 今年 '.$this->age.' 岁了 , 我是'.$this->sex.'孩子喔。';
}
}
$info = new Constructs('小洪帽','15','男');
unset($info);//销毁上面创建的对象 然后输出:我们下次再见,Bey~
__call() 在对象中调用一个不可访问方法时调用
该方法有两个参数,第一个参数 $function_name
会自动接收不存在的方法名,第二个 $arguments
则以数组的方式接收不存在方法的多个参数。
1.使用格式:
public function __call(){
}
2.方法的作用:
1)为了避免当调用的方法不存在时产生错误,而意外的导致程序中止,可以使用 __call() 方法来避免。
2)该方法在调用的方法不存在时会自动调用,程序仍会继续执行下去。
3.代码示例:
class Constructs
{
public function info(){
echo 'Hello World!';
}
/**
* [__call 被调用不存在的方法时调用此方法]
* @param [type] $function_name [被调用的方法名]
* @param [type] $arguments [传入的参数]
* @return [type] [description]
*/
public function __call($function_name,$arguments){
print_r($function_name);
echo '<br>';
print_r($arguments);
echo '<br>';
}
}
$info = new Constructs();
$info->index('首页');
$info->user('小洪帽',25);
$info->info();
__callStatic() 静态方式调用一个不可访问方法时调用
此方法与上面所说的 __call() 功能除了 __callStatic() 是未静态方法准备的之外,其它都是一样的。
代码示例:
class Constructs
{
public function info(){
echo 'Hello World!';
}
/**
* [__callStatic 静态方式调用一个不可访问方法时调用]
* @param [type] $function_name [被调用的方法名]
* @param [type] $arguments [传入的参数]
* @return [type] [description]
*/
public static function __callStatic($function_name,$arguments){
print_r($function_name);
echo '<br>';
print_r($arguments);
echo '<br>';
}
}
$info = new Constructs();
//注意:静态方式调用 -> 更改为 :: ;否则会报出(不推荐:非静态方法)的错误
$info::index('首页');
$info::user('小洪帽',25);
$info->info();
__get() 获得一个类的成员变量时调用
在 php 面向对象编程中,类的成员属性被设定为 private
后,如果我们试图在外面调用它则会出现“不能访问某个私有属性”的错误。那么为了解决这个问题,我们可以使用魔术方法 __get()
。
1.作用:
在程序运行过程中,通过它可以在对象的外部获取私有成员属性的值。
2.代码示例:
class Constructs
{
private $name;
private $age;
public function __construct($name='',$age=16){
$this->name = $name;
$this->age = $age;
}
/**
* [__get 获得一个类的成员变量时调用]
* @param [type] $getName [调用名,在类外面调用 $info->age ; age 就是这个调用名]
* @return [type] [description]
*/
public function __get($getName){
if($getName == 'age'){
if($this->age >= 16){
return $this->age;
}else{
return '你还未成年!';
}
}else{
return $this->$getName;
}
}
}
$info = new Constructs('小洪帽',15);
echo $info->name; //输出:小洪帽
echo $info->age; //输出:你还未成年!
__set() 设置一个类的成员变量时调用
__set( $keys, $values) 方法用来设置私有属性, 给一个未定义的属性赋值时,此方法会被触发,传递的参数是被设置的属性名和值。
代码示例:
class Constructs
{
private $name;
private $age;
public function __construct($name='',$age=16){
$this->name = $name;
$this->age = $age;
}
/**
* [__set 设置一个类的成员变量时调用]
* @param [type] $keys [key]
* @param [type] $values [value]
*/
public function __set($keys,$values){
$this->$keys = $values;
}
public function index(){
echo $this->name.'今年'.$this->age.'岁了';
}
}
$info = new Constructs('小洪帽',15);
$info->name = 'PHP之路';
$info->age = '20';
$info->index(); // 输出:PHP之路今年20岁了
__isset() 当对不可访问属性调用isset()或empty()时调用
在看这个方法之前我们看一下isset()
函数的应用,isset()
是测定变量是否设定用的函数,传入一个变量作为参数,如果传入的变量存在则传回true,否则传回false。
那么如果在一个对象外面使用isset()
这个函数去测定对象里面的成员是否被设定可不可以用它呢?
分两种情况,如果对象里面成员是公有的,我们就可以使用这个函数来测定成员属性,如果是私有的成员属性,这个函数就不起作用了,原因就是因为私有的被封装了,在外部不可见。那么我们就不可以在对象的外部使用isset()
函数来测定私有成员属性是否被设定了呢?当然是可以的,但不是一成不变。你只要在类里面加上一个__isset()
方法就可以了,当在类外部使用isset()
函数来测定对象里面的私有成员是否被设定时,就会自动调用类里面的__isset()
方法了帮我们完成这样的操作。
1.作用:
当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。
2.代码示例:
class Constructs
{
private $name;
private $age;
public function __construct($name='',$age=16){
$this->name = $name;
$this->age = $age;
}
/**
* [__isset 当对不可访问属性调用isset()或empty()时调用]
* @param [type] $content [调用名]
* @return boolean [description]
*/
public function __isset($content){
echo '当在类外部使用isset()函数测定私有成员{$content}时,自动调用<br>';
echo isset($this->$content).'<br>';
}
}
$info = new Constructs('小洪帽',15);
echo isset($info->age);
echo empty($info->name);
__unset() 当对不可访问属性调用unset()时被调用
看这个方法之前呢,我们也先来看一下 unset()
函数,unset()
这个函数的作用是删除指定的变量且传回true,参数为要删除的变量。
那么如果在一个对象外部去删除对象内部的成员属性用unset()
函数可以吗?
这里自然也是分两种情况:
1、 如果一个对象里面的成员属性是公有的,就可以使用这个函数在对象外面删除对象的公有属性。
2、 如果对象的成员属性是私有的,我使用这个函数就没有权限去删除。
虽然有以上两种情况,但我想说的是同样如果你在一个对象里面加上__unset()
这个方法,就可以在对象的外部去删除对象的私有成员属性了。在对象里面加上了__unset()
这个方法之后,在对象外部使用“unset()”函数删除对象内部的私有成员属性时,对象会自动调用__unset()
函数来帮我们删除对象内部的私有成员属性。
代码示例:
class Constructs
{
private $name;
private $age;
public function __construct($name='',$age=16){
$this->name = $name;
$this->age = $age;
}
/**
* [__unset 当对不可访问属性调用unset()时被调用]
* @param [type] $content [调用名]
*/
public function __unset($content){
echo "当在类外部使用unset()函数来删除私有成员时自动调用的<br>";
}
}
$info = new Constructs('小洪帽',15); //初始值
unset($info->name);
__sleep() 执行serialize()时,先会调用这个函数
serialize()
函数会检查类中是否存在一个魔术方法 __sleep()
。如果存在,则该方法会优先被调用,然后才执行序列化操作。
此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。
如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。
1.注意:
__sleep() 不能返回父类的私有成员的名字。这样做会产生一个 E_NOTICE 级别的错误。可以用 Serializable 接口来替代。
2.作用:
__sleep() 方法常用于提交未提交的数据,或类似的清理操作。同时,如果有一些很大的对象,但不需要全部保存,这个功能就很好用。
3.代码示例:
class Constructs
{
private $name;
private $age;
public function __construct($name='',$age=16){
$this->name = $name;
$this->age = $age;
}
public function __sleep(){
echo "当在类外部使用serialize()时会调用这里的__sleep()方法<br>";
$this->name = base64_encode($this->name);
return array('name', 'age'); // 这里必须返回一个数值,里边的元素表示返回的属性名称
}
}
$info = new Constructs('小洪帽',15); //初始值
echo serialize($info);
__wakeup() 执行unserialize()时,先会调用这个函数
如果说 __sleep()
是白的,那么 __wakeup()
就是黑的了。
那么为什么呢?因为:
与之相反,unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。
1.作用:
__wakeup() 经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。
2.代码示例:
class Constructs
{
private $name;
private $age;
public function __construct($name='',$age=16){
$this->name = $name;
$this->age = $age;
}
public function __sleep(){
echo "当在类外部使用serialize()时会调用这里的__sleep()方法<br>";
$this->name = base64_encode($this->name);
return array('name', 'age'); // 这里必须返回一个数值,里边的元素表示返回的属性名称
}
public function __wakeup(){
echo "当在类外部使用unserialize()时会调用这里的__wakeup()方法<br>";
$this->name = '小洪帽';
$this->age = 18;
// 这里不需要返回数组
}
}
$info = new Constructs('小洪帽',15); //初始值
echo serialize($info);
echo '<br><br>';
print_r(unserialize(serialize($info)));
__toString() 类被当成字符串时的回应方法
1.作用:
__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。
2.注意:
此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。
3警告:
不能在 __toString() 方法中抛出异常。这么做会导致致命错误。
4.代码示例:
class Constructs
{
private $name;
private $age;
public function __construct($name='',$age=16){
$this->name = $name;
$this->age = $age;
}
public function __toString(){
return 'Hello World!';
}
}
$info = new Constructs('小洪帽'); //初始值
echo $info;
那么如果类中没有 __toString() 这个魔术方法运行会发生什么呢?
页面会报一个致命错误,这是语法所不允许的。
__invoke() 调用函数的方式调用一个对象时的回应方法
1.作用:
当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。
2.注意:
本特性只在 PHP 5.3.0 及以上版本有效。
3.代码示例:
class Constructs
{
private $name;
private $age;
public function __construct($name='',$age=16){
$this->name = $name;
$this->age = $age;
}
public function __invoke(){
echo '这是一个对象';
}
}
$info = new Constructs('小洪帽'); //初始值
$info();
__set_state() 调用var_export()导出类时,此静态方法会被调用
1.作用:
自 PHP 5.1.0 起,当调用 var_export() 导出类时,此静态方法会被自动调用。
2.参数:
本方法的唯一参数是一个数组,其中包含按 array('property' => value, ...) 格式排列的类属性。
3.下面我们先来看看在没有加 __set_state() 情况按下,代码及运行结果如何:
class Constructs
{
public $name;
public $age;
public function __construct($name='',$age=16){
$this->name = $name;
$this->age = $age;
}
}
$info = new Constructs('小洪帽'); //初始值
var_export($info);
//输出结果:Constructs::__set_state(array( 'name' => '小洪帽', 'age' => 16, ))
4.加了 __set_state() 之后:
class Constructs
{
public $name;
public $age;
public function __construct($name='',$age=16){
$this->name = $name;
$this->age = $age;
}
public static function __set_state($an_array){
$a = new Constructs();
$a->name = $an_array['name'];
return $a;
}
}
$info = new Constructs('小洪帽'); //初始值
$info->name = 'PHP';
var_export($info);
//输出结果:Constructs::__set_state(array( 'name' => 'PHP', 'age' => 16, ))
__clone() 克隆方法,当对象复制完成时调用
在多数情况下,我们并不需要完全复制一个对象来获得其中属性。但有一个情况下确实需要:如果你有一个 GTK 窗口对象,该对象持有窗口相关的资源。你可能会想复制一个新的窗口,保持所有属性与原来的窗口相同,但必须是一个新的对象(因为如果不是新的对象,那么一个窗口中的改变就会影响到另一个窗口)。还有一种情况:如果对象 A 中保存着对象 B 的引用,当你复制对象 A 时,你想其中使用的对象不再是对象 B 而是 B 的一个副本,那么你必须得到对象 A 的一个副本。
1.作用:
对象复制可以通过 clone 关键字来完成(如果可能,这将调用对象的 __clone() 方法)。对象中的 __clone() 方法不能被直接调用。
2.语法:
$copy_of_object = clone $object;
3.注意:
当对象被复制后,PHP 5 会对对象的所有属性执行一个浅复制(shallow copy)。所有的引用属性 仍然会是一个指向原来的变量的引用。
当复制完成时,如果定义了 __clone() 方法,则新创建的对象(复制生成的对象)中的 __clone() 方法会被调用,可用于修改属性的值(如果有必要的话)。
4.代码示例:
class Constructs
{
public $name;
public $age;
public function __construct($name='',$age=16){
$this->name = $name;
$this->age = $age;
}
public function __clone(){
echo __METHOD__."你正在克隆对象<br>";
}
}
$info = new Constructs('小洪帽'); //初始值
$clone = clone $info;
var_dump('info:');
var_dump($info);
echo '<br>';
var_dump('clone:');
var_dump($clone);
//输出:
//Constructs::__clone你正在克隆对象
//string(5) "info:" object(Constructs)#1 (2) { ["name"]=> string(9) "小洪帽" ["age"]=> int(16) }
//string(6) "clone:" object(Constructs)#2 (2) { ["name"]=> string(9) "小洪帽" ["age"]=> int(16) }
__autoload() 尝试加载未定义的类
在魔术函数 __autoload()
方法出现以前,如果你要在一个程序文件中实例化100个对象,那么你必须用include或者require包含进来100个类文件,或者你把这100个类定义在同一个类文件中 —— 相信这个文件一定会非常大,然后你就痛苦了。
但是有了 __autoload()
方法,以后就不必为此大伤脑筋了,这个类会在你实例化对象之前自动加载制定的文件。
1.作用:
你可以通过定义这个函数来启用类的自动加载。
2.看看以往的实现方式:
require_once('project/class/A.php');
require_once('project/class/B.php');
require_once('project/class/C.php');
if (条件A) {
$a = new A();
$b = new B();
$c = new C();
// … 业务逻辑
} else if (条件B) {
$a = new A();
$b = new B();
// … 业务逻辑
}
看到了吗?不用100个,只是3个看起来就有点烦了。而且这样就会有一个问题:如果脚本执行“条件B”这个分支时,C.php这个文件其实没有必要包含。因为,任何一个被包含的文件,无论是否使用,均会被php引擎编译。如果不使用,却被编译,这样可以被视作一种资源浪费。更进一步,如果C.php包含了D.php,D.php包含了E.php。并且大部分情况都执行“条件B”分支,那么就会浪费一部分资源去编译C.php,D.php,E.php三个“无用”的文件。
3.使用 __autoload()
方式:
function __autoload($className) {
$filePath = "project/class/".$className.".php"; //路径 $className 为类名
if (is_readable($filePath)) {
require($filePath);
}
}
$a = new A();
$b = new B();
$c = new C();
PHP7.2以上版本使用上面的__autoload()会报错
报错:Deprecated: __autoload() is deprecated, use spl_autoload_register() instead in
解决:把PHP版本降低到7.2以下或者使用下面方法
spl_autoload_register('autoload');
function autoload($className){
$filePath = "project/class/".$className.".php";
print_r($filePath);die;
if (is_readable($filePath)) {
require($filePath);
}
}
$a = new A();
$b = new B();
$c = new C();
__debugInfo() 打印所需调试信息
1.注意:
该方法在PHP 5.6.0及其以上版本才可以用,如果你发现使用无效或者报错,请查看啊你的版本。
2.代码示例
class Constructs
{
private $prop;
public function __construct($val){
$this->prop = $val;
}
public function __debugInfo(){
return ['propSquared'=>$this->prop ** 2];
//注意:这里的 `**` 是乘方的意思,也是在PHP5.6.0及其以上才可以使用,详情请查看PHP手册
}
}
$info = new Constructs(10); //初始值
var_dump($info);
//结果:object(Constructs)#1 (1) { ["propSquared"]=> int(100) }
总结:
以上就是PHP中我了解到的魔术方法了,常用的包括 __set()
__get()
__autoload
() 等应该熟悉,其他的了解也没有关系,毕竟知识不怕多嘛。
好了,有兴趣的或者我这里没有说明白的,可以参考啊官方文档。
PHP在线手册地址:http://php.net/manual/zh/