WordPress بلا رأس: الصعود والهبوط في إنشاء WordPress المنفصل

نشرت: 2022-03-10
ملخص سريع ↬ يعلم الجميع أنه إذا كان موقع الويب بطيئًا ، فسيتخلى المستخدمون عنه. تظهر العديد من الدراسات العلاقة بين أداء موقع الويب ومعدلات التحويل. في هذه المقالة ، يشارك Denis Zoljom خبرته وأساسيات إنشاء WordPress المنفصل.

قطع WordPress شوطًا طويلاً منذ بدايته كأداة بسيطة لكتابة المدونات. بعد مرور 15 عامًا طويلة ، أصبح الخيار الأول لنظام إدارة المحتوى للمطورين وغير المطورين على حدٍ سواء. يشغل WordPress الآن ما يقرب من 30٪ من أفضل 10 ملايين موقع على الويب.

منذ أن تم تجميع REST API في نواة WordPress ، يمكن للمطورين تجربتها واستخدامها بطريقة منفصلة ، أي كتابة الجزء الأمامي باستخدام أطر عمل أو مكتبات JavaScript. في Infinum ، كنا (وما زلنا) نستخدم WordPress بطريقة "كلاسيكية": PHP للواجهة الأمامية وكذلك الخلفية. بعد فترة ، أردنا تجربة النهج المنفصل. في هذه المقالة ، سأشارك لمحة عامة عما أردنا تحقيقه وما واجهناه أثناء محاولتنا تنفيذ أهدافنا.

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

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

يوصى بالقراءة : كيفية استخدام خرائط التمثيل اللوني لتتبع النقرات على موقع WordPress الخاص بك

إذا كانت أوقات استجابة موقع الويب الطويلة تجعلك مستيقظًا طوال الليل ، فهذه طريقة مفيدة لك. سأغطي أساسيات إنشاء WordPress منفصل وبعض الدروس المستفادة ، بما في ذلك:

  1. معنى "فصل WordPress"
  2. العمل مع WordPress REST API الافتراضي
  3. تحسين الأداء باستخدام نهج JSON المنفصل
  4. مخاوف أمنية
المزيد بعد القفز! أكمل القراءة أدناه ↓

إذن ، ما هو WordPress المنفصل بالضبط؟

عندما يتعلق الأمر بكيفية برمجة WordPress ، هناك شيء واحد مؤكد: إنه لا يتبع نمط تصميم M odel- V iew- C ontroller (MVC) الذي يعرفه العديد من المطورين. نظرًا لتاريخها وكونها نوعًا من مفترق لمنصة تدوين قديمة تسمى "b2" (مزيد من التفاصيل هنا) ، فهي مكتوبة إلى حد كبير بطريقة إجرائية (باستخدام رمز قائم على الوظيفة). استخدم مطورو WordPress الأساسيون نظامًا من الخطافات يسمح للمطورين الآخرين بتعديل أو توسيع وظائف معينة.

إنه نظام الكل في واحد ومجهز بواجهة إدارة عاملة ؛ يدير اتصال قاعدة البيانات ، ويحتوي على مجموعة من واجهات برمجة التطبيقات المفيدة المكشوفة التي تتعامل مع مصادقة المستخدم والتوجيه والمزيد.

ولكن بفضل واجهة برمجة تطبيقات REST ، يمكنك فصل الواجهة الخلفية لـ WordPress كنوع من النموذج ووحدة التحكم المجمعة معًا للتعامل مع معالجة البيانات وتفاعل قاعدة البيانات ، واستخدام REST API Controller للتفاعل مع طبقة عرض منفصلة باستخدام نقاط نهاية API المختلفة. بالإضافة إلى فصل MVC ، يمكننا (لأسباب أمنية أو تحسينات السرعة) وضع تطبيق JS على خادم منفصل كما في المخطط أدناه:

صورة تصور مخطط WordPress مفصولاً مع فصل PHP وجزء JS
مخطط WordPress منفصل. (معاينة كبيرة)

مزايا استخدام النهج المنفصل

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

من خلال فصل الواجهة الأمامية عن الواجهة الخلفية ، يكون من الأسهل إعادة تصميمها في المستقبل ، دون تغيير نظام إدارة المحتوى. أيضًا ، يحتاج مطورو الواجهة الأمامية فقط إلى الاهتمام بما يجب فعله بالبيانات التي توفرها لهم الواجهة الخلفية. يتيح لهم ذلك الإبداع واستخدام المكتبات الحديثة مثل ReactJS أو Vue أو Angular لتقديم تطبيقات ويب ديناميكية للغاية. على سبيل المثال ، من الأسهل إنشاء تطبيق ويب تقدمي عند استخدام المكتبات المذكورة أعلاه.

تنعكس ميزة أخرى في أمان الموقع. يصبح استغلال موقع الويب من خلال الواجهة الخلفية أكثر صعوبة لأنه مخفي إلى حد كبير عن الجمهور.

يوصى بقراءة : أمن ووردبريس كعملية

أوجه القصور في استخدام النهج المنفصل

أولاً ، وجود WordPress منفصل يعني الحفاظ على حالتين منفصلتين:

  1. WordPress للخلفية ؛
  2. تطبيق أمامي منفصل ، بما في ذلك تحديثات الأمان في الوقت المناسب.

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

ثالثًا ، بفصل الواجهة الأمامية ، فإنك تفقد قوة محرر WYSIWYG ، ولا يعمل زر "Live Preview" في WordPress أيضًا.

العمل مع WordPress REST API

قبل أن نتعمق في الكود ، هناك بضعة أشياء أخرى حول WordPress REST API. جاءت القوة الكاملة لـ REST API في WordPress مع الإصدار 4.7 في 6 ديسمبر 2016.

ما يسمح لك WordPress REST API القيام به هو التفاعل مع تثبيت WordPress الخاص بك عن بعد عن طريق إرسال واستقبال كائنات JSON.

انشاء مشروع

نظرًا لأنه يأتي مرفقًا مع أحدث تثبيت لـ WordPress ، سنعمل على موضوع Twenty Seventeen. أنا أعمل على Varying Vagrant Vagrants ، وقمت بإعداد موقع اختبار بعنوان URL https://dev.wordpress.test/ . سيتم استخدام عنوان URL هذا في جميع أنحاء المقالة. سنقوم أيضًا باستيراد المنشورات من مستودع Wordpress.org Theme Review Teams بحيث يكون لدينا بعض بيانات الاختبار للعمل معها. لكن أولاً ، سوف نتعرف على العمل مع نقاط النهاية الافتراضية ، ثم سننشئ نقطة النهاية المخصصة الخاصة بنا.

الوصول إلى نقطة نهاية REST الافتراضية

كما ذكرنا سابقًا ، يأتي WordPress بالعديد من نقاط النهاية المضمنة التي يمكنك فحصها بالانتقال إلى المسار /wp-json/ :

 https://dev.wordpress.test/wp-json/

إما عن طريق وضع عنوان URL هذا مباشرة في متصفحك ، أو إضافته في تطبيق ساعي البريد ، ستحصل على استجابة JSON من WordPress REST API التي تبدو كالتالي:

 { "name": "Test dev site", "description": "Just another WordPress site", "url": "https://dev.wordpress.test", "home": "https://dev.wordpress.test", "gmt_offset": "0", "timezone_string": "", "namespaces": [ "oembed/1.0", "wp/v2" ], "authentication": [], "routes": { "/": { "namespace": "", "methods": [ "GET" ], "endpoints": [ { "methods": [ "GET" ], "args": { "context": { "required": false, "default": "view" } } } ], "_links": { "self": "https://dev.wordpress.test/wp-json/" } }, "/oembed/1.0": { "namespace": "oembed/1.0", "methods": [ "GET" ], "endpoints": [ { "methods": [ "GET" ], "args": { "namespace": { "required": false, "default": "oembed/1.0" }, "context": { "required": false, "default": "view" } } } ], "_links": { "self": "https://dev.wordpress.test/wp-json/oembed/1.0" } }, ... "wp/v2": { ...

لذلك من أجل الحصول على جميع المنشورات في موقعنا باستخدام REST ، سنحتاج إلى الانتقال إلى https://dev.wordpress.test/wp-json/wp/v2/posts . لاحظ أن wp/v2/ يحدد نقاط النهاية الأساسية المحجوزة مثل المنشورات والصفحات والوسائط والتصنيفات والفئات وما إلى ذلك.

إذن ، كيف نضيف نقطة نهاية مخصصة؟

قم بإنشاء نقطة نهاية REST مخصصة

لنفترض أننا نريد إضافة نقطة نهاية جديدة أو حقل إضافي إلى نقطة النهاية الحالية. هناك عدة طرق يمكننا القيام بذلك. أولاً ، يمكن القيام بذلك تلقائيًا عند إنشاء نوع منشور مخصص. على سبيل المثال ، نريد إنشاء نقطة نهاية للوثائق. لنقم بإنشاء مكون إضافي تجريبي صغير. أنشئ مجلد توثيق اختبار في مجلد wp-content / plugins ، وأضف ملف documents.php الذي يبدو كالتالي:

 <?php /** * Test plugin * * @since 1.0.0 * @package test_plugin * * @wordpress-plugin * Plugin Name: Test Documentation Plugin * Plugin URI: * Description: The test plugin that adds rest functionality * Version: 1.0.0 * Author: Infinum <[email protected]> * Author URI: https://infinum.co/ * License: GPL-2.0+ * License URI: https://www.gnu.org/licenses/gpl-2.0.txt * Text Domain: test-plugin */ namespace Test_Plugin; // If this file is called directly, abort. if ( ! defined( 'WPINC' ) ) { die; } /** * Class that holds all the necessary functionality for the * documentation custom post type * * @since 1.0.0 */ class Documentation { /** * The custom post type slug * * @var string * * @since 1.0.0 */ const PLUGIN_NAME = 'documentation-plugin'; /** * The custom post type slug * * @var string * * @since 1.0.0 */ const POST_TYPE_SLUG = 'documentation'; /** * The custom taxonomy type slug * * @var string * * @since 1.0.0 */ const TAXONOMY_SLUG = 'documentation-category'; /** * Register custom post type * * @since 1.0.0 */ public function register_post_type() { $args = array( 'label' => esc_html( 'Documentation', 'test-plugin' ), 'public' => true, 'menu_position' => 47, 'menu_icon' => 'dashicons-book', 'supports' => array( 'title', 'editor', 'revisions', 'thumbnail' ), 'has_archive' => false, 'show_in_rest' => true, 'publicly_queryable' => false, ); register_post_type( self::POST_TYPE_SLUG, $args ); } /** * Register custom tag taxonomy * * @since 1.0.0 */ public function register_taxonomy() { $args = array( 'hierarchical' => false, 'label' => esc_html( 'Documentation tags', 'test-plugin' ), 'show_ui' => true, 'show_admin_column' => true, 'update_count_callback' => '_update_post_term_count', 'show_in_rest' => true, 'query_var' => true, ); register_taxonomy( self::TAXONOMY_SLUG, [ self::POST_TYPE_SLUG ], $args ); } } $documentation = new Documentation(); add_action( 'init', [ $documentation, 'register_post_type' ] ); add_action( 'init', [ $documentation, 'register_taxonomy' ] );

من خلال تسجيل نوع المنشور الجديد والتصنيف ، وتعيين الوسيطة show_in_rest إلى true ، أنشأ WordPress تلقائيًا مسار REST في /wp/v2/ namespace. لديك الآن https://dev.wordpress.test/wp-json/wp/v2/documentation و https://dev.wordpress.test/wp-json/wp/v2/documentation-category endpoints متاحة. إذا أضفنا منشورًا في منشورنا المخصص للوثائق المنشأة حديثًا بالانتقال إلى https://dev.wordpress.test/?post_type=documentation ، فسوف يعطينا إجابة تبدو كالتالي:

 [ { "id": 4, "date": "2018-06-11T19:48:51", "date_gmt": "2018-06-11T19:48:51", "guid": { "rendered": "https://dev.wordpress.test/?post_type=documentation&p=4" }, "modified": "2018-06-11T19:48:51", "modified_gmt": "2018-06-11T19:48:51", "slug": "test-documentation", "status": "publish", "type": "documentation", "link": "https://dev.wordpress.test/documentation/test-documentation/", "title": { "rendered": "Test documentation" }, "content": { "rendered": "

هذا هو بعض محتوى التوثيق

\ن"، "محمية": خطأ } ، "الوسائط المميزة": 0 ، "قالب": ""، "فئة الوثائق": [ 2 ] ، "روابط _": { "الذات": [ { "href": "https: //dev.wordpress.test/wp-json/wp/v2/documentation/4" } ] ، "مجموعة": [ { "href": "https: //dev.wordpress.test/wp-json/wp/v2/documentation" } ] ، "حول": [ { "href": "https: //dev.wordpress.test/wp-json/wp/v2/types/documentation" } ] ، "تاريخ النسخة": [ { "href": "https: //dev.wordpress.test/wp-json/wp/v2/documentation/4/revisions" } ] ، "wp: مرفق": [ { "href": "https: //dev.wordpress.test/wp-json/wp/v2/media؟ parent = 4" } ] ، "wp: term": [ { "التصنيف": "فئة الوثائق" ، "قابل للتضمين": صحيح ، "href": "https: //dev.wordpress.test/wp-json/wp/v2/documentation-category؟ post = 4" } ] ، "كوري": [ { "الاسم": "wp"، "href": "https://api.w.org/{rel}" ، "قالب": صحيح } ] } } ]

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

 /** * Create a custom endpoint * * @since 1.0.0 */ public function create_custom_documentation_endpoint() { register_rest_route( self::PLUGIN_NAME . '/v1', '/custom-documentation', array( 'methods' => 'GET', 'callback' => [ $this, 'get_custom_documentation' ], ) ); } /** * Create a callback for the custom documentation endpoint * * @return string JSON that indicates success/failure of the update, * or JSON that indicates an error occurred. * @since 1.0.0 */ public function get_custom_documentation() { /* Some permission checks can be added here. */ // Return only documentation name and tag name. $doc_args = array( 'post_type' => self::POST_TYPE_SLUG, 'post_status' => 'publish', 'perm' => 'readable' ); $query = new \WP_Query( $doc_args ); $response = []; $counter = 0; // The Loop if ( $query->have_posts() ) { while ( $query->have_posts() ) { $query->the_post(); $post_id = get_the_ID(); $post_tags = get_the_terms( $post_id, self::TAXONOMY_SLUG ); $response[ $counter ]['title'] = get_the_title(); foreach ( $post_tags as $tags_key => $tags_value ) { $response[ $counter ]['tags'][] = $tags_value->name; } $counter++; } } else { $response = esc_html__( 'There are no posts.', 'documentation-plugin' ); } /* Restore original Post Data */ wp_reset_postdata(); return rest_ensure_response( $response ); }

وربط طريقة create_custom_documentation_endpoint() rest_api_init ، مثل:

 add_action( 'rest_api_init', [ $documentation, 'create_custom_documentation_endpoint' ] );

سيضيف هذا مسارًا مخصصًا في https://dev.wordpress.test/wp-json/documentation-plugin/v1/custom-documentation مع رد الاتصال الذي يعيد الاستجابة لهذا المسار.

 [{ "title": "Another test documentation", "tags": ["Another tag"] }, { "title": "Test documentation", "tags": ["REST API", "test tag"] }]

هناك الكثير من الأشياء الأخرى التي يمكنك القيام بها باستخدام REST API (يمكنك العثور على مزيد من التفاصيل في كتيب REST API).

العمل حول أوقات الاستجابة الطويلة عند استخدام واجهة برمجة تطبيقات REST الافتراضية

بالنسبة لأي شخص حاول إنشاء موقع WordPress منفصل ، فهذا ليس شيئًا جديدًا - REST API بطيئة.

لقد صادفت أنا وفريقي لأول مرة واجهة برمجة تطبيقات REST المتخلفة عن WordPress على موقع عميل (غير مفصول) ، حيث استخدمنا نقاط النهاية المخصصة للحصول على قائمة المواقع على خريطة Google ، جنبًا إلى جنب مع معلومات التعريف الأخرى التي تم إنشاؤها باستخدام Advanced Custom Fields Pro توصيل في. اتضح أن الوقت الذي يستغرقه البايت الأول (TTFB) - والذي يستخدم كمؤشر لاستجابة خادم الويب أو مصدر شبكة آخر - أكثر من 3 ثوانٍ.

بعد قليل من التحقيق ، أدركنا أن استدعاءات واجهة برمجة تطبيقات REST الافتراضية كانت بطيئة حقًا ، لا سيما عندما "أثقلنا" الموقع بمكونات إضافية. لذلك ، أجرينا اختبارًا صغيرًا. لقد قمنا بتثبيت اثنين من المكونات الإضافية الشائعة وواجهنا بعض النتائج المثيرة للاهتمام. أعطى تطبيق ساعي البريد وقت تحميل يبلغ 1.97 ثانية مقابل 41.9 كيلوبايت من حجم الاستجابة. كان وقت تحميل Chrome 1.25 ثانية (كان TTFB 1.25 ثانية ، وتم تنزيل المحتوى في 3.96 مللي ثانية). فقط لاسترداد قائمة بسيطة من الوظائف. لا يوجد تصنيف ولا بيانات مستخدم ولا حقول وصفية إضافية.

لماذا حدث هذا؟

اتضح أن الوصول إلى REST API على WordPress الافتراضي سيؤدي إلى تحميل نواة WordPress بالكامل لخدمة نقاط النهاية ، على الرغم من عدم استخدامها. أيضًا ، كلما زاد عدد الإضافات التي تضيفها ، ساءت الأمور. وحدة تحكم REST الافتراضية WP_REST_Controller هي فئة كبيرة حقًا تقوم بأكثر مما هو ضروري عند إنشاء صفحة ويب بسيطة. إنه يتعامل مع تسجيل المسارات ، والتحقق من الأذونات ، وإنشاء العناصر وحذفها ، وما إلى ذلك.

هناك نوعان من الحلول الشائعة لهذه المشكلة:

  1. اعتراض تحميل الملحقات ، ومنع تحميلها جميعًا عندما تحتاج إلى خدمة استجابة REST بسيطة ؛
  2. قم بتحميل الحد الأدنى من WordPress فقط وقم بتخزين البيانات في فترة مؤقتة ، والتي نقوم بعدها بإحضار البيانات باستخدام صفحة مخصصة.

تحسين الأداء باستخدام نهج JSON المنفصل

عندما تعمل مع مواقع عروض تقديمية بسيطة ، فلن تحتاج إلى جميع الوظائف التي توفرها لك REST API. بالطبع ، هذا هو المكان الذي يكون فيه التخطيط الجيد أمرًا بالغ الأهمية. لا تريد حقًا إنشاء موقعك بدون واجهة برمجة تطبيقات REST ، ثم تقول بعد سنوات أنك ترغب في الاتصال بموقعك ، أو ربما إنشاء تطبيق جوال يحتاج إلى استخدام وظيفة REST API. هل؟

لهذا السبب ، استخدمنا ميزتين في WordPress يمكن أن تساعدك عند تقديم بيانات JSON البسيطة:

  • العابر API للتخزين المؤقت ،
  • تحميل الحد الأدنى الضروري من WordPress باستخدام ثابت SHORTINIT .

إنشاء نقطة نهاية صفحات مفصولة بسيطة

لنقم بإنشاء مكون إضافي صغير يوضح التأثير الذي نتحدث عنه. أولاً ، أضف ملف wp-config-simple.php في json-transient الذي يبدو كالتالي:

 <?php /** * Create simple wp configuration for the routes * * @since 1.0.0 * @package json-transient */ define( 'SHORTINIT', true ); $parse_uri = explode( 'wp-content', $_SERVER['SCRIPT_FILENAME'] ); require_once filter_var( $parse_uri[0] . 'wp-load.php', FILTER_SANITIZE_STRING );

define( 'SHORTINIT', true ); سيمنع تحميل غالبية ملفات WordPress الأساسية ، كما يمكن رؤيته في ملف wp-settings.php .

قد لا نزال بحاجة إلى بعض وظائف WordPress ، لذا يمكننا طلب الملف (مثل wp-load.php ) يدويًا. نظرًا لأن wp-load.php موجود في جذر تثبيت WordPress الخاص بنا ، فسنقوم بإحضاره عن طريق الحصول على مسار ملفنا باستخدام $_SERVER['SCRIPT_FILENAME'] ، ثم تفجير هذه السلسلة بواسطة سلسلة wp-content . سيؤدي هذا إلى إرجاع مصفوفة ذات قيمتين:

  1. جذر التثبيت لدينا ؛
  2. باقي مسار الملف (الذي لا يهمنا).

ضع في اعتبارك أننا نستخدم التثبيت الافتراضي لـ WordPress ، وليس التثبيت المعدل ، على سبيل المثال في نموذج Bedrock المعياري ، الذي يقسم WordPress في منظمة ملفات مختلفة.

أخيرًا ، نحتاج إلى ملف wp-load.php ، مع القليل من التعقيم ، للأمان.

في ملف init.php الخاص بنا ، سنضيف ما يلي:

* Author URI: https://infinum.co/ * License: GPL-2.0+ * License URI: https://www.gnu.org/licenses/gpl-2.0.txt * Text Domain: json-transient */ namespace Json_Transient; // If this file is called directly, abort. if ( ! defined( 'WPINC' ) ) { die; } class Init { /** * Get the array of allowed types to do operations on. * * @return array * * @since 1.0.0 */ public function get_allowed_post_types() { return array( 'post', 'page' ); } /** * Check if post type is allowed to be save in transient. * * @param string $post_type Get post type. * @return boolean * * @since 1.0.0 */ public function is_post_type_allowed_to_save( $post_type = null ) { if( ! $post_type ) { return false; } $allowed_types = $this->get_allowed_post_types(); if ( in_array( $post_type, $allowed_types, true ) ) { return true; } return false; } /** * Get Page cache name for transient by post slug and type. * * @param string $post_slug Page Slug to save. * @param string $post_type Page Type to save. * @return string * * @since 1.0.0 */ public function get_page_cache_name_by_slug( $post_slug = null, $post_type = null ) { if( ! $post_slug || ! $post_type ) { return false; } $post_slug = str_replace( '__trashed', '', $post_slug ); return 'jt_data_' . $post_type . '_' . $post_slug; } /** * Get full post data by post slug and type. * * @param string $post_slug Page Slug to do Query by. * @param string $post_type Page Type to do Query by. * @return array * * @since 1.0.0 */ public function get_page_data_by_slug( $post_slug = null, $post_type = null ) { if( ! $post_slug || ! $post_type ) { return false; } $page_output = ''; $args = array( 'name' => $post_slug, 'post_type' => $post_type, 'posts_per_page' => 1, 'no_found_rows' => true ); $the_query = new \WP_Query( $args ); if ( $the_query->have_posts() ) { while ( $the_query->have_posts() ) { $the_query->the_post(); $page_output = $the_query->post; } wp_reset_postdata(); } return $page_output; } /** * Return Page in JSON format * * @param string $post_slug Page Slug. * @param string $post_type Page Type. * @return json * * @since 1.0.0 */ public function get_json_page( $post_slug = null, $post_type = null ) { if( ! $post_slug || ! $post_type ) { return false; } return wp_json_encode( $this->get_page_data_by_slug( $post_slug, $post_type ) ); } /** * Update Page to transient for caching on action hooks save_post. * * @param int $post_id Saved Post ID provided by action hook. * * @since 1.0.0 */ public function update_page_transient( $post_id ) { $post_status = get_post_status( $post_id ); $post = get_post( $post_id ); $post_slug = $post->post_name; $post_type = $post->post_type; $cache_name = $this->get_page_cache_name_by_slug( $post_slug, $post_type ); if( ! $cache_name ) { return false; } if( $post_status === 'auto-draft' || $post_status === 'inherit' ) { return false; } else if( $post_status === 'trash' ) { delete_transient( $cache_name ); } else { if( $this->is_post_type_allowed_to_save( $post_type ) ) { $cache = $this->get_json_page( $post_slug, $post_type ); set_transient( $cache_name, $cache, 0 ); } } } } $init = new Init(); add_action( 'save_post', [ $init, 'update_page_transient' ] );

ستمكننا الطرق المساعدة في الكود أعلاه من القيام ببعض التخزين المؤقت:

  • get_allowed_post_types()
    تسمح هذه الطريقة لأنواع المنشورات بمعرفة أننا نريد تمكين العرض في "نقطة النهاية" المخصصة لدينا. يمكنك تمديد هذا ، والمكوِّن الإضافي الذي قمنا به بالفعل جعل هذه الطريقة قابلة للتصفية بحيث يمكنك فقط استخدام عامل تصفية لإضافة عناصر إضافية.
  • is_post_type_allowed_to_save()
    تتحقق هذه الطريقة ببساطة لمعرفة ما إذا كان نوع المنشور الذي نحاول جلب البيانات منه موجود في المصفوفة المسموح بها المحددة بواسطة الطريقة السابقة.
  • get_page_cache_name_by_slug()
    ستعيد هذه الطريقة اسم المؤقت الذي سيتم جلب البيانات منه.
  • get_page_data_by_slug()
    هذه الطريقة هي الطريقة التي ستنفذ WP_Query على المنشور عبر slug ونوع المنشور الخاص به ويعيد محتويات مصفوفة التدوينة التي سنقوم بتحويلها باستخدام JSON باستخدام طريقة get_json_page() .
  • update_page_transient()
    سيتم تشغيل هذا على خطاف save_post المؤقت في قاعدة البيانات ببيانات JSON لمنشورنا. تُعرف هذه الطريقة الأخيرة باسم "طريقة التخزين المؤقت للمفتاح".

دعونا نشرح العابرين بمزيد من العمق.

العابرين API

يتم استخدام Transients API لتخزين البيانات في جدول الخيارات في قاعدة بيانات WordPress الخاصة بك لفترة زمنية محددة. إنها ذاكرة تخزين مؤقت للكائنات المستمرة ، مما يعني أنك تقوم بتخزين بعض العناصر ، على سبيل المثال ، نتائج الاستعلامات الكبيرة والبطيئة أو الصفحات الكاملة التي يمكن استمرارها عبر عمليات تحميل الصفحة. إنه مشابه لذاكرة التخزين المؤقت للكائنات WordPress العادية ، ولكن على عكس WP_Cache ، ستستمر البيانات العابرة عبر عمليات تحميل الصفحة ، حيث WP_Cache (تخزين البيانات في الذاكرة) بالبيانات طوال مدة الطلب فقط.

إنه متجر ذو قيمة رئيسية ، مما يعني أنه يمكننا بسهولة وبسرعة جلب البيانات المطلوبة ، على غرار ما تفعله in-memory caching systems مثل Memcached أو Redis. الاختلاف هو أنك ستحتاج عادةً إلى تثبيت هؤلاء بشكل منفصل على الخادم (والذي يمكن أن يكون مشكلة على الخوادم المشتركة) ، في حين أن العابرين مدمجة في WordPress.

كما هو مذكور في صفحة Codex الخاصة بها - يتم تسريع العابرين بطبيعتها عن طريق تخزين المكونات الإضافية. حيث يمكنهم تخزين العابرين في الذاكرة بدلاً من قاعدة البيانات. القاعدة العامة هي أنه لا يجب أن تفترض أن المؤقت موجود دائمًا في قاعدة البيانات - وهذا هو السبب في أنه من الممارسات الجيدة التحقق من وجوده قبل جلبه

 $transient_name = get_transient( 'transient_name' ); if ( $transient_name === false ) { set_transient( 'transient_name', $transient_data, $transient_expiry ); }

يمكنك استخدامه بدون انتهاء الصلاحية (كما نفعل الآن) ، ولهذا السبب قمنا بتنفيذ نوع من "خرق ذاكرة التخزين المؤقت" على حفظ آخر. بالإضافة إلى جميع الوظائف الرائعة التي يوفرونها ، يمكنهم الاحتفاظ بسعة تصل إلى 4 جيجابايت من البيانات ، لكننا لا نوصي بتخزين أي شيء بهذا الحجم في حقل قاعدة بيانات واحد.

القراءة الموصى بها : كن يقظًا: وظائف PHP و WordPress التي يمكن أن تجعل موقعك غير آمن

نقطة النهاية النهائية: الاختبار والتحقق

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

 get_page_cache_name_by_slug( $post_slug, $post_type ) ); // Return error on false. if ( $cache === false ) { wp_send_json( 'Error, the page does not exist or it is not cached correctly. Please try rebuilding cache and try again!' ); } // Decode json for output. wp_send_json( json_decode( $cache ) );

إذا ذهبنا إلى https://dev.wordpress.test/wp-content/plugins/json-transient/test.php ، فسنرى هذه الرسالة:

خطأ ، الصفحة الرئيسية أو النوع مفقود!

لذلك ، سنحتاج إلى تحديد نوع المنشور و post slug. عندما نذهب الآن إلى https://dev.wordpress.test/wp-content/plugins/json-transient/test.php?slug=hello-world&type=post سنرى:

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

اه انتظر! نحتاج إلى إعادة حفظ صفحاتنا ومنشوراتنا أولاً. لذلك عندما تبدأ ، يمكن أن يكون هذا سهلاً. ولكن إذا كان لديك بالفعل أكثر من 100 صفحة أو منشور ، فقد تكون هذه مهمة صعبة. هذا هو السبب في أننا قمنا بتنفيذ طريقة لمسح العابرين في المكون الإضافي Decoupled JSON Content ، وإعادة بنائها دفعة واحدة.

لكن انطلق وأعد حفظ منشور Hello World ثم افتح الرابط مرة أخرى. ما يجب أن يكون لديك الآن هو شيء يشبه هذا:

 { "ID": 1, "post_author": "1", "post_date": "2018-06-26 18:28:57", "post_date_gmt": "2018-06-26 18:28:57", "post_content": "Welcome to WordPress. This is your first post. Edit or delete it, then start writing!", "post_title": "Hello world!", "post_excerpt": "", "post_status": "publish", "comment_status": "open", "ping_status": "open", "post_password": "", "post_name": "hello-world", "to_ping": "", "pinged": "", "post_modified": "2018-06-30 08:34:52", "post_modified_gmt": "2018-06-30 08:34:52", "post_content_filtered": "", "post_parent": 0, "guid": "http:\/\/dev.wordpress.test\/?p=1", "menu_order": 0, "post_type": "post", "post_mime_type": "", "comment_count": "1", "filter": "raw" }

وهذا كل شيء. يحتوي المكون الإضافي الذي أنشأناه على بعض الوظائف الإضافية التي يمكنك استخدامها ، ولكن باختصار ، هذه هي الطريقة التي يمكنك بها جلب بيانات JSON من WordPress الخاص بك بطريقة أسرع من استخدام REST API.

قبل وبعد: تحسين وقت الاستجابة

أجرينا الاختبار في Chrome ، حيث تمكنا من رؤية وقت الاستجابة الإجمالي و TTFB بشكل منفصل. اختبرنا أوقات الاستجابة عشر مرات متتالية: أولاً بدون المكونات الإضافية ثم مع الإضافات المضافة. أيضًا ، قمنا باختبار الاستجابة لقائمة من المشاركات ومنشور واحد.

نتائج الاختبار موضحة في الجداول أدناه:

رسم بياني للمقارنة يوضح أوقات الاستجابة لاستخدام WordPress REST API مقابل استخدام النهج المنفصل دون إضافات مضافة. النهج المنفصل أسرع مرتين إلى ثلاث مرات
رسم بياني للمقارنة يوضح أوقات الاستجابة لاستخدام WordPress REST API مقابل استخدام النهج المنفصل دون إضافات مضافة. النهج المنفصل أسرع مرتين إلى ثلاث مرات. (معاينة كبيرة)
رسم بياني للمقارنة يصور أوقات الاستجابة لاستخدام WordPress REST API مقابل استخدام النهج المنفصل مع المكونات الإضافية المضافة. النهج المنفصل أسرع بما يصل إلى 8 مرات.
رسم بياني للمقارنة يصور أوقات الاستجابة لاستخدام WordPress REST API مقابل استخدام النهج المنفصل مع المكونات الإضافية المضافة. النهج المنفصل أسرع بما يصل إلى 8 مرات. (معاينة كبيرة)

كما ترى ، فإن الاختلاف جذري.

مخاوف أمنية

هناك بعض المحاذير التي ستحتاج إلى إلقاء نظرة فاحصة عليها. بادئ ذي بدء ، نقوم بتحميل ملفات WordPress الأساسية يدويًا ، والتي تعد في عالم WordPress أمرًا لا يمكن استخدامه. لماذا ا؟ حسنًا ، إلى جانب حقيقة أن جلب الملفات الأساسية يدويًا يمكن أن يكون صعبًا (خاصة إذا كنت تستخدم منشآت غير قياسية مثل Bedrock) ، فقد يمثل ذلك بعض المخاوف الأمنية.

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

أولاً ، أضف رؤوس HTML كما في ملف test.php :

 header( 'Access-Control-Allow-Origin: your-front-end-app.url' ); header( 'Content-Type: application/json' );

الرأس الأول هو طريقة لتجاوز إجراءات الأمان CORS بحيث لا يتمكن سوى تطبيق الواجهة الأمامية من جلب المحتويات عند الانتقال إلى الملف المحدد.

ثانيًا ، قم بتعطيل اجتياز الدليل لتطبيقك. يمكنك القيام بذلك عن طريق تعديل إعدادات nginx ، أو إضافة Options -Indexes إلى ملف htaccess الخاص بك إذا كنت تستخدم خادم Apache.

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

قد يبدو التحقق من رأس التفويض الذي تم إرساله بواسطة تطبيق الواجهة الأمامية كما يلي:

 if ( ! isset( $_SERVER['HTTP_AUTHORIZATION'] ) ) { return; } $auth_header = $_SERVER['HTTP_AUTHORIZATION'];

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

خاتمة

يعد REST API رائعًا لأنه يمكن استخدامه لإنشاء تطبيقات كاملة - إنشاء واسترداد وتحديث وحذف البيانات. عيب استخدامه هو سرعته.

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

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

الطريقة الموضحة في هذه المقالة تحل مشكلة السرعة التي تواجه WordPress REST API وستمنحك دفعة إضافية عند العمل في مشروع WordPress منفصل. نظرًا لأننا في سعينا الذي لا ينتهي للضغط على هذا المللي ثانية الأخيرة من كل طلب واستجابة ، فإننا نخطط لتحسين المكون الإضافي أكثر. في غضون ذلك ، يرجى مشاركة أفكارك حول تسريع WordPress المنفصل!