Architecture Overview

WPZylos is designed to solve one critical problem: enabling multiple plugins built with the same framework to run simultaneously without conflicts.

Core Architecture Principles

  1. Isolated Containers — Each plugin has its own PSR-11 container
  2. Context-Based Prefixing — Hooks, options, and transients are automatically scoped
  3. No Global State — Framework does not use global variables or singletons
  4. WordPress-First — Builds on WordPress APIs, doesn't replace them
  5. PHP-Scoper Ready — Designed for namespace isolation in distribution

High-Level Architecture

graph TB
    subgraph Plugin A
        PA[plugin-a.php] --> CA[PluginContext A]
        CA --> CONTA[Container A]
        CONTA --> SA[Services A]
    end

    subgraph Plugin B
        PB[plugin-b.php] --> CB[PluginContext B]
        CB --> CONTB[Container B]
        CONTB --> SB[Services B]
    end

    subgraph WordPress
        WP[WordPress Core]
        HOOKS[Hook System]
        DB[(Database)]
    end

    SA --> |prefixed hooks| HOOKS
    SB --> |prefixed hooks| HOOKS
    SA --> |prefixed options| DB
    SB --> |prefixed options| DB

    HOOKS --> WP
    DB --> WP

Package Layers

graph TD
    subgraph Application Layer
        SCAFFOLD[wpzylos-scaffold]
        WPCLI[wpzylos-wp-cli]
        CLIDEV[wpzylos-cli-devtool]
    end

    subgraph Feature Layer
        HTTP[wpzylos-http]
        ROUTING[wpzylos-routing]
        VIEWS[wpzylos-views]
        VALIDATION[wpzylos-validation]
        DATABASE[wpzylos-database]
        MIGRATIONS[wpzylos-migrations]
        I18N[wpzylos-i18n]
        SECURITY[wpzylos-security]
    end

    subgraph Foundation Layer
        CORE[wpzylos-core]
        CONTAINER[wpzylos-container]
        HOOKS[wpzylos-hooks]
        CONFIG[wpzylos-config]
        CLICORE[wpzylos-cli-core]
    end

    SCAFFOLD --> CORE
    WPCLI --> CLICORE
    CLIDEV --> CLICORE

    HTTP --> CORE
    ROUTING --> HTTP
    VIEWS --> CORE
    VALIDATION --> CORE
    DATABASE --> CORE
    MIGRATIONS --> DATABASE
    I18N --> CORE
    SECURITY --> CORE

    CORE --> CONTAINER
    CORE --> HOOKS
    CORE --> CONFIG
    CLICORE --> CORE

Boot Flow

sequenceDiagram
    participant WP as WordPress
    participant Plugin as plugin.php
    participant Context as PluginContext
    participant Container as Container
    participant App as Application
    participant Providers as ServiceProviders

    WP->>Plugin: Load plugin file
    Plugin->>Plugin: require autoload.php
    Plugin->>Context: Create PluginContext
    Plugin->>Container: Create Container
    Plugin->>App: new Application(context, container)

    WP->>WP: plugins_loaded

    App->>Providers: Register all providers
    Providers->>Container: Bind services
    App->>Providers: Boot all providers
    Providers->>WP: Register hooks

Why We Did It This Way

Isolated Containers (Not Singletons)

Problem: Traditional frameworks use singletons for the container. When two plugins share the same framework, they share the same container, causing service conflicts.

Solution: Each plugin instantiates its own Container. Services are scoped to their plugin.

// Plugin A
$containerA = new Container();
$containerA->bind(Logger::class, fn() => new Logger('plugin-a'));

// Plugin B (completely independent)
$containerB = new Container();
$containerB->bind(Logger::class, fn() => new Logger('plugin-b'));

Context-Based Prefixing

Problem: Two plugins using add_action('my_custom_hook', ...) would fire each other's hooks.

Solution: The PluginContext provides prefixed hook names for custom hooks while allowing direct access to WordPress core hooks.

// Custom hooks are prefixed
$context->hook('settings_saved'); // Returns: mp_settings_saved

// WordPress hooks remain unprefixed
$hooks->wpAction('init', fn() => ...); // Hooks into 'init'

Lightweight Activation

Problem: Heavy boot logic in activation hooks causes timeouts and database errors.

Solution: Activation hooks only perform essential database operations. Full boot happens on subsequent requests.

register_activation_hook(__FILE__, function () {
    // Good: Only essential operations
    update_option('mp_version', '1.0.0');

    // Bad: Not here: service providers, routes, views
});

WordPress-First Philosophy

Problem: Some frameworks try to abstract WordPress away, causing integration headaches.

Solution: WPZylos enhances WordPress APIs rather than replacing them. All WordPress functions remain available.

// WPZylos wraps WordPress, doesn't replace it
$security->verifyNonce($nonce, 'action');
// Internally calls: wp_verify_nonce($nonce, 'mp_action')

// Direct WordPress access is always available
wp_nonce_field('my_action', 'my_nonce');

Component Responsibilities

ComponentResponsibility
PluginContextPlugin identity (slug, prefix, text domain)
ContainerService registration and resolution
ApplicationBoot orchestration and lifecycle
ServiceProviderService registration in container
HookManagerWordPress hook integration
ConfigRepositoryConfiguration access

File Structure

my-plugin/
+-- my-plugin.php          # Entry point
+-- composer.json
+-- bootstrap/
|   +-- app.php            # Application factory
+-- config/
|   +-- app.php            # Application config
|   +-- services.php       # Provider config
+-- src/
|   +-- Providers/         # Service providers
|   +-- Http/
|   |   +-- Controllers/   # Request handlers
|   |   +-- Middleware/    # Request middleware
|   +-- Models/            # Data models
|   +-- Services/          # Business logic
+-- resources/
|   +-- views/             # PHP templates
+-- routes/
|   +-- admin.php          # Admin routes
|   +-- ajax.php           # AJAX routes
+-- tests/                 # PHPUnit tests

Next Steps