Table of Contents

Gutenberg Blocks System

Modern Block Development with Atomic Design

Jankx 2.0 sử dụng kiến trúc Gutenberg-first với hệ thống block development hiện đại, tuân thủ atomic design principles và tối ưu performance.

🧩 Block Architecture

Block Structure

blocks/
├── testimonial/
│   ├── block.json
│   ├── TestimonialBlock.php
│   ├── assets/
│   │   ├── js/
│   │   │   ├── editor.js
│   │   │   └── frontend.js
│   │   ├── css/
│   │   │   ├── editor.css
│   │   │   └── frontend.css
│   │   └── images/
│   └── templates/
│       ├── editor.html
│       └── frontend.html
├── hero/
├── features/
└── contact/

Block Class Structure

<?php
namespace Jankx\Gutenberg\Blocks;

class TestimonialBlock extends AbstractBlock
{
    protected static $attributes = [
        'content' => [
            'type' => 'string',
            'default' => '',
        ],
        'author' => [
            'type' => 'string',
            'default' => '',
        ],
        'position' => [
            'type' => 'string',
            'default' => '',
        ],
        'company' => [
            'type' => 'string',
            'default' => '',
        ],
        'avatar' => [
            'type' => 'object',
            'default' => null,
        ],
        'rating' => [
            'type' => 'number',
            'default' => 5,
        ],
        'style' => [
            'type' => 'string',
            'default' => 'default',
        ],
        'alignment' => [
            'type' => 'string',
            'default' => 'center',
        ],
        'showAvatar' => [
            'type' => 'boolean',
            'default' => true,
        ],
        'showRating' => [
            'type' => 'boolean',
            'default' => true,
        ],
        'backgroundColor' => [
            'type' => 'string',
            'default' => '',
        ],
        'textColor' => [
            'type' => 'string',
            'default' => '',
        ],
    ];

    protected static $supports = [
        'align' => ['wide', 'full'],
        'html' => false,
        'anchor' => true,
        'customClassName' => true,
        'spacing' => [
            'margin' => true,
            'padding' => true,
        ],
        'color' => [
            'background' => true,
            'text' => true,
        ],
    ];

    public static function getBlockName()
    {
        return 'testimonial';
    }

    public static function getTitle()
    {
        return __('Testimonial', 'jankx');
    }

    public static function getDescription()
    {
        return __('Display customer testimonials with author information and ratings.', 'jankx');
    }

    public static function getCategory()
    {
        return 'jankx-blocks';
    }

    public static function getIcon()
    {
        return 'format-quote';
    }

    public static function getKeywords()
    {
        return ['testimonial', 'quote', 'review', 'customer', 'feedback'];
    }

    public static function render($attributes, $content)
    {
        $block_id = self::getBlockId($attributes);
        $class_name = self::getClassName($attributes);

        // Get attributes with defaults
        $content = self::getAttribute($attributes, 'content', '');
        $author = self::getAttribute($attributes, 'author', '');
        $position = self::getAttribute($attributes, 'position', '');
        $company = self::getAttribute($attributes, 'company', '');
        $avatar = self::getAttribute($attributes, 'avatar', null);
        $rating = self::getAttribute($attributes, 'rating', 5);
        $style = self::getAttribute($attributes, 'style', 'default');
        $alignment = self::getAttribute($attributes, 'alignment', 'center');
        $show_avatar = self::getAttribute($attributes, 'showAvatar', true);
        $show_rating = self::getAttribute($attributes, 'showRating', true);
        $background_color = self::getAttribute($attributes, 'backgroundColor', '');
        $text_color = self::getAttribute($attributes, 'textColor', '');

        // Build inline styles
        $inline_styles = [];
        if ($background_color) {
            $inline_styles[] = "background-color: {$background_color}";
        }
        if ($text_color) {
            $inline_styles[] = "color: {$text_color}";
        }
        $style_attr = !empty($inline_styles) ? ' style="' . implode('; ', $inline_styles) . '"' : '';

        // Add style and alignment classes
        $class_name .= " jankx-testimonial-style-{$style} jankx-testimonial-align-{$alignment}";

        // Get avatar URL
        $avatar_url = '';
        if ($avatar && isset($avatar['url'])) {
            $avatar_url = $avatar['url'];
        }

        // Render testimonial
        ob_start();
        ?>
        <div id="<?php echo esc_attr($block_id); ?>" class="<?php echo esc_attr($class_name); ?>"<?php echo $style_attr; ?>>
            <div class="jankx-testimonial-content">
                <?php if ($show_rating && $rating > 0): ?>
                    <div class="jankx-testimonial-rating">
                        <?php for ($i = 1; $i <= 5; $i++): ?>
                            <span class="jankx-star <?php echo $i <= $rating ? 'filled' : 'empty'; ?>"></span>
                        <?php endfor; ?>
                    </div>
                <?php endif; ?>

                <blockquote class="jankx-testimonial-quote">
                    <?php echo wp_kses_post($content); ?>
                </blockquote>

                <?php if ($author || $position || $company): ?>
                    <div class="jankx-testimonial-author">
                        <?php if ($show_avatar && $avatar_url): ?>
                            <div class="jankx-testimonial-avatar">
                                <img src="<?php echo esc_url($avatar_url); ?>" alt="<?php echo esc_attr($author); ?>" />
                            </div>
                        <?php endif; ?>

                        <div class="jankx-testimonial-author-info">
                            <?php if ($author): ?>
                                <div class="jankx-testimonial-author-name"><?php echo esc_html($author); ?></div>
                            <?php endif; ?>

                            <?php if ($position || $company): ?>
                                <div class="jankx-testimonial-author-meta">
                                    <?php if ($position): ?>
                                        <span class="jankx-testimonial-position"><?php echo esc_html($position); ?></span>
                                    <?php endif; ?>

                                    <?php if ($position && $company): ?>
                                        <span class="jankx-testimonial-separator">, </span>
                                    <?php endif; ?>

                                    <?php if ($company): ?>
                                        <span class="jankx-testimonial-company"><?php echo esc_html($company); ?></span>
                                    <?php endif; ?>
                                </div>
                            <?php endif; ?>
                        </div>
                    </div>
                <?php endif; ?>
            </div>
        </div>
        <?php
        return ob_get_clean();
    }
}

📋 Block Registration System

JSON-Based Registration

// block.json
{
    "apiVersion": 2,
    "name": "jankx/testimonial",
    "title": "Testimonial",
    "category": "jankx",
    "icon": "format-quote",
    "description": "Display customer testimonials",
    "supports": {
        "html": false,
        "align": ["wide", "full"]
    },
    "attributes": {
        "author": {
            "type": "string",
            "default": ""
        },
        "content": {
            "type": "string",
            "default": ""
        },
        "avatar": {
            "type": "string",
            "default": ""
        }
    },
    "textdomain": "jankx",
    "editorScript": "file:./assets/js/editor.js",
    "editorStyle": "file:./assets/css/editor.css",
    "style": "file:./assets/css/frontend.css"
}

PHP Block Scanner

class BlockRegistry
{
    private $blocks = [];

    public function scanBlocks()
    {
        $blocksDir = get_template_directory() . '/blocks/';
        $blockDirs = glob($blocksDir . '*/block.json');

        foreach ($blockDirs as $blockJson) {
            $blockData = json_decode(file_get_contents($blockJson), true);
            $this->registerBlock($blockData);
        }
    }

    public function generateGlobalConfig()
    {
        $config = [];
        foreach ($this->blocks as $block) {
            $config[$block['name']] = $block;
        }

        return $config;
    }
}

JavaScript Global Config

// Generated global config
window.JankxBlocks = {
    'jankx/testimonial': {
        name: 'jankx/testimonial',
        title: 'Testimonial',
        category: 'jankx',
        attributes: {
            author: { type: 'string', default: '' },
            content: { type: 'string', default: '' },
            avatar: { type: 'string', default: '' }
        }
    },
    'jankx/hero': {
        // Hero block config
    }
};

🎨 Block Development

Abstract Block Class

abstract class AbstractBlock
{
    protected $blockName;
    protected $blockPath;

    abstract public function register();
    abstract public function render($attributes, $content);

    protected function getBlockPath()
    {
        return get_template_directory() . '/blocks/' . $this->getBlockDir();
    }

    protected function getEditorScript()
    {
        return 'jankx-block-' . $this->getBlockDir() . '-editor';
    }

    protected function getFrontendScript()
    {
        return 'jankx-block-' . $this->getBlockDir() . '-frontend';
    }

    protected function renderTemplate($template, $data = [])
    {
        $templatePath = $this->getBlockPath() . '/templates/' . $template . '.html';
        return $this->renderTemplateFile($templatePath, $data);
    }
}

Block Editor JavaScript

// assets/js/editor.js
(function() {
    const { registerBlockType } = wp.blocks;
    const { createElement } = wp.element;
    const { InspectorControls, useBlockProps } = wp.blockEditor;
    const { PanelBody, TextControl, TextareaControl } = wp.components;

    registerBlockType('jankx/testimonial', {
        edit: function(props) {
            const { attributes, setAttributes } = props;
            const blockProps = useBlockProps();

            return createElement('div', blockProps, [
                createElement(InspectorControls, {}, [
                    createElement(PanelBody, { title: 'Testimonial Settings' }, [
                        createElement(TextControl, {
                            label: 'Author',
                            value: attributes.author,
                            onChange: (author) => setAttributes({ author })
                        }),
                        createElement(TextareaControl, {
                            label: 'Content',
                            value: attributes.content,
                            onChange: (content) => setAttributes({ content })
                        })
                    ])
                ]),
                createElement('div', { className: 'testimonial-preview' }, [
                    createElement('blockquote', {}, attributes.content),
                    createElement('cite', {}, attributes.author)
                ])
            ]);
        },

        save: function(props) {
            return null; // Server-side rendering
        }
    });
})();

Block Frontend JavaScript

// assets/js/frontend.js
(function() {
    // Frontend interactions
    document.addEventListener('DOMContentLoaded', function() {
        const testimonials = document.querySelectorAll('.jankx-testimonial');

        testimonials.forEach(testimonial => {
            // Add frontend functionality
            testimonial.addEventListener('click', function() {
                // Handle testimonial interactions
            });
        });
    });
})();

🎯 Block Categories

Core Blocks

  • Content Blocks: Text, image, video, gallery
  • Layout Blocks: Container, grid, columns
  • Interactive Blocks: Forms, buttons, navigation
  • Dynamic Blocks: Posts, comments, search

Custom Blocks

  • Business Blocks: Testimonials, pricing, team
  • Marketing Blocks: CTA, newsletter, social
  • E-commerce Blocks: Products, cart, checkout
  • Utility Blocks: Breadcrumbs, pagination, search

🔧 Block Configuration

Block Attributes

protected function getBlockAttributes()
{
    return [
        'title' => [
            'type' => 'string',
            'default' => '',
        ],
        'description' => [
            'type' => 'string',
            'default' => '',
        ],
        'image' => [
            'type' => 'object',
            'default' => null,
        ],
        'alignment' => [
            'type' => 'string',
            'default' => 'left',
        ],
        'backgroundColor' => [
            'type' => 'string',
            'default' => '',
        ],
    ];
}

Block Supports

protected function getBlockSupports()
{
    return [
        'html' => false,
        'align' => ['wide', 'full'],
        'spacing' => [
            'margin' => true,
            'padding' => true,
        ],
        'color' => [
            'background' => true,
            'text' => true,
        ],
        'typography' => [
            'fontSize' => true,
            'lineHeight' => true,
        ],
    ];
}

🎨 Block Styling

Editor Styles

// assets/css/editor.scss
.wp-block-jankx-testimonial {
    .testimonial-preview {
        padding: 20px;
        border: 1px solid #ddd;
        border-radius: 8px;

        blockquote {
            font-style: italic;
            margin-bottom: 15px;
        }

        cite {
            font-weight: bold;
            color: #666;
        }
    }
}

Frontend Styles

// assets/css/frontend.scss
.jankx-testimonial {
    background: #f9f9f9;
    padding: 30px;
    border-radius: 12px;
    margin: 20px 0;

    .testimonial-content {
        font-size: 18px;
        line-height: 1.6;
        margin-bottom: 20px;
    }

    .testimonial-author {
        display: flex;
        align-items: center;

        .author-avatar {
            width: 50px;
            height: 50px;
            border-radius: 50%;
            margin-right: 15px;
        }

        .author-info {
            .author-name {
                font-weight: bold;
                margin-bottom: 5px;
            }

            .author-title {
                color: #666;
                font-size: 14px;
            }
        }
    }
}

🔄 Block Rendering

Server-Side Rendering

public function render($attributes, $content)
{
    $data = [
        'author' => $attributes['author'] ?? '',
        'content' => $attributes['content'] ?? '',
        'avatar' => $attributes['avatar'] ?? '',
        'alignment' => $attributes['alignment'] ?? 'left',
        'backgroundColor' => $attributes['backgroundColor'] ?? '',
    ];

    return $this->renderTemplate('frontend', $data);
}

Template Rendering

<!-- templates/frontend.html -->
<div class="jankx-testimonial" style="text-align: ; background-color: ;">
    <div class="testimonial-content">
        <blockquote><h1 id="block-registration-system">Block Registration System</h1>

<blockquote>
  <p><strong>JSON-Based Block Registration with PHP Scanner</strong></p>
</blockquote>

<p>Jankx 2.0 sử dụng hệ thống block registration hiện đại dựa trên JSON, với PHP scanner tự động và global JS config generation.</p>

<h2 id="-registration-architecture">🏗 Registration Architecture</h2>

<h3 id="registration-flow">Registration Flow</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌─────────────────────────────────────┐
│         PHP Block Scanner          │
│  ┌─────────────┐  ┌─────────────┐  │
│  │   Scan      │  │  Parse      │  │
│  │ block.json  │  │   JSON      │  │
│  └─────────────┘  └─────────────┘  │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│         Global Config Gen           │
│  ┌─────────────┐  ┌─────────────┐  │
│  │   PHP       │  │   JS        │  │
│  │  Config     │  │  Config     │  │
│  └─────────────┘  └─────────────┘  │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│         WordPress Registration      │
│  ┌─────────────┐  ┌─────────────┐  │
│  │  register   │  │  Enqueue    │  │
│  │ block_type  │  │  Assets     │  │
│  └─────────────┘  └─────────────┘  │
└─────────────────────────────────────┘
</code></pre></div></div>

<h2 id="-json-based-registration">📋 JSON-Based Registration</h2>

<h3 id="block-json-structure">Block JSON Structure</h3>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"apiVersion"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w">
    </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"jankx/testimonial"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Testimonial"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"jankx"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"icon"</span><span class="p">:</span><span class="w"> </span><span class="s2">"format-quote"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Display customer testimonials with author information"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"keywords"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"testimonial"</span><span class="p">,</span><span class="w"> </span><span class="s2">"quote"</span><span class="p">,</span><span class="w"> </span><span class="s2">"customer"</span><span class="p">,</span><span class="w"> </span><span class="s2">"review"</span><span class="p">],</span><span class="w">
    </span><span class="nl">"supports"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"html"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"align"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"wide"</span><span class="p">,</span><span class="w"> </span><span class="s2">"full"</span><span class="p">],</span><span class="w">
        </span><span class="nl">"spacing"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"margin"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
            </span><span class="nl">"padding"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"color"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"background"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
            </span><span class="nl">"text"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"typography"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"fontSize"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
            </span><span class="nl">"lineHeight"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"attributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"author"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"default"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"content"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"default"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"avatar"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"default"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"authorTitle"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"default"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"alignment"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"default"</span><span class="p">:</span><span class="w"> </span><span class="s2">"left"</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"backgroundColor"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"default"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"textdomain"</span><span class="p">:</span><span class="w"> </span><span class="s2">"jankx"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"editorScript"</span><span class="p">:</span><span class="w"> </span><span class="s2">"file:./assets/js/editor.js"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"editorStyle"</span><span class="p">:</span><span class="w"> </span><span class="s2">"file:./assets/css/editor.css"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"script"</span><span class="p">:</span><span class="w"> </span><span class="s2">"file:./assets/js/frontend.js"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"style"</span><span class="p">:</span><span class="w"> </span><span class="s2">"file:./assets/css/frontend.css"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"render_callback"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jankx</span><span class="se">\\</span><span class="s2">Gutenberg</span><span class="se">\\</span><span class="s2">Blocks</span><span class="se">\\</span><span class="s2">TestimonialBlock::render"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h3 id="block-directory-structure">Block Directory Structure</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>blocks/
├── testimonial/
│   ├── block.json
│   ├── TestimonialBlock.php
│   ├── assets/
│   │   ├── js/
│   │   │   ├── editor.js
│   │   │   └── frontend.js
│   │   ├── css/
│   │   │   ├── editor.css
│   │   │   └── frontend.css
│   │   └── images/
│   │       └── icon.svg
│   └── templates/
│       ├── editor.html
│       └── frontend.html
├── hero/
│   ├── block.json
│   ├── HeroBlock.php
│   └── assets/
│       ├── js/
│       ├── css/
│       └── images/
└── features/
    ├── block.json
    ├── FeaturesBlock.php
    └── assets/
        ├── js/
        ├── css/
        └── images/
</code></pre></div></div>

<h2 id="-php-block-scanner">🔧 PHP Block Scanner</h2>

<h3 id="block-registry-implementation">Block Registry Implementation</h3>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?php</span>
<span class="kn">namespace</span> <span class="nn">Jankx\Gutenberg</span><span class="p">;</span>

<span class="kd">class</span> <span class="nc">BlockRegistry</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="nv">$blocks</span> <span class="o">=</span> <span class="p">[];</span>
    <span class="k">private</span> <span class="nv">$globalConfig</span> <span class="o">=</span> <span class="p">[];</span>
    <span class="k">private</span> <span class="nv">$scanned</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>

    <span class="k">public</span> <span class="k">function</span> <span class="n">scanBlocks</span><span class="p">():</span> <span class="kt">void</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="n">scanned</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="nv">$blocksDir</span> <span class="o">=</span> <span class="nf">get_template_directory</span><span class="p">()</span> <span class="mf">.</span> <span class="s1">'/blocks/'</span><span class="p">;</span>
        <span class="nv">$blockDirs</span> <span class="o">=</span> <span class="nb">glob</span><span class="p">(</span><span class="nv">$blocksDir</span> <span class="mf">.</span> <span class="s1">'*/block.json'</span><span class="p">);</span>

        <span class="k">foreach</span> <span class="p">(</span><span class="nv">$blockDirs</span> <span class="k">as</span> <span class="nv">$blockJson</span><span class="p">)</span> <span class="p">{</span>
            <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">processBlockJson</span><span class="p">(</span><span class="nv">$blockJson</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">generateGlobalConfig</span><span class="p">();</span>
        <span class="nv">$this</span><span class="o">-&gt;</span><span class="n">scanned</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">function</span> <span class="n">processBlockJson</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$blockJson</span><span class="p">):</span> <span class="kt">void</span>
    <span class="p">{</span>
        <span class="nv">$blockData</span> <span class="o">=</span> <span class="nb">json_decode</span><span class="p">(</span><span class="nb">file_get_contents</span><span class="p">(</span><span class="nv">$blockJson</span><span class="p">),</span> <span class="kc">true</span><span class="p">);</span>

        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nv">$blockData</span><span class="p">)</span> <span class="p">{</span>
            <span class="nb">error_log</span><span class="p">(</span><span class="s2">"Invalid JSON in block.json: </span><span class="si">{</span><span class="nv">$blockJson</span><span class="si">}</span><span class="s2">"</span><span class="p">);</span>
            <span class="k">return</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="nv">$blockName</span> <span class="o">=</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'name'</span><span class="p">]</span> <span class="o">??</span> <span class="s1">''</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="k">empty</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">))</span> <span class="p">{</span>
            <span class="nb">error_log</span><span class="p">(</span><span class="s2">"Missing block name in: </span><span class="si">{</span><span class="nv">$blockJson</span><span class="si">}</span><span class="s2">"</span><span class="p">);</span>
            <span class="k">return</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="nv">$this</span><span class="o">-&gt;</span><span class="n">blocks</span><span class="p">[</span><span class="nv">$blockName</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$blockData</span><span class="p">;</span>
        <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">registerBlock</span><span class="p">(</span><span class="nv">$blockData</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">function</span> <span class="n">registerBlock</span><span class="p">(</span><span class="kt">array</span> <span class="nv">$blockData</span><span class="p">):</span> <span class="kt">void</span>
    <span class="p">{</span>
        <span class="nv">$blockName</span> <span class="o">=</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'name'</span><span class="p">];</span>
        <span class="nv">$blockPath</span> <span class="o">=</span> <span class="nb">dirname</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">findBlockJson</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">));</span>

        <span class="nv">$args</span> <span class="o">=</span> <span class="p">[</span>
            <span class="s1">'editor_script'</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getScriptHandle</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">,</span> <span class="s1">'editor'</span><span class="p">),</span>
            <span class="s1">'editor_style'</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getStyleHandle</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">,</span> <span class="s1">'editor'</span><span class="p">),</span>
            <span class="s1">'script'</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getScriptHandle</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">,</span> <span class="s1">'frontend'</span><span class="p">),</span>
            <span class="s1">'style'</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getStyleHandle</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">,</span> <span class="s1">'frontend'</span><span class="p">),</span>
        <span class="p">];</span>

        <span class="c1">// Add render callback if specified</span>
        <span class="k">if</span> <span class="p">(</span><span class="k">isset</span><span class="p">(</span><span class="nv">$blockData</span><span class="p">[</span><span class="s1">'render_callback'</span><span class="p">]))</span> <span class="p">{</span>
            <span class="nv">$args</span><span class="p">[</span><span class="s1">'render_callback'</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'render_callback'</span><span class="p">];</span>
        <span class="p">}</span>

        <span class="c1">// Register block with WordPress</span>
        <span class="nf">register_block_type</span><span class="p">(</span><span class="nv">$blockPath</span><span class="p">,</span> <span class="nv">$args</span><span class="p">);</span>

        <span class="c1">// Register assets</span>
        <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">registerBlockAssets</span><span class="p">(</span><span class="nv">$blockData</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">function</span> <span class="n">registerBlockAssets</span><span class="p">(</span><span class="kt">array</span> <span class="nv">$blockData</span><span class="p">):</span> <span class="kt">void</span>
    <span class="p">{</span>
        <span class="nv">$blockName</span> <span class="o">=</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'name'</span><span class="p">];</span>

        <span class="c1">// Register editor script</span>
        <span class="k">if</span> <span class="p">(</span><span class="k">isset</span><span class="p">(</span><span class="nv">$blockData</span><span class="p">[</span><span class="s1">'editorScript'</span><span class="p">]))</span> <span class="p">{</span>
            <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">registerScript</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">,</span> <span class="s1">'editor'</span><span class="p">,</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'editorScript'</span><span class="p">]);</span>
        <span class="p">}</span>

        <span class="c1">// Register editor style</span>
        <span class="k">if</span> <span class="p">(</span><span class="k">isset</span><span class="p">(</span><span class="nv">$blockData</span><span class="p">[</span><span class="s1">'editorStyle'</span><span class="p">]))</span> <span class="p">{</span>
            <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">registerStyle</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">,</span> <span class="s1">'editor'</span><span class="p">,</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'editorStyle'</span><span class="p">]);</span>
        <span class="p">}</span>

        <span class="c1">// Register frontend script</span>
        <span class="k">if</span> <span class="p">(</span><span class="k">isset</span><span class="p">(</span><span class="nv">$blockData</span><span class="p">[</span><span class="s1">'script'</span><span class="p">]))</span> <span class="p">{</span>
            <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">registerScript</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">,</span> <span class="s1">'frontend'</span><span class="p">,</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'script'</span><span class="p">]);</span>
        <span class="p">}</span>

        <span class="c1">// Register frontend style</span>
        <span class="k">if</span> <span class="p">(</span><span class="k">isset</span><span class="p">(</span><span class="nv">$blockData</span><span class="p">[</span><span class="s1">'style'</span><span class="p">]))</span> <span class="p">{</span>
            <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">registerStyle</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">,</span> <span class="s1">'frontend'</span><span class="p">,</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'style'</span><span class="p">]);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">function</span> <span class="n">registerScript</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$blockName</span><span class="p">,</span> <span class="kt">string</span> <span class="nv">$context</span><span class="p">,</span> <span class="kt">string</span> <span class="nv">$scriptPath</span><span class="p">):</span> <span class="kt">void</span>
    <span class="p">{</span>
        <span class="nv">$handle</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getScriptHandle</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">,</span> <span class="nv">$context</span><span class="p">);</span>
        <span class="nv">$dependencies</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getScriptDependencies</span><span class="p">(</span><span class="nv">$context</span><span class="p">);</span>

        <span class="nf">wp_register_script</span><span class="p">(</span>
            <span class="nv">$handle</span><span class="p">,</span>
            <span class="nf">get_theme_file_uri</span><span class="p">(</span><span class="nv">$scriptPath</span><span class="p">),</span>
            <span class="nv">$dependencies</span><span class="p">,</span>
            <span class="err">\</span><span class="nc">Jankx\Jankx</span><span class="o">::</span><span class="nf">getFrameworkVersion</span><span class="p">(),</span>
            <span class="kc">true</span>
        <span class="p">);</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">function</span> <span class="n">registerStyle</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$blockName</span><span class="p">,</span> <span class="kt">string</span> <span class="nv">$context</span><span class="p">,</span> <span class="kt">string</span> <span class="nv">$stylePath</span><span class="p">):</span> <span class="kt">void</span>
    <span class="p">{</span>
        <span class="nv">$handle</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getStyleHandle</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">,</span> <span class="nv">$context</span><span class="p">);</span>

        <span class="nf">wp_register_style</span><span class="p">(</span>
            <span class="nv">$handle</span><span class="p">,</span>
            <span class="nf">get_theme_file_uri</span><span class="p">(</span><span class="nv">$stylePath</span><span class="p">),</span>
            <span class="p">[],</span>
            <span class="err">\</span><span class="nc">Jankx\Jankx</span><span class="o">::</span><span class="nf">getFrameworkVersion</span><span class="p">()</span>
        <span class="p">);</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">function</span> <span class="n">getScriptHandle</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$blockName</span><span class="p">,</span> <span class="kt">string</span> <span class="nv">$context</span><span class="p">):</span> <span class="kt">string</span>
    <span class="p">{</span>
        <span class="nv">$sanitizedName</span> <span class="o">=</span> <span class="nf">sanitize_title</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">);</span>
        <span class="k">return</span> <span class="s2">"jankx-block-</span><span class="si">{</span><span class="nv">$sanitizedName</span><span class="si">}</span><span class="s2">-</span><span class="si">{</span><span class="nv">$context</span><span class="si">}</span><span class="s2">"</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">function</span> <span class="n">getStyleHandle</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$blockName</span><span class="p">,</span> <span class="kt">string</span> <span class="nv">$context</span><span class="p">):</span> <span class="kt">string</span>
    <span class="p">{</span>
        <span class="nv">$sanitizedName</span> <span class="o">=</span> <span class="nf">sanitize_title</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">);</span>
        <span class="k">return</span> <span class="s2">"jankx-block-</span><span class="si">{</span><span class="nv">$sanitizedName</span><span class="si">}</span><span class="s2">-</span><span class="si">{</span><span class="nv">$context</span><span class="si">}</span><span class="s2">"</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">function</span> <span class="n">getScriptDependencies</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$context</span><span class="p">):</span> <span class="kt">array</span>
    <span class="p">{</span>
        <span class="nv">$dependencies</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'wp-blocks'</span><span class="p">,</span> <span class="s1">'wp-element'</span><span class="p">];</span>

        <span class="k">if</span> <span class="p">(</span><span class="nv">$context</span> <span class="o">===</span> <span class="s1">'editor'</span><span class="p">)</span> <span class="p">{</span>
            <span class="nv">$dependencies</span><span class="p">[]</span> <span class="o">=</span> <span class="s1">'wp-editor'</span><span class="p">;</span>
            <span class="nv">$dependencies</span><span class="p">[]</span> <span class="o">=</span> <span class="s1">'wp-components'</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="nv">$dependencies</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">function</span> <span class="n">findBlockJson</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$blockName</span><span class="p">):</span> <span class="kt">string</span>
    <span class="p">{</span>
        <span class="nv">$blocksDir</span> <span class="o">=</span> <span class="nf">get_template_directory</span><span class="p">()</span> <span class="mf">.</span> <span class="s1">'/blocks/'</span><span class="p">;</span>
        <span class="nv">$blockDir</span> <span class="o">=</span> <span class="nb">str_replace</span><span class="p">(</span><span class="s1">'jankx/'</span><span class="p">,</span> <span class="s1">''</span><span class="p">,</span> <span class="nv">$blockName</span><span class="p">);</span>
        <span class="k">return</span> <span class="nv">$blocksDir</span> <span class="mf">.</span> <span class="nv">$blockDir</span> <span class="mf">.</span> <span class="s1">'/block.json'</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="-global-config-generation">🌐 Global Config Generation</h2>

<h3 id="php-config-generator">PHP Config Generator</h3>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">GlobalConfigGenerator</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="nv">$registry</span><span class="p">;</span>

    <span class="k">public</span> <span class="k">function</span> <span class="n">__construct</span><span class="p">(</span><span class="kt">BlockRegistry</span> <span class="nv">$registry</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="nv">$this</span><span class="o">-&gt;</span><span class="n">registry</span> <span class="o">=</span> <span class="nv">$registry</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">function</span> <span class="n">generateConfig</span><span class="p">():</span> <span class="kt">array</span>
    <span class="p">{</span>
        <span class="nv">$config</span> <span class="o">=</span> <span class="p">[];</span>

        <span class="k">foreach</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="n">registry</span><span class="o">-&gt;</span><span class="nf">getBlocks</span><span class="p">()</span> <span class="k">as</span> <span class="nv">$blockName</span> <span class="o">=&gt;</span> <span class="nv">$blockData</span><span class="p">)</span> <span class="p">{</span>
            <span class="nv">$config</span><span class="p">[</span><span class="nv">$blockName</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">normalizeBlockData</span><span class="p">(</span><span class="nv">$blockData</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="nv">$config</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">function</span> <span class="n">normalizeBlockData</span><span class="p">(</span><span class="kt">array</span> <span class="nv">$blockData</span><span class="p">):</span> <span class="kt">array</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="p">[</span>
            <span class="s1">'name'</span> <span class="o">=&gt;</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'name'</span><span class="p">],</span>
            <span class="s1">'title'</span> <span class="o">=&gt;</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'title'</span><span class="p">],</span>
            <span class="s1">'category'</span> <span class="o">=&gt;</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'category'</span><span class="p">],</span>
            <span class="s1">'icon'</span> <span class="o">=&gt;</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'icon'</span><span class="p">],</span>
            <span class="s1">'description'</span> <span class="o">=&gt;</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'description'</span><span class="p">],</span>
            <span class="s1">'keywords'</span> <span class="o">=&gt;</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'keywords'</span><span class="p">]</span> <span class="o">??</span> <span class="p">[],</span>
            <span class="s1">'supports'</span> <span class="o">=&gt;</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'supports'</span><span class="p">]</span> <span class="o">??</span> <span class="p">[],</span>
            <span class="s1">'attributes'</span> <span class="o">=&gt;</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'attributes'</span><span class="p">]</span> <span class="o">??</span> <span class="p">[],</span>
            <span class="s1">'textdomain'</span> <span class="o">=&gt;</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'textdomain'</span><span class="p">]</span> <span class="o">??</span> <span class="s1">'jankx'</span><span class="p">,</span>
        <span class="p">];</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">function</span> <span class="n">outputJSConfig</span><span class="p">():</span> <span class="kt">void</span>
    <span class="p">{</span>
        <span class="nv">$config</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">generateConfig</span><span class="p">();</span>
        <span class="nv">$jsonConfig</span> <span class="o">=</span> <span class="nb">json_encode</span><span class="p">(</span><span class="nv">$config</span><span class="p">,</span> <span class="no">JSON_PRETTY_PRINT</span><span class="p">);</span>

        <span class="nf">add_action</span><span class="p">(</span><span class="s1">'wp_head'</span><span class="p">,</span> <span class="k">function</span><span class="p">()</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$jsonConfig</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">echo</span> <span class="s2">"&lt;script&gt;window.JankxBlocks = </span><span class="si">{</span><span class="nv">$jsonConfig</span><span class="si">}</span><span class="s2">;&lt;/script&gt;"</span><span class="p">;</span>
        <span class="p">});</span>

        <span class="nf">add_action</span><span class="p">(</span><span class="s1">'admin_head'</span><span class="p">,</span> <span class="k">function</span><span class="p">()</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$jsonConfig</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">echo</span> <span class="s2">"&lt;script&gt;window.JankxBlocks = </span><span class="si">{</span><span class="nv">$jsonConfig</span><span class="si">}</span><span class="s2">;&lt;/script&gt;"</span><span class="p">;</span>
        <span class="p">});</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="javascript-global-config">JavaScript Global Config</h3>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Generated global config</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">JankxBlocks</span> <span class="o">=</span> <span class="p">{</span>
    <span class="dl">'</span><span class="s1">jankx/testimonial</span><span class="dl">'</span><span class="p">:</span> <span class="p">{</span>
        <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">jankx/testimonial</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">title</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Testimonial</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">category</span><span class="p">:</span> <span class="dl">'</span><span class="s1">jankx</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">icon</span><span class="p">:</span> <span class="dl">'</span><span class="s1">format-quote</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Display customer testimonials with author information</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">keywords</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">testimonial</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">quote</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">customer</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">review</span><span class="dl">'</span><span class="p">],</span>
        <span class="na">supports</span><span class="p">:</span> <span class="p">{</span>
            <span class="na">html</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
            <span class="na">align</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">wide</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">full</span><span class="dl">'</span><span class="p">],</span>
            <span class="na">spacing</span><span class="p">:</span> <span class="p">{</span>
                <span class="na">margin</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
                <span class="na">padding</span><span class="p">:</span> <span class="kc">true</span>
            <span class="p">},</span>
            <span class="na">color</span><span class="p">:</span> <span class="p">{</span>
                <span class="na">background</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
                <span class="na">text</span><span class="p">:</span> <span class="kc">true</span>
            <span class="p">},</span>
            <span class="na">typography</span><span class="p">:</span> <span class="p">{</span>
                <span class="na">fontSize</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
                <span class="na">lineHeight</span><span class="p">:</span> <span class="kc">true</span>
            <span class="p">}</span>
        <span class="p">},</span>
        <span class="na">attributes</span><span class="p">:</span> <span class="p">{</span>
            <span class="na">author</span><span class="p">:</span> <span class="p">{</span>
                <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">,</span>
                <span class="na">default</span><span class="p">:</span> <span class="dl">''</span>
            <span class="p">},</span>
            <span class="na">content</span><span class="p">:</span> <span class="p">{</span>
                <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">,</span>
                <span class="na">default</span><span class="p">:</span> <span class="dl">''</span>
            <span class="p">},</span>
            <span class="na">avatar</span><span class="p">:</span> <span class="p">{</span>
                <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">,</span>
                <span class="na">default</span><span class="p">:</span> <span class="dl">''</span>
            <span class="p">},</span>
            <span class="na">authorTitle</span><span class="p">:</span> <span class="p">{</span>
                <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">,</span>
                <span class="na">default</span><span class="p">:</span> <span class="dl">''</span>
            <span class="p">},</span>
            <span class="na">alignment</span><span class="p">:</span> <span class="p">{</span>
                <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">,</span>
                <span class="na">default</span><span class="p">:</span> <span class="dl">'</span><span class="s1">left</span><span class="dl">'</span>
            <span class="p">},</span>
            <span class="na">backgroundColor</span><span class="p">:</span> <span class="p">{</span>
                <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">,</span>
                <span class="na">default</span><span class="p">:</span> <span class="dl">''</span>
            <span class="p">}</span>
        <span class="p">},</span>
        <span class="na">textdomain</span><span class="p">:</span> <span class="dl">'</span><span class="s1">jankx</span><span class="dl">'</span>
    <span class="p">},</span>
    <span class="dl">'</span><span class="s1">jankx/hero</span><span class="dl">'</span><span class="p">:</span> <span class="p">{</span>
        <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">jankx/hero</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">title</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Hero Section</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">category</span><span class="p">:</span> <span class="dl">'</span><span class="s1">jankx</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">icon</span><span class="p">:</span> <span class="dl">'</span><span class="s1">align-wide</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Display hero section with title, description and CTA</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">keywords</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">hero</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">banner</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">cta</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">section</span><span class="dl">'</span><span class="p">],</span>
        <span class="na">supports</span><span class="p">:</span> <span class="p">{</span>
            <span class="na">html</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
            <span class="na">align</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">wide</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">full</span><span class="dl">'</span><span class="p">],</span>
            <span class="na">spacing</span><span class="p">:</span> <span class="p">{</span>
                <span class="na">margin</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
                <span class="na">padding</span><span class="p">:</span> <span class="kc">true</span>
            <span class="p">},</span>
            <span class="na">color</span><span class="p">:</span> <span class="p">{</span>
                <span class="na">background</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
                <span class="na">text</span><span class="p">:</span> <span class="kc">true</span>
            <span class="p">}</span>
        <span class="p">},</span>
        <span class="na">attributes</span><span class="p">:</span> <span class="p">{</span>
            <span class="na">title</span><span class="p">:</span> <span class="p">{</span>
                <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">,</span>
                <span class="na">default</span><span class="p">:</span> <span class="dl">''</span>
            <span class="p">},</span>
            <span class="na">description</span><span class="p">:</span> <span class="p">{</span>
                <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">,</span>
                <span class="na">default</span><span class="p">:</span> <span class="dl">''</span>
            <span class="p">},</span>
            <span class="na">backgroundImage</span><span class="p">:</span> <span class="p">{</span>
                <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">,</span>
                <span class="na">default</span><span class="p">:</span> <span class="dl">''</span>
            <span class="p">},</span>
            <span class="na">ctaText</span><span class="p">:</span> <span class="p">{</span>
                <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">,</span>
                <span class="na">default</span><span class="p">:</span> <span class="dl">''</span>
            <span class="p">},</span>
            <span class="na">ctaUrl</span><span class="p">:</span> <span class="p">{</span>
                <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">,</span>
                <span class="na">default</span><span class="p">:</span> <span class="dl">''</span>
            <span class="p">}</span>
        <span class="p">},</span>
        <span class="na">textdomain</span><span class="p">:</span> <span class="dl">'</span><span class="s1">jankx</span><span class="dl">'</span>
    <span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>

<h2 id="-block-registration-flow">🎯 Block Registration Flow</h2>

<h3 id="registration-process">Registration Process</h3>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">BlockRegistrationManager</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="nv">$registry</span><span class="p">;</span>
    <span class="k">private</span> <span class="nv">$configGenerator</span><span class="p">;</span>

    <span class="k">public</span> <span class="k">function</span> <span class="n">__construct</span><span class="p">(</span><span class="kt">BlockRegistry</span> <span class="nv">$registry</span><span class="p">,</span> <span class="kt">GlobalConfigGenerator</span> <span class="nv">$configGenerator</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="nv">$this</span><span class="o">-&gt;</span><span class="n">registry</span> <span class="o">=</span> <span class="nv">$registry</span><span class="p">;</span>
        <span class="nv">$this</span><span class="o">-&gt;</span><span class="n">configGenerator</span> <span class="o">=</span> <span class="nv">$configGenerator</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">function</span> <span class="n">registerAllBlocks</span><span class="p">():</span> <span class="kt">void</span>
    <span class="p">{</span>
        <span class="c1">// 1. Scan all block.json files</span>
        <span class="nv">$this</span><span class="o">-&gt;</span><span class="n">registry</span><span class="o">-&gt;</span><span class="nf">scanBlocks</span><span class="p">();</span>

        <span class="c1">// 2. Generate global config</span>
        <span class="nv">$this</span><span class="o">-&gt;</span><span class="n">configGenerator</span><span class="o">-&gt;</span><span class="nf">outputJSConfig</span><span class="p">();</span>

        <span class="c1">// 3. Register with WordPress</span>
        <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">registerWithWordPress</span><span class="p">();</span>

        <span class="c1">// 4. Enqueue assets</span>
        <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">enqueueBlockAssets</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">function</span> <span class="n">registerWithWordPress</span><span class="p">():</span> <span class="kt">void</span>
    <span class="p">{</span>
        <span class="k">foreach</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="n">registry</span><span class="o">-&gt;</span><span class="nf">getBlocks</span><span class="p">()</span> <span class="k">as</span> <span class="nv">$blockName</span> <span class="o">=&gt;</span> <span class="nv">$blockData</span><span class="p">)</span> <span class="p">{</span>
            <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">registerBlock</span><span class="p">(</span><span class="nv">$blockData</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">function</span> <span class="n">registerBlock</span><span class="p">(</span><span class="kt">array</span> <span class="nv">$blockData</span><span class="p">):</span> <span class="kt">void</span>
    <span class="p">{</span>
        <span class="nv">$blockName</span> <span class="o">=</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'name'</span><span class="p">];</span>
        <span class="nv">$blockPath</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getBlockPath</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">);</span>

        <span class="nv">$args</span> <span class="o">=</span> <span class="p">[</span>
            <span class="s1">'editor_script'</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getScriptHandle</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">,</span> <span class="s1">'editor'</span><span class="p">),</span>
            <span class="s1">'editor_style'</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getStyleHandle</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">,</span> <span class="s1">'editor'</span><span class="p">),</span>
            <span class="s1">'script'</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getScriptHandle</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">,</span> <span class="s1">'frontend'</span><span class="p">),</span>
            <span class="s1">'style'</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getStyleHandle</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">,</span> <span class="s1">'frontend'</span><span class="p">),</span>
        <span class="p">];</span>

        <span class="k">if</span> <span class="p">(</span><span class="k">isset</span><span class="p">(</span><span class="nv">$blockData</span><span class="p">[</span><span class="s1">'render_callback'</span><span class="p">]))</span> <span class="p">{</span>
            <span class="nv">$args</span><span class="p">[</span><span class="s1">'render_callback'</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'render_callback'</span><span class="p">];</span>
        <span class="p">}</span>

        <span class="nf">register_block_type</span><span class="p">(</span><span class="nv">$blockPath</span><span class="p">,</span> <span class="nv">$args</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">function</span> <span class="n">enqueueBlockAssets</span><span class="p">():</span> <span class="kt">void</span>
    <span class="p">{</span>
        <span class="nf">add_action</span><span class="p">(</span><span class="s1">'wp_enqueue_scripts'</span><span class="p">,</span> <span class="k">function</span><span class="p">()</span> <span class="p">{</span>
            <span class="k">foreach</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="n">registry</span><span class="o">-&gt;</span><span class="nf">getBlocks</span><span class="p">()</span> <span class="k">as</span> <span class="nv">$blockName</span> <span class="o">=&gt;</span> <span class="nv">$blockData</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">isBlockUsed</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">))</span> <span class="p">{</span>
                    <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">enqueueBlockAssets</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">);</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">});</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">function</span> <span class="n">isBlockUsed</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$blockName</span><span class="p">):</span> <span class="kt">bool</span>
    <span class="p">{</span>
        <span class="k">global</span> <span class="nv">$post</span><span class="p">;</span>

        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nv">$post</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="nv">$content</span> <span class="o">=</span> <span class="nv">$post</span><span class="o">-&gt;</span><span class="n">post_content</span><span class="p">;</span>
        <span class="k">return</span> <span class="nb">strpos</span><span class="p">(</span><span class="nv">$content</span><span class="p">,</span> <span class="s1">'&lt;!-- wp:'</span> <span class="mf">.</span> <span class="nv">$blockName</span><span class="p">)</span> <span class="o">!==</span> <span class="kc">false</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">function</span> <span class="n">enqueueBlockAssets</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$blockName</span><span class="p">):</span> <span class="kt">void</span>
    <span class="p">{</span>
        <span class="nv">$sanitizedName</span> <span class="o">=</span> <span class="nf">sanitize_title</span><span class="p">(</span><span class="nv">$blockName</span><span class="p">);</span>

        <span class="c1">// Enqueue frontend styles</span>
        <span class="nf">wp_enqueue_style</span><span class="p">(</span><span class="s2">"jankx-block-</span><span class="si">{</span><span class="nv">$sanitizedName</span><span class="si">}</span><span class="s2">-frontend"</span><span class="p">);</span>

        <span class="c1">// Enqueue frontend scripts</span>
        <span class="nf">wp_enqueue_script</span><span class="p">(</span><span class="s2">"jankx-block-</span><span class="si">{</span><span class="nv">$sanitizedName</span><span class="si">}</span><span class="s2">-frontend"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="-block-class-integration">🔧 Block Class Integration</h2>

<h3 id="abstract-block-class">Abstract Block Class</h3>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?php</span>
<span class="kn">namespace</span> <span class="nn">Jankx\Gutenberg\Blocks</span><span class="p">;</span>

<span class="k">abstract</span> <span class="kd">class</span> <span class="nc">AbstractBlock</span>
<span class="p">{</span>
    <span class="k">protected</span> <span class="nv">$blockName</span><span class="p">;</span>
    <span class="k">protected</span> <span class="nv">$blockPath</span><span class="p">;</span>
    <span class="k">protected</span> <span class="nv">$blockData</span><span class="p">;</span>

    <span class="k">public</span> <span class="k">function</span> <span class="n">__construct</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="nv">$this</span><span class="o">-&gt;</span><span class="n">blockName</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getBlockName</span><span class="p">();</span>
        <span class="nv">$this</span><span class="o">-&gt;</span><span class="n">blockPath</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getBlockPath</span><span class="p">();</span>
        <span class="nv">$this</span><span class="o">-&gt;</span><span class="n">blockData</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">loadBlockData</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="k">abstract</span> <span class="k">protected</span> <span class="k">function</span> <span class="n">getBlockName</span><span class="p">():</span> <span class="kt">string</span><span class="p">;</span>

    <span class="k">abstract</span> <span class="k">public</span> <span class="k">function</span> <span class="n">register</span><span class="p">():</span> <span class="kt">void</span><span class="p">;</span>

    <span class="k">abstract</span> <span class="k">public</span> <span class="k">function</span> <span class="n">render</span><span class="p">(</span><span class="kt">array</span> <span class="nv">$attributes</span><span class="p">,</span> <span class="kt">string</span> <span class="nv">$content</span><span class="p">):</span> <span class="kt">string</span><span class="p">;</span>

    <span class="k">protected</span> <span class="k">function</span> <span class="n">getBlockPath</span><span class="p">():</span> <span class="kt">string</span>
    <span class="p">{</span>
        <span class="nv">$blockDir</span> <span class="o">=</span> <span class="nb">str_replace</span><span class="p">(</span><span class="s1">'jankx/'</span><span class="p">,</span> <span class="s1">''</span><span class="p">,</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="n">blockName</span><span class="p">);</span>
        <span class="k">return</span> <span class="nf">get_template_directory</span><span class="p">()</span> <span class="mf">.</span> <span class="s1">'/blocks/'</span> <span class="mf">.</span> <span class="nv">$blockDir</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">protected</span> <span class="k">function</span> <span class="n">loadBlockData</span><span class="p">():</span> <span class="kt">array</span>
    <span class="p">{</span>
        <span class="nv">$blockJsonPath</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="n">blockPath</span> <span class="mf">.</span> <span class="s1">'/block.json'</span><span class="p">;</span>

        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">file_exists</span><span class="p">(</span><span class="nv">$blockJsonPath</span><span class="p">))</span> <span class="p">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="err">\</span><span class="nf">Exception</span><span class="p">(</span><span class="s2">"Block JSON not found: </span><span class="si">{</span><span class="nv">$blockJsonPath</span><span class="si">}</span><span class="s2">"</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="nv">$blockData</span> <span class="o">=</span> <span class="nb">json_decode</span><span class="p">(</span><span class="nb">file_get_contents</span><span class="p">(</span><span class="nv">$blockJsonPath</span><span class="p">),</span> <span class="kc">true</span><span class="p">);</span>

        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nv">$blockData</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="err">\</span><span class="nf">Exception</span><span class="p">(</span><span class="s2">"Invalid JSON in block.json: </span><span class="si">{</span><span class="nv">$blockJsonPath</span><span class="si">}</span><span class="s2">"</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="nv">$blockData</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">protected</span> <span class="k">function</span> <span class="n">getScriptHandle</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$context</span><span class="p">):</span> <span class="kt">string</span>
    <span class="p">{</span>
        <span class="nv">$sanitizedName</span> <span class="o">=</span> <span class="nf">sanitize_title</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="n">blockName</span><span class="p">);</span>
        <span class="k">return</span> <span class="s2">"jankx-block-</span><span class="si">{</span><span class="nv">$sanitizedName</span><span class="si">}</span><span class="s2">-</span><span class="si">{</span><span class="nv">$context</span><span class="si">}</span><span class="s2">"</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">protected</span> <span class="k">function</span> <span class="n">getStyleHandle</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$context</span><span class="p">):</span> <span class="kt">string</span>
    <span class="p">{</span>
        <span class="nv">$sanitizedName</span> <span class="o">=</span> <span class="nf">sanitize_title</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="n">blockName</span><span class="p">);</span>
        <span class="k">return</span> <span class="s2">"jankx-block-</span><span class="si">{</span><span class="nv">$sanitizedName</span><span class="si">}</span><span class="s2">-</span><span class="si">{</span><span class="nv">$context</span><span class="si">}</span><span class="s2">"</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">protected</span> <span class="k">function</span> <span class="n">renderTemplate</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$template</span><span class="p">,</span> <span class="kt">array</span> <span class="nv">$data</span> <span class="o">=</span> <span class="p">[]):</span> <span class="kt">string</span>
    <span class="p">{</span>
        <span class="nv">$templatePath</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="n">blockPath</span> <span class="mf">.</span> <span class="s1">'/templates/'</span> <span class="mf">.</span> <span class="nv">$template</span> <span class="mf">.</span> <span class="s1">'.html'</span><span class="p">;</span>

        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">file_exists</span><span class="p">(</span><span class="nv">$templatePath</span><span class="p">))</span> <span class="p">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="err">\</span><span class="nf">Exception</span><span class="p">(</span><span class="s2">"Template not found: </span><span class="si">{</span><span class="nv">$templatePath</span><span class="si">}</span><span class="s2">"</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="nv">$templateContent</span> <span class="o">=</span> <span class="nb">file_get_contents</span><span class="p">(</span><span class="nv">$templatePath</span><span class="p">);</span>

        <span class="c1">// Basic template engine</span>
        <span class="k">foreach</span> <span class="p">(</span><span class="nv">$data</span> <span class="k">as</span> <span class="nv">$key</span> <span class="o">=&gt;</span> <span class="nv">$value</span><span class="p">)</span> <span class="p">{</span>
            <span class="nv">$templateContent</span> <span class="o">=</span> <span class="nb">str_replace</span><span class="p">(</span><span class="s1">' . $key . '</span><span class="p">,</span> <span class="nv">$value</span><span class="p">,</span> <span class="nv">$templateContent</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="nv">$templateContent</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="example-block-implementation">Example Block Implementation</h3>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?php</span>
<span class="kn">namespace</span> <span class="nn">Jankx\Gutenberg\Blocks</span><span class="p">;</span>

<span class="kd">class</span> <span class="nc">TestimonialBlock</span> <span class="kd">extends</span> <span class="nc">AbstractBlock</span>
<span class="p">{</span>
    <span class="k">protected</span> <span class="k">function</span> <span class="n">getBlockName</span><span class="p">():</span> <span class="kt">string</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="s1">'jankx/testimonial'</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">function</span> <span class="n">register</span><span class="p">():</span> <span class="kt">void</span>
    <span class="p">{</span>
        <span class="nv">$args</span> <span class="o">=</span> <span class="p">[</span>
            <span class="s1">'editor_script'</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getScriptHandle</span><span class="p">(</span><span class="s1">'editor'</span><span class="p">),</span>
            <span class="s1">'editor_style'</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getStyleHandle</span><span class="p">(</span><span class="s1">'editor'</span><span class="p">),</span>
            <span class="s1">'script'</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getScriptHandle</span><span class="p">(</span><span class="s1">'frontend'</span><span class="p">),</span>
            <span class="s1">'style'</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getStyleHandle</span><span class="p">(</span><span class="s1">'frontend'</span><span class="p">),</span>
            <span class="s1">'render_callback'</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="nv">$this</span><span class="p">,</span> <span class="s1">'render'</span><span class="p">],</span>
        <span class="p">];</span>

        <span class="nf">register_block_type</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="n">blockPath</span><span class="p">,</span> <span class="nv">$args</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">function</span> <span class="n">render</span><span class="p">(</span><span class="kt">array</span> <span class="nv">$attributes</span><span class="p">,</span> <span class="kt">string</span> <span class="nv">$content</span><span class="p">):</span> <span class="kt">string</span>
    <span class="p">{</span>
        <span class="nv">$data</span> <span class="o">=</span> <span class="p">[</span>
            <span class="s1">'author'</span> <span class="o">=&gt;</span> <span class="nv">$attributes</span><span class="p">[</span><span class="s1">'author'</span><span class="p">]</span> <span class="o">??</span> <span class="s1">''</span><span class="p">,</span>
            <span class="s1">'content'</span> <span class="o">=&gt;</span> <span class="nv">$attributes</span><span class="p">[</span><span class="s1">'content'</span><span class="p">]</span> <span class="o">??</span> <span class="s1">''</span><span class="p">,</span>
            <span class="s1">'avatar'</span> <span class="o">=&gt;</span> <span class="nv">$attributes</span><span class="p">[</span><span class="s1">'avatar'</span><span class="p">]</span> <span class="o">??</span> <span class="s1">''</span><span class="p">,</span>
            <span class="s1">'authorTitle'</span> <span class="o">=&gt;</span> <span class="nv">$attributes</span><span class="p">[</span><span class="s1">'authorTitle'</span><span class="p">]</span> <span class="o">??</span> <span class="s1">''</span><span class="p">,</span>
            <span class="s1">'alignment'</span> <span class="o">=&gt;</span> <span class="nv">$attributes</span><span class="p">[</span><span class="s1">'alignment'</span><span class="p">]</span> <span class="o">??</span> <span class="s1">'left'</span><span class="p">,</span>
            <span class="s1">'backgroundColor'</span> <span class="o">=&gt;</span> <span class="nv">$attributes</span><span class="p">[</span><span class="s1">'backgroundColor'</span><span class="p">]</span> <span class="o">??</span> <span class="s1">''</span><span class="p">,</span>
        <span class="p">];</span>

        <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">renderTemplate</span><span class="p">(</span><span class="s1">'frontend'</span><span class="p">,</span> <span class="nv">$data</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="-registration-monitoring">📊 Registration Monitoring</h2>

<h3 id="block-usage-tracking">Block Usage Tracking</h3>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">BlockUsageTracker</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="nv">$usage</span> <span class="o">=</span> <span class="p">[];</span>

    <span class="k">public</span> <span class="k">function</span> <span class="n">trackBlockUsage</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$blockName</span><span class="p">):</span> <span class="kt">void</span>
    <span class="p">{</span>
        <span class="nv">$this</span><span class="o">-&gt;</span><span class="n">usage</span><span class="p">[</span><span class="nv">$blockName</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="n">usage</span><span class="p">[</span><span class="nv">$blockName</span><span class="p">]</span> <span class="o">??</span> <span class="mi">0</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">function</span> <span class="n">getBlockUsage</span><span class="p">():</span> <span class="kt">array</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="n">usage</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">function</span> <span class="n">getMostUsedBlocks</span><span class="p">(</span><span class="kt">int</span> <span class="nv">$limit</span> <span class="o">=</span> <span class="mi">10</span><span class="p">):</span> <span class="kt">array</span>
    <span class="p">{</span>
        <span class="nb">arsort</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="n">usage</span><span class="p">);</span>
        <span class="k">return</span> <span class="nb">array_slice</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="n">usage</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">$limit</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">function</span> <span class="n">getUnusedBlocks</span><span class="p">():</span> <span class="kt">array</span>
    <span class="p">{</span>
        <span class="nv">$allBlocks</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="nf">getAllRegisteredBlocks</span><span class="p">();</span>
        <span class="nv">$usedBlocks</span> <span class="o">=</span> <span class="nb">array_keys</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="n">usage</span><span class="p">);</span>

        <span class="k">return</span> <span class="nb">array_diff</span><span class="p">(</span><span class="nv">$allBlocks</span><span class="p">,</span> <span class="nv">$usedBlocks</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">function</span> <span class="n">getAllRegisteredBlocks</span><span class="p">():</span> <span class="kt">array</span>
    <span class="p">{</span>
        <span class="nv">$blocks</span> <span class="o">=</span> <span class="p">[];</span>
        <span class="nv">$blocksDir</span> <span class="o">=</span> <span class="nf">get_template_directory</span><span class="p">()</span> <span class="mf">.</span> <span class="s1">'/blocks/'</span><span class="p">;</span>
        <span class="nv">$blockDirs</span> <span class="o">=</span> <span class="nb">glob</span><span class="p">(</span><span class="nv">$blocksDir</span> <span class="mf">.</span> <span class="s1">'*/block.json'</span><span class="p">);</span>

        <span class="k">foreach</span> <span class="p">(</span><span class="nv">$blockDirs</span> <span class="k">as</span> <span class="nv">$blockJson</span><span class="p">)</span> <span class="p">{</span>
            <span class="nv">$blockData</span> <span class="o">=</span> <span class="nb">json_decode</span><span class="p">(</span><span class="nb">file_get_contents</span><span class="p">(</span><span class="nv">$blockJson</span><span class="p">),</span> <span class="kc">true</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="nv">$blockData</span> <span class="o">&amp;&amp;</span> <span class="k">isset</span><span class="p">(</span><span class="nv">$blockData</span><span class="p">[</span><span class="s1">'name'</span><span class="p">]))</span> <span class="p">{</span>
                <span class="nv">$blocks</span><span class="p">[]</span> <span class="o">=</span> <span class="nv">$blockData</span><span class="p">[</span><span class="s1">'name'</span><span class="p">];</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="nv">$blocks</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<hr />

<table>
  <tbody>
    <tr>
      <td><strong>Next</strong>: <a href="/jankx/gutenberg/layout-system/">Layout System</a></td>
      <td><a href="/jankx/gutenberg/ajax-system/">AJAX System</a></td>
      <td><a href="./frontend-rendering.md">Frontend Rendering</a></td>
    </tr>
  </tbody>
</table>
</blockquote>
    </div>

    <div class="testimonial-author">
        
        <img src="" alt="" class="author-avatar">
        

        <div class="author-info">
            <div class="author-name"></div>
            
            <div class="author-title"></div>
            
        </div>
    </div>
</div>

🚀 Performance Optimization

Lazy Loading Blocks

class LazyBlockLoader
{
    public function shouldLoadBlock($blockName)
    {
        return $this->isBlockUsed($blockName) || $this->isBlockRequested($blockName);
    }

    public function loadBlockAssets($blockName)
    {
        if ($this->shouldLoadBlock($blockName)) {
            $this->enqueueBlockAssets($blockName);
        }
    }
}

Asset Optimization

class BlockAssetManager
{
    public function optimizeAssets()
    {
        // Combine CSS files
        $this->combineCSS();

        // Minify JavaScript
        $this->minifyJS();

        // Optimize images
        $this->optimizeImages();

        // Generate critical CSS
        $this->generateCriticalCSS();
    }
}

🔒 Security Considerations

Input Sanitization

protected function sanitizeAttributes($attributes)
{
    return [
        'author' => sanitize_text_field($attributes['author'] ?? ''),
        'content' => wp_kses_post($attributes['content'] ?? ''),
        'avatar' => esc_url_raw($attributes['avatar'] ?? ''),
    ];
}

Output Escaping

protected function escapeOutput($data)
{
    return [
        'author' => esc_html($data['author']),
        'content' => wp_kses_post($data['content']),
        'avatar' => esc_url($data['avatar']),
    ];
}

🧪 Testing Blocks

Unit Testing

class TestimonialBlockTest extends TestCase
{
    public function testBlockRegistration()
    {
        $block = new TestimonialBlock();
        $block->register();

        $this->assertTrue(block_type_exists('jankx/testimonial'));
    }

    public function testBlockRendering()
    {
        $block = new TestimonialBlock();
        $attributes = ['author' => 'John Doe', 'content' => 'Great product!'];

        $output = $block->render($attributes, '');

        $this->assertStringContainsString('John Doe', $output);
        $this->assertStringContainsString('Great product!', $output);
    }
}

Integration Testing

class BlockIntegrationTest extends TestCase
{
    public function testBlockInEditor()
    {
        // Test block in Gutenberg editor
        $this->loginAsAdmin();
        $this->visit('/wp-admin/post-new.php');

        $this->assertSee('Testimonial');
        $this->click('Testimonial');

        $this->assertSee('Author');
        $this->assertSee('Content');
    }
}

📊 Block Analytics

Usage Tracking

class BlockAnalytics
{
    public function trackBlockUsage($blockName)
    {
        $usage = get_option('jankx_block_usage', []);
        $usage[$blockName] = ($usage[$blockName] ?? 0) + 1;
        update_option('jankx_block_usage', $usage);
    }

    public function getBlockUsage()
    {
        return get_option('jankx_block_usage', []);
    }
}

Performance Monitoring

class BlockPerformanceMonitor
{
    public function measureBlockRenderTime($blockName, $callback)
    {
        $start = microtime(true);
        $result = $callback();
        $end = microtime(true);

        $this->logRenderTime($blockName, $end - $start);

        return $result;
    }
}

Next: Block Registration Layout System AJAX System