في الشرح السابق أنشأنا أول Ability خاصة بنا في ووردبريس باسم
في هذا الشرح سنأخذ الـAbility نفسها ونفتح لها بابًا جديدًا: REST API. سنرى كيف نجعلها قابلة للاكتشاف من خارج الكود، وكيف نستدعيها عبر HTTP، ولماذا لا يكفي مجرد تفعيل REST للسماح لأي شخص بتشغيلها.
والأهم أننا لن ننشئ مثالًا جديدًا، بل سنكمل تطوير
iinkor/get-latest-posts، وكانت مهمتها جلب أحدث المقالات المنشورة مع إمكانية تحديد عدد النتائج المطلوبة. لكننا استخدمناها حتى الآن من داخل PHP فقط، وهذا لا يُظهر كامل الفائدة التي تقدمها WordPress Abilities API.في هذا الشرح سنأخذ الـAbility نفسها ونفتح لها بابًا جديدًا: REST API. سنرى كيف نجعلها قابلة للاكتشاف من خارج الكود، وكيف نستدعيها عبر HTTP، ولماذا لا يكفي مجرد تفعيل REST للسماح لأي شخص بتشغيلها.
والأهم أننا لن ننشئ مثالًا جديدًا، بل سنكمل تطوير
iinkor/get-latest-posts التي بنيناها في المقال السابق.من Ability داخلية إلى وظيفة يمكن الوصول إليها عبر HTTP
عندما سجلنا iinkor/get-latest-posts في الشرح السابق، أصبحت معروفة داخل سجل Abilities في ووردبريس، ولذلك استطعنا الحصول عليها باستخدام wp_get_ability() ثم تنفيذها من PHP.
لكن هذا لا يعني أن تطبيقًا خارجيًا يستطيع الوصول إليها تلقائيًا.
وهذا سلوك مقصود لأسباب أمنية. فالـAbilities المسجلة لا تظهر افتراضيًا عبر REST API، حتى لو كانت تعمل بصورة طبيعية داخل PHP. يجب على المطور أن يقرر صراحة أن الوظيفة مناسبة للاستخدام من خلال هذه القناة.
في WordPress 6.9 و7.0 يتم ذلك باستخدام
وهذا التغيير مهم، لأن ووردبريس بدأ يفصل بين سؤالين: هل هذه الوظيفة عامة للعملاء الخارجيين؟ وعبر أي قناة نريد إتاحتها؟
لكن هذا لا يعني أن تطبيقًا خارجيًا يستطيع الوصول إليها تلقائيًا.
وهذا سلوك مقصود لأسباب أمنية. فالـAbilities المسجلة لا تظهر افتراضيًا عبر REST API، حتى لو كانت تعمل بصورة طبيعية داخل PHP. يجب على المطور أن يقرر صراحة أن الوظيفة مناسبة للاستخدام من خلال هذه القناة.
في WordPress 6.9 و7.0 يتم ذلك باستخدام
show_in_rest. أما ابتداءً من WordPress 7.1، فقد أضيف أيضًا الخيار الأعلى مستوى public للإشارة إلى أن الـAbility مصممة أصلًا لتكون متاحة لعملاء خارجيين مثل REST API وMCP ووكلاء الذكاء الاصطناعي.وهذا التغيير مهم، لأن ووردبريس بدأ يفصل بين سؤالين: هل هذه الوظيفة عامة للعملاء الخارجيين؟ وعبر أي قناة نريد إتاحتها؟
إتاحة الـAbility عبر REST API
لنعد إلى تعريف
إذا كان الموقع يعمل على WordPress 7.1 أو أحدث، يمكن تعريف الـAbility كوظيفة عامة وإتاحتها عبر REST بهذا الشكل:
أما إذا كنت تعمل على WordPress 6.9 أو 7.0، فيكفي استخدام:
وبمجرد تفعيل
iinkor/get-latest-posts من المقال السابق. كان جزء meta لدينا على الشكل التالي:
PHP:
'meta' => array(
'annotations' => array(
'readonly' => true,
),
),
إذا كان الموقع يعمل على WordPress 7.1 أو أحدث، يمكن تعريف الـAbility كوظيفة عامة وإتاحتها عبر REST بهذا الشكل:
PHP:
'meta' => array(
'public' => true,
'show_in_rest' => true,
'annotations' => array(
'readonly' => true,
),
),
public تعني أن الوظيفة مخصصة للاستخدام بواسطة العملاء الخارجيين، بينما show_in_rest تحدد صراحة أننا نريد إتاحتها عبر REST API. وفي حالتنا يمكن الاعتماد على public => true ليكون أساس الإتاحة، لكن كتابة show_in_rest => true صراحة تجعل نية الكود أوضح، وخصوصًا في شرح يركز على REST.أما إذا كنت تعمل على WordPress 6.9 أو 7.0، فيكفي استخدام:
كود:
'meta' => array(
'show_in_rest' => true,
'annotations' => array(
'readonly' => true,
),
),
وبمجرد تفعيل
show_in_rest تصبح الـAbility جزءًا من واجهات Abilities REST API، مع بقاء قواعد المصادقة والصلاحيات سارية.أين توجد Abilities REST API؟
خصص ووردبريس Namespace مستقلًا لهذه الواجهة:
إذا أردنا مثلًا الحصول على قائمة الـAbilities المتاحة عبر REST، نستخدم:
الفائدة هنا لا تقتصر على تشغيل الوظيفة. النظام الخارجي يستطيع أولًا اكتشافها وقراءة تعريفها؛ أي معرفة اسمها ووصفها وتصنيفها و
وهنا يبدأ السبب الذي جعلنا نقضي وقتًا في وصف الـAbility في المقال السابق بالظهور بصورة عملية. هذه المعلومات ليست توثيقًا للمطور فقط، بل عقدًا تستطيع البرامج الأخرى قراءته وفهمه.
/wp-json/wp-abilities/v1/ ومن خلاله نستطيع اكتشاف التصنيفات والـAbilities المسجلة والوصول إلى معلوماتها وتشغيل الوظائف التي يسمح المطور بإتاحتها.إذا أردنا مثلًا الحصول على قائمة الـAbilities المتاحة عبر REST، نستخدم:
GET /wp-json/wp-abilities/v1/abilities. أما للحصول على معلومات Ability معينة، فيمكن استخدام مسارها داخل الواجهة.الفائدة هنا لا تقتصر على تشغيل الوظيفة. النظام الخارجي يستطيع أولًا اكتشافها وقراءة تعريفها؛ أي معرفة اسمها ووصفها وتصنيفها و
input_schema وoutput_schema والمعلومات الأخرى التي سجلناها سابقًا.وهنا يبدأ السبب الذي جعلنا نقضي وقتًا في وصف الـAbility في المقال السابق بالظهور بصورة عملية. هذه المعلومات ليست توثيقًا للمطور فقط، بل عقدًا تستطيع البرامج الأخرى قراءته وفهمه.
تشغيل iinkor/get-latest-posts عبر REST
توفر Abilities API مسارًا مخصصًا لتشغيل كل Ability. وبالنسبة لوظيفتنا يصبح المسار:
في المقال السابق وصفنا الوظيفة بأنها:
وبما أن الوظيفة تحتاج إلى
الفكرة قبل ترميزها هي:
وبذلك نطلب من الـAbility تنفيذ المهمة وإرجاع أحدث ثلاثة مقالات.
إذا نجحت العملية، ستعيد الواجهة نتيجة JSON تحتوي على البيانات التي ترجعها
الاختلاف هنا أن من طلب التنفيذ لم يعد كودًا يعمل داخل ووردبريس، بل عميلًا يتحدث مع الموقع عبر HTTP.
/wp-json/wp-abilities/v1/iinkor/get-latest-posts/run لكن هناك تفصيل مهم يتعلق بطريقة HTTP المستخدمة.في المقال السابق وصفنا الوظيفة بأنها:
'readonly' => true, وهذا يعني أنها تقرأ البيانات فقط ولا تغير حالة الموقع. لذلك تستخدم Abilities API طلب GET عند تشغيلها عبر REST.وبما أن الوظيفة تحتاج إلى
Input يحتوي على عدد المقالات، نمرر هذا المدخل في Query Parameter باسم input على هيئة JSON مشفر داخل الرابط.الفكرة قبل ترميزها هي:
كود:
{
"number": 3
}
وبذلك نطلب من الـAbility تنفيذ المهمة وإرجاع أحدث ثلاثة مقالات.
إذا نجحت العملية، ستعيد الواجهة نتيجة JSON تحتوي على البيانات التي ترجعها
execute_callback، أي البيانات نفسها التي حصلنا عليها عندما شغلنا الوظيفة من PHP.الاختلاف هنا أن من طلب التنفيذ لم يعد كودًا يعمل داخل ووردبريس، بل عميلًا يتحدث مع الموقع عبر HTTP.
لماذا استخدم ووردبريس GET وليس POST؟
اختيار HTTP Method ليس عشوائيًا في Abilities API، وإنما يرتبط بطبيعة الوظيفة التي وصفناها في
وظيفتنا
هذه التفاصيل تصبح مهمة عندما نبني تكاملات حقيقية، لأنها تجعل طبيعة العملية مفهومة من طريقة التعامل معها، بدل استخدام POST لكل شيء دون تمييز.
كما توضح لماذا يجب أن تكون الـannotations دقيقة. إذا وصف المطور Ability تغير البيانات بأنها
annotations.وظيفتنا
readonly، ولذلك تستخدم GET لأنها لا تغير شيئًا في الموقع. أما الوظائف التي تجري تغييرات فتستخدم طرقًا مناسبة لطبيعة العملية، والوظائف الموصوفة بأنها destructive يمكن أن تستخدم DELETE.هذه التفاصيل تصبح مهمة عندما نبني تكاملات حقيقية، لأنها تجعل طبيعة العملية مفهومة من طريقة التعامل معها، بدل استخدام POST لكل شيء دون تمييز.
كما توضح لماذا يجب أن تكون الـannotations دقيقة. إذا وصف المطور Ability تغير البيانات بأنها
readonly لمجرد الحصول على GET، فهو لا يقدم معلومة خاطئة للتوثيق فقط، بل يكسر المعنى الذي تعتمد عليه الأدوات عند التعامل مع الوظيفة.تفعيل REST لا يعني أن الـAbility أصبحت مفتوحة للجميع
هذه أهم نقطة أمنية في المقال. قد يبدو أن إضافة:
الوصول إلى واجهات Abilities عبر REST يتطلب مستخدمًا موثقًا، وبعد المصادقة لا يزال ووردبريس يطبق
في مثالنا السابق استخدمنا:
لذلك يمر الطلب بمرحلتين منفصلتين: يتأكد ووردبريس أولًا من هوية المستخدم، ثم يتحقق مما إذا كان هذا المستخدم يمتلك الصلاحية التي اشترطناها لتنفيذ الـAbility.
هذا الفصل مهم جدًا. Authentication تجيب عن سؤال «من أنت؟»، بينما Authorization تجيب عن سؤال «هل يسمح لك بتنفيذ هذه العملية؟».
ولهذا لا ينبغي أبدًا اعتبار
'show_in_rest' => true, تجعل الوظيفة متاحة لأي شخص يعرف الرابط، لكن Abilities REST API لا تعمل بهذه الطريقة.الوصول إلى واجهات Abilities عبر REST يتطلب مستخدمًا موثقًا، وبعد المصادقة لا يزال ووردبريس يطبق
permission_callback الخاصة بكل Ability قبل السماح بتنفيذها.في مثالنا السابق استخدمنا:
PHP:
'permission_callback' => function () { return current_user_can( 'read' ); },
لذلك يمر الطلب بمرحلتين منفصلتين: يتأكد ووردبريس أولًا من هوية المستخدم، ثم يتحقق مما إذا كان هذا المستخدم يمتلك الصلاحية التي اشترطناها لتنفيذ الـAbility.
هذا الفصل مهم جدًا. Authentication تجيب عن سؤال «من أنت؟»، بينما Authorization تجيب عن سؤال «هل يسمح لك بتنفيذ هذه العملية؟».
ولهذا لا ينبغي أبدًا اعتبار
show_in_rest بديلًا عن تصميم صلاحيات صحيحة.تجربة الطلب باستخدام Application Password
إذا أردنا استدعاء الـAbility من تطبيق خارجي، فنحتاج إلى طريقة مصادقة يدعمها WordPress REST API. إحدى الطرق المناسبة للاختبارات والتكاملات هي Application Passwords.
يوفر ووردبريس Application Password منفصلة يمكن لتطبيق خارجي استخدامها للمصادقة دون إعطائه كلمة المرور الأساسية لحساب المستخدم.
بعد إنشاء Application Password يمكن، على سبيل المثال، إرسال طلب باستخدام
سيكون الشكل العام للطلب:
استبدل
القيمة المشفرة في نهاية الرابط تمثل:
ولا ينبغي وضع بيانات المصادقة داخل أكواد JavaScript عامة أو مستودعات Git أو أي مكان يستطيع الزوار الوصول إليه. Application Password تعامل باعتبارها بيانات اعتماد سرية.
يوفر ووردبريس Application Password منفصلة يمكن لتطبيق خارجي استخدامها للمصادقة دون إعطائه كلمة المرور الأساسية لحساب المستخدم.
بعد إنشاء Application Password يمكن، على سبيل المثال، إرسال طلب باستخدام
curl مع بيانات المصادقة والمسار الخاص بالـAbility.سيكون الشكل العام للطلب:
كود:
curl --user "USERNAME:APPLICATION_PASSWORD" \
"https://example.com/wp-json/wp-abilities/v1/iinkor/get-latest-posts/run?input=%7B%22number%22%3A3%7D"
استبدل
USERNAME باسم المستخدم، وAPPLICATION_PASSWORD بكلمة مرور التطبيق، وexample.com بعنوان موقعك.القيمة المشفرة في نهاية الرابط تمثل:
{"number":3} إذا كان المستخدم يمتلك الصلاحية المطلوبة وكانت الـAbility مسجلة ومكشوفة عبر REST بصورة صحيحة، سيقوم ووردبريس بتنفيذ iinkor/get-latest-posts وإعادة النتيجة.ولا ينبغي وضع بيانات المصادقة داخل أكواد JavaScript عامة أو مستودعات Git أو أي مكان يستطيع الزوار الوصول إليه. Application Password تعامل باعتبارها بيانات اعتماد سرية.
ماذا يحدث إذا لم نستخدم show_in_rest؟
هذه تجربة مفيدة لفهم التصميم. إذا أبقينا الـAbility مسجلة، لكن جعلنا:
لكن محاولة الوصول إليها عبر Abilities REST API ستفشل، لأننا أخبرنا ووردبريس صراحة أنها ليست مخصصة لهذه القناة. وهكذا نستطيع امتلاك Abilities داخلية تستخدمها الإضافة نفسها، وأخرى عامة للتكاملات الخارجية، بدل فتح جميع وظائف الموقع تلقائيًا.
'show_in_rest' => false, فلن تصبح الوظيفة عديمة الفائدة. ستظل متاحة داخل PHP، ويمكننا الاستمرار في استخدام: $ability = wp_get_ability( 'iinkor/get-latest-posts' ); ثم تنفيذها بالطريقة التي استخدمناها في المقال السابق.لكن محاولة الوصول إليها عبر Abilities REST API ستفشل، لأننا أخبرنا ووردبريس صراحة أنها ليست مخصصة لهذه القناة. وهكذا نستطيع امتلاك Abilities داخلية تستخدمها الإضافة نفسها، وأخرى عامة للتكاملات الخارجية، بدل فتح جميع وظائف الموقع تلقائيًا.
الكود بعد إضافة REST API
لا نحتاج إلى إعادة بناء الـAbility. التعديل الأساسي على مثال المقال السابق يقع في
على WordPress 7.1 أو أحدث يصبح الجزء المهم:
وبقية تعريف
وهذه إحدى نقاط القوة في التصميم: منطق الوظيفة لم يتغير.
meta.على WordPress 7.1 أو أحدث يصبح الجزء المهم:
PHP:
'meta' => array(
'public' => true,
'show_in_rest' => true,
'annotations' => array(
'readonly' => true,
),
),
وبقية تعريف
input_schema وoutput_schema وexecute_callback وpermission_callback تبقى كما بنيناها سابقًا.وهذه إحدى نقاط القوة في التصميم: منطق الوظيفة لم يتغير.
iinkor_get_latest_posts() لا تحتاج إلى معرفة ما إذا كان من يشغلها PHP أو REST. نحن سجلنا الوظيفة مرة واحدة، ثم اخترنا القنوات التي يمكن استخدامها من خلالها.ما الذي تغير فعليًا بعد هذا الشرح؟
في المقال السابق كان لدينا كود يستطيع ووردبريس تشغيله كـAbility. الآن أصبحت الوظيفة نفسها قابلة للاكتشاف والتنفيذ بواسطة عميل خارجي عبر HTTP، دون إنشاء REST Route مخصص لها يدويًا.
لكن الأهم أننا لم نتخل عن بنية الأمان. لا تزال هناك مصادقة، ولا تزال
وهذا يقربنا خطوة أخرى من السبب الذي يجعل Abilities API مهمة: نستطيع تعريف وظيفة مرة واحدة، ثم جعل أنظمة مختلفة تكتشفها وتستخدمها وفق قواعد واضحة.
في المقال التالي سننتقل إلى جانب آخر مهم: كيفية اكتشاف واستخدام Server-Side Abilities من JavaScript باستخدام
وسنستمر باستخدام
لكن الأهم أننا لم نتخل عن بنية الأمان. لا تزال هناك مصادقة، ولا تزال
permission_callback تتحكم بمن يستطيع تنفيذ الوظيفة، ولا تزال الـschemas تصف البيانات التي تدخل وتخرج.وهذا يقربنا خطوة أخرى من السبب الذي يجعل Abilities API مهمة: نستطيع تعريف وظيفة مرة واحدة، ثم جعل أنظمة مختلفة تكتشفها وتستخدمها وفق قواعد واضحة.
في المقال التالي سننتقل إلى جانب آخر مهم: كيفية اكتشاف واستخدام Server-Side Abilities من JavaScript باستخدام
@wordpress/core-abilities، وهي الخطوة التي أصبحت أكثر أهمية بعد إضافة Client-Side Abilities API إلى WordPress 7.0.وسنستمر باستخدام
iinkor/get-latest-posts نفسها، حتى تصبح لدينا في نهاية السلسلة Ability واحدة تابعنا رحلتها كاملة من PHP إلى REST ثم JavaScript، وبعد ذلك إلى التكامل مع الذكاء الاصطناعي.
