Apa sebenarnya binding statis terlambat di PHP?

Jawaban:

203

Anda pasti perlu membaca Late Static Bindings di manual PHP. Namun, saya akan mencoba memberi Anda ringkasan singkat.

Pada dasarnya, ini bermuara pada fakta bahwa selfkata kunci tidak mengikuti aturan pewarisan yang sama. selfselalu menetapkan kelas yang digunakan. Artinya jika Anda membuat metode di kelas induk dan memanggilnya dari kelas anak, selftidak akan mereferensikan anak seperti yang Anda harapkan.

Pengikatan statis akhir memperkenalkan penggunaan baru untuk statickata kunci, yang membahas kekurangan khusus ini. Saat Anda menggunakannya static, ini mewakili kelas tempat Anda pertama kali menggunakannya, yaitu. itu 'mengikat' ke kelas runtime.

Itulah dua konsep dasar di baliknya. Cara self, parentdan staticpengoperasian saat staticsedang dimainkan bisa jadi tidak kentara, jadi daripada membahas lebih detail, saya sangat menyarankan Anda mempelajari contoh halaman manual. Setelah Anda memahami dasar-dasar setiap kata kunci, contoh sangat diperlukan untuk melihat hasil seperti apa yang akan Anda dapatkan.

zombat
sumber
saya menemukan artikel ini sangat berguna dan deskriptif, lihat [tautan] ( techflirt.com/tutorials/oop-in-php/late-static-binding.html )
Sadegh Shaikhi
"... selfkata kunci tidak mengikuti aturan pewarisan. selfselalu menentukan kelas penggunaannya." - Yang tidak berarti Anda tidak dapat memanggil metode statis induk dari objek anak melalui self, seperti metode non-statis. Anda mungkin bermaksud benar, tetapi Anda harus mengulanginya kembali. Itu semua hanya benar-benar penting setelah anak-anak memiliki nama anggota yang identik karena Anda kemudian dapat memutuskan mana yang akan dirujuk dengan menggunakan static::sebagai gantinya.
DanMan
82

Dari PHP: Late Static Bindings - Manual :

Pada PHP 5.3.0, PHP mengimplementasikan fitur yang disebut pengikatan statis akhir yang dapat digunakan untuk mereferensikan kelas yang dipanggil dalam konteks pewarisan statis.

Pengikatan statis akhir mencoba untuk memecahkan batasan itu dengan memperkenalkan kata kunci yang mereferensikan kelas yang awalnya dipanggil saat runtime. ... Diputuskan untuk tidak memperkenalkan kata kunci baru, melainkan menggunakan staticyang sudah dipesan.

Mari kita lihat contohnya:

<?php
    class Car
    {
        public static function run()
        {
            return static::getName();
        }

        private static function getName()
        {
            return 'Car';
        }
    }

    class Toyota extends Car
    {
        public static function getName()
        {
            return 'Toyota';
        }
    }

    echo Car::run(); // Output: Car
    echo Toyota::run(); // Output: Toyota
?>

Binding statis terlambat bekerja dengan menyimpan kelas yang dinamai dalam "panggilan non-penerusan" terakhir. Dalam kasus pemanggilan metode statis, ini adalah kelas yang dinamai secara eksplisit (biasanya yang ada di sebelah kiri ::operator); dalam kasus pemanggilan metode non-statis, itu adalah kelas dari objek. Sebuah "call forwarding" adalah salah satu statis yang diperkenalkan oleh self::, parent::, static::, atau, jika naik dalam hirarki kelas, forward_static_call(). Fungsi get_called_class()ini dapat digunakan untuk mengambil string dengan nama kelas yang dipanggil dan static::memperkenalkan cakupannya.

Mrinmoy Ghoshal
sumber
1
Posting ini adalah ~ 80% salinan kata demi kata dari artikel php.net tanpa penanda kutipan.
WoodrowShigeru
22

Tidak ada perilaku yang sangat jelas:

Kode berikut menghasilkan 'alphabeta'.

class alpha {

    function classname(){
        return __CLASS__;
    }

    function selfname(){
        return self::classname();
    }

    function staticname(){
        return static::classname();
    }
}

class beta extends alpha {

    function classname(){
        return __CLASS__;
    }
}

$beta = new beta();
echo $beta->selfname(); // Output: alpha
echo $beta->staticname(); // Output: beta

Namun, jika kita menghapus deklarasi fungsi nama kelas dari kelas beta, kita mendapatkan 'alphaalpha' sebagai hasilnya.

Jokerius
sumber
1
Sangat bagus. Hal yang sama ditunjukkan dalam manual PHP, tetapi ini jauh lebih jelas. Untuk referensi: php.net/manual/en/language.oop5.late-static-bindings.php (lihat contoh 4)
musicin3d
11

Saya mengutip dari buku: "PHP Master menulis kode mutakhir".

Pengikatan statis akhir adalah fitur yang diperkenalkan dengan php 5.3. Ini memungkinkan kita untuk mewarisi metode statis dari kelas induk, dan untuk merujuk kelas anak yang dipanggil.

Ini berarti Anda bisa memiliki kelas abstrak dengan metode statis, dan mereferensikan implementasi konkret kelas anak dengan menggunakan notasi static :: method () alih-alih self :: method ().

Jangan ragu untuk melihat dokumentasi resmi php juga: http://php.net/manual/en/language.oop5.late-static-bindings.php


Cara paling jelas untuk menjelaskan Late Static Binding adalah dengan contoh sederhana. Perhatikan dua definisi kelas di bawah ini, dan baca terus.

class Vehicle {
    public static function invokeDriveByStatic() {
        return static::drive(); // Late Static Binding
    }
    public static function invokeStopBySelf() {
        return self::stop(); // NOT Late Static Binding
    }
    private static function drive(){
        return "I'm driving a VEHICLE";
    }
    private static function stop(){
        return "I'm stopping a VEHICLE";
    }
}

class Car extends Vehicle  {
    protected static function drive(){
        return "I'm driving a CAR";
    }
    private static function stop(){
        return "I'm stopping a CAR";
    }
}

Kami melihat Kelas Orang Tua (Kendaraan) dan Kelas Anak (Mobil). Kelas Parent memiliki 2 metode publik:

  • invokeDriveByStatic
  • invokeStopBySelf

Kelas Parent juga memiliki 2 metode privat:

  • drive
  • stop

Kelas Anak mengesampingkan 2 metode:

  • drive
  • stop

Sekarang mari panggil metode publik:

  • invokeDriveByStatic
  • invokeStopBySelf

Tanyakan pada diri Anda: Kelas mana yang meminta invokeDriveByStatic/ invokeStopBySelf? Kelas Orang Tua atau Anak?

Lihat di bawah ini:

// This is NOT Late Static Binding
// Parent class invokes from Parent. In this case Vehicle.
echo Vehicle::invokeDriveByStatic(); // I'm driving a VEHICLE
echo Vehicle::invokeStopBySelf(); // I'm stopping a VEHICLE

// !!! This is Late Static Binding !!!!
// Child class invokes an inherited method from Parent.
// Child class = Car, Inherited method = invokeDriveByStatic().
// The inherited method invokes a method that is overridden by the Child class.
// Overridden method = drive()
echo Car::invokeDriveByStatic(); // I'm driving a CAR

// This is NOT Late Static Binding
// Child class invokes an inherited method from Parent.
// The inherited method invokes a method inside the Vehicle context.
echo Car::invokeStopBySelf(); // I'm stopping a VEHICLE

Kata statickunci digunakan dalam pola desain Singleton. Lihat tautan: https://refactoring.guru/design-patterns/singleton/php/example

Julian
sumber
7

Contoh paling sederhana untuk menunjukkan perbedaannya.
Catatan, diri :: $ c

class A
{
    static $c = 7;

    public static function getVal()
    {
        return self::$c;
    }
}

class B extends A
{
    static $c = 8;
}

B::getVal(); // 7

Pengikatan statis terlambat, perhatikan statis :: $ c

class A
{
    static $c = 7;

    public static function getVal()
    {
        return static::$c;
    }
}

class B extends A
{
    static $c = 8;
}

B::getVal(); // 8
Sergey Onishchenko
sumber
4

Sebagai contoh:

abstract class Builder {
    public static function build() {
        return new static;
    }
}

class Member extends Builder {
    public function who_am_i() {
         echo 'Member';
    }
}

Member::build()->who_am_i();
Petah
sumber
4

Melihatnya dari pertanyaan "mengapa saya menggunakan ini?" perspektif, ini pada dasarnya adalah cara untuk mengubah konteks dari mana metode statis sedang diinterpretasikan / dijalankan.

Dengan self, konteksnya adalah tempat Anda mendefinisikan metode aslinya. Dengan static, itu yang Anda panggil.

DanMan
sumber
1

Juga, perhatikan jika Anda memperbarui variabel statis di kelas anak. Saya menemukan ini (agak) hasil yang tidak terduga di mana anak B memperbarui anak C:

class A{
    protected static $things;
}

class B extends A {
    public static function things(){
        static::$things[1] = 'Thing B';
        return static::$things; 
    }
}

class C extends A{
    public static function things(){
        static::$things[2] = 'Thing C';
        return static::$things;        
    }
}

print_r(C::things());
// Array (
//   [2] => Thing C
// )

B::things();

print_r(C::things()); 
// Array (
//    [2] => Thing C
//    [1] => Thing B
// )

Anda dapat memperbaikinya dengan mendeklarasikan variabel yang sama di setiap kelas anak, misalnya:

class C extends A{
    protected static $things; // add this and B will not interfere!

    public static function things(){
        static::$things[2] = 'Thing C';
        return static::$things;        
    }
}
Frank Forte
sumber