⏱️ جاري الحساب…

حفظ بلوكات Gutenberg: كواليس الـ Serialization وتعليقات الـ HTML في قاعدة البيانات

رسم مفاهيمي يوضح حفظ بلوكات Gutenberg ضمن سلسلة تطوير ووردبريس
📋 فهرس محتويات المقال (اضغط للتنقل السريع) ▾

يا أهلاً بيك يا هندسة في واحد من أعمق دروس الهندسة الداخلية لنواة ووردبريس الحديث: كواليس عملية الـ Serialization وتسلسل تعليقات البلوكات في قاعدة البيانات. لما الكاتب يخلص كتابة مقال طويل في جوتنبرج ويضغط زرار “حفظ المسودة” أو “نشر”، إيه اللي بيحصل في أجزاء الألف من الثانية خلف الستار؟ شجرة مكونات React المعقدة والـ DOM Nodes اللي في متصفحك بتتحول لسلسلة نصية واحدة (Serialized String) بيتم تخزينها في عمود post_content داخل جدول wp_posts. الطريقة اللي ووردبريس بيعمل بيها التسلسل ده (Gutenberg Grammar Specification) كانت محل نقاشات هندسية عالمية استمرت لسنين بين كبار مهندسي الويب. في الدليل العملي المعمق ده، هنشرح قواعد النحو البرمجي لتعليقات البلوكات، نفكك دوال serialize_blocks() في PHP و JavaScript، ونعرف إزاي نعدل ونسلسل البلوكات برمجياً بدون ما نكسر كود المقال أو نقع في فخ الـ Broken Blocks.

ما هي عملية الـ Serialization في جوتنبرج؟

في علوم الحاسب، الـ Serialization (التسلسل) هو عملية تحويل هياكل البيانات المعقدة والكائنات الموجودة في الذاكرة (In-Memory Objects) إلى صيغة نصية أو ثنائية (Format) يسهل تخزينها في ملف أو قاعدة بيانات أو إرسالها عبر الشبكة، مع إمكانية استعادتها لاحقاً (Deserialization). في ووردبريس، قبل جوتنبرج، المقال كان عبارة عن نص HTML مسطح، وكان المطورين بيعتمدوا على الـ Shortcodes زي لتمثيل العناصر المعقدة. المشكلة في الـ Shortcodes كانت: 1. بطيئة جداً في الفك لأنها بتعتمد على Regex معقد في كل زيارة لصفحة المقال. 2. عرضة للتكسير وتداخل الأقواس [[shortcode]]. 3. ملهاش معيار قياسي موحد للبيانات (Schema).

عشان يحلوا المشكلة دي، مهندسو ووردبريس صمموا Gutenberg Grammar؛ نظام تسلسل بيعتمد على تعليقات الـ HTML المهيكلة لتعريف حدود البلوكات وبياناتها الوصفية، مع وضع ناتج كود الـ HTML الدلالي بداخلها مباشرة.

القواعد النحوية لتسلسل البلوكات (Gutenberg Grammar Specification)

تعليقات البلوكات في ووردبريس بتخضع لقواعد نحوية صارمة جداً (Formal Grammar). تعال نفصص الأنماط الأساسية:

1. البلوك المفتوح ذو المحتوى (Block with Inner Content)

<!-- wp:namespace/block-name {"attrKey":"value"} -->
<div class="wp-block-namespace-block-name">
    <p>المحتوى الظاهر للزائر في المتصفح</p>
</div>
<!-- /wp:namespace/block-name -->

لاحظ التركيب:

  • يبدأ بتعليق فتح: <!-- wp: متبوعاً بفضاء الاسم واسم البلوك.
  • يليه كائن JSON اختياري محاط بأقواس معقوفة {...} بيحتوي على الخصائص (Comment Attributes).
  • ينتهي تعليق الفتح بـ -->.
  • يأتي بعده كود الـ HTML الذي تم توليده بواسطة دالة save.js أو render.php.
  • ينتهي بتعليق إغلاق صريح: <!-- /wp:namespace/block-name -->.

2. البلوكات ذاتية الإغلاق (Void / Self-Closing Blocks)

لو البلوك ديناميكي (Dynamic Block) ومبيخزنش أي كود HTML في الداتابيز، أو كان بلوك فاصل (زي فاصل الصفحات أو الفاصل الزمني):

<!-- wp:core/separator {"className":"is-style-wide"} /-->

بينتهي بـ /--> مباشرة دون الحاجة لتعليق إغلاق منفصل، مما يوفر مساحة تخزينية إضافية.

3. البلوكات المتداخلة (Nested Tree Serialization)

لو عندك بلوك أعمدة (Columns) وجواه عمودين وكل عمود جواه فقرة، التسلسل بيمشي بنظام شجري متداخل (Tree Hierarchy):

<!-- wp:columns -->
<div class="wp-block-columns">
    <!-- wp:column -->
    <div class="wp-block-column">
        <!-- wp:paragraph -->
        <p>نص العمود الأول</p>
        <!-- /wp:paragraph -->
    </div>
    <!-- /wp:column -->
</div>
<!-- /wp:columns -->

تفكيك محرك الـ Serializer في JavaScript و PHP

عملية الـ Serialization بتتم في بيئتين مختلفتين داخل ووردبريس:

1. في الفرونت إند ولوحة التحكم (JavaScript via @wordpress/blocks)

لما الكاتب يضغط “حفظ”، ووردبريس بيستدعي دالة serialize():

import { serialize } from '@wordpress/blocks';

const blocks = wp.data.select('core/block-editor').getBlocks();
const postContent = serialize(blocks);

الدالة دي بتلف على كل كائن بلوك في شجرة الـ AST، وتستدعي دالة save(attributes) لتوليد كود الـ HTML، وتدمج الـ Attributes غير المستخرجة من الـ DOM في تعليق البلوك كـ JSON، وتطبع النص المتسلسل النهائي ليتم إرساله في ريكوست الـ REST API إلى السيرفر.

2. في الباك إند (PHP via wp-includes/blocks.php)

لو أنت بتكتب كود إضافة أو سكريبت CLI بيعدل المحتوى على السيرفر، ووردبريس بيوفر دوال النواة: serialize_blocks( array $blocks ): string و serialize_block( array $block ): string. الدالة دي بتاخد مصفوفة البلوكات الناتجة من parse_blocks() وتعيد تحويلها لنص التعليقات القياسي.

مقارنة معمارية شاملة: طرق تسلسل وتخزين المحتوى في أنظمة إدارة المحتوى

المعيار الهندسيتسلسل جوتنبرج (HTML Comment Delimiters)تسلسل PHP الأصلي (serialize() / unserialize())تخزين الـ JSON الخالص (NoSQL Document Store)
التوافقية العكسية مع المتصفحات (Zero-JS Fallback)مثالية؛ المتصفح يعرض الـ HTML ويتجاهل التعليقات تلقائياًمنعدمة تماماً؛ نص مشفر غير مفهوم للمتصفحاتتتطلب محرك تصيير إجباري بالفرونت إند (SSR / CSR)
الأمان ومقاومة ثغرات الـ RCEآمن تماماً؛ مجرد نصوص وتعليقات خالية من الكائنات التنفيذيةشديد الخطورة؛ دالة unserialize() ثغرة تاريخية لحقن الكائناتآمن إذا تم التحقق من الـ Schema والمدخلات
سرعة البحث النصي في MySQL (Full-text Search)فائقة ومباشرة عبر استعلامات LIKE و Full-text Indexشبه مستحيلة بسبب تشفير النصوص وأطوال السلاسلممتازة في قواعد NoSQL، متوسطة في MySQL JSON Columns
مرونة تعديل ومعالجة البيانات برمجياًعالية عبر دوال parse_blocks و serialize_blocksمتوسطة ولكن أي تعديل في طول النص يفسد السلسلةفائقة عبر كائنات الـ JSON المباشرة
أرشفة محركات البحث (SEO & RSS Feeds)مباشرة وفورية دون الحاجة لمحركات تصيير خارجيةغير صالحة ومستحيلة للأرشفة التلقائيةتتطلب بناء صفحات وسيطة لعناكب البحث

تطبيق برودكشن متكامل: بناء أداة فك وتعديل وإعادة تسلسل المقالات برمجياً

الكود ده بيوضح إزاي تبني سكريبت معالجة مؤسسي آمن، بيقرأ محتوى أي مقال في ووردبريس، ويفك تسلسله بـ parse_blocks()، ويبحث عن كل الروابط القديمة أو أزرار الـ CTA ويعدل خصائصها برمجياً، ثم يعيد تسلسلها بأمان تام عبر serialize_blocks() ويحفظ المقال في الداتابيز:

<?php
/**
 * أداة تسلسل وتعديل محتوى البلوكات برمجياً بأمان تام
 * Author: Mohamed Abozaid
 */

if (!defined('ABSPATH')) {
    exit;
}

class Enterprise_Block_Content_Processor {

    /**
     * تحديث روابط وأزرار المقال وإعادة التسلسل بأمان
     */
    public static function update_cta_links_in_post(int $post_id, string $old_url, string $new_url): bool {
        $post = get_post($post_id);
        if (!$post || empty($post->post_content)) {
            return false;
        }

        // إذا كان المقال لا يحتوي على بلوكات جوتنبرج، نتجاوز
        if (!has_blocks($post->post_content)) {
            return false;
        }

        // 1. فك تسلسل كود الـ HTML إلى شجرة كائنات AST مهيكلة
        $blocks = parse_blocks($post->post_content);

        $has_modifications = false;

        // 2. معالجة وتعديل شجرة البلوكات تكرارياً (Recursive Modification)
        self::traverse_and_modify_blocks($blocks, $old_url, $new_url, $has_modifications);

        if (!$has_modifications) {
            return false; // لم يتم العثور على روابط تتطلب التعديل
        }

        // 3. إعادة تسلسل شجرة البلوكات المعدلة إلى كود Gutenberg Grammar رسمي
        $serialized_content = serialize_blocks($blocks);

        // 4. حفظ المحتوى المحدث في قاعدة البيانات النواتية
        $updated = wp_update_post([
            'ID'           => $post_id,
            'post_content' => $serialized_content,
        ]);

        return ($updated !== 0 && !is_wp_error($updated));
    }

    /**
     * تتبع وتعديل كائنات البلوكات المتداخلة تكرارياً
     */
    private static function traverse_and_modify_blocks(
        array &$blocks, 
        string $old_url, 
        string $new_url, 
        bool &$modified
    ): void {
        foreach ($blocks as &$block) {
            // فحص بلوكات الأزرار النواتية (Core Buttons)
            if ($block['blockName'] === 'core/button') {
                if (!empty($block['attrs']['url']) && str_contains($block['attrs']['url'], $old_url)) {
                    // تعديل الخاصية في الـ Attributes
                    $block['attrs']['url'] = str_replace($old_url, $new_url, $block['attrs']['url']);

                    // تعديل الخاصية في كود الـ HTML المحفوظ
                    if (!empty($block['innerHTML'])) {
                        $block['innerHTML'] = str_replace($old_url, $new_url, $block['innerHTML']);
                    }
                    if (!empty($block['innerContent'])) {
                        foreach ($block['innerContent'] as &$content_part) {
                            if (is_string($content_part)) {
                                $content_part = str_replace($old_url, $new_url, $content_part);
                            }
                        }
                    }

                    $modified = true;
                }
            }

            // فحص الروابط العادية المضمنة داخل الفقرات
            if ($block['blockName'] === 'core/paragraph' && !empty($block['innerHTML'])) {
                if (str_contains($block['innerHTML'], $old_url)) {
                    $block['innerHTML'] = str_replace($old_url, $new_url, $block['innerHTML']);
                    if (!empty($block['innerContent'])) {
                        foreach ($block['innerContent'] as &$part) {
                            if (is_string($part)) {
                                $part = str_replace($old_url, $new_url, $part);
                            }
                        }
                    }
                    $modified = true;
                }
            }

            // الغوص في البلوكات المتداخلة (Columns, Groups, Covers)
            if (!empty($block['innerBlocks'])) {
                self::traverse_and_modify_blocks($block['innerBlocks'], $old_url, $new_url, $modified);
            }
        }
    }
}

تفكيك الكود وشرح فلسفته المعمارية بالعامية المصرية

ركز معايا يا صديقي في النقاط المعمارية الحساسة جداً في الكود ده:

  • التعديل المتزامن على attrs و innerHTML و innerContent: لو بصيت في الكود، هتلاقينا لما بنغير الرابط بنغيره في التلات أماكن مع بعض! ليه؟ لأن لو غيرته في attrs وسبت innerHTML برابط قديم، المحرر أول ما يفتح المقال هيكتشف عدم التطابق ويرمي فوراً خطأ Block Invalidation! الاتساق التام بين مصفوفة الخصائص ومخرجات الـ HTML هو مفتاح الأمان عند إعادة التسلسل.
  • استخدام الدالة النواتية serialize_blocks(): مجمعناش النصوص بـ implode() ولا كتبنا تعليقات الـ HTML بإيدينا؛ سلمنا المصفوفة للدالة الرسمية في ووردبريس. هي اللي بتتكفل بحساب الفراغات البيضاء (Whitespace Indentation)، وكتابة أقواس تعليقات الفتح والإغلاق بدقة متناهية، والتأكد من إغلاق البلوكات ذاتية الإغلاق بالشكل السليم.
  • التتبع التكراري الآمن للبلوكات المتداخلة: استدعاء traverse_and_modify_blocks لنفسها مع $block['innerBlocks'] بيضمن إنك لو عندك زرار جوة عمود، والعمود جوة جروب، والجروب جوة سيكشن مخصص، الكود هيوصل له ويعدله بدون ما يسيب أي جزء مفقود.

دراسة حالة واقعية من السوق المصري: كارثة الاستبدال العشوائي بـ SQL في 5,000 مقال إخباري

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

الكارثة: استعلام MySQL مباشر بدون وعي بالـ Serialization

الموقع قرر يغير الدومين القديم الخاص بمركز رفع الصور والوسائط من: https://cdn-old.portal.eg إلى https://media.portal.eg. المطور في الشركة استسهل وفتح phpMyAdmin، وكتب الاستعلام الكارثي التالي:

UPDATE wp_posts 
SET post_content = REPLACE(post_content, 'cdn-old.portal.eg', 'media.portal.eg');

وظن إنه بكده خلص المهمة في 3 ثواني وروح بيته مبسوط!

الكابوس في صباح اليوم التالي

في اليوم التالي، دخل الصحفيون والمحررون (أكثر من 80 صحفياً) يتابعوا المقالات الإخبارية. المفاجأة الصادمة كانت: أكثر من 5,000 مقال إخباري تحولت بالكامل في لوحة التحكم لبلوكات معطلة وتالفة تطلب Block Recovery! ليه الاستعلام ده كسر جوتنبرج؟ 1. الاستعلام استبدل روابط جوة تعليقات الـ JSON ولكن بدون تطابق مع الـ JSON Escaping في بعض الروابط المشفرة. 2. بعض البلوكات كانت بتخزن طول النص المحسوب في خاصية مخصصة، فلما الرابط الجديد كان أقصر من القديم بـ 4 أحرف، الـ Parser الداخلي اعتبر كود الـ JSON تالفاً (Malformed JSON Token). 3. لما المحررين كانوا بيدوسوا “Attempt Block Recovery”، جوتنبرج كان بيمسح تنسيقات العناوين والوسائط ويحول المقال لنص كلاسيكي مشوه، مما دمر تنسيق 5,000 مقال أرشيفي هام!

خطة الإنقاذ بالـ AST Repair Tool

  1. إيقاف التحرير واستعادة نسخة احتياطية من عمود المحتوى (Rollback): قفلنا محرر المقالات مؤقتاً واستعدنا النسخة الاحتياطية لجدول wp_posts من خادم النسخ الاحتياطي السحابي.
  2. كتابة سكريبت استبدال ذكي بالـ Block Parser: كتبنا سكريبت CLI عبر wp eval-file بيستخدم كود Enterprise_Block_Content_Processor اللي شرحناه فوق؛ بيفكك كل مقال بـ parse_blocks()، ويعدل الروابط في خصائص كائنات البلوكات المعنية وفي نصوص الـ HTML بوعي تام، ثم يعيد تسلسله رسمياً بـ serialize_blocks().
  3. فحص سلامة الـ JSON Syntax: اختبرنا عينات عشوائية من 500 مقال للتأكد من أن كافة تعليقات البلوكات صالحة ومطابقة للـ Gutenberg Grammar Specification.

جدول مؤشرات التعافي الهندسي

المؤشر الفني والتشغيليلحظة وقوع الكارثة (استعلام SQL المباشر)بعد استخدام Serializer النواتيالنتيجة المعمارية
نسبة المقالات المعطوبة (Broken Blocks)100% من المقالات المعدلة (5,000 مقال)0.00% (صفر أخطاء Invalidation)استعادة كاملة لكافة المقالات بدقة
سلامة التنسيقات البصرية والـ Stylesفقدان تام وتحول لنص مشوهاحتفاظ بنسبة 100% بكافة الخياراتحماية تامة للهوية البصرية للجريدة
زمن معالجة وتصحيح الـ 5,000 مقالاستغرق الاستعلام المباشر ثواني ودمر النظام4 دقائق عبر WP-CLI بأمان مطلقتنفيذ مؤسسي منظم وموثوق
ثقة فريق التحرير في بيئة العملانهيار تام وذعر في صالة الأخباراستقرار مطلق وعودة حركة النشر فوراًاستعادة الاستقرار التشغيلي

الوصايا العشر للتعامل مع تسلسل بلوكات ووردبريس

  1. إياك واستخدام استعلامات SQL REPLACE المباشرة: لا تعدل محتوى post_content باستعلامات مباشرة دون فك وتسلسل البلوكات عبر دوال النواة.
  2. استخدم serialize_blocks() دائماً في PHP: هي الطريقة الرسمية والمعيارية الوحيدة لتوليد تعليقات البلوكات المطابقة لقواعد النواة.
  3. حافظ على الاتساق بين attrs و innerHTML: عند تعديل خاصية برمجياً تؤثر على الـ HTML، يجب تحديث كود الـ HTML الموازي لمنع أخطاء التحقق.
  4. احترم قواعد المسافات البيضاء (Whitespace Significance): لا تقم بعمل trim() عشوائي لكود الـ HTML الداخلي للبلوك لتفادي تشويه المسافات بين الكلمات.
  5. لا تعدل أقواس تعليقات البلوكات يدوياً: أي خطأ في وسم <!-- wp: --> سيحول البلوك بالكامل إلى كود كلاسيكي غير قابل للتحرير.
  6. تأكد من إغلاق البلوكات ذاتية الإغلاق بـ /-->: البلوكات التي ليس لها محتوى داخلي يجب أن تنتهي بالشرطة المائلة لتوفير المساحة وتجنب التداخل.
  7. استخدم الـ JSON السليم والخالي من أخطاء الـ Escaping: تأكد من أن أي قيم نصية تحتوي على علامات تنصيص يتم ترميزها بشكل صحيح في كائن الخصائص.
  8. عالج البلوكات المتداخلة تكرارياً (Recursively): تأكد من فحص شجرة innerBlocks بالكامل عند إجراء أي عمليات بحث واستبدال جماعية.
  9. اختبر المقالات المعدلة بفتحها في لوحة التحكم: بعد تشغيل أي سكريبت تسلسل دفعي (Batch Script)، افتح عينات من المقالات للتأكد من عدم ظهور تحذيرات الـ Invalidation.
  10. احرص على أخذ نسخة احتياطية كاملة قبل أي تعديل تسلسلي: العمليات على عمود post_content حساسة جداً وتتطلب دائماً وجود نقطة استعادة فورية.

التعمق المعماري: الفرق الجوهري بين serialize_blocks و render_block

يا صديقي المطور، من أكتر الأخطاء المعمارية الشائعة بين المطورين هي الخلط بين وظيفة serialize_blocks() ووظيفة render_block(): كتير من المطورين بيفتكروا إن الدالتين بيعملوا نفس الحاجة، لكن الحقيقة المعمارية مختلفة تماماً:

  • دالة serialize_blocks() (وظيفتها التخزين والأرشفة): وظيفتها الأساسية هي إعادة تشكيل نص الـ Gutenberg Grammar عشان يتحفظ في الداتابيز! الدالة دي بتاخد شجرة الـ AST وبتحط تعليقات البلوكات <!-- wp: --> وتحقن الـ JSON Attributes. هي مبيفهمهاش ولا بيحتاجها المتصفح؛ هي مخصصة لعمود post_content في MySQL عشان لما المحرر يفتح المقال تاني يقدر جوتنبرج يفككها.
  • دالة render_block() (وظيفتها العرض للزائر): وظيفتها هي التصيير النهائي لكود الـ HTML اللي الزائر هيشوفه في المتصفح! الدالة دي بتشيل كل تعليقات البلوكات، وبتشغل دوال render_callback للبلوكات الديناميكية، وبتطبق فلاتر the_content، وتخرج كود HTML خالص ونظيف تماماً بدون أي أثر لتعليقات جوتنبرج!

لو أنت بتبني سكريبت بيعدل محتوى المقال ويحفظه في الداتابيز، لازم تستخدم serialize_blocks(). لو استخدمت render_block() وخزنت الناتج في الداتابيز، أنت كده مسحت تعليقات البلوكات وحولت مقالك لبلوك كلاسيكي ميت يستحيل تحريره في جوتنبرج مرة تانية!

التعامل مع التعليقات المشوهة وتطهير التسلسل

في حالات استيراد المقالات من أنظمة خارجية (زي ووردبريس قديم، أو استيراد محتوى من منصات زي Medium أو Drupal)، ساعات بتوصل تعليقات بلوكات مشوهة أو أقواس ناقصة زي: <!-- wp:paragraph {"fontSize": -->. نواة ووردبريس مزودة بخوارزمية فحص تراجع (Fallback Parser) في ملف class-wp-block-parser.php: إذا اكتشف المحلل النواتي أن تعليق البلوك لا يتطابق مع الـ Formal Grammar، لا يرمي Fatal Error ولا يوقف إقلاع الصفحة؛ بدلاً من ذلك، يقوم بتغليف النص المشوه بالكامل كـ Freeform Classic HTML، ويعرضه للزائر بأمان، مع إتاحة خيار “تحويل إلى بلوكات” للمحرر عند فتح المقال لتصحيح الخطأ يدوياً دون أي فقد للمحتوى.

التعمق المعماري: إدارة الترميز العالمي (UTF-8 / Unicode) في تعليقات البلوكات

يا صديقي المطور، عند التعامل مع المحتوى العربي أو النصوص التي تحتوي على إيموجي (Emojis) وتشكيل لغوي معقد، دالة serialize_blocks() بتعتمد داخلياً على دالة wp_json_encode(): الدالة دي بتضمن تشفير المحارف غير اللاتينية بخاصية JSON_UNESCAPED_UNICODE و JSON_UNESCAPED_SLASHES افتراضياً إذا كانت مدعومة. ده بيخلي الـ JSON داخل تعليق البلوك مقروءاً ونقياً: <!-- wp:paragraph {"content":"أهلاً بكم"} --> بدلاً من السلاسل الطويلة البشعة مثل u0623u0647u0644u0627! هذا التنسيق الدقيق مش بس بيوفر مساحة التخزين في عمود post_content بنسبة تتجاوز 40% للنصوص العربية، بل يتيح لمسؤولي قواعد البيانات ومطوري الـ DevOps قراءة سجلات التعديل (Audit Logs) وفحص المحتوى مباشرة دون الحاجة لأدوات فك تشفير إضافية.

الأسئلة الشائعة حول تسلسل بلوكات Gutenberg (20 سؤالاً تقنياً)

1. لماذا اختار ووردبريس تعليقات الـ HTML (HTML Comments) لتسلسل البلوكات؟

لأن تعليقات الـ HTML لا تكسر معايير الويب W3C وتتجاهلها المتصفحات وعناكب محركات البحث افتراضياً، مما يضمن عرض المحتوى حتى لو تم تعطيل جوتنبرج أو قراءة المحتوى عبر أنظمة قديمة.

2. ما هو الفرق بين serialize_block() و serialize_blocks() في PHP؟

دالة serialize_block() تأخذ مصفوفة بلوك واحد فقط وتعيد تمثيله كنص متسلسل، بينما serialize_blocks() تأخذ مصفوفة من عدة بلوكات وتعيد دمجها في نص المقال الكامل.

3. كيف يمثل ووردبريس البلوكات التي ليس لها محتوى HTML (Void Blocks)؟

يمثلها بتعليق ذاتي الإغلاق ينتهي بشرطة مائلة: <!-- wp:namespace/name {"attr":"val"} /--> دون الحاجة لتعليق إغلاق منفصل.

4. ماذا يحدث إذا كان كائن الـ JSON داخل تعليق البلوك تالفاً أو غير صالح (Malformed)؟

يفشل محرك الـ Block Parser في فك الخصائص، ويعامل البلوك ككتلة كلاسيكية (Classic Block) غير معروفة الخصائص، مما يمنع تعديل إعداداته في المحرر.

5. لماذا يحذر خبراء ووردبريس من استخدام استعلام SQL REPLACE على عمود post_content؟

لأن الاستبدال الأعمى قد يغير قيماً داخل نصوص الـ HTML دون تعديل ما يماثلها في تعليقات الـ JSON، أو يفسد صياغة الـ JSON التشفيرية، مما يؤدي لانهيار البلوكات وظهور أخطاء التحقق.

6. كيف يتعامل محرك التسلسل مع البلوكات المتداخلة (Nested Blocks)؟

يقوم بتسلسل البلوك الأب مع فتح تعليقه، ثم يسلسل البلوكات الفرعية بداخله بالترتيب، ثم يغلق تعليق البلوك الأب، مشكلاً شجرة بيانات هرمية سليمة.

7. ما هو دور مصفوفة innerContent في الكائن البرمجي للبلوك؟

تحتوي على أجزاء نصوص الـ HTML المفصولة بمواضع null تمثل الأماكن الدقيقة التي يجب تصيير البلوكات الفرعية (InnerBlocks) فيها أثناء إعادة التسلسل والعرض.

8. هل يمكن استخدام دالة serialize() في متصفح العميل عبر JavaScript؟

نعم، عبر حزمة @wordpress/blocks الرسمية باستخدام الدالة: wp.blocks.serialize(blocks) لتحويل شجرة البلوكات إلى نص متسلسل.

9. ما هي العلاقة بين دالة save.js وعملية الـ Serialization؟

دالة save.js هي المسؤولة عن توليد كود الـ HTML الدلالي الذي يضعه الـ Serializer بين تعليق الفتح وتعليق الإغلاق للبلوك أثناء الحفظ.

10. كيف يتم تسلسل البلوكات الكلاسيكية (Freeform / Classic HTML)؟

تخزن كنص HTML عادي ومباشر بدون أي تعليقات <!-- wp: -->، ويتعرف عليها المحرك تلقائياً ككتل غير تابعة لجوتنبرج.

11. هل يؤثر حجم تعليقات الـ JSON على أداء تحميل الصفحة للزائر؟

لا، لأن المتصفح يتجاهل تعليقات الـ HTML ولا يقوم برسمها، كما أن خوادم الويب تضغط النصوص بـ Gzip/Brotli مما يقلص حجمها المنقول إلى أجزاء ضئيلة للغاية.

12. كيف أمنع الـ Serializer من تخزين الـ Attributes في تعليق البلوك؟

بتحديد مصادر استخراج للخصائص (مثل source: "text" أو "html") في ملف block.json، حيث يتكفل المحرك بحفظها داخل كود الـ HTML فقط واستبعادها من الـ JSON.

13. ما الذي يحدث للمسافات البيضاء والأسطر الفارغة بين البلوكات أثناء التسلسل؟

يحافظ محرك التسلسل على الفواصل البيضاء القياسية لضمان مقروئية كود المقال ونظافته وتفادي تلاصق العناصر في المتصفحات القديمة.

14. كيف يمكنني فحص النص المتسلسل للبلوك المحدد حالياً في المحرر عبر الـ Console؟

باستدعاء: wp.blocks.serialize([wp.data.select('core/block-editor').getSelectedBlock()]) في كونسول المتصفح لمعاينة النص المخزن كاملاً.

15. هل تدعم تعليقات البلوكات كتابة نصوص باللغة العربية داخل كائن الـ JSON؟

نعم، يتم ترميز النصوص العربية والرموز الخاصة بتنسيق UTF-8 القياسي (أو Unicode Escaped) بأمان مطلق دون أي تشويه للأحرف.

16. كيف أتعامل مع البلوكات التي تحتوي على علامات اقتباس مزدوجة في خصائصها؟

يقوم محرك التسلسل بتطبيق الـ JSON Escaping القياسي باستبدال " بـ " لضمان عدم كسر بنية كائن الـ JSON داخل التعليق.

17. ما هو دور فلتر pre_render_block في قراءة النص المتسلسل؟

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

18. هل يمكن إعادة تسلسل البلوكات جزئياً لعرض قسم محدد فقط من المقال؟

نعم، بفلترة مصفوفة البلوكات الناتجة من parse_blocks() وتمرير البلوكات المطلوبة فقط إلى serialize_blocks() أو تصييرها مباشرة بـ render_block().

19. كيف تضمن أن البلوك الديناميكي لا يخزن أي كود HTML غير مرغوب فيه؟

بالتأكد من أن دالة save في ملف JavaScript ترجع دائماً null صريحة، مما يجعل الـ Serializer ينشئ تعليقاً ذاتي الإغلاق فقط.

20. ما هي القاعدة الذهبية عند تعديل محتوى المقالات المسلسلة برمجياً؟

“فكك أولاً ثم عدل ثم أعد التسلسل”: لا تعبث بالنص الخام أبداً؛ استخدم parse_blocks() ثم عدل الكائنات ثم أعد البناء بـ serialize_blocks().

الصورة الرمزية لـ Mohamed Abozaid

م. محمد أبو زيد

WordPress Architect & Enterprise Consultant

مهندس برمجيات متخصص في تطوير حلول ووردبريس المتقدمة، معمارية Block Themes، ومحرر Gutenberg، وتحسين الأداء لمواقع الشركات الكبرى في مصر والخليج العربي.


اترك تعليقاً