Files
rspade_system/app/RSpade/Core/Manifest/CLAUDE.md
root d523f0f600 Fix code quality violations and exclude Manifest from checks
Document application modes (development/debug/production)
Add global file drop handler, order column normalization, SPA hash fix
Serve CDN assets via /_vendor/ URLs instead of merging into bundles
Add production minification with license preservation
Improve JSON formatting for debugging and production optimization
Add CDN asset caching with CSS URL inlining for production builds
Add three-mode system (development, debug, production)
Update Manifest CLAUDE.md to reflect helper class architecture
Refactor Manifest.php into helper classes for better organization
Pre-manifest-refactor checkpoint: Add app_mode documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-14 10:38:22 +00:00

14 KiB
Executable File

Manifest System - Developer Documentation

This documentation is for developers working on Manifest.php itself. For usage documentation, see the man pages via php artisan rsx:man manifest_api.

CRITICAL: Testing Manifest.php Changes

When modifying Manifest.php or any helper class, you MUST run php artisan rsx:manifest:build --clean to test your changes.

The --clean flag performs both rsx:clean and a full manifest rebuild, ensuring your modifications are properly tested. Without this, the manifest may use cached or partially-built data that doesn't reflect your changes.

Architecture Overview

The Manifest is a compiled cache of all file metadata in the RSX application, stored at storage/rsx-build/manifest_data.php. It replaces Laravel's scattered discovery mechanisms with a unified system that enables path-agnostic class loading.

Helper Class Architecture

Manifest.php uses a delegator pattern with 8 helper classes for better organization:

Helper Class Purpose Functions
_Manifest_PHP_Reflection_Helper PHP class reflection php_find_class, php_get_extending, php_is_subclass_of, etc.
_Manifest_JS_Reflection_Helper JS class reflection js_find_class, js_get_extending, js_is_subclass_of, etc.
_Manifest_Reflection_Helper Views, attributes, routes find_view, get_with_attribute, get_routes, etc.
_Manifest_Scanner_Helper File discovery, change detection _get_rsx_files, _has_changed, _scan_directory_for_classes, etc.
_Manifest_Builder_Helper Index generation _build_autoloader_class_map, _collate_files_by_classes, etc.
_Manifest_Cache_Helper Persistence, validation _get_kernel, _load_cached_data, _save, _validate_cached_data, etc.
_Manifest_Quality_Helper Code quality, IDE support _process_code_quality_metadata, _generate_vscode_stubs, etc.
_Manifest_Database_Helper Database schema db_get_tables, db_get_table_columns, _verify_database_provisioned, etc.

Pattern: Manifest.php contains the public API and delegator methods. Each delegator forwards to the corresponding helper class method. Internal methods use single _ prefix (conceptually private but technically public for cross-class access).

When modifying: Edit the helper class containing the implementation, not Manifest.php (unless changing the public API signature).

Core Data Structure

The manifest cache contains:

[
    'generated' => '2025-09-28 05:02:15',
    'hash' => '42b9d0efb5c547eec0fb2ca19bf922e0',
    'data' => [
        'files' => [...],           // All indexed files with metadata
        'js_classes' => [...],       // JavaScript class map
        'php_classes' => [...],      // PHP class map
        'autoloader_class_map' => [...], // Simple name to FQCN mapping
        'models' => [...],           // Database model metadata
        'jqhtml' => [...]           // jqhtml component registry
    ]
]

Schema Discovery Tool

Use php artisan rsx:manifest:schema_dump to inspect the manifest structure:

# Pretty-printed JSON for human reading
php artisan rsx:manifest:schema_dump

# Compact JSON for LLM parsing (saves tokens)
php artisan rsx:manifest:schema_dump --no-pretty-print

This tool deduplicates array structures, showing unique patterns with example values. Essential for understanding the actual data structure when writing code that directly accesses manifest data.

6-Phase Build Process

The build process follows these phases (implementations in helper classes):

Phase 1: File Discovery

  • Implemented in _Manifest_Scanner_Helper::_get_rsx_files()
  • Scans directories from config('rsx.manifest.scan_directories')
  • Default: ['rsx', 'app/RSpade/Core/Js', 'app/RSpade/Core/Manifest/Modules']
  • Excludes: vendor/, node_modules/, .git/, storage/, public/
  • Returns array of file paths with basic stats (mtime, size)

Phase 2: Token Parsing

  • Implemented in _Manifest_Scanner_Helper::_process_file()
  • Uses token_get_all() for fast PHP parsing without loading
  • Extracts: namespace, class name, extends, implements
  • Normalizes all class references to simple names (strips namespace qualifiers)
  • Builds dependency graph for loading order

Phase 3: Dependency Loading

  • Implemented in _Manifest_Scanner_Helper::_load_changed_php_files()
  • Loads PHP files in dependency order (parents before children)
  • Uses _load_class_hierarchy() to ensure parent classes exist
  • Critical for reflection to work properly

Phase 4: Reflection & Module Processing

  • Implemented in _Manifest_Scanner_Helper::_extract_reflection_data()
  • Runs registered ManifestModule processors
  • Core processors:
    • Php_ManifestModule: Extracts attributes, methods via reflection
    • Blade_ManifestModule: Parses Blade directives
    • Javascript_ManifestModule: Parses JS classes/methods
    • Model_ManifestSupport: Adds database schema to models
  • Stores ALL attributes without validation (agnostic extraction)

Phase 5: Stub Generation

  • Implemented in Manifest.php (not delegated)
  • Creates JavaScript stub classes for:
    • Controllers with Ajax_Endpoint methods → storage/rsx-build/js-stubs/
    • Models with fetch() methods → storage/rsx-build/js-model-stubs/
  • Enables clean JavaScript API calls without manual Ajax wiring

Phase 6: Cache Writing

  • Implemented in _Manifest_Cache_Helper::_save()
  • Serializes to PHP array format
  • Writes to storage/rsx-build/manifest_data.php
  • Uses var_export() for fast include() loading

Key Implementation Details

Class Name Normalization Philosophy

CRITICAL: RSX enforces unique simple class names across the entire codebase. This architectural constraint allows the manifest to normalize ALL class references to simple names, eliminating an entire class of bugs related to namespace format variations.

The Problem: PHP allows multiple ways to reference classes:

  • \Rsx\Lib\DataGrid (leading backslash)
  • Rsx\Lib\DataGrid (no leading backslash)
  • DataGrid (simple name)

The Solution: Manifest::_normalize_class_name() strips namespace qualifiers:

public static function _normalize_class_name(string $class_name): string
{
    // Strip leading backslash
    $class_name = ltrim($class_name, '\\');

    // Extract just the class name (last part after final backslash)
    $parts = explode('\\', $class_name);
    return end($parts);
}

Applied At:

  1. Token parsing - Php_Parser::_extract_class_info() normalizes extends at extraction
  2. Parent lookups - _Manifest_PHP_Reflection_Helper::_load_class_hierarchy() normalizes before comparison
  3. All class name operations - Any code comparing class names uses normalization

Why This Works: RSX's unique simple class name enforcement means we only need FQCNs at actual include_once time. Throughout the manifest, simple names are sufficient and eliminate format inconsistencies.

Static State Management

The Manifest uses public static properties (accessible by helper classes):

public static ?array $data = null;           // Cached manifest data
public static bool $_has_init = false;       // Initialization flag
public static bool $_needs_manifest_restart = false; // Restart signal
public static ?ManifestKernel $kernel = null; // Kernel instance
public static array $_changed_files = [];    // Files changed in last scan

Public API Methods

Initialization & Loading

  • init() - Ensures manifest is loaded (auto-rebuilds in dev mode)
  • get_full_manifest() - Returns complete manifest structure with metadata
  • clear() - Clears the in-memory cache

File Lookups

  • get_all() - Returns all files array
  • get_file($path) - Get specific file metadata (throws if not found)
  • get_files_by_dir($dir) - Get all files in directory
  • get_changed_files() - Get files that changed in most recent manifest scan (for incremental code quality checks)

PHP Class Resolution

  • php_find_class($name) - Find by simple class name
  • find_php_fqcn($fqcn) - Find by fully qualified name
  • php_get_metadata_by_class($name) - Get metadata by class name
  • php_get_metadata_by_fqcn($fqcn) - Get metadata by FQCN
  • php_get_extending($parent) - Find all concrete classes extending parent (filters out abstract)
  • php_is_subclass_of($sub, $super) - Check inheritance

JavaScript Class Resolution

  • js_find_class($name) - Find JavaScript class file
  • js_get_extending($parent) - Find extending JS classes
  • js_is_subclass_of($sub, $super) - Check JS inheritance

View Resolution

  • find_view($id) - Find by dot notation (e.g., 'frontend.index')
  • find_view_by_rsx_id($id) - Find by @rsx_id value

Attribute & Route Discovery

  • get_with_attribute($attr) - Find all files with specific attribute
  • get_routes() - Extract all route definitions from attributes

Utility Methods

  • get_build_key() - Get manifest hash for cache validation
  • get_stats() - Get statistics (file counts, build time)
  • get_autoloader_class_map() - Get class-to-file mappings
  • is_built() - Check if manifest exists
  • _normalize_class_name($class_name) - Strip namespace qualifiers to get simple name

Attribute Extraction Philosophy

The manifest practices agnostic attribute extraction:

  1. No Validation - Stores all attributes without checking validity
  2. No Instantiation - Never creates attribute objects
  3. No Class Loading - Attributes don't need backing classes
  4. Raw Storage - Stores exactly what reflection provides

Example from _Manifest_Scanner_Helper::_extract_reflection_data():

foreach ($method->getAttributes() as $attribute) {
    $attributes[] = [
        'name' => $attribute->getName(),
        'arguments' => $attribute->getArguments()
    ];
}

Change Detection

Files are tracked by:

  • mtime - Modification time (primary)
  • size - File size in bytes (secondary)
  • hash - SHA1, only computed when mtime/size change

The _Manifest_Scanner_Helper::_has_changed() method compares these to detect changes.

Error Handling

All lookup methods throw RuntimeException when items not found:

if (!isset(Manifest::$data['data']['php_classes'][$class_name])) {
    throw new RuntimeException("PHP class not found in manifest: {$class_name}");
}

This ensures fail-fast behavior - no silent failures.

Module System

Built-in Modules

Located in /app/RSpade/Core/Manifest/Modules/:

  • Php_ManifestModule - PHP reflection and attribute extraction
  • Blade_ManifestModule - Blade directive parsing
  • Javascript_ManifestModule - JS class/method extraction
  • Scss_ManifestModule - SCSS metadata
  • Model_ManifestSupport - Database schema extraction
  • Jqhtml_ManifestSupport - jqhtml component registration

Creating Custom Modules

Implement the ManifestModule interface:

interface ManifestModule {
    function get_name(): string;
    function get_extensions(): array;  // File extensions to handle
    function process(string $file, array $metadata): array;
    function post_process(array $manifest): void;
}

Register in config/rsx.php:

'manifest' => [
    'modules' => [
        App\MyModule::class
    ]
]

Performance Considerations

Caching Strategy

  • Development: Auto-rebuilds on file changes
  • Production: Manual rebuild required, loads from cache file
  • Memory: Full manifest kept in static variable after first load

Optimization Points

  • Token parsing avoids loading PHP files unnecessarily
  • Dependency ordering minimizes class loading failures
  • SHA1 hashing only when size/mtime indicate changes
  • var_export() format allows fast PHP include()

Common Issues & Solutions

Issue: "Class not found" during reflection

Cause: Parent class not loaded before child Solution: Check _Manifest_PHP_Reflection_Helper::_load_class_hierarchy() is working correctly

Issue: Parent class not found (extends mismatch)

Cause: Namespace format variations in extends declarations (e.g., \Rsx\Lib\DataGrid vs Rsx\Lib\DataGrid) Solution: Use Manifest::_normalize_class_name() to strip namespace qualifiers before comparison

Issue: Attributes not appearing in manifest

Cause: Method not public static Solution: Only public static methods are indexed

Issue: Manifest not updating

Cause: In production mode or file timestamps unchanged Solution: Run php artisan rsx:manifest:build --force

Issue: JavaScript stubs not generating

Cause: Missing Ajax_Endpoint attribute or method not public static Solution: Check method has attribute and correct visibility

Code Quality Integration

The manifest integrates with code quality checks via metadata storage. Rules that run during manifest scan store violations in code_quality_metadata field for each file.

Incremental Code Quality Checks

Manifest-time code quality rules support incremental checking for better performance:

  • Incremental rules (is_incremental() = true): Only check files that changed in this manifest scan
  • Cross-file rules (is_incremental() = false): Run once per build, access full manifest via get_all()

The get_changed_files() API provides the list of files that changed, enabling rules to optimize their processing.

Debugging

Enable verbose output:

Manifest::$_debug_options['verbose'] = true;

Or via command:

php artisan rsx:manifest:build --verbose

Testing Considerations

When testing manifest functionality:

  1. Use Manifest::clear() between tests to reset state
  2. Mock file system for predictable test data
  3. Test both scan() and rebuild() paths
  4. Verify exception throwing for not-found cases
  5. Check stub generation for API methods

Important Constants & Paths

  • Cache file: storage/rsx-build/manifest_data.php
  • JS stubs: storage/rsx-build/js-stubs/
  • Model stubs: storage/rsx-build/js-model-stubs/
  • Default scan dirs: ['rsx', 'app/RSpade/Core/Js', 'app/RSpade/Core/Manifest/Modules']

Direct Data Access

When bypassing helper methods to access manifest data directly:

$manifest = Manifest::get_full_manifest();

// Access files
$file_data = $manifest['data']['files']['path/to/file.php'];

// Access class maps
$class_file = $manifest['data']['php_classes']['ClassName'];
$js_class = $manifest['data']['js_classes']['JsClass'];

// Access models with schema
$model = $manifest['data']['models']['User_Model'];
$columns = $model['columns'];

Use rsx:manifest:schema_dump to understand the exact structure before writing direct access code.