অবজেক্ট ওরিয়েন্টেড পিএইচপি (The Senior Architecture)

অনেকে মনে করেন class আর object তৈরি করতে পারলেই OOP শেখা শেষ। কিন্তু একজন Senior Developer এর কাছে OOP মানে সিনট্যাক্স নয়, OOP মানে হলো System Design এবং Memory Management

এই অধ্যায়ে আমরা পিএইচপির অবজেক্ট মডেলের একদম গভীরে (Zend Engine Level) প্রবেশ করব।

৬.১ ক্লাস এবং অবজেক্ট ইন্টারনালস (Zend Engine Deep Dive)

পিএইচপিতে অবজেক্ট আসলে কী? সি বা জাভার মতো পিএইচপি অবজেক্ট মেমোরিতে সরাসরি স্ট্রাকচার নয়। পিএইচপি অবজেক্ট হলো মূলত একটি HashTable-based Structure

📦 অবজেক্টের পেটের ভেতর যা থাকে:

একটি অবজেক্ট তৈরি হলে জেন্ড ইঞ্জিন (Zend Engine) ৩টি জিনিস তৈরি করে:

  • Class Entry (zend_class_entry): ক্লাসের মেথড, কনস্ট্যান্ট এবং ব্লু-প্রিন্ট এখানে থাকে
  • Object Handler: অবজেক্টের সাথে কীভাবে ব্যবহার হবে (Read/Write access rules) তা ঠিক করে
  • Property Table: অবজেক্টের ভ্যালুগুলো (Properties) এখানে জমা থাকে

🗂️ প্রপার্টি স্টোরেজ (The "Array" Secret)

বিশ্বাস করুন আর নাই করুন, অবজেক্টের প্রপার্টিগুলো ইন্টারনালি একটি Associative Array (HashTable) হিসেবেই থাকে।

// Internal C representation concept
object->properties = [
    "name"  => zval("Shagor"),
    "email" => zval("test@example.com")
];

তাই পিএইচপিতে অবজেক্ট এবং অ্যারের মেমোরি বিহেভিয়ার অনেকটা কাছাকাছি।

৬.২ কনস্ট্রাক্টর চেইনিং (Parent-Child Initialization)

পিএইচপি অটোমেটিক্যালি প্যারেন্ট ক্লাসের কনস্ট্রাক্টর কল করে না। এটি জুনিয়রদের একটি কমন ভুল।

❌ ভুল পদ্ধতি:

class A {
    public function __construct() { echo "Parent Init"; }
}

class B extends A {
    public function __construct() {
        // ❌ প্যারেন্ট কল করা হয়নি, তাই Parent Init প্রিন্ট হবে না
        // এবং প্যারেন্টের কোনো প্রপার্টি সেট হবে না।
        echo "Child Init";
    }
}

✅ Senior Fix:

সবসময় চাইল্ড কনস্ট্রাক্টরের শুরুতে parent::__construct() কল করুন।

class B extends A {
    public function __construct() {
        parent::__construct(); // Parent Init প্রিন্ট হবে
        echo "Child Init";
    }
}

৬.৩ ভিজিবিলিটি এবং মেমোরি হ্যাক (The Name Mangling)

public, private, protected—এগুলো কেবল এক্সেস কন্ট্রোল নয়, এগুলো মেমোরিতে কীভাবে ডাটা সেভ হবে তাও নিয়ন্ত্রণ করে।

🔧 ইন্টারনাল নেমিং (Name Mangling):

পিএইচপি প্রপার্টিগুলোর নাম গোপনে পরিবর্তন করে হ্যাশটেবিলে সেভ করে।

  • Public: নাম যা আছে তাই থাকে ("property")
  • Protected: নামের আগে * যুক্ত হয় ("*property")
  • Private: নামের আগে ক্লাসের নাম যুক্ত হয় ("ClassNameproperty")

💡 Senior Insight: private প্রপার্টি এক্সেস করা স্লাইটলি ফাস্টেস্ট কারণ ইঞ্জিনকে স্কোপ চেক করতে কম সময় ব্যয় করতে হয় (ক্লাসের নাম ফিক্সড থাকে)।

৬.৪ ইনহেরিটেন্স এবং LSP (Liskov Substitution Principle)

LSP রুল: "প্যারেন্ট ক্লাস যেখানে ব্যবহার করা যায়, চাইল্ড ক্লাসকেও সেখানে ব্যবহার করা যেতে হবে—কোনো এরর ছাড়া।"

❌ LSP ভায়োলেশন (ভুল ডিজাইন):

class Bird {
    public function fly() { return "Flying"; }
}

class Penguin extends Bird {
    public function fly() {
        throw new Exception("I can't fly!"); // 💥 কোড ব্রেক করবে
    }
}

এখানে পেঙ্গুইন Bird কে রিপ্লেস করতে পারছে না কারণ সে উড়তে পারে না।

✅ সঠিক ডিজাইন (Interface Segregation):

interface Bird {}
interface FlyingBird { public function fly(); }

class Eagle implements Bird, FlyingBird { ... }
class Penguin implements Bird { ... } // ফ্লাই মেথড নেই, তাই নিরাপদ

৬.৫ অ্যাবস্ট্রাক্ট ক্লাস বনাম ইন্টারফেস (The Contract)

অনেক সিনিয়রের কনফিউশন থাকে—কখন কোনটা ব্যবহার করব?

FeatureAbstract ClassInterface
Concept"Is-a" relationship (Common Base)"Can-do" relationship (Contract)
Methodsঅ্যাবস্ট্রাক্ট + নরমাল মেথড থাকতে পারেশুধু মেথড সিগনেচার (No body)
Propertiesপ্রপার্টি থাকতে পারেপ্রপার্টি থাকতে পারে না (PHP 8.1+ ছাড়া)
Extendsমাত্র ১টি ক্লাস এক্সটেন্ড করা যায়একাধিক ইন্টারফেস ইমপ্লিমেন্ট করা যায়

৬.৬ ট্রেইটস (Traits) এবং ডায়মন্ড প্রবলেম

পিএইচপি Multiple Inheritance সাপোর্ট করে না। কিন্তু Traits দিয়ে আমরা কোড কপি-পেস্ট স্টাইলে রিইউজ করতে পারি।

⚡ মেথড প্রায়োরিটি (খুব গুরুত্বপূর্ণ):

যদি প্যারেন্ট ক্লাস, ট্রেইট এবং চাইল্ড ক্লাসে একই নামের মেথড থাকে, তবে কার জোর বেশি?

ক্রম: Current Class > Trait > Parent Class

class ParentClass { function say() { echo "Parent"; } }
trait MyTrait { function say() { echo "Trait"; } }

class Child extends ParentClass {
    use MyTrait;
}

(new Child)->say(); // Output: "Trait" (প্যারেন্টকে ওভাররাইড করেছে)

৬.৭ স্ট্যাটিক মেথড এবং এর বিপদ (The Global Trap)

জুনিয়ররা static মেথড পছন্দ করে কারণ new Class() লিখতে হয় না। কিন্তু সিনিয়রা একে এড়িয়ে চলে।

❌ কেন স্ট্যাটিক খারাপ?

  • Global State: এটি গ্লোবাল ফাংশনের মতো আচরণ করে
  • Testing Nightmare: স্ট্যাটিক মেথড Mock বা ফেক করা খুব কঠিন
  • Tight Coupling: ক্লাস ডিপেন্ডেন্সি হার্ড-কোডেড হয়ে যায়

✅ Senior Advice:

ইউটিলিটি ফাংশন (যেমন Math::add()) ছাড়া অন্য কোথাও স্ট্যাটিক ব্যবহার করবেন না। Dependency Injection ব্যবহার করুন।

৬.৮ লেইট স্ট্যাটিক বাইন্ডিং (LSB) - self vs static

ইনহেরিটেন্সে self এবং static এর আচরণ আলাদা।

class A {
    public static function who() {
        echo __CLASS__;
    }
    public static function test() {
        static::who(); // LSB: রানটাইমে যে কল করেছে তাকে দেখবে
    }
}

class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}

B::test(); // Output: B (কারণ কল করেছে B)

যদি self::who() থাকত → আউটপুট হতো A (কারণ test মেথড A তে লেখা)।
যেহেতু static::who() আছে → আউটপুট B (কারণ কল করেছে B)।

৬.৯ নেমস্পেস এবং অটোলোডিং (PSR-4)

বড় প্রজেক্টে হাজার হাজার ক্লাস থাকে। require 'file.php' করা অসম্ভব।

📦 PSR-4 স্ট্যান্ডার্ড:

ফাইল পাথ এবং নেমস্পেসের ম্যাপিং।

Composer এর জাদুকরী:
composer.json ফাইলে আমরা বলে দিই: "App\\": "src/"
তখন new App\Controllers\User() কল করলে কম্পোজার অটোমেটিক src/Controllers/User.php ফাইলটি ইনকুড করে নেয়।

৬.১০ ডিপেন্ডেন্সি ইনজেকশন (DI) - The Game Changer

কোডের ভেতর new কিওয়ার্ড ব্যবহার করা মানে আপনি ক্লাসটিকে মেরে ফেললেন (Testability নষ্ট করলেন)।

❌ Hard Dependency (Bad):

class User {
    public function sendEmail() {
        $mailer = new Mailer(); // User ক্লাসটি Mailer এর ওপর নির্ভরশীল
        $mailer->send();
    }
}

✅ Dependency Injection (Senior):

class User {
    private $mailer;
    
    // মেইলার বাইরে থেকে দেওয়া হচ্ছে (Constructor Injection)
    public function __construct(MailerInterface $mailer) {
        $this->mailer = $mailer;
    }
}

এখন আপনি চাইলে Mailer এর বদলে FakeMailer দিয়ে টেস্ট করতে পারবেন।

৬.১১ SOLID প্রিন্সিপাল (এক নজরে)

সফটওয়্যার আর্কিটেকচারের ৫টি স্তম্ভ:

S - Single Responsibility: এক ক্লাসের এক কাজ। (User ক্লাস শুধু ইউজার ডাটা রাখবে, ইমেইল পাঠাবে না)
O - Open/Closed: এক্সটেনশনের জন্য খোলা, মডিফিকেশনের জন্য বন্ধ
L - Liskov Substitution: চাইল্ড ক্লাস প্যারেন্টের জায়গা নিতে পারবে
I - Interface Segregation: ফ্যাট ইন্টারফেস না বানিয়ে ছোট ছোট ইন্টারফেস বানাও
D - Dependency Inversion: কনক্রিট ক্লাসের ওপর নির্ভর না করে ইন্টারফেসের ওপর নির্ভর করো

৬.১২ ডিজাইন প্যাটার্নস (Senior Developer's Toolkit)

ডিজাইন প্যাটার্ন হলো কমন সমস্যার স্ট্যান্ডার্ড সমাধান।

🏗️ ক্রিয়েশনাল (Creational)

  • Singleton: পুরো অ্যাপে ক্লাসের মাত্র একটিই ইনস্ট্যান্স থাকবে (যেমন: Database Connection)
  • Factory: অবজেক্ট তৈরির লজিক লুকিয়ে রাখা
  • Builder: জটিল অবজেক্ট স্টেপ-বাই-স্টেপ তৈরি করা

🏛️ স্ট্রাকচারাল (Structural)

  • Adapter: দুই বা ততোধিক ইনকম্প্যাটিবল ইন্টারফেসকে একসাথে কাজ করানো
  • Facade: জটিল সিস্টেমের সামনে একটি সহজ মাস্ক বা ইন্টারফেস দেওয়া
  • Decorator: ক্লাসের কোড পরিবর্তন না করে নতুন ফিচার যোগ করা

🔄 বিহেভিয়ারাল (Behavioral)

  • Observer: এক অবজেক্টের পরিবর্তনে অন্যদের নোটিফাই করা (ইভেন্ট লিসেনার)
  • Strategy: রানটাইমে অ্যালগরিদম পরিবর্তন করা (যেমন: পেমেন্ট মেথড সিলেক্ট করা)

🎯 Senior Developer Interview Questions (Chapter 6)

Q: self এবং static এর মধ্যে মূল পার্থক্য কী? LSB কেন দরকার?

self কম্পাইল টাইমে ক্লাস নির্ধারণ করে (যেখানে কোড লেখা হয়েছে)। static রানটাইমে নির্ধারণ করে (যে ক্লাস কল করেছে)। ইনহেরিটেন্সে চাইল্ড ক্লাসের কনটেক্সট পেতে LSB (static) লাগে।

Q: ট্রেইট (Trait) ব্যবহারে মেথড ওভাররাইডিংয়ের প্রায়োরিটি ক্রমটি বলুন।

১. বর্তমান ক্লাস (Current Class) > ২. ট্রেইট (Trait) > ৩. প্যারেন্ট ক্লাস (Parent Class)

Q: private মেথড কি ইনহেরিট হয়? মেমোরিতে এটি কীভাবে থাকে?

না, private মেথড বা প্রপার্টি চাইল্ড ক্লাসে ইনহেরিট হয় না। মেমোরিতে এর নামের সাথে ক্লাসের নাম যুক্ত থাকে (ClassNameprop), তাই এটি শুধু ওই নির্দিষ্ট ক্লাসেই দৃশ্যমান।

Q: Dependency Injection কেন ব্যবহার করব?

কোডকে লুজলি কাপলড (Loosely Coupled) করার জন্য, যাতে সহজেই টেস্টিং (Unit Testing) এবং মেইনটেইন করা যায়।

🎯 অধ্যায় ৬ এর সারাংশ (Summary)

এই অধ্যায়ে আমরা শিখলাম:

  • Object Internals: HashTable-based structure, Property Table, zend_class_entry
  • Constructor Chaining: parent::__construct() কল করা বাধ্যতামূলক
  • Name Mangling: Public→plain, Protected→*, Private→Class
  • LSP: চাইল্ড ক্লাস প্যারেন্টের জায়গা নিতে পারবে
  • Trait Priority: Current Class > Trait > Parent
  • LSB: self vs static - static runtime binding করে
  • Dependency Injection: new এড়িয়ে চলুন, constructor injection ব্যবহার করুন
  • SOLID: SRP, OCP, LSP, ISP, DIP - আর্কিটেকচারের ৫ স্তম্ভ

✨ এটি ছিল একটি বিশাল এবং গভীর অধ্যায়। আপনার OOP নলেজ এখন মেমোরি লেভেল থেকে আর্কিটেকচার লেভেল পর্যন্ত বিস্তৃত।

পরবর্তী অধ্যায়: Part 7 — Advanced PHP Internals — যেখানে OPcache, Garbage Collection এবং Memory Leaks নিয়ে আলোচনা হবে।

প্রস্তুত তো? 🚀

🔒 কপিরাইট সুরক্ষিত কন্টেন্ট 🔒

কপি, স্ক্রিনশট, প্রিন্ট করা সম্পূর্ণ নিষিদ্ধ।