ป้ายกำกับ: Read-only

PHP: Readonly Properties & Readonly ClassesPHP: Readonly Properties & Readonly Classes

ในการเขียนโปรแกรมเชิงวัตถุ (OOP) ปัญหาหนึ่งที่เรามักเจอบ่อย ๆ คือ “ข้อมูลถูกเปลี่ยนแปลงโดยไม่ตั้งใจ” (Unexpected Mutation) ซึ่งเป็นสาเหตุหลักที่ทำให้เกิดบั๊กที่ไล่หายาก

เพื่อแก้ปัญหานี้ PHP จึงได้เพิ่มฟีเจอร์ Readonly Properties (เข้ามาใน PHP 8.1) และต่อยอดเป็น Readonly Classes (ใน PHP 8.2) เพื่อช่วยให้เราสร้าง Object ที่เสถียร ปลอดภัย และทำความเข้าใจได้ง่ายขึ้น หรือที่เรียกว่า Immutable Object ครับ


1. Readonly Properties (PHP 8.1)

ก่อนหน้า PHP 8.1 เวลาเราอยากได้ Property ที่อ่านได้อย่างเดียว แต่ห้ามแก้ไข (Read-only) เรามักจะต้องทำ Property นั้นให้เป็น private แล้วเขียน Method getter ขึ้นมาครอบแบบนี้

class User {
    private string $name;

    public function __construct(string $name) {
        $name; // [1] กำหนดค่าครั้งแรก
    }

    public function getName(): string {
        return $this->name; // [2] สร้าง getter เพื่อให้อ่านได้อย่างเดียว
    }
}

แต่พอมี Readonly Properties เราสามารถลดโค้ดฟุ่มเฟือย (Boilerplate code) เหล่านั้นให้เหลือสั้นๆ แบบนี้ได้เลย

class User {
    public readonly string $name;

    public function __construct(string $name) {
        $this->name = $name; // กำหนดค่าได้แค่ครั้งเดียวที่นี่
    }
}

$user = new User("Alice");
echo $user->name; // อ่านค่าได้ปกติ -> "Alice"

$user->name = "Bob"; // ❌ Error: Cannot modify readonly property

⚠️ กฎเหล็กของ Readonly Properties ที่ต้องรู้

  • ต้องระบุ Type เสมอ: คุณไม่สามารถใช้ readonly กับ Property ที่ไม่มีการระบุ Type ได้ (ยกเว้นจะใช้ mixed)
    public readonly $name; // ❌ แบบนี้ Error
    public readonly mixed $name; //  แบบนี้ทำได้
    
  • ห้ามตั้ง Default Value: ไม่สามารถใส่ค่าเริ่มต้นในตัวแปรตรง ๆ ได้ เพราะจะถือว่าเป็นการกำหนดค่าไปแล้ว
    public readonly string $role = 'admin'; // ❌ Error
    
  • กำหนดค่าได้เพียง “ครั้งเดียว” (Once Mutated): เมื่อ Property นั้นถูกใส่ค่าเข้าไปแล้ว (มักจะทำใน __construct) จะไม่สามารถเปลี่ยนค่าหรือใช้คำสั่ง unset() กับมันได้อีกเลย

2. Readonly Classes (PHP 8.2)

ต่อมาใน PHP 8.2 ถ้าเราต้องการให้ทุกๆ Property ใน Class นั้นเป็น readonly ทั้งหมด แทนที่เราจะต้องมานั่งพิมพ์คำว่า readonly หน้าตัวแปรทุกตัว PHP เลยใจดีให้เรายกคำว่า readonly ไปไว้หน้า class ได้เลย!

// โครงสร้างแบบ Readonly Class
readonly class BlogPost {
    public function __construct(
        public string $title,
        public string $content,
        public int $viewCount
    ) {}
}

$post = new BlogPost("PHP 8.2", "เนื้อหาเกี่ยวกับ Readonly Class...", 100);
$post->title = "เปลี่ยนชื่อบทความ"; // ❌ Error: Cannot modify readonly property

การประกาศแบบนี้ จะทำให้ Property ทั้งหมดใน Class (รวมถึงตัวที่เราอาจจะเพิ่มเข้ามาในอนาคต) กลายเป็น readonly โดยอัตโนมัติ

⚠️ กฎเหล็กของ Readonly Classes

  • ทุก Property ต้องมี Type: เช่นเดียวกับระดับ Property ย่อย ตัวแปรทุกตัวใน Class นี้ต้องระบุ Type เสมอ
  • ห้ามใช้ Dynamic Properties: คุณไม่สามารถแอบยัด Property แปลกปลอมเข้าไปใน Object ทีหลังได้ (เช่น $post->unknownProp = 'test'; จะพังทันที)
  • การสืบทอด (Inheritance): Class ลูกที่มาสืบทอดจาก Readonly Class จะต้องเป็น Readonly Class ด้วยเท่านั้น

สรุป: ควรใช้เมื่อไหร่?

ฟีเจอร์เหมาะสำหรับ
Readonly PropertiesClass ทั่วไปที่เราต้องการล็อกแค่บาง Property ไม่ให้แก้ไข (เช่น ID, วันที่สร้าง) แต่ยังอยากให้ Property อื่น ๆ เปลี่ยนแปลงค่าได้ตามปกติ
Readonly Classesเหมาะอย่างยิ่งสำหรับทำ Data Transfer Objects (DTOs), Value Objects หรือพวก Configurationต่าง ๆ ที่เราต้องการให้ส่งผ่านข้อมูลข้ามระบบไปมาอย่างปลอดภัย โดยมั่นใจได้ว่าข้อมูลระหว่างทางจะไม่ถูกแก้ไขแน่นอน

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