تقسيم التطبيق إلى ملفات: include و require
اكتب كل جزء مرة واحدة، وابنِ الصفحات من وحدات. ⏱ نحو 30 دقيقة.
- تشرح فائدة تقسيم التطبيق إلى وحدات.
- تميّز
includeوrequireوrequire_onceوتختار المناسب. - تبني الصفحات من ترويسة وتذييل ودوال مشتركة.
- تستخدم
__DIR__للمسارات، وتنظّم المشروع في مجلدات.
١مشكلة النسخ واللصق
موقع فيه عشر صفحات، وكل صفحة تكرر الترويسة والقائمة والتذييل ودوال مثل e() و price(). الآن طُلب منك إضافة رابط جديد للقائمة: ستعدّل عشرة ملفات، وستنسى واحدًا حتمًا.
الحل: ضع كل جزء مشترك في ملف واحد، ثم ضمّنه حيث تحتاجه. هكذا يصير التطبيق وحدات Modules صغيرة، لكل وحدة مهمة واحدة.
٢include و require
كلاهما يضع محتوى ملف آخر في مكانه ويشغّله، كأنك نسخته هناك. والفرق عند غياب الملف:
| الأمر | إن لم يوجد الملف |
|---|---|
require 'x.php'; | خطأ قاتل: يتوقف التطبيق. استخدمه للملفات الضرورية (الدوال، والإعداد، والاتصال بقاعدة البيانات). |
include 'x.php'; | تحذير ويكمل التنفيذ. للأجزاء الاختيارية فقط، مثل شريط إعلانات. |
require_once 'x.php'; | مثل require، لكن لا يضمّن الملف مرتين. مهم لملفات الدوال والأصناف (تعريف دالة مرتين خطأ). |
<?phprequire 'helpers.php';echo "لن تصل إلى هنا";
<?phpinclude 'banner.php';echo "وصلنا رغم غياب الملف";
كلا الملفين غير موجود. require أوقفت التطبيق بخطأ قاتل، و include اكتفت بتحذير وأكملت. وفي الحالتين: الأصل ألا يغيب ملف تحتاجه!
٣موقع من وحدات
هذا موقع صغير بأسلوب احترافي. الصفحات index.php و products.php قصيرة: تضمّن الدوال، وتحدد العنوان، وتضمّن الترويسة، ثم تكتب محتواها الخاص فقط، ثم تضمّن التذييل. تنقّل بين تبويبات الملفات:
<?phprequire __DIR__ . '/inc/functions.php';$title = 'الرئيسية';require __DIR__ . '/inc/header.php';?><p>أهلًا بك في متجر الواحة. لدينا <?= count(products()) ?> منتجات.</p><?php require __DIR__ . '/inc/footer.php'; ?>
<?phprequire __DIR__ . '/inc/functions.php';$title = 'المنتجات';require __DIR__ . '/inc/header.php';?><ul><?php foreach (products() as $p): ?> <li><?= e($p['name']) ?>: <?= price($p['price']) ?></li><?php endforeach; ?></ul><?php require __DIR__ . '/inc/footer.php'; ?>
<?php// دوال مشتركة يستخدمها كل الموقعfunction e(string $s): string{ return htmlspecialchars($s);}function price(float $p): string{ return number_format($p, 2) . ' ريال';}function products(): array{ return [ ['name' => 'قهوة عربية', 'price' => 35], ['name' => 'تمر سكري', 'price' => 45], ['name' => 'شاي أخضر', 'price' => 18], ];}
<!DOCTYPE html><html lang="ar" dir="rtl"><head><meta charset="UTF-8"><title><?= e($title) ?> | متجر الواحة</title></head><body style="font-family:Tahoma;margin:0"><header style="background:#4F5B93;color:#fff;padding:8px 12px"> <b>متجر الواحة</b> · <a style="color:#fff" href="index.php">الرئيسية</a> · <a style="color:#fff" href="products.php">المنتجات</a></header><main style="padding:12px"><h2><?= e($title) ?></h2>
</main><footer style="border-top:1px solid #ccc;padding:8px 12px;color:#777">© متجر الواحة</footer></body></html>
الصفحتان تشتركان في الترويسة والتذييل والدوال. لإضافة رابط إلى القائمة تعدّل ملفًّا واحدًا: inc/header.php. ولاحظ أن المتغير $title الذي عرّفته الصفحة ظهر داخل header.php: الملف المضمَّن يرى متغيرات الملف الذي ضمّنه.
inc/header.php، ثم تنقّل بين الصفحتين: ظهر في كلتيهما.٤المسارات: استخدم __DIR__
المسار النسبي مثل 'inc/header.php' يُحسب من المجلد الذي يعمل منه PHP، لا من مكان الملف الذي كتبته فيه. فإن ضمّن ملف داخل مجلد ملفًّا آخر، قد يضيع المسار. الحل الآمن: الثابت __DIR__، وهو مجلد الملف الحالي دائمًا:
<?php// داخل admin/users.phprequire __DIR__ . '/../inc/functions.php'; // .. تعني المجلد الأعلىrequire __DIR__ . '/../config.php';
٥بنية مشروع مرتّبة
مع نمو التطبيق نفصل الملفات حسب دورها. هذه بنية بسيطة ستستخدمها في مشروع المقرر:
my-app/├── public/ ← جذر الموقع (DocumentRoot): ما يُطلب من المتصفح فقط│ ├── index.php│ ├── products.php│ └── css/style.css├── inc/ ← الوحدات: لا تُطلب مباشرة│ ├── config.php ← الإعدادات وكلمات المرور│ ├── db.php ← الاتصال بقاعدة البيانات│ └── functions.php└── templates/ ← قوالب العرض ├── header.php └── footer.php
publicهو الوحيد المكشوف للإنترنت. فلا يستطيع أحد طلبinc/config.phpمباشرة.incللمنطق، وtemplatesللعرض. فصلهما يجعل تعديل الشكل لا يمس المنطق.- ملف الإعداد يحوي ثوابت مثل اسم قاعدة البيانات، ويُضمَّن بـ
require_onceفي كل صفحة.
توقّع وتحقّق
ملف الاتصال بقاعدة البيانات ضروري. نضمّنه بـ:
ماذا تفعل include عند غياب الملف؟
require.__DIR__ تعني:
PHP.هل يرى الملف المضمَّن متغيرات الملف الذي ضمّنه؟
الخلاصة
- كل جزء مشترك في ملف واحد، تضمّنه حيث تحتاجه.
require: خطأ قاتل إن غاب الملف، وinclude: تحذير فقط، و_once: مرة واحدة.- الملف المضمَّن يرى متغيرات من ضمّنه (مثل $
title). __DIR__.'/inc/x.php'مسار ثابت مهما كان مكان التشغيل.publicللمكشوف، وincللمنطق، وtemplatesللعرض.
تحدٍّ برمجي: أزل التكرار
متوسطالصفحتان index.php و courses.php تكرران الدالة e() والقائمة والتذييل. انقل الأجزاء المشتركة إلى ملفي layout، ثم أضف رابطًا ثالثًا للقائمة.
- انقل الدالة
e()والقائمة<nav>إلىlayout/top.php، والتذييل إلىlayout/bottom.php. - في كل صفحة:
requireللملفين (مع __DIR__)، ويبقى فيها محتواها الخاص فقط. - أضف إلى القائمة رابطًا ثالثًا:
<a href="contact.php">تواصل</a>(مرة واحدة في top.php).
👀 اعرض الاستجابة المطلوبة
GET /index.phpGET /courses.php
- الكود يعمل بلا أخطاء أو تحذيرات
- الرئيسية: فيها القائمة الجديدة والعنوان والتذييل
- الدورات: فيها القائمة الجديدة والمحتوى والتذييل
- الصفحتان تضمّنان
layout/top.phpوlayout/bottom.php - القائمة لا تتكرر داخل الصفحة
- الدالة
e()معرّفة مرة واحدة فقط (في top.php)
💡 تلميح 1
<?php require __DIR__ . '/layout/top.php'; ?>💡 تلميح 2
Cannot redeclare e()» فالدالة ما زالت في إحدى الصفحتين.حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
<?php require __DIR__ . '/layout/top.php'; ?><h1>معهد التدريب</h1><p>مرحبًا بك.</p><?php require __DIR__ . '/layout/bottom.php'; ?>
<?php require __DIR__ . '/layout/top.php'; ?><h1>الدورات</h1><p><?= e('تطوير الويب') ?></p><?php require __DIR__ . '/layout/bottom.php'; ?>
<?phpfunction e($s) { return htmlspecialchars($s); }?><nav><a href="index.php">الرئيسية</a> | <a href="courses.php">الدورات</a> | <a href="contact.php">تواصل</a></nav>
<footer>© المعهد</footer>