From 3452ac1e12312b97c31a7f19418908f8263b3c55 Mon Sep 17 00:00:00 2001 From: Funky Waddle Date: Sun, 14 Dec 2025 17:10:01 -0600 Subject: [PATCH] initial commit --- .editorconfig | 16 ++ .env.example | 6 + .gitattributes | 10 + .github/workflows/ci.yml | 56 ++++++ .gitignore | 56 +++++- MILESTONES.md | 163 +++++++++++++++ README.md | 124 +++++++++++- bin/phred | 51 +++++ composer.json | 86 ++++++++ phpstan.neon.dist | 8 + phred | 3 + phred.bat | 2 + src/Console/Command.php | 67 +++++++ src/Http/ApiResponseFactoryInterface.php | 30 +++ src/Http/Controllers/HealthController.php | 22 ++ src/Http/JsonApi/JsonApiResponseFactory.php | 99 +++++++++ src/Http/Kernel.php | 79 ++++++++ .../ContentNegotiationMiddleware.php | 29 +++ src/Http/Middleware/DispatchMiddleware.php | 61 ++++++ .../Middleware/ProblemDetailsMiddleware.php | 93 +++++++++ src/Http/Middleware/RoutingMiddleware.php | 44 ++++ src/Http/Rest/RestResponseFactory.php | 47 +++++ src/Http/Router.php | 39 ++++ src/Support/Config.php | 33 +++ src/commands/create_command.php | 83 ++++++++ src/commands/install.php | 189 ++++++++++++++++++ tests/CommandDiscoveryTest.php | 49 +++++ tests/HttpKernelTest.php | 28 +++ tests/MakeCommandTest.php | 41 ++++ 29 files changed, 1610 insertions(+), 4 deletions(-) create mode 100644 .editorconfig create mode 100644 .env.example create mode 100644 .gitattributes create mode 100644 .github/workflows/ci.yml create mode 100644 MILESTONES.md create mode 100644 bin/phred create mode 100644 composer.json create mode 100644 phpstan.neon.dist create mode 100755 phred create mode 100644 phred.bat create mode 100644 src/Console/Command.php create mode 100644 src/Http/ApiResponseFactoryInterface.php create mode 100644 src/Http/Controllers/HealthController.php create mode 100644 src/Http/JsonApi/JsonApiResponseFactory.php create mode 100644 src/Http/Kernel.php create mode 100644 src/Http/Middleware/ContentNegotiationMiddleware.php create mode 100644 src/Http/Middleware/DispatchMiddleware.php create mode 100644 src/Http/Middleware/ProblemDetailsMiddleware.php create mode 100644 src/Http/Middleware/RoutingMiddleware.php create mode 100644 src/Http/Rest/RestResponseFactory.php create mode 100644 src/Http/Router.php create mode 100644 src/Support/Config.php create mode 100644 src/commands/create_command.php create mode 100644 src/commands/install.php create mode 100644 tests/CommandDiscoveryTest.php create mode 100644 tests/HttpKernelTest.php create mode 100644 tests/MakeCommandTest.php diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d712c29 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# EditorConfig helps maintain consistent coding styles +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.php] +indent_style = space +indent_size = 4 + +[*.{yml,yaml,json,md}] +indent_style = space +indent_size = 2 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e0cd5ab --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +APP_NAME=Phred App +APP_ENV=local +APP_DEBUG=true +APP_TIMEZONE=UTC + +API_FORMAT=rest diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3b8c5cf --- /dev/null +++ b/.gitattributes @@ -0,0 +1,10 @@ +# Exclude dev files from exported archives +/.github export-ignore +/tests export-ignore +/.editorconfig export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/phpstan.neon.dist export-ignore +/.php-cs-fixer.php export-ignore +/MILESTONES.md export-ignore +/README.md export-ignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..38731b4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,56 @@ +name: CI + +on: + push: + branches: ["**"] + pull_request: + branches: ["**"] + +jobs: + build: + name: PHP ${{ matrix.php }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: ["8.1", "8.2", "8.3"] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: mbstring, intl, json + tools: composer:v2 + coverage: none + + - name: Validate composer.json + run: composer validate --no-check-publish --strict + + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache composer + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-interaction --prefer-dist --no-progress + + - name: PHP CS Fixer (dry-run) + run: vendor/bin/php-cs-fixer fix --dry-run --using-cache=no --verbose src + + - name: PHPStan + run: vendor/bin/phpstan analyse --no-progress --memory-limit=1G + + - name: Codeception (if configured) + if: hashFiles('**/codeception.yml') != '' + run: vendor/bin/codecept run --verbosity 1 diff --git a/.gitignore b/.gitignore index 49a80d2..259f09c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,9 +2,9 @@ composer.phar /vendor/ -# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control -# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file -# composer.lock +# Template policy: do not commit composer.lock in this template repo +# (apps generated from this template should commit THEIR lock file) +composer.lock # ---> JetBrains # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider @@ -85,3 +85,53 @@ fabric.properties # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser + +# --- Additional ignores (Phred project) --- + +# Environment files (keep .env.example tracked) +.env +.env.local +.env.*.local + +# IDE/editor folders +.idea/ +.vscode/ + +# OS files +.DS_Store +Thumbs.db + +# Editor swap/backup files +*.swp +*.swo +*~ + +# Tool caches and build artifacts +.php-cs-fixer.cache +.phpunit.result.cache +.cache/ +coverage/ +.coverage/ +build/ +var/ +tmp/ + +# Scaffolding output created during local development of this template +# These are part of generated apps, not this template repo +/public/ +/bootstrap/ +/routes/ +/resources/ +/modules/ +/storage/* +!/storage/.gitkeep +/config/ +/console/ + +# Codeception outputs +tests/_output/ +tests/_support/_generated/ + +/.env +/.phpunit.cache +/.php-cs-fixer.cache diff --git a/MILESTONES.md b/MILESTONES.md new file mode 100644 index 0000000..d5710c6 --- /dev/null +++ b/MILESTONES.md @@ -0,0 +1,163 @@ +# Phred Framework Milestones +Phred supports REST and JSON:API via env setting; batteries-included defaults, swappable components. +## ~~M0 — Project bootstrap (repo readiness)~~ +* ~~Tasks:~~ + * ~~Finalize `composer.json` (namespaces, scripts, suggests) and `LICENSE`.~~ + * ~~Add `.editorconfig`, `.gitattributes`, `.gitignore`, example `.env.example`.~~ + * ~~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.~~ +## ~~M1 — Core HTTP kernel and routing~~ +* ~~Tasks:~~ + * ~~Implement the HTTP kernel: `PSR-15` pipeline via `Relay`.~~ + * ~~Wire `nyholm/psr7(-server)` factories and server request creation.~~ + * ~~Integrate `nikic/fast-route` with a RouteCollector and dispatcher.~~ + * ~~Define route → controller resolution (invokable controllers).~~ + * ~~Add minimal app bootstrap (front controller) and DI container wiring (`PHP-DI`).~~ +* ~~Acceptance:~~ + * ~~Sample route returning a JSON 200 via controller.~~ + * ~~Controllers are invokable (`__invoke(Request)`), one route per controller.~~ +## 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. +## M3 — API formats and content negotiation +* Tasks: + * Finalize `ContentNegotiationMiddleware` using `.env` and `Accept` header. + * Bind `ApiResponseFactoryInterface` to `RestResponseFactory` or `JsonApiResponseFactory` based on format. + * 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`. +## M4 — Error handling and problem details +* Tasks: + * Finalize `ProblemDetailsMiddleware` with RFC7807 (REST) and JSON:API error documents. + * Integrate `filp/whoops` for dev mode (`APP_DEBUG=true`). + * 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. +## M5 — Dependency Injection and Service Providers +* Tasks: + * Define Service Provider interface and lifecycle (register, boot). + * Module discovery loads providers in order (core → app → module). + * Add examples for registering controllers, services, config, and routes via providers. + * Define contracts: `Phred\Contracts\Template\RendererInterface`, `Phred\Contracts\Orm\*`, `Phred\Contracts\Flags\FeatureFlagClientInterface`, `Phred\Contracts\Testing\TestRunnerInterface` (optional). + * Define config/env keys for driver selection (e.g., `TEMPLATE_DRIVER`, `ORM_DRIVER`, `FLAGS_DRIVER`, `TEST_RUNNER`). + * Provide “default adapter” Service Providers for the shipped packages and document swap procedure. +* Acceptance: + * Providers can contribute bindings and routes; order is deterministic and tested. + * A sample module can switch template/ORM/flags provider by changing `.env` and provider registration, without touching controllers/services. +## M6 — MVC: Controllers, Views, Templates +* Tasks: + * Controller base class and conventions (request/response helpers). + * View layer (data preparation) with `getphred/eyrie` template engine integration. + * Template rendering helper: `$this->render(