VOOZH about

URL: https://deepwiki.com/calevans/staticforge/7-feature-system

⇱ Feature System | calevans/staticforge | DeepWiki


Loading...
Last indexed: 11 February 2026 (5f6a2a)
Menu

Feature System

This document describes StaticForge's plugin architecture. Features are self-contained modules that extend the site generation process by listening to events and providing functionality like content rendering, navigation building, SEO optimization, and deployment assistance.

For information about the event system that features hook into, see Event System. For details on individual built-in features, see Built-in Features Reference.


Feature Architecture

StaticForge implements a plugin system where nearly all functionality is provided by features. The core system is minimal—it bootstraps the application, fires events, and manages the build pipeline. Features do the actual work.

Feature Interface Contract

All features must implement FeatureInterface, which requires two methods:

  • getName(): string - Returns the unique feature identifier
  • register(EventManager $eventManager, Container $container): void - Registers event listeners

Most features extend BaseFeature, which provides common infrastructure and automatic event registration from the $eventListeners array property.

Feature Class Structure:


Sources: src/Core/FeatureManager.php240-260 content/development/features.md20-48

Feature Lifecycle

Features follow a simple lifecycle:

  1. Discovery - FeatureManager scans feature directories
  2. Instantiation - Feature class is instantiated via new
  3. Registration - register() method called with EventManager and Container
  4. Event Handling - Feature methods called when subscribed events fire
  5. Cleanup - Optional DESTROY event for resource cleanup

Feature Registration Diagram:


Sources: src/Core/FeatureManager.php186-231 content/development/features.md74-85

Event Listener Registration

Features declare their event subscriptions in the $eventListeners array. BaseFeature automatically registers these during register():


Event listener methods must have this signature:


Sources: content/development/features.md34-47 content/development/events.md84-109


Feature Discovery and Priority

StaticForge implements a three-tier feature discovery system with explicit override semantics. Features are loaded in priority order, with higher-priority features taking precedence when names conflict.

Discovery Priority System

Feature Discovery Hierarchy:


Sources: src/Core/FeatureManager.php52-96 content/development/features.md128-140

Feature Loading Process

The FeatureManager::loadFeatures() method orchestrates discovery:

OrderDirectoryTypePriority
1stsrc/Features/CustomHighest
2ndComposer packagesComposerMedium
3rdvendor/eicc/staticforge/src/Features/StandardLowest

Key mechanisms:

  1. Directory scanning - discoverFeatureDirectories() finds directories with Feature.php files
  2. Namespace resolution - getPossibleFeatureClasses() tries multiple namespaces per directory name
  3. Duplicate checking - First feature with a given name wins, subsequent duplicates are skipped
  4. Disabled checking - Features in siteconfig.yaml disabled_features list are not loaded

Sources: src/Core/FeatureManager.php146-182 src/Core/FeatureManager.php269-280

Composer Feature Discovery

Composer packages can register features via composer.json:


The discoverComposerFeatures() method:

  1. Reads vendor/composer/installed.json
  2. Checks each package for extra.staticforge.feature
  3. Instantiates and registers the specified class

Sources: src/Core/FeatureManager.php286-369

Feature Class Resolution

When loading from a directory, FeatureManager tries multiple namespace patterns:


This allows flexibility in project autoloading configuration while maintaining a standard directory structure.

Sources: src/Core/FeatureManager.php238-280


Creating Custom Features

Custom features extend StaticForge's functionality without modifying core code. They are placed in src/Features/ and automatically discovered during bootstrap.

Feature Directory Structure

src/Features/
└── MyFeature/
 ├── Feature.php # Required: Main feature class
 ├── Services/ # Optional: Service classes
 │ └── MyService.php
 └── Models/ # Optional: Data models
 └── MyModel.php

Minimal Feature Implementation

Basic Feature Template:


Sources: content/development/features.md50-70 content/development/architecture.md186-212

Event Listener Patterns

Features interact with the build process through event listeners. Common patterns:

EventPriority RangeUse Case
CREATE100-500Initialize state, set defaults
POST_GLOB50-250Build site structure, analyze all files
PRE_RENDER100-500Enrich individual file metadata
RENDER100-200Transform content (Markdown, etc.)
POST_RENDER100-500Collect data for aggregation
POST_LOOP50-200Generate site-wide artifacts

Priority Guidelines:

  • 50-100: Early execution (structure building)
  • 100-200: Standard execution (most features)
  • 200-500: Late execution (depends on other features)
  • 500+: Very late (final polish)

Sources: content/development/events.md31-62 content/development/architecture.md104-113

Accessing Container Services

Features access application services through the Container:

Common Container Operations:


Sources: src/Core/FeatureManager.php41-46 content/development/architecture.md213-232

Service-Oriented Feature Pattern

Complex features delegate logic to service classes:

Feature Architecture Pattern:


Example from RssFeed:

  • Feature.php - Registers events, orchestrates services
  • RssFeedService.php - Collects files, generates XML
  • RssBuilder.php - Builds RSS markup
  • FeedChannel.php, FeedItem.php - Data models

Sources: src/Features/RssFeed/Services/RssFeedService.php1-305 content/development/features.md144-148

Feature Configuration

Features can read configuration from siteconfig.yaml:

Configuration Access:


Configuration in siteconfig.yaml:


Sources: content/development/features.md97-123 content/guide/site-config.md111-125

Configurable Feature Interface

Features requiring configuration should implement ConfigurableFeatureInterface:


This enables the audit:config command to validate project setup and provide helpful error messages.

Sources: content/guide/configuration.md398-410


Feature Types and Examples

StaticForge ships with features organized by functional domain. Understanding these categories helps when building custom features.

Feature Type Taxonomy

CategoryPurposeExamples
RenderersContent transformationMarkdownRenderer, HtmlRenderer
StructureNavigation and organizationMenuBuilder, Categories, ChapterNav
SEOSearch engine optimizationSitemap, RssFeed, RobotsTxt
InteractiveUser engagementForms, Search
DeploymentBuild optimizationCacheBuster, TemplateAssets

Renderer Features

Extend BaseRendererFeature for content transformation:

Renderer Pattern:


Key characteristics:

  • Listen to RENDER event (priority 100)
  • Register file extensions with ExtensionRegistry
  • Extract frontmatter, transform content, apply templates
  • Return rendered_content in data array

Sources: content/features/html-renderer.md1-62

Structure Features

Build navigation and organize content:

MenuBuilder Data Flow:


MenuBuilder specifics:

  • Parses menu: X.Y notation from frontmatter
  • Supports multiple menu positions per file (comma-separated)
  • Generates hierarchical HTML structure with dropdowns
  • Provides both raw data and pre-rendered HTML

Sources: content/features/menu-builder.md1-215 content/development/architecture.md135-154

Aggregation Features

Collect data during rendering, generate artifacts in POST_LOOP:

RssFeed Collection Pattern:


Common aggregation pattern:

  1. Initialize collector in CREATE
  2. Collect data in POST_RENDER
  3. Generate output in POST_LOOP
  4. Use lower priority in POST_LOOP to run after other features

Sources: src/Features/RssFeed/Services/RssFeedService.php30-145 src/Features/Sitemap/Services/SitemapService.php1-122


Feature Configuration and Management

Features can be configured through siteconfig.yaml and selectively disabled when not needed.

Feature Configuration Schema

Feature-specific configuration in siteconfig.yaml:


Sources: content/guide/site-config.md111-170

Disabling Features

Features can be disabled in three ways:

MethodScopeUse Case
disabled_features listProject-levelPermanent feature removal
Delete/rename directoryDevelopmentTesting, troubleshooting
Override with empty featureUser featuresCompletely replace library feature

Configuration-based disabling:

The FeatureManager checks the disabled_features list during loading:


Sources: src/Core/FeatureManager.php209-214 content/guide/configuration.md428-435

Feature Status Tracking

FeatureManager maintains status information:


This information is available via:

  • getFeatures() - All loaded feature instances
  • getFeature($name) - Specific feature instance
  • getFeatureStatuses() - Status map for all features
  • isFeatureEnabled($name) - Check if feature is active

Sources: src/Core/FeatureManager.php18-40 src/Core/FeatureManager.php99-141

Feature Interdependencies

Features can depend on other features. Use early return if dependencies are missing:


This allows features to gracefully degrade when dependencies are disabled.

Sources: content/guide/site-config.md122-125


Feature Data Flow and Integration

Features integrate with the build pipeline through standardized data structures and container variables.

Container Variables Used by Features

VariableTypeDescriptionSet ByUsed By
discovered_filesarrayAll files with metadataFileDiscoveryAll structure features
site_configarrayParsed siteconfig.yamlbootstrap.phpAll features
featuresarrayFeature data for templatesFeaturesTemplateRenderer
OUTPUT_DIRstringOutput directory path.envAll output features
SITE_BASE_URLstringSite base URL.envURL generation

Data Flow Through Container:


Sources: content/development/bootstrap.md74-96 content/development/architecture.md39-61

Feature Data Structure

Features expose data to templates through the features array:


This standardized approach allows templates to access feature data without knowing implementation details.

Sources: content/features/menu-builder.md126-189 content/development/architecture.md213-232

Feature-Specific Events

Features can fire their own events to allow extension:

RssFeed Event Ecosystem:


Event firing in feature:


This pattern allows features to be extended without modification.

Sources: src/Features/RssFeed/Services/RssFeedService.php176-218 content/development/events.md128-148


Best Practices

Feature Design Principles

  1. Single Responsibility - Each feature should do one thing well
  2. Event-Driven - React to events, don't poll or search
  3. Stateless When Possible - Prefer computing from discovered_files over maintaining state
  4. Fail Gracefully - Check for dependencies, return data unchanged if unavailable
  5. Document Requirements - Implement ConfigurableFeatureInterface if configuration needed

Performance Considerations

  • Avoid re-reading files - Use discovered_files from container
  • Use appropriate event - POST_GLOB for site structure, POST_RENDER for per-file data
  • Minimize POST_RENDER work - This runs once per file
  • Batch operations in POST_LOOP - Generate site-wide artifacts together

Code Organization

Recommended feature structure:

MyFeature/
├── Feature.php # Thin, event registration only
├── Services/
│ ├── DataCollector.php # Collects during POST_RENDER
│ └── Generator.php # Generates during POST_LOOP
├── Models/
│ └── DataStructure.php # Value objects
└── README.md # Feature documentation

Testing Features

Create unit tests that mock the container:


Sources: tests/Unit/Features/Sitemap/SitemapServiceTest.php1-105


Sources for this document:

Refresh this wiki

On this page