พิมพ์คำใบ้สำหรับคุณสมบัติใน PHP 7 หรือไม่

ประเภทการสนับสนุน php 7 บอกใบ้คุณสมบัติของคลาสหรือไม่

ฉันหมายถึง ไม่ใช่แค่สำหรับ setters/getters แต่สำหรับทรัพย์สินด้วย

สิ่งที่ต้องการ:

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

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

person CarlosCarucce    schedule 16.05.2016    source แหล่งที่มา
comment
ไม่ใช่ว่าฉันรู้ตัว อย่างไรก็ตาม โดยทั่วไปแล้ว ข้อจำกัด ใดๆ เกี่ยวกับมูลค่าของทรัพย์สินควรจะดำเนินการผ่านผู้ตั้งค่าอยู่แล้ว เนื่องจากผู้ตั้งค่าสามารถพิมพ์คำใบ้สำหรับอาร์กิวเมนต์ค่าได้อย่างง่ายดาย คุณจึงพร้อมใช้งาน   -  person Niet the Dark Absol    schedule 16.05.2016
comment
เฟรมเวิร์กจำนวนมากใช้แอตทริบิวต์ protected (ส่วนใหญ่สำหรับคอนโทรลเลอร์) สำหรับกรณีเหล่านั้นโดยเฉพาะมันจะมีประโยชน์มาก   -  person CarlosCarucce    schedule 16.05.2016


คำตอบ (4)


PHP 7.4 จะรองรับคุณสมบัติการพิมพ์ ดังนี้:

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

PHP 7.3 และรุ่นก่อนหน้าไม่รองรับสิ่งนี้ แต่มีทางเลือกอื่นอยู่

คุณสามารถสร้างทรัพย์สินส่วนตัวที่สามารถเข้าถึงได้ผ่าน getters และ setters เท่านั้นซึ่งมีการประกาศประเภท:

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

คุณยังสามารถสร้างทรัพย์สินสาธารณะและใช้ docblock เพื่อให้ข้อมูลประเภทแก่ผู้ที่อ่านโค้ดและใช้ IDE ได้ แต่จะไม่มีการตรวจสอบประเภทรันไทม์:

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

และแน่นอน คุณสามารถรวม getters และ setters และ docblock ได้

หากคุณชอบผจญภัยมากขึ้น คุณสามารถสร้างทรัพย์สินปลอมได้ด้วย __get, __set, __isset และ __unset วิธีเวทย์มนตร์ และตรวจสอบประเภทด้วยตัวเอง ฉันไม่แน่ใจว่าจะแนะนำได้ไหม

person Andrea    schedule 16.05.2016
comment
ฟังดูดีจริงๆ แทบรอไม่ไหวที่จะเห็นว่ามีอะไรออกมาในรุ่นต่อไป! - person CarlosCarucce; 17.05.2016
comment
ปัญหาที่สำคัญอีกประการหนึ่งคือการจัดการข้อมูลอ้างอิง ซึ่งไม่ค่อยโต้ตอบกับการประกาศประเภทและอาจต้องปิดใช้งานคุณสมบัติดังกล่าว แม้ว่าจะไม่มีปัญหาด้านประสิทธิภาพก็ตาม การไม่สามารถทำ array_push($this->foo, $bar) หรือ sort($this->foobar) ได้คงเป็นเรื่องใหญ่ - person Andrea; 21.12.2017
comment
การบังคับประเภทจะทำงานอย่างไร? ตัวอย่างเช่น: (new Person())->dateOfBirth = '2001-01-01';... ให้ไว้ declare(strict_types=0); นั่นคือ มันจะโยนหรือใช้ตัวสร้าง DateTimeImmutable หรือไม่ และหากเป็นเช่นนั้น ข้อผิดพลาดประเภทใดที่จะเกิดขึ้นหากสตริงเป็นวันที่ที่ไม่ถูกต้อง TypeError? - person lmerino; 11.05.2019
comment
@Imerino ไม่มีการแปลงโดยนัยเป็น DateTime (ไม่เปลี่ยนรูป) และไม่เคยมีมาก่อน - person Andrea; 11.05.2019

7.4+:

ข่าวดีที่จะมีการนำมาใช้ในรุ่นใหม่ ตามที่ @Andrea ชี้ให้เห็น ฉันจะทิ้งวิธีแก้ปัญหานี้ไว้ที่นี่เผื่อมีคนต้องการใช้ก่อน 7.4


7.3 หรือน้อยกว่า

จากการแจ้งเตือนที่ฉันยังคงได้รับจากกระทู้นี้ ฉันเชื่อว่าผู้คนจำนวนมากมี/กำลังประสบปัญหาเดียวกันกับฉัน วิธีแก้ปัญหาของฉันสำหรับกรณีนี้คือการรวม setters + __set magic method ไว้ภายในลักษณะเพื่อจำลองพฤติกรรมนี้ นี่คือ:

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;
        }
    }
}

และนี่คือการสาธิต:

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

คำอธิบาย

ก่อนอื่น กำหนดให้ bar เป็นทรัพย์สินส่วนตัว ดังนั้น PHP จะส่ง __set โดยอัตโนมัติ

__set จะตรวจสอบว่ามีตัวตั้งค่าบางตัวประกาศในวัตถุปัจจุบัน (method_exists($this, $setter)) มิฉะนั้นมันจะตั้งค่าของมันตามปกติเท่านั้น

ประกาศวิธีการ setter (setBar) ที่ได้รับอาร์กิวเมนต์ประเภทคำแนะนำ (setBar(Bar $bar))

ตราบใดที่ PHP ตรวจพบว่ามีบางสิ่งที่ไม่ใช่ Bar instance ถูกส่งผ่านไปยัง setter มันจะทำให้เกิดข้อผิดพลาดร้ายแรงโดยอัตโนมัติ: Uncaught TypeError: อาร์กิวเมนต์ 1 ที่ส่งผ่านไปยัง Foo::setBar() จะต้องเป็นอินสแตนซ์ของ Bar อินสแตนซ์ของ NotBar ที่ได้รับ

person CarlosCarucce    schedule 19.06.2018

แก้ไขสำหรับ PHP 7.4 :

ตั้งแต่ PHP 7.4 คุณสามารถพิมพ์แอตทริบิวต์ได้ (เอกสารประกอบ / Wiki) ซึ่งหมายความว่าคุณสามารถทำได้ :

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

ตามวิกิ ค่าที่ยอมรับได้ทั้งหมดคือ:

  • บูล, int, ลอย, สตริง, อาร์เรย์, วัตถุ
  • ทำซ้ำได้
  • ตนเองผู้ปกครอง
  • ชื่อคลาสหรืออินเทอร์เฟซใด ๆ
  • ?type // โดยที่ type อาจเป็นอย่างใดอย่างหนึ่งข้างต้น

PHP ‹ 7.4

มันเป็นไปไม่ได้จริงๆ และคุณมีเพียง 4 วิธีในการจำลองมัน:

  • ค่าเริ่มต้น
  • ผู้ตกแต่งในบล็อกความคิดเห็น
  • ค่าเริ่มต้นในตัวสร้าง
  • ทะเยอทะยานและผู้ตั้งค่า

ฉันรวมทั้งหมดไว้ที่นี่

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;
    }
}

โปรดทราบว่าจริงๆ แล้วคุณสามารถพิมพ์ return เป็น ?Bar ได้ตั้งแต่ php 7.1 (nullable) เพราะอาจเป็น null (ไม่มีใน php7.0)

คุณยังสามารถพิมพ์การส่งคืนเป็นโมฆะตั้งแต่ php7.1

person Bruno Guignard    schedule 04.09.2018

คุณสามารถใช้ตัวตั้งค่า

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);

เอาท์พุท:

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