php中的类与对象(魔术方法、克隆和重载)

简介

魔术方法是可以定义在类中的一些特殊方法,这些方法的名称、参数和返回值都是PHP预定义的,设计这些方法时只需要设计方法体中的具体业务逻辑即可。设计一个类时,魔术方法不一定非得设计,而如果设计了,那么程序运行过程中,php会在适当的时候自动调用这些魔术方法。


__toString

public string __toString ( void )
对象被转换为字符串时,自动调用此方法,注意,不能在 __toString() 方法中抛出异常。这么做会导致致命错误。

class MyClass
{
  private $dat;

  public function __construct($dat) {
    $this->dat = $dat;
  }
  
  public function __toString() {
    return "Dat: $this->dat";
  }
}

$a = new MyClass(5);
$b = (string)$a;
echo $a."\n";   // Dat: 5
echo $b."\n";   // Dat: 5

__invoke() 

mixed __invoke ([ $... ] )
当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。 

class MyClass
{
  private $dat;

  public function __construct($dat) {
    $this->dat = $dat;
  }
  
  public function __invoke($num = 1) {
    for($i=0; $i<$num; $i++)
      echo "Dat: $this->dat\n";
  }
}

$a = new MyClass(5);
$a(3);
/*
Dat: 5
Dat: 5
Dat: 5
*/

__set_state() 

static object __set_state ( array $properties )
var_export() 导出类时,会包含对此方法的调用。也就是说如果想用var_export()的导出结果来创建对象,那么就需要定义好此方法。 
本方法的唯一参数是一个数组,其中包含按 array('property' => value, ...) 格式排列的类属性。 

class MyClass
{
  public $var1;
  public $var2;

  static function __set_state($an_array)
  {
  }
}

$a = new MyClass();
$a->var1 = 2;
$a->var2 = 3;
$b = var_export($a, true);
echo $b;
/*
MyClass::__set_state(array(
   'var1' => 2,
   'var2' => 3,
))
*/
本例中,var_export()的返回值是一个字符串,该字符串是一段可执行的php代码,而这段代码的运行结果就是得到一个$a对象。从这段代码中可以看出,调用的方法是静态方法__set_state(),而实参是一个数组。所以可依此来设计__set_state()方法。
class MyClass
{
  public $var1;
  public $var2;

  public static function __set_state($an_array)
  {
    $obj = new MyClass;
    $obj->var1 = $an_array['var1'];
    $obj->var2 = $an_array['var2'];
    return $obj;
  }
}

$a = new MyClass();
$a->var1 = 2;
$a->var2 = 3;
$b = var_export($a, true);
eval('$n = '.$b.";");
var_dump($n);
/*
object(MyClass)#2 (2) {
  ["var1"]=>
  int(2)
  ["var2"]=>
  int(3)
}
*/

__debugInfo() 

array __debugInfo ( void )
定制var_dump()函数的输出,本函数返回一个数组,并可由var_dump()函数打印。

class MyClass
{
  public $var1 = 1;
  protected $var2 = 2;
  private $var3 = 3;
}

$a = new MyClass();
var_dump($a);
/*
object(MyClass)#1 (3) {
  ["var1"]=>
  int(1)
  ["var2":protected]=>
  int(2)
  ["var3":"MyClass":private]=>
  int(3)
}
*/

class MyClass
{
  public $var1 = 1;
  protected $var2 = 2;
  private $var3 = 3;

  public function __debugInfo()
  {
    return Array("var1"=>$this->var1,
                 "var2"=>$this->var2);
  }
}

$a = new MyClass();
var_dump($a);
/*
object(MyClass)#1 (2) {
  ["var1"]=>
  int(1)
  ["var2"]=>
  int(2)
}
*/

__destruct

void __destruct ( void )
析构函数,在对象的所有引用都被删除或者当对象被显式销毁时执行。和构造函数一样,父类的析构函数不会被引擎暗中调用。要执行父类的析构函数,必须在子类的析构函数体中显式调用 parent::__destruct()。此外也和构造函数一样,子类如果自己没有定义析构函数则会继承父类的。 即使在使用 exit() 终止脚本运行时也会调用析构函数。但是在析构函数中调用 exit() 则会中止其余关闭操作的运行。 另外,试图在析构函数(在脚本终止时被调用)中抛出一个异常会导致致命错误。 


克隆

当直接复制一个对象时,实际上复制到的仅仅是一个引用。如果使用clone操作符进行复制,则可以得到一个副本。

class OtherClass
{
  public $otherVar = 0;
}

class MyClass
{
  public $var;
  public $other;
}

$a = new MyClass();
$o = new OtherClass();
$a->var = 3;
$a->other = $o;

$b = $a;
$c = clone $a;
$a->var = 4;
$o->otherVar = 1;

var_dump($a);
var_dump($b);
var_dump($c);
/*
object(MyClass)#1 (2) {
  ["var"]=>
  int(4)
  ["other"]=>
  object(OtherClass)#2 (1) {
    ["otherVar"]=>
    int(1)
  }
}
object(MyClass)#1 (2) {
  ["var"]=>
  int(4)
  ["other"]=>
  object(OtherClass)#2 (1) {
    ["otherVar"]=>
    int(1)
  }
}
object(MyClass)#3 (2) {
  ["var"]=>
  int(3)
  ["other"]=>
  object(OtherClass)#2 (1) {
    ["otherVar"]=>
    int(1)
  }
}
*/
本例中,$c就是使用clone赋值的,其中的$var确实是一个副本,但$other仍然与$a和$b共享。

为解决此问题,可为类定义__clone()方法,该方法会在clone后自动调用,在方法中完成对象的重新创建即可。当复制完成时,如果定义了 __clone() 方法,则新创建的对象(复制生成的对象)中的 __clone() 方法会被调用,可用于修改属性的值(如果有必要的话)。

class OtherClass
{
  public $otherVar = 0;

  public function __clone() {
  }
}

class MyClass
{
  public $var;
  public $other;

  public function __clone() {
    $this->other = clone $this->other;
  }
}

$a = new MyClass();
$o = new OtherClass();
$a->var = 3;
$a->other = $o;

$b = $a;
$c = clone $a;
$a->var = 4;
$o->otherVar = 1;

var_dump($a);
var_dump($b);
var_dump($c);
/*
object(MyClass)#1 (2) {
  ["var"]=>
  int(4)
  ["other"]=>
  object(OtherClass)#2 (1) {
    ["otherVar"]=>
    int(1)
  }
}
object(MyClass)#1 (2) {
  ["var"]=>
  int(4)
  ["other"]=>
  object(OtherClass)#2 (1) {
    ["otherVar"]=>
    int(1)
  }
}
object(MyClass)#3 (2) {
  ["var"]=>
  int(3)
  ["other"]=>
  object(OtherClass)#4 (1) {
    ["otherVar"]=>
    int(0)
  }
}
 */


属性重载

当调用当前环境下未定义或不可见的类属性或方法时,会有一些预定义的魔术方法被自动调用,这些魔术方法也称为重载方法。所有的重载方法都必须被声明为 public,而且参数都不能通过引用传递。 

__set
public void __set ( string $name , mixed $value )
在给不可访问属性赋值时,__set() 会被调用。 

__get
public mixed __get ( string $name )
读取不可访问属性的值时,__get() 会被调用。 

__isset
public bool __isset ( string $name )
当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。 

__unset
public void __unset ( string $name )
当对不可访问属性调用 unset() 时,__unset() 会被调用。 

属性重载只能在对象中进行。在静态方法中,这些魔术方法将不会被调用。
注意,因为 PHP 处理赋值运算的方式,__set() 的返回值将被忽略。类似的, 在下面这样的链式赋值中,__get() 不会被调用: $a = $obj->b = 8; 
在除 isset() 外的其它语言结构中无法使用重载的属性,这意味着当对一个重载的属性使用 empty() 时,重载魔术方法将不会被调用。 为避开此限制,必须将重载属性赋值到本地变量再使用 empty()。 

class MyClass
{
  // 被重载的数据保存在此
  private $data = array();

  // 只有从类外部访问这个属性时,重载才会发生
  private $hidden = 2;

  public function __set($name, $value) {
    echo "/// Setting '$name' to '$value'\n";
    $this->data[$name] = $value;
  }

  public function __get($name) {
    echo "/// Getting '$name'\n";
    if (array_key_exists($name, $this->data)) {
      return $this->data[$name];
    }
    return null;
  }

  public function __isset($name) {
    echo "/// Is '$name' set?\n";
    return isset($this->data[$name]);
  }

  public function __unset($name) {
    echo "/// Unsetting '$name'\n";
    unset($this->data[$name]);
  }

  public function getHidden() {
    return $this->hidden;
  }
}

$a = new MyClass();
var_dump(isset($a->x));       // bool(false)
var_dump(isset($a->hidden));  // bool(false)

$a->x = 3;
$a->hidden = 4;

var_dump(isset($a->x));       // bool(true)
var_dump(isset($a->hidden));  // bool(true)
var_dump($a->x);              // int(3)
var_dump($a->hidden);         // int(4)
var_dump($a->getHidden());    // int(2)

unset($a->x);
unset($a->hidden);

/*
/// Is 'x' set?
bool(false)
/// Is 'hidden' set?
bool(false)
/// Setting 'x' to '3'
/// Setting 'hidden' to '4'
/// Is 'x' set?
bool(true)
/// Is 'hidden' set?
bool(true)
/// Getting 'x'
int(3)
/// Getting 'hidden'
int(4)
int(2)
/// Unsetting 'x'
/// Unsetting 'hidden'
*/

方法重载 


__call
public mixed __call ( string $name , array $arguments )
在对象中调用一个不可访问方法时,__call() 会被调用。 

__callStatic
public static mixed __callStatic ( string $name , array $arguments )
在静态上下文中调用一个不可访问方法时,__callStatic() 会被调用。 

$name 参数是要调用的方法名称。$arguments 参数是一个枚举数组,包含着要传递给方法 $name 的参数。 

class MyClass
{
  public function __call($name, $arguments) {
    // 注意: $name 的值区分大小写
    echo "Calling object method '$name' "
    . implode(', ', $arguments) . "\n";
  }

  public static function __callStatic($name, $arguments) {
    // 注意: $name 的值区分大小写
    echo "Calling static method '$name' "
    . implode(', ', $arguments) . "\n";
  }
}

$a = new MyClass();
$a->func(1,2,3);
MyClass::staticFunc(4,5,6);
/*
Calling object method 'func' 1, 2, 3
Calling static method 'staticFunc' 4, 5, 6
 */


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值