6.8 KiB
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
Relayfor the middleware stack. - Middleware Layers:
- Global: CORS, Security Headers, Error Handling (Problem Details).
- Route-specific: Auth (JWT), Validation, Rate Limiting.
- Negotiation:
ContentNegotiationMiddlewareto handle REST vs JSON:API vs HTML.
2. Pluggability & Service Providers
- Contracts Package: Define all core interfaces in
Phred\Support\Contracts. - Driver Selection: Use
.envkeys likeORM_DRIVER,TEMPLATE_DRIVER,CACHE_DRIVER. - Lifecycle: Providers should have
register()(binding to container) andboot()(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
-
Flattened Directory Structure:
modules/<Module>/Models/: Contains the data objects/entities (POPOs).modules/<Module>/Repositories/: Contains the repository interfaces.
-
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).
- The framework boots an active ORM Bridge (e.g.,
-
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.
- Similar to Tasker's command discovery, Phred scans the
-
Environment-Driven: Connection details and driver selection are handled exclusively via environment variables, keeping module code pure.
-
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.
- Auto-Discovery: Bridges scan
Example Domain Model (Attribute-Driven)
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
-
Directory Scan: Use
RecursiveDirectoryIteratorto find all PHP files insrc/Commands. -
Class Resolution: Map file paths to fully qualified class names following PSR-4 conventions.
-
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.
-
Registration: Pass the resolved class name or instance to
$taskerRunner->register(). -
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.
- Auto-Discovery: Bridges scan
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
CommandInterfaceor uses theHasAttributestrait.
Example Discovery Logic
$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.