ما هو حقن التبعية في PHP ولماذا هو مهم؟
\n\nتخيل أنك تبني منزلاً. بدلاً من أن تصنع كل لبنة بنفسك، يمكنك ببساطة طلبها واستخدامها جاهزة. حقن التبعية (DI) في البرمجة يعمل بنفس المبدأ — أنت لا تنشئ الكائنات داخل الفئة، بل تحصل عليها من الخارج. هذا يجعل الكود الخاص بك مرنًا، قابلاً للاختبار، وسهل الفهم.
\n\nفي PHP، كما في أي لغة كائنية التوجه، غالبًا ما تعتمد الفئات على فئات أخرى. تنشأ المشكلة عندما يتم إنشاء التبعيات مباشرة داخل الفئة — وهذا ما يسمى الاقتران المحكم (tight coupling). يحل حقن التبعية هذه المشكلة عن طريق تمرير التبعيات من الخارج.
\n\nالمشكلة بدون حقن التبعية
\nلنأخذ مثالًا نموذجيًا حيث تقوم فئة UserService بإنشاء كائن قاعدة البيانات بنفسها:
<?php\nclass Database {\n public function query($sql) {\n // منطق الاستعلام\n return "النتيجة: " . $sql;\n }\n}\n\nclass UserService {\n private $db;\n \n public function __construct() {\n // اقتران محكم — الفئة تنشئ التبعية بنفسها\n $this->db = new Database();\n }\n \n public function getUser($id) {\n return $this->db->query("SELECT * FROM users WHERE id = $id");\n }\n}\n\n$service = new UserService();\necho $service->getUser(1);\n?>\n\nمشاكل هذا الأسلوب:
\n- \n
- لا يمكن استبدال
Databaseبتنفيذ آخر (مثلًا للاختبارات) \n - صعوبة الاختبار — لا يمكن استخدام كائن وهمي (mock) \n
- يصبح الكود مترابطًا ويصعب توسيعه \n
الحل: حقن التبعية عبر المُنشئ
\nالآن سنعيد كتابة نفس الفئة باستخدام حقن التبعية:
\n\n<?php\nclass UserService {\n private $db;\n \n // يتم تمرير التبعية من الخارج\n public function __construct(Database $db) {\n $this->db = $db;\n }\n \n public function getUser($id) {\n return $this->db->query("SELECT * FROM users WHERE id = $id");\n }\n}\n\n// إنشاء التبعية بشكل منفصل\n$database = new Database();\n$service = new UserService($database);\necho $service->getUser(1);\n?>\n\nالآن UserService لا يعرف كيف يتم إنشاء Database. إنه ببساطة يستخدم ما تم تمريره إليه. وهذا هو حقن التبعية.
أنواع حقن التبعية في PHP
\nهناك ثلاث طرق رئيسية لحقن التبعيات. دعنا نستعرض كل منها مع أمثلة.
\n\n1. الحقن عبر المُنشئ (Constructor Injection)
\nالطريقة الأكثر شيوعًا والموصى بها. يتم تمرير التبعيات عند إنشاء الكائن عبر المُنشئ.
\n\n<?php\nclass Logger {\n public function log($message) {\n echo "[LOG]: " . $message;\n }\n}\n\nclass OrderProcessor {\n private $logger;\n \n // التبعية في المُنشئ\n public function __construct(Logger $logger) {\n $this->logger = $logger;\n }\n \n public function process($order) {\n // معالجة الطلب\n $this->logger->log("تمت معالجة الطلب: " . $order);\n }\n}\n\n$logger = new Logger();\n$processor = new OrderProcessor($logger);\n$processor->process("الطلب #123");\n?>\n\nالمزايا: التبعية متاحة دائمًا، الكائن دائمًا في حالة صالحة. العيوب: إذا كان هناك العديد من التبعيات، قد يصبح المُنشئ ضخمًا.
\n\n2. الحقن عبر المُحدد (Setter Injection)
\nيُستخدم عندما تكون التبعية اختيارية أو تحتاج إلى استبدالها أثناء عمل الكائن.
\n\n<?php\nclass MailService {\n private $transport;\n \n // مُحدد للتبعية\n public function setTransport($transport) {\n $this->transport = $transport;\n }\n \n public function send($to, $mess