diff --git a/MILESTONES.md b/MILESTONES.md index 03384ef..a68572b 100644 --- a/MILESTONES.md +++ b/MILESTONES.md @@ -1,5 +1,35 @@ # Phred Framework Milestones + +[← Back to README](./README.md) | [SPECS.md](./SPECS.md) + Phred supports REST and JSON:API via env setting; batteries-included defaults, swappable components. + +[↑ Back to Top](#table-of-contents) +## Table of Contents +- [~~M0 — Project bootstrap (repo readiness)~~](#m0-project-bootstrap-repo-readiness) +- [~~M1 — Core HTTP kernel and routing~~](#m1-core-http-kernel-and-routing) +- [~~M2 — Configuration and environment~~](#m2-configuration-and-environment) +- [~~M3 — API formats and content negotiation~~](#m3-api-formats-and-content-negotiation) +- [~~M4 — Error handling and problem details~~](#m4-error-handling-and-problem-details) +- [~~M5 — Dependency Injection and Service Providers~~](#m5-dependency-injection-and-service-providers) +- [~~M6 — MVC: Controllers, Views, Templates~~](#m6-mvc-controllers-views-templates) +- [~~M7 — Modules (Django‑style app structure)~~](#m7-modules-django-style-app-structure) +- [~~M8 — Database access, migrations, and seeds~~](#m8-database-access-migrations-and-seeds) +- [~~M9 — CLI (phred) and scaffolding~~](#m9-cli-phred-and-scaffolding) +- [~~M10 — Security middleware and auth primitives~~](#m10-security-middleware-and-auth-primitives) +- [~~M11 — Logging, HTTP client, and filesystem~~](#m11-logging-http-client-and-filesystem) +- [~~M12 — Serialization/validation utilities and pagination~~](#m12-serialization-validation-utilities-and-pagination) +- [M13 — OpenAPI and documentation](#m13-openapi-and-documentation) +- [M14 — Testing, quality, and DX](#m14-testing-quality-and-dx) +- [M15 — Caching and performance (optional default)](#m15-caching-and-performance-optional-default) +- [M16 — Production hardening and deployment](#m16-production-hardening-and-deployment) +- [M17 — JSON:API enhancements (optional package)](#m17-json-api-enhancements-optional-package) +- [M18 — Examples and starter template](#m18-examples-and-starter-template) +- [M19 — Documentation site](#m19-documentation-site) +- [M20 — Dynamic Command Help](#m20-dynamic-command-help) +- [M21 — Governance and roadmap tracking](#m21-governance-and-roadmap-tracking) +- [Notes on sequencing and parallelization](#notes-on-sequencing-and-parallelization) + ## ~~M0 — Project bootstrap (repo readiness)~~ * ~~Tasks:~~ * ~~Finalize `composer.json` (namespaces, scripts, suggests) and `LICENSE`.~~ @@ -7,6 +37,8 @@ Phred supports REST and JSON:API via env setting; batteries-included defaults, s * ~~Set up CI (lint, static analysis, unit tests) and basic build badge.~~ * ~~Acceptance:~~ * ~~Fresh clone installs (without running suggested packages) and passes linters/analysis/tests.~~ + +[↑ Back to Top](#table-of-contents) ## ~~M1 — Core HTTP kernel and routing~~ * ~~Tasks:~~ * ~~Implement the HTTP kernel: `PSR-15` pipeline via `Relay`.~~ @@ -19,12 +51,16 @@ Phred supports REST and JSON:API via env setting; batteries-included defaults, s * ~~Sample route returning a JSON 200 via controller.~~ * ~~Controllers are invokable (`__invoke(Request)`), one route per controller.~~ * ~~Route groups (prefix only) work and are tested.~~ + +[↑ Back to Top](#table-of-contents) ## ~~M2 — Configuration and environment~~ * ~~Tasks:~~ * ~~Load `.env` via `vlucas/phpdotenv` and expose `Phred\Support\Config`.~~ * ~~Define configuration precedence and document keys (e.g., `API_FORMAT`, `APP_ENV`, `APP_DEBUG`).~~ * ~~Acceptance:~~ * ~~App reads config from `.env`; unit test demonstrates override behavior.~~ + +[↑ Back to Top](#table-of-contents) ## ~~M3 — API formats and content negotiation~~ * ~~Tasks:~~ * ~~Finalize `ContentNegotiationMiddleware` using `.env` and `Accept` header.~~ @@ -32,6 +68,8 @@ Phred supports REST and JSON:API via env setting; batteries-included defaults, s * ~~Provide developer‑facing helpers for common responses (`ok`, `created`, `error`).~~ * ~~Acceptance:~~ * ~~Demo endpoints respond correctly as REST or JSON:API depending on `API_FORMAT` and `Accept`.~~ + +[↑ Back to Top](#table-of-contents) ## ~~M4 — Error handling and problem details~~ * ~~Tasks:~~ * ~~Finalize `ProblemDetailsMiddleware` with RFC7807 (REST) and JSON:API error documents.~~ @@ -39,6 +77,8 @@ Phred supports REST and JSON:API via env setting; batteries-included defaults, s * ~~Map common exceptions to HTTP status codes; include correlation/request IDs in responses/logs.~~ * ~~Acceptance:~~ * ~~Throwing an exception yields a standards‑compliant error response; debug mode shows Whoops page.~~ + +[↑ Back to Top](#table-of-contents) ## ~~M5 — Dependency Injection and Service Providers~~ * ~~Tasks:~~ * ~~Define Service Provider interface and lifecycle (register, boot).~~ @@ -50,6 +90,8 @@ Phred supports REST and JSON:API via env setting; batteries-included defaults, s * ~~Acceptance:~~ * ~~Providers can contribute bindings and routes; order is deterministic and tested.~~ * ~~Drivers can be switched via `.env`/config without changing controllers/services; example provider route covered by tests.~~ + +[↑ Back to Top](#table-of-contents) ## ~~M6 — MVC: Controllers, Views, Templates~~ * ~~Tasks:~~ * ~~Controller base class and conventions (request/response helpers).~~ @@ -58,6 +100,8 @@ Phred supports REST and JSON:API via env setting; batteries-included defaults, s * ~~Acceptance:~~ * ~~Example page rendered through View → Template; API coexists with full‑site rendering.~~ * ~~Rendering works via RendererInterface and can be swapped (e.g., Eyrie → Twig demo) with only configuration/provider changes.~~ + +[↑ Back to Top](#table-of-contents) ## ~~M7 — Modules (Django‑style app structure)~~ * ~~Tasks:~~ * ~~Define module filesystem layout (Nested Controllers/Views/Services/Models/Templates/Routes/Tests).~~ @@ -73,6 +117,8 @@ Phred supports REST and JSON:API via env setting; batteries-included defaults, s * ~~Acceptance:~~ * ~~Creating a module with the CLI makes it discoverable; routes/templates work without manual wiring.~~ * ~~Switching `ORM_DRIVER` between `pairity` and `eloquent` requires no changes to services/controllers; providers bind repository interfaces to driver implementations.~~ + +[↑ Back to Top](#table-of-contents) ## ~~M8 — Database access, migrations, and seeds~~ * ~~Tasks:~~ * ~~Integrate `getphred/pairity` for ORM/migrations/seeds.~~ @@ -86,6 +132,8 @@ Phred supports REST and JSON:API via env setting; batteries-included defaults, s * ~~Acceptance:~~ * ~~Running migrations modifies a test database; seeds populate sample data; CRUD demo works.~~ * ~~All persistence usage in examples goes through Orm contracts; can be swapped (Pairity → Doctrine adapter demo optional).~~ + +[↑ Back to Top](#table-of-contents) ## ~~M9 — CLI (phred) and scaffolding~~ * ~~Tasks:~~ * ~~Implement Symfony Console app in `bin/phred`.~~ ✓ @@ -93,6 +141,8 @@ Phred supports REST and JSON:API via env setting; batteries-included defaults, s * ~~Utility commands: `test[:]`, `run`, `db:backup`, `db:restore`.~~ ✓ * ~~Acceptance:~~ * ~~Commands generate files with correct namespaces/paths and pass basic smoke tests.~~ + +[↑ Back to Top](#table-of-contents) ## ~~M10 — Security middleware and auth primitives~~ * ~~Tasks:~~ * ~~Add CORS, Secure Headers middlewares; optional CSRF for template routes.~~ ✓ @@ -101,6 +151,8 @@ Phred supports REST and JSON:API via env setting; batteries-included defaults, s * ~~Bind FeatureFlagClientInterface with a default adapter (Flagpole); add small sample usage and env config.~~ ✓ * ~~Acceptance:~~ * ~~CORS preflight and secured endpoints behave as configured; JWT‑protected route example works.~~ ✓ + +[↑ Back to Top](#table-of-contents) ## ~~M11 — Logging, HTTP client, and filesystem~~ * ~~Tasks:~~ * ~~Monolog setup with handlers and processors (request ID, memory, timing).~~ ✓ @@ -108,18 +160,25 @@ Phred supports REST and JSON:API via env setting; batteries-included defaults, s * ~~Flysystem integration with local adapter; abstraction for storage disks.~~ ✓ * ~~Standardize all core service providers with robust driver validation (similar to OrmServiceProvider).~~ ✓ * ~~Opportunity Radar: Multiple storage disks, log channel management, and HTTP client middleware.~~ ✓ + * ~~Opportunity Radar 2: Storage Disk "Cloud" Adapters (S3), Advanced HTTP Middleware (Circuit Breaker), and Log Alerting (Slack/Sentry).~~ ✓ + * ~~Opportunity Radar 3: Githook Integration, Breadcrumb Automation, and TOC for MILESTONES.md.~~ ✓ * ~~Acceptance:~~ * ~~Logs include correlation IDs; sample outbound HTTP call via client; file upload/storage demo works.~~ ✓ -## M12 — Serialization/validation utilities and pagination -* Tasks: - * REST default: Symfony Serializer normalizers/encoders; document extension points. - * Add simple validation layer using `Phred\Http\Middleware\Middleware` base. - * Pagination helpers (links/meta), REST and JSON:API compatible outputs. - * URL extension negotiation: add XML support - * Provide `XmlResponseFactory` (or encoder) and integrate with negotiation. - * Enable `xml` in `URL_EXTENSION_WHITELIST` by default. -* Acceptance: - * Example endpoint validates input, returns 422 with details; paginated listing includes links/meta. + +[↑ Back to Top](#table-of-contents) +## ~~M12 — Serialization/validation utilities and pagination~~ +* ~~Tasks:~~ + * ~~REST default: Symfony Serializer normalizers/encoders; document extension points.~~ ✓ + * ~~Add simple validation layer using `Phred\Http\Middleware\Middleware` base.~~ ✓ + * ~~Pagination helpers (links/meta), REST and JSON:API compatible outputs.~~ ✓ + * ~~URL extension negotiation: add XML support~~ ✓ + * ~~Provide `XmlResponseFactory` (or encoder) and integrate with negotiation.~~ ✓ + * ~~Enable `xml` in `URL_EXTENSION_WHITELIST` by default.~~ ✓ + * ~~Opportunity Radar: Storage URL generation, Circuit Breaker persistence (PSR-16), and HTTP client profiling.~~ ✓ +* ~~Acceptance:~~ + * ~~Example endpoint validates input, returns 422 with details; paginated listing includes links/meta.~~ ✓ + +[↑ Back to Top](#table-of-contents) ## M13 — OpenAPI and documentation * Tasks: * Integrate `zircote/swagger-php` annotations. @@ -127,6 +186,8 @@ Phred supports REST and JSON:API via env setting; batteries-included defaults, s * Document auth, pagination, error formats. * Acceptance: * Generated OpenAPI document validates; matches sample endpoints. + +[↑ Back to Top](#table-of-contents) ## M14 — Testing, quality, and DX * Tasks: * Establish testing structure with Codeception (unit, integration, API suites). @@ -138,6 +199,8 @@ Phred supports REST and JSON:API via env setting; batteries-included defaults, s * `composer test` runs green across suites; static analysis passes. * CLI tests run via TestRunnerInterface; * CLI tests run green per module and across suites. + +[↑ Back to Top](#table-of-contents) ## M15 — Caching and performance (optional default) * Tasks: * Provide `PSR-16` cache interface binding; suggest `symfony/cache` when enabled. @@ -145,6 +208,8 @@ Phred supports REST and JSON:API via env setting; batteries-included defaults, s * Rate limiting middleware (token bucket) suggestion/integration point. * Acceptance: * Sample endpoint demonstrates cached responses and conditional requests. + +[↑ Back to Top](#table-of-contents) ## M16 — Production hardening and deployment * Tasks: * Config for envs (dev/test/stage/prod), error verbosity, trusted proxies/hosts. @@ -152,12 +217,16 @@ Phred supports REST and JSON:API via env setting; batteries-included defaults, s * Healthcheck endpoint, readiness/liveness probes. * Acceptance: * Containerized demo serves both API and template pages; healthchecks pass. + +[↑ Back to Top](#table-of-contents) ## M17 — JSON:API enhancements (optional package) * Tasks: * If enabled, integrate `neomerx/json-api` fully: includes, sparse fieldsets, relationships, sorting, filtering, pagination params. * Adapters/Schema providers per resource type. * Acceptance: * JSON:API conformance tests for selected endpoints pass; docs updated. + +[↑ Back to Top](#table-of-contents) ## M18 — Examples and starter template * Tasks: * Create `examples/blog` module showcasing controllers, views, templates, ORM, auth, pagination, and both API formats. @@ -165,18 +234,34 @@ Phred supports REST and JSON:API via env setting; batteries-included defaults, s * Provide `composer create-project` skeleton template instructions. * Acceptance: * New users can scaffold a working app in minutes following README. + +[↑ Back to Top](#table-of-contents) ## M19 — Documentation site * Tasks: - * Expand README into a docs site (MkDocs or similar): getting started, concepts, reference, guides. + * Build the official documentation site (getphred.com) using the Phred framework. + * Internal Documentation API: Design a "Documentation Module" that parses Markdown files from the repo to serve them dynamically. + * ~~Automated TOC Generation: Script to regenerate SPECS.md Table of Contents.~~ ✓ * Versioned docs and upgrade notes. * Acceptance: * Docs published; links in README; examples maintained. -## M20 — Governance and roadmap tracking + +[↑ Back to Top](#table-of-contents) +## M20 — Dynamic Command Help +* Tasks: + * Implement a `docs` command in `phred` (e.g., `php phred docs `) that opens the relevant documentation page in the user's browser. + * Build the `docs` command logic after the documentation site (getphred.com) is functional. +* Acceptance: + * `php phred docs create:module` opens the correct URL in the default browser. + +[↑ Back to Top](#table-of-contents) +## M21 — Governance and roadmap tracking * Tasks: * Define contribution guide, issue templates, RFC process for changes. * Public roadmap (this milestone list) tracked as GitHub Projects/Issues. * Acceptance: * Contributors can propose features via RFC; roadmap is visible and updated. + +[↑ Back to Top](#table-of-contents) ## Notes on sequencing and parallelization * M0–M4 are critical path for the HTTP core and should be completed sequentially. * M5–M8 can progress in parallel with M9 (CLI) once the kernel is stable. diff --git a/README.md b/README.md index 8964fdb..de9d23c 100644 --- a/README.md +++ b/README.md @@ -1,329 +1,94 @@ # Phred -A PHP MVC framework: -* Intended for projects of all sizes, and solo or team development. - * The single router call per controller style makes it easy for teamwork without stepping on each others toes. -* REQUIREMENTS - * PHP 8.1+ - * Primarily meant for Apache/Nginx webservers, will look into supporting other webservers in the future. -* PSR-4 autoloading. -* Installed through Composer (`composer create-project getphred/phred`) -* Environment variables (.env) for configuration. -* Supports two API formats (with content negotiation) - * Pragmatic REST (default) - * JSON:API - * Choose via .env: - * `API_FORMAT=rest` (plain JSON responses, RFC7807 error format) - * `API_FORMAT=jsonapi` (JSON:API compliant documents and error objects) - * Or negotiate per request using the `Accept` header: - * `Accept: application/vnd.api+json` forces JSON:API for that request -* TESTING environment variables (.env) - * `TEST_RUNNER=codeception` - * `TEST_PATH=tests` - * `TEST_PATH` is relative to both project root and each module root. -* Dependency Injection -* Fully Pluggable, but ships with defaults: - * Pluggability model (M5) - * Core depends on Phred contracts and PSRs; concrete implementations are provided by Service Providers. - * Providers implement `Phred\Support\Contracts\ServiceProviderInterface` with `register(ContainerBuilder)` and `boot(Container)` methods. - * Providers are loaded in deterministic order: core → app → modules, configured in `config/providers.php`. - * Swap packages by changing `.env` / `config/app.php` drivers and enabling a provider. - * Driver keys (examples) - * `ORM_DRIVER=pairity|doctrine` - * `TEMPLATE_DRIVER=eyrie|twig|plates` - * `FLAGS_DRIVER=flagpole|unleash` - * `TEST_RUNNER=codeception` - * Primary contracts - * `Template\Contracts\RendererInterface` - * `Orm\Contracts\ConnectionInterface` (or repositories in future milestones) - * `Flags\Contracts\FeatureFlagClientInterface` - * `Testing\Contracts\TestRunnerInterface`. - * Default Plug-ins - * Feature Flags through `getphred/flagpole` - * ORM through `getphred/pairity` (handles migrations, seeds, and db access) - * Unit Testing through `codeception/codeception` - * Testing is provided as a CLI dev capability only; it is not part of the HTTP request lifecycle. - * Template Engine through `getphred/eyrie` -* Other dependencies: - * Dependency Injection through `php-di/php-di` - * Static Analysis through `phpstan/phpstan` - * Code Style Enforcement through `friendsofphp/php-cs-fixer` - * Logging through `monolog/monolog` - * Config and environment handling through `vlucas/phpdotenv` - * HTTP client through `guzzlehttp/guzzle` -* URL extension negotiation (optional) - * Opt-in middleware that parses a trailing URL extension and hints content negotiation. - * Enable via env: `URL_EXTENSION_NEGOTIATION=true` (default true) - * Control allowed extensions via: `URL_EXTENSION_WHITELIST="json|php|none"` - * Defaults to `json|php|none`. XML support will be added in M12. - * Examples: - * `/users/1.json` → JSON response - * `/users/1` or `/users/1.php` → HTML (views) by convention - * Extensible: future formats can be added with a factory and whitelisting. -* CONTROLLERS - * Invokable controllers (Actions), - * Single router call per controller, - * `public function __invoke(Request $request)` method entry point on controller class, - * Response helpers via dependency injection: - * Inject `Phred\Http\Contracts\ApiResponseFactoryInterface` to build responses consistently across formats. - * The factory is negotiated per request (env default or `Accept` header) and sets appropriate `Content-Type`. - * Common methods: `ok(array $data)`, `created(array $data, ?string $location)`, `noContent()`, `error(int $status, string $title, ?string $detail, array $extra = [])`. - * Example: - ```php - use Phred\Http\Contracts\ApiResponseFactoryInterface as Responses; - use Psr\Http\Message\ServerRequestInterface as Request; - use Phred\Http\Middleware\ContentNegotiationMiddleware as Negotiation; +A PHP MVC framework intended for projects of all sizes, designed for both solo and team development. - final class ExampleController { - public function __construct(private Responses $responses) {} - public function __invoke(Request $request) { - $format = $request->getAttribute(Negotiation::ATTR_API_FORMAT, 'rest'); - return $this->responses->ok(['format' => $format]); - } - } - ``` - * MVC controller bases (M6) - * For API endpoints inside modules, extend `Phred\Mvc\APIController` and use response helpers: - ```php - use Phred\Mvc\APIController; - use Psr\Http\Message\ServerRequestInterface as Request; +## Requirements - final class UserShowController extends APIController { - public function __invoke(Request $request) { - $user = ['id' => 123, 'name' => 'Ada']; - return $this->ok(['user' => $user]); - } - } - ``` - * For HTML endpoints inside modules, extend `Phred\Mvc\ViewController` and delegate to a module `View`: - ```php - use Phred\Mvc\ViewController; - use Psr\Http\Message\ServerRequestInterface as Request; +* **PHP**: 8.2+ +* **Web Server**: Apache/Nginx (recommended) +* **Package Manager**: Composer - final class HomePageController extends ViewController { - public function __invoke(Request $request, HomePageView $view) { - // domain data (normally from a Service) - $data = ['title' => 'Welcome', 'name' => 'world']; - // Use the view's default template by omitting the template param - return $this->renderView($view, $data); - // Or override template explicitly via 3rd param: - // return $this->renderView($view, $data, 'home'); - } - } - ``` -* VIEWS - * Classes for data manipulation/preparation before rendering Templates, - * `$this->render(, );` to render a template. - * Base class (M6): extend `Phred\Mvc\View` in your module and optionally override `transformData()`: - ```php - use Phred\Mvc\View; +## Installation - final class HomePageView extends View { - protected string $template = 'home'; // default template - protected function transformData(array $data): array { - $data['upper'] = strtoupper($data['name'] ?? ''); - return $data; - } - } - ``` - * With the default Eyrie renderer, a simple template string `"

Hello {{upper}}

"` would produce `

Hello WORLD

`. - * Template selection conventions (M6) - * Controllers always call `renderView($view, $data, ?$templateOverride)`: - - Use default template: `renderView($view, $data)` (no third parameter) - - Override template explicitly: `renderView($view, $data, 'template_name')` - * Views own default template selection and presentation logic: - - Declare `protected string $template = 'default_name';` or override `defaultTemplate()` for dynamic selection - - The `View` decides which template to use when the controller does not pass an override - * Rationale: keeps template/presentation decisions in the View layer; controllers only make explicit overrides when necessary (flags, A/B tests, special flows). -* SERVICES - * for business logic. -* MODULES (M7) - * Django-style: all user Controllers, Views, Templates, Services, etc. live inside modules. - * Suggested module layout (ORM-agnostic): - - `modules//Controllers/` - - `modules//Views/` - - `modules//Templates/` - - `modules//Services/` - - `modules//Models/` (domain models, ORM-neutral) - - `modules//Repositories/` (interfaces consumed by services) - - `modules//Persistence/Pairity/` (DAOs, DTOs, mappers, repo implementations) - - `modules//Persistence/Eloquent/` (Eloquent models, repo implementations) - - `modules//Database/Migrations/` (canonical migrations for the module) - - `modules//Routes/web.php` and `api.php` - - `modules//Providers/*ServiceProvider.php` - * Swap ORM driver (`ORM_DRIVER=pairity|eloquent`) without changing services/controllers: providers bind repository interfaces to driver-specific implementations. Migrations remain a single canonical set per module. - * Creating a Module - - With prompt (interactive): - ```bash - php phred create:module Blog - # Enter URL prefix for module 'Blog' [/blog]: - ``` - - The command will scaffold `modules/Blog/*`, register the provider in `config/providers.php` (modules section), and append an inclusion snippet to `routes/web.php` mounting the module at `/blog`. - - Without prompt (explicit prefix argument): - ```bash - php phred create:module Blog /blog - # or - php phred create:module Blog --prefix=/blog - ``` - - After creation, add PSR-4 to `composer.json` and dump autoload: - ```json - { - "autoload": { - "psr-4": { - "Project\\\\Modules\\\\Blog\\\\": "modules/Blog/" - } - } - } - ``` - ```bash - composer dump-autoload - ``` - - Route inclusion pattern in `routes/web.php` (added automatically): - ```php - use Phred\\Http\\Routing\\RouteGroups; - use Phred\\Http\\Router; +Install Phred via Composer: - // Module 'Blog' mounted at '/blog' - RouteGroups::include($router, '/blog', function (Router $router) { - /** @noinspection PhpIncludeInspection */ - (static function ($router) { require __DIR__ . '/../modules/Blog/Routes/web.php'; })($router); - }); - ``` - - Define module-local routes relative to the module in `modules/Blog/Routes/web.php`: - ```php - use Phred\\Http\\Router; - use Project\\Modules\\Blog\\Controllers as C; - - $router->get('/', C\\HomeController::class); // -> /blog - $router->get('/posts', C\\PostIndexController::class); // -> /blog/posts - ``` -* SERVICE PROVIDERS (M5) - * for dependency injection and runtime bootstrapping. - * Configure providers in `config/providers.php`: - ```php - return [ - 'core' => [ - Phred\Providers\Core\RoutingServiceProvider::class, - Phred\Providers\Core\TemplateServiceProvider::class, - Phred\Providers\Core\OrmServiceProvider::class, - Phred\Providers\Core\FlagsServiceProvider::class, - Phred\Providers\Core\TestingServiceProvider::class, - ], - 'app' => [ - Phred\Providers\AppServiceProvider::class, - ], - 'modules' => [], - ]; - ``` - * Add routes from a provider using the `RouteRegistry` helper: - ```php - use Phred\Http\Routing\RouteRegistry; - use Phred\Http\Router; - - RouteRegistry::add(static function ($collector, Router $router): void { - $router->get('/hello', fn() => ['message' => 'Hello from a provider']); - }); - ``` - * Select drivers via env or `config/app.php` under `drivers`: - - `TEMPLATE_DRIVER=eyrie` - - `ORM_DRIVER=pairity` - - `FLAGS_DRIVER=flagpole` - - `TEST_RUNNER=codeception` - * Defaults are provided by core providers and can be swapped by changing these keys and adding alternate providers. -* MIGRATIONS - * for database changes. -* Modular separation, similar to Django apps. - * Nested Models - * Nested Controllers - * Nested Views - * Nested Services - * Nested Migrations - * Nested Service Providers - * Nested Routes - * Nested Templates - * Nested Tests -* CLI Helper called phred - * `php phred create:command ` // Creates a CLI command under `console/commands - * `php phred create:module ` // Creates a module - * `php phred create::controller` // Creates a controller in the specified module - * `php phred create::model` // Creates a model in the specified module - * `php phred create::migration` // Creates a migration in the specified module - * `php phred create::seed` // Creates a seeder in the specified module - * `php phred create::test` // Creates a test in the specified module - * `php phred create::view` / Creates a view in the specified module - * `php phred db:backup` // Backup the database - * `php phred db:restore -f ` // Restore the database from the specified backup file - * `php phred migrate [-m ]` // Migrate entire project or module - * `php phred migration:rollback [-m ]` // Rollback entire project or module - * `php phred seed` - * `php phred seed:rollback` - * `php phred test[:]` // Test entire project or module - * Runs tests using the configured test runner (dev only). - * Requires `require-dev` dependencies. - * `php phred run [-p ]` - * Spawns a local PHP webserver on port 8000 (unless specified otherwise using `-p`) -* CLI Helper is extendable through CLI Commands. - -Command discovery - -* Core commands (bundled with Phred) are discovered from `src/commands`. -* User/project commands are discovered from `console/commands` in your project root. - -Run the CLI: - -``` -php phred list +```bash +composer create-project getphred/phred ``` -Add your own command by creating a PHP file under `console/commands`, returning an instance of `Phred\Console\Command` (or an anonymous class extending it). +## Getting Started -(Or by running `php phred create:command `) +### Creating a Module -Example: +Phred uses a modular (Django-style) architecture. All your application logic lives inside modules. -``` -writeln('Hello!'); return 0; } -}; +```bash +php phred create:module Blog ``` -Configuration and environment +This will create the module structure under `modules/Blog`, register the service provider, and mount the routes. -- Phred uses `vlucas/phpdotenv` to load a `.env` file from your project root (loaded in `bootstrap/app.php`). -- Access configuration anywhere via `Phred\Support\Config::get(, )`. - - Precedence: environment variables > config files > provided default. - - Keys may be provided in either UPPER_SNAKE (e.g., `APP_ENV`) or dot.notation (e.g., `app.env`). - - Config files live under `config/*.php` and return arrays; dot keys are addressed as `.` (e.g., `app.timezone`). +After creating a module, update your `composer.json` to include the new namespace: -Common keys - -- `APP_ENV` (default from config/app.php: `local`) -- `APP_DEBUG` (`true`/`false`) -- `APP_TIMEZONE` (default `UTC`) -- `API_FORMAT` (`rest` | `jsonapi`; default `rest`) - -API formats and negotiation - -- Middleware `ContentNegotiationMiddleware` determines the active API format per request. -- Precedence: - 1. `Accept: application/vnd.api+json` → JSON:API - 2. `.env`/config `API_FORMAT` (fallback to `rest`) -- The chosen format is stored on the request as `phred.api_format` and used by the injected `ApiResponseFactoryInterface` to produce the correct response shape and `Content-Type`. -- Demo endpoint: `GET /_phred/format` responds with the active format; `GET /_phred/health` returns a simple JSON 200. - -Examples - -```php -use Phred\Support\Config; - -$env = Config::get('APP_ENV', 'local'); // reads from env, then config/app.php, else 'local' -$tz = Config::get('app.timezone', 'UTC'); // reads nested key from config files -$fmt = strtolower(Config::get('API_FORMAT', 'rest')); +```json +{ + "autoload": { + "psr-4": { + "Project\\\\Modules\\\\Blog\\\\": "modules/Blog/" + } + } +} ``` + +Then run: +```bash +composer dump-autoload +``` + +### Running the Application + +Start the local development server: + +```bash +php phred run +``` + +The application will be available at `http://localhost:8000`. + +## CLI Usage (phred) + +The `phred` binary provides several utility and scaffolding commands. + +### Generators +* `php phred create:module ` — Create a new module. +* `php phred create:command ` — Create a custom CLI command. +* `php phred create::controller ` — Create a controller. +* `php phred create::view ` — Create a view and template. +* `php phred create::model ` — Create a domain model. +* `php phred create::migration ` — Create a migration. +* `php phred create::seed ` — Create a database seeder. +* `php phred create::test ` — Create a test. + +### Database +* `php phred migrate` — Run database migrations. +* `php phred migration:rollback` — Rollback migrations. +* `php phred seed` — Seed the database. +* `php phred db:backup` — Backup the database. +* `php phred db:restore` — Restore the database. + +### Testing & Utilities +* `php phred test` — Run tests for the entire project. +* `php phred test:` — Run tests for a specific module. +* `php phred list` — List all available commands. + +## Technical Specifications + +For detailed information on the framework architecture, service providers, configuration, and MVC components, please refer to: + +👉 **[SPECS.md](./SPECS.md)** | [MILESTONES.md](./MILESTONES.md) + +## License + +Phred is open-source software licensed under the MIT license. diff --git a/SPECS.md b/SPECS.md new file mode 100644 index 0000000..2da94be --- /dev/null +++ b/SPECS.md @@ -0,0 +1,161 @@ +# Phred Technical Specifications + +[← Back to README](./README.md) | [MILESTONES.md](./MILESTONES.md) + +## Table of Contents +- [1. Core Framework](#1-core-framework) + - [1.1 API Formats and Content Negotiation](#1-1-api-formats-and-content-negotiation) + - [1.2 Pluggability Model (M5)](#1-2-pluggability-model-m5) + - [1.3 URL Extension Negotiation](#1-3-url-extension-negotiation) + - [1.4 Dependencies](#1-4-dependencies) +- [2. MVC Components](#2-mvc-components) + - [2.1 Controllers](#2-1-controllers) + - [2.2 Views (M6)](#2-2-views-m6) + - [2.3 Services](#2-3-services) +- [3. Modular Architecture (M7)](#3-modular-architecture-m7) + - [3.1 Module Layout (ORM-Agnostic)](#3-1-module-layout-orm-agnostic) + - [3.2 Modular Separation](#3-2-modular-separation) +- [4. Infrastructure](#4-infrastructure) + - [4.1 Service Providers (M5)](#4-1-service-providers-m5) + - [4.2 Configuration and Environment](#4-2-configuration-and-environment) + - [4.3 Command Discovery](#4-3-command-discovery) +- [5. Technical Examples](#5-technical-examples) + - [5.1 Configuration Access](#5-1-configuration-access) + - [5.2 API Negotiation](#5-2-api-negotiation) + - [5.3 Route Group Inclusion](#5-3-route-group-inclusion) + +This document outlines the technical specifications and architectural standards of the Phred framework. + +## 1. Core Framework + +### 1.1 API Formats and Content Negotiation +Phred supports two primary API formats: +* **Pragmatic REST (default)**: Plain JSON responses using RFC 7807 for error handling. +* **JSON:API**: Compliant with the JSON:API specification for documents and error objects. + +Negotiation can be set globally via `.env`: +* `API_FORMAT=rest` +* `API_FORMAT=jsonapi` + +Clients can override the format per request using the `Accept` header: +* `Accept: application/vnd.api+json` forces JSON:API. +* `Accept: application/xml` or `text/xml` (M12+). + +### 1.2 Pluggability Model (M5) +The framework is fully pluggable. Core components depend on Phred contracts and PSRs; concrete implementations are provided by Service Providers. +* Providers implement `Phred\Support\Contracts\ServiceProviderInterface`. +* Lifecycle methods: `register(ContainerBuilder)` and `boot(Container)`. +* Loading order: Core → App → Modules (defined in `config/providers.php`). + +**Primary Contracts:** +* `Template\Contracts\RendererInterface` +* `Orm\Contracts\ConnectionInterface` +* `Flags\Contracts\FeatureFlagClientInterface` +* `Testing\Contracts\TestRunnerInterface` + +### 1.3 URL Extension Negotiation +Optional middleware that hints content negotiation based on URL suffix. +* Enabled via `URL_EXTENSION_NEGOTIATION=true` (default). +* Whitelist via `URL_EXTENSION_WHITELIST` (default: "json|xml|php|none"). +* **Mappings:** + * `.json` → `application/json` + * `.xml` → `application/xml` + * `.php` or none → `text/html` (View convention) + +### 1.4 Dependencies +Phred leverages several industry-standard packages: +* **Dependency Injection**: `php-di/php-di` +* **Static Analysis**: `phpstan/phpstan` +* **Code Style**: `friendsofphp/php-cs-fixer` +* **Logging**: `monolog/monolog` +* **Environment**: `vlucas/phpdotenv` +* **HTTP Client**: `guzzlehttp/guzzle` +* **CORS**: `middlewares/cors` +* **Authentication**: `lcobucci/jwt` +* **Feature Flags**: `getphred/flagpole` +* **ORM**: `getphred/pairity` +* **Template Engine**: `getphred/eyrie` +* **Router**: `nikic/fast-route` +* **PSR-7/15**: `relay/relay`, `nyholm/psr7` + +## 2. MVC Components + +### 2.1 Controllers +* **Invokable**: Controllers are "Actions" with a single entry point: `public function __invoke(Request $request)`. +* **Response Factories**: Injected via `Phred\Http\Contracts\ApiResponseFactoryInterface`. +* **Base Classes (M6)**: + * `Phred\Mvc\APIController`: For API-only endpoints. + * `Phred\Mvc\ViewController`: For HTML/Template endpoints. + +### 2.2 Views (M6) +* Classes dedicated to data preparation before rendering. +* Extend `Phred\Mvc\View`. +* Methods: + * `transformData(array $data)`: Manipulate data before it hits the template. + * `defaultTemplate()`: Define the default template for the view. +* **Conventions**: Controllers call `renderView($view, $data, ?$templateOverride)`. If no override is provided, the View decides the template. + +### 2.3 Services +* Business logic should reside in Service classes to keep controllers lean and models ORM-neutral. + +## 3. Modular Architecture (M7) + +Phred follows a Django-style modular structure where all user code lives inside modules. + +### 3.1 Module Layout (ORM-Agnostic) +* `modules//Controllers/` +* `modules//Views/` +* `modules//Templates/` +* `modules//Services/` +* `modules//Models/` (Domain models, pure PHP) +* `modules//Repositories/` (Interfaces consumed by services) +* `modules//Persistence/Pairity/` (Driver-specific implementations) +* `modules//Persistence/Eloquent/` (Driver-specific implementations) +* `modules//Database/Migrations/` (Canonical migrations) +* `modules//Routes/web.php` and `api.php` +* `modules//Providers/*ServiceProvider.php` + +### 3.2 Modular Separation +Modules encapsulate their own: +* Models, Controllers, Views, Services, Migrations, Providers, Routes, Templates, and Tests. + +## 4. Infrastructure + +### 4.1 Service Providers (M5) +Providers are the glue of the framework. +* **Configuration**: `config/providers.php` +* **Route Registration**: Use `RouteRegistry::add(static function($collector, $router) { ... })`. + +### 4.2 Configuration and Environment +* **Dotenv**: Loads `.env` from the project root. +* **Config Facade**: `Phred\Support\Config::get(, )`. +* **Precedence**: Environment variables > Config files (`config/*.php`) > Defaults. +* **Notation**: Supports both `UPPER_SNAKE` and `dot.notation`. + +### 4.3 Command Discovery +* **Core Commands**: Discovered from `src/commands`. +* **User Commands**: Discovered from `console/commands`. +* Custom commands must return an instance of `Phred\Console\Command`. + +## 5. Technical Examples + +### 5.1 Configuration Access +```php +use Phred\Support\Config; + +$env = Config::get('APP_ENV', 'local'); // environment check +$tz = Config::get('app.timezone', 'UTC'); // dotted notation from config/app.php +``` + +### 5.2 API Negotiation +The chosen format is stored on the request as `phred.api_format` and used by the `ApiResponseFactoryInterface` to determine the response shape. + +### 5.3 Route Group Inclusion +```php +use Phred\Http\Routing\RouteGroups; +use Phred\Http\Router; + +RouteGroups::include($router, '/prefix', function (Router $router) { + require __DIR__ . '/../path/to/routes.php'; +}); +```