انجمن عمومی گسترش فناوری آگو
PHP و XML قسمت اول - نسخه‌ی قابل چاپ

+- انجمن عمومی گسترش فناوری آگو (http://forums.ago.ir)
+-- انجمن: انجمن های عمومی گسترش فناوری آگو (/forumdisplay.php?fid=1)
+--- انجمن: برنامه نویسی (/forumdisplay.php?fid=13)
+--- موضوع: PHP و XML قسمت اول (/showthread.php?tid=2197)



PHP و XML قسمت اول - agotd - 11-24-2013 01:56 PM

PHP 5 & XML اکثر افرادی که در مورد PHP 5 صحبت می کنند، بيشتر تمايل دارند به پيشرفتهايی که در زمينه کار با آبجکتها حاصل شده است تمرکز کنند. البته Object Model جديد PHP 5 يقينا يکی از مولفه های شايان توجه اين نسخه است، اما پيشرفت مهم ديگری که اغلب مورد غفلت قرار می گيرد، تغييرات عمده ای است که در PHP برای کار با اسناد XML در نظر گرفته شده است. هدف اين سلسله مقالات آشنا کردن شما با تغییرات نحوه کار با XML در PHP 5 است. در پايان اين مقالات مشاهده خواهيد کرد که تفسير اسناد XML با PHP 5 به راحتی خوردن يک ليوان آب است!

يکی از تغييراتی که در زمينه کار با XML در PHP 5 صورت گرفته است، تغيير در کتابخانه ای است که در حال حاضر برای تفسير اسناد XML استفاده می شود. در PHP 4 از کتابخانه Expat برای تفسير اسناد XML استفاده می شد. برای اطمينان از فعال بودن توسعه XML، اين کتابخانه به همراه PHP عرضه می شد؛ از این رو همواره شرط لازم و کافی برای استفاده ار کتابخانه برقرار بود. پروانه بهره برداری این کتابخانه به گونه ای بود که به PHP اجازه چنین کاری را بدهد. از طرفی انتشار نسخه های جدید این کتابخانه مدتها طول می کشید پس همواره مطمئن بودیم آخرین نسخه پایدار کتابخانه همراه PHP بود.
از آنجا که چنین امکانی مطلوب بود، منجر به بروز مشکلاتی نيز شد. نخست‌ اينکه کتابخانه های همراه، در زمان کامپايل به PHP معرفی می شدند؛ به اين علت زمانيکه می خواستيد کتابخانه را بروزرسانی کنيد می بايست PHP را دوباره کامپايل می کرديد. از اين گذشته اگر PHP به صورت ماجول استفاده می شد - برای مثال با آپاچی - این کتابخانه ممکن بود با گونه دیگری از کتابخنه Expatای که با نرم افزار دیگری نظیر سرویس دهنده آپاچی ۲ کار می کرد در تعارض باشد. البته می توان PHP را با مشخص کردن یک کتابخانه Expat خارجی از طریق گزینه with-expat-dir- کامپایل کرد، اما افراد کمی پیدا می شوند که بدین طریق عمل کنند.
مشکل ديگر Expat، کند بودن چرخه توسعه آن بود. بدين معنی که قابليت های جديد ندرتا به آن افزوده می شد و بدين ترتيب عملکردهای آن تا حدودی محدود می شد.
توسعه های ديگر XML نظير DOMXML از کتابخانه ديگری به نام Libxml2 برای تفسير اسناد XML بهره می برد. در نهایت چیزی که PHP با آن مواجه بود دو کتابخانه برای تفسیر XML بود که به سبکهای مختلفی، عملکرد يکديگر را تکرار می کردند.

Libxml2: سرآمد کتابخانه های XML
با انتشار قریب الوقوع PHP 5 به منظور يکی سازی مفسرهای XML، تمام توسعه هايی که با XML کار می کردند بر پايه کتابخانه Libxml2 بنا نهاده شدند. اين کتابخانه یک مفسر XML است که با زبان C نوشته شده و در اصل جعبه ابزاری است که برای پروژه Gnome توسعه يافت. اين کتابخانه، يک مفسر XML بسيار رايج و پراستفاده است که تقريبا روی هر توزيع NIX* امروزی به صورت پيش فرض نصب شده است. از آنجا که اين کتابخانه با جديت و پشتکار بيشتری توسعه يافته، از تمام ويژگيهای جديد و پيشرفته XML نظير Xpath، صحت سنجی DTD، پشتيبانی از HTML و... حمايت می کند. بعد از صحبتهای زيادی که در ليست توسعه PHP انجام شد، تصميم بر اين شد که اين کتابخانه را همراه PHP قرار ندهند زيرا حجم آن تقريبا به پای حجم خود بسته PHP می رسيد و به دليل انتشارات مکرر PHP، همراه سازی آن، به مشکل عديده ای منجر می شد. هر چند اين کتابخانه تقريبا روی همه سيستم ها يافت می شود(برای نسخه های Win32 فایل libxml2.dll در کنار PHP گنجانده شده است) به اين علت بروزرسانی(Upgrade) آن به سادگی صورت گرفته و نياز به کامپايل مجدد PHP نمی باشد چون اين کتابخانه بطور Dynamic لينک می شود.
تصادفا Libxml2 از شیوه ای نظیر Expat هم پشتیبانی می کند و از نظر رفتار و عملکرد تقریبا مشابه کتابخانه Expat اصلی است. این بدین معناست که در اکثر مواقع تفاوتی در رفتار توسعه XML مبتنی بر Expat در PHP 4 با توسعه متناظر آن در PHP 5 نمی باشد. سرعت کتابخانه جدید در تفسیر اسناد XML به طرز قابل توجهی بالاست بویژه وقتی درخت DOM در حافظه ساخته می شود. وقتی از SAX استفاده می کنید، در برخی موارد کتابخانه Expat اصلی سریعتر عمل می کند. به همین علت است، هنوز افرادی که تنها از SAX استفاده می کنند می توانند توسعه XML در PHP 5 را با مشخص کردن دستورالعمل پیکربندی with-libexpat-dir- کامپایل کنند.
به خاطر داشته باشید از آنجا که خود کتابخانه Expat دیگر همراه PHP نیست، اگر خواستید راه ذکر شده را طی کنید می بایست از نصب جداگانه آن در سیستم، قبل از پیکربندی و کامپایل PHP اطمینان حاصل کنید.
شيوه قديمی
قديمی ترين شيوه تفسير اسناد XML، استفاده از خانواده توابع *_xml_parser می باشد که تغییر زیادی بین PHP 4 و ۵ نکرده اند. تنها تغییرات ممکنه، به دلیل ناسازگاری اندک کتابخانه های زیربنایی تفسیر XML است. این متدولوژی یا روش تفسیر از Simple API for XML یا به اختصار SAX بهره می برد که یک واسط رویدادگراست؛ (Event-Driven) شیوه کار SAX بر اینست که ابتدا اداره کننده های رویدادی (Event-Handler) را برای آن تعریف می کنیم و سپس مفسر در برخورد با رویداد یا Eventای، اداره کننده مناسب را صدا زده و داده ها را به آن ارسال می کند. یک رویداد می تواند مواجهه با یک تگ XML، بروز خطا، برخورد با ارجاعی به یک موجودیت خارجی‌(External Entity) و یا پردازش مشخصات DTD باشد. مزیت SAX در این است که نیازی به بارگذاری تمام سند در حافظه نیست؛ این امتیاز بویژه برای فایلهای XML بزرگ مطلوب است که اگر در حافظه بارگذاری شود، حافظه گزافی را می طلبد. مزیت دیگر آن، پشتیبانی از شیوه جریانی داده هاست،(Stream) یعنی قابلیت تقسیر XML به محض ورود داده ها از یک منبع خارجی نظیر HTTP Feed را میسر می سازد به جای اینکه صبر کنیم تا تمام سند دریافت شود و بعد به مرحله تفسیر قدم بگذاریم. آز آنجا که مفسر با تمام سند کار نمی کند، بنابراین قادر به بهینه سازی تفسیر سند، که مستلزم دانستن ساختار سند است، نمی باشد. بدین معنا که برای اسناد XML کوچک که حافظه زیادی هم نمی خواهند، استفاده از DOM برای ساخت درخت DOM در حافظه به مراتب سریعتر است. برای اداره کردن رویدادهای SAX در PHP می بایست تابع یا متدی از یک کلاس، برای مدیریت آن رویداد تعیین شود. حداقل باید توابعی برای مدیریت سه رویداد (آغاز تگ، پایان تگ و داده تگ) مشخص کنید.

کد php:
$x xml_parser_create(); // initialize XML parser
// specify tag start and end handlers
xml_set_element_handler($x,'tag_start_func''tag_end_func');
// this function will handle the data inside the tag
xml_set_character_data_handler($x'tag_data_func');
xml_parse($x"xml_file.xml"); // parse document
xml_parser_free($x); // free memory 

فرآيند تفسير هيچ داده ای را به عنوان خروجی برنمی گرداند؛ کار جمع آوری داده و برگشت آن به طريقی بر عهده توابع اداره کننده رويداد نهاده شده است. اين توابع می بايست بدانند که در حال حاضر روی کدام تگ عمليات را انجام می دهند و کدام رويداد در حال پردازش می باشد و با استفاده از متغيرهای سراسری يا خصيصه های کلاس در حين تفسير سند، داده ها را ذخيره کنيم.

کد php:
function tag_start_func($x$tag$attr) {
    global 
$tmp_data;

    
$tmp_data = array($tag$attr'');    


در تکه کد بالا، تابع tag_start_func يک آرايه موقتی ايجاد می کند و نام تگ فعلی و خصيصه های(Attributes) آن را در صورت وجود در اين آرايه ذخيره می کند. همانطور که می بينيد ما سومين عنصر آرايه را هم با مقدار رشته کاراکتر پوچ مقداردهی می کنيم تا در آينده، داده تگ (Tag Data) را در آن ذخيره کنيم. اگر تگ دارای خصيصه ای باشد، اين خصايص در متغير attr$ ذخيره می شود. اين خصايص در يک آرايه قرار می گيرند و هر خصیصه، یک عنصر این آرایه را تشکیل می دهد به اين ترتيب که نام خصيصه کليد آن عنصر است. اگر هيچ خصيصه ای موجود نباشد، در اين متغير مقدار NULL قرار می گيرد.
تکه کد زير تابعی است که در هنگام برخورد مفسر با داده های محصور در تگ فراخوانی می شود. کار اين تابع بسيار ساده است و تنها هر داده ای را که مفسر به عنوان آرگومان ورودی به آن ارسال می کند را به عنصر دوم (اندیس آرایه از صفر شروع می شود) آرايه موقتی ما اضافه می کند. اگر دقت کنيد با استفاده از عملگر =. داده های جديد به داده های قبلی اضافه می شوند زيرا اگر داده های تگ بسيار بزرگ باشد يا حاوی کاراکترهای خط جديد(New Line) باشند، در چند بخش به تابع ارسال می شوند.از اين رو، استفاده از عملگر فوق به ما اطمينان می بخشد که تمام داده های تگ بازيابی می شوند بجای اينکه تنها بخش آخر داده ها را داشته باشيم.

کد php:
function tag_data_func($x$data) {
    global 
$tmp_data;
    
$tmp_data[2] .= $data;    


حال اگر به پايان تگ برخورد کنيم، مفسر تابع tag_end_func را فراخوانی می کند. در اين تابع، داده ها را داخل متغير سراسری final$ قرار می دهيم که اين متغير در انتهای فرآيند تفسير، حاوی داده های سند XML ماست. نام تگ بعنوان کليد آرايه final$ استفاده شده که هر عنصر آرايه خود به آرايه ای از مقادير اشاره دارد، زيرا يک تگ ممکن است بيش از يک بار در سند XML بکار رود.مقدار ذخيره شده برای هر تگ، شامل تمام خصيصه های تگ و مقادير هر يک است. به محض اينکه داده ها را در متغير final$ ذخيره کرديم ديگر به آرايه موقتی tmp_data$ نيازی نداريم و آن را نابود می کنيم و بدين ترتيب مقادير فعلی آن با داده های تگ بعدی در تعارض نيست.

کد php:
function tag_end_func($x$tag) {
    global 
$final$tmp_data;
    if (isset(
$final[$tag])) {
        
$final[$tag] = array();
    }
    
$final[$tag][] = array('val' => $tmp_data[1], 
            
'attr' => $tmp_data[2]);
    unset(
$tmp_data);


قسمت اول اين سلسله مقالات را مطالعه فرموديد در قسمتهای بعدی در مورد SimpleXML، XPath، DOM، XSL و ساخت سرويس دهنده و سرويس گيرنده SOAP مقالاتی خواهيد خواند و همانند مقاله امروز، تکه کدهای ساده اما کاربردی متناسب با هر موضوع را در لابلای متن مقاله خواهید یافت.
هدف بنده از ارائه اين مقالات، گردآوری مطالب نسبتا کاملی در زمينه XML و PHP 5 می باشد و به جرات می توان گفت، اين سلسله مقالات، کاملترین منبع فارسی برای دوستداران PHP خواهد بود.