اذهب إلى المحتوى

صيغة JSON وXML في PHP


سارة محمد2

الدالة json_encode تأخذ المعاملات التالية:

المعامل تفاصيل
value القيمة المطلوب ترميزها، يمكن أن تكون من أي نوع عدا الموارد ويجب ترميز جميع بيانات السّلاسل UTF-8 النصيّة بصيغة
options

خيارات على شكل قناع بتي وهي:

  • JSON_HEX_AMP
  • JSON_HEX_APOS
  • JSON_HEX_QUOT
  • JSON_HEX_TAG
  • JSON_NUMERIC_CHECK
  • JSON_PRETTY_PRINT
  • JSON_UNESCAPED_SLASHES
  • JSON_FORCE_OBJECT
  • JSON_PRESERVE_ZERO_FRACTION
  • JSON_UNESCAPED_UNICODE
  • JSON_PARTIAL_OUTPUT_ON_ERROR

ويوجد وصف لسلوك هذه الثوابت في صفحة JSON ثوابت‎

depth يحدد العمق الأقصى، ويجب أن يكون أكبر من الصّفر

الدالة json_decode فتأخذ المعاملات التالية:

المعامل تفاصيل
json السلسلة النصية المُراد فك ترميزها بصيغة ‏ فقط UTF-8 تعمل هذه الدّالّة مع سلاسل نصّيّة مُرمَّزة بترميز JSON
assoc تُحوَّل الكائنات المُعادة من الدالة إلى مصفوفات ترابطية عندما تكون قيمته TRUE
options خيارات لفك ترميز JSON على شكل قناع بتي يوجد خيار واحد مدعوم حاليًا وهو JSON_BIGINT_AS_STRING ويسمح بقلب الأعداد الصحيحة الكبيرة إلى سلاسل نصّيّة بدلًا من أعداد عشرية 

فك ترميز سلسلة نصية بصيغة JSON

تأخذ الدالة json_decode المعامل الأول على شكل سلسلة نصية مرمّزة بصيغة JSON وتحلله إلى متغير PHP، إذا كان كائن JSON هو مصفوفة فهي تعيد كائنًا من ‎\stdClass إذا كان عنصر المستوى الأعلى في الكائن هو قاموس (dictionary) أو مصفوفة مفهرسة، وتعيد قيمًا عددية أو NULL من أجل قيم عددية معينة مثل السلاسل النصية البسيطة "true" و"false" و"null"، وتعيد NULL عند أي خطأ.

تعيد الشيفرة التالية كائنًا بما أنّ عنصر المستوى الأعلى في السلسلة النصية بصيغة JSON هو قاموس JSON:

$json_string = '{"name": "Jeff", "age": 20, "active": true, "colors": ["red", "blue"]}';

$object = json_decode($json_string);
printf('Hello %s, You are %s years old.', $object->name, $object->age);
// Hello Jeff, You are 20 years old.

تعيد الشيفرة التالية مصفوفة بما أنّ عنصر المستوى الأعلى في السلسلة النصية بصيغة JSON هو مصفوفةJSON:

$json_string = '["Jeff", 20, true, ["red", "blue"]]';

$array = json_decode($json_string);
printf('Hello %s, You are %s years old.', $array[0], $array[1]);

يمكنك استخدام الدالة var_dump()‎ للاطلاع على أنواع وقيم كل خاصيّة للكائن الذي فُكَّ ترميزه، فإذا استخدمناها للكائن ‎$object في مثالنا السابق:

var_dump($object);

يكون الخرج:

class stdClass#2 (4) {
    ["name"] => string(4) "Jeff"
    ["age"] => int(20)
    ["active"] => bool(true)
    ["colors"] =>
    array(2) {
        [0] => string(3) "red"
        [1] => string(4) "blue"
    }
}

ملاحظة: حُوِّلت أنواع المتغيرات في JSON إلى مكافئاتها في PHP.

نقوم بتمرير true معاملًا ثانيًا للدالة json_decode()‎ لإرجاع مصفوفة ترابطية من كائنات JSON بدلًا من إرجاع كائن. مثال:

$json_string = '{"name": "Jeff", "age": 20, "active": true, "colors": ["red", "blue"]}';
$array = json_decode($json_string, true);
var_dump($array);

خرج الشيفرة السابقة:

array(4) {
    ["name"] => string(4) "Jeff"
    ["age"] => int(20)
    ["active"] => bool(true)
    ["colors"] =>
    array(2) {
        [0] => string(3) "red"
        [1] => string(4) "blue"
    }
}

ليس للمعامل الثاني ‎$assoc تأثير إذا كان المتغير الذي سيُرجع ليس كائنًا.

ملاحظة: ستفقد التمييز بين مصفوفة فارغة وكائن فارغ إذا كنت تستخدم المعامل ‎$assoc، أي أنّ تنفيذ json_encode()‎ على الخرج المفكوك ترميزه مرةً ثانية سينتج بنية JSON مختلفة.

تعيد الدالة json_decode()‎ القيمة NULL إذا كان عمق السلسلة النصية بصيغة JSON أكثر من 512 عنصر (20 عنصر في إصدارات PHP أقدم من 5.2.3 أو 128 في الإصدار 5.2.3) بالتعاود (recursion)، يمكن التحكم بهذا الحد في الإصدار 5.3 وما بعده باستخدام المعامل الثالث ‎$depth كما هو موضح في الدليل الرسمي للغة PHP:

تنفذ PHP مجموعة عليا من JSON كما ذُكر في RFC 4627 - سترمّز أيضًا وتفك ترميز الأنواع العددية وNULL، تدعم RFC 4627 فقط هذه القيم عندما تكون متداخلة ضمن مصفوفة أو كائن. بالرغم من أن هذه المجموعة العليا متوافقة مع التعريف الموسع لنص JSON في RFC 7159 الأحدث (الذي يهدف إلى التفوق على RFC 4627) وECMA-404 وهذا قد يسبب مشاكل في قابلية التشغيل المتبادل مع محللات JSON التي تلتزم بدقة بالنوع RFC 4627 عند تشفير قيمة عددية.

يعني هذا أنّ السلسلة النصية البسيطة ستعدّ كائن JSON صحيح في PHP مثال:

$json = json_decode('"some string"', true);
var_dump($json, json_last_error_msg());

الخرج:

string(11) "some string"
string(8) "No error"

لكن السلاسل النصية البسيطة التي ليست في مصفوفة أو كائن ليست جزءًا من معيار RFC 4627، أي أن المتفحصات (checkers) على الإنترنت مثل JSLint وJSON Formatter & Validator التي تعمل بالنمط RFC 4627 ستعطيك خطأً.

المعامل الثالث للدالة json_decode()‎ هو ‎$depth يحدد العمق التعاودي (القيمة الافتراضية 512)، أي عدد الكائنات المتداخلة ضمن الكائن الأصلي الذي سيُفَك تشفيره.

المعامل الرابع ‎$options يقبل قيمة واحدة حاليًا وهي JSON_BIGINT_AS_STRING، أي أنّ السلوك الافتراضي للدالة (مع عدم وجود هذا المعامل) يحول القيم الصحيحة الكبيرة إلى أعداد عشرية بدلًا من سلاسل نصية.

لا تُعدّ المتغيرات غير الصحيحة ذات الأحرف الكبيرة للكلمات true و‏false وnull دخلًا صحيحًا.

لدينا المثال التالي:

var_dump(json_decode('tRue'), json_last_error_msg());
var_dump(json_decode('tRUe'), json_last_error_msg());
var_dump(json_decode('tRUE'), json_last_error_msg());
var_dump(json_decode('TRUe'), json_last_error_msg());
var_dump(json_decode('TRUE'), json_last_error_msg());
var_dump(json_decode('true'), json_last_error_msg());

الخرج في الإصدار قبل PHP 5.6:

bool(true)
string(8) "No error"
bool(true)
string(8) "No error"
bool(true)
string(8) "No error"
bool(true)
string(8) "No error"
bool(true)
string(8) "No error"
bool(true)
string(8) "No error"

أما في الإصدارات اللاحقة:

NULL
string(12) "Syntax error"
NULL
string(12) "Syntax error"
NULL
string(12) "Syntax error"
NULL
string(12) "Syntax error"
NULL
string(12) "Syntax error"
bool(true)
string(8) "No error"

ينطبق الأمر أيضًا على false وnull.

لاحظ أنّ الدالة json_decode()‎ تُعيد NULL إذا كان من غير الممكن تحويل السلسلة النصية.

// غير صحيحة json صيغة
$json = "{'name': 'Jeff', 'age': 20 }" ;

$person = json_decode($json);
// null ملاحظة: محاولة الوصول إلى خاصيّة عنصر ليس كائنًا تُرجع
echo $person->name; 

echo json_last_error();
# 4 (JSON_ERROR_SYNTAX)

echo json_last_error_msg();
# unexpected character

ليس من الآمن أن نعتمد فقط على القيمة المُرجعة بأن تكون NULL لاكتشاف الأخطاء، فمثلًا إذا كانت السلسلة النصية بصيغة JSON لا تتضمن شيء إلا "null" فإنّ الدالة json_decode()‎ ستُرجع null حتى في حال عدم حدوث أخطاء.

ترميز سلسلة نصية بصيغة JSON

تحوّل الدالة json_encode مصفوفة PHP (أو كائن ينفذ الواجهة JsonSerializable بدءًا من الإصدار PHP 5.4) إلى سلسلة نصية بترميز صيغة JSON وتعيدها، وفي حال الفشل تعيد FALSE.

$array = [
    'name' => 'Jeff',
    'age' => 20,
    'active' => true,
    'colors' => ['red', 'blue'],
    'values' => [0=>'foo', 3=>'bar'],
];

تُحوَّل عند الترميز أنواع بيانات PHP (السلاسل النصية والأعداد الصحيحة والقيم المنطقية) إلى مكافئاتها في JSON، وتُرمَّز المصفوفات الترابطية ككائنات JSON وعند الاستدعاء مع الوسطاء الافتراضيين تُرمَّز المصفوفات المفهرسة كمصفوفات JSON، ما لم تكن مفاتيح المصفوفة سلسلة عددية مستمرة تبدأ من الصفر، فعندها ستُرمَّز المصفوفة ككائن JSON.

echo json_encode($array);

الخرج:

{"name":"Jeff","age":20,"active":true,"colors":["red","blue"],"values":{"0":"foo","3":"bar"}}

الوسائط

بدءًا من الإصدار PHP 5.3 فإنّ المعامل الثاني للدالة json_encode هو قناع بتي والذي يمكن أن يكون واحد أو أكثر من التالي، ويمكن دمجه كما هو الحال مع أي قناع بتي مع العامل الثنائي OR |.

الإصدار PHP 5.3 ومابعده

JSON_FORCE_OBJECT: يفرض إنشاء كائن بدلًا من مصفوفة.

$array = ['Joel', 23, true, ['red', 'blue']];
echo json_encode($array);
echo json_encode($array, JSON_FORCE_OBJECT);

الخرج:

["Joel",23,true,["red","blue"]]
{"0":"Joel","1":23,"2":true,"3":{"0":"red","1":"blue"}}

JSON_HEX_TAG، ‏JSONHEXAMP، ‏JSON_HEX_APOS، ‏JSON_HEX_QUOT.

تضمن الخيارات السابقة التحويلات التالية عند الترميز:

الثابت الدخل الخرج
JSON_HEX_TAG < \u003C
JSON_HEX_TAG > \u003E
JSON_HEX_AMP & \u0026
JSON_HEX_APOS ' \u0027
JSON_HEX_QUOT " \u0022
$array = ["tag"=>"<>", "amp"=>"&", "apos"=>"'", "quot"=>"\""];
echo json_encode($array);
echo json_encode($array, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);

الخرج:

{"tag":"<>","amp":"&","apos":"'","quot":"\""}
{"tag":"\u003C\u003E","amp":"\u0026","apos":"\u0027","quot":"\u0022"}

الإصدار PHP 5.3 ومابعده

JSON_NUMERIC_CHECK: يضمن تحويل السلاسل النصية العددية إلى أعداد صحيحة.

$array = ['23452', 23452];
echo json_encode($array);
echo json_encode($array, JSON_NUMERIC_CHECK);

الخرج:

["23452",23452]
[23452,23452]

الإصدار PHP 5.4 ومابعده

JSON_PRETTY_PRINT: يجعل صيغة JSON مقروءة بسهولة.

$array = ['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4];
echo json_encode($array);
echo json_encode($array, JSON_PRETTY_PRINT);

الخرج:

{"a":1,"b":2,"c":3,"d":4}
{
    "a": 1,
    "b": 2,
    "c": 3,
    "d": 4
}

JSON_UNESCAPED_SLASHES: يضمّن هذا الخيار خطوط الهروب المائلة الأمامية / في الخرج:

$array = ['filename' => 'example.txt', 'path' => '/full/path/to/file/'];
echo json_encode($array);
echo json_encode($array, JSON_UNESCAPED_SLASHES);

الخرج:

{"filename":"example.txt","path":"\/full\/path\/to\/file"}
{"filename":"example.txt","path":"/full/path/to/file"}

JSON_UNESCAPED_UNICODE: يضمّن محارف بترميز UTF8 في الخرج بدلًا من السلاسل النصية بترميز يونيكود.

$blues = ["english"=>"blue", "norwegian"=>"blå", "german"=>"blau"];
echo json_encode($blues);
echo json_encode($blues, JSON_UNESCAPED_UNICODE);

الخرج:

{"english":"blue","norwegian":"bl\u00e5","german":"blau"}
{"english":"blue","norwegian":"blå","german":"blau"}

الإصدار PHP 5.5 ومابعده

JSON_PARTIAL_OUTPUT_ON_ERROR: يسمح بمتابعة الترميز حتى لو وُجدت بعض القيم غير قابلة للترميز.

$fp = fopen("foo.txt", "r");
$array = ["file"=>$fp, "name"=>"foo.txt"];
echo json_encode($array); // no output
echo json_encode($array, JSON_PARTIAL_OUTPUT_ON_ERROR);

الخرج:

{"file":null,"name":"foo.txt"}

الإصدار PHP 5.6 ومابعده

JSON_PRESERVE_ZERO_FRACTION: يضمن ترميز الأعداد العشرية كأعداد عشرية دومًا.

$array = [5.0, 5.5];
echo json_encode($array);
echo json_encode($array, JSON_PRESERVE_ZERO_FRACTION);

الخرج:

[5,5.5]
[5.0,5.5]

الإصدار PHP 7.1 ومابعده

JSON_UNESCAPED_LINE_TERMINATORS: عندما يُستخدم مع JSON_UNESCAPED_UNICODE يعود إلى سلوك إصدارات PHP القديمة ولا يتجاوز المحرف الفاصل للأسطر U+2028 والمحرف الفاصل للفقرات U+2029. بالرغم من أن هذه المحارف صحيحة في JSON فهي غير صحيحة في جافاسكربت لذا فقد تغيّر السلوك الافتراضي للخيار JSON_UNESCAPED_UNICODE في الإصدار PHP 7.1.

$array = ["line"=>"\xe2\x80\xa8", "paragraph"=>"\xe2\x80\xa9"];
echo json_encode($array, JSON_UNESCAPED_UNICODE);
echo json_encode($array, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_LINE_TERMINATORS);

الخرج:

{"line":"\u2028","paragraph":"\u2029"}
{"line":" ","paragraph":" "}

تنقيح أخطاء JSON

عندما تفشل إحدى الدالتين json_encode أوjson_decode في التحليل إلى السلسلة النصية المطلوبة فإنّها تعيد false، ولا تنشر PHP أي أخطاء أو تحذيرات في هذه الحالة إنما تقع المسؤولية على المستخدم الذي يجب أن يستخدم الدالتين json_last_error()‎ وjson_last_error_msg()‎ للتحقق من حدوث أخطاء ومعالجتها وفق التطبيق (تنقيحها، عرض رسالة خطأ وغير ذلك).

يظهر المثال التالي خطأً مشهورًا عند العمل مع JSON وهو الفشل في ترميز/فك ترميز سلسلة نصية بصيغة JSON (بسبب تمرير سلسلة نصية بترميز UTF-8 غير صحيح مثلًا).

// غير صحيحة JSON سلسلة نصية بصيغة
$jsonString = json_encode("{'Bad JSON':\xB1\x31}");

if (json_last_error() != JSON_ERROR_NONE) {
    printf("JSON Error: %s", json_last_error_msg());
}

// JSON Error: Malformed UTF-8 characters, possibly incorrectly encoded

json_last_error_msg

تعيد هذه الدالة رسالة مقروءة من قبل الإنسان للخطأ الأخير الذي حدث عند محاولة ترميز/فك ترميز سلسلة نصية.

تعيد هذه الدالة دومًا سلسلة نصية حتى لو لم يحدث خطأ والرسالة الافتراضية في حال عدم حدوث خطأ هي No Error، وتعيد false إذا حدث خطأ ما (غير معروف).

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

// لا تكتب هذه الشيفرة
// لأن نتيجة الدالة سلسلة نصية true النتيجة دائمًا
if (json_last_error_msg()){}

// ممارسة سيئة
if (json_last_error_msg() != "No Error"){}

// (يمكنك كتابة هذا (اختبار العدد الصحيح مع إحدى الثوابت المعرفة مسبقًا‎‏
if (json_last_error() != JSON_ERROR_NONE) {
    // لعرض رسالة فقط وليس للاختبار json_last_error_msg استخدمنا
    printf("JSON Error: %s", json_last_error_msg());
}

لم تكن هذه الدالة موجودة في الإصدارات السابقة للإصدار PHP 5.5. إليك تنفيذ الترقيع المتعدد (polyfill):

if (!function_exists('json_last_error_msg')) {
    function json_last_error_msg() {
        static $ERRORS = array(
            JSON_ERROR_NONE => 'No error',
            JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
            JSON_ERROR_STATE_MISMATCH => 'State mismatch (invalid or malformed JSON)',
            JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded',
            JSON_ERROR_SYNTAX => 'Syntax error',
            JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded'
        );

        $error = json_last_error();
        return isset($ERRORS[$error]) ? $ERRORS[$error] : 'Unknown error';
    }
}

json_last_error()‎

تعيد هذه الدالة عددًا صحيحًا مربوطًا بإحدى ثوابت PHP المعرفة مسبقًا.

الثابت المعنى
JSON_ERROR_NONE لم يحدث أي خطأ
JSON_ERROR_DEPTH حدث تجاوز للحد الأقصى لعمق المكدس
JSON_ERROR_STATE_MISMATCH صيغة JSON غير صحيحة أو تالفة
JSON_ERROR_CTRL_CHAR خطأ محرف التحكم ربما مرمَّز بشكلٍ خاطئ
JSON_ERROR_SYNTAX خطأ في الصياغة (منذ الإصدار ‎PHP 5.3.3)
JSON_ERROR_UTF8 محارف UTF-8 تالفة ربما مرمَّزة بشكلٍ خاطئ (منذ الإصدار‎‎PHP 5.5.0)
JSON_ERROR_RECURSION مرجع تعاودي أو أكثر في القيمة المطلوب ترميزها
JSON_ERROR_INF_OR_NAN قيمة غير رقمية أو لا نهائية في القيمة المطلوب ترميزها
JSON_ERROR_UNSUPPORTED_TYPE القيمة المُعطاة من نوع لا يمكن ترميزه

استخدام الواجهة JsonSerializable في كائن

الإصدار PHP 5.4 وما بعده

قد تحتاج عند بناء واجهة برمجة التطبيقات REST (‏Representational state transfer) إلى تقليل بيانات كائن ليُمرَّر إلى تطبيق العميل، ولهذا الهدف يوضّح المثال التالي كيفية استخدام الواجهة JsonSerializable.

في هذا المثال يوسّع الصنف User كائن DB model من تقنية ORM الافتراضية (تقنية ربط الكائنات بالعلاقات).

class User extends Model implements JsonSerializable {
    public $id;
    public $name;
    public $surname;
    public $username;
    public $password;
    public $email;
    public $date_created;
    public $date_edit;
    public $role;
    public $status;

    public function jsonSerialize() {
        return [
            'name' => $this->name,
            'surname' => $this->surname,
            'username' => $this->username
        ];
    }
}

نضيف تنفيذ JsonSerializable للصنف بتوفير التابع jsonSerialize()‎.

public function jsonSerialize()

الآن عند تمرير الكائن User في وحدة تحكم التطبيق (controller) أو السكربت إلى الدالة json_encode()‎ ستحصل على مصفوفة معادة بصيغة JSON للتابع jsonSerialize()‎ بدلًا من الكائن بأكمله.

json_encode($User);

تعيد الشيفرة السابقة:

{"name":"John", "surname":"Doe", "username" : "TestJson"}

مثال قيم الخاصيات

سيقلل هذا كمية البيانات المُعادة من النقطة النهائية (endpoint) لواجهة برمجة التطبيقات RESTful ويسمح باستبعاد خاصيّات الكائن من تمثيل JSON.

استخدام الخاصيّات الخاصة والمحمية مع الدالة json_encode()‎

إذا أردت تجنّب استخدام JsonSerializable فمن الممكن استخدام الخاصيّات الخاصة أو المحمية لإخفاء بيانات الصنف من خرج الدالة json_encode()‎ وعندها لا يحتاج الصنف إلى تنفيذ الواجهة JsonSerializable.

ترمّز الدالة json_encode()‎ الخاصيّات العامة فقط من الصنف إلى صيغة JSON.

<?php
    class User {
        // الخاصيّات الخاصة تُستخدم فقط ضمن هذا الصنف
        private $id;
        private $date_created;
        private $date_edit;

        // الخاصيّات المستخدمة في الأصناف الموسّعة
        protected $password;
        protected $email;
        protected $role;
        protected $status;

        // مشاركة هذه الخاصيّات مع المستخدم النهائي
        public $name;
        public $surname;
        public $username;

        // هنا jsonSerialize() لا نحتاج لاستخدام
    }

$theUser = new User();

var_dump(json_encode($theUser));

الخرج:

string(44) "{"name":null,"surname":null,"username":null}"

ترويسة JSON والرد المُعاد

نضيف في الشيفرة التالية ترويسة مع نوع المحتوى JSON:

<?php
    $result = array('menu1' => 'home', 'menu2' => 'code php', 'menu3' => 'about');

// json إرجاع الرد بصيغة
header('Content-Type: application/json'); // تصريح الترويسة

// الترميز
echo json_encode($result, true); 
exit();

الترويسة موجودة لذا يتمكن التطبيق من اكتشاف ما البيانات المُعادة وكيف يجب معالجتها، ولا حظ أنّ ترويسة المحتوى هي فقط معلومات حول نوع البيانات المُعادة.

إذا كنت تستخدم UTF-8 يمكنك كتابة:

header("Content-Type: application/json;charset=utf-8");

مثال jQuery:

$.ajax({
    url:'url_your_page_php_that_return_json'
}).done(function(data){
    console.table('json ',data);
    console.log('Menu1: ', data.menu1);
});

إنشاء ملف XML باستخدام DomDocument

نحتاج لإنشاء ملف XML باستخدام الصنف DOMDocument إلى إنشاء كل الوسوم والخاصيّات باستخدام التابعين createElement()‎ وcreateAttribute()‎ اللذين ينشئان بنية XML باستخدام التابع appendChild()‎.

يتضمن المثال التالي وسوم وخاصيات وقسم CDATA (‏Character Data) وفضاء اسم مختلف للوسم الثاني:

$dom = new DOMDocument('1.0', 'utf-8');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;

// إنشاء الوسوم الأساسية دون القيم
$books = $dom->createElement('books');
$book_1 = $dom->createElement('book');

// إنشاء بعض الوسوم مع قيم
$name_1 = $dom->createElement('name', 'PHP - An Introduction');
$price_1 = $dom->createElement('price', '$5.95');
$id_1 = $dom->createElement('id', '1');

// إنشاء وإضافة خاصية
$attr_1 = $dom->createAttribute('version');
$attr_1->value = '1.0';
// إضافة الخاصية
$id_1->appendChild($attr_1);

// الثاني بفضاء اسم مختلف book إنشاء
$namespace = 'www.example.com/libraryns/1.0';

// books تضمين بادئة فضاء الاسم في الوسم
$books->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:ns', $namespace);
$book_2 = $dom->createElementNS($namespace,'ns:book');
$name_2 = $dom->createElementNS($namespace, 'ns:name');

// name أخرى ووضعه ضمن الوسم DOMNode الذي هو نسخة CDATA إنشاء قسم
$name_cdata = $dom->createCDATASection('PHP - Advanced');
$name_2->appendChild($name_cdata);
$price_2 = $dom->createElementNS($namespace, 'ns:price', '$25.00');
$id_2 = $dom->createElementNS($namespace, 'ns:id', '2');

// XML إنشاء ملف
$books->appendChild($book_1);
$book_1->appendChild($name_1);
$book_1->appendChild($price_1);
$book_1->appendChild($id_1);
$books->appendChild($book_2);
$book_2->appendChild($name_2);
$book_2->appendChild($price_2);
$book_2->appendChild($id_2);

$dom->appendChild($books);

// في سلسلة نصية XML ملف saveXML() يعيد التابع
print_r ($dom->saveXML());

ستنتج الشيفرة السابقة ملف XML التالي:

<?xml version="1.0" encoding="utf-8"?>
<books xmlns:ns="www.example.com/libraryns/1.0">
    <book>
        <name>PHP - An Introduction</name>
        <price>$5.95</price>
        <id version="1.0">1</id>
    </book>
    <ns:book>
        <ns:name><![CDATA[PHP - Advanced]]></ns:name>
        <ns:price>$25.00</ns:price>
        <ns:id>2</ns:id>
    </ns:book>
</books>

قراءة ملف XML باستخدام DOMDocument

يمكن استخدام الصنف DOMDocument بطريقة مشابهة لاستخدام SimpleXML لتحليل XML من سلسلة نصية أو من ملف XML.

  • من سلسلة نصية:
$doc = new DOMDocument();
$doc->loadXML($string);
  • من ملف:
$doc = new DOMDocument();
// استخدم مسار الملف الفعلي النسبي أو المطلق
$doc->load('books.xml');

مثال عن التحليل: بفرض لدينا صيغة XML التالية:

<?xml version="1.0" encoding="UTF-8"?>
<books>
    <book>
        <name>PHP - An Introduction</name>
        <price>$5.95</price>
        <id>1</id>
    </book>
    <book>
        <name>PHP - Advanced</name>
        <price>$25.00</price>
        <id>2</id>
    </book>
</books>

هذا مثال شيفرة لتحليل الصيغة السابقة:

$books = $doc->getElementsByTagName('book');
foreach ($books as $book) {
    $title = $book->getElementsByTagName('name')->item(0)->nodeValue;
    $price = $book->getElementsByTagName('price')->item(0)->nodeValue;
    $id = $book->getElementsByTagName('id')->item(0)->nodeValue;
    print_r ("The title of the book $id is $title and it costs $price." . "\n");
}

سينتج عنها الخرج التالي:

The title of the book 1 is PHP - An Introduction and it costs $5.95.
The title of the book 2 is PHP - Advanced and it costs $25.00.

الاستفادة من XML مع مكتبة SimpleXML في PHP

تعد SimpleXML مكتبة قوية تحوّل السلاسل النصية بصيغة XML إلى صيغة سهلة لاستخدام كائن PHP.

بفرض لدينا بنية XML التالية:

<?xml version="1.0" encoding="UTF-8"?>
<document>
    <book>
        <bookName>StackOverflow SimpleXML Example</bookName>
        <bookAuthor>PHP Programmer</bookAuthor>
    </book>
    <book>
        <bookName>Another SimpleXML Example</bookName>
        <bookAuthor>Stack Overflow Community</bookAuthor>
        <bookAuthor>PHP Programmer</bookAuthor>
        <bookAuthor>FooBar</bookAuthor>
    </book>
</document>

قراءة بياناتنا في SimpleXML

نحتاج بدايةً إلى قراءة بياناتنا في SimpleXML، يمكننا ذلك باستخدام 3 طرق مختلفة. يمكننا أولًا تحميل البيانات من عقدة DOM.

$xmlElement = simplexml_import_dom($domNode);

الخيار الثاني لدينا هو تحميل البيانات من ملف XML.

$xmlElement = simplexml_load_file($filename);

أخيرًا يمكننا تحميل البيانات من متغير.

$xmlString = '<?xml version="1.0" encoding="UTF-8"?>
<document>
    <book>
        <bookName>StackOverflow SimpleXML Example</bookName>
        <bookAuthor>PHP Programmer</bookAuthor>
    </book>
    <book>
        <bookName>Another SimpleXML Example</bookName>
        <bookAuthor>Stack Overflow Community</bookAuthor>
        <bookAuthor>PHP Programmer</bookAuthor>
        <bookAuthor>FooBar</bookAuthor>
    </book>
</document>';
$xmlElement = simplexml_load_string($xmlString);

سواء اخترنا التحميل من عنصر DOM أو من ملف أو من سلسلة نصية ستحصل على متغير من الصنف SimpleXMLElement يدعى ‎$xmlElement ويمكننا الآن البدء باستخدام XML في PHP.

الوصول إلى بيانات SimpleXML

أسهل طريقة للوصول إلى البيانات في عنصر SimpleXMLElement هي استدعاء الخاصيّات مباشرةً، يمكننا الوصول إلى bookName كما يلي:

echo $xmlElement->book->bookName;

تفترض SimpleXML عند هذه النقطة أننا نريد الكتاب الأول لأننا لم نذكر بشكلٍ صريح أي كتاب نريده، ويمكننا اختيار كتاب غير الكتاب الأول بالطريقة التالية:

echo $xmlElement->book[1]->bookName;

ويجب ملاحظة أنّ استخدام [0] يكافئ عدم استخدام شيء أي أنّ:

$xmlElement->book

تعمل نفس عمل:

$xmlElement->book[0]

تمرير حلقة عبر XML

يوجد عدة أسباب لرغبتك في تكرار حلقة عبر XML مثل أن يكون عندك عدد كبير من العناصر، الكتب في مثالنا، تريد عرضه في صفحة ويب، عندها يمكنك استخدام حلقة foreach أوحلقة for المعيارية مستفيدين من إيجابيات دالة SimpleXMLElement للعد.

foreach ( $xmlElement->book as $thisBook ) {
    echo $thisBook->bookName
}

أو

$count = $xmlElement->count();
for ( $i=0; $i<$count; $i++ ) {
    echo $xmlElement->book[$i]->bookName;
}

معالجة الأخطاء

وصلنا الآن إلى النهاية ومن المهم أن ندرك أننا نخطئ كبشر ومن المحتمل أن نواجه أخطاء خاصةً إذا كنا نتعامل مع ملفات XML مختلفة لذا نريد معالجة هذه الأخطاء.

بفرض أننا أنشأنا ملف XML ستلاحظ إذا كان هذا الملف يشبه الملف الذي أنشأناه سابقًا ستكون فيه مشكلة وهي أنّ وسم الإغلاق ‎/doc‎ بدلًا من ‎/document.

<?xml version="1.0" encoding="UTF-8"?>
<document>
    <book>
        <bookName>StackOverflow SimpleXML Example</bookName>
        <bookAuthor>PHP Programmer</bookAuthor>
    </book>
    <book>
        <bookName>Another SimpleXML Example</bookName>
        <bookAuthor>Stack Overflow Community</bookAuthor>
        <bookAuthor>PHP Programmer</bookAuthor>
        <bookAuthor>FooBar</bookAuthor>
    </book>
</doc>

بفرض أننا حمّلناه ضمن شيفرة PHP كمتغير ‎$file.

libxml_use_internal_errors(true);
$xmlElement = simplexml_load_file($file);
if ( $xmlElement === false ) {
    $errors = libxml_get_errors();
    foreach ( $errors as $thisError ) {
        switch ( $thisError->level ) {
            case LIBXML_ERR_FATAL:
                echo "FATAL ERROR: ";
                break;
            case LIBXML_ERR_ERROR:
                echo "Non Fatal Error: ";
                break;
            case LIBXML_ERR_WARNING:
                echo "Warning: ";
                break;
        }
        echo $thisError->code . PHP_EOL .
            'Message: ' . $thisError->message . PHP_EOL .
            'Line: ' . $thisError->line . PHP_EOL .
            'Column: ' . $thisError->column . PHP_EOL .
            'File: ' . $thisError->file;
    }
    libxml_clear_errors();
} else {
    echo 'Happy Days';
}

ستكون النتيجة:

FATAL ERROR: 76
Message: Opening and ending tag mismatch: document line 2 and doc
Line: 13
Column: 10
File: filepath/filename.xml

لكن بمجرد حل هذه المشكلة سنرى الخرج "Happy Days".

إنشاء ملف XML باستخدام الصنف XMLWriter

ننشئ نسخة كائن XMLWriter:

$xml = new XMLWriter();

ثم نفتح الملف الذي نريد الكتابة فيه فمثلًا إذا أردنا الكتابة في ‎/var/www/example.com/xml/output.xml نكتب:

$xml->openUri('file:///var/www/example.com/xml/output.xml');

نكتب الشيفرة التالية للبدء بالملف (إنشاء وسم بداية ملف XML):

$xml->startDocument('1.0', 'utf-8');

ستؤدي إلى الخرج:

<?xml version="1.0" encoding="UTF-8"?>

يمكننا الآن البدء بكتابة العناصر:

$xml->writeElement('foo', 'bar');

سينتج XML التالي:

<foo>bar</foo>

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

$xml->startElement('foo');
$xml->writeAttribute('bar', 'baz');
$xml->writeCdata('Lorem ipsum');
$xml->endElement();

سينتج الخرج التالي:

<foo bar="baz"><![CDATA[Lorem ipsum]]></foo>

قراءة ملف XML باستخدام SimpleXML

يمكنك تحليل XML من سلسلة نصية أو من ملف XML.

  • من سلسلة نصية:
$xml_obj = simplexml_load_string($string);
  • من ملف:
$xml_obj = simplexml_load_file('books.xml');

مثال عن التحليل:

بفرض لدينا ملف XML التالي:

<?xml version="1.0" encoding="UTF-8"?>
<books>
    <book>
        <name>PHP - An Introduction</name>
        <price>$5.95</price>
        <id>1</id>
    </book>
    <book>
        <name>PHP - Advanced</name>
        <price>$25.00</price>
        <id>2</id>
    </book>
</books>

هذا مثال شيفرة لتحليله:

$xml = simplexml_load_string($xml_string);
$books = $xml->book;
foreach ($books as $book) {
    $id = $book->id;
    $title = $book->name;
    $price = $book->price;
    print_r ("The title of the book $id is $title and it costs $price." . "\n");
}

سينتج عنها الخرج التالي:

The title of the book 1 is PHP - An Introduction and it costs $5.95.
The title of the book 2 is PHP - Advanced and it costs $25.00.

مكتبة SimpleXML

تحميل بيانات XML إلى simplexml

التحميل من سلسلة نصية

نستخدم simplexml_load_string لإنشاء SimpleXMLElement من سلسلة نصية:

$xmlString = "<?xml version='1.0' encoding='UTF-8'?>";
$xml = simplexml_load_string($xmlString) or die("Error: Cannot create object");

لاحظ أنّه يجب استخدام or وليس || لأنّ أولوية or أعلى من =، وستُنفَّذ الشيفرة بعد or فقط إذا كانت نتيجة ‎$xml هي false.

التحميل من ملف نستخدم simplexml_load_file لتحميل بيانات XML من ملف أو رابط:

$xml = simplexml_load_string("filePath.xml");
$xml = simplexml_load_string("https://example.com/doc.xml");

يمكن أن يكون الرابط من أي مخطط تدعمه PHP ?أو مُغلِّف مجرى مخصص.

تحليل HTML

تحليل HTML من سلسلة نصية

تنفّذ PHP محللًا متوافقًا مع DOM المستوى 2 مما يسمح لنا بالعمل مع HTML باستخدام التوابع المعروفة مثل getElementById()‎ أوappendChild()‎.

$html = '<html><body><span id="text">Hello, World!</span></body></html>';

$doc = new DOMDocument();
libxml_use_internal_errors(true);
$doc->loadHTML($html);

echo $doc->getElementById("text")->textContent;

الخرج:

Hello, World!

لاحظ أنّ PHP ستصدر تحذيرات بشأن مشاكل HTML التي قد تحدث، خاصةً إذا كنت تستورد جزء من ملف، لتجنب هذه التحذيرات نخبر مكتبة DOM ‏(libxml) أن تعالج أخطائها قبل استيراد HTML باستدعاء libxml_use_internal_errors()‎، ويمكنك بعدها استخدام libxml_get_errors()‎ لمعالجة الأخطاء عند الحاجة.

استخدام XPath

$html = '<html><body><span class="text">Hello, World!</span></body></html>';

$doc = new DOMDocument();
$doc->loadHTML($html);
$xpath = new DOMXPath($doc);
$span = $xpath->query("//span[@class='text']")->item(0);

echo $span->textContent;

الخرج:

Hello, World!

SimpleXML

SimpleXML هي مكتبة PHP توفر طريقة سهلة للتعامل مع ملفات XML (خاصةً القراءة والتكرار عبر بيانات XML)، القيد الوحيد هو أنّ ملف XML يجب أن يكون مُصاغ جيدًا (well-formed).

تحليل XML باستخدام المنهجية الإجرائية

// XML تحميل سلسلة نصية
$xmlstr = file_get_contents('library.xml');
$library = simplexml_load_string($xmlstr);

// XML تحميل ملف
$library = simplexml_load_file('library.xml');

لتحميل ملف XML يمكنك كتابة مسار الملف المحلي أو رابط صالح إذا كان الإعداد allow_url_fopen مضبوطًا إلى "On" في php.ini.

تحليل XML باستخدام المنهجية الكائنية التوجه

// XML تحميل سلسلة نصية
$xmlstr = file_get_contents('library.xml');
$library = new SimpleXMLElement($xmlstr);

// XML تحميل ملف
$library = new SimpleXMLElement('library.xml', NULL, true);

الوصول إلى الأبناء والخاصيّات

عندما تحلل SimpleXML ملف XML تحوّل كل عناصر XML فيه، أو العقد، إلى خاصيّات لكائن SimpleXMLElement ناتج، وتحوّل خاصيّات XML إلى مصفوفة ترابطية يمكن الوصول إليها من الخاصيّة التي تعود لها.

  • عندما تعرف أسمائهم:
$library = new SimpleXMLElement('library.xml', NULL, true);
foreach ($library->book as $book){
    echo $book['isbn'];
    echo $book->title;
    echo $book->author;
    echo $book->publisher;
}

العيب الأساسي لهذه المنهجية أنّه من الضروري معرفة اسم كل عنصر وخاصية في ملف XML.

  • عندما لا تعرف أسمائهم (أو لا تريد معرفتهم):
foreach ($library->children() as $child){
    echo $child->getName();

    // الحصول على خاصيّات هذا العنصر
    foreach ($child->attributes() as $attr){
        echo ' ' . $attr->getName() . ': ' . $attr;
    }

    // الحصول على الأبناء
    foreach ($child->children() as $subchild){
        echo ' ' . $subchild->getName() . ': ' . $subchild;
    }
}

ترجمة -وبتصرف- للفصول [JSON - XML - SimpleXML - Parsing HTML] من كتاب PHP Notes for Professionals book

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

إن فك ترميز سلسلة نصية بصيغة JSON باستخدام الدالة json_decode أسرع كثيرا في التنفيذ من فك ترميز سلسلة نصية بصيغة XML باستخدام الدالة SimpleXML وذلك نظرا لأن صيغة JSON تصف فقط تسلسل من النصوص المتداخلة دون الحاجة إلى توفير واجهة DOM أو تحليل للسمات. لذا إن كان الأداء عنصرا حرجا في تصميمك (كالحاجة للتعامل مع كم كبير من البيانات) فقد يكون من المفيد الاعتماد على صيغة JSON لتبادل البيانات بدلا من XML إن كان بإمكانك الاختيار فيما بينهما.

رابط هذا التعليق
شارك على الشبكات الإجتماعية



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...