1. كائن DBQuery
الآن، كائن DBQuery الخاص بنا يحاكي ببساطة الإجراء المخزن - بمجرد تنفيذه، يقوم بإرجاع مورد النتيجة الذي يجب حفظه، وإذا كنت تريد استخدام الوظائف في مجموعة النتائج (مثل num_rows() أو fetch_row() ) )، يجب عليك تمرير كائن MySqlDB. إذن، ما هو التأثير إذا قام كائن DBQuery بتنفيذ الوظائف التي ينفذها كائن MySqlDB (الذي تم تصميمه للعمل على نتائج الاستعلام المنفذ)؟ لنستمر في استخدام الكود من المثال السابق ولنفترض أن موارد النتائج تتم إدارتها الآن بواسطة كائن DBQuery. يظهر الكود المصدري لفئة DBQuery في القائمة 1.
القائمة 1. استخدام فئة DBQuery.
تتطلب "mysql_db.php"؛
require_once 'query.php'؛
$db = new MySqlDb;
$db->connect('host', 'username', 'pass');
$db->query('استخدام content_management_system');
$query = new DBQuery($db);
$query->prepare('SELECT fname،sname from users WHERE username=:1S AND pword=:2S ANDExpire_time<:3I');
يحاول {
إذا($query->execute("visualad", "apron", time()))->num_rows() == 1) {
صدى ("بيانات الاعتماد الصحيحة")؛
} آخر {
صدى ("بيانات اعتماد غير صحيحة / انتهت صلاحية الجلسة")؛
}
} التقاط (QueryException $e) {
echo("خطأ في تنفيذ الاستعلام:" .$e);
}
أكثر ما يهمنا في الكود المعدل أعلاه هو بيان الالتقاط وبيان التنفيذ.
· لم تعد عبارة التنفيذ تُرجع مصدرًا للنتيجة، بل تُرجع الآن كائن DBQuery نفسه.
· يقوم كائن DBQuery الآن بتنفيذ وظيفة num_rows() — التي نعرفها بالفعل من واجهة قاعدة البيانات.
· إذا فشل تنفيذ الاستعلام، فإنه يطرح استثناءً من النوع QueryException. عند تحويلها إلى سلسلة، تقوم بإرجاع تفاصيل الخطأ الذي حدث.
للقيام بذلك، تحتاج إلى استخدام وكيل. في الواقع، أنت تستخدم بالفعل الوكلاء في كائن DBQuery الخاص بنا، ولكنك الآن ستستخدمه بشكل أكثر عمقًا لربطه بإحكام بكائن MySqlDB. تمت تهيئة كائن DBQuery بكائن ينفذ واجهة قاعدة البيانات، وهو يحتوي بالفعل على وظيفة عضو يتم تنفيذها - والتي تستدعي طريقة query() لكائن قاعدة البيانات لتنفيذ الاستعلام. لا يقوم كائن DBQuery نفسه بالاستعلام عن قاعدة البيانات فعليًا، بل يترك هذه المهمة لكائن قاعدة البيانات. هذا هو الوكيل، وهو عملية يمكن للكائن من خلالها تنفيذ سلوك معين عن طريق إرسال رسائل إلى كائن آخر ينفذ نفس السلوك أو سلوكًا مشابهًا.
للقيام بذلك، تحتاج إلى تعديل كائن DBQuery ليشمل كافة الوظائف التي تعمل على مورد نتيجة من كائن قاعدة البيانات. تحتاج إلى استخدام النتائج المخزنة عند تنفيذ استعلام لاستدعاء الوظيفة المقابلة لكائن قاعدة البيانات وإرجاع نتائجها. ستتم إضافة الوظائف التالية:
القائمة 2: توسيع فئة DBQuery باستخدام الوكلاء.
classDBQuery
{
.....
الوظيفة العامة fetch_array()
{
إذا (! is_resource($this->result)) {
طرح استثناء جديد ("لم يتم تنفيذ الاستعلام.");
}
إرجاع $this->db->fetch_array($this->result);
}
الوظيفة العامة fetch_row()
{
إذا (! is_resource($this->result)) {
طرح استثناء جديد ("لم يتم تنفيذ الاستعلام.");
}
إرجاع $this->db->fetch_row($this->result);
}
الوظيفة العامة fetch_assoc()
{
إذا (! is_resource($this->result)) {
طرح استثناء جديد ("لم يتم تنفيذ الاستعلام.");
}
إرجاع $this->db->fetch_assoc($this->result);
}
الوظيفة العامة fetch_object()
{
إذا (! is_resource($this->result)) {
طرح استثناء جديد ("لم يتم تنفيذ الاستعلام.");
}
إرجاع $this->db->fetch_object($this->result);
}
الوظيفة العامة num_rows()
{
إذا (! is_resource($this->result)) {
طرح استثناء جديد ("لم يتم تنفيذ الاستعلام.");
}
إرجاع $this->db->num_rows($this->result);
}
}
إن تنفيذ كل وظيفة بسيط جدًا. يقوم أولاً بالتحقق للتأكد من تنفيذ الاستعلام، ثم يقوم بتفويض المهمة إلى كائن قاعدة البيانات، ويعيد نتائجه كما لو كان كائن الاستعلام نفسه (يسمى وظيفة قاعدة البيانات الأساسية).
2. تلميح النوع
لكي يعمل الوكيل، نحتاج إلى التأكد من أن المتغير $db لكائن DBQuery هو مثيل لكائن يقوم بتنفيذ واجهة قاعدة البيانات. تلميحات الكتابة هي ميزة جديدة في PHP 5 تمكنك من فرض معلمات الوظيفة على كائنات من نوع معين. قبل PHP 5، كانت الطريقة الوحيدة للتأكد من أن معلمة الدالة هي نوع كائن محدد هي استخدام وظيفة التحقق من النوع المتوفرة في PHP (أي is_a()). الآن، يمكنك ببساطة تحويل نوع الكائن — عن طريق بادئة معلمة الدالة باسم النوع. لقد رأيت بالفعل تلميحات الكتابة من كائن DBQuery الخاص بنا، والذي يضمن تمرير الكائن الذي ينفذ واجهة قاعدة البيانات إلى مُنشئ الكائن.
الوظيفة العامة __construct(DB $db)
{
$this->db = $db;
}
عند استخدام تلميحات الكتابة، لا يمكنك تحديد أنواع الكائنات فحسب، بل يمكنك أيضًا تحديد الفئات والواجهات المجردة.
3. رمي الاستثناءات
ربما لاحظت من الكود أعلاه أن ما لاحظته هو استثناء يسمى QueryException (سنقوم بتنفيذ هذا الكائن لاحقًا). الاستثناء يشبه الخطأ، ولكنه أكثر عمومية. أفضل طريقة لوصف الاستثناء هي استخدام الطوارئ. على الرغم من أن حالة الطوارئ قد لا تكون "قاتلة"، إلا أنه لا يزال يتعين التعامل معها. عندما يتم طرح استثناء في PHP، يتم إنهاء نطاق التنفيذ الحالي بسرعة، سواء كان دالة أو كتلة Try..catch أو البرنامج النصي نفسه. يقوم الاستثناء بعد ذلك باجتياز مكدس الاستدعاءات — مما يؤدي إلى إنهاء كل نطاق تنفيذ — حتى يتم اكتشافه في كتلة محاولة..التقاط أو يصل إلى أعلى مكدس الاستدعاءات — وعند هذه النقطة يُنشئ خطأ فادحًا.
تعد معالجة الاستثناءات ميزة جديدة أخرى في PHP 5. عند استخدامها مع OOP، يمكنها تحقيق تحكم جيد في معالجة الأخطاء والإبلاغ عنها. تعتبر كتلة Try..catch آلية مهمة للتعامل مع الاستثناءات. بمجرد اكتشاف الاستثناء، سيستمر تنفيذ البرنامج النصي من السطر التالي من التعليمات البرمجية حيث تم اكتشاف الاستثناء ومعالجته.
إذا فشل الاستعلام، فستحتاج إلى تغيير وظيفة التنفيذ الخاصة بك لطرح استثناء. سوف تقوم برمي كائن استثناء مخصص يسمى QueryException - يتم تمرير كائن DBQuery الذي تسبب في الخطأ إليه.
قائمة 3. يلقي استثناء.
/**
* تنفيذ الاستعلام الحالي
*
* تنفيذ الاستعلام الحالي — مع استبدال أي نقاط بالوسيطات المتوفرة
* .
*
* @parameters: $queryParams مختلط،... معلمات الاستعلام
* @return: المورد أ - مرجع يصف المورد الذي يتم تنفيذ الاستعلام عليه.
*/
تنفيذ الوظيفة العامة($queryParams = '')
{
// على سبيل المثال: حدد * من الجدول حيث الاسم=:1S والنوع=:2I والمستوى=:3N
$args = func_get_args();
إذا ($هذا->store_procedure) {
/*استدعاء وظيفة الترجمة للحصول على الاستعلام*/
$query = call_user_func_array(array($this, 'compile'), $args);
} آخر {
/*لم تتم تهيئة الإجراء المخزن، وبالتالي يتم تنفيذه كاستعلام قياسي*/
$query = $queryParams;
}
$result = $this->db->query($query);
إذا (! نتيجة $) {
طرح QueryException جديد($this);
}
$this->result = $result;
/* لاحظ كيف نعيد الكائن نفسه الآن، مما يسمح لنا باستدعاء وظائف الأعضاء من نتيجة إرجاع هذه الوظيفة */
إرجاع $هذا؛
}
4. استخدم الوراثة لطرح استثناءات مخصصة
في PHP، يمكنك طرح أي كائن كاستثناء؛ ومع ذلك، أولاً، يجب أن يرث الاستثناء من فئة الاستثناء المضمنة في PHP. من خلال إنشاء الاستثناء المخصص الخاص بك، يمكنك تسجيل معلومات أخرى حول الخطأ، أو إنشاء إدخال في ملف سجل، أو القيام بأي شيء تريده. سيقوم الاستثناء المخصص الخاص بك بما يلي:
· تسجيل رسالة الخطأ من كائن قاعدة البيانات الناتج عن الاستعلام.
· قم بإعطاء تفاصيل دقيقة عن سطر التعليمات البرمجية الذي حدث فيه خطأ الاستعلام — عن طريق فحص مكدس الاستدعاءات.
· عرض رسائل الخطأ ونص الاستعلام — عند تحويلها إلى سلسلة.
للحصول على رسالة الخطأ ونص الاستعلام، يجب إجراء العديد من التغييرات على كائن DBQuery.
1. يجب إضافة سمة محمية جديدة — compiledQuery — إلى الفصل.
2. تقوم وظيفة التحويل البرمجي () بتحديث خاصية الاستعلام المترجم بنص الاستعلام.
3. يجب إضافة وظيفة لاسترداد نص الاستعلام المترجم.
4. يجب أيضًا إضافة وظيفة - فهي تحصل على كائن قاعدة البيانات الحالي المرتبط بكائن DBQuery.
القائمة 4. رمي استثناء.
classDBQuery
{
/**
* قم بتخزين النسخة المترجمة من الاستعلام بعد استدعاء التحويل البرمجي () أو التنفيذ () *
* @var string $compiledQuery
*/
محمي $compiledQuery;
/**
* إرجاع الاستعلام المترجم دون تنفيذه.
* @parameters: معلمات $ مختلطة،...معلمات الاستعلام* @return: سلسلة - استعلام مترجم*/
ترجمة الوظيفة العامة($params='')
{
إذا (! $this->stored_procedure) {
رمي استثناء جديد("لم تتم تهيئة الإجراء المخزن.");
}
/*استبدال المعلمات*/
$params = func_get_args(); // احصل على معلمات الدالة $query = preg_replace("/(?compile_callback($params, 1, "2")', $this->query);
return ($this->compiledQuery = $this->add_strings($query)); // ضع السلسلة مرة أخرى في الاستعلام}
الوظيفة العامة getDB()
{
إرجاع $this->db;
}
الوظيفة العامة getCompiledQuery ()
{
إرجاع $this->compiledQuery;
}
}
الآن، يمكنك تنفيذ فئة QueryException. لاحظ كيف تتنقل خلال مكدس الاستدعاءات للعثور على الموقع في البرنامج النصي الذي تسبب بالفعل في الخطأ. هذا هو الحال تمامًا عندما يكون كائن DBQuery الذي يطرح الاستثناء فئة فرعية ترث من كائن DBQuery.
القائمة 5: فئة QueryException.
/**
*استثناء الاستعلام
*
*عند محاولة تنفيذ استعلام، في حالة حدوث خطأ، سيتم طرح خطأ بواسطة الكائن {@link DBQuery}
*/
فئة QueryException يمتد الاستثناء
{
/**
*نص الاستعلام*
* @var string $QueryText؛
*/
محمي $QueryText؛
/**
*رقم الخطأ/الرمز من قاعدة البيانات*
* @var سلسلة $ErrorCode
*/
محمي $ErrorNumber؛
/**
*رسالة خطأ من قاعدة البيانات*
* @var سلسلة $ErrorMessage
*/
محمية $ErrorMessage;
/**
*منشئ الصف*
* @Parameter: DBQuery $db، وهو كائن الاستعلام الذي يطرح الاستثناء */
الوظيفة العامة __construct(DBQuery $query)
{
/*الحصول على مكدس الاستدعاءات*/
$backtrace = $this->GetTrace();
/*قم بتعيين السطر والملف إلى الموقع الذي حدث فيه الخطأ بالفعل*/
إذا (العدد($backtrace) > 0) {
$س = 1;
/*إذا كانت فئة الاستعلام موروثة، فسنحتاج إلى تجاهل الاستدعاءات التي تجريها الفئات الفرعية*/
while((! isset($backtrace[$x]['line'])) ||
(isset($backtrace[$x]['class']) && is_subclass_of($backtrace[$x]['class'], 'DBQuery')) ||
(strpos(strtolower(@$backtrace[$x]['function']), 'call_user_func')) !== false ) {
/*تنفيذ التكرار، طالما لا يوجد رقم سطر أو أن الوظيفة التي يتم استدعاؤها هي فئة فرعية من فئة DBQuery*/
++$x;
/*إذا وصلنا إلى أسفل المكدس، نستخدم المتصل الأول*/
إذا (($x) >= العد($backtrace)) {
$x = العد($backtrace);
استراحة؛
}
}
/*إذا تم تنفيذ الحلقة أعلاه مرة واحدة على الأقل، فيمكننا إنقاصها بمقدار 1 للعثور على السطر الفعلي من التعليمات البرمجية الذي تسبب في الخطأ*/
إذا ($x != 1) {
$x -= 1;
}
/*أخيرًا، يمكننا تعيين أرقام الملفات والأسطر، والتي يجب أن تعكس عبارة SQL التي تسببت في الخطأ*/
$this->line = $backtrace[$x]['line'];
$this->file = $backtrace[$x]['file'];
}
$this->QueryText = $query->getCompiledQuery();
$this->ErrorNumber = $query->getDB()->errno();
$this->ErrorMessage = $query->getDB()->error();
/*استدعاء مُنشئ استثناءات الفئة الفائقة*/
الأصل::__construct('خطأ في الاستعلام', 0);
}
/**
*الحصول على نص الاستعلام*
* @return نص استعلام السلسلة */
الوظيفة العامة GetQueryText ()
{
إرجاع $this->QueryText;
}
/**
*حصلت على رقم الخطأ*
* @return رقم خطأ السلسلة */
الوظيفة العامة GetErrorNumber()
{
إرجاع $this->ErrorNumber;
}
/**
*تلقيت رسالة خطأ*
* @return رسالة خطأ السلسلة */
الوظيفة العامة GetErrorMessage ()
{
إرجاع $this->ErrorMessage;
}
/**
* يتم استدعاؤه عند تحويل الكائن إلى سلسلة.
* @سلسلة العودة */
الوظيفة العامة __toString()
{
$output = "خطأ في الاستعلام في {$this->file} على السطر {$this->line}nn";
$output .= "الاستعلام: {$this->QueryText}n";
$output .= "خطأ: {$this->ErrorMessage} ({$this->ErrorNumber})nn"
;
}
}
الآن يعمل الكود الذي رأيته في بداية هذا القسم.
5. الاستنتاج
في هذه المقالة، رأيت كيف يقوم الوكيل بتعيين واجهة قاعدة البيانات المرتبطة بالاستعلام للعمليات على نتيجة استعلام محددة. تعرض كائنات DBQuery نفس الوظائف، مثل fetch_assoc()، مثل كائنات قاعدة البيانات. ومع ذلك، كل هذه تعمل لاستعلام واحد. لقد تعلمت أيضًا كيفية استخدام الاستثناءات المخصصة لتقديم معلومات تفصيلية حول متى وأين حدث خطأ، وكيف يمكنها التحكم بشكل أفضل في معالجة الأخطاء.