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

วันอาทิตย์ที่ 9 มีนาคม พ.ศ. 2568

Attributes (Annotations) ใน PHP 8: ปฏิวัติเมตาดาต้าแบบใหม่

PHP 8 ไม่ได้มาแค่ปรับปรุงประสิทธิภาพ แต่ยังนำเสนอฟีเจอร์ใหม่ ๆ ที่ช่วยให้โค้ดสะอาดขึ้น หนึ่งในนั้นก็คือ Attributes หรือที่บางคนอาจเรียกว่า Annotations (คล้าย ๆ กับใน Java หรือ C#) ซึ่งช่วยให้เราสามารถกำหนด เมตาดาต้า ให้กับคลาส, เมธอด, พร็อพเพอร์ตี้ และพารามิเตอร์ได้แบบสวยงาม ไม่ต้องใช้ DocBlock แบบเดิมให้รกตา

วันนี้เราจะมาเจาะลึกว่า PHP 8 Attributes คืออะไร ใช้งานยังไง และมีประโยชน์ยังไงบ้าง พร้อมตัวอย่างโค้ดที่เข้าใจง่าย!

1. ปัญหาของ DocBlock Annotation แบบเก่า

ก่อน PHP 8 เวลาต้องการแนบข้อมูลเพิ่มเติม (เมตาดาต้า) ให้กับโค้ด เรามักใช้ DocBlock Annotation ซึ่งเป็นแค่คอมเมนต์ธรรมดา แล้วค่อยใช้ Reflection API อ่านค่าออกมา

ตัวอย่างเช่น

class User {
    /**
     * @ORM\Column(type="string", length=255)
     */
    private string $name;
}

ในโค้ดนี้ @ORM\Column(...) เป็นเพียงคอมเมนต์เฉย ๆ ไม่มีผลกับ PHP โดยตรง ต้องใช้ไลบรารีอย่าง Doctrine ORM เพื่ออ่านค่ามันออกมา ซึ่งปัญหาคือ...

  • มันเป็น คอมเมนต์ → ไม่มีการตรวจสอบไวยากรณ์ (Syntax) จาก PHP
  • ถ้าพิมพ์ผิดหรือใช้พารามิเตอร์ผิด → ไม่มี Error หรือ Warning แจ้งเตือน
  • ต้องใช้ไลบรารีภายนอกเพื่ออ่านค่า

แล้ว PHP 8 ก็เข้ามา ปฏิวัติวงการ ด้วย Attributes!

2. PHP 8 Attributes คืออะไร?

Attributes คือการกำหนดเมตาดาต้าให้กับโค้ด โดยใช้ไวยากรณ์ใหม่ที่ PHP เข้าใจได้โดยตรง ไม่ต้องใช้คอมเมนต์อีกต่อไป

ตัวอย่างโค้ดเดียวกันในแบบใหม่

use App\Attributes\Column;

class User {
    #[Column(type: "string", length: 255)]
    private string $name;
}

แตกต่างจาก DocBlock ยังไง?

✅ PHP อ่านและเข้าใจ Attributes ได้โดยตรง
✅ Syntax ถูกตรวจสอบโดย PHP → ถ้าผิด จะมี Error แจ้งเตือน
✅ ใช้ร่วมกับ Reflection API ได้ง่ายขึ้น
✅ ไม่ต้องพึ่งไลบรารีภายนอก (แต่สามารถใช้ร่วมกันได้)

3. ไวยากรณ์พื้นฐานของ Attributes

การกำหนด Attributes

#[Attribute]
class ExampleAttribute {
    public function __construct(public string $value) {}
}

#[ExampleAttribute("Hello, Attributes!")]
class Demo {}

ตัวอย่างนี้กำหนด Attribute ที่ชื่อ ExampleAttribute และใช้กับคลาส Demo
ใช้ Attributes กับ เมธอด, พร็อพเพอร์ตี้ และพารามิเตอร์

#[Attribute]
class Route {
    public function __construct(public string $path) {}
}

class Controller {
    #[Route("/home")]
    public function home() {
        return "Welcome Home!";
    }
}

กำหนดได้หลาย Attributes

#[Attribute]
class Role {
    public function __construct(public string $name) {}
}

class AdminController {
    #[Route("/dashboard")]
    #[Role("admin")]
    public function dashboard() {
        return "Admin Dashboard";
    }
}

สามารถใส่ได้หลาย Attribute ซ้อนกัน

4. วิธีอ่านค่า Attributes ด้วย Reflection API

เพื่ออ่านค่าจาก Attributes เราจะใช้ Reflection API ซึ่งใน PHP 8 ได้เพิ่ม getAttributes() มาให้โดยเฉพาะ

$reflection = new ReflectionClass(AdminController::class);
$method = $reflection->getMethod('dashboard');
$attributes = $method->getAttributes();

foreach ($attributes as $attribute) {
    $instance = $attribute->newInstance();
    var_dump($instance);
}

โค้ดนี้จะดึงข้อมูลของ Attribute Route และ Role ออกมา

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

✅ ใช้กับ Routing System

เหมาะกับ Frameworks หรือ Custom Routing เช่น

#[Attribute]
class Route {
    public function __construct(public string $path) {}
}

class MyRouter {
    public static function getRoutes(object $controller): array {
        $routes = [];
        $reflection = new ReflectionClass($controller);

        foreach ($reflection->getMethods() as $method) {
            foreach ($method->getAttributes(Route::class) as $attribute) {
                $route = $attribute->newInstance();
                $routes[$route->path] = [$controller, $method->getName()];
            }
        }
        return $routes;
    }
}

class BlogController {
    #[Route("/blog")]
    public function index() {
        return "Blog Home";
    }
}

$routes = MyRouter::getRoutes(new BlogController());
print_r($routes);

✅ ใช้กับ Validation

สามารถใช้ Attributes เพื่อกำหนด กฎการตรวจสอบค่าในฟอร์ม ได้ เช่น

#[Attribute]
class Required {}

class UserForm {
    #[Required]
    public string $name;
}

function validate(object $object) {
    $reflection = new ReflectionClass($object);

    foreach ($reflection->getProperties() as $property) {
        if ($property->getAttributes(Required::class)) {
            if (empty($property->getValue($object))) {
                echo "{$property->getName()} is required!";
            }
        }
    }
}

$user = new UserForm();
validate($user); // "name is required!"

✅ ใช้กับ Database ORM

คล้ายกับ Doctrine ORM แต่ใช้ Attributes แทน DocBlock

#[Attribute]
class Column {
    public function __construct(public string $type, public int $length) {}
}

class User {
    #[Column(type: "string", length: 255)]
    private string $name;
}

จากนั้นใช้ Reflection API เพื่ออ่านค่าและสร้างตารางอัตโนมัติ

6. ข้อจำกัดของ Attributes

❌ PHP 7 ใช้ไม่ได้ → ต้องใช้ PHP 8 ขึ้นไป
❌ ไม่รองรับตัวแปรหลายตัวในบรรทัดเดียวกัน

#[Column(type: "string", length: 255)]
private string $name, $email; // ❌ ใช้แบบนี้ไม่ได้ ต้องแยกบรรทัด

7. ควรใช้ Attributes ตอนไหน?

✅ เมื่อใช้ข้อมูลเมตาดาต้าเป็นส่วนหนึ่งของตรรกะโปรแกรม
✅ เมื่อต้องการให้ PHP ตรวจสอบไวยากรณ์
✅ เมื่อต้องการลดการใช้คอมเมนต์ให้น้อยลง


สรุป

PHP 8 Attributes คือการกำหนดเมตาดาต้าให้โค้ดในแบบที่ PHP เข้าใจได้โดยตรง ไม่ต้องใช้ DocBlock Annotations แบบเก่าอีกต่อไป

🔹 อ่านง่ายขึ้น
🔹 PHP ตรวจสอบ Syntax ให้
🔹 ใช้กับ Reflection API ได้สะดวก
🔹 ลดโค้ดซ้ำซ้อนและดูโปรมากขึ้น

ถ้าคุณใช้ PHP 8 อยู่แล้ว ลองใช้ Attributes ดู แล้วจะรักมันแน่นอน! 🚀

วันเสาร์ที่ 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 เป็นแนวคิดที่คุณควรรู้!

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

[PHP] Magic Methods พลังวิเศษที่คุณอาจไม่รู้

Magic Methods คืออะไร?

ถ้าคุณเขียน PHP มาสักพัก คุณอาจเคยเห็นเมธอดที่มีเครื่องหมายขีดล่างสองตัวนำหน้า (__) เช่น __construct(), __destruct(), __toString(), หรือ __get() แล้วสงสัยว่ามันคืออะไร

เจ้าเมธอดเหล่านี้เรียกว่า Magic Methods หรือ "เมธอดวิเศษ" ของ PHP ซึ่งเป็นฟังก์ชันพิเศษที่ PHP มีไว้ให้ใช้เพื่อจัดการพฤติกรรมของอ็อบเจ็กต์ในบางสถานการณ์โดยอัตโนมัติ

Magic Methods ช่วยให้โค้ดของเรามีความยืดหยุ่นและใช้งานง่ายขึ้น แต่ในขณะเดียวกันก็อาจทำให้เกิดพฤติกรรมที่คาดไม่ถึงถ้าใช้อย่างไม่ระมัดระวัง

ในบทความนี้ เราจะมาเจาะลึก Magic Methods ที่สำคัญ พร้อมตัวอย่างการใช้งานจริงที่เข้าใจง่าย


1. __construct() และ __destruct() - ตัวสร้างและตัวทำลาย

  • __construct(): ทำงานเมื่อมีการสร้างอ็อบเจ็กต์ใหม่
  • __destruct(): ทำงานเมื่ออ็อบเจ็กต์ถูกทำลายหรือจบการทำงาน


class User {
    public function __construct($name) {
        echo "Hello, $name!<br>";
    }

    public function __destruct() {
        echo "Object is being destroyed.<br>";
    }
}

$user = new User("John");
// Output: Hello, John!

unset($user);
// Output: Object is being destroyed.

  • __construct() มักใช้ในการกำหนดค่าเริ่มต้นให้กับอ็อบเจ็กต์
  • __destruct() ใช้ในการเคลียร์ทรัพยากร เช่น ปิด database connection



2. __get() และ __set() - จัดการ property ที่ไม่มีอยู่จริง


บางครั้งเราต้องการให้ class มี property ที่ไม่ได้กำหนดไว้โดยตรง แต่สามารถเรียกใช้งานได้ นี่คือจุดที่ __get() และ __set() เข้ามาช่วย

  • __get($name): เรียกใช้ property ที่ไม่มีอยู่
  • __set($name, $value): กำหนดค่าให้ property ที่ไม่มีอยู่


class MagicUser {
    private $data = [];

    public function __set($name, $value) {
        $this->data[$name] = $value;
    }

    public function __get($name) {
        return isset($this->data[$name]) ? $this->data[$name] : "Property '$name' not found.";
    }
}

$user = new MagicUser();
$user->age = 25;  // ใช้ __set()
echo $user->age;  // ใช้ __get()
// Output: 25

echo $user->name;  
// Output: Property 'name' not found.
 

__get() และ __set() มีประโยชน์มากในกรณีที่เราต้องการให้ class รองรับ property แบบไดนามิก


3. __call() และ __callStatic() - จัดการเมธอดที่ไม่มีอยู่จริง

  • __call($name, $arguments): ใช้เมื่อเรียกเมธอดที่ไม่มีอยู่ในอ็อบเจ็กต์
  • __callStatic($name, $arguments): ใช้เมื่อเรียกเมธอดที่ไม่มีอยู่แบบ static


class MagicMethods {
    public function __call($name, $arguments) {
        return "Method '$name' not found. Arguments: " . implode(", ", $arguments);
    }

    public static function __callStatic($name, $arguments) {
        return "Static method '$name' not found. Arguments: " . implode(", ", $arguments);
    }
}

$obj = new MagicMethods();
echo $obj->sayHello("John", "Doe");  
// Output: Method 'sayHello' not found. Arguments: John, Doe

echo MagicMethods::sayBye("Alice");  
// Output: Static method 'sayBye' not found. Arguments: Alice

__call() และ __callStatic() มีประโยชน์ใน dynamic API หรือ proxy class ที่ต้องการรองรับเมธอดแบบไดนามิก


4. __toString() - แปลงอ็อบเจ็กต์เป็น string

ใช้เมื่อมีการพยายาม echo หรือ print อ็อบเจ็กต์

class MagicUser {
    private $name;

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

    public function __toString() {
        return "User: " . $this->name;
    }
}

$user = new MagicUser("John");
echo $user;  
// Output: User: John

__toString() ช่วยให้เราสามารถแปลงอ็อบเจ็กต์เป็นข้อความได้ง่ายขึ้น


5. __invoke() - ทำให้อ็อบเจ็กต์ถูกเรียกเหมือนฟังก์ชัน

 ใช้เมื่อมีการเรียกอ็อบเจ็กต์เหมือนเป็นฟังก์ชัน

class CallableClass {
    public function __invoke($message) {
        return "Invoked with message: " . $message;
    }
}

$obj = new CallableClass();
echo $obj("Hello!");  
// Output: Invoked with message: Hello!
 

__invoke() ใช้บ่อยในกรณีที่เราต้องการให้ class ทำงานเหมือน callback function


6. __isset() และ __unset() - จัดการ isset() และ unset()


class MagicUser {
    private $data = [];

    public function __set($name, $value) {
        $this->data[$name] = $value;
    }

    public function __isset($name) {
        return isset($this->data[$name]);
    }

    public function __unset($name) {
        unset($this->data[$name]);
    }
}

$user = new MagicUser();
$user->age = 30;

echo isset($user->age) ? "Exists" : "Not Exists";  
// Output: Exists

unset($user->age);
echo isset($user->age) ? "Exists" : "Not Exists";  
// Output: Not Exists
 

ช่วยให้เราควบคุม isset() และ unset() บน property ที่ไม่มีอยู่จริง


สรุป: ใช้ Magic Methods อย่างไรให้มีประโยชน์?


Magic Methods เป็นคุณสมบัติที่ทรงพลังของ PHP ที่ช่วยให้เราสามารถควบคุมพฤติกรรมของอ็อบเจ็กต์ได้อย่างยืดหยุ่น แต่ก็ควรใช้ด้วยความระมัดระวัง เพราะถ้าใช้มากเกินไปอาจทำให้โค้ดอ่านยากและ debug ยากขึ้น


กรณีที่ควรใช้

  • สร้าง dynamic properties (__get(), __set())
  • รองรับ dynamic methods (__call(), __callStatic())
  • แปลง object เป็น string (__toString())
  • ใช้ object เหมือนฟังก์ชัน (__invoke())


กรณีที่ควรเลี่ยง

  • ใช้มากเกินไปจนทำให้โค้ดอ่านยาก
  • ใช้กับ property ที่ควรกำหนดไว้ชัดเจน

วันพฤหัสบดีที่ 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 แนะนำให้เริ่มต้นวันนี้ แล้วคุณจะเห็นความแตกต่าง!

วันพุธที่ 19 กุมภาพันธ์ พ.ศ. 2568

[PHP] การสืบทอดคลาส (Class Inheritance) : เข้าใจง่าย ใช้งานได้จริง!

การสืบทอดคลาส (Inheritance) คืออะไร?

ถ้าพูดถึงเรื่อง "มรดกตกทอด" ทุกคนคงเข้าใจกันดี เช่น พ่อแม่อาจจะส่งต่อบ้าน รถ หรือธุรกิจให้ลูก ๆ ได้ใช้งานต่อ ในโลกของการเขียนโปรแกรมก็มีแนวคิดแบบนี้เหมือนกัน ซึ่งเรียกว่า การสืบทอดคลาส (Class Inheritance)

ใน PHP (และภาษา OOP อื่น ๆ) การสืบทอดคลาสช่วยให้เราสร้างคลาสใหม่จากคลาสที่มีอยู่แล้ว โดยไม่ต้องเขียนโค้ดใหม่ทั้งหมด คลาสใหม่ (เรียกว่า คลาสลูก หรือ Subclass) จะได้รับคุณสมบัติ (Properties) และพฤติกรรม (Methods) จากคลาสเดิม (เรียกว่า คลาสแม่ หรือ Superclass) แบบอัตโนมัติ

พูดง่าย ๆ คือ เราไม่ต้องเริ่มจากศูนย์ แต่สามารถใช้ของที่มีอยู่แล้วมาต่อยอดได้เลย!

 

ทำไมต้องใช้การสืบทอดคลาส?

1. ลดโค้ดซ้ำซ้อน (Code Reusability) ถ้าเรามีโค้ดที่ใช้บ่อย ๆ ในหลายคลาส แทนที่จะก็อปปี้โค้ดไปวางซ้ำ ๆ เราสามารถให้คลาสลูกสืบทอดจากคลาสแม่เพื่อใช้โค้ดเดิมได้เลย

2. แก้ไขง่ายขึ้น (Easier Maintenance) ถ้าต้องการแก้ไขพฤติกรรมของคลาสหลาย ๆ ตัวที่คล้ายกัน เราแค่แก้ที่คลาสแม่ คลาสลูกทั้งหมดก็จะได้รับการเปลี่ยนแปลงโดยอัตโนมัติ

3. โครงสร้างโปรแกรมชัดเจน (Better Code Structure) ทำให้โปรแกรมมีโครงสร้างที่เป็นลำดับชั้น เข้าใจง่าย เช่น มีคลาส Animal เป็นคลาสแม่ และมี Dog, Cat, Bird เป็นคลาสลูก

4. รองรับการเขียนโปรแกรมเชิงวัตถุ (OOP Principles) ช่วยให้เราใช้หลักการ OOP อย่าง Encapsulation, Inheritance, และ Polymorphism ได้เต็มที่ 

 

ตัวอย่างโค้ด PHP: การสืบทอดคลาสแบบง่าย ๆ

มาดูกันว่าการสืบทอดคลาสใน PHP ทำงานอย่างไร

<?php
// คลาสแม่ (Superclass)
class Animal {
    public $name;

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

    public function makeSound() {
        return "เสียงสัตว์";
    }
}

// คลาสลูก (Subclass) สืบทอดจาก Animal
class Dog extends Animal {
    public function makeSound() {
        return "โฮ่ง โฮ่ง!";
    }
}

// ใช้งานจริง
$dog = new Dog("บ๊อบบี้");
echo $dog->name; // บ๊อบบี้
echo "<br>";
echo $dog->makeSound(); // โฮ่ง โฮ่ง!
?>


อธิบายโค้ด:

  • คลาส Animal เป็นคลาสแม่ มีคุณสมบัติ $name และเมธอด makeSound()
  • คลาส Dog เป็นคลาสลูก ใช้ extends เพื่อสืบทอดจาก Animal และเขียน makeSound() ใหม่ (Override)
  • ตอนสร้างอ็อบเจ็กต์ Dog เราสามารถใช้ $dog->name และ $dog->makeSound() ได้เลย!

 

parent:: เรียกใช้เมธอดจากคลาสแม่

บางครั้งเราต้องการใช้เมธอดของคลาสแม่ในคลาสลูก เช่น เราอยากให้ makeSound() ของ Dog ใช้เสียงของ Animal แล้วเติมคำเพิ่ม 

<?php
class Animal {
    public function makeSound() {
        return "เสียงสัตว์ทั่วไป";
    }
}

class Dog extends Animal {
    public function makeSound() {
        return parent::makeSound() . " และเสียงของหมาคือ โฮ่ง โฮ่ง!";
    }
}

$dog = new Dog();
echo $dog->makeSound(); // เสียงสัตว์ทั่วไป และเสียงของหมาคือ โฮ่ง โฮ่ง!
?>


อธิบายโค้ด:

  • parent::makeSound() คือการเรียกใช้เมธอด makeSound() จากคลาสแม่ แล้วเติมคำเพิ่ม

 

protected และ private มีผลกับการสืบทอดยังไง?

  • public = ใช้ได้ทุกที่ (ทั้งในคลาสแม่ คลาสลูก และจากภายนอก)
  • protected = ใช้ได้ในคลาสแม่และคลาสลูกเท่านั้น
  • private = ใช้ได้เฉพาะในคลาสที่ประกาศเท่านั้น (คลาสลูกมองไม่เห็น!)

มาดูตัวอย่างกัน 

<?php
class Animal {
    public $name = "สัตว์"; // ใช้ได้ทุกที่
    protected $color = "น้ำตาล"; // ใช้ได้ในคลาสแม่และลูก
    private $age = 5; // ใช้ได้แค่ในคลาสนี้เท่านั้น

    public function getAge() {
        return $this->age;
    }
}

class Dog extends Animal {
    public function getColor() {
        return $this->color; // ใช้ได้ เพราะเป็น protected
    }

    public function getAgeFromParent() {
        // return $this->age; //  Error! เพราะ age เป็น private
        return $this->getAge(); // ใช้ผ่านเมธอด public ของคลาสแม่ได้
    }
}

$dog = new Dog();
echo $dog->name; // ใช้ได้
// echo $dog->color; // Error! เพราะเป็น protected
// echo $dog->age; // Error! เพราะเป็น private
echo $dog->getColor(); // ใช้ได้
echo $dog->getAgeFromParent(); // ใช้ได้
?>


final ป้องกันการสืบทอดและ Override

ถ้าต้องการห้ามไม่ให้มีคลาสลูกสืบทอดจากคลาสแม่ ให้ใช้ final หน้า class

final class Animal {
    // โค้ดในคลาส
}

// class Dog extends Animal {} //  Error! เพราะ Animal เป็น final

หรือหากต้องการห้าม Override เมธอด ให้ใช้ final หน้า function

class Animal {
    final public function makeSound() {
        return "เสียงสัตว์ทั่วไป";
    }
}

class Dog extends Animal {
    // public function makeSound() {} //  Error! เพราะเมธอด makeSound() เป็น final
}
 

สรุปแนวคิดสำคัญเกี่ยวกับการสืบทอดคลาสใน PHP

  • ใช้ extends เพื่อให้คลาสลูกสืบทอดจากคลาสแม่
  • คลาสลูกได้รับทุกอย่างจากคลาสแม่ (ยกเว้น private)
  • ใช้ parent::method() เรียกเมธอดจากคลาสแม่
  • ใช้ protected เพื่อให้ใช้ได้ในคลาสแม่และลูก แต่ไม่ให้ใช้จากภายนอก
  • ใช้ final ถ้าต้องการห้ามสืบทอดหรือห้าม Override

การสืบทอดคลาสทำให้โค้ดสะอาดขึ้น ง่ายต่อการบำรุงรักษา และรองรับหลักการ OOP อย่างแท้จริง! ถ้าใครยังไม่ได้ลองใช้ในโปรเจกต์ของตัวเอง ลองเอาไปปรับใช้ดูนะ รับรองว่าชีวิตนักพัฒนาจะง่ายขึ้นแน่นอน!

 

 

 

 

วันอังคารที่ 18 กุมภาพันธ์ พ.ศ. 2568

[PHP] การใช้ Interfaces และ Traits ใน PHP

    เมื่อพูดถึง Object-Oriented Programming (OOP) ในภาษา PHP หลายๆ คนอาจคุ้นเคยกับการใช้ คลาส (Classes) และ ออบเจ็กต์ (Objects) กันเป็นอย่างดีอยู่แล้ว แต่การใช้ Interfaces และ Traits อาจจะยังคงเป็นสิ่งที่หลายคนยังไม่เข้าใจหรือยังไม่ได้นำมาใช้กันอย่างเต็มที่ บทความนี้จะอธิบายให้ฟังถึงการใช้ Interfaces และ Traits ใน PHP โดยใช้ภาษาที่เข้าใจง่ายและมีตัวอย่างโค้ดประกอบ เพื่อให้มือใหม่สามารถเข้าใจแนวคิดและการใช้งานได้อย่างเต็มที่

วันจันทร์ที่ 17 กุมภาพันธ์ พ.ศ. 2568

[PHP] การใช้ Constructor และ Destructor ในการเขียนโปรแกรม OOP (Object-Oriented Programming)

    การใช้ Constructor และ Destructor ในการเขียนโปรแกรม OOP (Object-Oriented Programming) ใน PHP เป็นหัวข้อที่สำคัญอย่างยิ่งในการสร้างและจัดการออบเจ็กต์ภายในคลาส ในบทความนี้ เราจะพูดถึงทั้ง Constructor และ Destructor ในการเขียน PHP โดยใช้ภาษาที่เข้าใจง่าย และค่อยๆ เจาะลึกไปถึงรายละเอียดเพื่อให้มือใหม่ที่กำลังศึกษาการเขียนโปรแกรม OOP ใน PHP สามารถเข้าใจการทำงานของทั้งสองฟังก์ชันนี้ได้ดีขึ้น

1. รู้จักกับ Constructor และ Destructor

ก่อนที่เราจะไปลึกในเรื่องของ Constructor และ Destructor มาทำความเข้าใจพื้นฐานของคำสองคำนี้กันก่อน

  • Constructor (คอนสตรัคเตอร์): ใน OOP, Constructor คือฟังก์ชันพิเศษที่ถูกเรียกใช้โดยอัตโนมัติเมื่อมีการสร้างออบเจ็กต์จากคลาสนั้นๆ หรือกล่าวง่ายๆ ว่ามันคือฟังก์ชันที่ใช้สำหรับการตั้งค่าหรือการกำหนดค่าตั้งต้นให้กับออบเจ็กต์ที่ถูกสร้างขึ้น
  • Destructor (ดีสทรัคเตอร์): ในทางตรงกันข้าม, Destructor คือฟังก์ชันที่ถูกเรียกใช้เมื่อออบเจ็กต์จะถูกทำลายหรือไม่สามารถใช้งานได้อีกต่อไป โดยมันจะทำหน้าที่จัดการทรัพยากรที่อาจยังคงอยู่ เช่น การปิดการเชื่อมต่อฐานข้อมูล หรือการทำความสะอาดหน่วยความจำ

2. การสร้าง Constructor ใน PHP

    การสร้าง Constructor ใน PHP จะใช้ชื่อฟังก์ชันเป็น __construct โดยการใช้ double underscore (__) นำหน้าชื่อฟังก์ชันเป็นสิ่งที่ทำให้ Constructor เป็นฟังก์ชันพิเศษที่มีคุณสมบัติการทำงานที่ไม่เหมือนกับฟังก์ชันทั่วไป

ตัวอย่างโค้ด PHP สำหรับการสร้าง Constructor:

<?php
class Car {
    // Properties
    public $brand;
    public $model;
    public $year;

    // Constructor
    public function __construct($brand, $model, $year) {
        $this->brand = $brand;
        $this->model = $model;
        $this->year = $year;
    }

    // Method to display car information
    public function displayInfo() {
        echo "Car: $this->brand $this->model, Year: $this->year\n";
    }
}

// Creating an object of the Car class
$car1 = new Car("Toyota", "Corolla", 2020);
$car1->displayInfo();  // Output: Car: Toyota Corolla, Year: 2020
?>

ในตัวอย่างนี้, __construct จะรับค่าพารามิเตอร์ 3 ตัว คือ $brand, $model, และ $year เพื่อกำหนดค่าต่างๆ ให้กับออบเจ็กต์ที่ถูกสร้างขึ้นจากคลาส Car เมื่อสร้างออบเจ็กต์ $car1 ขึ้นมา จะมีการส่งค่าเหล่านี้ไปยัง Constructor เพื่อกำหนดค่าให้กับคุณสมบัติของออบเจ็กต์นั้นๆ

3. การใช้ Constructor เพื่อกำหนดค่าเริ่มต้น

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

ตัวอย่าง:

<?php
class Person {
    public $name;
    public $age;

    // Constructor to initialize properties
    public function __construct($name, $age) {
        $this->name = $name;
        $this->age = $age;
    }

    // Method to display person info
    public function displayPerson() {
        echo "Name: $this->name, Age: $this->age\n";
    }
}

// Creating a new object
$person1 = new Person("John", 25);
$person1->displayPerson(); // Output: Name: John, Age: 25
?>

ในตัวอย่างนี้, เมื่อเราสร้างออบเจ็กต์ $person1 ของคลาส Person, เราต้องส่งค่าพารามิเตอร์ไปที่ Constructor เพื่อกำหนดชื่อและอายุของบุคคลนั้นให้กับออบเจ็กต์

4. การใช้ Default Values ใน Constructor

บางครั้งเราอาจไม่ต้องการให้พารามิเตอร์ที่ส่งเข้า Constructor เป็นค่าบังคับทุกครั้งที่สร้างออบเจ็กต์ เราสามารถกำหนดค่าเริ่มต้น (default value) ให้กับพารามิเตอร์ใน Constructor ได้

ตัวอย่าง:

<?php
class Book {
    public $title;
    public $author;

    // Constructor with default values
    public function __construct($title = "Unknown Title", $author = "Unknown Author") {
        $this->title = $title;
        $this->author = $author;
    }

    // Method to display book info
    public function displayBook() {
        echo "Title: $this->title, Author: $this->author\n";
    }
}

// Creating objects with and without parameters
$book1 = new Book("1984", "George Orwell");
$book1->displayBook();  // Output: Title: 1984, Author: George Orwell

$book2 = new Book();
$book2->displayBook();  // Output: Title: Unknown Title, Author: Unknown Author
?>

ในตัวอย่างนี้, เราได้กำหนดค่าพื้นฐานให้กับพารามิเตอร์ $title และ $author ซึ่งหมายความว่า ถ้าเราไม่ส่งค่าพารามิเตอร์เข้าไปในตอนที่สร้างออบเจ็กต์ ค่าพื้นฐานเหล่านี้จะถูกใช้

5. Destructor ใน PHP

เมื่อพูดถึง Destructor, มันจะทำงานตรงข้ามกับ Constructor โดยที่ Destructor จะถูกเรียกเมื่อออบเจ็กต์ถูกทำลายหรือไม่สามารถใช้งานได้อีกต่อไป เช่น เมื่อออบเจ็กต์ไม่ได้ถูกใช้งานหรือเมื่อหมดอายุการใช้งาน

ใน PHP, Destructor จะถูกกำหนดด้วยฟังก์ชันชื่อว่า __destruct โดยที่ไม่ต้องมีการรับพารามิเตอร์ใดๆ

ตัวอย่างการใช้ Destructor:

<?php
class DatabaseConnection {
    public $connection;

    // Constructor to initialize connection
    public function __construct($host, $username, $password) {
        $this->connection = new mysqli($host, $username, $password);
        if ($this->connection->connect_error) {
            die("Connection failed: " . $this->connection->connect_error);
        }
        echo "Connected successfully\n";
    }

    // Destructor to close connection
    public function __destruct() {
        $this->connection->close();
        echo "Connection closed\n";
    }
}

// Creating an object
$db = new DatabaseConnection("localhost", "root", "password");

// When the script ends, the destructor is automatically called to close the connection
?>

ในตัวอย่างนี้, __destruct จะทำหน้าที่ปิดการเชื่อมต่อฐานข้อมูลเมื่อออบเจ็กต์ DatabaseConnection ไม่ได้ถูกใช้งานอีกต่อไป ตัวอย่างนี้แสดงให้เห็นถึงการจัดการทรัพยากรอย่างเหมาะสมด้วย Destructor

6. ความสำคัญของ Constructor และ Destructor

การใช้ Constructor และ Destructor ช่วยให้การจัดการออบเจ็กต์เป็นไปได้อย่างมีประสิทธิภาพ ซึ่งช่วยให้โค้ดของเราสะอาดและดูแลรักษาง่าย นอกจากนี้ยังช่วยให้เราสามารถตั้งค่าหรือทำความสะอาดทรัพยากรต่างๆ ที่เกี่ยวข้องกับออบเจ็กต์ได้โดยอัตโนมัติ


    ในบทความนี้, เราได้เรียนรู้เกี่ยวกับ Constructor และ Destructor ใน PHP ซึ่งเป็นฟังก์ชันที่ช่วยในการสร้างและทำลายออบเจ็กต์ พร้อมทั้งการกำหนดค่าเริ่มต้นและการจัดการทรัพยากรของออบเจ็กต์ โดยใช้ฟังก์ชันเหล่านี้ในการสร้างโค้ดที่สามารถจัดการได้ง่ายและมีประสิทธิภาพมากขึ้น

ไม่ว่าจะเป็นการสร้างออบเจ็กต์หรือการทำความสะอาดหลังจากใช้งานเสร็จ, Constructor และ Destructor เป็นเครื่องมือสำคัญที่ทุกคนควรเข้าใจและนำไปใช้ในการพัฒนาโปรแกรม PHP ตามแนวทาง OOP

วันอาทิตย์ที่ 16 กุมภาพันธ์ พ.ศ. 2568

[PHP] การใช้ Static ใน PHP แบบเข้าใจง่าย

ถ้าเคยเขียนโปรแกรม OOP ใน PHP มาก่อน คงเคยเห็นหรือได้ยินคำว่า static ใช่ไหม? แต่ static มันคืออะไร และใช้ยังไง? วันนี้เรามาดูกันแบบเข้าใจง่าย ๆ กันเลย!

Static คืออะไร?

static เป็นคีย์เวิร์ดที่ใช้กำหนดว่า ตัวแปรหรือเมทอดในคลาสสามารถเรียกใช้ได้โดยไม่ต้องสร้างอ็อบเจกต์ (Object) ซึ่งต่างจากปกติที่ต้อง new ก่อน

ง่าย ๆ ก็คือ มันเป็นของ คลาส ไม่ใช่ของ อ็อบเจกต์ นั่นเอง

ตัวอย่างการใช้ Static

<?php
class MathHelper {
    public static $pi = 3.14159;

    public static function square($number) {
        return $number * $number;
    }
}

// เรียกใช้ static property
echo MathHelper::$pi; // แสดงค่า 3.14159

// เรียกใช้ static method
echo MathHelper::square(4); // แสดงค่า 16
?>

อธิบายโค้ดง่าย ๆ

  • public static $pi เป็นตัวแปรที่สามารถเรียกใช้ได้โดยตรงจากชื่อคลาส MathHelper::$pi
  • public static function square($number) เป็นฟังก์ชันที่สามารถเรียกใช้ได้โดยตรงจากคลาส MathHelper::square(4)

เปรียบเทียบ Static กับ Non-Static

ปกติแล้ว ถ้าเราไม่ใช้ static เราต้องสร้างอ็อบเจกต์ก่อนถึงจะใช้งานได้

<?php
class MathHelper {
    public $pi = 3.14159;
    public function square($number) {
        return $number * $number;
    }
}

$math = new MathHelper();
echo $math->pi; // ต้องเรียกใช้ผ่านอ็อบเจกต์
?>

แต่ถ้าใช้ static เราก็เรียกได้เลยโดยไม่ต้อง new

Static กับ self:: และ parent::

เมื่ออยู่ในคลาสเดียวกัน ถ้าจะอ้างถึงตัวแปรหรือเมทอดที่เป็น static เราจะใช้ self::

<?php
class Test {
    public static function hello() {
        return "Hello, Static!";
    }
    public static function callHello() {
        return self::hello(); // ใช้ self:: เรียกเมทอด static
    }
}

echo Test::callHello(); // แสดงผล "Hello, Static!"
?>

ถ้ามีการสืบทอด (extends) และต้องการอ้างถึงของคลาสแม่ ให้ใช้ parent::

<?php
class ParentClass {
    public static function sayHi() {
        return "Hi from Parent!";
    }
}

class ChildClass extends ParentClass {
    public static function greet() {
        return parent::sayHi(); // ใช้ parent:: อ้างถึงของคลาสแม่
    }
}

echo ChildClass::greet(); // แสดงผล "Hi from Parent!"
?>

เมื่อไหร่ควรใช้ Static?

  • เมื่อไม่ต้องการสร้างอ็อบเจกต์ แต่อยากเรียกใช้ข้อมูลหรือฟังก์ชันจากคลาส
  • เมื่อต้องการสร้าง Helper Class ที่มีแต่ฟังก์ชันช่วยเหลือต่าง ๆ
  • เมื่อต้องการเก็บค่าคงที่หรือค่าที่ใช้ร่วมกันทั่วโปรแกรม


สรุป

  • static ใช้กับตัวแปรและเมทอดในคลาสที่ต้องการเรียกใช้โดยไม่ต้องสร้างอ็อบเจกต์
  • ใช้ self:: ในคลาสเดียวกัน และ parent:: ถ้าเรียกจากคลาสลูก
  • มีประโยชน์มากกับ Helper Functions หรือค่าคงที่ที่ใช้ร่วมกันทั่วโปรแกรม


ทีนี้ก็รู้แล้วว่า static มีไว้ทำอะไร! ลองนำไปใช้ดูนะ

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

[PHP] การสร้าง Class อย่างง่ายใน PHP

การเขียนโปรแกรมเชิงวัตถุ (OOP) ใน PHP ทำให้การจัดการโค้ดสะดวกขึ้นมาก วันนี้เราจะมาดูกันว่าเราสามารถสร้างคลาส (Class) ใน PHP ได้อย่างไรแบบง่าย ๆ และเข้าใจได้เร็ว

Class คืออะไร?

คิดซะว่า Class เป็นพิมพ์เขียว หรือแม่แบบ สำหรับสร้างวัตถุ (Object) ในโปรแกรมของเรา ซึ่งภายใน Class จะมีทั้ง คุณสมบัติ (Properties) และ พฤติกรรม (Methods)

เริ่มต้นสร้าง Class แบบง่าย

มาลองสร้าง Class กันเลย สมมติว่าเราจะสร้าง Class ที่ชื่อว่า Car เพื่อใช้จำลองรถยนต์คันหนึ่ง

<?php
class Car {
    public $brand; // คุณสมบัติของรถ (ยี่ห้อ)
    public $color; // สีของรถ

    public function __construct($brand, $color) {
        $this->brand = $brand;
        $this->color = $color;
    }

    public function showInfo() {
        echo "รถยี่ห้อ $this->brand สี $this->color";
    }
}

// ใช้งาน Class
$myCar = new Car("Toyota", "แดง");
$myCar->showInfo(); // แสดงผล: รถยี่ห้อ Toyota สี แดง
?>

อธิบายโค้ดแบบง่าย ๆ

  • class Car - เราสร้างคลาสชื่อ Car
  • public $brand; และ public $color; - เป็นตัวแปรที่ใช้เก็บคุณสมบัติของรถ
  • __construct($brand, $color) - เป็นฟังก์ชันพิเศษที่ถูกเรียกใช้อัตโนมัติเมื่อสร้างวัตถุ (Object)
  • showInfo() - เป็นเมทอดที่ใช้แสดงรายละเอียดของรถ

สร้างวัตถุ (Object) จาก Class

หลังจากสร้าง Class แล้ว เราสามารถสร้างวัตถุจากคลาสนั้นได้ด้วย new เช่น:

$car1 = new Car("Honda", "น้ำเงิน");
$car1->showInfo(); // รถยี่ห้อ Honda สี น้ำเงิน

สรุป

การสร้าง Class ใน PHP ไม่ได้ยากเลย! แค่กำหนดคุณสมบัติและพฤติกรรมที่เราต้องการ จากนั้นก็สร้างวัตถุขึ้นมาใช้งานได้ทันที หวังว่าบทความนี้จะช่วยให้คุณเข้าใจแนวคิดพื้นฐานของ OOP ใน PHP ได้ง่ายขึ้นนะ!