• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Automating CI/CD Workflows for Enterprise Full Site Editing (FSE) Block Themes and theme.json Using Modern PHP 8.x Features

Automating CI/CD Workflows for Enterprise Full Site Editing (FSE) Block Themes and theme.json Using Modern PHP 8.x Features

Leveraging PHP 8.x for Robust FSE Theme CI/CD Pipelines

Enterprise-grade WordPress Full Site Editing (FSE) themes, particularly those heavily reliant on the `theme.json` configuration and complex block structures, demand a sophisticated CI/CD strategy. Traditional approaches often fall short when dealing with the nuances of FSE, such as dynamic template generation, style variations, and the intricate interplay between PHP and JavaScript. This post outlines an advanced CI/CD workflow, emphasizing modern PHP 8.x features to enhance reliability, performance, and maintainability for FSE block themes.

Automated Theme Linting and Validation with PHP 8.x Attributes

A critical first step in any CI pipeline is rigorous code validation. For FSE themes, this extends beyond standard PHP linting to include validation of `theme.json` structure and adherence to WordPress coding standards. PHP 8.x attributes provide a powerful, declarative way to annotate code, enabling custom linters and static analysis tools to enforce theme-specific rules more effectively.

Consider a scenario where we want to enforce specific naming conventions or required properties within custom block classes. We can define an attribute to mark these classes and then use a static analysis tool (or a custom PHP script) to check for compliance.

Defining Custom Validation Attributes

First, let’s define a simple attribute to mark blocks that require a specific configuration key in their `block.json` or `theme.json` context.

namespace MyTheme\Attributes;

use Attribute;

#[Attribute(Attribute::TARGET_CLASS)]
class RequiresThemeJsonConfig {
    public function __construct(public string $configKey) {}
}

Now, let’s apply this attribute to a hypothetical custom block class.

namespace MyTheme\Blocks;

use MyTheme\Attributes\RequiresThemeJsonConfig;
use WP_Block_Type_Registry;

#[RequiresThemeJsonConfig('my_custom_block_settings')]
class CustomFeaturedPostBlock {
    public static function register() {
        register_block_type( __DIR__ . '/custom-featured-post' );
    }

    // ... other block methods
}

Implementing a Custom Linter/Validator

We can then create a PHP script that uses reflection to find classes with this attribute and check their corresponding `block.json` or `theme.json` configurations. This script can be integrated into the CI pipeline.

namespace MyTheme\CI;

use ReflectionClass;
use MyTheme\Attributes\RequiresThemeJsonConfig;
use Exception;

class ThemeValidator {
    public static function validateBlocks() {
        $blockClasses = [
            \MyTheme\Blocks\CustomFeaturedPostBlock::class,
            // ... other block classes
        ];

        foreach ($blockClasses as $blockClass) {
            $reflectionClass = new ReflectionClass($blockClass);
            $attributes = $reflectionClass->getAttributes(RequiresThemeJsonConfig::class);

            if (!empty($attributes)) {
                $attribute = $attributes[0]->newInstance();
                $configKey = $attribute->configKey;

                // In a real scenario, this would involve parsing theme.json or block.json
                // and checking for the presence of $configKey.
                // For demonstration, we'll simulate a check.
                if (!self::themeJsonContainsConfig($configKey)) {
                    throw new Exception("Block '{$blockClass}' requires theme.json config key '{$configKey}' which is missing.");
                }
            }
        }
    }

    private static function themeJsonContainsConfig(string $key): bool {
        // Simulate checking theme.json. In production, load and parse theme.json.
        $mockThemeJson = [
            'version' => 2,
            'settings' => [
                'color' => [],
                'typography' => [],
                'layout' => [],
                'my_custom_block_settings' => [ // This key is present
                    'enabled' => true,
                    'default_style' => 'modern'
                ]
            ]
        ];
        return isset($mockThemeJson['settings'][$key]);
    }
}

// Example usage within a CI script:
try {
    ThemeValidator::validateBlocks();
    echo "Block validation passed.\n";
} catch (Exception $e) {
    echo "Block validation failed: " . $e->getMessage() . "\n";
    exit(1); // Indicate failure
}

Optimizing `theme.json` Processing with PHP 8.x Typed Properties and Union Types

The `theme.json` file is the cornerstone of FSE. Efficiently parsing and validating its structure, especially when dealing with complex nested configurations for styles, typography, and layout, is crucial. PHP 8.x’s typed properties and union types can significantly improve the robustness and readability of the code that handles this data.

Structured Data Representation

Instead of relying on associative arrays, we can define PHP classes that mirror the structure of `theme.json`. This allows for strong typing, autocompletion, and compile-time checks.

namespace MyTheme\Config;

class ThemeJsonSettings {
    public array $color = [];
    public array $typography = [];
    public array $layout = [];
    public ?array $spacing = null; // Optional property
    public array $custom = []; // For custom block settings

    // PHP 8.1+ constructor property promotion
    public function __construct(
        array $color = [],
        array $typography = [],
        array $layout = [],
        ?array $spacing = null,
        array $custom = []
    ) {
        $this->color = $color;
        $this->typography = $typography;
        $this->layout = $layout;
        $this->spacing = $spacing;
        $this->custom = $custom;
    }
}

class ThemeJson {
    public int $version;
    public ThemeJsonSettings $settings;

    public function __construct(int $version, ThemeJsonSettings $settings) {
        $this->version = $version;
        $this->settings = $settings;
    }

    public static function fromJson(string $jsonString): self {
        $data = json_decode($jsonString, true, 512, JSON_THROW_ON_ERROR);

        // Basic validation and type casting
        if (!isset($data['version']) || !is_int($data['version'])) {
            throw new \InvalidArgumentException("Invalid theme.json: 'version' is missing or not an integer.");
        }

        $settingsData = $data['settings'] ?? [];
        $settings = new ThemeJsonSettings(
            $settingsData['color'] ?? [],
            $settingsData['typography'] ?? [],
            $settingsData['layout'] ?? [],
            $settingsData['spacing'] ?? null,
            $settingsData['custom'] ?? []
        );

        return new self($data['version'], $settings);
    }
}

This structured approach makes it easier to access and manipulate theme settings within PHP, reducing the likelihood of runtime errors due to unexpected data types or missing keys. The `JSON_THROW_ON_ERROR` flag in `json_decode` is a PHP 7.3+ feature that simplifies error handling for JSON parsing.

Advanced Asset Management and Enqueueing with PHP 8.x Match Expressions

Managing JavaScript and CSS assets for FSE themes can become complex, especially when different blocks or template parts require specific dependencies or conditional loading. PHP 8.x’s `match` expression offers a more concise and readable alternative to long `if/elseif/else` chains for handling these scenarios.

Conditional Asset Enqueuing Based on Block Usage

Imagine a scenario where a specific JavaScript module is only needed if a custom block is present on the current page. We can use `has_block()` to detect this and then enqueue the asset.

function mytheme_enqueue_block_assets() {
    // Example: Enqueue a custom JS for a specific block
    if ( has_block( 'mytheme/custom-gallery' ) ) {
        wp_enqueue_script(
            'mytheme-custom-gallery-js',
            get_template_directory_uri() . '/assets/js/custom-gallery.js',
            ['wp-blocks', 'wp-element', 'wp-editor'],
            filemtime( get_template_directory() . '/assets/js/custom-gallery.js' )
        );
    }

    // Example: Enqueue a global CSS for FSE features
    wp_enqueue_style(
        'mytheme-fse-global-styles',
        get_template_directory_uri() . '/assets/css/fse-global.css',
        [],
        filemtime( get_template_directory() . '/assets/css/fse-global.css' )
    );
}
add_action( 'enqueue_block_assets', 'mytheme_enqueue_block_assets' );

Now, let’s consider a more complex scenario where we need to enqueue different sets of assets based on the *type* of FSE template being rendered (e.g., single post, page, archive). PHP 8.x’s `match` expression shines here.

function mytheme_conditional_template_assets() {
    $template_type = get_page_template_slug(); // Or other methods to determine template type

    match ($template_type) {
        'templates/single-post.html' => {
            wp_enqueue_script('mytheme-single-post-enhancements', get_template_directory_uri() . '/assets/js/single-post.js', ['wp-jquery'], '1.0', true);
            wp_enqueue_style('mytheme-single-post-styles', get_template_directory_uri() . '/assets/css/single-post.css', [], '1.0');
        },
        'templates/page.html' => {
            wp_enqueue_script('mytheme-page-layout-js', get_template_directory_uri() . '/assets/js/page-layout.js', ['wp-element'], '1.0', true);
        },
        'templates/archive.html' => {
            wp_enqueue_script('mytheme-archive-filter-js', get_template_directory_uri() . '/assets/js/archive-filter.js', ['wp-jquery-ui-core'], '1.0', true);
        },
        default => {
            // Default assets or no specific assets for other templates
        }
    };
}
add_action( 'template_redirect', 'mytheme_conditional_template_assets' );

The `match` expression is exhaustive, meaning all possible cases must be handled, or a `default` case must be provided, preventing subtle bugs that can arise from incomplete `if/elseif` structures. The `filemtime()` function is used to automatically version assets based on their modification time, ensuring cache busting during development and deployment.

Automating `theme.json` Style Variations and Theme Building

FSE themes often define multiple style variations within `theme.json`. Automating the generation and testing of these variations is crucial for maintaining consistency and preventing regressions. This can involve PHP scripts that programmatically modify `theme.json` or generate CSS based on its settings.

Programmatic `theme.json` Modification for Variations

During the build process, we might want to generate different `theme.json` files for each style variation, or perhaps inject specific variables into a base `theme.json`.

namespace MyTheme\Build;

use MyTheme\Config\ThemeJson;
use Exception;

class ThemeJsonGenerator {
    private string $baseThemeJsonPath;
    private string $outputDir;

    public function __construct(string $baseThemeJsonPath, string $outputDir) {
        $this->baseThemeJsonPath = $baseThemeJsonPath;
        $this->outputDir = $outputDir;
    }

    public function generateVariations(array $variations): void {
        if (!is_dir($this->outputDir)) {
            mkdir($this->outputDir, 0755, true);
        }

        $baseJsonContent = file_get_contents($this->baseThemeJsonPath);
        if ($baseJsonContent === false) {
            throw new Exception("Could not read base theme.json from: {$this->baseThemeJsonPath}");
        }

        $baseThemeJson = ThemeJson::fromJson($baseJsonContent);

        foreach ($variations as $variationName => $variationSettings) {
            // Deep merge variation settings into base settings
            $mergedSettings = $this->deepMerge(
                ['version' => $baseThemeJson->version, 'settings' => $baseThemeJson->settings],
                ['settings' => $variationSettings]
            );

            $outputFilePath = "{$this->outputDir}/theme-{$variationName}.json";
            file_put_contents($outputFilePath, json_encode($mergedSettings, JSON_PRETTY_PRINT));
            echo "Generated: {$outputFilePath}\n";
        }
    }

    // Simple deep merge for demonstration. A more robust solution might be needed.
    private function deepMerge(array $base, array $override): array {
        foreach ($override as $key => $value) {
            if (is_array($value) && isset($base[$key]) && is_array($base[$key])) {
                $base[$key] = $this->deepMerge($base[$key], $value);
            } else {
                $base[$key] = $value;
            }
        }
        return $base;
    }
}

// Example usage in a build script (e.g., a Makefile or a custom PHP build tool)
$variations = [
    'default' => [
        'color' => [
            'primary' => '#0073aa',
            'secondary' => '#d3d3d3',
        ],
        'typography' => [
            'fontSizes' => [
                ['size' => '1rem', 'slug' => 'base'],
                ['size' => '1.5rem', 'slug' => 'large'],
            ],
        ],
    ],
    'dark' => [
        'color' => [
            'background' => '#1a1a1a',
            'text' => '#ffffff',
            'primary' => '#00bcd4',
        ],
        'typography' => [
            'fontSizes' => [
                ['size' => '0.9rem', 'slug' => 'small'],
                ['size' => '1.2rem', 'slug' => 'medium'],
            ],
        ],
    ],
];

$generator = new ThemeJsonGenerator('theme.json', './build/theme-json/');
try {
    $generator->generateVariations($variations);
} catch (Exception $e) {
    echo "Error generating theme.json variations: " . $e->getMessage() . "\n";
    exit(1);
}

This script leverages the structured `ThemeJson` class and performs a deep merge of variation-specific settings into a base configuration. The output can then be used by WordPress or other build tools.

Integrating with CI/CD Platforms (GitHub Actions Example)

These PHP-based validation and build steps can be seamlessly integrated into popular CI/CD platforms like GitHub Actions. A typical workflow would involve:

  • Checking out the repository.
  • Setting up a PHP environment (e.g., using `shivammathur/setup-php`).
  • Installing Composer dependencies.
  • Running PHP linting (`php -l`).
  • Executing the custom theme validator script (e.g., `php build/validate-theme.php`).
  • Running automated tests (unit, integration, E2E).
  • Building theme assets (e.g., compiling SCSS, JS).
  • Generating `theme.json` variations.
  • Deploying to staging or production environments upon successful builds.

Here’s a simplified example of a GitHub Actions workflow file (`.github/workflows/ci.yml`):

name: CI Pipeline

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3

    - name: Setup PHP
      uses: shivammathur/setup-php@v2
      with:
        php-version: '8.1' # Use a specific PHP 8.x version
        extensions: mbstring, xml, json, zip # Required extensions
        coverage: none

    - name: Install Composer dependencies
      run: composer install --prefer-dist --no-progress --no-suggest

    - name: PHP Linting
      run: find . -name "*.php" -print0 | xargs -0 php -l

    - name: Run Custom Theme Validator
      run: php build/validate-theme.php # Assumes your validator script is here

    - name: Run Theme JSON Generator
      run: php build/generate-theme-json.php # Assumes your generator script is here

    # Add steps for running PHPUnit tests, WP-CLI commands, JS linters, etc.
    # - name: Run PHPUnit Tests
    #   run: vendor/bin/phpunit tests/

    # - name: Build theme assets (example)
    #   run: npm install && npm run build

    # - name: Deploy to staging (example)
    #   if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    #   run: ./deploy.sh staging

By integrating these advanced PHP 8.x features and robust validation steps into your CI/CD pipeline, you can significantly improve the quality, stability, and maintainability of your enterprise FSE block themes.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Debugging Guide: Diagnosing PHP-FPM child process pool exhaustion in multi-site network environments with modern tools
  • Debugging and Resolving complex namespace class loading collisions issues during heavy concurrent database traffic
  • Step-by-Step Guide: Offloading high-frequency customer support tickets metadata writes to a Redis KV store
  • How to refactor legacy event ticket registers queries using modern WP_Query and custom Transient caching
  • Step-by-Step Guide: Offloading high-frequency member profile directories metadata writes to a Redis KV store

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (662)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (873)
  • PHP (5)
  • PHP Development (49)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (647)
  • SEO & Growth (492)
  • Server (118)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (726)
  • WordPress Theme Development (357)

Recent Posts

  • Debugging Guide: Diagnosing PHP-FPM child process pool exhaustion in multi-site network environments with modern tools
  • Debugging and Resolving complex namespace class loading collisions issues during heavy concurrent database traffic
  • Step-by-Step Guide: Offloading high-frequency customer support tickets metadata writes to a Redis KV store

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (873)
  • WordPress Plugin Development (726)
  • Debugging & Troubleshooting (662)
  • Security & Compliance (647)
  • SEO & Growth (492)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala