بادئ ذي بدء، يجب أن أعترف أنني أحب معايير الكمبيوتر. وإذا اتبع الجميع معايير الصناعة، فإن الإنترنت ستكون وسيلة أفضل. إن استخدام تنسيقات تبادل البيانات الموحدة يجعل نماذج الحوسبة المفتوحة والمستقلة عن النظام الأساسي ممكنة. ولهذا السبب أنا من عشاق XML.
لحسن الحظ، لغة البرمجة النصية المفضلة لدي لا تدعم لغة XML فحسب، بل تدعمها بشكل متزايد. يتيح لي PHP نشر مستندات XML بسرعة على الإنترنت، وجمع معلومات إحصائية حول مستندات XML، وتحويل مستندات XML إلى تنسيقات أخرى. على سبيل المثال، غالبًا ما أستخدم إمكانات معالجة XML الخاصة بـ PHP لإدارة المقالات والكتب التي أكتبها بتنسيق XML.
في هذه المقالة، سأناقش أي استخدام لمحلل Expat المدمج في PHP لمعالجة مستندات XML. ومن خلال الأمثلة سأوضح طريقة تجهيز المغترب. وفي الوقت نفسه، يمكن أن يوضح لك المثال كيفية:
إنشاء وظيفة المعالجة الخاصة بك
تحويل مستندات XML إلى هياكل بيانات PHP الخاصة بك
مقدمة يسمح محلل Expat
XML، والذي يُسمى أيضًا معالج XML، للبرامج بالوصول إلى بنية ومحتوى مستندات XML. Expat هو محلل XML للغة البرمجة النصية PHP. كما يتم استخدامه في مشاريع أخرى، مثل Mozilla وApache وPerl.
ما هو المحلل اللغوي القائم على الحدث؟
هناك نوعان أساسيان من موزعي XML:
الموزعون المعتمدون على الشجرة: يقومون بتحويل مستندات XML إلى هياكل شجرة. يقوم هذا النوع من المحلل اللغوي بتحليل المقالة بأكملها مع توفير واجهة برمجة التطبيقات (API) للوصول إلى كل عنصر من عناصر الشجرة الناتجة. معيارها المشترك هو DOM (نموذج كائن المستند).
المحلل اللغوي المعتمد على الأحداث: تعامل مع مستندات XML كسلسلة من الأحداث. عند حدوث حدث خاص، سيقوم المحلل باستدعاء الوظيفة التي يوفرها المطور للتعامل معها.
يحتوي المحلل اللغوي المعتمد على الحدث على عرض يركز على البيانات لمستند XML، مما يعني أنه يركز على جزء البيانات في مستند XML بدلاً من بنيته. يقوم هؤلاء المحللون بمعالجة المستند من البداية إلى النهاية والإبلاغ عن أحداث مثل - بداية العنصر، نهاية العنصر، بداية بيانات الميزة، وما إلى ذلك - إلى التطبيق من خلال وظائف رد الاتصال. فيما يلي مثال لوثيقة XML لـ "Hello-World":
<greeting>
مرحبا بالعالم
</greeting>
سيقوم المحلل اللغوي المعتمد على الأحداث بالإبلاغ عن ثلاثة أحداث:
عنصر البداية: الترحيب
بداية عنصر CDATA، القيمة هي: Hello World
عنصر النهاية: التحية
على عكس الموزعين المعتمدين على الشجرة، لا يقوم الموزعون المعتمدون على الأحداث بإنتاج بنية تصف المستند. في عناصر CDATA، لن يسمح لك المحلل اللغوي المعتمد على الحدث بالحصول على معلومات الترحيب الخاصة بالعنصر الأصلي.
ومع ذلك، فهو يوفر وصولاً بمستوى أقل، مما يسمح باستخدام الموارد بشكل أفضل ووصول أسرع. بهذه الطريقة، ليست هناك حاجة لملاءمة المستند بأكمله في الذاكرة؛ في الواقع، يمكن أن يكون المستند بأكمله أكبر من قيمة الذاكرة الفعلية.
المغترب هو مثل هذا المحلل اللغوي القائم على الحدث. بالطبع، إذا كنت تستخدم Expat، فيمكنه أيضًا إنشاء بنية شجرة أصلية كاملة في PHP إذا لزم الأمر.
يتضمن مثال Hello-World أعلاه تنسيق XML الكامل. ولكنه غير صالح لأنه لا يوجد DTD (تعريف نوع المستند) مرتبط به ولا DTD مضمن.
بالنسبة للمغتربين، لا يشكل هذا أي فرق: Expat هو محلل لا يتحقق من الصلاحية وبالتالي يتجاهل أي DTD مرتبط بالمستند. ومع ذلك، تجدر الإشارة إلى أن المستند لا يزال بحاجة إلى التنسيق بالكامل، وإلا فسيتوقف Expat (مثل المحللين الآخرين المتوافقين مع XML) مع ظهور رسالة خطأ.
باعتباره محللًا لا يتحقق من الصلاحية، فإن سرعة Exapt وخفة الوزن تجعله مناسبًا تمامًا لتطبيقات الإنترنت.
تجميع المغتربين
يمكن تجميعه في إصدار PHP3.0.6 (أو أعلى). بدءًا من Apache 1.3.9، تم تضمين Expat كجزء من Apache. في أنظمة Unix، يمكنك تجميعها إلى PHP عن طريق تكوين PHP باستخدام خيار -with-xml.
إذا قمت بترجمة PHP كوحدة Apache، فسيتم تضمين Expat كجزء من Apache افتراضيًا. في نظام التشغيل Windows، يجب عليك تحميل مكتبة الارتباط الديناميكي XML.
أمثلة XML: XMLstats
إحدى الطرق للتعرف على وظائف Expat هي من خلال الأمثلة. المثال الذي سنناقشه هو استخدام Expat لجمع الإحصائيات حول مستندات XML.
لكل عنصر في الوثيقة، سيتم إخراج المعلومات التالية:
عدد مرات استخدام العنصر في الوثيقة
مقدار بيانات الشخصية في هذا العنصر
العنصر الأصل للعنصر
العناصر الفرعية للعنصر
ملاحظة: من أجل التوضيح، نستخدم PHP لإنشاء بنية لحفظ العنصر الأصلي والعنصر الفرعي للعنصر.
المعدة
لإنشاء مثيل محلل XML هي xml_parser_create(). سيتم استخدام هذا المثيل لجميع الوظائف المستقبلية. تشبه هذه الفكرة إلى حد كبير علامة الاتصال الخاصة بوظيفة MySQL في PHP. قبل تحليل المستند، عادةً ما يطلب منك المحللون المعتمدون على الأحداث تسجيل وظيفة رد الاتصال - ليتم استدعاؤها عند وقوع حدث معين. ليس لدى Expat أحداث استثناء، فهو يحدد الأحداث السبعة المحتملة التالية:
عنصروصف وظيفة تحليل XML
xml_set_element_handler() بيانات حرف البداية والنهاية
للعنصر xml_set_character_data_handler() بداية بيانات الحرف كيان
خارجي xml_set_external_entity_ref_handler() كيان خارجي
كيان خارجي غير محلل xml_set_unparsed_entity_decl_handler ( ) حدوث تعليمات معالجة كيان خارجي لم يتم حلها
xml_set_processing_instruction_handler() حدوثإعلان تدوين
تعليمات المعالجة
xml_set_notation_decl_handler() حدوث إعلان التدوينالافتراضي xml_set_default_handler() أحداث أخرى بدون وظيفة معالج محددة
يجب على جميع وظائف رد الاتصال استخدام مثيل المحلل اللغوي هو المعلمة الأولى (هناك معلمات أخرى بالإضافة إلى ذلك).
للحصول على نموذج البرنامج النصي في نهاية هذه المقالة. ما تحتاج إلى ملاحظته هو أنه يستخدم كلاً من وظائف معالجة العناصر ووظائف معالجة بيانات الأحرف. يتم تسجيل وظيفة معالج رد الاتصال للعنصر من خلال xml_set_element_handler().
تأخذ هذه الوظيفة ثلاث معلمات:
مثيل المحلل اللغوي
اسم وظيفة رد الاتصال التي تعالج عنصر البداية
اسم وظيفة رد الاتصال التي تعالج عنصر الإغلاق
يجب أن تكون وظيفة رد الاتصال موجودة عند بدء تحليل مستند XML. يجب أن يتم تعريفها بما يتوافق مع النماذج الأولية الموضحة في دليل PHP.
على سبيل المثال، يقوم Expat بتمرير ثلاث وسيطات إلى وظيفة المعالج لعنصر البداية. في مثال البرنامج النصي، يتم تعريفه على النحو التالي:
function start_element($parser, $name, $attrs)
المعلمة الأولى هي معرف المحلل اللغوي، والمعلمة الثانية هي اسم عنصر البداية، والمعلمة الثالثة تحتوي على كافة السمات و قيم مصفوفة العناصر.
بمجرد البدء في تحليل مستند XML، سيقوم Expat باستدعاء الدالة start_element() الخاصة بك وتمرير المعلمات عندما يواجه عنصر البداية.
يستخدمخيار Case Folding في XML
وظيفة xml_parser_set_option () لإيقاف تشغيل خيار Case Folding. يتم تشغيل هذا الخيار افتراضيًا، مما يؤدي إلى تحويل أسماء العناصر التي تم تمريرها إلى وظائف المعالج تلقائيًا إلى أحرف كبيرة. لكن XML حساس لحالة الأحرف (لذا فإن حالة الأحرف مهمة جدًا لمستندات XML الإحصائية). على سبيل المثال، يجب إيقاف تشغيل خيار طي العلبة.
تحليل المستند
بعد الانتهاء من جميع الاستعدادات، أصبح بإمكان البرنامج النصي الآن تحليل مستند XML أخيرًا:
Xml_parse_from_file()، وهي وظيفة مخصصة، تفتح الملف المحدد في المعلمة وتحلله بحجم 4 كيلو بايت
ستُرجع الدالة xml_parse()، مثل xml_parse_from_file()، خطأ عند حدوث خطأ، أي عندما لا يتم تنسيق مستند XML بالكامل.
يمكنك استخدام الدالة xml_get_error_code() للحصول على الرمز الرقمي للخطأ الأخير. قم بتمرير هذا الرمز الرقمي إلى الدالة xml_error_string() للحصول على معلومات نص الخطأ.
يقوم بإخراج رقم السطر الحالي لملف XML، مما يجعل تصحيح الأخطاء أسهل.
أثناء عملية التحليل، يتم استدعاء وظيفة رد الاتصال.
وصف بنية المستند
عند تحليل مستند، فإن السؤال الذي يجب معالجته مع Expat هو: كيفية الحفاظ على الوصف الأساسي لبنية المستند؟
كما ذكرنا سابقًا، فإن المحلل اللغوي المبني على الحدث نفسه لا ينتج أي معلومات هيكلية.
ومع ذلك، فإن بنية العلامة هي سمة مهمة لـ XML. على سبيل المثال، تسلسل العناصر <book><title> يعني شيئًا مختلفًا عن <الشكل><العنوان>. ومع ذلك، سيخبرك أي مؤلف أن عناوين الكتب وعناوين الصور لا علاقة لها ببعضها البعض، على الرغم من أن كلاهما يستخدم مصطلح "العنوان". لذلك، من أجل معالجة XML بكفاءة باستخدام محلل مستند إلى الأحداث، يجب عليك استخدام مجموعاتك أو قوائمك الخاصة للحفاظ على المعلومات الهيكلية حول المستند.
من أجل عكس بنية الوثيقة، يحتاج البرنامج النصي إلى معرفة العنصر الأصلي للعنصر الحالي على الأقل. هذا غير ممكن مع واجهة برمجة التطبيقات الخاصة بـ Exapt، فهي تقوم فقط بالإبلاغ عن أحداث العنصر الحالي دون أي معلومات سياقية. لذلك، تحتاج إلى بناء هيكل المكدس الخاص بك.
يستخدم مثال البرنامج النصي بنية مكدس أول ما يدخل أخيرًا (FILO). من خلال المصفوفة، ستحفظ المكدس جميع عناصر البداية. بالنسبة لوظيفة معالجة عنصر البداية، سيتم دفع العنصر الحالي إلى أعلى المكدس بواسطة الدالة array_push(). في المقابل، تقوم وظيفة معالجة العنصر النهائي بإزالة العنصر العلوي من خلال array_pop().
بالنسبة للتسلسل <book><title></title></book>، تتم تعبئة المكدس على النحو التالي:
كتاب عنصر البداية: قم بتعيين "كتاب" للعنصر الأول من المكدس ($stack[0]).
عنوان عنصر البداية: قم بتعيين "عنوان" لأعلى المكدس ($stack[1]).
عنوان عنصر النهاية: قم بإزالة العنصر العلوي من المكدس ($stack[1]).
عنوان عنصر النهاية: قم بإزالة العنصر العلوي من المكدس ($stack[0]).
ينفذ PHP3.0 المثال عن طريق التحكم يدويًا في تداخل العناصر من خلال متغير العمق $. وهذا يجعل البرنامج النصي يبدو أكثر تعقيدًا. يستخدم PHP4.0 الدالتين array_pop() و array_push() لجعل البرنامج النصي يبدو أكثر إيجازًا.
جمع البيانات
من أجل جمع معلومات حول كل عنصر، يحتاج البرنامج النصي إلى تذكر الأحداث الخاصة بكل عنصر. احفظ جميع العناصر المختلفة في المستند باستخدام عناصر $ المتغيرة للصفيف العام. عناصر المصفوفة هي مثيلات لفئة العنصر ولها 4 خصائص (متغيرات الفئة)
$count - عدد مرات العثور على العنصر في المستند
$chars - عدد بايتات أحداث الأحرف في العنصر
$parents - العنصر الأصلي
$ تشايلدز - عناصر فرعية
كما ترون، فإن حفظ مثيلات الفصل في مصفوفة يعد أمرًا سهلاً.
ملحوظة: إحدى ميزات PHP هي أنه يمكنك اجتياز بنية الفصل بالكامل من خلال حلقة while(list() = every())، تمامًا مثلما تقوم باجتياز المصفوفة المقابلة بأكملها. يتم إخراج كافة متغيرات الفئة (وأسماء الطرق عند استخدام PHP3.0) كسلاسل.
عندما يتم العثور على عنصر ما، نحتاج إلى زيادة العداد المقابل له لتتبع عدد مرات ظهوره في المستند. تتم أيضًا زيادة عنصر العدد في عنصر $elements المقابل بمقدار واحد.
نحتاج أيضًا إلى السماح للعنصر الأصلي بمعرفة أن العنصر الحالي هو العنصر الفرعي الخاص به. ولذلك، سيتم إضافة اسم العنصر الحالي إلى العنصر الموجود في المصفوفة $childs الخاصة بالعنصر الأصلي. وأخيرًا، يجب أن يتذكر العنصر الحالي من هو والده. لذلك، تتم إضافة العنصر الأصلي إلى العنصر الموجود في المصفوفة $parents الخاصة بالعنصر الحالي.
عرض الإحصائيات
يتم تكرار التعليمات البرمجية المتبقية عبر مصفوفة $elements ومصفوفاتها الفرعية لعرض إحصائياتها. هذه هي أبسط حلقة متداخلة، على الرغم من أنها تنتج النتائج الصحيحة، إلا أن الكود ليس موجزًا ولا يحتوي على أي مهارات خاصة، فهو مجرد حلقة يمكنك استخدامها كل يوم لإكمال عملك.
تم تصميم أمثلة البرنامج النصي ليتم استدعاؤها من سطر الأوامر عبر نهج CGI الخاص بـ PHP. ولذلك، فإن تنسيق إخراج النتيجة الإحصائية هو تنسيق النص. إذا كنت تريد استخدام البرنامج النصي على الإنترنت، فأنت بحاجة إلى تعديل وظيفة الإخراج لإنشاء تنسيق HTML.
ملخص
Exapt هو محلل XML لـ PHP. وباعتباره محللًا يعتمد على الحدث، فإنه لا ينتج وصفًا هيكليًا للمستند. ولكن من خلال توفير وصول منخفض المستوى، يسمح ذلك باستخدام الموارد بشكل أفضل ووصول أسرع.
كمحلل لا يتحقق من الصلاحية، يتجاهل Expat DTDs المرفقة بمستندات XML، لكنه سيتوقف برسالة خطأ إذا لم تكن الوثيقة منسقة بشكل جيد.
توفير معالجات الأحداث لمعالجة المستندات
قم ببناء هياكل الأحداث الخاصة بك مثل الأكوام والأشجار للاستفادة من ترميز المعلومات المنظمة بتنسيق XML.
تظهر برامج XML جديدة كل يوم، ويتم تعزيز دعم PHP لـ XML باستمرار (على سبيل المثال، تمت إضافة دعم LibXML لمحلل XML المعتمد على DOM).
باستخدام PHP وExpat، يمكنك الاستعداد للمعايير القادمة الصالحة والمفتوحة والمستقلة عن النظام الأساسي.
مثال
<؟
/****************************************************** ***** *****************************
* الاسم: مثال تحليل XML: إحصائيات معلومات مستند XML
* يصف
* يستخدم هذا المثال المحلل اللغوي PHP's Expat لجمع وإحصاء معلومات مستند XML (على سبيل المثال: عدد مرات ظهور كل عنصر، والعناصر الأصلية، والعناصر الفرعية
* ملف XML كمعلمة./xmlstats_PHP4.php3 test.xml
* يتطلب $: متطلبات المغتربين: يتم تجميع Expat PHP4.0 في وضع CGI
*************************************************************************************************************************************************************************** * **************************/
// المعلمة الأولى هي ملف XML
$file = $argv[1];
// تهيئة المتغيرات
عناصر $ = $stack = array();
$total_elements = $total_chars = 0;
// الفئة الأساسية للعناصر
عنصر الطبقة
{
فار $count = 0;
فار $chars = 0;
فار $parents = array();
var $childs = array();
}
// وظيفة تحليل ملفات XML
الدالة xml_parse_from_file(محلل $، ملف $)
{
إذا (! file_exists($file))
{
die("لا يمكن العثور على الملف "$file".");
}
إذا(!($fp = @fopen($file, "r")))
{
die("لا يمكن فتح الملف "$file".");
}
بينما($data = fread($fp, 4096))
{
إذا (!xml_parse($محلل، $data، feof($fp)))
{
عودة (خطأ)؛
}
}
f Close($fp)
;
}
// وظيفة نتيجة الإخراج (نموذج مربع)
وظيفة print_box(عنوان $، قيمة $)
{
printf("n+%'-60s+n", "");
printf("|%20s", "$title:");
printf("%14s", $value);
printf("%26s|n", "");
printf("+%'-60s+n", "");
}
// وظيفة نتيجة الإخراج (نموذج الخط)
وظيفة print_line(عنوان $، قيمة $)
{
printf("%20s", "$title:");
printf("%15sn", $value);
}
// وظيفة الفرز
الدالة my_sort($a, $b)
{
return(is_object($a) && is_object($b) ? $b->count - $a->count: 0);
}
الدالة start_element($parser, $name, $attrs)
{
عناصر $ العالمية، $stack؛
// هل العنصر موجود بالفعل في مصفوفة $elements العالمية؟
إذا (! isset($elements[$name]))
{
// لا - أضف مثيل فئة للعنصر
عنصر $ = عنصر جديد؛
$elements[$name] = $element;
}
// زيادة عداد هذا العنصر بمقدار واحد
$elements[$name]->count++;
// هل يوجد عنصر أصل؟
إذا (isset($stack[count($stack)-1]))
{
// نعم - قم بتعيين العنصر الأصلي إلى $last_element
$last_element = $stack[count($stack)-1];
// إذا كانت مصفوفة العناصر الأصلية للعنصر الحالي فارغة، فقم بتهيئتها إلى 0
إذا(!isset($elements[$name]->parents[$last_element]))
{
$elements[$name]->parents[$last_element] = 0;
}
// قم بزيادة عداد العنصر الأصلي لهذا العنصر بمقدار واحد
$elements[$name]->parents[$last_element]++;
// إذا كانت مصفوفة العناصر الفرعية للعنصر الأصلي للعنصر الحالي فارغة، فستتم تهيئتها إلى 0
if(!isset($elements[$last_element]-> الطفل [اسم $]))
{
$elements[$last_element]->childs[$name] = 0;
}
// أضف واحدًا إلى عداد العناصر الفرعية للعنصر الأصلي للعنصر.
$elements[$last_element]->childs[$name]++;
}
// أضف العنصر الحالي إلى المكدس
array_push($stack, $name);
}
وظيفة stop_element($محلل، $name)
{
global $stack;
// إزالة العنصر العلوي من المكدس
array_pop($stack);
}
وظيفة char_data($محلل، $data)
{
عناصر $ العالمية، $stack، $ Deep؛
// زيادة عدد أحرف العنصر الحالي
$elements[$stack][count($stack)-1]]->chars += strlen(trim($data));
}
// إنشاء مثيل محلل
$parser = xml_parser_create();
// تعيين وظيفة المعالجة
xml_set_element_handler($parser, "start_element", "stop_element");
xml_set_character_data_handler($parser, "char_data");
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
// تحليل الملف
$ret = xml_parse_from_file($parser, $file);
إذا (!$ret)
{
يموت (sprintf ("خطأ XML: %s في السطر %d"،
xml_error_string(xml_get_error_code($parser)))،
xml_get_current_line_number($parser)));
}
// حرر المحلل اللغوي
xml_parser_free($parser);
// حرر العنصر المساعد
unset($elements["current_element"]);
unset($elements["last_element"]);
// فرز حسب عدد العناصر
uasort($elements, "my_sort");
// قم بالتكرار عبر العناصر $ لجمع معلومات العنصر
بينما (قائمة (اسم $، عنصر $) = كل (عناصر $))
{
print_box("اسم العنصر"، $name
print_line("عدد العناصر"، $element->count);
print_line("عدد الأحرف", $element->chars);
printf("n%20sn", "* العناصر الأصلية")
;
بينما (قائمة(مفتاح $، قيمة $) = كل($element->parents))
{
print_line(مفتاح $, قيمة $);
}
إذا (العدد($element->parents) == 0)
{
printf("%35sn", "[العنصر الجذري]");
}
// قم بالمرور عبر فرع هذا العنصر وأخرج النتيجة
printf("n%20sn", "* العناصر الفرعية");
بينما (قائمة(مفتاح $، قيمة $) = كل($element->childs))
{
print_line(مفتاح $, قيمة $);
}
إذا (العدد($element->childs) == 0)
{
printf("%35sn", "[لا يوجد أطفال]");
}
$total_elements += $element->count;
$total_chars += $element->chars;
}
// النتيجة النهائية
print_box("إجمالي العناصر"، $total_elements);
print_box("إجمالي الأحرف"، $total_chars);
?>