ป้ายกำกับ: Abstract

PHP: Constructor Property PromotionPHP: Constructor Property Promotion

PHP: Constructor Property Promotion เป็นฟีเจอร์ที่ถูกเพิ่มเข้ามาตั้งแต่ PHP 8.0 ซึ่งช่วยให้เราสามารถประกาศและกำหนดค่าให้กับ Property (ตัวแปรของคลาส) ได้โดยตรงภายใน Constructor เลย พูดง่าย ๆ คือมันช่วยลดโค้ดที่ซ้ำซ้อน (Boilerplate code) ลงไปได้เยอะมากครับ

เพื่อให้เห็นภาพชัดเจน ลองมาดูการเปรียบเทียบระหว่างแบบเดิม กับแบบใหม่กันครับ


เปรียบเทียบแบบเก่า vs แบบใหม่

สมมติว่าเราต้องการสร้างคลาส User ที่มี property 3 ตัวคือ id, name และ email

แบบเดิม (ก่อน PHP 8.0): เราต้องประกาศ Property ด้านบน ทำ Type hinting จากนั้นก็ต้องมาเขียนสั่ง $this->... = $... ซ้ำๆ อีกรอบใน constructor

class User {
    public int $id;
    public string $name;
    public string $email;

    public function __construct(int $id, string $name, string $email) {
        $this->id = $id;
        $this->name = $name;
        $this->email = $email;
    }
}

แบบใหม่ที่ใช้ Constructor Property Promotion:

เราสามารถใส่ Visibility Keyword (public, protected, private) ไว้ข้างหน้าพารามิเตอร์ใน constructor ได้เลย PHP จะเข้าใจทันทีว่าเราต้องการให้มันเป็นทั้ง Property ของคลาส และรับค่าไปพร้อมกัน

class User {
    public function __construct(
        public int $id,
        public string $name,
        public string $email
    ) {} // ตัว Body ของ constructor ปล่อยว่างไว้ได้เลยถ้าไม่มีลอจิกอื่น
}

ผลลัพธ์: โค้ดสั้นลง อ่านง่ายขึ้น และลดโอกาสเขียนชื่อตัวแปรผิดไปได้เยอะเลยครับ


กฎและข้อควรรู้ในการใช้งาน

แม้ว่าจะสะดวกมาก แต่ก็มีข้อกำหนดบางอย่างที่ต้องระวังครับ

  • ต้องมี Visibility Keyword เสมอ: ถ้าเราไม่ใส่ public, protected, หรือ private หน้าพารามิเตอร์ PHP จะมองว่าเป็นพารามิเตอร์ของฟังก์ชันธรรมดา ไม่ใช่การทำ Property Promotion
  • กำหนด Default Value ได้ตามปกติ: เราสามารถใส่ค่าเริ่มต้นให้ตัวแปรได้เหมือนเดิม
    public function __construct(public string $role = 'guest') {}
    
  • ทำงานร่วมกับพารามิเตอร์ปกติได้: ใน constructor เดียวกัน สามารถมีทั้งตัวแปรธรรมดา และตัวแปรที่ทำ Promotion ร่วมกันได้
    public function __construct(
        public string $name, 
        $normalArgument // ตัวนี้จะเป็นแค่ตัวแปรธรรมดา ไม่ถูกสร้างเป็น property ของคลาส
    ) {}
    
  • ไม่สามารถใช้กับ Abstract Constructor ได้: เพราะคลาสที่เป็น Abstract หรือ Interface ไม่มีการทำงานของตัว body ใน constructor จริง ๆ
  • ใช้กับ Callable Type ไม่ได้: เราไม่สามารถใช้ callable เป็น Type ของ promoted property ได้เนื่องจากข้อจำกัดของโครงสร้าง PHP Engine

แล้วถ้ายังอยากมี Logic ใน Constructor ล่ะ?

ถ้าเราจำเป็นต้องเช็คความถูกต้องของข้อมูล (Validation) ก่อนบันทึกค่า ก็ยังสามารถเขียนโค้ดลงไปใน {} ของ constructor ได้ตามปกติครับ เพราะ PHP จะแอบแปลงโค้ดและส่งค่าเข้า property ให้เราก่อนที่โค้ดใน {} จะทำงาน

class Product {
    public function __construct(
        public string $name,
        public float $price
    ) {
        // สามารถเขียน logic ตรวจสอบต่อได้ทันที
        if ($this->price < 0) {
            throw new InvalidArgumentException("ราคาติดลบไม่ได้นะ!");
        }
    }
}

อ่านเพิ่มเติม