魔术方法
是指某些情况下,会自动调用的方法,称为魔术方法
php面向对象中,提供了这几个魔术方法
他们的特点 都是以双下滑线开头的
__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state() 和 __clone()
__construct: 构造方法__destruct: 析构方法__clone(): 克隆方法,当对象被克隆时,将会自动调用
接下来讲几个,在项目中,尤其是自己想写框架时,非常实用
__clone
<?php
class Human{
public $age = 24;
public function __clone(){
echo "有人克隆人!假冒";
}
}
$lisi = new Human();
$zhangsan = clone $lisi;
下面内容可直接替换原文中的 ## __call() 和 ## __callStatic() 两个空小节。
__call()
当调用对象中不存在的方法,或调用的方法权限不足(private/protected)时,PHP 会自动调用 __call()。它接收两个参数:
$method:被调用的方法名$args:调用时传入的参数数组
<?php
class Human
{
public $name = 'lily';
private function money()
{
return '30两';
}
public function __call($method, $args)
{
echo "你调用了不存在或不可访问的方法:{$method}<br/>";
echo "参数是:";
print_r($args);
echo "<br/>";
}
}
$lily = new Human();
$lily->say('hello', 123); // 方法不存在,触发 __call
$lily->money(); // private 方法,外部调用,触发 __call
运行结果:
你调用了不存在或不可访问的方法:say
参数是:Array ( [0] => hello [1] => 123 )
你调用了不存在或不可访问的方法:money
参数是:Array ( )
实际项目中,__call() 常用于动态代理、ORM 动态查询、API SDK 等。例如:
<?php
class UserModel
{
public function __call($method, $args)
{
if (strpos($method, 'findBy') === 0) {
$field = lcfirst(substr($method, 6));
$value = isset($args[0]) ? $args[0] : '';
echo "SELECT * FROM user WHERE {$field} = '{$value}'";
return;
}
throw new Exception("方法 {$method} 不存在");
}
}
$user = new UserModel();
$user->findByName('lily'); // 动态拼出 findByName 对应的查询
__callStatic()
当调用类中不存在或不可访问的静态方法时,PHP 会自动调用 __callStatic()。它同样接收两个参数:方法名和参数数组。注意:__callStatic() 必须声明为 public static。
<?php
class Human
{
private static function money()
{
return '30两';
}
public static function __callStatic($method, $args)
{
echo "你调用了不存在或不可访问的静态方法:{$method}<br/>";
echo "参数是:";
print_r($args);
echo "<br/>";
}
}
Human::say('hello', 123); // 静态方法不存在,触发 __callStatic
Human::money(); // private static 方法,外部调用,触发 __callStatic
运行结果:
你调用了不存在或不可访问的静态方法:say
参数是:Array ( [0] => hello [1] => 123 )
你调用了不存在或不可访问的静态方法:money
参数是:Array ( )
实际项目中,__callStatic() 常用于门面类(Facade)、静态代理等。例如:
<?php
class CacheDriver
{
public function get($key)
{
return "缓存值:{$key}";
}
}
class Cache
{
public static function __callStatic($method, $args)
{
$driver = new CacheDriver();
if (!method_exists($driver, $method)) {
throw new Exception("缓存方法 {$method} 不存在");
}
return call_user_func_array([$driver, $method], $args);
}
}
// 实际调用的是 CacheDriver 实例的 get() 方法
echo Cache::get('username'); // 缓存值:username
注意:
__call()处理对象方法调用,__callStatic()处理静态方法调用;如果方法存在且当前作用域可访问,则不会触发这两个魔术方法。
__get()
当我们调用一个权限上不允许调用的属性时或不存在的属性时,
__get魔术方法会自动 调用,
并且自动传参,参数值是被访问的属性
<?php
/*
class Human{
private $money = '30两';
protected $age = 23;
public $name = 'lily';
}
$lily = new Human();
echo $lily->name;//lily
echo $lily->age;//权限错误
echo $lily->sister;//undefined property
*/
class Human{
private $money = '30两';
protected $age = 23;
public $name = 'lily';
public function __get($property){
echo "你想访问我的",$property,'属性 :)<br/>';
}
}
$lily = new Human();
echo $lily->name,"<br />";//lily
echo $lily->age;//你想访问我的age属性
echo $lily->money;//你想访问我的money属性
echo $lily->sister;//你想访问我的sister属性
__set()
如下:
当为无权操作的属性赋值时,
或不存在的属性赋值时
__set方法自动调用
且自动传2个参数,属性/属性值
例如: $hmm->age = 444 --无权---> __set('age',444)
<?php
$lily->aaa = 1111;
$lily->bbb = 2222;
print_r($lily);
/*
竟然给加上了属性,
其实,对象就是一个属性集,在js中更为明显
如果这么随便就加了属性,导致这个对象的属性过多,不好管理
*/
class Stu{
private $money = "30两";
protected $age = 24;
public $name = "Hmm";
public function __set($a,$b){
echo "你想设置我的",$a,'属性,<br/>';
echo '且其值是',$b,'<br />';
}
}
echo "<hr>";
$hmm = new Stu();
$hmm->aaa = 1111;
$hmm->money = '40两';
$hmm->age = 444;
print_r($hmm);
echo "<br/>";
$hmm->name = "HanMM";
print_r($hmm);
__isset() 和 __unset()
__isset() 方法
当isset()判断对象不可见的属性(protected/private/不存在)
会引发__isset()来执行
当用unset销毁对象的不可见属性时,
会引发__unset();
echo "<hr>";
class Dog{
public $leg = 4;
protected $bone = "猪腿骨";
public function __isset($p){
echo "<br/>你想判断我的",$p,'属性存不存在?';
}
public function __unset($p){
echo "<br/>你想去掉我的",$p,'<br>';
}
}
$hua = new Dog();
if (isset($hua->leg) ){
echo "{$hua->leg}";
}
if (isset($hua->tail) ){
echo "{$hua->tail}";
}else{
echo "<br/>答:不存在";
}
/*
问: isset($obj->key) 属性,为真,
能说明 类有一个xyz属性吗?
答: 不能
*/
echo "<hr />";
echo "__unset测试<br/>";
print_r($hua);
unset($hua->leg);
print_r($hua);
unset($hua->tail);
unset($hua->bone);
/*
unset($hua->tail) ----没有tail属性--> __unset('tail')
*/
案例一
<?php
class UserModel {
protected $email = "user@163.com";
protected $data = array();
public function __set($k,$v) {
//$this->$k = $v,并没有真正赋成自己的属性
$this->data[$k] = $v;//而是放在一个数组里
}
public function __get($p) {
$p = $this->data[$p] ? $this->data[$p] : null;
return isset($p);
}
public function __unset($p){
unset($this->data[$p]);
}
public function __isset($p){
return isset($this->data[$p]);
}
public function add(){
$sql = 'insert into table (';
$sql .= implode(',',array_keys($this->data));
$sql .= ') values (\'';
$sql .= implode("','",array_values($this->data));
$sql .= "')";
return $sql;
}
}
echo "<pre>";
$userModel = new UserModel();
print_r($userModel);
$userModel->username = "lisi";
$userModel->eamil = "lisi@126.com";
print_r($userModel);
//echo $userModel->add();
unset($userModel->eamil);
print_r($userModel);
echo "</pre>";