Skip to content

RFC - 6.0 - PHP Attribute Registry #19167

@josbeir

Description

@josbeir

Description

Reference Implementation: cakephp-attribute-registry

Abstract

This RFC proposes integrating a centralized attribute discovery and management system into CakePHP core. The system provides automatic scanning, caching, and querying of PHP 8+ attributes across application and plugin codebases, enabling attribute-driven architecture patterns within the framework.

This attribute registry represents the foundational first step toward attribute support in CakePHP, establishing the infrastructure necessary for future attribute-based features across routing, validation, ORM, events, and other framework components.

Motivation

PHP 8 attributes provide native metadata capabilities that enable cleaner, more declarative code. Modern frameworks increasingly adopt attribute-based patterns for routing, validation, ORM mapping, and dependency injection. CakePHP currently lacks a standardized mechanism for discovering and managing attributes across applications and plugins.

Current Pain Points:

  • No unified approach for attribute-based features across core components
  • Each feature requiring attributes must implement its own discovery logic
  • No caching strategy for attribute reflection, leading to performance overhead
  • Plugin attributes cannot be easily discovered or queried
  • Developers must manually scan and parse attributes for custom features

Proposed Solution Benefits:

  • Standardized attribute discovery across framework and applications
  • Performance optimization through compiled caching
  • Foundation for attribute-driven features (routing, validation, events, etc.)
  • Consistent API for attribute queries and filtering
  • Plugin ecosystem support out of the box

Terminology

  • Attribute: PHP 8+ native metadata attached to code elements via #[Attribute] syntax
  • Target: The code element an attribute is attached to (class, method, property, parameter, constant)
  • Scanner: Service responsible for discovering PHP files and extracting attributes
  • Parser: Service that uses reflection to extract attributes from code elements
  • Registry: Central facade providing high-level API for attribute operations
  • AttributeInfo: Value object encapsulating complete attribute metadata
  • AttributeCollection: Fluent interface for filtering and querying discovered attributes

Architecture

Core Components

1. AttributeRegistry (Facade)

Central access point providing high-level API for attribute operations:

AttributeRegistry
├── discover(): AttributeCollection
├── findByAttribute(string): AttributeCollection
├── findByClass(string): AttributeCollection
├── findByTargetType(AttributeTargetType): AttributeCollection
├── warmCache(): int
└── clearCache(): bool

Responsibilities:

  • Coordinate attribute discovery and caching
  • Provide unified query interface
  • Manage lifecycle events
  • Support both singleton and dependency injection access patterns

2. Service Layer

AttributeScanner

  • Discovers PHP files using configurable glob patterns
  • Resolves paths for application and plugins
  • Filters files based on include/exclude patterns
  • Yields file paths for processing

AttributeParser

  • Uses reflection to extract attributes from code elements
  • Supports all PHP attribute targets except standalone functions
  • Instantiates attribute classes with proper arguments
  • Generates AttributeInfo value objects

CompiledCache

  • Serializes discovered attributes to compiled PHP files
  • Validates cache integrity via file modification times
  • Handles cache invalidation and rebuilding
  • Thread-safe cache operations

PathResolver

  • Resolves glob patterns to absolute file paths
  • Integrates with PluginLocator for plugin path discovery
  • Normalizes paths across platforms

PluginLocator

  • Discovers loaded CakePHP plugins
  • Provides plugin base paths for scanning
  • Integrates with CakePHP plugin system

3. Data Layer

AttributeInfo (Value Object)

AttributeInfo
├── name: string (attribute FQCN)
├── target: AttributeTarget
├── instance: object (attribute instance)
├── reflectionTarget: Reflector
├── fileTime: int (file modification timestamp)
└── methods: getArguments(), getClassName(), etc.

AttributeTarget (Value Object)

AttributeTarget
├── type: AttributeTargetType (enum)
├── className: string
├── name: string (method/property/constant/parameter name)
└── filePath: string

AttributeCollection

  • Immutable collection of AttributeInfo objects
  • Fluent filtering API (byAttribute, byClass, byTargetType)
  • Chainable query methods
  • Implements Countable and IteratorAggregate

4. Supporting Components

AttributeTargetType (Enum)

enum AttributeTargetType: string {
    case CLASS = 'class';
    case METHOD = 'method';
    case PROPERTY = 'property';
    case PARAMETER = 'parameter';
    case CLASS_CONSTANT = 'class_constant';
}

Events

  • AttributeRegistry.beforeDiscover: Triggered before attribute discovery
  • AttributeRegistry.afterDiscover: Triggered after discovery with results
  • AttributeRegistry.cacheWarmed: Triggered after cache compilation
  • AttributeRegistry.cacheCleared: Triggered after cache invalidation

Configuration

Framework configuration namespace AttributeRegistry:

[
    'cache' => [
        'enabled' => true,              // Enable compiled caching
        'validateFiles' => false,       // Validate files on load
        'path' => null,                 // Cache directory path
    ],
    'scanner' => [
        'paths' => ['src/**/*.php'],    // Glob patterns to scan
        'exclude_paths' => [            // Patterns to exclude
            'vendor/**',
            'tmp/**'
        ],
    ],
]

Dependency Injection

Services registered via ServiceProvider pattern:

  • AttributeRegistry::class - Singleton instance
  • CLI commands registered in console command collection

CLI Integration

Console commands for cache management and inspection:

cake attributes cache          # Warm attribute cache
cake attributes list           # List all discovered attributes
cake attributes inspect <name> # Inspect specific attribute

Development Workflow

The attribute registry supports multiple operational modes to balance performance and developer experience based on environment needs.

Cache Modes

Production Mode (cache = true, validateFiles = false)

'cache' => [
    'enabled' => true,
    'validateFiles' => false,
]
  • Attributes discovered on first request and compiled to cache file
  • Subsequent requests load from compiled cache instantly
  • Cache persists until explicitly cleared via CLI or programmatically
  • Optimal performance with zero overhead after initial discovery
  • Requires manual cache refresh: bin/cake attributes cache

Development Mode (cache = true, validateFiles = true)

'cache' => [
    'enabled' => true,
    'validateFiles' => true,  // Recommended for development
]
  • Validates cache freshness by comparing file modification times (filemtime)
  • Automatically refreshes cache when attributes change in existing files
  • Detects modifications without manual intervention
  • Limitation: New files with attributes require manual cache refresh
  • Balanced approach: automatic refresh for edits, minimal performance impact

No Cache Mode (cache = false)

'cache' => [
    'enabled' => false,
]
  • Performs full attribute discovery on every request
  • No caching overhead or stale data concerns
  • Automatically detects all changes including new files
  • Viable for small applications or development environments with fast disk I/O
  • Performance impact: ~50-100ms per request depending on codebase size

Recommended Configuration by Environment

// config/app.php or config/attribute_registry.php

return [
    'AttributeRegistry' => [
        'cache' => [
            'enabled' => env('CACHE_ATTRIBUTES', true),
            'validateFiles' => env('DEBUG', false),  // Validate in debug mode
            'path' => CACHE . 'attributes' . DS,
        ],
    ],
];

This configuration automatically:

  • Enables validation during development (DEBUG = true)
  • Disables validation in production for maximum performance
  • Allows environment-specific overrides via environment variables

Workflow Examples

Adding Attributes to Existing Files (Development Mode)

// Edit existing controller method
class UsersController extends AppController
{
    #[Route('/users', methods: ['GET'])]  // Added or modified attribute
    public function index()
    {
        // ...
    }
}
  • With validateFiles = true: Automatically detected on next request
  • With validateFiles = false: Run bin/cake attributes cache to refresh

Adding New Files with Attributes

// Create new controller file
class PostsController extends AppController
{
    #[Route('/posts')]
    public function index() {}
}
  • All cache modes: Requires manual refresh bin/cake attributes cache
  • Cache validation only detects changes to existing cached files, not new files

Development Best Practices

  1. Use validateFiles = true during active development
  2. Run bin/cake attributes cache after adding new files with attributes
  3. Disable validation in CI/production environments
  4. Consider cache = false for small projects or during heavy attribute development

Usage Patterns

Basic Discovery

$registry = AttributeRegistry::getInstance();
$attributes = $registry->discover();

Filtered Queries

// Find all route attributes
$routes = $registry->findByAttribute(Route::class);

// Find all attributes on a specific class
$controllerAttrs = $registry->findByClass(UsersController::class);

// Find all method attributes
$methodAttrs = $registry->findByTargetType(AttributeTargetType::METHOD);

Fluent Filtering

$registry->discover()
    ->byAttribute(Route::class)
    ->byTargetType(AttributeTargetType::METHOD)

Dependency Injection

class MyService {
    public function __construct(
        private AttributeRegistry $registry
    ) {}
}

Integration Points

Potential Core Applications

  1. Routing System

    • Attribute-based route definitions on controller methods
    • Automatic route discovery and registration
    • Middleware and prefix configuration via attributes
  2. Validation

    • Attribute-driven validation rules on entity properties
    • Custom validator attributes
    • Nested validation configuration
  3. ORM/Table

    • Column metadata and associations via attributes
    • Behavior configuration
    • Finder and query scope definitions
  4. Events

    • Event listener registration via attributes
    • Priority and condition configuration
  5. Dependency Injection

    • Service configuration and autowiring hints
    • Factory method designation

Performance Considerations

Cache Strategy:

  • Compiled PHP cache for optimal deserialization speed
  • File modification time validation for automatic cache refresh in development
  • Three operational modes: production (static cache), development (validated cache), no cache (discovery per request)
  • Lazy loading - attributes discovered only when needed
  • Cache invalidation via CLI, programmatically, or automatically when validateFiles = true

Memory Usage:

  • AttributeInfo objects are lightweight value objects
  • Collection uses generators where possible
  • No memory overhead when cache disabled

Benchmark Data (Reference Implementation):

  • Cache warm: ~100-200ms for medium application
  • Cache load: <5ms with serialized cache
  • Discovery without cache: ~50-100ms per request

Compiled Cache Format

The cache is stored as a compiled PHP file returning an array of AttributeInfo value objects. This format provides optimal performance through PHP's native opcache:

<?php
// phpcs:ignoreFile
/**
 * Pre-compiled Attribute Registry Cache
 *
 * Generated: 2025-12-25 11:38:03
 * Attributes: 16
 *
 * DO NOT EDIT THIS FILE MANUALLY
 * Regenerate with: bin/cake attributes cache
 */

declare(strict_types=1);

return [
    new \Cake\Core\AttributeRegistry\ValueObject\AttributeInfo(
        className: 'App\\Controller\\UsersController',
        attributeName: 'Cake\\Routing\\Attribute\\Route',
        arguments: [
            'path' => '/users',
            'methods' => ['GET'],
        ],
        filePath: '/var/www/html/src/Controller/UsersController.php',
        lineNumber: 19,
        target: new \Cake\Core\AttributeRegistry\ValueObject\AttributeTarget(
            type: \Cake\Core\AttributeRegistry\Enum\AttributeTargetType::METHOD,
            targetName: 'index',
            parentClass: 'UsersController',
        ),
        fileTime: 1735125483,
        pluginName: null,
    ),
    // ... additional entries
];

Benefits:

  • Near-instant loading via PHP opcache
  • Type-safe with full object instantiation
  • File modification time validation for cache invalidation (using native filemtime)
  • Plugin-aware with source tracking

Backward Compatibility

This is a new feature with no breaking changes:

  • Opt-in functionality requiring explicit usage
  • No modifications to existing core components
  • Compatible with PHP 8.2+ requirement
  • Does not affect applications not using attributes

Security Considerations

  • No code execution during attribute discovery (reflection only)
  • Cache files use PHP serialization format with integrity checks
  • File paths validated and normalized
  • No external input processed during discovery
  • Cache directory should be outside webroot

Testing Strategy

  • Unit tests for all service components
  • Integration tests for discovery and caching
  • Performance benchmarks for cache operations
  • Plugin integration testing
  • Edge cases: malformed attributes, missing classes, circular references

Next Steps

Before submitting a formal pull request to CakePHP core based on my plugin, this RFC seeks community feedback and discussion on:

  • Architecture and design decisions
  • Integration approach with existing core components
  • Naming conventions and API design
  • Potential use cases and adoption patterns
  • Migration strategy and backward compatibility concerns

Community input is essential to ensure this functionality aligns with CakePHP's philosophy and meets the needs of the broader ecosystem.

Conclusion

Integrating an attribute registry into CakePHP core provides a foundation for modern, declarative programming patterns. The proposed architecture offers a clean separation of concerns, high performance through caching, and extensibility for future attribute-driven features. The reference implementation demonstrates production-readiness with testing and documentation.

CakePHP Version

6.0

RFC changelog

  • 01-08-26 Added Development Workflow
  • 01-08-26 Changed fileHash => fileTime

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions