Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

الربط الاستاتيكي المتأخر (Late Static Binding)

جزء من قسم Object Oriented Programming في رحلة PHP على Coddy — الدرس 36 من 91.

عندما تستخدم self:: في فئة أب (parent class)، فإنه يشير دائمًا إلى الفئة التي كُتب فيها، وليس الفئة التي يتم استخدامها فعليًا. يمكن أن يتسبب هذا في سلوك غير متوقع في الوراثة. يحل الارتباط الاستاتيكي المتأخر (Late static binding) هذه المشكلة باستخدام الكلمة المفتاحية static::.

تأمل هذا المثال حيث لا يعمل self:: كما هو متوقع:

<?php
class ParentClass {
    public static function create() {
        return new self();
    }
    
    public static function getClassName() {
        return self::class;
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();
echo get_class($obj) . "\n";
echo ChildClass::getClassName();

المخرجات:

ParentClass
ParentClass

على الرغم من أننا استدعينا ChildClass::create()، فقد حصلنا على كائن ParentClass لأن self:: يتم حله في وقت التصريف (compile time). الآن دعونا نستخدم static:: بدلاً من ذلك:

<?php
class ParentClass {
    public static function create() {
        return new static();
    }
    
    public static function getClassName() {
        return static::class;
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();
echo get_class($obj) . "\n";
echo ChildClass::getClassName();

المخرجات:

ChildClass
ChildClass

مع static::، ينتظر PHP حتى وقت التشغيل لتحديد الفئة التي سيتم استخدامها. يُسمى هذا بالربط "المتأخر" لأن القرار يتم تأجيله. إنه ضروري لأساليب المصنع (factory methods) وأي أسلوب ثابت يجب أن يحترم الوراثة.

نقطة رئيسية: استخدم static:: بدلاً من self:: عندما تريد أن تقوم الفئات الفرعية (child classes) بوراثة وتخصيص السلوك الساكن (static behavior) بشكل صحيح.

challenge icon

التحدي

سهل

لنقم ببناء نظام سجل نماذج (model registry) يوضح قوة الربط الاستاتيكي المتأخر (late static binding). ستقوم بإنشاء فئة أساسية Model مع طريقة مصنع (factory method) يمكن للفئات التابعة وراثتها — وبفضل static::، ستقوم كل فئة تابعة بإنشاء مثيلات لنفسها بشكل صحيح بدلاً من الفئة الأب.

ستقوم بتنظيم الكود الخاص بك عبر أربعة ملفات:

  • Model.php — أنشئ فئة أساسية Model مع خاصية استاتيكية محمية (protected static property) باسم $tableName مضبوطة على "models". أضف طريقة استاتيكية create() تستخدم new static() لإرجاع مثيل جديد من أي فئة قامت باستدعائها. أضف طريقة استاتيكية أخرى getTable() ترجع static::$tableName. أخيرًا، أضف طريقة describe() ترجع "[ClassName] from table [tableName]" باستخدام static::class و static::$tableName.
  • User.php — أنشئ فئة User ترث من Model. قم بتضمين ملف الأب. قم بتجاوز (Override) الخاصية الاستاتيكية $tableName لتصبح "users". ترث الفئة جميع الطرق من Model دون أي تغييرات.
  • Product.php — أنشئ فئة Product ترث من Model. قم بتضمين ملف الأب. قم بتجاوز الخاصية الاستاتيكية $tableName لتصبح "products". مرة أخرى، ترث الفئة جميع الطرق من الأب.
  • main.php — قم بتضمين ملفي User و Product. استخدم طريقة المصنع create() الموروثة لإنشاء مثيلات لكل من User و Product. لكل مثيل، استدعِ describe() واطبع النتيجة في سطر منفصل. ثم اطبع اسم الجدول لكل فئة باستخدام User::getTable() و Product::getTable()، كل منهما في سطر منفصل.

السحر هنا يكمن في أن الطريقتين create() و getTable() معرفتان فقط في الفئة الأب Model، ومع ذلك تعملان بشكل صحيح مع الفئات التابعة لأن static:: تؤخر الربط حتى وقت التشغيل. عندما تستدعي User::create()، يقوم PHP بإنشاء مثيل User — وليس مثيل Model — لأن static:: تشير إلى الفئة التي تم استدعاؤها بالفعل.

يجب أن يظهر مخرجك أربعة أسطر: وصف مثيل User، ووصف مثيل Product، واسم جدول User، واسم جدول Product.

ورقة مرجعية

تشير الكلمة المفتاحية self:: في الفئة الأب دائمًا إلى الفئة التي كُتبت فيها، ويتم تحديدها في وقت التصريف (compile time). أما **الارتباط الاستاتيكي المتأخر (Late static binding)** باستخدام static:: فيقوم بتحديد المرجع في وقت التشغيل (runtime)، مما يسمح للفئات الابنة بوراثة وتخصيص السلوك الاستاتيكي بشكل صحيح.

استخدام self:: (الارتباط في وقت التصريف)

<?php
class ParentClass {
    public static function create() {
        return new self();  // تُنشئ دائمًا ParentClass
    }
    
    public static function getClassName() {
        return self::class;  // تُرجع دائمًا "ParentClass"
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();  // تُرجع كائنًا من ParentClass
echo ChildClass::getClassName();  // المخرجات: ParentClass

استخدام static:: (الارتباط في وقت التشغيل)

<?php
class ParentClass {
    public static function create() {
        return new static();  // تُنشئ كائنًا من الفئة التي قامت بالاستدعاء
    }
    
    public static function getClassName() {
        return static::class;  // تُرجع اسم الفئة التي قامت بالاستدعاء
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();  // تُرجع كائنًا من ChildClass
echo ChildClass::getClassName();  // المخرجات: ChildClass

نقطة رئيسية: استخدم static:: بدلاً من self:: عندما تريد أن تقوم الفئات الابنة بوراثة وتخصيص السلوك الاستاتيكي بشكل صحيح. هذا أمر ضروري لأنماط المصنع (factory methods) وأي دالة استاتيكية يجب أن تحترم الوراثة.

جرّب بنفسك

<?php

require_once 'User.php';
require_once 'Product.php';

// TODO: استخدم طريقة المصنع create() الموروثة لإنشاء مثيل (instance) من User

// TODO: استخدم طريقة المصنع create() الموروثة لإنشاء مثيل (instance) من Product

// TODO: استدعِ describe() على مثيل User واطبع النتيجة

// TODO: استدعِ describe() على مثيل Product واطبع النتيجة

// TODO: اطبع User::getTable()

// TODO: اطبع Product::getTable()

?>
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming