حقن التبعيات (Dependency Injection) في PHP: دليل شامل

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة إلى Dependency Injection (DI) في PHP



تطوير PHP الحديث لا يمكن أن يتم دون فهم مبادئ الاقتران الضعيف بين المكونات. Dependency Injection (DI)، أو حقن التبعيات، هو أحد أنماط التصميم الرئيسية التي تسمح بإنشاء تطبيقات مرنة وقابلة للاختبار وسهلة الصيانة. بدلاً من أن يقوم الفصل (class) بإنشاء تبعياته بنفسه (مثل كائن قاعدة البيانات أو المسجل logger)، فإنه يتلقاها من الخارج. هذا يغير بنية الكود بشكل جذري.



في هذه المقالة، سنقوم بتحليل مفصل لماهية Dependency Injection في PHP، وما هي طرق الحقن الموجودة، وكيف يعمل حاوية DI (DI-container)، ولماذا أصبح هذا النمط معيارًا للأطر الحديثة مثل Laravel و Symfony و Yii2.



1. المشكلة: الاقتران الصلب (Tight Coupling)



قبل التحدث عن الحل، دعونا نلقي نظرة على المشكلة. مثال كلاسيكي هو الفصل UserController، الذي يقوم بإنشاء كائن Database مباشرة داخل نفسه:



<?phpclass Database {    public function query(string $sql): array {        // الاتصال بقاعدة البيانات وتنفيذ الاستعلام        return [];    }}

class UserController { private Database $db;

public function __construct() { // تبعية صلبة - نقوم بإنشاء الكائن داخل المُنشئ $this->db = new Database(); }

public function getUsers(): array { return $this->db->query('SELECT * FROM users'); }}?>


عيوب هذا الأسلوب:

  • صعوبة الاختبار: لن تتمكن من استبدال Database بكائن وهمي (mock) في اختبارات الوحدة.
  • انتهاك مبدأ المسؤولية الواحدة (SRP): الفصل مسؤول ليس فقط عن منطق الأعمال، ولكن أيضًا عن إنشاء التبعيات.
  • صعوبة التعديل: إذا أردت استبدال MySQL بـ PostgreSQL، فسيتعين عليك تغيير الكود في جميع الفصول التي يتم فيها إنشاء Database.


2. الحل: حقن التبعيات (Dependency Injection)



يقترح Dependency Injection تمرير التبعيات من الخارج. هناك ثلاث طرق رئيسية للحقن: عبر المُنشئ، وعبر الدوال المسماة setters، وعبر الواجهة (الطريقة). دعونا ننظر في كل منها.



2.1. الحقن عبر المُنشئ (Constructor Injection)



الطريقة الأكثر شيوعًا والموصى بها. يتم تمرير التبعيات مرة واحدة عند إنشاء الكائن ولا يمكن تغييرها لاحقًا.



<?phpclass UserController {    private Database $db;

// يتم تمرير التبعية من الخارج public function __construct(Database $db) { $this->db = $db; }

public function getUsers(): array { return $this->db->query('SELECT * FROM users'); }}

// الاستخدام:$db = new Database();$controller = new UserController($db);?>


الإيجابيات: يضمن أن التبعية متاحة دائمًا؛ مثالي للتبعيات الإلزامية. السلبيات: إذا كان للفصل العديد من التبعيات، يصبح المُنشئ ضخمًا.



2.2. الحقن عبر الدوال المسماة (Setter Injection)



مناسب للتبعيات الاختيارية التي يمكن تعيينها بعد إنشاء الكائن.



<?phpclass Logger {    public function log(string $message): void {        echo $message;    }}

class UserController { private ?Logger $logger = null;

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

public function getUsers(): array { if ($this->logger) { $this->logger->log('Fetching users...'); } // منطق الحصول على المستخدمين return []; }}

// الاستخدام:$controller = new UserController();$controller->setLogger(new Logger());?>


الإيجابيات:

المدونات

توصيات الكتب