Theme Architecture

Theme Architecture

BuddyX Pro uses a modern, component-based architecture with PSR-4 autoloading for organized, maintainable code.

Overview

The theme follows object-oriented principles with a modular component system. Each feature is encapsulated in its own component that implements the Component_Interface.

Key Features:

  • PSR-4 autoloading with fallback
  • Component-based architecture
  • Template tag system
  • Namespace: BuddyxPro\BuddyxPro
  • Minimum requirements: WordPress 5.4+, PHP 8.0+

Directory Structure

buddyx-pro/
├── assets/               # CSS, JS, images, fonts
│   ├── css/
│   ├── js/
│   ├── images/
│   └── svg/
├── external/             # Third-party libraries (Kirki)
├── inc/                  # Theme components (PSR-4 autoloaded)
│   ├── compatibility/    # Plugin integrations
│   │   ├── buddypress/
│   │   ├── woocommerce/
│   │   ├── learndash/
│   │   ├── fluentcart/
│   │   └── ...
│   ├── Customizer/       # Theme options
│   ├── Dynamic_Style/    # CSS generation
│   ├── Helpers/          # Helper classes
│   ├── widgets/          # Custom widgets
│   ├── Component_Interface.php
│   ├── Theme.php         # Main theme class
│   ├── Template_Tags.php # Template tag system
│   └── functions.php     # Entry point function
├── template-parts/       # Reusable template parts
├── vendor/               # Composer autoloader
├── functions.php         # Theme initialization
└── style.css             # Theme stylesheet

Core Architecture

Entry Point (functions.php)

// Setup autoloader (Composer or custom fallback)
if ( file_exists( get_template_directory() . '/vendor/autoload.php' ) ) {
    require get_template_directory() . '/vendor/autoload.php';
} else {
    spl_autoload_register( '_buddyxpro_autoload' );
}

// Load entry point function
require get_template_directory() . '/inc/functions.php';

// Initialize theme
call_user_func( 'BuddyxPro\BuddyxPro\buddyxpro' );

Component Interface

All theme components must implement Component_Interface:

namespace BuddyxPro\BuddyxPro;

interface Component_Interface {
    /**
     * Gets the unique identifier for the theme component.
     *
     * @return string Component slug.
     */
    public function get_slug(): string;

    /**
     * Adds the action and filter hooks to integrate with WordPress.
     */
    public function initialize();
}

Main Theme Class (Theme.php)

The Theme class orchestrates all components:

namespace BuddyxPro\BuddyxPro;

class Theme {
    protected $components = array();
    protected \BuddyxPro\BuddyxPro\Template_Tags $template_tags;

    public function __construct( array $components = array() ) {
        // Register components
        foreach ( $components as $component ) {
            $this->components[ $component->get_slug() ] = $component;
        }
    }

    public function initialize() {
        // Initialize all components
        array_walk( $this->components, function( $component ) {
            $component->initialize();
        });
    }
}

Default Components

Components registered in Theme.php:

  • Localization – Text domain and translations
  • Base_Support – WordPress theme features
  • Editor – Block editor support
  • Accessibility – ARIA labels, skip links
  • Image_Sizes – Custom image sizes
  • Nav_Menus – Menu locations
  • Sidebars – Widget areas
  • Customizer – Theme options
  • Dynamic_Style – CSS generation from customizer
  • Styles – Stylesheet enqueuing
  • Scripts – JavaScript enqueuing
  • Blocks – Gutenberg block support
  • Kirki_Option – Kirki framework integration

Conditional Components:

// Jetpack integration (if active)
if ( defined( 'JETPACK__VERSION' ) ) {
    $components[] = new Jetpack\Component();
}

// Dynamic styles (requires Kirki)
if ( class_exists( 'Kirki' ) ) {
    $components[] = new Dynamic_Style\Component();
}

PSR-4 Autoloading

File-to-Class Mapping

Class namespace maps directly to file path:

Namespace: BuddyxPro\BuddyxPro\Customizer\Component
File:      inc/Customizer/Component.php

Namespace: BuddyxPro\BuddyxPro\Dynamic_Style\Component
File:      inc/Dynamic_Style/Component.php

Custom Autoloader (Fallback)

If Composer is unavailable:

function _buddyxpro_autoload( $class_name ) {
    $namespace = 'BuddyxPro\BuddyxPro';

    if ( 0 !== strpos( $class_name, $namespace . '\\' ) ) {
        return false;
    }

    // Convert namespace to file path
    $parts = explode( '\\', substr( $class_name, strlen( $namespace . '\\' ) ) );
    $path = get_template_directory() . '/inc';

    foreach ( $parts as $part ) {
        $path .= '/' . $part;
    }
    $path .= '.php';

    if ( file_exists( $path ) ) {
        require_once $path;
        return true;
    }

    return false;
}
spl_autoload_register( '_buddyxpro_autoload' );

Template Tag System

Accessing Template Tags

Template tags are accessible via the buddyxpro() function:

// In template files
buddyxpro()->display_header();
buddyxpro()->posted_on();
buddyxpro()->get_version();

Template_Tags Class

The Template_Tags class provides magic method access:

class Template_Tags {
    protected $template_tags = array();

    public function __call( string $method, array $args ) {
        if ( ! isset( $this->template_tags[ $method ] ) ) {
            throw new BadMethodCallException(
                sprintf( 'The template tag %s does not exist.', $method )
            );
        }

        return call_user_func_array(
            $this->template_tags[ $method ]['callback'],
            $args
        );
    }
}

Registering Template Tags

Components implement TemplatingComponentInterface:

namespace BuddyxPro\BuddyxPro\MyComponent;

use BuddyxPro\BuddyxPro\Component_Interface;
use BuddyxPro\BuddyxPro\Templating_Component_Interface;

class Component implements Component_Interface, Templating_Component_Interface {

    public function template_tags(): array {
        return array(
            'my_custom_tag' => array( $this, 'render_custom_tag' ),
        );
    }

    public function render_custom_tag() {
        // Template tag logic
    }
}

Plugin Integration Pattern

Plugin integrations are isolated in inc/compatibility/:

Directory Structure

inc/compatibility/
├── buddypress/
│   ├── buddypress-functions.php
│   └── README.md
├── woocommerce/
│   ├── woocommerce-functions.php
│   └── README.md
├── fluentcart/
│   ├── fluentcart-functions.php
│   ├── HOOKS-REFERENCE.md
│   └── README.md
└── learndash/
    ├── learndash-functions.php
    └── README.md

Conditional Loading

In functions.php:

// Load WooCommerce functions
if ( class_exists( 'WooCommerce' ) ) {
    require get_template_directory() . '/inc/compatibility/woocommerce/woocommerce-functions.php';
}

// Load FluentCart functions
if ( defined( 'FLUENTCART_PLUGIN_FILE_PATH' ) ) {
    require get_template_directory() . '/inc/compatibility/fluentcart/fluentcart-functions.php';
}

// Load LearnDash functions
if ( class_exists( 'SFWD_LMS' ) ) {
    require get_template_directory() . '/inc/compatibility/learndash/learndash-functions.php';
}

Integration Class Pattern

class BuddyXPro_FluentCart_Support {
    private static $instance = null;

    public static function get_instance() {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __construct() {
        $this->init_hooks();
    }

    private function init_hooks() {
        // Add hooks for plugin integration
        add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_styles' ) );
        add_filter( 'body_class', array( $this, 'add_body_classes' ) );
    }
}

// Initialize
BuddyXPro_FluentCart_Support::get_instance();

Customizer Architecture

Kirki Framework

BuddyX Pro uses Kirki for theme options:

// Load Kirki
require get_template_directory() . '/external/include-kirki.php';

// Register config
Kirki::add_config( 'buddyx_config', array(
    'capability'    => 'edit_theme_options',
    'option_type'   => 'theme_mod',
) );

Adding Customizer Options

// Panel
Kirki::add_panel( 'my_panel', array(
    'title'    => __( 'My Panel', 'buddyxpro' ),
    'priority' => 10,
) );

// Section
Kirki::add_section( 'my_section', array(
    'title' => __( 'My Section', 'buddyxpro' ),
    'panel' => 'my_panel',
) );

// Control
Kirki::add_field( 'buddyx_config', array(
    'type'     => 'toggle',
    'settings' => 'my_option',
    'label'    => __( 'Enable Feature', 'buddyxpro' ),
    'section'  => 'my_section',
    'default'  => false,
) );

Dynamic Style Generation

Component-Based CSS

The Dynamic_Style component generates CSS from customizer settings:

namespace BuddyxPro\BuddyxPro\Dynamic_Style;

class Component implements Component_Interface {

    public function initialize() {
        add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_dynamic_css' ), 20 );
    }

    public function enqueue_dynamic_css() {
        $css = $this->generate_css();
        wp_add_inline_style( 'buddyx-style', $css );
    }

    private function generate_css() {
        $primary_color = get_theme_mod( 'primary_color', '#ef5455' );

        return "
            :root {
                --color-primary: {$primary_color};
            }
        ";
    }
}

Widget Registration

Custom Widgets

Widgets are in inc/widgets/:

// Load widgets
require get_template_directory() . '/inc/widgets/login-widget.php';
require get_template_directory() . '/inc/widgets/bp-profile-completion-widget.php';

Widget Class Example

class BuddyX_Login_Widget extends WP_Widget {

    public function __construct() {
        parent::__construct(
            'buddyx_login_widget',
            __( 'BuddyX Login', 'buddyxpro' ),
            array( 'description' => __( 'Login form widget', 'buddyxpro' ) )
        );
    }

    public function widget( $args, $instance ) {
        // Widget output
    }
}

// Register widget
function buddyx_register_login_widget() {
    register_widget( 'BuddyX_Login_Widget' );
}
add_action( 'widgets_init', 'buddyx_register_login_widget' );

Helper Functions

Modular Helpers (inc/Helpers/)

// Login and registration popups
require_once get_template_directory() . '/inc/Helpers/Login_Forms.php';

// BuddyPress activity enhancements
require_once get_template_directory() . '/inc/Helpers/Activity_Functions.php';

// Side panel functionality
require_once get_template_directory() . '/inc/Helpers/Side_Panel.php';

// Dark mode toggle
require_once get_template_directory() . '/inc/Helpers/Dark_Mode.php';

Utility Functions (inc/extra.php)

Core template functions:

// Content wrapper hooks
add_action( 'buddyx_before_content', 'buddyx_content_top' );
add_action( 'buddyx_after_content', 'buddyx_content_bottom' );

// Sub header
add_action( 'buddyx_sub_header', 'buddyx_sub_header' );

// Menu icons
add_action( 'buddyx_header', 'buddyx_site_menu_icon' );

Version Management

Theme Version

// Get theme version
$version = wp_get_theme( get_template() )->get( 'Version' );

// Via template tags
buddyxpro()->get_version();

Asset Versioning

// Development: file modification time
// Production: theme version
buddyxpro()->get_asset_version( $filepath );

Best Practices

Do

  • Use PSR-4 autoloading for new classes
  • Implement Component_Interface for new components
  • Use template tag system for reusable functions
  • Add plugin integrations to inc/compatibility/
  • Use Kirki for customizer options
  • Prefix all functions with buddyx or buddyxpro
  • Use namespace BuddyxPro\BuddyxPro for classes

Don’t

  • Modify core theme files directly
  • Add business logic to templates
  • Use global variables without prefixing
  • Skip component initialization
  • Bypass the autoloader
  • Hardcode plugin paths

Related Documentation

Last updated: January 31, 2026