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>
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 reflectionBlade_ManifestModule: Parses Blade directivesJavascript_ManifestModule: Parses JS classes/methodsModel_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_Endpointmethods →storage/rsx-build/js-stubs/ - Models with
fetch()methods →storage/rsx-build/js-model-stubs/
- Controllers with
- 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 fastinclude()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:
- Token parsing -
Php_Parser::_extract_class_info()normalizesextendsat extraction - Parent lookups -
_Manifest_PHP_Reflection_Helper::_load_class_hierarchy()normalizes before comparison - 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 metadataclear()- Clears the in-memory cache
File Lookups
get_all()- Returns all files arrayget_file($path)- Get specific file metadata (throws if not found)get_files_by_dir($dir)- Get all files in directoryget_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 namefind_php_fqcn($fqcn)- Find by fully qualified namephp_get_metadata_by_class($name)- Get metadata by class namephp_get_metadata_by_fqcn($fqcn)- Get metadata by FQCNphp_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 filejs_get_extending($parent)- Find extending JS classesjs_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 attributeget_routes()- Extract all route definitions from attributes
Utility Methods
get_build_key()- Get manifest hash for cache validationget_stats()- Get statistics (file counts, build time)get_autoloader_class_map()- Get class-to-file mappingsis_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:
- No Validation - Stores all attributes without checking validity
- No Instantiation - Never creates attribute objects
- No Class Loading - Attributes don't need backing classes
- 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 extractionBlade_ManifestModule- Blade directive parsingJavascript_ManifestModule- JS class/method extractionScss_ManifestModule- SCSS metadataModel_ManifestSupport- Database schema extractionJqhtml_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 PHPinclude()
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 viaget_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:
- Use
Manifest::clear()between tests to reset state - Mock file system for predictable test data
- Test both scan() and rebuild() paths
- Verify exception throwing for not-found cases
- 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.