تخزين البيانات واسترجاعها في PHP


لقد تعلمنا فيما سبق كيفية الوصول إلى البيانات المدخلة في HTML ومعالجتها ،  لكن في معظم الحالات نحن نحتاج إلى تخزين هذه البيانات وتحميلها لاستخدامها في وقتٍ لاحق.
تشمل الموضوعات الرئيسية التي سوف نتعلمها في هذه المحاضرة:
·         حفظ البيانات لاستخدامها في وقت لاحق.
·         فتح ملف.
·         إنشاء ملف والكتابة فيه.
·         إغلاق ملف.
·         القراءة من ملف.
·         قفل الملفات.
·         حذف الملفات.
·         استخدام التوابع  functions  المتعلقة بالملفات.
·         استخدام أنظمة إدارة قواعد البيانات.

أولاً : حفظ البيانات لاستخدامها في وقت لاحق:


يمكن تخزين البيانات بطريقتين أساسيتين: في ملفات عادية أو في قواعد بيانات.
يمكن أن يكون للملفات العديد من التنسيقات ، لكن بشكل عام عندما نتكلم عن الملفات فإننا نعني بذلك الملفات النصية البسيطة.
في الحالات التي نتعامل فيها مع حجوم كبيرة من البيانات يصبح استخدام الملفات غير مفيد لذا نلجأ إلى استخدام قواعد البيانات بدلاً منها ، ومع ذلك تبقى هناك حالات نحتاج فيها إلى الملفات لذا لا بد من معرفة كيفية استخدامها.
عمليات الكتابة إلى الملفات والقراءة منها في لغة PHP  مشابهة لباقي لغات البرمجة.

·         مثال شركة بوب لقطع السيارات:

سنقوم هنا بالتعديل على نموذج الطلب الذي كتبناه في المحاضرة السابقة ، سنضيف حقل للحصول على عنوان الشحن للزبون  Shipping Address ، على الشكل التالي:




نمط الحقل الخاص بعنوان الشحن يسمى address ، ونستطيع الوصول إلى هذا الحقل من خلال

 $_REQUEST[‘address’] أو $_POST[‘address’] أو $_GET[‘address’]

في هذه المحاضرة سنكتب كل طلب من طلبات الزبائن إلى نفس الملف ، ثم ننشئ واجهة ويب لموظفي الشركة لعرض الطلبات التي تم تلقيها.

·         معالجة الملفات:

           تتطلب كتابة البيانات إلى ملف ثلاث خطوات هي:
1.      فتح الملف وإذا كان الملف غير موجود بالفعل ، فسنحتاج إلى إنشائه.
2.      كتابة البيانات إلى الملف.
3.      إغلاق الملف.
          وبالمثل ، تتطلب قراءة البيانات من ملف ثلاث خطوات هي:
1.      فتح الملف وإذا لم نتمكن من فتح الملف (غير موجود مثلاً ) فسنحتاج إلى معرفة ذلك والخروج بأمان.
2.      قراءة البيانات من الملف.
3.      إغلاق الملف.
    

ثانياً : فتح ملف:

لفتح ملف في PHP  يمكنك استخدام التابع   fopen() ، وعند فتح الملف سنحتاج إلى تحديد طريقة استخدامه وهذا ما يعرف باسم وضع الملف file mode .

·         اختيار أوضاع الملفات:

يحتاج نظام التشغيل على المخدم إلى معرفة ما تريد القيام به عند فتح ملف ما ، كما يحتاج إلى معرفة ما إذا كان لديك سماحيات باستخدامه ، وهذا يتم من خلال ما يسمى أوضاع الملفات.
أوضاع الملفات تعطي نظام التشغيل آلية لتحديد كيفية التعامل مع طلبات الوصول إلى الملفات والتحقق من سماحيات الوصول.
عند فتح ملف يجب تحديد أحد الخيارات التالية:
1-     يجب أن نحدد ما إذا كان الملف للقراءة فقط ، أم للكتابة فقط ، أم لكليهما معاً.
2-     في حالة الكتابة إلى ملف ، يجب أن نحدد ما إذا كنا نريد الكتابة فوق المحتوى الموجود أو نريد إلحاق البيانات جديدة بنهاية الملف أو نريد إنهاء البرنامج بأمان بدلاً من الكتابة في حال كان الملف غير موجود بالفعل.
3-      إذا كنا نحاول الكتابة إلى ملف موجود على نظام يميز بين الملفات الثنائية والملفات النصية نحتاج إلى تحديد ذلك.

·         استخدام التابع fopen()  لفتح ملف :

في مثال شركت بوب لنفترض أننا نريد كتابة طلبات الزبائن إلى ملف ، عندها سنقوم بفتح هذا الملف للكتابة على الشكل التالي:
$fp = fopen(“$DOCUMENT_ROOT/../orders/orders.txt”, ‘w’);


هذا التابع يأخذ وسيطين أو ثلاثة أو أربعة ، عادةً  يمكننا استخدام اثنين فقط.

1- الوسيط الأول :

يجب أن يكون الوسيط الأول هو الملف الذي نريد فتحه ، يمكن تحديد مسار هذا الملف.
 في هذا المثال لدينا الملف orders.txt  الموجود ضمن المجلد  orders وحتى نحدد مساره يجب أن نستخدم المصفوفة  $_SERVER المبنية مسبقاً في لغة PHP على الشكل :  
$_SERVER[‘DOCUMENT_ROOT’]

لذا نحتاج إلي كتابة السطر التالي في البداية:
          $DOCUMENT_ROOT = $_SERVER[‘DOCUMENT_ROOT’];

هناك طرق أخرى أيضاً للوصول إلى جذر الملف على الشكل التالي:
$ HTTP_SERVER_VARS [OC DOCUMENT_ROOT ']

     إذا لم يتم تحديد مسار، فسيتم إنشاء الملف أو البحث عنه في المجلد الذي يحوي البرنامج النصي نفسه.

في نظام التشغيل UNIX نستخدم خطوط مائلة للأمام (/) في تحدي المسارات ، أما في نظلم التشغيل Windows يمكنك استخدام خطوط مائلة للأمام (/) أو خطوط مائلة عكسية (\)، لكن يجب الانتباه إلى أن هذه الخطوط المائلة العكسية يتم تمييزها كمحرف خاص لذا يجب الابتعاد عن استخدامها من خلال إضافة خط مائل عكسي إضافي أمام الخط المائل العكسي الأساسي على الشكل التالي:
     $fp = fopen(“$DOCUMENT_ROOT\\..\\orders\\orders.txt”, ‘w’);


لكن من الأفضل استخدام الخطوط المائلة للأمام (/).

2- الوسيط الثاني:

الوسيط الثاني للتابع fopen()  هو وضع الملف و يجب أن يكون نمط هذا الوسيط هو string وهو يحدد ما نريد القيام به عند فتح الملف.
                        نلاحظ أنه في مثالنا كتبنا   'w'  وهذا يعني أننا نريد فتح الملف للكتابة.
           وسنعرض هنا باقي الأوضاع على الشكل التالي :
-          r (Read)   : 
       وهي تعني فتح الملف للقراء ، وبادية القراءة هي من بداية الملف.
-          r+ (Read) :
       وهي تعني فتح الملف للقراءة والكتابة ، وبداية القراءة أو الكتابة تكون من بداية الملف.
-          w (Write) :
 وهي تعني فتح الملف للكتابة ، وبداية الكتابة تكون من بداية الملف ، وفي حال كان الملف موجود تتم الكتابة فوق المحتوى الموجود ، وإذا لم يكن موجود يحاول إنشاء الملف.
-          w+ (Write) :
وهي تعني فتح الملف للقراءة والكتابة ، وبداية القراءة أو الكتابة تكون من بداية الملف  وفي حال كان الملف موجود تتم الكتابة فوق المحتوى الموجود ، وإذا لم يكن موجود يحاول إنشاء الملف.
-          X (Cautious write)  الكتابة بحذر:
وهي تعني فتح الملف للكتابة ، وبداية الكتابة تكون من بداية الملف ، وفي حال كان الملف موجود لا يقوم التابع fopen()  بفتح الملف وإنما يعيد النتيجة  false وبالتالي فإن لغة PHP تولد تحذير بذلك.
-          X+ (Cautious write)  الكتابة بحذر:
وهي تعني فتح الملف للقراءة والكتابة ، وبداية القراءة أو الكتابة تكون من بداية الملف  وفي حال كان الملف موجود لا يقوم التابع fopen()  بفتح الملف وإنما يعيد النتيجة  false وبالتالي فإن لغة PHP تولد تحذير بذلك.
-          a (Append) :
وهي تعني فتح الملف لإلحاق الكتابة ، وبداية الكتابة تكون من نهاية المحتوى الموجود بالملف وإذا لم يكن الملف موجود يحاول إنشاءه.
-          a+ (Append) :
وهي تعني فتح الملف لإلحاق الكتابة و للقراءة ، وبداية الكتابة تكون من نهاية المحتوى الموجود بالملف وإذا لم يكن الملف موجود يحاول إنشاءه.
-          b (Binary) :
وهي تفيد في الاقتران مع الأوضاع الأخرى ، وتستخدم في الأنظمة التي تميز بين الملفات الثنائية والملفات النصية مثل Windows  أما أنظمة  UNIX لا تميز بين هذه الملفات  لكن من الأفضل استخدام هذا الوضع.
-          t (text) :
وهي تفيد في الاقتران مع الأوضاع الأخرى ، وتستخدم في أنظمة Windows فقط .

بالنسبة لمثال شركة بوب فإن استخدام التابع fopen() مع الوضع w  يعني أن البرنامج سيقوم بتخزين أمر واحد فقط في الملف أي طلب واحد من طلبات الزبائن وبالتالي في كل مرة سيحل الطلب الجديد محل الطلب القديم ، لذا لا بد من استخدام وضع أفضل وهو وضع الالحاق   Append  مع الوضع الثنائي Binary الموصى به ، وذلك على الشكل التالي:
$fp = fopen(“$DOCUMENT_ROOT/../orders/orders.txt”, ‘ab’);

3- الوسيط الثالث:
الوسيط الثالث للتابع fopen () اختياري ، يمكن استخدامه إذا كنا نريد البحث في include_path  (وهي موجودة في ملف إعدادات الـ PHP) ، فإذا كنا نريد ذلك نضع true في الوسيط الثالث على الشكل التالي:
 $fp = fopen(‘orders.txt’, ‘ab’, true);

4- الوسيط الرابع:
الوسيط الرابع للتابع fopen () اختياري أيضاً ، حيث يتيح هذا التابع إمكانية أن يبدأ اسم الملف ببروتوكول مثل HTTP   وإمكانية أن يفتح في مكان آخر بعيد، سنتكلم عن ذلك بشكل أوسع فيما بعد.
 

·         بالنسبة للمتغير $fp :

 فهو مؤشر إلى الملف الذي تم فتحه ، ويستخدم للوصول إلى الملف كلما أردنا القراءة من الملف أو الكتابة فيه.

·         فتح الملفات من خلال  FTP   أو   HTTP:

بالإضافة إلى فتح الملفات المحلية للقراءة والكتابة ، يمكنك فتح الملفات عبر بروتوكولات مثل  FTP , HTTP    وذلك باستخدام التابع fopen ()   يمكنك تعطيل هذه الإمكانية عن طريق إيقاف تشغيل الأمر  allow_url_fopen  في الملف php.ini .
إذا كان اسم الملف الذي تستخدمه يبدأ بـ ftp: //  فسيكون هناك اتصال FTP  مفتوح على المخدم الذي تم تحديده بهذا الاسم وسيتم إرجاع مؤشر إلى بداية الملف.
إذا كان اسم الملف الذي تستخدمه يبدأ بـ http: // ، فسيكون هناك اتصال  HTTP مفتوح على المخدم الذي تم تحديده بهذا الاسم وسيتم إرجاع مؤشر إلى بداية الملف.

في الإصدارات القديمة من PHP يجب وضع مائلة زائدة على اسم الملف على الشكل التالي:
 يجب الانتباه إلى أن أسماء النطاقات في العنوان URL ليست حساسة لحالة الأحرف ، بينما المسار واسم الملف قد يكون حساس لحالة الأحرف.

·         معالجة مشاكل فتح الملفات :

الخطأ الذي قد يحدث أثناء فتح ملف ما هو أنه ليس لدينا إذن بالقراءة منه أو الكتابة إليه ، في هذه الحالة تعيد لنا لغة PHP تحذير عن هذا الخطأ كما في الشكل التالي:




إذا تلقينا مثل هذا التحذير يجب أن نتأكد من سماحيات المجلد الذي يحوي الملف الذي نحاول فتحه ، بشكل عام سماحيات المجلدات يجب أن توضع بطريقة مدروسة خاصة المجلدات التي يمكن الوصول إليها من الويب فهذه المجلدات يجب ألا تكون قابلة للكتابة عليها من قبل أي شخص لان هذا يشكل خطورة كبيرة. سنناقش القضايا المتعلقة بالسماحيات والأمان فيما بعد.

إن الخطأ في السماحيات هو الخطأ الشائع عند فتح ملف ما لكنه ليس الخطأ الوحيد ، وفي جميع الأحوال يجب أن نعلم بهذا الخطأ لذا سوف نعالجه بطريقة أخرى نمنع فيها رسالة PHP من الظهور و نعطي بدلاً عنها رسالة خاصة بنا على الشكل التالي:
@ $fp = fopen(“$DOCUMENT_ROOT/../orders/orders.txt”, ‘ab’);
if (!$fp)
 {
    echo "<p><strong> Your order could not be processed at this time. "
     .Please try again later.</strong></p></body></html>";
    exit;
}
الرمز @ الموجود أمام استدعاء التابع  fopen() يخبر لغة PHP   بعدم إظهار أي أخطاء ناتجة عن هذا الاستدعاء لأننا سنقوم يدوياً بإظهار الرسالة الخاصة بنا (سنتعامل مع هذه المشكلة بطريقة أخرى في مكان آخر).
يمكننا أن نكتب السطر الأول في المثال السابق على الشكل التالي أيضاً:
$fp = @fopen(“$DOCUMENT_ROOT/../orders/orders.txt”, ‘a’);

لكن هذا الأسلوب هو أقل وضوحًا وغير محبذ.
بالنسبة لعبارة  if  فهي تختبر المتغير $fp  لمعرفة ما إذا كان المؤشر إلى الملف صالح أم لا (true or false) فإذا كان غير صالح (false) فإنه يطبع رسالة خطأ وينهي تنفيذ البرنامج النصي.
الناتج من تنفيذ الكود السابق سيكون على الشكل التالي:




ثالثاً : الكتابة إلى ملف:

        نستطيع الكتابة إلى ملف في لغة  PHP  بشكل بسيط  من خلال التوابع التالية :
-          fwrite()
-          fputs() وهو اسم مستعار لـ  fwrite() .
يتم استدعاءfwrite()  بالطريقة التالية:
fwrite($fp , $outputstring);

هذا التابع يخبر لغة PHP بكتابة السلسلة المخزنة في المتغير  $outputstring إلى الملف المشار إليه بالمتغير $fp .
و هناك تابع بديل لـ  fwrite() هو التابع  file_put_contents() الذي يكتب على الشكل التالي:
int file_put_contents ( string filename,
                                 string data
                                         , [int flags]
                      , [resource context])
يكتب هذا التابع السلسلة الموجودة في data  إلى الملف الذي يسمى  filename  بدون أي حاجة إلى استدعاء التوابع  fopen()  أو   fclose() ، كما يمكن استخدام الوسائط flags  و  context عند استخدام البروتوكولات  HTTP  أو   FTP .

·         وسائط التابع (fwrite(  :

           يأخذ التابع fwrite()  بالفعل ثلاثة وسائط لكن الوسيط الثالث اختياري ، ويكتب على الشكل التالي: 
int fwrite ( resource handle, string string ,[ int length])

الوسيط الثالث يعبر عن الطول والذي نعني فيه الحد الأقصى لعدد بايتات الكتابة ، فإذا تم تحديد هذا الوسيط يقوم التابع fwrite()  بكتابة السلسلة string إلى الملف المشار إليه بواسطة handle حتى يصل إلى نهاية السلسلة أو حتى يصل إلى الطول الموجود في المتغير length ، حسب أيهما يأتي أولاً.
يمكنك الحصول على طول السلسلة باستخدام التابع strlen() الموجود في لغة  PHP ، كما يلي:
fwrite($fp,$outputstring,strlen($outputstring));

نستفيد من الوسيط الثالث عندما نكتب إلى ملف بالوضع الثنائي لأنها تساعد على تجنب بعض المشكلات المتعلقة بالتوافق بين الأنظمة الأساسي.

·         تنسيق الملفات :

لا يوجد قواعد معينة تعلق بتنسيق المعلومات والبيانات ضمن الملف الذي نقوم بإنشائه فهذا يعود للمبرمج نفسه فهو الذي يقرر الشكل الذي ستكون عليه هذه المعلومات.
مثلاً : يمكننا إنشاء سلسلة تمثل سجلًا واحدًا في ملف البيانات على الشكل التالي:
$outputstring = $date.”\t”.$tireqty.” tires \t”.$oilqty.” oil\t”
.$sparkqty.” spark plugs\t\$”.$totalamount
.”\t”. $address.”\n”;
ففي هذا المثال البسيط ، نقوم بتخزين كل طلب من طلبات الزبائن في سطر منفصل في الملف ، حيث يعبر الرمز  \n  عن المحرف Enter  وبالتالي يقوم هذا الرمز بتوليد سطر جديد في الملف ، كما يعبر الرمز \t  عن المحرف Tab  وبالتالي يقوم هذا الرمز بتوليد فواصل بين العبارات.
إن النتيجة النهائية لملف الطلبات ستكون على الشكل التالي:
20:30, 31st March 2008 4 tires 1 oil 6 spark plugs $434.00 22 Short St,
Smalltown
20:42, 31st March 2008 1 tires 0 oil 0 spark plugs $100.00 33 Main Rd,
Newtown
20:43, 31st March 2008 0 tires 1 oil 4 spark plugs $26.00 127 Acacia St,
Springfield

رابعاً : إغلاق ملف:


بعد الانتهاء من استخدام الملفات يجب إغلاقها ويتم ذلك من خلال التابع fclose() والذي له الشكل التالي: 
fclose($fp);

يعيد هذا التابع قيمة منطقية true  في حال تم إغلاق الكلف بنجاح ، أو false  إذا لم يتم ذلك.

الآن سنعرض النسخة الكاملة والأخيرة من الملف processorder.php على الشكل التالي:
<?php
// create short variable names
$tireqty = $_POST['tireqty'];
$oilqty = $_POST['oilqty'];
$sparkqty = $_POST['sparkqty'];
$address = $_POST['address'];
$DOCUMENT_ROOT = $_SERVER['DOCUMENT_ROOT'];
$date = date('H:i, jS F Y');
?>
<html>
<head>
    <title>Bob's Auto Parts - Order Results</title>
</head>
<body>
   <h1>Bob's Auto Parts</h1>
  <h2>Order Results</h2>
<?php
echo "<p>Order processed at ".date('H:i, jS F Y')."</p>";
echo "<p>Your order is as follows: </p>";
$totalqty = 0;
$totalqty = $tireqty + $oilqty + $sparkqty;
echo "Items ordered: ".$totalqty."<br />";
if ($totalqty == 0)
 {
echo "You did not order anything on the previous page!<br />";
}
 else {
if ($tireqty > 0)
 {
echo $tireqty." tires<br />";
 }
if ($oilqty > 0)
 {
echo $oilqty." bottles of oil<br />";
 }
if ($sparkqty > 0)
 {
echo $sparkqty." spark plugs<br />";
 }
                 }
     $totalamount = 0.00;
    define('TIREPRICE', 100);
    define('OILPRICE', 10);
    define('SPARKPRICE', 4);
   $totalamount = $tireqty * TIREPRICE + $oilqty * OILPRICE + $sparkqty * SPARKPRICE;
   $totalamount=number_format($totalamount, 2, '.', ' ');
   echo "<p>Total of order is $".$totalamount."</p>";
   echo "<p>Address to ship to is ".$address."</p>";
   $outputstring = $date."\t".$tireqty." tires \t".$oilqty." oil\t"
   .$sparkqty." spark plugs\t\$".$totalamount
   ."\t". $address."\n";
  // open file for appending
  @ $fp = fopen("$DOCUMENT_ROOT/../orders/orders.txt", 'ab');
  flock($fp, LOCK_EX);
  if (!$fp)
   {
echo "<p><strong> Your order could not be processed at this time.
Please try again later.</strong></p></body></html>";
exit;
  }
 fwrite($fp, $outputstring, strlen($outputstring));
 flock($fp, LOCK_UN);
 fclose($fp);
 echo "<p>Order written.</p>";
?>
</body>

خامساً : القراءة من ملف:

في مثال شركة بوب يحتاج موظفو الشركة إلى قراءة الطلبات التي قام الزبائن بإرسالها من خلال صفحة الويب ، وبالتالي سيكونون بحاجة إلى فتح ملف الطلبات والقراءة منه.
لذا سنعرض الملف vieworders.php  الذي يحوي الكود الخاص بالقراءة من ملف الطلبات على الشكل التالي:
<?php
//create short variable name
$DOCUMENT_ROOT = $_SERVER['DOCUMENT_ROOT'];
?>
<html>
<head>
<title>Bob's Auto Parts - Customer Orders</title>
</head>
<body>
<h1>Bob's Auto Parts</h1>
<h2>Customer Orders</h2>
<?php
  @$fp = fopen("$DOCUMENT_ROOT/../orders/orders.txt", 'rb');
  if (!$fp)
   {
        echo "<p><strong>No orders pending.
        Please try again later.</strong></p>";
        exit;
  }
 while (!feof($fp))
  {
    $order= fgets($fp, 999);
    echo $order."<br />";
  }
?>
</body>

حيث يقوم هذا الكود بفتح ملف الطلبات والقراءة منه ومن ثم إغلاقه.
والنتيجة النهائية لتنفيذ الكود السابق تكون على الشكل التالي:




الآن سنلقي نظرة على التوابع الموجودة في الكود السابق بالتفصيل:

1-     فتح ملف للقراءة : fopen()

قلنا سابقاً أنه لفتح ملف نستخدم التابع fopen() ، وبما أننا نريد فتح الملف للقاءة نستخدم الوضع 'rb'  على الشكل التالي:
$fp = fopen(“$DOCUMENT_ROOT/../orders/orders.txt”, ‘rb’);

2-     معرفة متى نتوقف عن القراءة feof() :

في هذا المثال ، نستخدم حلقة while  للقراءة من الملف إلى أن نصل إلى نهاية الملف ، حيث نستطيع معرفة الوصول إلى نهاية الملف من خلال التابع feof()   الذي يأخذ وسيط واحد وهو مؤشر إلى الملف المفتوح ويعيد لنا قيمة منطقية true  في حال كان هذا المؤشر قد وصل إلى نهاية الملف وبالتالي فإن حلقة while ستكون على الشكل التالي:
while (!feof($fp))


3-     قراءة سطر واحد من الملف fgetss()  , fgetcsv()   fgets() ,   :

إذاً لقراءة سطر من الملف نستخدم التابع  fgets()  على الشكل التالي:
$order= fgets($fp, 999);

يقوم هذا التابع بقراءة سطر واحد في كل مرة من ملف إلى أن يصادف المحرف (\ n) أو يصادف EOF أو إلى أن يقرأ  998 بايت.
التابع fgets يكون مفيد عند التعامل مع الملفات التي تحوي على نص عادي ونريد التعامل معه كقطع.
أما في حالات أخرى يمكن استخدام التوابع الأخرى للقراءة من ملف مثل fgetss()  والذي له الشكل التالي:
string fgetss(resource fp, int length, string [allowable_tags]);

هذا التابع يشبه التابع  fgets()  باستثناء أنه يستبعد أي علامات PHP   و   HTML موجودة ضمن الملف وبالتالي إذا كنا لا نريد استبعاد هذه العلامات نضعها ضمن الوسيط الثالث  allowable_tags.
 إن استبعاد علامات PHP   و   HTML  قد يكون مفيد في بعض الأحيان كالحالات التي يكون فيها الملف مكتوب من قبل شخص آخر أو يحتوي على إدخال من قبل المستخدم ، لأن وجود مثل هذه العلامات بالملف يمكن أن يحدث فوضى بالتنسيق أو قد يسمح للمستخدم بالتحكم بالمخدم.
يوجد أيضاً التابع fgetcs () الذي له الشكل التالي:

array fgetcsv ( resource fp, int length , string [delimiter ] , string [enclosure])

حيث يقوم هذا التابع بتقسيم الملف إلى أسطر عندما يصادف علامة التبويب المحددة في delimiter   مثال على ذلك:
$order = fgetcsv($fp, 100, “\t”);

هذا الكود يقوم باسترداد سطر من الملف وإعادة تقسيمه كلما وجد علامة التبويب   (\ t) ويعيد النتيجة في مصفوفة وبالنسبة للوسيط الثاني length  يجب أن يكون أكبر من أطول سطر موجود في الملف الذي نحاول قراءته أما الوسيط الرابع enclosure فهو يحدد العلامة التي يجب أن تحيط باسم كل حقل في السطر الواحد ، وفي حال لم يتم تحديد هذا الوسيط فإن العلامة الافتراضية هي علامة الاقتباس المزدوجة.

4-     قراءة الملف بأكمله fpassthru() ,  file()   ,   readfile() :

يمكننا قراءة الملف بأكمله دفعة واحدة بدلاً من قراءة الملف سطر سطر ، وهناك أربع طرق مختلفة للقيام بذلك.
o       الطريقة الأولى:
نستخدم التابع  readfile()  الذي يقوم بفتح الملف ثم يطبع المحتوى بالكامل إلى المتصفح ومن ثم يغلق الملف وهذا التابع له الشكل التالي: 
int readfile(string filename, int [use_include_path] , resource [context] );

الوسيط الثاني اختياري وهو يحدد ما إذا كان يجب على الـ PHP أن يبحث عن الملف قبل فتحه وبالتالي يعمل بنفس الطريقة التي يعمل بها التابع fopen() ، أما الوسيط الثالث (وهو اختياري أيضاً ) يستخدم فقط في الحالات التي يتم فيها فتح الملف عن بعد كما في حال استخدام HTTP وبالنهاية يعيد لنا هذا التابع إجمالي عدد البايتات المقروءة من الملف.
باستخدام هذا التابع يمكن أن نستبدل البرنامج النصي السابق بأكمله تقريباً بالسطر التالي:
readfile(“$DOCUMENT_ROOT/../orders/orders.txt”);


o       الطريقة الثانية:
نستخدم التابع  fpassthru()  وهنا نحتاج أولاً إلى فتح الملف باستخدام التابع fopen() ثم نمرر مؤشر الملف المفتوح كوسيط للتابع fpassthru()  والذي بدوره يقوم بتفريغ محتويات الملف من موضع المؤشر إلى المتصفح ومن ثم يقوم بإغلاق الملف المفتوح وبالنهاية يعيد لنا هذا التابع نتيجة منطقية true في حال نجاح عملية القراءة و إلا فهو يعيد القيمة false .
باستخدام هذا التابع نستبدل الكود السابق بالأسطر التالية:
$fp = fopen(“$DOCUMENT_ROOT/../orders/orders.txt”, ‘rb’);
fpassthru($fp);



o       الطريقة الثالثة:
نستخدم التابع file() الذي يشبه التابع readfile()  إلا أنه بدلاً من أن يقوم بطباعة الملف إلى المتصفح ، يقوم بتخزين محتوى الملف في مصفوفة.
وتكون طريقة استخدام هذا التابع على النحو التالي:

$filearray = file($DOCUMENT_ROOT/../orders/orders.txt”);

حيث يقوم هنا التابع file() بقراءة الملف سطر سطر ، ويخزن كل سطر كعنصر منفصل في المصفوفة $filearray   أي أن عناصر هذه المصفوفة هي أسطر الملف.

o       الطريقة الرابعة:
نستخدم التابع file_get_contents()  الذي يشبه التابع readfile()  إلا أنه بدلاً من أن يقوم بطباعة الملف إلى المتصفح ، يقوم بتخزين محتوى الملف في سلسلة string .
o      

5-     قراءة الملف محرف محرف fgetc() :

هناك خيار آخر لمعالجة الملفات وهو قراءة محرف واحد في كل مرة من الملف ، ويتم ذلك من خلال التابع fgetc()  الذي يأخذ وسيط واحد وهو مؤشر إلى الملف الذي نقرأ منه ، ويعيد لنا المحرف التالي من الملف أي المحرف الذي يلي المحرف الحالي.
نستطيع كتابة البرنامج السابق باستخدام هذا التابع على الشكل التالي: 
while (!feof($fp))
  {
           $char = fgetc($fp);
           if (!feof($fp))
            echo ($char==”\n” ? "<br />": $char);
 }

حيث يقوم هنا التابع fgetc()  بقراءة الملف حرف حرف ويخزن هذه المحارف ضمن المتغير $char  إلى أن يصل إلى نهاية الملف.
ثم نقوم هنا بقليل من المعالجة على محتويات المتغير $char  لنحول محارف نهاية السطر (\n)  إلى فواصل أسطر (<br />)  ضمن لغة HTML ، وهذا فقط من أجل المحافظة على تنسيق واضح ومقروء ، ففي حال طبعنا المتغير $char  بدون هذا الاستبدال لظهر لنا الملف بأكمله على سطر واحد لأن المتصفح لا يستطيع ترجمة المحارف (\n)  على أنها أسطر جديدة.
إن قراءة الملف حرف حرف ليست معقولة أو فعالة عموماً إلا إذا كنا نستخدمها لأسباب معينة.

6-     قراءة طول ما من الملف fread() :

الطريقة الأخيرة التي نستخدمها للقراءة من ملف هي استخدام التابع  fread()  الذي له الشكل التالي: 
string fread(resource fp, int length);

حيث يقوم هذا التابع بقراءة بايتات من الملف إلى أن يصل إلى الطول length أو إلى أن يصل إلى نهاية الملف ، حسب أيهما يأتي أولاً.  

سادساً : استخدام التوابع  functions  المتعلقة بالملفات:

هناك العديد من التوابع المفيدة للتعامل مع الملفات سنقوم بذكرها الآن.

1-     التحقق من وجود ملف file_exists() :

إذا كنا تريد التحقق مما إذا كان الملف موجودًا دون فتحه، يمكننا استخدام التابع file_exists()  على الشكل التالي:
if (file_exists("$DOCUMENT_ROOT/../orders/orders.txt"))
  {
       echo 'There are orders waiting to be processed.';
  }
Else
  {
               echo 'There are currently no orders.';
 }

2-     تحديد حجم ملف : filesize()

يمكننا التحقق من حجم ملف ما باستخدام التابع  filesize()على الشكل التالي:
echo filesize(“$DOCUMENT_ROOT/../orders/orders.txt”);

حيث يعيد لنا هذا التابع حجم الملف المطلوب بالبايتات.
يمكن استخدام هذا التابع بالاقتران مع التابع  fread()  لقراءة ملف كامل (أو أجزاء من ملف) على الشكل التالي:
$fp = fopen(“$DOCUMENT_ROOT/../orders/orders.txt”, ‘rb’);
echo nl2br(fread( $fp, filesize(“$DOCUMENT_ROOT/../orders/orders.txt” )));
fclose( $fp );
حيث استخدمنا هنا التابع  nl2br()  الذي يقوم بتحويل الرمز ( \ n )  إلى الرمز (<br />) في الإخراج.

3-     حذف ملف : unlink()

يمكننا القيام بحذف ملف ما عند عدم الحاجة إليه ، فمثلاً في مثال شركة بوب يمكن حذف ملف الطلبات بعد معالجة الطلبات على الشكل التالي:
unlink(“$DOCUMENT_ROOT/../orders/orders.txt”);

حيث يعيد لنا هذا التابع قيمة منطقية false إذا تعذر حذف الملف ، وهذا يحدث عادة إذا كانت الأذونات الموجودة على الملف غير كافية أو إذا كان الملف غير موجود.

4-     التنقل داخل ملف : rewind()  ,  fseek()  ,  ftell()

تفيد هذه التوابع في معالجة واكتشاف موضع المؤشر داخل ملف ما.
التابع rewind () يعيد تعيين المؤشر إلى بداية الملف ، بينما التابع ftell() يخبرنا بمقدار بعد المؤشر عن بداية الملف بالبايتات.

مثال على ذلك : لنقم بإضافة الكود التالي إلى نهاية البرنامج الأصلي قبل استدعاء التابع fclose():
echo ‘Final position of the file pointer is ‘.(ftell($fp));
echo ‘<br />’;
rewind($fp);
echo ‘After rewind, the position is ‘.(ftell($fp));
echo ‘<br />’;

سيكون الإخراج على المتصفح على الشكل التالي:




لاحظنا أنه في أول عملية طباعة كان المؤشر عند نهاية الملف ، أما بعد استدعاء التابع rewind()  أصبح المؤشر عند بداية الملف.
أما بالنسبة للتابع fseek()  فهو يستخدم لتعيين المؤشر على نقطة ما داخل الملف ، وله الشكل التالي:
int fseek ( resource fp , int offset , int [whence] )

عند استدعاء التابع  fseek()  يتم وضع المؤشر عند نقطة البداية المحددة بالوسيط الثالث whence ويتحرك ضمن الملف بالمقدار المحدد بالوسيط الثاني offset.
الوسيط الثالث اختياري وفي حال عدم تحديده تكون القيمة الافتراضية له هي SEEK_SET  والتي تعني بداية الملف ، وهناك قيم أخرى ممكن وضعها هي  SEEK_CUR  وتعني الموقع الحالي للمؤشر ضمن الملف  و  SEEK_END  وتعني نهاية الملف .
إن استدعاء التابع  rewind()  مكافئ لاستدعاء التابع  fseek()  إلا أن الإزاحة تكون من الصفر أي من بداية الملف.

سابعاً : قفل الملفات :

في مثال شركة بوب لنتخيل موقفًا يحاول فيه زبونان طلب منتج في نفس الوقت ، ماذا سيحدث إذا قام أحد الزبائن باستدعاء fopen()  وبدأ في الكتابة ، ثم جاء زبون آخر وطلب أيضاً fopen()  وبدأ أيضاً في الكتابة ؟
ماذا ستكون المحتويات النهائية لملف الطلبات ؟ هل سيكون الطلب الأول متبوعًا بالطلب الثاني أم العكس؟

الجواب هنا يعتمد على نظام التشغيل ولكن في كثير من الأحيان من المستحيل أن نعرف الجواب.
لذا وحتى نتجنب مثل هذه المشاكلات لا بد من استخدام ما يسمى قفل الملفات ، ويتم ذلك في لغة PHP بواسطة التابع flock() الذي يجب استدعاءه بعد فتح الملف ولكن قبل قراءة أي بيانات من الملف أو الكتابة فيه ، وهذا التابع له الشكل التالي:
bool flock (resource fp , int operation , int [&wouldblock] )

حيث يكون الوسيط الأول للتابع  مؤشر إلى الملف المفتوح  ، والوسيط الثاني ثابت يمثل نوع القفل الذي نحتاجه، ويعيد لنا هذا التابع قيمة منطقية true إذا تم القفل بنجاح ، و  false فيما عدا ذلك ، أما الوسيط الثالث فهو اختياري وسوف يحوي على القيمة الحقيقية true    إذا كانت عملية الحصول على القفل ستسبب منع العملية الحالية من الإكمال وهنا يجب الانتظار. 
القيم المحتملة للوسيط الثاني هي:
LOCK_SH : قفل الملف للقراءة ، يمكن مشاركة الملف مع القراء الآخرين.
LOCK_EX : قفل الملف للكتابة ، هذه العملية حصرية ولا يمكن مشاركة الملف مع الآخرين.
LOCK_UN : تحرير القفل الموجود.
LOCK_NB : منع الحظر عند محاولة الحصول على القفل.
عند استخدام التابع flock()  يجب إضافته إلى جميع البرامج التي تستخدم الملف ، وإلا سيكون استخدامه بلا فائدة.
يجب الانتباه إلى أن  التابع flock()  لا يعمل مع نظام الملفات  NFS  ولا يعمل مع أنظمة الملفات الأقدم التي لا تدعم القفل  مثل FAT .
يمكن تغيير الملف  processorder.php  باستخدام التابع flock()  على الشكل التالي:


$fp = fopen(“$DOCUMENT_ROOT/../orders/orders.txt”, ‘ab’);
flock($fp, LOCK_EX);           // lock the file for writing
fwrite($fp, $outputstring);
flock($fp, LOCK_UN);        // release write lock
fclose($fp);

في هذه الحالة يجب أيضاً إضافة أقفال إلى البرنامج   vieworders.php على الشكل التالي:
$fp = fopen(“$DOCUMENT_ROOT /../orders/orders.txt”, ‘r’);
flock($fp, LOCK_SH);      // lock file for reading
// read from the file
flock($fp, LOCK_UN);    // release read lock
fclose($fp);

الكود الآن أصبح أكثر قوة ولكن لا يزال غير مثالي ، فماذا لو حاول برنامجان الحصول على القفل في نفس الوقت؟ في هذه الحالة من غير المؤكد من سينجح وهذا سيؤدي إلى مزيد من المشاكل ، لذا الحل في هذه الحالات هو استخدام نظام إدارة قواعد البيانات  (DBMS).

ثامناً : استخدام نظم إدارة قواعد البيانات :

·         مشاكل في استخدام الملفات :

هناك عدد من المشاكل في العمل مع الملفات :
1-     عندما يزداد حجم الملف ، قد يكون العمل معه بطيئًا جدًا.
2-     من الصعب البحث عن سجل معين أو مجموعة سجلات في ملف ما ، و إذا كانت السجلات في الملف مرتبة بترتيب معين ، يمكن استخدام نوع من البحث الثنائي بالتزامن مع البحث في حقل المفتاح.
كذلك الأمر إذا أردنا البحث عن نمط معين من المعلومات ( مثلاً نريد جميع الزبائن الذين يعيشون في مكان ما) سيكون علينا قراءة كل سجل بالملف والتحقق منه بشكل فردي.
3-     صعوبة التعامل مع الوصول المتزامن إلى ملف ما من قبل أكثر من جهة ، وقد رأينا ذلك في حالة قفل الملفات ، حتى أن قفل الملفات قد يسبب ضغط في الوصول إلى الموقع لأنه قد يكون هناك مجموعة كبيرة من المستخدمين في انتظار فتح الملف قبل أن يتمكنوا من تقديم طلباتهم وإذا كان هذا الانتظار طويل جدًا ، سيذهب الناس إلى مكان آخر للشراء.
4-     جميع عمليات معالجة الملفات التي رأيناها حتى الآن تتعامل مع الملف باستخدام معالجة متسلسلة أي علينا أن نبدأ من بداية الملف ونتابع قراءته حتى النهاية ، وبالتالي فإن إدراج السجلات في أو حذفها من منتصف الملف يمكن أن يكون صعبًا ، لأننا يجب أولاً أن ننهي قراءة الملف بالكامل إلى الذاكرة ثم نجري التغييرات المطلوبة ، ثم نكتب الملف بالكامل مرة أخرى ، فلو تخيلنا ذلك مع ملف بيانات كبير سنجد أن القيام بهذه الخطوات يصبح عبئاً كبيراً.
5-     وجود سماحيات معينة على الملفات أيضاً يؤدي إلى مشاكل تتعلق بالوصول إلى البيانات.

·         كيف تعالج أنظمة إدارة قواعد البيانات العلائقية RDBMS كل هذه المشكلات :


1-     توفر أنظمة إدارة قواعد البيانات العلائقية وصول أسرع بكثير إلى البيانات من الملفات.
2-     نستطيع من خلال أنظمة إدارة قواعد البيانات العلائقية الاستعلام بسهولة عن مجموعة من البيانات واستخراجها.
3-     يتوفر لدى أنظمة إدارة قواعد البيانات العلائقية آليات مبنية ضمنها للتعامل مع الوصول المتزامن إلى البيانات ، لذا لا داعي لأن يهتم المبرمج بهذا الشأن.
4-     توفر أنظمة إدارة قواعد البيانات العلائقية الوصول العشوائي إلى البيانات.
ربما يكون السبب الرئيسي لاستخدام RDBMS هو أن جميع أو معظم التوابع التي تحتاجها في نظام تخزين البيانات موجودة ومبنية مسبقاً ضمن هذه الأنظمة.

مروان المعلم / ماجستير بالاقتصاد المالي والنقدي
كاتب المقالة
كاتب ومحرر اخبار اعمل في موقع آفاق .

جديد قسم : استخدام لغة PHP

إرسال تعليق