تخطَّ إلى المحتوى

تقسيم المشروع في PHP: include و require

الدرس 12 من 12 · PHP

موقعك فيه عشر صفحات، وكلها تبدأ بالقائمة العلوية نفسها وتنتهي بالتذييل نفسه. ماذا لو أردت إضافة رابط جديد في القائمة؟ هل تعدّل عشرة ملفات؟ في هذا الدرس، وهو الأخير في الكورس، ستتعلم كيف تكتب كل جزء مرة واحدة في ملف مستقل، ثم تضمّنه حيث تحتاجه بـ include و require.

الفكرة: ملفات مثل قطع المكعبات

Section titled “الفكرة: ملفات مثل قطع المكعبات”

تضمين ملف يعني: «يا PHP، ضعي محتوى ذلك الملف هنا، كأنني كتبته في هذا المكان». تخيّل مكعبات البناء: تصنع قطعة للترويسة وقطعة للتذييل، ثم تركّبهما في كل صفحة. وإذا عدّلت القطعة، تغيّرت في كل مكان رُكّبت فيه.

لنبدأ بقطعة الترويسة:

header.php
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<title><?= htmlspecialchars($pageTitle) ?> | متجري</title>
</head>
<body>
<nav>
<a href="index.php">الرئيسية</a>
<a href="about.php">من نحن</a>
</nav>

ثم قطعة التذييل:

footer.php
<footer>
<p>كل الحقوق محفوظة - متجري</p>
</footer>
</body>
</html>

والآن أي صفحة في الموقع تصبح قصيرة جداً:

index.php
<?php
$pageTitle = "الرئيسية";
require "header.php";
?>
<h1>أهلاً بك في متجري</h1>
<p>أحدث المنتجات تجدها هنا.</p>
<?php require "footer.php"; ?>

وهذا ما يصل إلى المتصفح:

مصدر الصفحة الناتجة
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<title>الرئيسية | متجري</title>
</head>
<body>
<nav>
<a href="index.php">الرئيسية</a>
<a href="about.php">من نحن</a>
</nav>
<h1>أهلاً بك في متجري</h1>
<p>أحدث المنتجات تجدها هنا.</p>
<footer>
<p>كل الحقوق محفوظة - متجري</p>
</footer>
</body>
</html>

لاحظ شيئاً مهماً: المتغير $pageTitle عرّفناه في index.php قبل التضمين، فاستطاع header.php أن يستخدمه. الملف المضمَّن يرى كل المتغيرات الموجودة في مكان تضمينه، كأنه جزء من الملف نفسه.

الاثنتان تضمّنان الملف، والفرق في ما يحدث إذا لم يوجد الملف:

  • include تعرض تحذيراً، وتكمل تنفيذ الصفحة.
  • require توقف الصفحة فوراً بخطأ قاتل.
test.php
<?php
include "missing.php";
echo "أكملت بعد include\n";
require "missing.php";
echo "هذا السطر لن يظهر\n";

عند التشغيل ستظهر رسالة Warning: include(missing.php): Failed to open stream: No such file or directory، ثم الجملة «أكملت بعد include»، ثم يتوقف كل شيء عند require بالرسالة Fatal error: Uncaught Error: Failed opening required 'missing.php'.

أيهما تختار؟ اسأل نفسك: هل تعمل الصفحة بدون هذا الملف؟ ملف الدوال أو الاتصال بقاعدة البيانات ضروري، وإكمال الصفحة بدونه يسبب أخطاء أغرب، فاستخدم require. وهذا حال أغلب الملفات، ولذلك require هي الاختيار الأشهر.

الدوال التي تحتاجها في أكثر من صفحة تجمعها في ملف واحد:

functions.php
<?php
function formatPrice(float $price): string {
return number_format($price, 2) . " جنيه";
}
function discount(float $price, int $percent): float {
return $price - $price * $percent / 100;
}

ثم تضمّنه حيث تحتاجه:

products.php
<?php
require_once "functions.php";
require_once "functions.php";
$products = ["قلم" => 5, "حقيبة" => 149.5];
foreach ($products as $name => $price) {
echo $name . ": " . formatPrice($price) . "\n";
}
echo "بعد الخصم: " . formatPrice(discount(200, 15)) . "\n";
الناتج
قلم: 5.00 جنيه
حقيبة: 149.50 جنيه
بعد الخصم: 170.00 جنيه

كتبنا سطر التضمين مرتين عمداً، ولم يحدث شيء! الكلمة _once تعني «مرة واحدة فقط»: إذا كان الملف قد ضُمّن من قبل، تتجاهل PHP الطلب الثاني. جرّب تغيير السطرين إلى require العادية، وستتوقف الصفحة بالخطأ Cannot redeclare، لأن الدالة عُرّفت مرتين.

هذا يحدث كثيراً في المشاريع الكبيرة دون قصد: ملف يضمّن الدوال، ويضمّن ملفاً آخر يضمّنها أيضاً.

الصيغة الملف غير موجود التضمين المكرر
include تحذير وتكمل يُضمَّن مرة أخرى
require خطأ يوقف الصفحة يُضمَّن مرة أخرى
include_once تحذير وتكمل يُتجاهل
require_once خطأ يوقف الصفحة يُتجاهل

القاعدة العملية: require_once لملفات الدوال والكلاسات والإعدادات، و require لقطع HTML مثل الترويسة والتذييل.

الملف المضمَّن يستطيع أن يعيد قيمة بـ return، وتستقبلها أنت في متغير. هذه طريقة أنيقة لملفات الإعدادات:

config.php
<?php
return [
"site_name" => "متجري",
"items_per_page" => 12,
];
settings.php
<?php
$config = require "config.php";
echo $config["site_name"] . "\n";
echo $config["items_per_page"] . "\n";
الناتج
متجري
12

هكذا تجتمع كل إعدادات الموقع في مكان واحد، وهو الأسلوب نفسه الذي يتبعه Laravel في مجلد config.

عندما يكبر المشروع ستضع الملفات في مجلدات:

my-shop/
├─ index.php
├─ about.php
├─ config.php
└─ includes/
├─ header.php
├─ footer.php
└─ functions.php

وهنا يظهر فخ: المسار النسبي مثل "includes/header.php" يُحسب من الملف الذي بدأ منه الطلب، لا من الملف الذي كُتب فيه السطر. فإذا ضمّن ملف داخل includes ملفاً آخر، فقد لا يجده.

الحل هو الثابت __DIR__، وقيمته دائماً المسار الكامل لمجلد الملف الحالي:

<?php
require __DIR__ . "/includes/header.php";

بهذه الصيغة يعمل التضمين مهما كان الملف الذي بدأ منه الطلب. استخدمها دائماً في المشاريع الحقيقية.

ابنِ موقعاً صغيراً من ثلاث صفحات: index.php و about.php و contact.php، تشترك كلها في header.php و footer.php. أضف في الترويسة قائمة تنقل، واجعل كل صفحة تحدد عنوانها في $pageTitle. ثم أضف رابطاً رابعاً في القائمة، وتأكد أنه ظهر في الصفحات الثلاث بتعديل ملف واحد.

تحدٍّ إضافي: ميّز رابط الصفحة الحالية في القائمة بلون مختلف، بمقارنة $pageTitle داخل header.php.

  • تضمين ملف الدوال بـ require أكثر من مرة: يسبب Cannot redeclare. استخدم require_once.
  • استخدام متغير قبل تعريفه: إذا ضمّنت header.php قبل تعريف $pageTitle فسترى تحذير Undefined variable. عرّف المتغيرات أولاً.
  • الاعتماد على المسارات النسبية في المجلدات الفرعية: استخدم __DIR__.
  • مسافة أو سطر فارغ بعد ?> في ملف مضمَّن: يُرسل إلى المتصفح كمخرجات، ويمنع header و session_start. الأفضل ألا تكتب ?> في آخر ملفات PHP الخالصة.

سؤال

ما الفرق بين include و require عند غياب الملف؟

سؤال

ماذا يعطي الثابت __DIR__؟

  • التضمين يضع محتوى ملف في مكان السطر، فتكتب الأجزاء المشتركة مرة واحدة.
  • require للملفات الضرورية، و include لما يمكن الاستغناء عنه.
  • require_once تمنع تضمين الملف مرتين، وهي الأنسب لملفات الدوال.
  • الملف المضمَّن قد يعيد قيمة بـ return، مثل ملف الإعدادات.
  • استخدم __DIR__ لبناء المسارات، ولا تضمّن ملفاً يحدده الزائر.

مبروك! أنهيت كورس PHP كاملاً. الخطوة التالية: كورس Laravel لبناء تطبيقات ويب احترافية بسرعة.