ป้ายกำกับ: ฟังก์ชัน

PHP: Intersection TypesPHP: Intersection Types

Intersection Types เป็นหนึ่งในฟีเจอร์เด่นที่ถูกเพิ่มเข้ามาตั้งแต่วันที่ PHP 8.1 ซึ่งช่วยให้เราสามารถกำหนด Type ให้กับตัวแปรหรือ Parameter โดยบังคับว่าข้อมูลนั้นจะต้องเป็นสมาชิกของ ทุกๆ Type ที่กำหนดไว้พร้อมกัน (โอเปอเรเตอร์ &)

ถ้าอธิบายให้เห็นภาพง่าย ๆ

  • Union Types (|): เลือกอย่างใดอย่างหนึ่ง (อันนี้หรืออันนั้น)
  • Intersection Types (&): ต้องเป็นทั้งหมด (อันนี้และอันนั้น)

ทำไมเราถึงต้องใช้ Intersection Types?

ในโลกของการเขียนโค้ดแบบ Object-Oriented Programming (OOP) เรามักจะแบ่งความสามารถของ Object ออกเป็น Interface ย่อยๆ ตามหลัก Interface Segregation Principle (ซอย Interface ให้เล็กลงเพื่อให้ทำงานเฉพาะทาง)

แต่ปัญหาจะเกิดตอนที่เราอยากสร้างฟังก์ชันที่ต้องการ Object ที่มีความสามารถจาก หลาย ๆ Interface พร้อมกัน ### ตัวอย่างปัญหา (ก่อน PHP 8.1) สมมติว่าเรามี 2 อินเตอร์เฟส คือ Countable (นับจำนวนได้) และ Iterator (วนลูปได้) ถ้าเราอยากสร้างฟังก์ชันที่รับค่าเข้ามาแล้วต้องทั้งนับได้และวนลูปได้ด้วย เราทำได้แค่

  1. เลือก Type ใด Type หนึ่ง แล้วไปเช็คด้วย is_a() หรือ instanceof ข้างในฟังก์ชันเอาเอง (ซึ่งโค้ดจะไม่สวยและเสี่ยงพังตอน Runtime)
  2. สร้าง Interface ใหม่ขึ้นมาผูกรวมกัน เช่น interface CountableIterator extends Countable, Iterator {} ซึ่งทำให้เกิดขยะในระบบโดยไม่จำเป็น

วิธีการใช้งานใน PHP 8.1+

ด้วย Intersection Types เราสามารถใช้เครื่องหมายแอมเพอร์แซนด์ (&) เชื่อม Type ที่เราต้องการได้ทันที โดยไม่ต้องสร้าง Interface ใหม่ขึ้นมาให้รกตระกูล

interface Serializable {
    public function serialize(): string;
}

interface Loggable {
    public function log(string $message): void;
}

// ฟังก์ชันนี้จะรับเฉพาะ Object ที่ implement ทั้ง Serializable และ Loggable เท่านั้น
function processData(Serializable & Loggable $item) {
    // สามารถเรียกใช้ Method จากทั้งสอง Interface ได้อย่างปลอดภัย
    $item->log("Processing item...");
    return $item->serialize();
}

หากเราส่ง Object ที่มีแค่ความสามารถเดียว หรือไม่ครบตามเงื่อนไข PHP จะโยน TypeError ออกมาให้ทันทีตั้งแต่ต้นทาง ช่วยลด Bug ได้มหาศาล


ข้อจำกัดสำคัญที่ควรรู้

แม้ว่าจะทรงพลัง แต่ Intersection Types มีกฎเหล็กที่ต้องจำไว้ดังนี้ครับ

  • ใช้ได้กับ Class และ Interface เท่านั้น: คุณไม่สามารถใช้กับ Scalar types เช่น string & int หรือ array & callable ได้ (เพราะมันไม่มีทางที่ตัวแปรเดียวจะเป็นทั้ง string และ int พร้อมกันได้ในทางตรรกะ)
  • ไม่สามารถผสมกับ Union Types ตรง ๆ ได้ (ใน PHP 8.1): คุณไม่สามารถเขียน A & B | C ได้
    โน้ต: ตั้งแต่ PHP 8.2 เป็นต้นไป คุณสามารถทำได้แล้วผ่านฟีเจอร์ DNF Types (Disjunctive Normal Form) โดยต้องใส่ฟังก์ชันวงเล็บให้ชัดเจน เช่น (A & B) | C

สรุป

Intersection Types ช่วยให้การทำ Type Hinting ใน PHP มีความยืดหยุ่นและปลอดภัยมากขึ้น เหมาะมากสำหรับการเขียนโค้ดสไตล์ Clean Architecture หรือการออกแบบระบบที่เน้น Composition over Inheritance (การประกอบร่างมากกว่าการสืบทอดคลาส)


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