# Phred Framework: Project Notes & Brainstorming This document serves as a mind-map and repository for ideas, architectural decisions, and future considerations for the Phred Framework. It is a living document that captures the "why" behind the "what" defined in the specifications and milestones. ## Core Vision & Philosophy - **Batteries-Included but Swappable**: Provide sensible defaults (REST, JSON:API, Eyrie, Pairity) while ensuring every core component can be swapped via Service Providers. - **Standards First**: Strict adherence to PSRs (PSR-7, PSR-11, PSR-14, PSR-15, PSR-18). - **Modular by Nature**: Django-style "Apps" (Modules) where features are encapsulated. - **Developer Happiness (DX)**: Zero-config where possible, robust scaffolding, and clear documentation. ## Architectural Brainstorming ### 1. The HTTP Pipeline (PSR-15) - Use `Relay` for the middleware stack. - **Middleware Layers**: - Global: CORS, Security Headers, Error Handling (Problem Details). - Route-specific: Auth (JWT), Validation, Rate Limiting. - Negotiation: `ContentNegotiationMiddleware` to handle REST vs JSON:API vs HTML. ### 2. Pluggability & Service Providers - **Contracts Package**: Define all core interfaces in `Phred\Support\Contracts`. - **Driver Selection**: Use `.env` keys like `ORM_DRIVER`, `TEMPLATE_DRIVER`, `CACHE_DRIVER`. - **Lifecycle**: Providers should have `register()` (binding to container) and `boot()` (executing logic like route registration). ### 3. MVC & View Layer - **Invokable Controllers**: "One action, one class" to prevent controller bloat. - **View Objects**: A dedicated layer for data transformation/preparation before the template. This keeps controllers focused on flow and templates focused on markup. - **Response Factories**: Abstract the creation of REST vs JSON:API responses. ### 4. Modular Architecture (Django-style) - All user code lives in `modules/`. - **Contract-First Persistence**: Modules should define pure domain models (POPOs) and repository interfaces. The framework uses a "Bridge" architecture to map these to a specific ORM. - **Scaffolding**: CLI should be able to generate an entire module structure in one command. ### 5. Persistence Bridge Strategy (ORM-Agnostic) To achieve true decoupling, Phred adopts a "Persistence Bridge" pattern, mirroring the TaskerBridges architecture. This removes the need for driver-specific directories (like `Persistence/Pairity/`) within modules and moves implementation details to the framework's infrastructure layer. #### Implementation Concept 1. **Flattened Directory Structure**: - `modules//Models/`: Contains the data objects/entities (POPOs). - `modules//Repositories/`: Contains the repository interfaces. 2. **The Bridge as a Translator**: - The framework boots an active **ORM Bridge** (e.g., `PairityBridge`, `EloquentBridge`) based on `.env`. - The Bridge is responsible for taking a module's model and explaining it to its respective persistence engine using PHP 8 Attributes (metadata). 3. **Automated Discovery & Wiring**: - Similar to Tasker's command discovery, Phred scans the `Models/` directory to register entities. - Phred auto-binds repository implementations to their corresponding interfaces in the PSR-11 container. 4. **Environment-Driven**: Connection details and driver selection are handled exclusively via environment variables, keeping module code pure. 5. **Migration & Schema Management**: - **Auto-Discovery**: Bridges scan `Models/` to detect schema changes. - **Versioned Snapshots**: The framework can cache a "last-known" state of the models to generate diff-based migrations. - **Tooling**: CLI commands can generate migration files by comparing current POPO attributes against the database or a cached snapshot. #### Example Domain Model (Attribute-Driven) ```php namespace App\Modules\User\Models; use Phred\Persistence\Attributes\Entity; use Phred\Persistence\Attributes\Column; #[Entity(table: 'users')] class User { #[Column(primary: true, autoIncrement: true)] public int $id; #[Column(unique: true)] public string $email; #[Column(nullable: true)] public ?string $name; #[Column(name: 'created_at')] public \DateTimeImmutable $createdAt; } ``` #### Benefits - **Zero-Config Persistence**: Developers only write the Model and the Interface; Phred handles the connection and injection. - **Multi-ORM Support**: The same model can work across different ORMs by switching the Bridge. - **Simplified DX**: No deeply nested folders for every module; logic is localized and clean. ### 6. Command Discovery Strategy (Tasker Integration) To provide a "zero-configuration" experience for developers, the framework should implement an automated directory-based command loader that bridges the application's `src/Commands` directory with Tasker. #### Proposed Implementation Flow 1. **Directory Scan**: Use `RecursiveDirectoryIterator` to find all PHP files in `src/Commands`. 2. **Class Resolution**: Map file paths to fully qualified class names following PSR-4 conventions. 3. **Container-First Loading**: - Check if the class is already registered in the PSR-11 container. - If found, retrieve the instance from the container (ensuring dependency injection). - If not found, Tasker's `Runner::register($className)` will instantiate it directly. 4. **Registration**: Pass the resolved class name or instance to `$taskerRunner->register()`. 5. **Migration & Schema Management**: - **Auto-Discovery**: Bridges scan `Models/` to detect schema changes. - **Versioned Snapshots**: The framework can cache a "last-known" state of the models to generate diff-based migrations. - **Tooling**: CLI commands can generate migration files by comparing current POPO attributes against the database or a cached snapshot. #### Benefits - **Zero Config**: Developers just drop a class into the directory. - **DI Support**: Automatically respects container-managed services. - **Type Safety**: Tasker validates that the class implements `CommandInterface` or uses the `HasAttributes` trait. #### Example Discovery Logic ```php $commandPath = $appRoot . '/src/Commands'; $namespace = 'App\Commands\'; $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($commandPath)); foreach ($files as $file) { if ($file->getExtension() !== 'php') { continue; } $className = $namespace . str_replace( ['/', '.php'], ['\', ''], substr($file->getPathname(), strlen($commandPath) + 1) ); $taskerRunner->register($className); } ``` ### 7. Security & Auth - JWT by default for APIs. - CSRF protection for traditional web routes. - **Feature Flags**: Native integration with `FlagPole`. ### 8. Documentation & Discovery - Documentation site built *with* Phred. - **Dynamic Help**: A CLI command that opens the online docs for a specific framework command. - **OpenAPI**: Auto-generation from annotations.