php的魔术方法__get(),__set(),__call(),__callStatic()以及static延迟静态绑定

本文介绍了PHP5中引入的魔术方法,包括__get(), __set(), __call() 和 __callStatic(),这些方法如何简化了类属性和方法的访问及管理。通过具体示例展示了这些方法的实际应用。

php5之后实现了一些魔术方法还是比较有意思的,之前一直用面向过程的编程方法,对oop研究的比较少,最近在看oop的东西,还是比较有意思的。

魔术方法这些东西,感觉很大一部分就是为了偷懒用的,记得最早写php的时候,那时候做博客,用的是国外的一个叫lifetype的开源框架,那时候还是php4.3,但是那个框架里全部实现了对象,所有的数据都被封装到对象中。

于是当从db里select出来一堆东西之后,还要逐个循环封装成对象,每一个字段也要实现getField()和getField()方法,写起来还真有点麻烦,感觉就是在做重复性的工作。

那么__get(),__set(),__call(),__callStatic()这几个魔术方法的诞生,就完全解决了这个问题。

__get()和__set()是针对类中属性的而__call()是针对方法的,__callStatic() 是针对静态类的方法。

一、__get()和__set()魔术方法:

当实例化一个对象后,调用类中不存在或者没有权限访问的属性的时候,php会默认调用__get()方法,这样做,不仅可以少些很多代码,让结构更清晰,而且也提供了一条外部访问类的私有成员的一种方法。

比如:

<?php
class testGet
{
  private $name = 'test';
}
$test = new testGet();
$test->name;

上面的代码,如果我们运行,会报一个错误:PHP Fatal error:  Cannot access private property testGet::$name in /Library/WebServer/Documents/workspace/learn/call/a.php on line 7

但是我们修改一下,通过__get()方法就可以访问

<?php
class testGet
{
  private $name = 'test';

  function __get($property) {
    if ( isset($this->$property) ) 
      return $this->$property;
    else
      return NULL;
  }
}
$test = new testGet();
echo $test->name . PHP_EOL;
代码改成这样之后我们再访问就没有问题。 

注意:如果把属性定义成是static的,那么通过__get()访问也会报错。原因是static的成员,是属于类本身的,不因为实例化而改变,可以自己测试。

利用__set()方法,可以禁止动态创建类属性,这样可以很好的避免给后来开发者,或者程序维护者带来不必要的麻烦。

funciton __set($property) {

//$property接收的是属性的名字

}

是实话,oop这个东西的设计,会掺杂很多设计者自己的思想,如果没有文档,后来者去读代码还是很费劲的,当然和后来者的水平也有很大关系。

下面是一个__get和__set配合使用的例子:


<?php
class testGet
{
  private $name = 'test';

  function __get($property) {
    if ( isset($this->$property) ) 
      return $this->$property;
    else
      return NULL;
  }
  
  public function __set($property, $value) {
    if ( isset($this->$property) )
      $this->$property = $value;
    else
      return NULL;
  }
}
$test = new testGet();
$test->name = 'my test name';
echo $test->name . PHP_EOL;

function __set($property, $value) {

//$property接收的属性的名字

//$value接收的是属性的值

}

二、__call()和__callStatic()方法:

当对象调用类中一个不存在或者没有权限访问的方法的时候,就会自动调用__call()方法。

记得以前有个同事问我,tp框架中为什么有很多底层的方法没有,但是在上层还能调用,其实就是__call()这个方法在起作用。

如果你不知道这个方法,那么肯定会很疑惑,而且问题也不好定位。

<?php
abstract class Obj 
{
        private $objData        = array();
        /** 
         * __call魔术方法,如果对象请求的方法不存在或者没有权限访问的时候
         * 调用魔术方法
         */
        public function __call($name, $args) {
            $field = preg_match('/^get(\w+)/', $name, $matches);
            if ( $field && $matches[1] )
                return $this->objData[strtolower($matches[1])];
            $field = preg_match('/^set(\w+)/', $name, $matches);
            if ( $field && $matches[1] ) { 
                return $this->objData[strtolower($matches[1])] = $args[0];
            }   
        }   
}
class User extends Obj
{
        
}
$user = new User();
$user->setName('test');
echo $user->getName();
User类什么都没干,但是通过继承类的__call()方法,把所有的事都做了(getName和setName)。

function __call($methodName, $args) {

//$methodName调用的方法名

//$args传递的参数数组

}

和__call()对应的是__callStatic()方法,是位静态类的静态方法服务的。

例子:

<?php
    abstract class Obj 
    {
        private static $objData        = array();
        /** 
         * __call魔术方法,如果对象请求的方法不存在或者没有权限访问的时候
         * 调用魔术方法
         */
        public static function __callStatic($name, $args) {
            $field = preg_match('/^get(\w+)/', $name, $matches);
            if ( $field && $matches[1] )
                return self::$objData[strtolower($matches[1])];
            $field = preg_match('/^set(\w+)/', $name, $matches);
            if ( $field && $matches[1] ) { 
                return self::$objData[strtolower($matches[1])] = $args[0];
            }   
        }   
    }
    class User extends Obj 
    {
    
    }
    User::setName('test');
    echo User::getName() . PHP_EOL;
三、延迟静态绑定:static这个对象

### PHP 魔术方法概述 PHP魔术方法是一组特殊的类成员函数,这些方法的名字以双下划线 `__` 开头。它们的主要作用是在特定条件下被触发执行,从而提供了一种灵活的方式来处理各种场景下的需求。 #### 常见的魔术方法及其功能 以下是几个常见的魔术方法以及其具体用途: 1. **`__construct()`** - 当创建一个新的对象实例时会自动调用此方法。 - 它通常用于初始化对象的状态或设置默认属性值。 - 示例代码如下: ```php class Example { public function __construct() { echo "对象已创建"; } } $obj = new Example(); // 输出: 对象已创建 ``` 2. **`__destruct()`** - 此方法会在对象销毁之前被调用。 - 主要用来清理资源或者保存数据到文件等操作。 - 示例代码如下: ```php class Example { public function __destruct() { echo "对象即将销毁"; } } $obj = new Example(); unset($obj); // 输出: 对象即将销毁 ``` 3. **`__get()` 和 `__set()`** - 这两个方法分别用于访问和修改未定义或不可达的私有/受保护属性。 - 如果试图读取不存在的属性,则会触发 `__get()`;如果尝试写入这样的属性则会触发 `__set()`。 - 示例代码如下: ```php class Example { private $data; public function __set($key, $value) { $this->data[$key] = $value; } public function __get($key) { return isset($this->data[$key]) ? $this->data[$key] : null; } } $example = new Example(); $example->test = 'hello'; // 调用了 __set() echo $example->test; // 调用了 __get(), 输出: hello ``` 4. **`__call()` 和 `__callStatic()`** - 当调用一个不存在的方法时,这两个方法会被激活。 - `__call()` 处理的是实例方法调用,而 `__callStatic()` 是静态方法调用的情况。 - 示例代码如下: ```php class Example { public function __call($method, $args) { echo "$method was called with arguments: "; print_r($args); } public static function __callStatic($method, $args) { echo "$method was statically called with arguments: "; print_r($args); } } $example = new Example(); $example->nonExistentMethod(1, 2, 3); // 动态方法调用 Example::anotherNonExistentMethod('a', 'b'); // 静态方法调用 ``` 5. **`__toString()`** - 将对象转换成字符串表示形式的时候会调用该方法。 - 特别适用于打印调试信息或是将对象嵌套于其他字符串之中。 - 示例代码如下: ```php class Example { public function __toString() { return "这是一个Example类的对象"; } } $example = new Example(); echo $example; // 输出: 这是一个Example类的对象 ``` 6. **`__invoke()`** - 如同引用所描述的一样,在尝试像函数一样调用某个对象时,这个方法就会被执行[^2]。 - 示例代码已在引用中给出。 7. **`__clone()`** - 创建当前对象副本之后立即调用它。 - 可在此处实现深拷贝逻辑或者其他必要的克隆后动作[^2]。 - 示例代码如下: ```php class Example { protected $property; public function __clone() { $this->property = clone $this->property; } } ``` 8. **`__sleep()` 和 `__wakeup()`** - 序列化过程中涉及到了这两个方法。 - `__sleep()` 返回一个数组指定哪些变量应该序列化存储下来; - `__wakeup()` 则负责反序列化后的恢复工作。 9. **`__debugInfo()` (自 PHP 5.6 起可用)** - 提供定制化的 var_dump 结果展示方式。 ### 总结 利用好这些魔术方法可以极大地提升程序设计的质量与效率,同时也让代码更加简洁易懂。然而需要注意过度依赖可能会降低可维护性和清晰度,因此应当谨慎选用合适的场合应用之[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值