الربط الاستاتيكي المتأخر (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) بشكل صحيح.
التحدي
سهللنقم ببناء نظام سجل نماذج (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()
?>يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةمقدمة في OOPClasses مقابل Objectsالكلمة المفتاحية $thisالـ Methodsالـ Propertiesالمنشئ (__construct)الهادم (__destruct)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسيةالكلمة المفتاحية parent::إعادة تعريف الدوال (Method Overriding)الكلمة المفتاحية finalالأصناف المجردة (Abstract Classes)مراجعة - هيكلية الموظفين7التغليف (Encapsulation)
العام، المحمي، الخاص (Public, Protected, Private)نظرة متعمقة على معدلات الوصولدوال Getters و Settersإخفاء المعلوماتميزة Constructor Promotion (8.0)مراجعة - نظام سجلات الطلاب10مفاهيم البرمجة كائنية التوجه المتقدمة
التركيب مقابل الوراثةحقن التبعيةالأصناف المجهولةالتعدادات (PHP 8.1)الألياف (PHP 8.1)تعمق في استنساخ الكائناتالمولدات والمكررات13أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع2مساحات الأسماء والتحميل التلقائي
مقدمة إلى مساحات الأسماءالكلمة المفتاحية useمعيار التحميل التلقائي PSR-4أداة التحميل التلقائي Composerملخص - مشروع منظم5الواجهات والعقود
مقدمة إلى الواجهاتتنفيذ الواجهاتتنفيذ واجهات متعددةالواجهة مقابل الـ Abstract Classالـ Type Hinting مع الواجهاتملخص - حاسبة الأشكال8الميثودات السحرية (Magic Methods)
مقدمة في الميثودات السحرية__toString و __debugInfo__get, __set, __isset, __unset__call و __callStatic__clone واستنساخ الكائنات__serialize و __unserializeمراجعة - Custom Collection3خصائص الكلاس
خصائص الـ Instance مقابل الخصائص الساكنة (Static)الثوابت في الكلاساتالدوال والخصائص الساكنة (Static)الخصائص الخاصة (Private) والمحمية (Protected)خصائص للقراءة فقط (Readonly) في PHP 8.1مراجعة - مدير الحساب البنكي6تعدد الأشكال (Polymorphism)
مراجعة إعادة تعريف الدوال (Method Overriding)تعدد الأشكال عبر الـ Interfacesتلميح النوع (Type Hinting) وأنواع الاتحاد (Union Types)الربط الاستاتيكي المتأخر (Late Static Binding)ملخص - معالج الدفع (Payment Processor)