إعادة هيكلة كود PHP القديم: من الألف إلى الياء
\n\nالعمل مع كود PHP القديم (legacy) هو واقع لا مفر منه للعديد من المطورين. المشاريع التي كُتبت قبل 5-10 سنوات غالباً ما تمثل "كود سباغيتي" مع متغيرات عامة، وخلط بين منطق الأعمال والعرض، وغياب التصنيف، واستخدام دوال قديمة (mysql_*, ereg، إلخ). في هذه المقالة، سنشرح نهجاً خطوة بخطوة لإعادة هيكلة كود PHP القديم، مما سيسمح بتحسين المشروع دون إيقاف تشغيله.
\n\nلماذا تعتبر إعادة هيكلة الكود القديم صعبة؟
\n\nالمشكلة الرئيسية في الكود القديم هي غياب الاختبارات. لا يمكنك التأكد من أن التغييرات لن تعطل الوظائف الحالية. الصعوبات الإضافية:
\n\n- \n
- غياب التوثيق — منطق العمل غالباً ما يكون غير مفهوم \n
- خلط الطبقات — استعلامات SQL داخل قوالب HTML \n
- الحالة العامة — متغيرات يمكن الوصول إليها من أي نقطة في الكود \n
- الأنماط القديمة — استخدام register_globals، دوال mysql_* \n
- التبعيات الصلبة — إنشاء الكائنات مباشرة عبر
newداخل الفئات \n
التحضير لإعادة الهيكلة: إنشاء "شبكة أمان"
\n\nقبل تغيير الكود، يجب التأكد من أنك ستتمكن من اكتشاف الأخطاء. إليك الخطوات الإلزامية:
\n\n1. إضافة اختبارات التوصيف
\n\nاختبارات التوصيف (characterization tests) تثبت السلوك الحالي للكود. تُكتب بعد كتابة الكود، وتسمح بالتأكد من أن إعادة الهيكلة لم تغير السلوك.
\n\n<?php\n// مثال على اختبار توصيف لدالة قديمة\nclass LegacyCalculatorTest extends PHPUnit\\Framework\\TestCase\n{\n public function testCalculateTotal()\n {\n // تثبيت السلوك الحالي\n $result = legacy_calculate_total(100, 0.2);\n $this->assertEquals(120, $result);\n }\n}\n\n\n2. إنشاء "غطاء" من الاختبارات
\n\nابدأ باختبارات التكامل التي تتحقق من سيناريوهات المستخدم الرئيسية (التسجيل، إتمام الطلب، إلخ). سيعطيك هذا ثقة أساسية أثناء إعادة الهيكلة.
\n\nاستراتيجية خطوة بخطوة لإعادة هيكلة كود PHP القديم
\n\nنستخدم نهج "Strangler Fig Pattern" (التين الخانق) — استبدال المكونات القديمة تدريجياً بمكونات جديدة دون إيقاف تشغيل النظام.
\n\nالخطوة 1: عزل وتغليف الحالة العامة
\n\nالمتغيرات والدوال العامة هي العدو الرئيسي لإعادة الهيكلة. ابدأ بتغليفها:
\n\n<?php\n// قبل: دالة ومتغير عامان\n$db = mysql_connect('localhost', 'user', 'pass');\nfunction get_user($id) {\n global $db;\n $result = mysql_query("SELECT * FROM users WHERE id = $id", $db);\n return mysql_fetch_assoc($result);\n}\n\n// بعد: فئة مع تبعية صريحة\nclass UserRepository {\n private PDO $pdo;\n \n public function __construct(PDO $pdo) {\n $this->pdo = $pdo;\n }\n \n public function findById(int $id): ?array {\n $stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = ?');\n $stmt->execute([$id]);\n $user = $stmt->fetch();\n return $user ?: null;\n }\n}\n\n\nالخطوة 2: فصل المنطق عن العرض
\n\nالكود القديم النموذجي يحتوي على SQL ومنطق PHP مباشرة داخل HTML. استخدم نمط "طريقة القالب" للفصل:
\n\n<?php\n// قبل: كود سباغيتي في القالب\n?>\n<ul>\n<?php\n$result = mysql_query("SELECT * FROM products");\nwhile ($row = mysql_fetch_assoc($result)) {\n echo "<li>" . $row['name'] . " - " . $row['price'] . " روبل</li>";\n}\n?>\n</ul>\n\n// بعد: فصل المنطق إلى خدمة\nclass ProductService {\n public function __construct(private ProductRepository $repo) {}\n \n public function getProductList(): array {\n return $this->repo->findAll();\n }\n}\n\n// في القالب