نص/تم تجميعه بواسطة Zhu Xianzhong
1. مقدمة
لحسن الحظ، تم تقديم تقنية التحميل الزائد للكائنات في PHP 5.0. ستستكشف هذه المقالة إمكانية التحميل الزائد للطرق __call() و __set() و __get(). بعد مقدمة موجزة عن نظرية التحميل الزائد، سننتقل مباشرة إلى الموضوع من خلال مثالين: المثال الأول هو تنفيذ فئة تخزين ثابتة، والمثال الثاني هو إيجاد طريقة لتنفيذ getter/setter الديناميكي.
2. ما هو التحميل الزائد للكائن؟
عند الحديث عن التحميل الزائد للكائنات في PHP، علينا أن نميز بين نوعين:
· التحميل الزائد للطريقة
· التحميل الزائد للسمات
في حالة التحميل الزائد للطريقة، علينا تحديد طريقة سحرية __call()، والتي ستنفذ استدعاء عام لطريقة غير محددة في الصف المقابل. يتم استدعاء هذه الطريقة العامة فقط عندما تريد الوصول إلى طريقة غير محددة في الفصل. بدون التحميل الزائد للطريقة، سيؤدي المثال التالي إلى قيام PHP بعرض رسالة خطأ فادحة: استدعاء طريقة غير محددة ThisWillFail::bar() in/some/directory/example.php في السطر 9 وإحباط تنفيذ البرنامج:
< ?php
فئة ThisWillFail {
الوظيفة العامة foo() {
إرجاع "Hello World!"؛
}
}
$class = new ThisWillFail;
$class->bar();
?>
بمساعدة التحميل الزائد للطريقة، يمكن للكود التقاط هذه المكالمة والتعامل معها بأمان.
التحميل الزائد للخاصية يشبه التحميل الزائد للطريقة. في هذه الحالة، تقوم الفئة بإعادة توجيه (وتسمى أيضًا الوكيل) عمليات القراءة/الكتابة إلى خصائص الفئة التي لم يتم تعريفها بشكل صريح في الفئة. الطرق المتخصصة هنا هي __set() و __get(). اعتمادًا على مستوى الإبلاغ عن الأخطاء، عادةً ما يقوم مترجم PHP إما بإصدار إشعار عند الوصول إلى خاصية غير محددة، أو تأجيل وربما تحديد المتغير. إذا كنت تستخدم التحميل الزائد للسمة، فيمكن للمترجم استدعاء __set() عند تعيين سمة غير محددة، واستدعاء __get() عند الوصول إلى قيمة سمة غير محددة.
خلاصة القول، إن استخدام تقنية التحميل الزائد يمكن أن يقلل بشكل كبير من وقت تطوير البرمجيات عند استخدام اللغات الديناميكية مثل PHP.
عند هذه النقطة، يتم تقديم النظرية، ويتم تحليل الترميز المحدد أدناه.
3. أمثلة على فئات التخزين المستمر
الكود التالي ينفذ فئة التخزين المستمر المذكورة أعلاه بأقل من 50 سطرًا من كود PHP باستخدام تقنية التحميل الزائد للسمات. المصطلح المستمر يعني أن الفصل يمكنه وصف عنصر من بنية البيانات ويظل متزامنًا مع نظام التخزين الأساسي. في مصطلحات الترميز، يمكن للتعليمات البرمجية الخارجية استخدام الفئات لتحديد صف من جدول قاعدة البيانات. بهذه الطريقة، عند تشغيل البرنامج، يمكنك الوصول مباشرة إلى سمات الفئة لمعالجة العناصر الموجودة في الصف (القراءة/الجلب). في نهاية البرنامج النصي، ستكون PHP مسؤولة عن إرسال بيانات الصف المحدثة مرة أخرى إلى قاعدة البيانات.
ستساعدك دراسة الكود التالي بعناية على فهم ماهية التحميل الزائد للسمات.
<?php
// تحميل PEAR's <a href=" http://pear.php.net/package/DB/ "> حزمة DB</a>
require_once "DB.php";
فئة ثابتة {
بيانات $ خاصة = صفيف () ؛
جدول خاص $ = "المستخدمين";
الوظيفة العامة __construct($user) {
$this->dbh = DB::Connect("mysql://user:password@localhost/database");
$query = "حدد المعرف والاسم والبريد الإلكتروني والبلد من" .
$this->table . "أين الاسم =؟";
$this->data = $this->dbh->getRow($query, array($user),
DB_FETCHMODE_ASSOC);
}
الوظيفة العامة __get($عضو) {
إذا (isset($this->data[$ member])) {
إرجاع $this->data[$ member];
}
}
الوظيفة العامة __set($عضو، قيمة $) {
// معرف مجموعة البيانات للقراءة فقط if ($ member == "id") {
يعود؛
}
إذا (isset($this->data[$ member])) {
$this->data[$ member] = $value;
}
}
الوظيفة العامة __destruct() {
$query = "UPDATE" $this->table "اسم التعيين = ?,
البريد الإلكتروني = ?, البلد = أين المعرف = ؟";
$this->dbh->query($query, $this->name, $this->email,
$this->country, $this->id);
}
}
$class = new Persistable("مارتن يانسن");
$class->name = "جون دو";
$class->country = "الولايات المتحدة";
$class->email = " [email protected] ";
?>
المشكلة الأولى التي قد تواجهها هي __construct()، وهي طريقة البناء الجديدة المقدمة في PHP 5. في أيام PHP 4، كان المنشئون يطابقون دائمًا أسماء فئاتهم. لم يعد هذا هو الحال في PHP 5. لا تحتاج إلى معرفة الكثير عن طريقة الإنشاء، باستثناء أن استدعائها يؤدي إلى إنشاء مثيل لفئة؛ ولاحظ أنه يتم استخدام معلمة هنا - يتم تنفيذ قاعدة بيانات بناءً على هذه المعلمة. يقوم هذا المُنشئ بتعيين نتائج الاستعلام إلى سمة الفئة $data.
بعد ذلك، يحدد البرنامج طريقتين خاصتين __get() و __set(). يجب أن تكون على دراية بها بالفعل: يتم استخدام __get() لقراءة قيم السمات غير المحددة، ويتم استخدام __set() لتعديل قيم السمات غير المحددة.
هذا يعني أنه عندما تتم قراءة/كتابة خاصية غير محددة من فئة تخزين ثابتة، تكون هذه الطرق المتخصصة مسؤولة عن إدارة المعلومات في متغير صفيف الخاصية $data، بدلاً من تغيير خصائص الفئة مباشرة (تذكر: المتغير $data يحتوي على صف من قاعدة البيانات!).
الطريقة الأخيرة في الفصل هي عكس __construct() - المدمر __destruct(). تستدعي PHP أداة التدمير أثناء "مرحلة إيقاف تشغيل البرنامج النصي"، والتي تكون عادةً قريبة من نهاية تنفيذ برنامج PHP النصي. يقوم المدمر بكتابة المعلومات من سمة $data مرة أخرى إلى قاعدة البيانات. هذا هو بالضبط ما يعنيه مصطلح التزامن السابق.
ربما لاحظت أن الكود هنا يستخدم حزمة طبقة تجريد قاعدة بيانات PEAR. في الواقع، لا يهم، التواصل مع قاعدة البيانات من خلال طرق أخرى يمكن أن يوضح أيضًا موضوع هذه المقالة.
إذا نظرت بعناية، ستجد أن وصف فئة التخزين المستمر هذه بسيط نسبيًا. يتضمن المثال جدول قاعدة بيانات فقط، ولا يأخذ في الاعتبار نماذج البيانات الأكثر تعقيدًا، مثل استخدام LEFT JOIN وتقنيات تشغيل قاعدة البيانات المعقدة الأخرى. ومع ذلك، لا يتعين عليك الالتزام بهذا، وبمساعدة التحميل الزائد للخاصية، يمكنك استخدام نموذج قاعدة البيانات المثالي الخاص بك. مع القليل من التعليمات البرمجية، يمكنك الاستفادة من ميزات قاعدة البيانات المعقدة في فئة التخزين الدائمة هذه.
هناك أيضًا مشكلة صغيرة - لا يتم تقديم آلية معالجة الأخطاء عند فشل الاستعلام في أداة التدمير. إن طبيعة أدوات التدمير هي التي تجعل من المستحيل عرض رسالة خطأ مناسبة في هذه الحالة، لأن إنشاء علامة HTML غالبًا ما ينتهي قبل أن تستدعي PHP أداة التدمير.
لحل هذه المشكلة، يمكنك إعادة تسمية __destruct() إلى شيء مثل saveData() وتنفيذ هذه الطريقة يدويًا في مكان ما في البرنامج النصي للاتصال. هذا لا يغير مفهوم التخزين المستمر للفئات، فهو مجرد بضعة أسطر أخرى من التعليمات البرمجية. وبدلاً من ذلك، يمكنك استخدام الدالة error_log() في أداة التدمير لتسجيل رسالة الخطأ إلى ملف سجل الأخطاء على مستوى النظام.
هذه هي الطريقة التي يعمل بها التحميل الزائد للممتلكات. بعد ذلك نناقش طريقة التحميل الزائد.
4. أمثلة على التحميل الزائد للطريقة
1. أساليب Getter/Setter الديناميكية
تطبق التعليمات البرمجية التالية أساليب getter/setter "الديناميكية" للتحكم في الفصل بمساعدة طريقة التحميل الزائد. دعنا نحللها بناءً على الكود المصدري:
<?php
فئة DynamicGetterSetter {
اسم $ خاص = "مارتن يانسن";
خاص $starbucksdrink = "كاراميل كابتشينو دوامة";
الدالة __call(طريقة $, $arguments) {
$prefix = strtolower(substr($method, 0, 3));
$property = strtolower(substr($method, 3));
إذا (فارغ($بادئة) || فارغ($خاصية)) {
يعود؛
}
إذا ($prefix == "get" && isset($this->$property)) {
إرجاع $this->$property;
}
إذا ($بادئة == "مجموعة") {
$this->$property = $arguments[0];
}
}
}
$class = new DynamicGetterSetter;
صدى "الاسم: " . $class->getName() .
echo "نكهة ستاربكس المفضلة:" $class->getStarbucksDrink() "nn";
$class->setName("جون دو");
$class->setStarbucksDrink("القهوة الكلاسيكية");
صدى "الاسم: " . $class->getName() .
echo "نكهة ستاربكس المفضلة:" $class->getStarbucksDrink() "nn";
?>
من الواضح أن السمتين $name و$starbucksdrink هنا خاصتان، مما يعني أنه لا يمكن الوصول إلى هاتين السمتين من خارج الفصل. في البرمجة الموجهة للكائنات، من الشائع جدًا تنفيذ أساليب getter/setter العامة للوصول إلى قيم الخصائص غير العامة أو تعديلها. وتنفيذ هذه الأمور شاق ويستغرق وقتًا وجهدًا.
يمكن حل هذه المشكلة بسهولة بمساعدة التحميل الزائد للطريقة. بدلاً من تنفيذ أساليب getter/setter لكل خاصية، ما ورد أعلاه يطبق فقط أسلوب __call() العام. هذا يعني أنه عند استدعاء طريقة getter/setter غير محددة مثل setName() أو getStarbucksdrink()، لن تقوم PHP بإنشاء خطأ فادح وإحباطها، ولكن بدلاً من ذلك ستنفذ (أو تفوض إلى) الطريقة السحرية __call().
هذه بعض المقدمات المختصرة، فلنقم بتحليل متعمق لـ __call().
2. التحليل التفصيلي لطريقة __call()
المعلمة الأولى لـ __call() هي الطريقة الأصلية وغير المحددة (مثل setName). المعلمة الثانية عبارة عن مصفوفة أحادية البعد تحتوي على فهرس رقمي، والتي تحتوي على جميع الطرق الأصلية المعلمة. سيؤدي استدعاء أسلوب غير محدد بمعلمتين ("Martin" و 42) إلى إنتاج المصفوفة التالية:
$class->thisMethodDoesNotExist("Martin", 42);
/ دليل إلى المعلمة الثانية لـ __call()
صفيف
(
[0] =>مارتن
[1] => 42
)
داخل الطريقة __call()، إذا كانت الطريقة الأصلية تبدأ بـ get أو set، فيجب إجراء بعض العمليات الحسابية لتحديد ما إذا كان الكود يستدعي طريقة getter/setter. علاوة على ذلك، ستقوم هذه الطريقة بتحليل مكون آخر من اسم الطريقة (باستثناء الأحرف الثلاثة الأولى)، لأن الجزء الأخير من السلسلة يمثل اسم السمة المشار إليها بواسطة getter/setter.
إذا كان اسم الطريقة يشير إلى مُحضر/أداة ضبط، فإن الطريقة إما تُرجع قيمة الخاصية المقابلة أو تقوم بتعيين قيمة المعلمة الأولى للطريقة الأصلية. إذا لم يكن الأمر كذلك، فإنه لا يفعل شيئًا ويستمر في تنفيذ البرنامج وكأن شيئًا لم يحدث.
3. لتحقيق الهدف
، في الأساس، تتوافق أي سمة مع طريقة تسمح للكود باستدعاء أي طريقة getter/setter ديناميكيًا. يعد هذا مناسبًا عند تطوير نموذج أولي للبرنامج على المدى القصير: فبدلاً من قضاء الكثير من الوقت في تنفيذ الحروف/المحددات، يمكن للمطور التركيز على نمذجة واجهة برمجة التطبيقات والتأكد من صحة التطبيق بشكل أساسي. قد يؤدي دمج طريقة __call() في فئة مجردة إلى تمكينك من إعادة استخدام التعليمات البرمجية في تطوير مشروع PHP المستقبلي
4. بالإضافة إلى أوجه القصور،
هناك مزايا وعيوب. هناك عدة عيوب في الطريقة المذكورة أعلاه: قد تستخدم المشاريع الكبيرة أدوات مثل phpDocumentor لتتبع بنية واجهة برمجة التطبيقات (API). مع الطريقة الديناميكية المقدمة أعلاه، بالطبع لن تظهر جميع أساليب getter/setter في المستند الذي تم إنشاؤه تلقائيًا، الأمر الذي لا يتطلب المزيد من الشرح.
عيب آخر هو أن الكود الموجود خارج الفصل يمكنه الوصول إلى كل خاصية خاصة داخل الفصل. عند استخدام أساليب getter/setter الحقيقية، من الممكن التمييز بين الخصائص الخاصة التي يمكن الوصول إليها عن طريق تعليمات برمجية خارجية والخصائص الخاصة "الحقيقية" غير المرئية خارج الفصل - لأن لدينا طريقة تحميل زائدة، ولدينا حروف وSetters افتراضية يمكن استخدام الأساليب.
5. الخلاصة
تحلل هذه المقالة بعناية حالتي التحميل الزائد للكائنات في PHP 5.0 من خلال مثالين. آمل بشدة أن تساعدك الطريقة الواردة في هذه المقالة على تحسين كفاءة برمجة PHP، وفي الوقت نفسه، يجب أن ترى بوضوح عيوب هذه الطريقة.