PHP 中 bind 的用法 self 和 static 的區別

nongnong發表於2019-12-07

bind是bindTo的靜態版本,因此只說bind吧。(還不是太瞭解為什麼要弄出兩個版本)

官方文件: 複製一個閉包,繫結指定的$this物件和類作用域。

其實後半句表述很不清楚。 我的理解: 把一個閉包轉換為某個類的方法(只是這個方法不需要通過物件呼叫), 這樣閉包中的$this、static、self就轉換成了對應的物件或類。

因為有幾種情況:

1、只繫結$this物件.\
2、只繫結類作用域.\
3、同時繫結$this物件和類作用域.(文件的說法)\
4、都不繫結.(這樣一來只是純粹的複製, 文件說法是使用cloning代替bind或bindTo)

下面詳細講解這幾種情況:

1、只繫結$this物件


$closure = function ($name, $age) {
    $this->name = $name;
    $this->age = $age;
};

class Person {
    public $name;
    public $age;

    public function say() {
        echo "My name is {$this->name}, I'm {$this->age} years old.\n";
    }
}

$person = new Person();

//把$closure中的$this繫結為$person
//這樣在$bound_closure中設定name和age的時候實際上是設定$person的name和age
//也就是繫結了指定的$this物件($person)
$bound_closure = Closure::bind($closure, $person);

$bound_closure('php', 100);
$person->say();

注意: 在上面的這個例子中,是不可以在$closure中使用static的,如果需要使用static,通過第三個引數傳入帶名稱空間的類名。

2、只繫結類作用域.

$closure = function ($name, $age) {
  static::$name =  $name;
  static::$age = $age;
};

class Person {
    static $name;
    static $age;

    public static function say()
    {
        echo "My name is " . static::$name . ", I'm " . static::$age. " years old.\n";
    }
}

//把$closure中的static繫結為Person類
//這樣在$bound_closure中設定name和age的時候實際上是設定Person的name和age
//也就是繫結了指定的static(Person)
$bound_closure = Closure::bind($closure, null, Person::class);

$bound_closure('php', 100);

Person::say();

注意: 在上面的例子中,是不可以在$closure中使用$this的,因為我們的bind只繫結了類名,也就是static,如果需要使用$this,新建一個物件作為bind的第二個引數傳入。

3、同時繫結$this物件和類作用域.(文件的說法)

$closure = function ($name, $age, $sex) {
    $this->name = $name;
    $this->age = $age;
    static::$sex = $sex;
};

class Person {
    public $name;
    public $age;

    static $sex;

    public function say()
    {
        echo "My name is {$this->name}, I'm {$this->age} years old.\n";
        echo "Sex: " . static::$sex . ".\n";
    }
}

$person = new Person();

//把$closure中的static繫結為Person類, $this繫結為$person物件
$bound_closure = Closure::bind($closure, $person, Person::class);
$bound_closure('php', 100, 'female');

$person->say();

在這個例子中可以在$closure中同時使用$this和static

4、都不繫結.(這樣一來只是純粹的複製, 文件說法是使用cloning代替bind或bindTo)

$closure = function () {
    echo "bind nothing.\n";
};

//與$bound_closure = clone $closure;的效果一樣
$bound_closure = Closure::bind($closure, null);

$bound_closure();

self 和static 的區別

class A {

    protected $name = 'A';
    static $alias = 'a';
    const HASH = 'md5';

    public function dd() {
        echo $this->name; echo '--';
        echo static::$alias; echo '--';     // 後期靜態繫結
        echo static::HASH; echo '--';     // 後期靜態繫結
        echo self::$alias; echo '--';
        echo self::HASH; echo '--';

        var_dump(new self); echo '--';
        var_dump($this); echo '--';
        var_dump(new static); echo '<br>';   // 後期靜態繫結
    }

    public static function who() {
        echo __CLASS__;
        echo ' [ This is A ]'; echo '<br>';
    }

    public static function test() {
        self::who();
    }

    public static function test2() {
        static::who();  // 後期靜態繫結
    }

    public static function getInstance() {
        var_dump(new self); echo '--';
        var_dump(new static); echo '<br>';  // 後期靜態繫結
    }
}

class B extends A {
    protected $name = 'B';
    static $alias = 'b';
    const HASH = 'sha1';

    public static function who() {
        echo __CLASS__;
        echo ' [ This is B ]'; echo '<br>';
    }
}

class C extends B {
    public static function who() {
        echo __CLASS__;
        echo ' [ This is C]'; echo '<br>';
    }
}

(new A)->dd();  // A--a--md5--a--md5--object(A)#2 (1) { ["name":protected]=> string(1) "A" } --object(A)#1 (1) { ["name":protected]=> string(1) "A" } --object(A)#2 (1) { ["name":protected]=> string(1) "A" }
(new B)->dd();  // B--b--sha1--a--md5--object(A)#2 (1) { ["name":protected]=> string(1) "A" } --object(B)#1 (1) { ["name":protected]=> string(1) "B" } --object(B)#2 (1) { ["name":protected]=> string(1) "B" }

A::who();  // A [ This is A ]
B::who();  // B [ This is B ]

A::test();  // A [ This is A ]
B::test();  // A [ This is A ]

A::test2(); // A [ This is A ]
B::test2(); // B [ This is B ]
C::test2(); // C [ This is C]

A::getInstance();   // object(A)#1 (1) { ["name":protected]=> string(1) "A" } --object(A)#1 (1) { ["name":protected]=> string(1) "A" }
B::getInstance();   // object(A)#1 (1) { ["name":protected]=> string(1) "A" } --object(B)#1 (1) { ["name":protected]=> string(1) "B" }

總結說明:

  • self 和 CLASS,都是對當前類的靜態引用,取決於定義當前方法所在的類。也就是說,self 寫在哪個類裡面, 它引用的就是誰。
  • $this 指向的是實際呼叫時的物件,也就是說,實際執行過程中,誰呼叫了類的屬性或方法,$this 指向的就是哪個物件。但 $this 不能訪問類的靜態屬性和常量,且 $this 不能存在於靜態方法中。
  • static 關鍵字除了可以宣告類的靜態成員(屬性和方法)外,還有一個非常重要的作用就是後期靜態繫結。
  • self 可以用於訪問類的靜態屬性、靜態方法和常量,但 self 指向的是當前定義所在的類,這是 self 的限制。
    $this 指向的物件所屬的類和 static 指向的類相同。
  • static 可以用於靜態或非靜態方法中,也可以訪問類的靜態屬性、靜態方法、常量和非靜態方法,但不能訪問非靜態屬性。
    靜態呼叫時,static 指向的是實際呼叫時的類;非靜態呼叫時,static 指向的是實際呼叫時的物件所屬的類。
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章