ป้ายกำกับ: Callback

PHP: First-class Callable Syntax วิธีอ้างอิงฟังก์ชันแบบโมเดิร์นPHP: First-class Callable Syntax วิธีอ้างอิงฟังก์ชันแบบโมเดิร์น

นับตั้งแต่ PHP 8.1 เป็นต้นมา มีการฟีเจอร์หนึ่งที่ถูกเพิ่มเข้ามาและช่วยเปลี่ยนวิถีการเขียนโค้ดของเราให้สะอาดขึ้นอย่างมาก นั่นคือ First-class Callable Syntax ครับ หากคุณเคยเบื่อกับการที่ต้องส่งชื่อฟังก์ชันเป็น String หรือส่ง Array แปลก ๆ เวลาทำ Callback ฟังก์ชันนี้คือคำตอบครับ


ปัญหาของการอ้างอิงฟังก์ชันแบบเดิม (ก่อน PHP 8.1)

ก่อนหน้านี้เวลาที่เราต้องการนำฟังก์ชันหรือเมธอดไปเป็นอาร์กิวเมนต์ (Callback) ในฟังก์ชันอื่น เช่น array_map หรือ usort เรามักจะใช้วิธีส่งเป็น String หรือ Array ดังนี้ครับ

// 1. อ้างอิงฟังก์ชันทั่วไปด้วย String
$lengths = array_map('strlen', ['apple', 'banana']);

// 2. อ้างอิง Method ของ Object ด้วย Array [$object, 'method']
$processor = new DataProcessor();
$result = array_map([$processor, 'cleanData'], $dirtyData);

// 3. อ้างอิง Static Method ด้วย String "Class::method" หรือ Array
$slugs = array_map('StringHelper::slugify', $titles);

ทำไมวิธีเดิมถึงมีปัญหา?

  • ไม่มี Type Safety: IDE ไม่รู้ว่า String หรือ Array นั้นคือฟังก์ชันจริง ๆ หรือเปล่า จนกว่าโค้ดจะรัน (Runtime)
  • หา Bug ยาก: ถ้าคุณพิมพ์ชื่อฟังก์ชันผิดใน String (เช่น 'str_len' แทน 'strlen') IDE จะไม่แจ้งเตือน (No Static Analysis support)
  • Refactor ลำบาก: เวลาจะเปลี่ยนชื่อ Method ด้วยเครื่องมือใน IDE ชื่อใน String มักจะไม่ถูกเปลี่ยนตามไปด้วย

พระเอกมาแล้ว: First-class Callable Syntax คืออะไร?

ใน PHP 8.1 ได้นำรูปแบบการเขียนแบบใหม่มาใช้ โดยการใช้เครื่องหมาย ... (Ellipsis) ต่อท้ายชื่อฟังก์ชันหรือเมธอดที่เราต้องการอ้างอิง ระบบจะทำการสร้าง Closure (หรืออ็อบเจกต์ที่เรียกใช้งานได้) ขึ้นมาให้เราทันที โดยที่เราไม่ต้องครอบด้วย String อีกต่อไป

รูปแบบโครงสร้าง (Syntax)

ฟังก์ชันที่ต้องการ(…)


เปรียบเทียบการใช้งาน: อดีต VS ปัจจุบัน

ลองมาดูกันครับว่าเมื่อเปลี่ยนมาใช้ First-class Callable Syntax แล้ว โดคจะดูดีขึ้นขนาดไหน


ฟังก์ชันทั่วไป (Standard Functions)

  • แบบเดิม: 'strlen'
  • แบบใหม่: strlen(...)
$words = ['hello', 'php', 'world'];
// แบบใหม่: ไม่ต้องใส่เครื่องหมายคำพูดอีกต่อไป
$lengths = array_map(strlen(...), $words);

เมธอดของอ็อบเจกต์ (Instance Methods)

  • แบบเดิม: [$this, 'formatDate']
  • แบบใหม่: $this->formatDate(...)
class UserRenderer {
    public function format(array $users): array {
        // อ้างอิงเมธอดภายใน Class ตัวเองได้หล่อๆ
        return array_map($this->renderRow(...), $users);
    }

    private function renderRow(User $user): string {
        return "<li>{$user->name}</li>";
    }
}

สแตติกเมธอด (Static Methods)

  • แบบเดิม: ['Helper', 'generateUuid'] หรือ 'Helper::generateUuid'
  • แบบใหม่: Helper::generateUuid(...)
$ids = [1, 2, 3];
// IDE รู้ทันทีว่าเรียกใช้ Static Method ตัวไหน
$uuids = array_map(UUIDGenerator::fromId(...), $ids);

ข้อดีที่คุณจะได้รับทันทีที่ย้ายมาใช้

  1. IDE Friendly & Autocomplete: สิ้นสุดยุคเดาชื่อฟังก์ชันใน String เพราะตอนนี้ IDE (เช่น PHPStorm, VS Code) จะมีระเบิดความสามารถ Autocomplete ขึ้นมาให้ และช่วยเช็กให้ทันทีว่าฟังก์ชันนั้นมีอยู่จริงไหม
  2. ปลอดภัยตอน Refactor: ถ้าคุณเปลี่ยนชื่อเมธอด ตัว IDE จะไล่เปลี่ยนโค้ดที่ใช้ ... ให้โดยอัตโนมัติ โอกาสเกิด Bug ลดลงมหาศาล
  3. ประสิทธิภาพที่จับต้องได้: การใช้ ... จะสร้าง Closure ขึ้นมาตั้งแต่ตอนที่คอมไพล์ ซึ่งเร็วกว่าและกินทรัพยากรน้อยกว่าการให้ PHP ไปแกะ String/Array ในตอน Runtime

ข้อควรรู้เพิ่มเติม

[!NOTE] ฟีเจอร์นี้ไม่สามารถใช้ร่วมกับ ตรรกะการเรียกแบบ Dynamic ที่ระบุชื่อฟังก์ชันในตัวแปรตรง ๆ ได้ เช่น $func(...) (ถ้า $func เป็น string) แต่คุณสามารถเลี่ยงไปใช้ Closure::fromCallable($func) แทนได้หากจำเป็นจริง ๆ


สรุป

First-class Callable Syntax (...) เป็นการยกระดับภาษา PHP ให้มีความเป็น Modern Language มากขึ้น มันช่วยปิดช่องโหว่เรื่องความผิดพลาดจากการพิมพ์ String ผิด และทำให้เครื่องมือพัฒนาทำงานได้อย่างเต็มประสิทธิภาพ

ถ้าโปรเจกต์ของคุณรันบน PHP 8.1 ขึ้นไป แล้วล่ะก็ แนะนำให้เริ่มเปลี่ยนมาใช้รูปแบบนี้แทน String หรือ Array Callback ได้เลยครับ โค้ดสะอาดขึ้นแน่นอน!


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