แสดงบทความที่มีป้ายกำกับ Coding แสดงบทความทั้งหมด
แสดงบทความที่มีป้ายกำกับ Coding แสดงบทความทั้งหมด

วันอังคารที่ 11 มีนาคม พ.ศ. 2568

[PHP] การใช้ isset() และ empty() ต่างกันอย่างไร?

ถ้าพูดถึง PHP ฟังก์ชันที่เกี่ยวกับการเช็กค่าตัวแปรสองตัวที่ถูกใช้บ่อยมาก ๆ ก็คือ isset() และ empty() หลายคนอาจจะงงว่ามันต่างกันยังไง? ใช้ตัวไหนเมื่อไหร่? วันนี้เราจะมาอธิบายให้เข้าใจแบบง่าย ๆ เหมือนนั่งคุยกับเพื่อน!

1. isset() คืออะไร?

isset() ใช้สำหรับตรวจสอบว่าตัวแปรนั้น ถูกกำหนดค่าแล้วหรือไม่ และ ไม่ได้เป็นค่า NULL ถ้าตัวแปรนั้นถูกกำหนดค่าแล้ว (ไม่ว่าจะเป็นค่าที่ว่างเปล่าแต่ไม่ใช่ NULL) มันจะคืนค่า true แต่ถ้าตัวแปรนั้นไม่มีอยู่หรือมีค่าเป็น NULL จะคืนค่า false

ตัวอย่างการใช้งาน isset()

$var1 = "Hello, PHP!";
$var2 = NULL;

var_dump(isset($var1)); // true
var_dump(isset($var2)); // false
var_dump(isset($var3)); // false เพราะ $var3 ไม่มีการประกาศ

2. empty() คืออะไร?

empty() ใช้เพื่อตรวจสอบว่าตัวแปร เป็นค่าที่ถือว่า "ว่างเปล่า" หรือไม่ หมายถึง ค่าเป็น NULL, 0, "" (สตริงว่าง), false, array() (อาร์เรย์ว่าง), หรือไม่มีการกำหนดค่ามาก่อน ถ้าตัวแปรอยู่ในสถานะ "ว่างเปล่า" มันจะคืนค่า true ถ้าไม่ว่างเปล่าจะคืนค่า false

ตัวอย่างการใช้งาน empty()

$var1 = ""; // สตริงว่าง
$var2 = 0;   // ศูนย์
$var3 = NULL;
$var4 = [];
$var5 = "Hello";

var_dump(empty($var1)); // true
var_dump(empty($var2)); // true
var_dump(empty($var3)); // true
var_dump(empty($var4)); // true
var_dump(empty($var5)); // false เพราะมันมีค่าจริงๆ

3. เปรียบเทียบ isset() และ empty()


สถานะของตัวแปร isset($var) empty($var)
ไม่ได้กำหนดค่า false true
NULL false true
false true true
0 true true
"" (สตริงว่าง) true true
"PHP" (สตริงที่มีค่า) true false
[] (อาร์เรย์ว่าง) true true

4. แล้วเราควรใช้ตัวไหน?

  • ถ้าต้องการ เช็กว่าตัวแปรถูกกำหนดค่าหรือไม่ ให้ใช้ isset()
  • ถ้าต้องการ เช็กว่าตัวแปรมีค่าที่ "ว่างเปล่า" หรือไม่ ให้ใช้ empty()

ตัวอย่างการใช้งานในชีวิตจริง

ใช้ isset() ตรวจสอบว่ามีค่าหรือไม่ก่อนใช้งาน

if (isset($_POST['username'])) {
    echo "Username: " . $_POST['username'];
} else {
    echo "กรุณากรอกชื่อผู้ใช้";
}

ใช้ empty() ตรวจสอบว่าค่าว่างหรือไม่

if (empty($_POST['password'])) {
    echo "กรุณากรอกรหัสผ่าน";
} else {
    echo "รหัสผ่านของคุณถูกบันทึกแล้ว";
}

5. ใช้ isset() และ empty() ร่วมกัน

บางกรณีเราอาจใช้ทั้งสองฟังก์ชันร่วมกัน เช่น

if (isset($_POST['email']) && !empty($_POST['email'])) {
    echo "อีเมลของคุณคือ " . $_POST['email'];
} else {
    echo "กรุณากรอกอีเมล";
}

ในกรณีนี้ isset() จะช่วยให้แน่ใจว่าตัวแปรถูกกำหนดแล้ว และ empty() จะช่วยให้แน่ใจว่ามันไม่ใช่ค่าที่ว่างเปล่า

6. ข้อควรระวังในการใช้ isset() และ empty()

  1. isset() ไม่สามารถตรวจสอบได้ว่าค่าตัวแปรว่างเปล่าหรือไม่
  2. empty() สามารถเช็กตัวแปรที่ไม่ได้ประกาศได้เลยโดยไม่เกิด Error
  3. ระวังเรื่องค่าของตัวเลข 0 เพราะ empty(0) จะคืนค่า true

ตัวอย่างที่ต้องระวัง:

$var = 0;
if (empty($var)) {
    echo "ตัวแปรนี้ว่าง";
} else {
    echo "ตัวแปรนี้มีค่า";
}
// ผลลัพธ์: ตัวแปรนี้ว่าง (เพราะ empty(0) ให้ค่าเป็น true)

7. สรุปแบบเข้าใจง่าย

  • isset($var): เช็กว่าตัวแปรถูกกำหนดค่าแล้ว และไม่ใช่ NULL
  • empty($var): เช็กว่าตัวแปร "ไม่มีค่า" หรือเป็นค่าที่ถือว่าว่างเปล่า
  • isset() คืนค่า false เมื่อตัวแปรเป็น NULL หรือไม่มีอยู่
  • empty() คืนค่า true เมื่อค่าของตัวแปรเป็น 0, "", false, NULL, array(), หรือไม่มีอยู่เลย
  • ควรใช้ isset() เช็กว่าตัวแปรมีอยู่หรือไม่ และ empty() เช็กว่าตัวแปรมีค่าหรือเปล่า

 

วันเสาร์ที่ 8 มีนาคม พ.ศ. 2568

PHP8 Constructor Property Promotion: ลดโค้ดซ้ำซ้อนในคลาส

ถ้าคุณเป็นสาย PHP และเคยเขียนคลาสในเวอร์ชันก่อน PHP 8 ก็คงเคยเจอปัญหานี้แน่ ๆ … "ทำไมต้องเขียน constructor เยอะขนาดนี้?"
จะประกาศ property ก็ต้องเขียน ต้องกำหนดค่าให้มันอีก แถมยังต้องรับค่ามาใน constructor และกำหนดค่าอีกที มันดูซ้ำซ้อนและวุ่นวายไปหมด

แต่พอมา PHP 8 ปุ๊บ! ก็เหมือนมีพระมาโปรด เพราะฟีเจอร์ Constructor Property Promotion ช่วยให้เราลดโค้ดที่ไม่จำเป็นไปได้เยอะมาก! วันนี้เราจะมาดูกันว่า มันคืออะไร ใช้งานยังไง และช่วยให้ชีวิตง่ายขึ้นได้แค่ไหน

1. ปัญหาของ Constructor ใน PHP เวอร์ชันก่อน

ก่อนจะเข้าเรื่องใหม่ ลองมาดูโค้ดของ PHP 7 กันก่อน สมมติว่าเราจะสร้างคลาส User ที่มี name และ email

class User {
    private string $name;
    private string $email;

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

เห็นอะไรมั้ย?

  • ต้องประกาศตัวแปร สองรอบ (ข้างบนและใน constructor)
  • ต้องกำหนดค่าให้ property ใน constructor ซึ่งดูเหมือนเป็นงานซ้ำซ้อน

ยิ่งถ้าคลาสมี property เยอะ เช่น age, address, phoneNumber ก็ต้องเพิ่ม constructor ยาวเหยียด ซึ่งไม่สนุกเลย!

2. Constructor Property Promotion ใน PHP 8

PHP 8 มาพร้อมกับฟีเจอร์ Constructor Property Promotion ที่ช่วยให้โค้ดสะอาดขึ้น ลดความซ้ำซ้อน โดยให้เราสามารถประกาศและกำหนดค่า property ได้ ใน constructor parameter list เลย

มาดูเวอร์ชันใหม่กัน

class User {
    public function __construct(
        private string $name,
        private string $email
    ) {}
}

จบ! แค่ 5 บรรทัด 🚀

อธิบายโค้ด

  • private string $name, private string $email อยู่ใน parameter list เลย
  • ไม่ต้องประกาศ property แยก
  • ไม่ต้องใช้ $this->name = $name; อีกต่อไป

ผลลัพธ์เหมือนเดิม แต่ โค้ดสั้นลง สะอาดขึ้น และอ่านง่ายขึ้น!

3. ตัวอย่างการใช้งานจริง

ใช้งานคลาสที่เขียนด้วย Constructor Property Promotion

$user = new User("John Doe", "john@example.com");

var_dump($user);

ง่ายเหมือนเดิม แต่โค้ดในคลาสสะอาดขึ้น
ใช้ร่วมกับ Visibility Modifier (public, protected, private)

คุณสามารถใช้ public, protected, หรือ private ได้ใน property ที่ประกาศไว้ใน constructor เช่น

class Product {
    public function __construct(
        public string $name,
        private float $price,
        protected int $stock
    ) {}
}

  • name เป็น public → สามารถเข้าถึงได้จากภายนอก
  • price เป็น private → ใช้ได้เฉพาะในคลาส
  • stock เป็น protected → ใช้ได้เฉพาะในคลาสและ subclass

เรียบง่ายกว่า PHP 7 เยอะ!

4. ใช้กับ readonly เพื่อให้ property ไม่เปลี่ยนค่า

PHP 8.1 เพิ่ม readonly ซึ่งใช้กับ Property Promotion ได้ด้วย เช่น

class Config {
    public function __construct(
        public readonly string $appName,
        public readonly string $version
    ) {}
}

ค่าของ appName และ version จะเปลี่ยนไม่ได้หลังจากกำหนดใน constructor

5. ใช้กับ default value ได้

Constructor Property Promotion ยังรองรับค่าเริ่มต้น เช่น

class Order {
    public function __construct(
        private int $quantity = 1,
        private string $status = "pending"
    ) {}
}

ถ้าสร้างออเดอร์ใหม่โดยไม่ส่งค่าเข้ามา ค่าเริ่มต้นก็จะถูกใช้

$order = new Order(); // quantity = 1, status = "pending"

6. ข้อจำกัดของ Constructor Property Promotion

ถึงแม้จะสะดวก แต่มันก็มีข้อจำกัด เช่น

❌ ไม่รองรับการกำหนดค่าแบบซับซ้อน
เช่น ถ้าต้องมีการคำนวณหรือดึงค่าจากฟังก์ชันอื่น จะใช้ Property Promotion ไม่ได้

class Invoice {
    public function __construct(
        private float $total = calculateTotal() // ❌ ใช้ไม่ได้
    ) {}
}

❌ ใช้กับ property ที่ต้องกำหนดค่าเริ่มต้นจากเงื่อนไขไม่ได้
ถ้าต้องมี logic ซับซ้อนใน constructor ต้องกลับไปใช้แบบเดิม เช่น

class Subscription {
    private string $status;

    public function __construct($isActive) {
        $this->status = $isActive ? "active" : "inactive"; // ✅ ใช้แบบนี้แทน
    }
}

7. ควรใช้ Constructor Property Promotion ตอนไหน?

✅ เมื่อ property เป็นค่าธรรมดา เช่น string, int, float, bool
✅ เมื่อ property ไม่ต้องการ logic เพิ่มเติมใน constructor
✅ เมื่ออยากให้โค้ดสะอาดขึ้นและอ่านง่ายขึ้น

แต่ถ้ามี logic ซับซ้อน ควรใช้แบบเดิม

สรุป

ฟีเจอร์ Constructor Property Promotion ใน PHP 8 ทำให้เราสามารถประกาศ property และกำหนดค่าได้ใน constructor parameter list เลย ไม่ต้องเขียนโค้ดซ้ำซ้อนแบบเดิมอีกต่อไป
🔥 จุดเด่น

✔ ลดโค้ดซ้ำซ้อน
✔ อ่านง่ายขึ้น
✔ ใช้ public, private, protected ได้เลย
✔ ใช้ readonly ได้
⚠ ข้อควรระวัง

❌ ใช้ไม่ได้กับ logic ซับซ้อน
❌ ใช้ไม่ได้กับ property ที่ต้องการค่าเริ่มต้นจากฟังก์ชัน

แต่โดยรวมแล้ว ช่วยให้การเขียน PHP สนุกขึ้นเยอะ! ใครยังไม่ได้ลอง ใช้เลย 🚀

วันเสาร์ที่ 22 กุมภาพันธ์ พ.ศ. 2568

[PHP] SOLID Principles เขียนโค้ดยังไงให้ Maintain ง่าย

SOLID Principles คืออะไร?

ถ้าคุณเป็น PHP Developer ที่ต้องการเขียนโค้ดให้ดีขึ้น มีโครงสร้างที่ชัดเจน และง่ายต่อการดูแล (maintain) SOLID Principles คือแนวคิดที่คุณต้องรู้!

SOLID เป็นชุดของ 5 หลักการออกแบบเชิงวัตถุ (OOP) ที่ช่วยให้โค้ดของคุณ อ่านง่าย, ขยายได้, และลดบั๊กในอนาคต โดยคำว่า SOLID มาจากอักษรตัวแรกของ 5 หลักการดังนี้:

  • S – Single Responsibility Principle (SRP)
  • O – Open/Closed Principle (OCP)
  • L – Liskov Substitution Principle (LSP)
  • I – Interface Segregation Principle (ISP)
  • D – Dependency Inversion Principle (DIP)


ในบทความนี้ เราจะมาดูกันว่าแต่ละหลักการทำงานยังไง และเราจะใช้มันกับ PHP ได้ยังไง


1. Single Responsibility Principle (SRP) - หนึ่งคลาส หนึ่งหน้าที่

หลักการ:

    "A class should have only one reason to change."
    (คลาสควรมีเหตุผลเดียวที่ต้องเปลี่ยนแปลง)

แปลว่า: คลาสหนึ่งควรมีหน้าที่เดียว ไม่ควรทำหลายอย่างในที่เดียวกัน

❌ ตัวอย่างที่ไม่ดี:

class User {
    public function saveToDatabase() {
        // บันทึกข้อมูลลง Database
    }

    public function sendEmail() {
        // ส่งอีเมลแจ้งเตือน
    }
}

User ควรรับผิดชอบเฉพาะข้อมูลของผู้ใช้ ไม่ควรจัดการเรื่อง Database หรือ Email

✅ วิธีแก้:

class UserRepository {
    public function save(User $user) {
        // บันทึกข้อมูลลง Database
    }
}

class EmailService {
    public function send(User $user) {
        // ส่งอีเมลแจ้งเตือน
    }
}

ข้อดีของ SRP:

  • แยกโค้ดตามหน้าที่ ทำให้แก้ไขง่าย
  • ลดผลกระทบเมื่อเปลี่ยนแปลงโค้ด



2. Open/Closed Principle (OCP) - เปิดสำหรับขยาย ปิดสำหรับแก้ไข

หลักการ:

    "Software entities should be open for extension but closed for modification."
    (โค้ดควรเปิดให้ขยาย แต่ปิดสำหรับการแก้ไข)

แปลว่า: เราควรออกแบบโค้ดให้เพิ่มฟีเจอร์ใหม่ได้โดยไม่ต้องแก้ไขโค้ดเก่า

❌ ตัวอย่างที่ไม่ดี:

class Payment {
    public function pay($method) {
        if ($method == "credit_card") {
            // จ่ายผ่านบัตรเครดิต
        } elseif ($method == "paypal") {
            // จ่ายผ่าน PayPal
        }
    }
}

ถ้าอยากเพิ่มวิธีชำระเงินใหม่ เราต้องไปแก้ไขโค้ดเดิม ซึ่งไม่ดีเลย

✅ วิธีแก้: ใช้ Polymorphism

interface PaymentMethod {
    public function pay();
}

class CreditCardPayment implements PaymentMethod {
    public function pay() {
        // จ่ายผ่านบัตรเครดิต
    }
}

class PayPalPayment implements PaymentMethod {
    public function pay() {
        // จ่ายผ่าน PayPal
    }
}

class PaymentProcessor {
    public function process(PaymentMethod $paymentMethod) {
        $paymentMethod->pay();
    }
}


ข้อดีของ OCP:

  • เพิ่มวิธีชำระเงินใหม่ได้ง่าย โดยไม่ต้องแก้โค้ดเดิม
  • ลดโอกาสเกิดบั๊กจากการแก้ไขโค้ด


3. Liskov Substitution Principle (LSP) - ใช้แทนกันได้โดยไม่พัง

หลักการ:

    "Objects of a derived class must be substitutable for objects of the base class."
    (คลาสลูกควรใช้แทนคลาสแม่ได้โดยไม่ทำให้โปรแกรมพัง)

❌ ตัวอย่างที่ไม่ดี:

class Bird {
    public function fly() {
        return "บินไปเลย!";
    }
}

class Penguin extends Bird {
    public function fly() {
        throw new Exception("เพนกวินบินไม่ได้!");
    }
}

Penguin เป็น Bird แต่ดันบินไม่ได้ ทำให้โปรแกรมอาจพังเมื่อเรียก fly()

✅ วิธีแก้: แยก Bird ออกเป็น FlyingBird และ NonFlyingBird

abstract class Bird {}

class FlyingBird extends Bird {
    public function fly() {
        return "บินไปเลย!";
    }
}

class Penguin extends Bird {
    public function swim() {
        return "ว่ายน้ำ!";
    }
}


ข้อดีของ LSP:

  • ใช้คลาสลูกแทนคลาสแม่ได้อย่างปลอดภัย
  • ลดปัญหาบั๊กที่เกิดจากพฤติกรรมที่ไม่คาดคิด


4. Interface Segregation Principle (ISP) - ใช้ Interface ให้เหมาะสม

หลักการ:

    "Clients should not be forced to depend on methods they do not use."
    (อย่าทำให้คลาสต้องพึ่งพาเมธอดที่มันไม่ได้ใช้)

❌ ตัวอย่างที่ไม่ดี:

interface Worker {
    public function work();
    public function eat();
}

class Robot implements Worker {
    public function work() {
        return "ทำงาน!";
    }

    public function eat() {
        throw new Exception("หุ่นยนต์ไม่กินข้าว!");
    }
}

✅ วิธีแก้: แยก Interface

interface Workable {
    public function work();
}

interface Eatable {
    public function eat();
}

class Robot implements Workable {
    public function work() {
        return "ทำงาน!";
    }
}

ข้อดีของ ISP:

  • ลดการใช้ method ที่ไม่จำเป็น
  • คลาสลูก implement interface เท่าที่จำเป็น


5. Dependency Inversion Principle (DIP) - แยกส่วนการพึ่งพาออกจากกัน

หลักการ:

    "Depend on abstractions, not concretions."
    (ควรพึ่งพา abstraction มากกว่าการอ้างอิงถึง class โดยตรง)

❌ ตัวอย่างที่ไม่ดี:

class MySQLDatabase {
    public function connect() {
        return "เชื่อมต่อ MySQL!";
    }
}

class UserRepository {
    private $db;

    public function __construct() {
        $this->db = new MySQLDatabase(); // ❌ ผูกกับ MySQL โดยตรง
    }
}

✅ วิธีแก้: ใช้ Dependency Injection

interface Database {
    public function connect();
}

class MySQLDatabase implements Database {
    public function connect() {
        return "เชื่อมต่อ MySQL!";
    }
}

class UserRepository {
    private $db;

    public function __construct(Database $db) {
        $this->db = $db;
    }
}

ข้อดีของ DIP:

  • เปลี่ยน Database ได้ง่าย
  • ลดการพึ่งพาของ class




SOLID Principles ช่วยให้โค้ด PHP ของเรามีโครงสร้างที่ดีขึ้น แก้ไขและขยายโค้ดได้ง่าย ลดบั๊กที่อาจเกิดขึ้น ถ้าคุณต้องการเป็น PHP Developer ที่เก่งขึ้น SOLID เป็นแนวคิดที่คุณควรรู้!

วันพฤหัสบดีที่ 20 กุมภาพันธ์ พ.ศ. 2568

[PHP] Dependency Injection หลักการออกแบบที่ช่วยให้โค้ดสะอาดขึ้น

Dependency Injection คืออะไร?

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

ลองจินตนาการว่าเรามีร้านกาแฟ และเครื่องบดกาแฟคือสิ่งที่เราต้องใช้ทุกวัน สมมติว่าเจ้าของร้านต้องไปซื้อเครื่องบดใหม่ทุกครั้งที่ต้องชงกาแฟ แบบนี้มันคงไม่สะดวกใช่ไหม? แต่ถ้ามีซัพพลายเออร์คอยส่งเครื่องบดที่พร้อมใช้งานมาให้เราตลอด เจ้าของร้านก็แค่รับมาใช้ นี่แหละคือแนวคิดของ Dependency Injection

พูดง่ายๆ DI คือการ ส่ง dependencies เข้าไปใน object แทนที่ object จะสร้างมันขึ้นมาเอง วิธีนี้ช่วยให้โค้ดของเรายืดหยุ่น และเปลี่ยนแปลงได้ง่ายขึ้น

 

ทำไมต้องใช้ Dependency Injection?

1. โค้ดสะอาดขึ้น (Clean Code)

ถ้า class ต้องสร้าง dependencies เอง มันจะทำให้โค้ดของเราดูรกไปหมด DI ช่วยให้เราสามารถแยกความรับผิดชอบของแต่ละ class ออกจากกันได้อย่างชัดเจน

2. ทดสอบง่ายขึ้น (Easier Testing)

ถ้าเราใช้ DI เราสามารถ Inject mock dependencies เข้าไปตอนเขียน unit test ได้เลย ไม่ต้องไปยุ่งกับ database หรือ external services ทำให้การทดสอบรวดเร็วและแม่นยำขึ้น

3. ลดการผูกมัดระหว่าง class (Loose Coupling)

สมมติว่าเรามี class ที่ต้องใช้ Logger ถ้า Logger ถูกสร้างขึ้นภายใน class นั้นๆ เวลาเราอยากเปลี่ยน Logger ใหม่ เราต้องไปแก้โค้ดใน class นั้นด้วย ซึ่งไม่ดีเลย แต่ถ้าใช้ DI เราสามารถเปลี่ยน Logger ใหม่ได้โดยไม่ต้องแตะโค้ดเดิม

4. รองรับการเปลี่ยนแปลงและขยายโค้ดได้ง่าย (More Flexibility)

เมื่อโค้ดของเราไม่ต้องพึ่ง dependencies แบบตายตัว การเปลี่ยนแปลงก็จะง่ายขึ้น เช่น ถ้าเราต้องการเปลี่ยนจาก FileLogger เป็น DatabaseLogger เราสามารถทำได้โดยไม่ต้องไปไล่แก้โค้ดทุกที่
ตัวอย่างการใช้ Dependency Injection ใน PHP

ลองดูตัวอย่างง่ายๆ

 

โค้ดแบบไม่มี DI (Tightly Coupled Code)

class Logger {
    public function log($message) {
        echo "Log: " . $message;
    }
}

class UserService {
    private $logger;

    public function __construct() {
        $this->logger = new Logger(); // สร้าง instance เอง = tightly coupled
    }

    public function registerUser($username) {
        $this->logger->log("User $username registered.");
    }
}

ปัญหา:

  • UserService สร้าง Logger เอง ทำให้เราต้องแก้โค้ดทุกครั้งถ้าอยากเปลี่ยนไปใช้ Logger ตัวอื่น
  • ทำให้เขียน unit test ยาก เพราะ Logger ถูกสร้างขึ้นภายใน class

 

โค้ดที่ใช้ Dependency Injection (Loosely Coupled Code)


class Logger {
    public function log($message) {
        echo "Log: " . $message;
    }
}

class UserService {
    private $logger;

    public function __construct(Logger $logger) { // Inject ผ่าน constructor
        $this->logger = $logger;
    }

    public function registerUser($username) {
        $this->logger->log("User $username registered.");
    }
}

// ใช้งาน
$logger = new Logger();
$userService = new UserService($logger);
$userService->registerUser("JohnDoe");

ข้อดีของวิธีนี้คือ

  • UserService ไม่ต้องสร้าง Logger เอง แค่รับมันเข้ามาใช้
  • ถ้าเราอยากเปลี่ยนไปใช้ Logger ตัวใหม่ ก็แค่ Inject Logger ตัวใหม่เข้าไป
  • ง่ายต่อการเขียน unit test


รูปแบบของ Dependency Injection ใน PHP

1. Constructor Injection (นิยมใช้มากที่สุด)

แบบที่เราเห็นในตัวอย่างด้านบน นี่เป็นวิธีที่ดีที่สุด เพราะทำให้ object ถูกสร้างขึ้นมาพร้อม dependencies ที่มันต้องการ

class UserService {
    private $logger;

    public function __construct(Logger $logger) {
        $this->logger = $logger;
    }
}
 

 

2. Setter Injection (กำหนด dependencies ผ่าน method)

class UserService {
    private $logger;

    public function setLogger(Logger $logger) {
        $this->logger = $logger;
    }
}

ข้อเสียคือเราอาจลืมเรียก setLogger() ทำให้ object อยู่ในสถานะที่ไม่สมบูรณ์

 

3. Interface Injection (ใช้ Interface กำหนด dependencies)

interface LoggerInterface {
    public function log($message);
}

class FileLogger implements LoggerInterface {
    public function log($message) {
        file_put_contents('log.txt', $message, FILE_APPEND);
    }
}

class UserService {
    private $logger;

    public function __construct(LoggerInterface $logger) {
        $this->logger = $logger;
    }
}

ข้อดีของวิธีนี้คือเราสามารถเปลี่ยน Logger ได้ง่ายมาก


ใช้ Dependency Injection Container (DIC) เพื่อจัดการ dependencies

ถ้าโปรเจกต์ของเรามีหลาย dependencies การ Inject dependencies ด้วยตัวเองอาจจะวุ่นวาย เราสามารถใช้ Dependency Injection Container (DIC) มาช่วยได้ เช่น

  • Laravel → มี Service Container ในตัว
  • Symfony → ใช้ Symfony Dependency Injection Component
  • PHP-DI → Lightweight DI Container


ตัวอย่างการใช้ PHP-DI

use DI\Container;

$container = new Container();
$container->set(Logger::class, new Logger());

$userService = new UserService($container->get(Logger::class));


สรุป

Dependency Injection เป็นเทคนิคที่ช่วยให้โค้ดของเราสะอาดขึ้น ยืดหยุ่นขึ้น และง่ายต่อการทดสอบ การใช้ DI สามารถช่วยให้โค้ดของเราเป็นแบบ loosely coupled ทำให้สามารถเปลี่ยนแปลง dependencies ได้โดยไม่ต้องแก้โค้ดเดิมมากมาย

ถ้าคุณยังไม่ได้ใช้ DI ใน PHP แนะนำให้เริ่มต้นวันนี้ แล้วคุณจะเห็นความแตกต่าง!