طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 132 · تقنيات تطوير الواجهات الخلفية
الدرس 10 · الوحدة 4: إنشاء وحدات من الجانب الخادم لوظائف التطبيق

الأصناف والكائنات لتنظيم الكود

اجمع البيانات وقواعدها في وحدة واحدة تحميها. ⏱ نحو 35 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تشرح الصنف والكائن والخصائص والدوال والمُنشئ.
  • تنشئ الكائنات وتستخدمها بالسهم -> و $this.
  • تحمي بيانات الكائن بـ private وتطبّق القواعد في دواله.
  • تضع الصنف في ملف مستقل وتعيد استخدامه، وتعرف الوراثة.

١من الدوال المتفرقة إلى الأصناف

كلما كبر التطبيق كثرت الدوال والمتغيرات المترابطة: سلة المشتريات لها بياناتها (العناصر) ولها عملياتها (أضف، احسب المجموع). الصنف Class يجمعهما في وحدة واحدة، والكائن Object نسخة حقيقية منه. وإن درست البرمجة الكائنية في بايثون أو جافا فالأفكار نفسها، بصياغة PHP:

بايثونPHP
تعريف صنفclass Product:class Product { … }
المُنشئdef __init__(self, name):public function __construct(string $name)
الكائن الحاليself.name$this->name
إنشاء كائنp = Product("قهوة")$p = new Product("قهوة");
استدعاء دالةp.label()$p->label()

٢أول صنف

<?phpclass Product{    public string $name;    public float $price;     public function __construct(string $name, float $price)    {        $this->name = $name;        $this->price = $price;    }     public function label(): string    {        return "$this->name: " . number_format($this->price, 2) . " ريال";    }} $a = new Product("قهوة عربية", 35);$b = new Product("تمر سكري", 45.5);echo $a->label() . "\n";echo $b->label() . "\n";echo $b->name;

وفي PHP 8 اختصار جميل: تعريف الخصائص داخل المُنشئ مباشرة (Constructor promotion):

<?phpclass Product{    public function __construct(        public string $name,        public float $price,    ) {}}

٣الإخفاء: private تحمي قواعدك

لو كانت عناصر السلة public لاستطاع أي كود أن يضع فيها كمية سالبة مباشرة. نجعلها private، فلا يغيّرها أحد إلا عبر دوال الصنف التي تطبّق القواعد. هذا مبدأ التغليف Encapsulation:

<?phpclass Cart{    private array $items = [];     public function add(string $name, float $price, int $qty = 1): void    {        if ($qty < 1) {            return;   // قاعدة: لا كميات سالبة أو صفرية        }        $this->items[$name] = [            'price' => $price,            'qty' => ($this->items[$name]['qty'] ?? 0) + $qty,        ];    }     public function count(): int    {        return array_sum(array_column($this->items, 'qty'));    }     public function total(): float    {        $t = 0;        foreach ($this->items as $it) {            $t += $it['price'] * $it['qty'];        }        return $t;    }} $cart = new Cart();$cart->add('قهوة', 35, 2);$cart->add('تمر', 45);$cart->add('قهوة', 35);      // تزيد الكمية ولا تكرر العنصر$cart->add('شاي', 18, -5);   // تُرفض حسب القاعدة echo "العناصر: " . $cart->count() . "\n";echo "المجموع: " . $cart->total() . "\n";
الكلمةمن يصل إلى العضو؟
publicأي كود.
privateدوال الصنف نفسه فقط.
protectedالصنف والأصناف التي ترثه.

جرّب في المختبر أن تكتب بعد إنشاء السلة $cart->items = [];: ستحصل على خطأ، لأن الخاصية private. وهذا بالضبط ما نريده.

٤صنف في ملف: وحدة قابلة لإعادة الاستخدام

العادة: كل صنف في ملف باسمه، داخل مجلد مثل src. والصفحات تضمّنه بـ require_once وتستخدمه:

<?phpsession_start();require_once __DIR__ . '/src/Cart.php'; $cart = new Cart();$cart->add('قهوة', 35, 2);$cart->add('تمر', 45);?><p>في السلة <?= $cart->count() ?> عناصر، والمجموع <?= $cart->total() ?> ريال.</p>

٥الوراثة باختصار

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

<?phpclass User{    public function __construct(protected string $name) {}     public function role(): string { return 'مستخدم'; }     public function badge(): string    {        return "$this->name (" . $this->role() . ")";    }} class Admin extends User{    public function role(): string { return 'مدير'; }} $users = [new User('نورة'), new Admin('سعود')];foreach ($users as $u) {    echo $u->badge() . "\n";}

الدالة badge() واحدة، لكنها أعطت نتيجتين لأن كل كائن نفّذ role() الخاصة به. وسنستخدم أصنافًا كهذه حين نبني تسجيل الدخول والصلاحيات.

توقّع وتحقّق

للوصول إلى دالة في كائن نكتب:

السهم ->

الدالة التي تعمل تلقائيًّا عند new:

المُنشئ.

خاصية private يصل إليها:

التغليف.

الكلمة التي تجعل صنفًا يرث آخر:

class Admin extends User

الخلاصة

تحدٍّ برمجي: صنف المتدرب

متوسط

اكتب الصنف Trainee في src/Trainee.php، واستخدمه في report.php لطباعة تقرير الدرجات.

المطلوب:
  • المُنشئ يستقبل الاسم string $name ويخزنه في خاصية private، والدرجات مصفوفة private تبدأ فارغة.
  • الدالة addGrade(float $g): void تضيف الدرجة فقط إن كانت بين 0 و 100.
  • الدالة average(): float تعيد المتوسط مقرّبًا لخانة واحدة (0 إن لم توجد درجات).
  • الدالة summary(): string تعيد مثلًا: ريم: 86.7 (ناجح) وناجح إن كان المتوسط 60 فأكثر، وإلا «متعثر».
  • report.php جاهز؛ لا تغيّره.
👀 اعرض الاستجابة المطلوبة
✓ التحقق الآلي: يرسل المختبر هذه الطلبات إلى خادمك بالترتيب ثم يفحص الاستجابات
  1. GET /report.php
  • الكود يعمل بلا أخطاء أو تحذيرات
  • ريم: 86.7 (ناجح) — والدرجة 150 مرفوضة
  • خالد: 55 (متعثر)
  • هند بلا درجات: 0 (متعثر)
  • الدرجات والاسم خصائص private
  • تستخدم $this-> للوصول إلى الخصائص

💡 تلميح 1
استخدم اختصار PHP 8: public function __construct(private string $name) {}
💡 تلميح 2
المتوسط: round(array_sum($this->grades) / count($this->grades), 1) بعد التأكد أن المصفوفة غير فارغة.

حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.

<?phprequire_once __DIR__ . '/src/Trainee.php'; $a = new Trainee('ريم');$a->addGrade(90);$a->addGrade(85);$a->addGrade(85);$a->addGrade(150);   // مرفوضة $b = new Trainee('خالد');$b->addGrade(40);$b->addGrade(70); $c = new Trainee('هند'); echo "<p>" . $a->summary() . "</p>";echo "<p>" . $b->summary() . "</p>";echo "<p>" . $c->summary() . "</p>";
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس