دوال الملاءمة (Convenience functions)
الخرج والدخل الخام المباشر
توفر الدالتان file_get_contents
و file_put_contents
قابلية القراءة/الكتابة إلى/من ملف من/إلى سلسلة نصية في PHP في استدعاء واحد.
يمكن أن تستخدم الدالة file_put_contents
أيضًا مع راية القناع البتي FILE_APPEND
للإضافة إلى الملف بدلًا من الكتابة فوقه، ويمكن استخدامها مع القناع البتي LOCK_EX
للحصول على قفل حصري للملف أثناء إجراء الكتابة. يمكن دمج رايات القناع البتي باستخدام عامل العملية الثنائية OR
|
.
$path = "file.txt"; // قراءة محتويات الملف file.txt إلى $contents $contents = file_get_contents($path); // إذا غيّرنا شيئًا ما مثلًا `CRLF` إلى `LF` $contents = str_replace("\r\n", "\n", $contents); // نكتب هذا التغيير في file.txt مستبدلين المحتويات الأصلية file_put_contents($path, $contents);
تفيد الراية FILE_APPEND
في الإضافة إلى ملفات السجلات، بينما تساعد الراية LOCK_EX
في منع حالة سباق عدة عمليات للكتابة في الملف، فمثلًا لكتابة حالة الجلسة الحالية في ملف سجل:
file_put_contents("logins.log", "{$_SESSION["username"]} logged in", FILE_APPEND | LOCK_EX);
الخرج والدخل بصيغة CSV
fgetcsv($file, $length, $separator)
تحلل الدالة fgetcsv
سطرًا من الملف المفتوح وتبحث عن حقول csv
، ثم تعيد هذه الحقول في مصفوفة في حالة النجاح وإلا تعيد FALSE
. تقرأ بشكلٍ افتراضي سطرًا واحدًا فقط من ملف csv
.
$file = fopen("contacts.csv","r"); print_r(fgetcsv($file)); print_r(fgetcsv($file,5," ")); fclose($file);
محتويات ملف contacts.csv
:
Kai Jim, Refsnes, Stavanger, Norway Hege, Refsnes, Stavanger, Norway
الخرج:
Array ( [0] => Kai Jim [1] => Refsnes [2] => Stavanger [3] => Norway ) Array ( [0] => Hege, )
قراءة ملف إلى مجرى الخرج القياسي مباشرةً
تنسخ الدالة readfile
ملفًا إلى المخزن المؤقت للخرج، ولا تؤدي إلى ظهور أيّة مشاكل في الذاكرة حتى عند إرسال ملفات كبيرة من تلقاء نفسها.
$file = 'monkey.gif'; if (file_exists($file)) { header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="'.basename($file).'"'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($file)); readfile($file); exit; }
القراءة بدءًا من مكان مؤشر في ملف
نستخدم الدالة fpassthru
بدلًا من ذلك للبحث عن نقطة في الملف للبدء بالنسخ إلى مجرى الخرج القياسي، في المثال التالي يُنسخ آخر 1024 بايت إلى الخرج القياسي:
$fh = fopen("file.txt", "rb"); fseek($fh, -1024, SEEK_END); fpassthru($fh);
قراءة ملف إلى مصفوفة
تعيد الدالة file
أسطر الملف الممرَّر في مصفوفة، يقابل كل عنصر من المصفوفة سطرًا من الملف مع استمرار إضافة السطر الجديد.
print_r(file("test.txt"));
محتويات الملف test.txt
:
Welcome to File handling This is to test file handling
الخرج:
Array ( [0] => Welcome to File handling [1] => This is to test file handling )
حذف ملفات ومجلدات
حذف ملفات
تحذف الدالة unlink
ملفًا واحدًا وتعيد القيمة TRUE
عند نجاح العملية وFALSE
عند فشلها.
$filename = '/path/to/file.txt'; if (file_exists($filename)) { $success = unlink($filename); if (!$success) { throw new Exception("Cannot delete $filename"); } }
حذف مجلدات مع حذف عودي (recursive deletion)
يمكن حذف المجلدات باستخدام الدالة rmdir
إلا أنّ هذه الدالة تحذف المجلدات الفارغة فقط، ولحذف مجلد يحتوي ملفات يجب حذف هذه الملفات أولًا، إذا احتوى المجلد على مجلدات فرعية نحتاج إلى استخدام العودية.
يفحص المثال التالي الملفات في مجلد ويحذف ما بداخله من ملفات ومجلدات بشكلٍ تعاودي ويعيد عدد الملفات (وليس المجلدات) المحذوفة.
function recurse_delete_dir(string $dir) : int { $count = 0; // التأكد من أنّ $dir ينتهي بخط مائل لنتمكن من ربطه مع أسماء الملفات مباشرةً $dir = rtrim($dir, "/\\") . "/"; // استخدام dir() للحصول على قائمة الملفات $list = dir($dir); // تخزين اسم الملف التالي في $file، إذا كانت قيمة $file هي false أي لا توجد ملفات بعد عندها تُنهى الحلقة while(($file = $list->read()) !== false) { if($file === "." || $file === "..") continue; if(is_file($dir . $file)) { unlink($dir . $file); $count++; } elseif(is_dir($dir . $file)) { $count += recurse_delete_dir($dir . $file); } } // يمكننا الآن حذف المجلد rmdir($dir); return $count; }
الحصول على معلومات ملف
التحقق من كون المسار ملف أو مجلد
تتحقق الدالة is_dir
إن كان الوسيط الممرر إليها مجلدًا أم لا، فيما تتحقق الدالة is_file
إن كان الوسيط الممرر إليها ملفًا أم لا، ويمكن استخدام الدالة file_exists
للتحقق من وجود ملف أو مجلد محدد.
$dir = "/this/is/a/directory"; $file = "/this/is/a/file.txt"; echo is_dir($dir) ? "$dir is a directory" : "$dir is not a directory", PHP_EOL, is_file($dir) ? "$dir is a file" : "$dir is not a file", PHP_EOL, file_exists($dir) ? "$dir exists" : "$dir doesn't exist", PHP_EOL, is_dir($file) ? "$file is a directory" : "$file is not a directory", PHP_EOL, is_file($file) ? "$file is a file" : "$file is not a file", PHP_EOL, file_exists($file) ? "$file exists" : "$file doesn't exist", PHP_EOL;
الخرج:
/this/is/a/directory is a directory /this/is/a/directory is not a file /this/is/a/directory exists /this/is/a/file.txt is not a directory /this/is/a/file.txt is a file /this/is/a/file.txt exists
التحقق من نوع ملف
نستخدم الدالة filetype
لمعرفة نوع ملف محدد والذي قد يكون إمَّا أنبوبة مسماة FIFO
أو ملف محرفي خاص char
أو مجلد dir
أو ملف كتلي خاص block
أو وصلة رمزية link
أو ملف عادي file
أو مقبس socket
أو نوع غير معروف unknown
.
تمرير اسم الملف إلى الدالة filetype
مباشرةً:
echo filetype("~"); // dir
لاحظ أنّه إذا لم يكن الملف موجودًا فإنّ الدالة تعيد false
وتشغّل E_WARNING
.
التحقق من قابلية القراءة والكتابة
نمرر اسم الملف للدالتين is_writable
وis_readable
للتحقق من كون الملف قابلًا للكتابة أو القراءة.
تعيد الدالتان القيمة false
إذا لم يكن الملف موجودًا.
التحقق من وقت الوصول إلى ملف وتعديله
تعيد الدالتان filemtime
وfileatime
الختم الزمني لآخر تعديل أو وصول للملف، ويكون الختم الزمني المعاد بصيغة Unix
.
echo "File was last modified on " . date("Y-m-d", filemtime("file.txt")); echo "File was last accessed on " . date("Y-m-d", fileatime("file.txt"));
الحصول على أجزاء المسار مع fileinfo
$fileToAnalyze = ('/var/www/image.png'); $filePathParts = pathinfo($fileToAnalyze); echo '<pre>'; print_r($filePathParts); echo '</pre>';
خرج المثال السابق:
Array ( [dirname] => /var/www [basename] => image.png [extension] => png [filename] => image )
يمكن استخدام هذا الخرج كالتالي:
$filePathParts['dirname'] $filePathParts['basename'] $filePathParts['extension'] $filePathParts['filename']
المعامل | تفاصيل |
$path | المسار الكامل للملف الذي نريد تحليله |
$option | أحد الخيارات التالية: PATHINFO_DIRNAME أو PATHINFO_BASENAME أو PATHINFO_EXTENSION أو PATHINFO_FILENAME |
- تُعاد مصفوفة ترابطية (associative array) إذا لم يُمرَّر المعامل الثاني وإلا تُعاد سلسلة نصية.
- لا تتحقق من وجود الملف
- تُحلَّل السلسلة النصية ببساطة إلى أجزاء ولا يُجرى أي تحقق على الملف (مثل التحقق من نوع الترويسة وغير ذلك)
-
الامتداد هو الامتداد الأخير للمتغير
$path
، سيكون مسار الملفimage.jpg.png
هو.png
حتى لو كان هو ملف.jpg
تقنيًا، وإذا كان الملف بدون امتداد لن يُعاد عنصر الامتداد في المصفوفة.
خرج ودخل ملف معتمد على المجرى
فتح مجرى
تفتح الدالة fopen
ملفًا وتخصص له مقبضًا يشبه مقبض الباب يمكن أن تستخدمه دوالًا أخرى للقراءة والكتابة والبحث وغيرها، هذه القيمة من النوع مورد ولا يمكن تمريرها إلى النياسب الأخرى المستمرة في وظيفتها.
$f = fopen("errors.log", "a");
ستحاول الشيفرة السابقة فتح الملف errors.log
للكتابة.
المعامل الثاني هو نمط مجرى الملف:
النمط | الوصف |
r | يفتح الملف للقراءة فقط بدءًا من أول الملف |
r+ | يفتح الملف للقراءة والكتابة بدءًا من أول الملف |
w | يفتح الملف للكتابة فقط بدءًا من أول الملف، إذا كان الملف موجودًا سيفرغه وإذا لم يكن موجودًا سيحاول أن ينشئه |
w+ | يفتح الملف للقراءة والكتابه بدءًا من أول الملف، إذا كان الملف موجودًا سيفرغه وإذا لم يكن موجودًا سيحاول أن ينشئه |
a | يفتح الملف للكتابة فقط بدءًا من نهاية الملف، إذا لم يكن الملف موجودًا سيحاول أن ينشئه |
a+ | يفتح الملف للقراءة والكتابة بدءًا من نهاية الملف، إذا لم يكن الملف موجودًا سيحاول أن ينشئه |
x | ينشئ ويفتح ملف للكتابة فقط، وإذا كان الملف موجودًا ستفشل الدالة fopen |
x+ | ينشئ ويفتح ملف للكتابة فقط، وإذا كان الملف موجودًا ستفشل الدالة fopen |
c | يفتح الملف للكتابة فقط، إذا لم يكن موجودًا سيحاول أن ينشئه، يبدأ الكتابة من أول الملف لكنه لا يفرغ الملف قبل الكتابة |
c+ | يفتح الملف للقراءة والكتابة، إذا لم يكن موجودًا سيحاول أن ينشئه، يبدأ الكتابة من أول الملف لكنه لا يفرغ الملف قبل الكتابة |
إنّ إضافة المحرف t
بعد النمط (مثل a+b
، wt
وغير ذلك) في نظام التشغيل ويندوز ستترجم نهايات الأسطر "\n" إلى "\r\n" عند العمل مع الملف، نضيف المحرف b
بعد النمط إذا لم يكن هذا مقصودًا خاصةً إذا كان الملف ثنائيًا.
يجب أن يغلق تطبيق PHP أي مجرى بعد الانتهاء من استخدامه باستخدام الدالة fclose
وذلك منعًا لظهور الخطأ Too many open files
، يعدّ هذا مهمًا خاصةً في برامج واجهة سطر الأوامر (CLI) بما أنّ المجاري تُغلق عند إيقاف التشغيل فقط، وهذا يعني أن ذلك ليس ضروريًا جدًا في خوادم الويب (لكن يجب استخدامه أيضًا كممارسة لمنع تسرب المورد) إذا لم تتوقع تنفيذ العملية لوقتٍ طويل ولن تفتح عدة مجاري.
القراءة
نستخدم الدالة fread
لقراءة عدد مُعطى من البايتات بدءًا من مؤشر الملف أو حتى الوصول إلى نهاية الملف (EOF).
قراءة أسطر
نستخدم الدالة fgets
لقراءة ملف وتستمر بالقراءة حتى تصل إلى نهاية السطر (EOL) أو حتى تنتهي قراءة العدد المُعطى.
ستحرِّك كل من الدالتين fread
وfgets
مؤشر الملف أثناء القراءة.
قراءة كل البايتات المتبقية
تضع الدالة stream_get_contents
كل البايتات المتبقية في المجرى في سلسلة نصية وتعيدها.
تعديل موضع مؤشر ملف
يكون مؤشر الملف عند فتح المجرى افتراضيًا في بداية الملف (أو في نهايته إذا اُستعمل النمط)، نستخدم الدالة fseek
لنحرّك مؤشر الملف إلى موضع جديد، نسبةً لإحدى القيم الثلاثة:
-
SEEK_SET
: هذه هي القيمة الافتراضية، تكون إزاحة موقع مؤشر الملف نسبةً لبدايته. -
SEEK_CUR
: إزاحة موقع مؤشر الملف نسبةً للموقع الحالي. -
SEEK_END
: إزاحة موقع مؤشر الملف نسبةً إلى نهاية الملف، ومن الشائع تمرير إزاحة سالبة لهذه القيمة، ستحرّك موقع مؤشر الملف عددًا محددًا من البايتات قبل نهاية الملف.
تعد الدالة rewind
اختصارًا ملائمًا للشيفرة fseek($fh, 0, SEEK_SET)
.
يُظهر استخدام الدالة ftell
الموقع المطلق لمؤشر الملف.
يتخطى السكربت التالي أول 10 بايتات، ويقرأ 10 بايتات التالية ثم يتخطى 10 بايتات ويقرأ 10 بايتات ثم يقرأ 10 بايتات الأخيرة من الملف file.txt
:
$fh = fopen("file.txt", "rb"); // البدء من الإزاحة 10 fseek($fh, 10); // قراءة 10 بايتات echo fread($fh, 10); // تخطي 10 بايتات fseek($fh, 10, SEEK_CUR); // قراءة 10 بايتات echo fread($fh, 10); // تخطي 10 بايتات قبل نهاية الملف fseek($fh, -10, SEEK_END); // قراءة 10 بايتات echo fread($fh, 10); fclose($fh);
الكتابة
نستخدم الدالة fwrite
لكتابة سلسلة في ملف بدءًا من موقع المؤشر الحالي.
fwrite($fh, "Some text here\n");
نقل ونسخ ملفات ومجلدات
نسخ ملفات
تنسخ الدالة copy
الملف المصدر المحدد في الوسيط الأول إلى الهدف المحدد في الوسيط الثاني، يجب أن يكون الهدف في مجلد مُنشأ بالفعل.
if (copy('test.txt', 'dest.txt')) { echo 'File has been copied successfully'; } else { echo 'Failed to copy file to destination given.' }
نسخ مجلدات مع عودية (recursion)
يشبه نسخ الملفات حذفها إلى درجة كبيرة، باستثناء أننا نستخدم الدالة copy
بدلًا من unlink
للملفات وmkdir
بدلًا من rmdir
للمجلدات في بداية بدلًا من كونها في نهاية الدالة.
function recurse_delete_dir(string $src, string $dest) : int { $count = 0; // التأكد من أنّ كل من $src و$dest ينتهي بخط مائل حتى يمكننا دمجه مع أسماء الملفات $src = rtrim($dest, "/\\") . "/"; $dest = rtrim($dest, "/\\") . "/"; // استخدام dir() للحصول على قائمة الملفات $list = dir($src); // إنشاء المجلد $dest إذا لم يكن موجودًا @mkdir($dest); // تخزين اسم الملف التالي في $file، إذا أصبحت قيمة $file هي false تُنهى الحلقة while(($file = $list->read()) !== false) { if($file === "." || $file === "..") continue; if(is_file($src . $file)) { copy($src . $file, $dest . $file); $count++; } elseif(is_dir($src . $file)) { $count += recurse_copy_dir($src . $file, $dest . $file); } } return $count; }
إعادة التسمية/النقل
عملية إعادة التسمية/النقل أبسط بكثير، إذ يمكن نقل أو إعادة تسمية كامل المجلد في استدعاء واحد للدالة rename
، أمثلة:
rename("~/file.txt", "~/file.html"); rename("~/dir", "~/old_dir"); rename("~/dir/file.txt", "~/dir2/file.txt");
تقليل استخدام الذاكرة عند التعامل مع ملفات كبيرة
يمكننا استخدام إحدى الدالتين file
أو file_get_contents
إذا احتجنا إلى تحليل ملف كبير مثل ملف CSV
يتضمن ملايين الأسطر وحجمه أكبر من 10 ميجابايت وينتهي الأمر بالحصول على الخطأ:
Allowed memory size of XXXXX bytes exhausted
بفرض أنّ المصدر التالي top-1m.csv
يحتوي على مليون سطر وحجمه حوالي 22 ميجابايت.
var_dump(memory_get_usage(true)); $arr = file('top-1m.csv'); var_dump(memory_get_usage(true));
الخرج:
int(262144) int(210501632)
بما أنّ المفسّر يحتاج إلى حمل الأسطر في مصفوفة $arr
لذا فإنّه يستهلك 200 ميجابايت من ذاكرة الوصول العشوائي (RAM)، لاحظ أنّه لا يمكننا فعل أي شيء مع محتويات المصفوفة.
بفرض أنّه لدينا الشيفرة التالية:
var_dump(memory_get_usage(true)); $index = 1; if (($handle = fopen("top-1m.csv", "r")) !== FALSE) { while (($row = fgetcsv($handle, 1000, ",")) !== FALSE) { file_put_contents('top-1m-reversed.csv',$index . ',' . strrev($row[1]) . PHP_EOL,FILE_APPEND); $index++; } fclose($handle); } var_dump(memory_get_usage(true));
خرج الشيفرة السابقة:
int(262144) int(262144)
لا نستخدم بايتًا واحدًا إضافيًا من الذاكرة لكن نحلل كامل ملف CSV
ونحفظه إلى ملف آخر مع عكس قيمة العمود الثاني، وذلك لأنّ الدالة fgetcsv
تقرأ سطر واحد فقط ويُكتب فوق $row
عند كل تكرار.
المجاري (streams)
اسم المعامل | الوصف |
Stream Resource |
يتألف مزود البيانات من الصيغة ` |
تسجيل مغلِّف مجرى
يوفر مغلِّف المجرى مقبضًا لمخطط محدد أو أكثر، يُظهر المثال التالي مُغلِّف مجرى بسيط يرسل طلبات PATCH HTTP
عند إغلاق المجرى.
// تسجيل الصنف FooWrapper مغلِّفًا لمجرى روابط foo:// stream_wrapper_register("foo", FooWrapper::class, STREAM_IS_URL) or die("Duplicate stream wrapper registered"); class FooWrapper { // سيُعدَّل هذا من PHP لإظهار السياق الممرَّر في الاستدعاء الحالي public $context; // يستخدم هذا داخليًا في هذا المثال لتخزين الرابط private $url; // عند استدعاء fopen() مع بروتوكول لهذا المغلِّف، يمكن تنفيذ هذا التابع لتخزين بيانات مثل المضيف. public function stream_open(string $path, string $mode, int $options, string &$openedPath) :bool { $url = parse_url($path); if($url === false) return false; $this->url = $url["host"] . "/" . $url["path"]; return true; } // معالجة استدعاءات الدالة fwrite() على هذا المجرى public function stream_write(string $data) : int { $this->buffer .= $data; return strlen($data); } // معالجة استدعاءات الدالة fclose() على هذا المجرى public function stream_close() { $curl = curl_init("http://" . $this->url); curl_setopt($curl, CURLOPT_POSTFIELDS, $this->buffer); curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PATCH"); curl_exec($curl); curl_close($curl); $this->buffer = ""; } // مُعالج استثناء قيمة تراجعية في حال القيام بعملية غير مدعومة // هذا ليس ضروريًا public function __call($name, $args) { throw new \RuntimeException("This wrapper does not support $name"); } // تُستدعى الشيفرة التالية عند استدعاء unlink("foo://something-else") public function unlink(string $path) { $url = parse_url($path); $curl = curl_init("http://" . $url["host"] . "/" . $url["path"]); curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE"); curl_exec($curl); curl_close($curl); } }
يُظهر هذا المثال بعض الأمثلة على ما قد يحتويه مُغلِّف مجرى عام، ليست هذه كل التوابع المتوفرة، يمكنك أن تجد هنا كل التوابع المتوفرة والتي يمكن تنفيذها.
ترميز UTF-8
الدخل
يجب التحقق من كل سلسلة نصية مُستقبَلة أنّها مكتوبة بترميز UTF-8
صحيح قبل محاولة تخزينها في أي مكان، تقوم دالة mbcheckencoding()
بهذا العمل لكن يجب أن تستخدمها بشكلٍ منتظم، لا توجد طريقة لحل هذه المشكلة إذ يمكن للعملاء الضارّين إرسال البيانات بأي تشفير يريدونه.
$string = $_REQUEST['user_comment']; if (!mb_check_encoding($string, 'UTF-8')) { // ترميز السلسلة النصية ليس UTF-8 لذا أعد تشفيرها $actualEncoding = mb_detect_encoding($string); $string = mb_convert_encoding($string, 'UTF-8', $actualEncoding); }
يمكنك تجاهل النقطة الأخيرة إذا كنت تستخدم HTML5
، إذ أنّ الطريقة الموثوقة الوحيدة لتكون كل البيانات المرسلة إليك عبر المتصفحات بالترميز UTF-8
هي إضافة السمة accept-charset
لكل وسوم <form>
كالتالي:
<form action="somepage.php" accept-charset="UTF-8">
الخرج
إذا كان تطبيقك ينقل نصًا إلى الأنظمة الأخرى فأنت تحتاج إلى التأكد من ترميز المحارف، يمكنك في PHP استخدام خيار default_charset
في php.ini
أو التصريح عن نوع المحتوى يدويًا Content-Type MIME
في الترويسة وهذه الطريقة الأفضل عند استهداف المتصفحات الحديثة.
header('Content-Type: text/html; charset=utf-8');
يمكنك ضبط الترميز في ملف HTML
باستخدام البيانات الوصفية إذا كنت غير قادرٍ على ضبط ترويسة الرد.
// HTML5 <meta charset="utf-8"> // الإصدارات الأقدم من HTML <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
تخزين البيانات والوصول إليها
تتحدث هذه الفقرة بشكلٍ أساسي عن الترميز UTF-8
ودوافع استخدامه مع قاعدة البيانات.
تخزين البيانات في قاعدة بيانات MySQL: عندما تحدد مجموعة المحارف (character set) utf8mb4
لكل الجداول والأعمدة النصية في قاعدة البيانات فأنت تجعل MySQL
تخزّن وتستعيد القيم المرمّزة محليًا بالترميز UTF-8
.
تستخدم MySQL
ضمنيًا الترميز utf8mb4
إذا حُدِّد الترتيب utf8mb4_*
(دون أي مجموعة محارف صريحة).
لا تدعم النسخ القديمة من MySQL
(الإصدار ما قبل 5.5.3) الترميز utf8mb4
لذا ستضطر إلى استخدام الترميز utf8
الذي يدعم مجموعة فرعية من محارف يونيكود (Unicode).
الوصول إلى البيانات في قاعدة بيانات MySQL: في شيفرة التطبيق (PHP مثلًا)، مهما كانت الطريقة التي تستخدمها للوصول إلى قاعدة البيانات فأنت تحتاج إلى ضبط ترميز محارف الاتصال لتكون utf8mb4
، بهذه الطريقة لا تحوّل MySQL
من ترميز UTF-8
الأصلي عند تسليم البيانات إلى تطبيقك وبالعكس.
توفر بعض برامج التشغيل آلية خاصة بها لضبط ترميز محارف الاتصال والتي تحدّث حالتها الداخلية وتُخبر MySQL
بالترميز الذي سيُستخدم في الاتصال، وهذه هي الطريقة المفضّلة عادةً.
مثلًا (نطبق نفس الدوافع التي في الأعلى فيما يتعلق باستخدام utf8mb4/utf8
?
$handle = new PDO('mysql:charset=utf8mb4');
-
يمكنك استدعاء الدالة
set_charset()
إذا كنت تستخدمmysqli
:
$conn = mysqli_connect('localhost', 'my_user', 'my_password', 'my_db'); // نمط كائني التوجه $conn->set_charset('utf8mb4'); // نمط إجرائي mysqli_set_charset($conn, 'utf8mb4');
-
يمكنك استدعاء الدالة
mysql_set_charset
إذا كنت تستخدمMySQL
البسيطة وإصدار PHP ≥ 5.2.3.
$conn = mysql_connect('localhost', 'my_user', 'my_password'); // نمط كائني التوجه $conn->set_charset('utf8mb4'); // نمط إجرائي mysql_set_charset($conn, 'utf8mb4');
-
إذا لم يوفر نظام تشغيل قاعدة البيانات آلية خاصة به لضبط ترميز محارف الاتصال، قد تضطر إلى إصدار استعلام لإخبار
MySQL
عن الطريقة التي يتوقع بها تطبيقك تشفير البيانات عند الاتصال بالشكل: SET NAMES 'utf8mb4'.
دعم يونيكود في PHP
تحويل محارف يونيكود إلى تنسيق "\uxxxx" باستخدام PHP
يمكنك استخدام الشيفرة التالية للترميز وفك الترميز:
if (!function_exists('codepoint_encode')) { function codepoint_encode($str) { return substr(json_encode($str), 1, -1); } } if (!function_exists('codepoint_decode')) { function codepoint_decode($str) { return json_decode(sprintf('"%s"', $str)); } }
طريقة الاستخدام
echo "\\nUse JSON encoding / decoding\\n"; var_dump(codepoint_encode("我好")); var_dump(codepoint_decode('\\u6211\\u597d'));
الخرج:
Use JSON encoding / decoding string(12) "\\u6211\\u597d" string(6) "我好"
تحويل محارف يونيكود إلى قيمها الرقمية و/أو كيانات HTML باستخدام PHP
يمكنك استخدام الشيفرة التالية للترميز وفك الترميز:
if (!function_exists('mb_internal_encoding')) { function mb_internal_encoding($encoding = NULL) { return ($from_encoding === NULL) ? iconv_get_encoding() : iconv_set_encoding($encoding); } } if (!function_exists('mb_convert_encoding')) { function mb_convert_encoding($str, $to_encoding, $from_encoding = NULL) { return iconv(($from_encoding === NULL) ? mb_internal_encoding() : $from_encoding,$to_encoding, $str); } } if (!function_exists('mb_chr')) { function mb_chr($ord, $encoding = 'UTF-8') { if ($encoding === 'UCS-4BE') { return pack("N", $ord); } else { return mb_convert_encoding(mb_chr($ord, 'UCS-4BE'), $encoding, 'UCS-4BE'); } } } if (!function_exists('mb_ord')) { function mb_ord($char, $encoding = 'UTF-8') { if ($encoding === 'UCS-4BE') { list(, $ord) = (strlen($char) === 4) ? @unpack('N', $char) : @unpack('n', $char); return $ord; } else { return mb_ord(mb_convert_encoding($char, 'UCS-4BE', $encoding), 'UCS-4BE'); } } } if (!function_exists('mb_htmlentities')) { function mb_htmlentities($string, $hex = true, $encoding = 'UTF-8') { return preg_replace_callback('/[\x{80}-\x{10FFFF}]/u', function ($match) use ($hex) { return sprintf($hex ? '&#x%X;' : '&#%d;', mb_ord($match[0])); }, $string); } } if (!function_exists('mb_html_entity_decode')) { function mb_html_entity_decode($string, $flags = null, $encoding = 'UTF-8') { return html_entity_decode($string, ($flags === NULL) ? ENT_COMPAT | ENT_HTML401 : $flags,$encoding); } }
طريقة الاستخدام
echo "Get string from numeric DEC value\n"; var_dump(mb_chr(50319, 'UCS-4BE')); var_dump(mb_chr(271)); echo "\nGet string from numeric HEX value\n"; var_dump(mb_chr(0xC48F, 'UCS-4BE')); var_dump(mb_chr(0x010F)); echo "\nGet numeric value of character as DEC string\n"; var_dump(mb_ord('ď', 'UCS-4BE')); var_dump(mb_ord('ď')); echo "\nGet numeric value of character as HEX string\n"; var_dump(dechex(mb_ord('ď', 'UCS-4BE'))); var_dump(dechex(mb_ord('ď'))); echo "\nEncode / decode to DEC based HTML entities\n"; var_dump(mb_htmlentities('tchüß', false)); var_dump(mb_html_entity_decode('tchüß')); echo "\nEncode / decode to HEX based HTML entities\n"; var_dump(mb_htmlentities('tchüß')); var_dump(mb_html_entity_decode('tchüß'));
الخرج:
Get string from numeric DEC value string(4) "ď" string(2) "ď" Get string from numeric HEX value string(4) "ď" string(2) "ď" Get numeric value of character as DEC int int(50319) int(271) Get numeric value of character as HEX string string(4) "c48f" string(3) "10f" Encode / decode to DEC based HTML entities string(15) "tchüß" string(7) "tchüß" Encode / decode to HEX based HTML entities string(15) "tchüß" string(7) "tchüß"
استخدام الإضافة Intl لدعم يونيكود
تُربط دوال السلسلة النصية الأصلية إلى دوال البايت المفرد ولا تعمل بشكلٍ جيد مع ترميز يونيكود (unicode)، توفر الإضافات iconv
وmbstring
بعض الدعم لترميز يونيكود بينما توفر الإضافة Intl
الدعم الكامل. تعدّ الإضافة Intl
مغلّفة للمعيار المعتمد لمكتبة ICU (International Components for unicode)، يمكنك الاطلاع على الموقع لمعرفة المزيد من المعلومات التفصيلية غير الموجودة هنا. اطّلع على تنفيذ بديل للإضافة Intl من بيئة العمل Symfony إذا لم تتمكن من تثبيت الإضافة.
توفر ICU تدويلًا كاملًا وترميز يونيكود هو جزء صغير منه، يمكنك التحويل بسهولة:
// إزالة البايتات السيئة للحماية من الهجمات \UConverter::transcode($sString, 'UTF-8', 'UTF-8');
لكن يجب ألا تتخلى عن الإضافة iconv
:
\iconv('UTF-8', 'ASCII//TRANSLIT', "Cliënt"); // "Client"
ترجمة -وبتصرف- للفصول [File handling - Streams - UTF-8 - Unicode Support in PHP] من كتاب PHP Notes for Professionals book
اقرأ أيضًا
- المقال التالي: المرشحات ودوال المرشح (Filter) المعقمة في PHP
- المقال السابق: التوابع السحرية (Magic Methods) في PHP
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.