Ketik petunjuk untuk properti di PHP 7?

Apakah php 7 mendukung petunjuk tipe untuk properti kelas?

Maksud saya, bukan hanya untuk setter/getter tapi juga untuk properti itu sendiri.

Sesuatu seperti:

class Foo {
    /**
     *
     * @var Bar
     */
    public $bar : Bar;
}

$fooInstance = new Foo();
$fooInstance->bar = new NotBar(); //Error

person CarlosCarucce    schedule 16.05.2016    source sumber
comment
Bukannya aku menyadarinya. Namun, secara umum, batasan apa pun pada nilai properti harus dilakukan melalui penyetel. Karena penyetel dapat dengan mudah mengetikkan argumen nilai, Anda siap melakukannya.   -  person Niet the Dark Absol    schedule 16.05.2016
comment
Banyak kerangka kerja di luar sana yang menggunakan atribut protected (kebanyakan untuk pengontrol). Khususnya untuk kasus-kasus seperti itu, ini akan sangat berguna.   -  person CarlosCarucce    schedule 16.05.2016


Jawaban (4)


PHP 7.4 akan mendukung properti yang diketik seperti:

class Person
{
    public string $name;
    public DateTimeImmutable $dateOfBirth;
}

PHP 7.3 dan sebelumnya tidak mendukung ini, tetapi ada beberapa alternatif.

Anda dapat membuat properti pribadi yang hanya dapat diakses melalui getter dan setter yang memiliki deklarasi tipe:

class Person
{
    private $name;
    public function getName(): string {
        return $this->name;
    }
    public function setName(string $newName) {
        $this->name = $newName;
    }
}

Anda juga dapat membuat properti publik dan menggunakan docblock untuk memberikan informasi tipe kepada orang yang membaca kode dan menggunakan IDE, namun ini tidak menyediakan pemeriksaan tipe runtime:

class Person
{
    /**
      * @var string
      */
    public $name;
}

Dan memang, Anda dapat menggabungkan getter dan setter serta docblock.

Jika Anda lebih suka bertualang, Anda bisa membuat properti palsu dengan __get, __set, __isset dan __unset metode ajaib, dan periksa sendiri jenisnya. Namun, saya tidak yakin apakah saya akan merekomendasikannya.

person Andrea    schedule 16.05.2016
comment
Kedengarannya sangat bagus. Tidak sabar untuk melihat apa yang akan terjadi pada rilis berikutnya! - person CarlosCarucce; 17.05.2016
comment
Masalah penting lainnya adalah penanganan referensi, yang tidak berinteraksi dengan baik dengan deklarasi tipe dan mungkin harus dinonaktifkan untuk properti tersebut. Bahkan tanpa masalah kinerja, tidak dapat melakukan, katakanlah, array_push($this->foo, $bar) atau sort($this->foobar) akan menjadi masalah besar. - person Andrea; 21.12.2017
comment
Bagaimana cara kerja pemaksaan tipe? Misalnya: (new Person())->dateOfBirth = '2001-01-01';... Asalkan declare(strict_types=0); yaitu. Apakah ia akan membuang atau menggunakan konstruktor DateTimeImmutable? Dan jika itu masalahnya, jenis kesalahan apa yang akan terjadi jika stringnya adalah tanggal yang tidak valid? TypeError? - person lmerino; 11.05.2019
comment
@Imerino Tidak ada konversi implisit ke DateTime (Immutable) dan belum pernah ada - person Andrea; 11.05.2019

7.4+:

Kabar baik bahwa ini akan diterapkan dalam rilis baru, seperti yang ditunjukkan oleh @Andrea. Saya hanya akan meninggalkan solusi ini di sini jika seseorang ingin menggunakannya sebelum 7.4


7,3 atau kurang

Berdasarkan notifikasi yang masih saya terima dari thread ini, saya yakin banyak orang di luar sana yang mengalami/sedang mengalami masalah yang sama seperti yang saya alami. Solusi saya untuk kasus ini adalah menggabungkan metode ajaib setter + __set di dalam suatu sifat untuk mensimulasikan perilaku ini. Ini dia:

trait SettersTrait
{
    /**
     * @param $name
     * @param $value
     */
    public function __set($name, $value)
    {
        $setter = 'set'.$name;
        if (method_exists($this, $setter)) {
            $this->$setter($value);
        } else {
            $this->$name = $value;
        }
    }
}

Dan inilah demonstrasinya:

class Bar {}
class NotBar {}

class Foo
{
    use SettersTrait; //It could be implemented within this class but I used it as a trait for more flexibility

    /**
     *
     * @var Bar
     */
    private $bar;

    /**
     * @param Bar $bar
     */
    protected function setBar(Bar $bar)
    {
        //(optional) Protected so it wont be called directly by external 'entities'
        $this->bar = $bar;
    }
}

$foo = new Foo();
$foo->bar = new NotBar(); //Error
//$foo->bar = new Bar(); //Success

Penjelasan

Pertama-tama, tentukan bar sebagai properti pribadi sehingga PHP akan menampilkan __set secara otomatis.

__set akan memeriksa apakah ada penyetel yang dideklarasikan di objek saat ini (method_exists($this, $setter)). Jika tidak, ia hanya akan menetapkan nilainya seperti biasanya.

Deklarasikan metode penyetel (setBar) yang menerima argumen petunjuk tipe (setBar(Bar $bar)).

Selama PHP mendeteksi bahwa sesuatu yang bukan instance Bar diteruskan ke penyetel, maka secara otomatis akan memicu Kesalahan Fatal: Uncaught TypeError: Argument 1 yang diteruskan ke Foo::setBar() harus berupa instance Bar, contoh NotBar diberikan

person CarlosCarucce    schedule 19.06.2018

Edit untuk PHP 7.4 :

Sejak PHP 7.4 Anda dapat mengetikkan atribut (Dokumentasi / Wiki) yang berarti Anda dapat melakukan :

    class Foo
{
    protected ?Bar $bar;
    public int $id;
    ...
}

Menurut wiki, semua nilai yang dapat diterima adalah:

  • bool, int, float, string, array, objek
  • dapat diubah
  • diri sendiri, orang tua
  • nama kelas atau antarmuka apa pun
  • ?type // dimana tipenya bisa berupa salah satu dari yang di atas

PHP ‹ 7,4

Ini sebenarnya tidak mungkin dan Anda hanya memiliki 4 cara untuk mensimulasikannya:

  • Nilai dasar
  • Dekorator di blok komentar
  • Nilai default di konstruktor
  • Pengambil dan penyetel

Saya menggabungkan semuanya di sini

class Foo
{
    /**
     * @var Bar
     */
    protected $bar = null;

    /** 
    * Foo constructor
    * @param Bar $bar
    **/
    public function __construct(Bar $bar = null){
        $this->bar = $bar;
    }
    
    /**
    * @return Bar
    */
    public function getBar() : ?Bar{
        return $this->bar;
    }

    /**
    * @param Bar $bar
    */
    public function setBar(Bar $bar) {
        $this->bar = $bar;
    }
}

Perhatikan bahwa Anda sebenarnya dapat mengetikkan return sebagai ?Bar sejak php 7.1 (nullable) karena bisa jadi null (tidak tersedia di php7.0.)

Anda juga dapat mengetikkan return sebagai void sejak php7.1

person Bruno Guignard    schedule 04.09.2018

Anda dapat menggunakan penyetel

class Bar {
    public $val;
}

class Foo {
    /**
     *
     * @var Bar
     */
    private $bar;

    /**
     * @return Bar
     */
    public function getBar()
    {
        return $this->bar;
    }

    /**
     * @param Bar $bar
     */
    public function setBar(Bar $bar)
    {
        $this->bar = $bar;
    }

}

$fooInstance = new Foo();
// $fooInstance->bar = new NotBar(); //Error
$fooInstance->setBar($fooInstance);

Keluaran:

TypeError: Argument 1 passed to Foo::setBar() must be an instance of Bar, instance of Foo given, called in ...
person Richard    schedule 16.05.2016