PHP Attributes (หรือที่หลายคนคุ้นเคยในชื่อ “Annotations” จากภาษาอื่น) คือฟีเจอร์ที่ถูกเพิ่มเข้ามาตั้งแต่ PHP 8.0 มันคือการใส่ Metadata (ข้อมูลที่อธิบายข้อมูล) ให้กับโครงสร้างของโค้ด ไม่ว่าจะเป็น คลาส, เมธอด, ฟังก์ชัน, พร็อพเพอร์ตี้ หรือแม้กระทั่งพารามิเตอร์ โดยข้อมูลเหล่านี้จะไม่ส่งผลต่อการทำงานของโค้ดโดยตรง แต่เราสามารถดึงมาใช้ตรวจสอบหรือเปลี่ยนแปลงพฤติกรรมของโปรแกรมได้ผ่าน Reflection API
ก่อนหน้า PHP 8.0 เรามักจะใช้ Docblocks (คอมเมนต์ / ... */) ในการเขียนอธิบาย ซึ่งมีข้อเสียคือระบบไม่ได้ตรวจสอบไวยากรณ์ (Syntax) และดึงข้อมูลไปใช้ค่อนข้างยาก แต่พอมี Attributes ทุกอย่างก็กลายเป็นเรื่องที่ถูกต้องตามโครงสร้างของภาษา และใช้งานง่ายขึ้นมากครับ
💡 โครงสร้างและไวยากรณ์ (Syntax)
การประกาศใช้ Attribute ใน PHP จะใช้เครื่องหมาย #[ ] วางไว้เหนือสิ่งที่เราต้องการกำหนด Metadata
#[AttributeName]
class MyClass {
#[AttributeName]
public string $myProperty;
#[AttributeName]
public function myMethod(#[AttributeName] $argument) {}
}
นอกจากนี้เรายังสามารถส่งค่า (Arguments) เข้าไปใน Attribute ได้เหมือนกับการเรียกใช้งานฟังก์ชันปกติเลยครับ
#[JoinTable(name: "users_roles")]
#[Column("username", type: "string", length: 50)]
🛠️ วิธีการสร้างและใช้งาน Attributes (Step-by-Step)
ลองมาดูตัวอย่างการสร้างระบบ Validation ง่ายๆ ด้วย Attributes กันครับ เพื่อให้เห็นภาพการทำงานจริง
1. ประกาศคลาสสำหรับ Attribute
การสร้าง Attribute ก็คือการสร้างคลาสธรรมดา แต่เราต้องใส่ #[Attribute] ไว้บนหัวคลาส เพื่อบอกให้ PHP รู้ว่าคลาสนี้จะถูกนำไปใช้เป็น Attribute นะ
<?php
#[Attribute(Attribute::TARGET_PROPERTY)] // กำหนดให้ใช้ได้เฉพาะกับ Property เท่านั้น
class StringLength {
public function __construct(
public int $min,
public int $max
) {}
}
2. นำ Attribute ไปใช้งานในคลาสอื่น
ลองสร้างคลาส UserRegistration แล้วนำ #[StringLength] ที่เราสร้างเมื่อกี้ไปแปะไว้ที่พาสเวิร์ด
class UserRegistration {
#[StringLength(min: 8, max: 20)]
public string $password;
public function __construct(string $password) {
$this->password = $password;
}
}
3. อ่านค่า Metadata ด้วย Reflection API
นี่คือขั้นตอนสำคัญ เพราะถ้าเราไม่ใช้ Reflection API มาอ่านค่า ตัว Attribute ก็จะอยู่เฉย ๆ ไม่มีผลอะไรครับ
function validate($object) {
$reflection = new ReflectionClass($object);
// วนลูปดู Property ทั้งหมดในคลาส
foreach ($reflection->getProperties() as $property) {
// ดึง Attribute ชื่อ StringLength ออกมา
$attributes = $property->getAttributes(StringLength::class);
foreach ($attributes as $attribute) {
// แปลง Attribute กลับมาเป็น Instance ของคลาส StringLength เพื่อใช้งาน
$validator = $attribute->newInstance();
$value = $property->getValue($object);
// ตรวจสอบเงื่อนไข
if (strlen($value) < $validator->min || strlen($value) > $validator->max) {
throw new Exception("Property {$property->getName()} ต้องมีความยาวระหว่าง {$validator->min} ถึง {$validator->max} ตัวอักษร");
}
}
}
echo "Validation ผ่านฉลุย! ✨";
}
// --- ทดลองใช้งาน ---
try {
$user = new UserRegistration("12345"); // สั้นเกินไป (เงื่อนไขคือ 8-20)
validate($user);
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
// ผลลัพธ์: Error: Property password ต้องมีความยาวระหว่าง 8 ถึง 20 ตัวอักษร
}
🎯 ประโยชน์ของการใช้ Attributes
- Syntax Validation: มีระบบตรวจไวยากรณ์ตั้งแต่ตอนเขียน (IDE ช่วยเตือนได้) ไม่เหมือน Docblock ที่เป็นแค่คอมเมนต์ตัวหนังสือธรรมดา
- Performance: อ่านค่าได้เร็วกว่าการไปคอย Parse คอมเมนต์ผ่าน Regex แบบสมัยก่อนมาก
- Clean Code: แยกส่วนของ Logic หลัก ออกจากส่วนที่เป็น Configuration หรือ Metadata อย่างชัดเจน
- Framework Friendly: ปัจจุบันเฟรมเวิร์กดัง ๆ อย่าง Symfony (เช่น Routing) หรือ Laravel และคลังไลบรารีอย่าง Doctrine ORM ต่างเปลี่ยนมาใช้ Attributes เป็นมาตรฐานหลักแล้ว
อ่านเพิ่มเติม