Kodokon kodokon.com

البرمجة الكائنية الحديثة: الأصناف، الخصائص المُرقّاة، مستوى الوصول

اكتب أصناف PHP 8 وفق أعرافها: ترقية الخصائص، readonly، البواني المُسمّاة وكائنات القيمة غير القابلة للتغيير.

9 دقيقة · 3 أسئلة

افتح هذا الدرس في Kodokon

أنت تكتب الأصناف منذ زمن طويل، لكن PHP 8 جعلت عدة عادات قديمة عفا عليها الزمن. وأبرزها: ترقية الخصائص في الباني. فإضافة كلمة مفتاحية لمستوى الوصول (private، protected، public) أمام وسيط الباني تنشئ الخاصية وتُسنِد إليها القيمة في خطوة واحدة. وهكذا تنكمش الكتل الثلاث المتكررة في الأسلوب القديم - التصريح، والوسيط، والإسناد - لتصبح كتلة واحدة.

PHP
<?php

declare(strict_types=1);

class LegacyProduct
{
    private string $name;
    private int $priceCents;

    public function __construct(
        string $name,
        int $priceCents,
    ) {
        $this->name = $name;
        $this->priceCents = $priceCents;
    }
}

final class Product
{
    public function __construct(
        private string $name,
        private int $priceCents,
    ) {
    }

    public function label(): string
    {
        return sprintf(
            '%s (%.2f EUR)',
            $this->name,
            $this->priceCents / 100,
        );
    }
}

$product = new Product('Keyboard', 4990);
echo $product->label();
قبل / بعد: تزيل الترقية كل الكود التكراري الخاص بالإسناد.

يظل مستوى الوصول أداتك الأولى للتغليف: private افتراضيًا، وprotected فقط إذا احتاج صنف ابن إلى الوصول، وpublic كحل أخير. ومنذ PHP 8.1، تضيف readonly ضمانة أقوى: إذ لا يمكن الكتابة إلى الخاصية إلا مرة واحدة، من داخل نطاق الصنف الذي يصرّح بها. وبدمجها مع الترقية، تُنتِج كائنات قيمة غير قابلة للتغيير في بضعة أسطر فقط: فأيّ "تغيير" يُرجِع نسخة جديدة، ما يُلغي الآثار الجانبية المشتركة.

PHP
<?php

declare(strict_types=1);

final class Money
{
    public function __construct(
        public readonly int $amount,
        public readonly string $currency,
    ) {
    }

    public function add(Money $other): self
    {
        if ($other->currency !== $this->currency) {
            throw new InvalidArgumentException(
                'Currency mismatch'
            );
        }

        return new self(
            $this->amount + $other->amount,
            $this->currency,
        );
    }
}

$total = (new Money(500, 'EUR'))
    ->add(new Money(250, 'EUR'));

echo $total->amount;
كائن قيمة غير قابل للتغيير: تُرجِع add() نسخة جديدة.

أما على صعيد الدوال، فيسود نمطان بين المطوّرين ذوي الخبرة. البواني المُسمّاة: باني private مقترن بمصانع ساكنة (static factories) يجعل كل نيّة صريحة ويمنع الإنشاءات غير المتّسقة. الواجهات الانسيابية: إرجاع $this (قابل للتغيير) أو new self(...) (غير قابل للتغيير) يتيح لك سَلسَلة الاستدعاءات. والمفاضلة معروفة جيدًا: فالنسخة غير القابلة للتغيير أكثر أمانًا لكنها تخصّص كائنًا واحدًا لكل استدعاء - وهو أمر لا يُذكر في معظم الأماكن، لكنه قابل للقياس داخل حلقة كثيفة التنفيذ.

PHP
<?php

declare(strict_types=1);

final class User
{
    private function __construct(
        public readonly string $email,
        public readonly string $role,
    ) {
    }

    public static function admin(string $email): self
    {
        return new self($email, 'admin');
    }

    public static function member(string $email): self
    {
        return new self($email, 'member');
    }
}

$user = User::admin('lea@example.com');
echo $user->role;
باني private + مصانع ساكنة: لا يمكن إنشاء إلا الحالات الصحيحة.

اختبار المعرفة

تأكّد من أنك تذكّرت النقاط الأساسية في هذا الدرس.

  1. ماذا تفعل ترقية الخصائص في باني PHP 8؟
    • تجعل كل خاصية عامة (public) تلقائيًا
    • تصرّح بالخاصية وتُسنِد إليها قيمة الوسيط، في خطوة واحدة
    • تولّد دوال وصول (getters) لكل خاصية
    • تحوّل الخصائص إلى ثوابت الصنف
  2. متى يمكن الكتابة إلى خاصية readonly؟
    • أبدًا: إذ يجب أن تتلقّى قيمة افتراضية عند التصريح
    • مرة واحدة بالضبط، من داخل نطاق الصنف الذي يصرّح بها
    • في أيّ وقت، ما دامت الكتابة تأتي من الصنف نفسه
  3. أيّ مستوى وصول تختار لخاصية يجب ألّا يستخدمها إلا الصنف وأصنافه الأبناء؟
    • public
    • protected
    • private