更新

析构函数 __destruct()

析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。

简单一点理解就是对象里面所有方法或者属性执行玩之后自动执行

例子:

<?php 

class MyDestructableClass {
    function __construct() {

        print "1\n";
        $this->name = "MyDestructableClass";
    }
   
    function index(){
        echo 'index';
    }

    function __destruct() {
        print "Destroying " . $this->name . "\n";
    }
 }
 
 $obj = new MyDestructableClass();
 $obj->index();
 ?> 

打印结果:

In constructor Destroying MyDestructableClass

1.(class) 关键字

简单的类定义

class SimpleClass
{
    // 声明属性
    public $var = 'a default value';

    // 声明方法
    public function displayVar() {
        echo $this->var;
    }
}

2.($this) 关键字

当一个方法在类的内部被调用时,有一个可用的伪变量 $this (指的就是当前对象)
class A
{
    function foo()
    {
        if (isset($this)) {
            echo get_class($this); //获得类名
        } else {
            echo "类不存在";
            echo "类不存在";
        }
    }
}
$a = new A();
$a->foo(); //. 会输出 A

3.(parent::) 关键字

调用父类的方法
class SimpleClass
{
    // 声明属性
    public $var = '我是父类属性';

    // 声明属性
    protected $file;

    // 声明方法
    public function displayVar()
    {
        echo $this->var;
    }

}

class ExtendClass extends SimpleClass  // extends继承
{
    // Redefine the parent method
    function displayVar()
    {
        echo $this->file = '给属性赋值';
        parent::displayVar();  // parent:: 关键字 调用父类的方法
    }
}

$extended = new ExtendClass();
$extended->displayVar();

parent::__construct();

class SimpleClass
{
   public function __construct(){
      echo 1;
   }

}
class ExtendClass extends SimpleClass  // extends继承
{

   public function __construct(){
       parent::__construct();
       echo 2;
   }
}
$extended = new ExtendClass(); //输出1 2

4.::class 关键字

关键词 class 可用于获取类名。使用 ClassName::class 你可以获取一个字符串(例如 user\app\UserModel的完整路径)一般和 命名空间 搭配使用。
以下为例:
1.

namespace user\app;

class UserModel
{
}
echo UserModel::class;  //. 会输出 user\app\UserModel
2.
class File
{

}
echo File::class;  //. 会输出 File

5.const关键字

类常量  
以下为例:
class MyClass
{
    const data = 'value';

   public function showConstant() {
       /**
        * 两种方式获取变量的值
        */
        echo MyClass::data . "\n";

        echo self::data ;
    }
}
$obj =new MyClass();
$obj->showConstant(); //.会输出value value

6.__destruct()

当对象里所以应用执行完毕后并__destruct()方法自动执行,当主动unset($obj)之后后面的应用无法在执行 (__destruct()一般都没什么用)
和构造函数一样,父类的析构函数不会被引擎暗中调用。要执行父类的析构函数,必须在子类的析构函数体中显式调用 parent::__destruct()。此外也和构造函数一样,子类如果自己没有定义析构函数则会继承父类的。
以下为例:
class sf
{
    public function index()
    {
        echo '首页';
        return $this->update();
    }

    public function __destruct()
    {
        echo '销毁对象';
    }
    public function update()
    {
        echo 'update';
    }
}

$obj = new sf;
$obj->index();
unset($obj);
$obj->index(); // .无法执行 会提示undefined $obj

6.抽象类

抽象类不能被实例化,继承一个抽象类的时候,子类必须定义完父类中的所有抽象方法;这些方法的访问控制必须和父类中一样(或者更为宽松)。
子类方法可以包含父类抽象方法中不存在的可选参数。
例如,子类定义了一个可选参数,而父类抽象方法的声明里没有,则也是可以正常运行的。
以下为例:
//定义一个抽象类
abstract class AbstractClass
{
    // 强制要求子类定义这些方法
    abstract protected function getValue();
    // 抽象方法可定义需要的参数
    abstract protected function prefixValue($prefix);

    // 普通方法(非抽象方法)
    public function printOut() {
        print $this->getValue() . PHP_EOL;
    }
}

//继承抽象类
class ConcreteClass1 extends AbstractClass
{
    protected function getValue() {
        return "ConcreteClass1";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass1";
    }
}
//再次继承抽象类
class ConcreteClass2 extends AbstractClass
{
    public function getValue() {
        return "ConcreteClass2";
    }
    // 我们的子类可以定义父类签名中不存在的可选参数
    public function prefixValue($prefix, $separator = ".可选参数") {
        return "{$prefix}ConcreteClass2" .$separator;
    }
    public function create()
    {
        return 'create';
    }
}
$obj2 = new ConcreteClass2;
$obj2->printOut();
echo $obj2->prefixValue('FOO_') . PHP_EOL;
echo $obj2->create();

多太实例

<?php
class Mobile{
    public function doMobile( ){
        echo '手机充电';
    }  
}
class Mouse{
    public function doMouse( ){
        echo '实现mouse功能';
    }  
}
class Keybord{
    public function doKeybord( ){
        echo '实现Keybord功能';
    }  
}

function test($obj){
    if($obj instanceof Mobile){
        $obj->doMobile();
    }elseif($obj instanceof Keybord) {
        $obj->doKeybord();
    }
}

test(new Mobile());

实现多态

<?php

class TypeC{
    public function work(){
        echo 'null';
    }
}

class Mobile extends TypeC{
    public function work( ){
        echo '手机充电';
    }  
}
class Mouse extends TypeC{
    public function work( ){
        echo '实现mouse功能';
    }  
}
class Keybord extends TypeC{
    public function work( ){
        echo '实现Keybord功能';
    }  
}

function test($obj){
    if($obj instanceof TypeC){
        $obj->work();
    }
}

test(new Mouse());

7.接口 interface

可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。
接口中定义的所有方法都必须是公有,这是接口的特性
要实现一个接口,使用 implements 操作符。类中必须实现接口中定义的所有方法,否则会报错。类可以实现多个接口,用逗号来分隔多个接口的名称。
实现多个接口时,接口中的方法不能有重名。
接口也可以继承,通过使用 extends 操作符。
以下为例:
// 声明一个'iTemplate'接口
interface iTemplate
{
    public function setVariable($name, $var);
    public function getHtml($template);
    public function edit();
}


// 实现接口
class Template implements iTemplate
{
    private $vars = array();
    public function setVariable($name, $var)
    {
        $this->vars[$name] = $var;
    }

    public function getHtml($template)
    {
        foreach($this->vars as $name => $value) {
            $template = str_replace('{' . $name . '}', $value, $template);
        }

        return $template;
    }
    public function edit()
    {
        // TODO: Implement edit() method.
    }
}

继承(entends)
interface a
{
    public function foo();
}

interface b extends a
{
    public function baz( $baz);
}

// 正确写法
class c implements b
{
    public function foo()
    {
    }

    public function baz( $baz)
    {
    }
}

继承多个接口
interface a
{
    public function foo();
}

interface b
{
    public function bar();
}

interface c extends a, b
{
    public function baz();
}

class d implements c
{
    public function foo()
    {
    }

    public function bar()
    {
    }

    public function baz()
    {
    }
}

常量
//定义一个接口
interface a
{
    const b = 'Interface constant';
}

// 输出接口常量
echo a::b;

// 错误写法,因为常量不能被覆盖。接口常量的概念和类常量是一样的。
class b implements a
{
    const b = 'Class constant';
}

8.Trait 代码复用

以下为例:
trait UserContreller  //. trait 声明
{
    public function getReturnType()
    {
        echo 'hello';
    }

    public function getReturnDescription()
    {
        echo 'world';
    }
}

class ArticleController
{
    use UserContreller;  //. use 使用
}

class ezcReflectionFunction
{
    use UserContreller;
}

(new ArticleController())->getReturnType();

继承的优先级 trait 方法覆盖继承中的方法。
trait SayWorld {
    public function sayHello() {
        parent::sayHello();
        echo 'World!';  //最后输出
    }
}

class Base {
    public function sayHello() {
        echo 'Hello ';
    }
}

class MyHelloWorld extends Base {
  
    use SayWorld;

}

$o = new MyHelloWorld();
$o->sayHello();   //. 输出为 HelloWorld


trait HelloWorld {
    public function sayHello() {
        echo 'Hello World!';
    }
}

class TheWorldIsNotEnough {
    use HelloWorld;
    public function sayHello() {
        echo 'Hello Universe!';
    }
}

$o = new TheWorldIsNotEnough();
$o->sayHello(); //. 会输出Hello Universe!

多个 trait 的用法 通过逗号分隔,在 use 声明列出多个 trait,可以都插入到一个类中。

trait Hello {
    public function sayHello() {
        echo 'Hello ';
    }
}

trait World {
    public function sayWorld() {
        echo 'World';
    }
}

class MyHelloWorld {
    use Hello, World;
    public function sayExclamationMark() {
        echo '!';
    }
}

$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
$o->sayExclamationMark();

冲突的解决
如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误。

为了解决多个 trait 在同一个类中的命名冲突,需要使用 insteadof 操作符来明确指定使用冲突方法中的哪一个。

以上方式仅允许排除掉其它方法,as 操作符可以 为某个方法引入别名。 注意,as 操作符不会对方法进行重命名,也不会影响其方法。

trait A {
    public function smallTalk() {
        echo 'a';
    }
    public function bigTalk() {
        echo 'A';
    }
}

trait B {
    public function smallTalk() {
        echo 'b';
    }
    public function bigTalk() {
        echo 'B';
    }
}

class Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
    }
}

class Aliased_Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
        B::bigTalk as talk;
    }
}

匿名类

PHP 7 开始支持匿名类。 匿名类很有用,可以创建一次性的简单对象。

可以传递参数到匿名类的构造器,也可以扩展(extend)其他类、实现接口(implement interface),以及像其他普通的类一样使用 trait:

class SomeClass {}
interface SomeInterface {}
trait SomeTrait {}

var_dump(new class(10) extends SomeClass implements SomeInterface {
    private $num;

    public function __construct($num)
    {
        $this->num = $num;
    }

    use SomeTrait;
}); 

以上例程会输出:

object(class@anonymous)#1 (1) {
  ["Command line code0x104c5b612":"class@anonymous":private]=>
  int(10)
}

匿名类被嵌套进普通 Class 后,不能访问这个外部类(Outer class)的 private(私有)、protected(受保护)方法或者属性。为了访问外部类(Outer class)protected 属性或方法,匿名类可以 extend(扩展)此外部类。为了使用外部类(Outer class)的 private 属性,必须通过构造器传进来:
匿名类被嵌套进普通 Class 后,不能访问这个外部类(Outer class)的 private(私有)、protected(受保护)方法或者属性。为了访问外部类(Outer class)protected 属性或方法,匿名类可以 extend(扩展)此外部类。为了使用外部类(Outer class)的 private 属性,必须通过构造器传进来:

例子:

class Outer
{
    private $prop = 1;
    protected $prop2 = 2;

    protected function func1()
    {
        return 3;
    }

    public function func2()
    {
        return new class($this->prop) extends Outer {
            private $prop3;

            public function __construct($prop)
            {
                $this->prop3 = $prop;
            }

            public function func3()
            {
                return $this->prop2 + $this->prop3 + $this->func1();
            }
        };
    }
}

echo (new Outer)->func2()->func3(); 

以上例程会输出:

6

声明的同一个匿名类,所创建的对象都是这个类的实例。

<?php
function anonymous_class()
{
    return new class {};
}

if (get_class(anonymous_class()) === get_class(anonymous_class())) {
    echo 'same class';
} else {
    echo 'different class';
} 

以上例程会输出:

same class

对象比较

当使用比较运算符(==)比较两个对象变量时,比较的原则是:如果两个对象的属性和属性值 都相等,而且两个对象是同一个类的实例,那么这两个对象变量相等。

而如果使用全等运算符(===),这两个对象变量一定要指向某个类的同一个实例(即同一个对象)。

示例:

class Test {

    public $name = 'itbsl';
    public $age  = 25;
}

$testObj  = new Test();
$testObj2 = new Test();

if ($testObj == $testObj2) {
    echo '相等<br>';
} else {
    echo '不相等<br>';
}

$testObj2->name = 'jack';

if ($testObj == $testObj2) {
    echo '相等<br>';
} else {
    echo '不相等<br>';
}

以上会输出:

相等
不相等

对象遍历

foreach用法和之前的数组遍历是一样的,只不过这里遍历的key是属性名,value是属性值。在类外部遍历时,只能遍历到public属性的,因为其它的都是受保护的,类外部不可见。
示例:

class HardDiskDrive {

    public $brand;
    public $color;
    public $cpu;
    public $workState;

    protected $memory;
    protected $hardDisk;

    private $price;

    public function __construct($brand, $color, $cpu, $workState, $memory, $hardDisk, $price) {

        $this->brand = $brand;
        $this->color = $color;
        $this->cpu   = $cpu;
        $this->workState = $workState;
        $this->memory = $memory;
        $this->hardDisk = $hardDisk;
        $this->price = $price;
    }

}

$hardDiskDrive = new HardDiskDrive('希捷', 'silver', 'tencent', 'well', '1T', 'hard', '$456');

foreach ($hardDiskDrive as $property => $value) {

    var_dump($property, $value);
    echo '<br>';
}

如果我们想遍历出对象的所有属性,就需要控制foreach的行为,就需要给类对象,提供更多的功能,需要继承自Iterator的接口:

该接口,实现了foreach需要的每个操作。foreach的执行流程如下图:

class MyIterator implements Iterator
{
    private $var = array();

    public function __construct($array)
    {
        if (is_array($array)) {
            $this->var = $array;
        }
    }

    public function rewind() {
        echo "rewinding\n";
        reset($this->var);
    }

    public function current() {
        $var = current($this->var);
        echo "current: $var\n";
        return $var;
    }

    public function key() {
        $var = key($this->var);
        echo "key: $var\n";
        return $var;
    }

    public function next() {
        $var = next($this->var);
        echo "next: $var\n";
        return $var;
    }

    public function valid() {
        $var = $this->current() !== false;
        echo "valid: {$var}\n";
        return $var;
    }
}

$values = array(1,2,3);
$it = new MyIterator($values);

foreach ($it as $a => $b) {
    print "$a: $b\n";
}
?> 

可以用 IteratorAggregate 接口以替代实现所有的 Iterator 方法。IteratorAggregate 只需要实现一个方法 IteratorAggregate::getIterator(),其应返回一个实现了 Iterator 的类的实例。

class MyCollection implements IteratorAggregate
{
    private $items = array();
    private $count = 0;

    // Required definition of interface IteratorAggregate
    public function getIterator() {
        return new MyIterator($this->items);
    }

    public function add($value) {
        $this->items[$this->count++] = $value;
    }
}

$coll = new MyCollection();
$coll->add('value 1');
$coll->add('value 2');
$coll->add('value 3');

foreach ($coll as $key => $val) {
    echo "key/value: [$key -> $val]\n\n";
}
?> 

final关键字

(1) 作用:

​ 因为安全的考虑,类的某个方法不允许子类通过重写来修改。

​ 不希望某个类被其它的类继承。

(2) PHP5新增了一个final关键字。如果父类中的方法被声明为final,则子类无法覆盖该方法。如果一个类被声明为final,则该类不能被继承。

(3) final不能够修饰成员属性(变量) 属性不能被定义为 final,只有类和方法才能被定义为 final。

(4) final方法不能被重写,但可以被继承。即使是被声明为final的方法也依然能够被继承并被使用,只是不能重写(修改)罢了。

(5) 一般来说,final类中不会出现final方法,因为final类都不能被继承,也就不会去重写override final类的方法了。

(6) final类是可以被实例化的。

Example #1 Final 方法示例



<?php
class BaseClass {
   public function test() {
       echo "BaseClass::test() called\n";
   }
   
   final public function moreTesting() {
       echo "BaseClass::moreTesting() called\n";
   }
}

class ChildClass extends BaseClass {
   public function moreTesting() {
       echo "ChildClass::moreTesting() called\n";
   }
}
// Results in Fatal error: Cannot override final method BaseClass::moreTesting()
?> 
Example #2 Final 类示例



<?php
final class BaseClass {
   public function test() {
       echo "BaseClass::test() called\n";
   }
   
   // 这里无论你是否将方法声明为final,都没有关系
   final public function moreTesting() {
       echo "BaseClass::moreTesting() called\n";
   }
}

class ChildClass extends BaseClass {
}
// 产生 Fatal error: Class ChildClass may not inherit from final class (BaseClass)
?> 

对象复制

在多数情况下,我们并不需要完全复制一个对象来获得其中属性。但有一个情况下确实需要:如果你有一个 GTK 窗口对象,该对象持有窗口相关的资源。你可能会想复制一个新的窗口,保持所有属性与原来的窗口相同,但必须是一个新的对象(因为如果不是新的对象,那么一个窗口中的改变就会影响到另一个窗口)。还有一种情况:如果对象 A 中保存着对象 B 的引用,当你复制对象 A 时,你想其中使用的对象不再是对象 B 而是 B 的一个副本,那么你必须得到对象 A 的一个副本。

对象复制可以通过 clone 关键字来完成(如果可能,这将调用对象的 __clone() 方法)。对象中的 __clone() 方法不能被直接调用。

$copy_of_object = clone $object;

当对象被复制后,PHP 5 会对对象的所有属性执行一个浅复制(shallow copy)。所有的引用属性仍然会是一个指向原来的变量的引用。

__clone( void) : void
当复制完成时,如果定义了 __clone() 方法,则新创建的对象(复制生成的对象)中的 __clone() 方法会被调用,可用于修改属性的值(如果有必要的话)。

Example #1 复制一个对象
<?php
class SubObject
{
    static $instances = 0;
    public $instance;

    public function __construct() {
        $this->instance = ++self::$instances;
    }

    public function __clone() {
        $this->instance = ++self::$instances;
    }
}

class MyCloneable
{
    public $object1;
    public $object2;

    function __clone()
    {
      
        // 强制复制一份this->object, 否则仍然指向同一个对象
        $this->object1 = clone $this->object1;
    }
}

$obj = new MyCloneable();

$obj->object1 = new SubObject();
$obj->object2 = new SubObject();

$obj2 = clone $obj;


print("Original Object:\n");
print_r($obj);

print("Cloned Object:\n");
print_r($obj2);

?> 
以上例程会输出:


Original Object:
MyCloneable Object
(
    [object1] => SubObject Object
        (
            [instance] => 1
        )

    [object2] => SubObject Object
        (
            [instance] => 2
        )

)
Cloned Object:
MyCloneable Object
(
    [object1] => SubObject Object
        (
            [instance] => 3
        )

    [object2] => SubObject Object
        (
            [instance] => 2
        )

)

__get() __set()

__get()读取不可访问属性的值时,__get() 会被调用。
__set()在给不可访问属性赋值时,__set() 会被调用。
<?php
//判断 dd 函数是否存在  如果不存在

if (!function_exists('dd')) {
    //    定义p函数
    function dd($var)
    {
        echo '<pre style="background: #cccccc;padding: 10px;border-radius: 10px;">';
        if (is_null($var) || is_bool($var)) {
            var_dump($var);
        } else {
            print_r($var);
        }
        echo '</pre>';
    }
}

abstract class Query
{
    abstract protected function record($data);
    public function select()
    {
        return $this->record([
            'name' => '后盾人',
            'age' => '33'
        ]);
    }
}

class Model extends Query
{

    protected $field = [];
    public function all()
    {
        $this->select();
        return $this->field;
    }
    protected function record($data)
    {
        $this->field = $data;
        $data = $this->field;
        dd($data);
    }
    // 读取不可访问属性的值时,__get() 会被调用。 
    public function __get($name)
    {
        return  $this->field[$name] ?? null;
    }
    // 在给不可访问属性赋值时,__set() 会被调用。 
    public function __set($name, $value)
    {
      $this->field[$name]=$value;
      $data =$this->field;
      dd($data);
    }
}

$user = new Model;
$user->all();
//  __get() 会被调用。 
echo $user->filed;
//  __set() 会被调用。 
// $user->name='zhanglong';
// echo $user->names;

后期静态绑定

自 PHP 5.3.0 起,PHP 增加了一个叫做后期静态绑定的功能,用于在继承范围内引用静态调用的类。

准确说,后期静态绑定工作原理是存储了在上一个"非转发调用"(non-forwardingcall)的类名。当进行静态方法调用时,该类名即为明确指定的那个(通常在 :: 运算符左侧部分);当进行非静态方法调用时,即为该对象所属的类。所谓的"转发调用"(forwardingcall)指的是通过以下几种方式进行的静态调用:self::,parent::,static:: 以及 forward_static_call()。可用 get_called_class() 函数来得到被调用的方法所在的类名,static:: 则指出了其范围。

该功能从语言内部角度考虑被命名为"后期静态绑定"。"后期绑定"的意思是说,static:: 不再被解析为定义当前方法所在的类,而是在实际运行时计算的。也可以称之为"静态绑定",因为它可以用于(但不限于)静态方法的调用。

self:: 的限制

使用 self:: 或者 CLASS 对当前类的静态引用,取决于定义当前方法所在的类:

Example #1 self:: 用法



<?php
class A {
    public static function who() {
        echo __CLASS__;
    }
    public static function test() {
        self::who();
    }
}

class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}

B::test();
?>  
以上例程会输出:


A



后期静态绑定的用法 

后期静态绑定本想通过引入一个新的关键字表示运行时最初调用的类来绕过限制。简单地说,这个关键字能够让你在上述例子中调用 test() 时引用的类是 B 而不是 A。最终决定不引入新的关键字,而是使用已经预留的 static 关键字。 


Example #2 static:: 简单用法



<?php
class A {
    public static function who() {
        echo __CLASS__;
    }
    public static function test() {
        static::who(); // 后期静态绑定从这里开始
    }
}

class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}

B::test();
?>  


以上例程会输出:


B


Note:

在非静态环境下,所调用的类即为该对象实例所属的类。由于 $this-> 会在同一作用范围内尝试调用私有方法,而 static:: 则可能给出不同结果。另一个区别是 static:: 只能用于静态属性。

Example #3 非静态环境下使用 static::



<?php
class A {
    private function foo() {
        echo "success!\n";
    }
    public function test() {
        $this->foo();
        static::foo();
    }
}

class B extends A {
   /* foo() will be copied to B, hence its scope will still be A and
    * the call be successful */
}

class C extends A {
    private function foo() {
        /* original method is replaced; the scope of the new one is C */
    }
}

$b = new B();
$b->test();
$c = new C();
$c->test();   //fails
?>  


以上例程会输出:


success!
success!
success!


Fatal error:  Call to private method C::foo() from context 'A' in /tmp/test.php on line 9



Note: 

后期静态绑定的解析会一直到取得一个完全解析了的静态调用为止。另一方面,如果静态调用使用 parent:: 或者 self:: 将转发调用信息。 
Example #4 转发和非转发调用



<?php
class A {
    public static function foo() {
        static::who();
    }

    public static function who() {
        echo __CLASS__."\n";
    }
}

class B extends A {
    public static function test() {
        A::foo();
        parent::foo();
        self::foo();
    }

    public static function who() {
        echo __CLASS__."\n";
    }
}
class C extends B {
    public static function who() {
        echo __CLASS__."\n";
    }
}

C::test();
?>  


以上例程会输出:


A
C
C

对象和引用

在php5 的对象编程经常提到的一个关键点是"默认情况下对象是通过引用传递的"。但其实这不是完全正确的。下面通过一些例子来说明。 

PHP 的引用是别名,就是两个不同的变量名字指向相同的内容。在 PHP 5,一个对象变量已经不再保存整个对象的值。只是保存一个标识符来访问真正的对象内容。当对象作为参数传递,作为结果返回,或者赋值给另外一个变量,另外一个变量跟原来的不是引用的关系,只是他们都保存着同一个标识符的拷贝,这个标识符指向同一个对象的真正内容。 


Example #1 引用和对象



<?php
class A {
    public $foo = 1;
}  

$a = new A;
$b = $a;     // $a ,$b都是同一个标识符的拷贝
             // ($a) = ($b) = <id>
$b->foo = 2;
echo $a->foo."\n";


$c = new A;
$d = &$c;    // $c ,$d是引用
             // ($c,$d) = <id>

$d->foo = 2;
echo $c->foo."\n";


$e = new A;

function foo($obj) {
    // ($obj) = ($e) = <id>
    $obj->foo = 2;
}

foo($e);
echo $e->foo."\n";

?>  


以上例程会输出:


2
2
2

对象序列化

序列化对象 - 在会话中存放对象 

所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。unserialize()函数能够重新把字符串变回php原来的值。序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。 

为了能够unserialize()一个对象,这个对象的类必须已经定义过。如果序列化类A的一个对象,将会返回一个跟类A相关,而且包含了对象所有变量值的字符串。如果要想在另外一个文件中解序列化一个对象,这个对象的类必须在解序列化之前定义,可以通过包含一个定义该类的文件或使用函数spl_autoload_register()来实现。 




<?php
// classa.inc:
  
  class A {
      public $one = 1;
    
      public function show_one() {
          echo $this->one;
      }
  }
  
// page1.php:

  include("classa.inc");
  
  $a = new A;
  $s = serialize($a);
  // 把变量$s保存起来以便文件page2.php能够读到
  file_put_contents('store', $s);

// page2.php:
  
  // 要正确了解序列化,必须包含下面一个文件
  include("classa.inc");

  $s = file_get_contents('store');
  $a = unserialize($s);

  // 现在可以使用对象$a里面的函数 show_one()
  $a->show_one();
?>  

当一个应用程序使用函数session_register()来保存对象到会话中时,在每个页面结束的时候这些对象都会自动序列化,而在每个页面开始的时候又自动解序列化。所以一旦对象被保存在会话中,整个应用程序的页面都能使用这些对象。但是,session_register()在php5.4.0之后被移除了。 

在应用程序中序列化对象以便在之后使用,强烈推荐在整个应用程序都包含对象的类的定义。不然有可能出现在解序列化对象的时候,没有找到该对象的类的定义,从而把没有方法的类__PHP_Incomplete_Class_Name作为该对象的类,导致返回一个没有用的对象。 

所以在上面的例子中,当运行session_register("a"),把变量$a放在会话里之后,需要在每个页面都包含文件classa.inc,而不是只有文件page1.php和page2.php。 

除了以上建议,可以在对象上使用 __sleep() 和 __wakeup() 方法对序列化/反序列化事件挂载钩子。使用 __sleep() 也能够让仅仅序列化对象的某些属性。 

__clone() __toString()

1.当对象对象被克隆时会执行__clone() 方法
2.当对象被输出时会调用__toString() 方法

   class User{
         public function __clone(){
             echo 'hello';
         }

        public function __toString(){
            return 'world';
        }
   }
   $obj = new User;
   $Article =clone $obj; //调用 __clone()
   echo $Article;  //调用 __toString()

本文由 admin 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

还不快抢沙发

添加新评论