From 0a5ffb56ba968bf663d9ec095e8156d7c094105c Mon Sep 17 00:00:00 2001 From: Joey Smith Date: Fri, 9 Oct 2026 22:54:55 -0500 Subject: [PATCH] chore: replace em and en dashes with ASCII throughout An em dash cannot be typed on a standard keyboard without a numeric-alt or IME sequence, so every occurrence was machine-written rather than authored. They also propagate by copy, since the preset artifacts are copied between packages. Replacement is context-aware rather than a blind substitution: | Pattern | Becomes | |---|---| | Markdown heading or bold label followed by a dash | a colon | | Anything else mid-sentence | a spaced ASCII hyphen | | Numeric ranges | a bare hyphen | Documentation and comment text, plus two console status strings that are noted separately. Signed-off-by: Joey Smith Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/copilot-instructions.md | 18 +-- Dockerfile | 2 +- compose.yml | 10 +- docs/Project_Architecture_Blueprint.md | 54 ++++----- docs/error-logging.md | 2 +- docs/handlers.md | 2 +- docs/middleware.md | 2 +- plan/refactor-axleus-log-0.1.0.md | 104 +++++++++--------- .../MonologMiddlewareFactoryTest.php | 2 +- 9 files changed, 98 insertions(+), 98 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index e3acfe1..dfb7964 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,12 +1,12 @@ -# Webware Log — Copilot Agent Instructions +# Webware Log: Copilot Agent Instructions -## Read this before acting — fleet working agreements +## Read this before acting: fleet working agreements `vendor/webware/webware-tools/agent-working-agreements.md` is authoritative for how work is done in this fleet, and it overrides anything else in this file or in any local note. Read it before acting, and again before reporting. The non-negotiables: -- Do exactly the scope asked — only the files, branches and repositories named. No adjacent edits, +- Do exactly the scope asked - only the files, branches and repositories named. No adjacent edits, no cleanup, no onboarding, no surveys. - Anything else you notice gets one line at most, with no proposed fix. - Never close a reply with a condensed restatement of it, and never present an inference as a @@ -21,22 +21,22 @@ PHPUnit 13 enforces a strict separation between mocks and stubs. Violating these - **Use `createStub()`** when the test double only needs to return values (`method()->willReturn()`, `method()->willReturnCallback()`). No expectations are configured. - **Use `createMock()`** only when the test verifies behavior with `expects()` (e.g. `expects($this->once())`, `expects($this->never())`). -- **Never** call `createMock()` without also calling `expects()` on at least one method — PHPUnit 13 will issue a notice. +- **Never** call `createMock()` without also calling `expects()` on at least one method - PHPUnit 13 will issue a notice. - **Remove** `/** @var ClassName&MockObject */` annotations and `MockObject` intersection types on variables created with `createStub()`. - **Remove** the `use PHPUnit\Framework\MockObject\MockObject;` import from any file where no `createMock()` + `expects()` usage remains. ### Examples ```php -// CORRECT — stub returns value, no expectations +// CORRECT - stub returns value, no expectations $container = $this->createStub(ContainerInterface::class); $container->method('get')->willReturnCallback(static fn(string $id): mixed => ...); -// CORRECT — mock verifies behavior with expects() +// CORRECT - mock verifies behavior with expects() $handler = $this->createMock(ErrorHandler::class); $handler->expects($this->once())->method('attachListener'); -// WRONG — createMock() with no expects() triggers PHPUnit notice +// WRONG - createMock() with no expects() triggers PHPUnit notice $logger = $this->createMock(Logger::class); $logger->method('withName')->willReturn($logger); // should be createStub() ``` @@ -45,6 +45,6 @@ $logger->method('withName')->willReturn($logger); // should be createStub() `phpunit.xml.dist` has `requireCoverageMetadata="true"`. Every test class **must** have: -1. `#[CoversClass(ClassName::class)]` — one per source class under test. -2. `#[CoversMethod(ClassName::class, 'methodName')]` — one per public/protected method exercised. +1. `#[CoversClass(ClassName::class)]` - one per source class under test. +2. `#[CoversMethod(ClassName::class, 'methodName')]` - one per public/protected method exercised. 3. `use PHPUnit\Framework\Attributes\CoversClass;` and `use PHPUnit\Framework\Attributes\CoversMethod;` imports. diff --git a/Dockerfile b/Dockerfile index 7fc91b8..9e633be 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ # # Builds a self-contained, interactive PHP development environment carrying # the Webware toolchain: PHP CLI, Composer, Mago, and Xdebug. Developers work -# inside this container — either through the VS Code Dev Container (backed by +# inside this container: either through the VS Code Dev Container (backed by # compose.yml) or by `docker compose up -d` + `docker compose exec tooling`. # The host machine (Windows, WSL, Linux, or macOS) never needs a native PHP # toolchain. diff --git a/compose.yml b/compose.yml index 97fb3d0..7188545 100644 --- a/compose.yml +++ b/compose.yml @@ -9,7 +9,7 @@ # This compose file is the single source of truth for the environment. Two # ways to use it: # -# 1. VS Code Dev Container — "Reopen in Container" uses +# 1. VS Code Dev Container: "Reopen in Container" uses # .devcontainer/devcontainer.json, which is *backed by* this file (it # references the `tooling` service rather than defining its own). # @@ -55,13 +55,13 @@ services: - "host.docker.internal:host-gateway" volumes: # Bind-mount the source so edits are visible on both the host and the - # container. vendor/ is NOT overridden — it is the host's own copy. + # container. vendor/ is NOT overridden - it is the host's own copy. # Composer/installs inside the container write straight into the # developer's working tree on disk. - .:/app # The Composer cache lives in a named volume (Linux-native I/O, fast on # Windows) so downloads survive container rebuilds. It is only a cache - # — the developer's real artifacts all live on the host. + # - the developer's real artifacts all live on the host. - composer-cache:/tmp/composer-cache depends_on: mysql: @@ -69,7 +69,7 @@ services: working_dir: /app # ------------------------------------------------------------------------- - # MySQL — required by the PhpDbHandler integration tests. Credentials mirror + # MySQL - required by the PhpDbHandler integration tests. Credentials mirror # the CI `db-env-json` (root, empty password, `webware_log_test` database). # The test schema and data are created by the PHPUnit ListenerExtension # (MysqlFixtureLoader) at test time, not by this service. @@ -91,7 +91,7 @@ services: - mysql-data:/var/lib/mysql # ------------------------------------------------------------------------- - # phpMyAdmin — web UI for the `mysql` service above. Reachable at + # phpMyAdmin - web UI for the `mysql` service above. Reachable at # http://localhost:8080 (auto-login as root). Note: root uses an empty # password, matching the test/CI convention. # ------------------------------------------------------------------------- diff --git a/docs/Project_Architecture_Blueprint.md b/docs/Project_Architecture_Blueprint.md index b124710..ff38193 100644 --- a/docs/Project_Architecture_Blueprint.md +++ b/docs/Project_Architecture_Blueprint.md @@ -1,7 +1,7 @@ -# Project Architecture Blueprint — `webware/webware-log` +# Project Architecture Blueprint: `webware/webware-log` > **Current stable:** 0.0.x (branch: `add-phpdb-handler`) -> **Next target:** 0.1.0 — see [Roadmap](#14-roadmap--version-scope) for planned changes +> **Next target:** 0.1.0 - see [Roadmap](#14-roadmap--version-scope) for planned changes > **License:** BSD-3-Clause > **PHP requirement:** ~8.4.0 || ~8.5.0 || ~8.6.0 @@ -36,7 +36,7 @@ - Provides Monolog handlers that persist log records to a relational database using either `laminas-db` or `php-db/phpdb`. - Decorates Mezzio's built-in `ErrorHandler` so that uncaught exceptions are automatically logged. -> **Note — Laminas MVC:** The Laminas team is retiring the MVC framework. All MVC-specific integration (`Runtime::Mvc`, `AbstractController` listener identifiers) was removed in 0.1.0, and the Laminas EventManager bridge listener has since been removed entirely — PSR-14 is the only supported dispatch path. +> **Note - Laminas MVC:** The Laminas team is retiring the MVC framework. All MVC-specific integration (`Runtime::Mvc`, `AbstractController` listener identifiers) was removed in 0.1.0, and the Laminas EventManager bridge listener has since been removed entirely - PSR-14 is the only supported dispatch path. ### Guiding Principles @@ -71,7 +71,7 @@ ## 2. Architecture Visualization -### C4 — Context Diagram +### C4: Context Diagram ``` ┌──────────────────────────────────────────────────────────────────────┐ @@ -90,7 +90,7 @@ └──────────────────────────────────────────────────────────────────────┘ ``` -### C4 — Component Diagram +### C4: Component Diagram ``` ┌─────────────────────────────────────────────────────────────────────────────┐ @@ -132,7 +132,7 @@ └─────────────────────────────────────────────────────────────────────────────┘ ``` -### Data Flow — Request Log Entry +### Data Flow: Request Log Entry ``` HTTP Request @@ -156,7 +156,7 @@ LaminasDbHandler | PhpDbHandler .write(LogRecord) MySQL log table ``` -### Data Flow — PSR-14 Log Event +### Data Flow: PSR-14 Log Event ``` Handler / Middleware / Service @@ -172,7 +172,7 @@ Psr3LogPsr14Listener.__invoke(LogEvent) Monolog\Logger.log() → handler chain → DB ``` -### Data Flow — Uncaught Exception +### Data Flow: Uncaught Exception ``` Mezzio ErrorHandler.process() @@ -233,7 +233,7 @@ The root wiring class for the Laminas component installer. When invoked, it retu | `LogFactory` | Factory | `Monolog\Logger` as `LoggerInterface` | Pushes handlers and processors in order | | `MezzioErrorHandlerDelegator` | Delegator | `Laminas\Stratigility\Middleware\ErrorHandler` | Conditionally attaches `MezzioErrorListener` when `log_errors = true` | -The delegator pattern means the error handler is extended non-destructively — if `log_errors` is `false`, the original handler is returned unmodified. +The delegator pattern means the error handler is extended non-destructively - if `log_errors` is `false`, the original handler is returned unmodified. --- @@ -367,7 +367,7 @@ CREATE TABLE `log` ( ### UUID Strategy -UUID v7 (time-ordered, monotonic) is used — generated per record using `ramsey/uuid` with the record's own `datetime`. This allows chronological sorting on the `uuid` column while maintaining global uniqueness. +UUID v7 (time-ordered, monotonic) is used - generated per record using `ramsey/uuid` with the record's own `datetime`. This allows chronological sorting on the `uuid` column while maintaining global uniqueness. --- @@ -408,7 +408,7 @@ The component itself does not log its own operations. Internal errors propagate - `ConfigProvider` is the entry point for `laminas-component-installer`. - The `extra.laminas` key in `composer.json` identifies the component and its config-provider FQCN. -- The component registers itself as a **library** (not an application) — it ships defaults that applications can override. +- The component registers itself as a **library** (not an application) - it ships defaults that applications can override. ### 7.2 Mezzio Middleware Pipeline @@ -435,9 +435,9 @@ The dispatcher calls `Psr3LogPsr14Listener::__invoke(LogEvent)`, which translate Handler and processor registration order in `LogFactory`: ``` LaminasDbHandler | PhpDbHandler ← handler (persists to DB) -[RamseyUuidProcessor] ← processor #1 (UUID v7) — guarded by process_uuid flag (0.1.0+) +[RamseyUuidProcessor] ← processor #1 (UUID v7) - guarded by process_uuid flag (0.1.0+) PsrLogMessageProcessor ← processor #2 (interpolate placeholders) -[LaminasI18nProcessor] ← processor #3 (translate, optional) — guarded by process_translation flag +[LaminasI18nProcessor] ← processor #3 (translate, optional) - guarded by process_translation flag ``` Monolog processes records in **LIFO** order for processors and passes through handler stack in registration order. @@ -479,7 +479,7 @@ The `PhpDbHandler` is the current development focus (branch `add-phpdb-handler`) ### 8.2 Adding a New Processor 1. Create `src/Processor/MyProcessor.php` implementing `Monolog\Processor\ProcessorInterface`. -2. `__invoke(LogRecord $record): LogRecord` — enrich `$record->extra` and return `$record->with(extra: ...)`. +2. `__invoke(LogRecord $record): LogRecord` - enrich `$record->extra` and return `$record->with(extra: ...)`. 3. If container dependencies are needed, create `src/Processor/MyProcessorFactory.php`. 4. Register as `invokables` (no deps) or `factories` (with deps) in `ConfigProvider`. 5. Add `$logger->pushProcessor(...)` in `LogFactory`. @@ -522,7 +522,7 @@ The `EventDispatcherInterface` is resolved from the container (provided by `phly ``` test/ ├── unit/ -│ └── TestAsset/ (shared test doubles — currently empty) +│ └── TestAsset/ (shared test doubles - currently empty) └── integration/ ├── Extension/ │ ├── ListenerExtension.php (PHPUnit bootstrap extension) @@ -589,7 +589,7 @@ The `mysql.sql` fixture is auto-executed by MySQL's `docker-entrypoint-initdb.d` ### Runtime Dependency: DB Adapter -This component requires a `Laminas\Db\Adapter\AdapterInterface` (for `LaminasDbHandler`) or `PhpDb\Adapter\AdapterInterface` (for `PhpDbHandler`) to be registered in the application container. These are not shipped with this component — the host application is responsible for configuring the DB adapter. +This component requires a `Laminas\Db\Adapter\AdapterInterface` (for `LaminasDbHandler`) or `PhpDb\Adapter\AdapterInterface` (for `PhpDbHandler`) to be registered in the application container. These are not shipped with this component - the host application is responsible for configuring the DB adapter. --- @@ -599,7 +599,7 @@ This component requires a `Laminas\Db\Adapter\AdapterInterface` (for `LaminasDbH 1. Push `PhpDbHandler` onto the logger in `LogFactory` (optionally remove `LaminasDbHandler`). 2. Update the DB schema: rename `userIdentifier` → `user_identifier` and add a `context JSON` column. -3. Ensure the host application registers `PhpDb\Adapter\AdapterInterface::class` in the container via `php-db/phpdb`'s own config provider — **not** using laminas-db configuration structure; the two config structures are incompatible. +3. Ensure the host application registers `PhpDb\Adapter\AdapterInterface::class` in the container via `php-db/phpdb`'s own config provider - **not** using laminas-db configuration structure; the two config structures are incompatible. 4. Both adapters can coexist in the container under separate FQCN service IDs. ### 11.2 Adding a Non-DB Handler @@ -607,7 +607,7 @@ This component requires a `Laminas\Db\Adapter\AdapterInterface` (for `LaminasDbH 1. Registering it as a service in the DI container. 2. Pushing it onto the logger in `LogFactory`. -The handler stack in Monolog is ordered — add higher-priority handlers last (they are tried first). +The handler stack in Monolog is ordered - add higher-priority handlers last (they are tried first). ### 11.4 Scoping Processors per Channel @@ -685,7 +685,7 @@ test (phpunit unit suite) | Branch | Purpose | |---|---| | `0.0.x` | Default / stable release branch | -| `add-phpdb-handler` | Active development — adds `PhpDbHandler` (merges to 0.0.x) | +| `add-phpdb-handler` | Active development - adds `PhpDbHandler` (merges to 0.0.x) | | `0.1.x` _(planned)_ | Target for all 0.1.0 refactoring work | ### Known Technical Debt (0.0.x) @@ -695,13 +695,13 @@ Items marked ✅ are addressed by the [0.1.0 refactoring plan](../plan/refactor- | Location | Issue | 0.1.0 | |---|---|---| | `LaminasDbHandler` | `Sql` re-created per `write()` call | ✅ TASK-025 | -| `LaminasDbHandler` | Column named `userIdentifier` (camelCase) — inconsistent with `PhpDbHandler` | ✅ TASK-026 | +| `LaminasDbHandler` | Column named `userIdentifier` (camelCase) - inconsistent with `PhpDbHandler` | ✅ TASK-026 | | `MonologMiddleware` | Hard-coded `UserInterface` attribute key | ✅ TASK-027 | | `LogEvent::getChannel()` | Instantiates `new ConfigProvider()` at runtime | ✅ TASK-007 | | `LogFactory` | `process_uuid` / `process_translation` flags not honoured | ✅ TASK-028 | | `phpunit.xml.dist` | Schema targets PHPUnit 11.4 but requirement is ^13.0 | ✅ TASK-029 | -| `phpcs.xml` | Legacy PHP_CodeSniffer config — tool is not used; style enforced by `php-cs-fixer` | ✅ TASK-031 | -| `psalm.xml.dist` / `psalm-baseline.xml` | Psalm config files — `vimeo/psalm` replaced by PHPStan | ✅ TASK-032 | +| `phpcs.xml` | Legacy PHP_CodeSniffer config - tool is not used; style enforced by `php-cs-fixer` | ✅ TASK-031 | +| `psalm.xml.dist` / `psalm-baseline.xml` | Psalm config files - `vimeo/psalm` replaced by PHPStan | ✅ TASK-032 | | `composer.json` scripts | `cs-check`/`cs-fix` pointed to phpcs/phpcbf; `static-analysis` pointed to `psalm` | ✅ TASK-033 | | `ConfigProvider` | Config key `ConfigProvider::class` should reflect what it configures | ✅ TASK-001 | | `Runtime::Mvc` | MVC case defined but never wired; Laminas MVC being retired | ✅ TASK-009 | @@ -713,11 +713,11 @@ Items marked ✅ are addressed by the [0.1.0 refactoring plan](../plan/refactor- ### 0.0.x (Current) - Core PSR-3 / Monolog integration for Mezzio -- `LaminasDbHandler` — persist log records via `laminas-db` -- `PhpDbHandler` — persist log records via `php-db/phpdb` _(branch: `add-phpdb-handler`)_ -- `MezzioErrorHandlerDelegator` — automatic error logging -- `MonologMiddleware` — user identity enrichment -- `Psr3LogLaminasListener` — Laminas EventManager → PSR-3 bridge +- `LaminasDbHandler` - persist log records via `laminas-db` +- `PhpDbHandler` - persist log records via `php-db/phpdb` _(branch: `add-phpdb-handler`)_ +- `MezzioErrorHandlerDelegator` - automatic error logging +- `MonologMiddleware` - user identity enrichment +- `Psr3LogLaminasListener` - Laminas EventManager → PSR-3 bridge ### 0.1.0 (Planned) diff --git a/docs/error-logging.md b/docs/error-logging.md index 0681b28..e7b9870 100644 --- a/docs/error-logging.md +++ b/docs/error-logging.md @@ -42,4 +42,4 @@ The error is logged at `ERROR` level with the exception, request, and response a - The `ErrorHandler` continues to generate its own error response as normal. - No exception is swallowed. -- The delegator pattern means the `ErrorHandler` service ID in the container still resolves to the original `ErrorHandler` instance — only a listener is attached to it. +- The delegator pattern means the `ErrorHandler` service ID in the container still resolves to the original `ErrorHandler` instance - only a listener is attached to it. diff --git a/docs/handlers.md b/docs/handlers.md index 0496d13..c0a34d9 100644 --- a/docs/handlers.md +++ b/docs/handlers.md @@ -30,7 +30,7 @@ Uses `laminas/laminas-db` to write records. By default the handler reads `extra.email` for the user column. Override via config: ```php -// Not yet exposed as a top-level config key — override by extending +// Not yet exposed as a top-level config key - override by extending // LaminasDbHandlerFactory and passing a different $extraAuthIdentifier // to the LaminasDbHandler constructor. ``` diff --git a/docs/middleware.md b/docs/middleware.md index 931f93e..c6bc7cd 100644 --- a/docs/middleware.md +++ b/docs/middleware.md @@ -17,7 +17,7 @@ use Webware\Log\Http\Middleware\MonologMiddleware; $app->pipe(MonologMiddleware::class); ``` -**Config-driven pipeline** — uncomment the `middleware_pipeline` section in `ConfigProvider`: +**Config-driven pipeline**: uncomment the `middleware_pipeline` section in `ConfigProvider`: ```php // src/ConfigProvider.php (or your own config file) diff --git a/plan/refactor-axleus-log-0.1.0.md b/plan/refactor-axleus-log-0.1.0.md index 432eb67..bd8c756 100644 --- a/plan/refactor-axleus-log-0.1.0.md +++ b/plan/refactor-axleus-log-0.1.0.md @@ -1,5 +1,5 @@ --- -goal: Refactor webware-log to v0.1.0 — PSR-14 support, config key migration, MVC removal, dual DB adapter, and technical debt resolution +goal: Refactor webware-log to v0.1.0 - PSR-14 support, config key migration, MVC removal, dual DB adapter, and technical debt resolution version: 0.1.0 date_created: 2026-05-01 last_updated: 2026-05-02 @@ -14,11 +14,11 @@ tags: [refactor, architecture, feature, chore] This plan covers all changes targeted for the `0.1.0` release of `webware/webware-log`. The changes fall into five areas: -1. **Config key migration** — replace `ConfigProvider::class` as the top-level config array key with `LoggerInterface::class`. -2. **Laminas MVC removal** — remove all MVC-specific integration code since the Laminas team is retiring the MVC framework. -3. **PSR-14 event dispatcher support** — add `phly/phly-event-dispatcher` to bridge log events via the standards-compliant PSR-14 dispatcher alongside (and eventually replacing) the non-PSR-compliant Laminas EventManager bridge. -4. **Dual DB adapter resolution** — consolidate factory container resolution so both `laminas-db` and `php-db/phpdb` adapters are resolved via `AdapterInterface::class`, with explicit documentation of phpdb's different configuration structure. -5. **Technical debt resolution** — fix all known `// todo` items and configuration inconsistencies identified in the v0.0.x codebase. +1. **Config key migration**: replace `ConfigProvider::class` as the top-level config array key with `LoggerInterface::class`. +2. **Laminas MVC removal**: remove all MVC-specific integration code since the Laminas team is retiring the MVC framework. +3. **PSR-14 event dispatcher support**: add `phly/phly-event-dispatcher` to bridge log events via the standards-compliant PSR-14 dispatcher alongside (and eventually replacing) the non-PSR-compliant Laminas EventManager bridge. +4. **Dual DB adapter resolution**: consolidate factory container resolution so both `laminas-db` and `php-db/phpdb` adapters are resolved via `AdapterInterface::class`, with explicit documentation of phpdb's different configuration structure. +5. **Technical debt resolution**: fix all known `// todo` items and configuration inconsistencies identified in the v0.0.x codebase. --- @@ -40,14 +40,14 @@ This plan covers all changes targeted for the `0.1.0` release of `webware/webwar - **CON-004**: Breaking changes in config key and event system are acceptable for a minor version bump (0.0.x → 0.1.0). A CHANGELOG entry and migration note in the README are required. - **GUD-001**: All new classes must follow the `webware/coding-standard` rules enforced by `.php-cs-fixer.dist.php` (`@Webware/coding-standard-1.0` rule set via `php-cs-fixer`). - **GUD-002**: All new factories must follow the existing pattern: read `$config[LoggerInterface::class]` from the container's `config` service. -- **PAT-001**: Constructor injection only — no service-locator usage inside domain classes. +- **PAT-001**: Constructor injection only - no service-locator usage inside domain classes. - **PAT-002**: PSR-14 listener must be a standalone callable class, not a closure, to allow container resolution and testing. --- ## 2. Implementation Steps -### Implementation Phase 1 — Config Key Migration +### Implementation Phase 1: Config Key Migration - **GOAL-001**: Replace `ConfigProvider::class` with `LoggerInterface::class` as the top-level component config key in all source files. @@ -64,7 +64,7 @@ This plan covers all changes targeted for the `0.1.0` release of `webware/webwar --- -### Implementation Phase 2 — Remove Laminas MVC Integration +### Implementation Phase 2: Remove Laminas MVC Integration - **GOAL-002**: Excise all Laminas MVC-specific code, leaving only Mezzio (PSR-15) integration. @@ -78,7 +78,7 @@ This plan covers all changes targeted for the `0.1.0` release of `webware/webwar --- -### Implementation Phase 3 — PSR-14 Event Dispatcher Support +### Implementation Phase 3: PSR-14 Event Dispatcher Support - **GOAL-003**: Add a standards-compliant PSR-14 log event and listener using `phly/phly-event-dispatcher`. @@ -94,7 +94,7 @@ This plan covers all changes targeted for the `0.1.0` release of `webware/webwar --- -### Implementation Phase 4 — Dual DB Adapter Resolution +### Implementation Phase 4: Dual DB Adapter Resolution - **GOAL-004**: Ensure both `LaminasDbHandler` and `PhpDbHandler` factories correctly resolve their respective `AdapterInterface` from the PSR-11 container without cross-contamination of config structures. @@ -107,14 +107,14 @@ This plan covers all changes targeted for the `0.1.0` release of `webware/webwar --- -### Implementation Phase 5 — Technical Debt Resolution +### Implementation Phase 5: Technical Debt Resolution - **GOAL-005**: Resolve all `// todo` items and known inconsistencies identified in v0.0.x. | Task | Description | Completed | Date | |------|-------------|-----------|------| | TASK-025 | `src/Handler/LaminasDbHandler.php`: move `$sql = new Sql($this->adapterInterface, $this->table)` from `write()` into the constructor. Declare `private readonly Sql $sql` property. Remove the per-call instantiation. | | | -| TASK-026 | `src/Handler/LaminasDbHandler.php`: rename column `userIdentifier` to `user_identifier` (snake_case) to align with `PhpDbHandler` and update `test/integration/TestFixtures/mysql.sql` fixture accordingly. This is a **breaking schema change** — document in CHANGELOG. | | | +| TASK-026 | `src/Handler/LaminasDbHandler.php`: rename column `userIdentifier` to `user_identifier` (snake_case) to align with `PhpDbHandler` and update `test/integration/TestFixtures/mysql.sql` fixture accordingly. This is a **breaking schema change**: document in CHANGELOG. | | | | TASK-027 | `src/Middleware/MonologMiddleware.php`: resolve the `UserInterface` attribute key by reading `$config[LoggerInterface::class]['auth_attribute']` with a fallback to `Mezzio\Authentication\UserInterface::class`. Inject config via constructor. Create `MonologMiddlewareFactory` update to pass config. | | | | TASK-028 | `src/Container/LogFactory.php`: wrap `$logger->pushProcessor($uuidProcessor)` in an `if ($config['process_uuid'])` guard. Wrap the `LaminasI18nProcessor` push in an `if ($config['process_translation'])` guard (the existing container check can remain as an additional guard). | | | | TASK-029 | `phpunit.xml.dist`: update `xsi:noNamespaceSchemaLocation` from `https://schema.phpunit.de/11.4/phpunit.xsd` to `https://schema.phpunit.de/13.0/phpunit.xsd` to match the `phpunit/phpunit: ^13.0` requirement. | | | @@ -127,61 +127,61 @@ This plan covers all changes targeted for the `0.1.0` release of `webware/webwar ## 3. Alternatives -- **ALT-001**: Keep `ConfigProvider::class` as the config key and add `LoggerInterface::class` as an alias. Rejected — maintaining two keys adds complexity and the goal is a clean migration. -- **ALT-002**: Keep `Runtime` enum and support MVC conditionally via feature flags. Rejected — the Laminas team is retiring MVC; continued support would be maintenance burden with no upstream future. -- **ALT-003**: Replace `Psr3LogLaminasListener` entirely in 0.1.0 rather than deprecating. Rejected — some host applications may still dispatch via `Laminas\EventManager` in Mezzio contexts. Deprecation in 0.1.0 with full removal in 0.2.0 is safer. -- **ALT-004**: Provide a single `DbHandlerFactory` that auto-detects the available adapter. Rejected — the two adapters have different config structures (phpdb does not use laminas-db config); auto-detection would silently select the wrong adapter. Explicit factories are unambiguous. -- **ALT-005**: Use `ramsey/uuid` UUID v4 instead of v7. Rejected — UUID v7 is time-ordered, which improves B-tree index write performance on the `uuid` column. +- **ALT-001**: Keep `ConfigProvider::class` as the config key and add `LoggerInterface::class` as an alias. Rejected - maintaining two keys adds complexity and the goal is a clean migration. +- **ALT-002**: Keep `Runtime` enum and support MVC conditionally via feature flags. Rejected - the Laminas team is retiring MVC; continued support would be maintenance burden with no upstream future. +- **ALT-003**: Replace `Psr3LogLaminasListener` entirely in 0.1.0 rather than deprecating. Rejected - some host applications may still dispatch via `Laminas\EventManager` in Mezzio contexts. Deprecation in 0.1.0 with full removal in 0.2.0 is safer. +- **ALT-004**: Provide a single `DbHandlerFactory` that auto-detects the available adapter. Rejected - the two adapters have different config structures (phpdb does not use laminas-db config); auto-detection would silently select the wrong adapter. Explicit factories are unambiguous. +- **ALT-005**: Use `ramsey/uuid` UUID v4 instead of v7. Rejected - UUID v7 is time-ordered, which improves B-tree index write performance on the `uuid` column. --- ## 4. Dependencies -- **DEP-001**: `phly/phly-event-dispatcher: ^1.5.0` — move from `require-dev` to `require`. -- **DEP-002**: `psr/event-dispatcher` — transitively required by `phly/phly-event-dispatcher`; provides `Psr\EventDispatcher\StoppableEventInterface` and `Psr\EventDispatcher\EventDispatcherInterface`. -- **DEP-003**: `laminas/laminas-eventmanager: ^3.14` — remains in `require-dev` only; used in tests and for the deprecated `Psr3LogLaminasListener`. -- **DEP-004**: `php-db/phpdb` and associated driver packages — must be registered in the host application container. Not a direct `composer.json` dependency of this component. -- **DEP-005**: `laminas/laminas-db` — must be registered in the host application container when `LaminasDbHandler` is used. Not a direct `composer.json` dependency of this component. +- **DEP-001**: `phly/phly-event-dispatcher: ^1.5.0` - move from `require-dev` to `require`. +- **DEP-002**: `psr/event-dispatcher` - transitively required by `phly/phly-event-dispatcher`; provides `Psr\EventDispatcher\StoppableEventInterface` and `Psr\EventDispatcher\EventDispatcherInterface`. +- **DEP-003**: `laminas/laminas-eventmanager: ^3.14` - remains in `require-dev` only; used in tests and for the deprecated `Psr3LogLaminasListener`. +- **DEP-004**: `php-db/phpdb` and associated driver packages - must be registered in the host application container. Not a direct `composer.json` dependency of this component. +- **DEP-005**: `laminas/laminas-db` - must be registered in the host application container when `LaminasDbHandler` is used. Not a direct `composer.json` dependency of this component. - **DEP-006**: Remove `vimeo/psalm` and `psalm/plugin-phpunit` from `require-dev`; update `scripts.static-analysis` from `"psalm --shepherd --stats"` to `"phpstan analyse"`. Psalm is replaced by PHPStan as the sole static analysis tool. --- ## 5. Files -- **FILE-001**: `src/ConfigProvider.php` — config key migration, `log_runtime` removal, listener registration update, method rename. -- **FILE-002**: `src/Runtime.php` — **deleted**. -- **FILE-003**: `src/Event/LogEvent.php` — refactored: remove Laminas EM inheritance, implement PSR-14 `StoppableEventInterface`, remove `new ConfigProvider()` call, update constructor. -- **FILE-004**: `src/Container/LogFactory.php` — config key migration, `process_uuid`/`process_translation` flag guards. -- **FILE-005**: `src/Container/MezzioErrorHandlerDelegator.php` — config key migration. -- **FILE-006**: `src/Handler/LaminasDbHandler.php` — move `Sql` to constructor, rename column `userIdentifier` → `user_identifier`. -- **FILE-007**: `src/Handler/LaminasDbHandlerFactory.php` — config key migration, explicit `\Laminas\Db\Adapter\AdapterInterface::class` resolution. -- **FILE-008**: `src/Handler/PhpDbHandlerFactory.php` — config key migration, explicit `\PhpDb\Adapter\AdapterInterface::class` resolution, inline comment on config structure. -- **FILE-009**: `src/Listener/Psr3LogLaminasListener.php` — remove `AbstractController` identifier; add `@deprecated` annotation. -- **FILE-010**: `src/Listener/Psr3LogPsr14Listener.php` — **new file**. -- **FILE-011**: `src/Listener/Psr3LogPsr14ListenerFactory.php` — **new file**. -- **FILE-012**: `src/Middleware/MonologMiddleware.php` — configurable auth attribute key; inject config. -- **FILE-013**: `src/Middleware/MonologMiddlewareFactory.php` — pass config to `MonologMiddleware`. -- **FILE-014**: `test/integration/TestFixtures/mysql.sql` — rename `userIdentifier` column to `user_identifier`. -- **FILE-015**: `phpunit.xml.dist` — update PHPUnit schema URL to 13.0. -- **FILE-016**: `composer.json` — move `phly/phly-event-dispatcher` from `require-dev` to `require`. -- **FILE-017**: `docs/Project_Architecture_Blueprint.md` — update for 0.1.0 scope (separate update, see blueprint). -- **FILE-018**: `phpcs.xml` — **deleted**. PHP_CodeSniffer is no longer used; code style is enforced entirely by `.php-cs-fixer.dist.php`. -- **FILE-019**: `psalm.xml.dist` — **deleted**. Psalm is replaced by PHPStan as the sole static analysis tool. -- **FILE-020**: `psalm-baseline.xml` — **deleted**. Psalm baseline is no longer relevant after removing `vimeo/psalm`. -- **FILE-021**: `src/Container/ListenerProviderAggregateFactory.php` — **new file**. Builds `ListenerProviderAggregate` from `'listeners'` and `'listener_providers'` config keys, compatible with `webware/commandbus-event` conventions. -- **FILE-022**: `test/unit/ConfigProviderTest.php` — extended with PSR-14 alias, listener key, and `getListeners()` format assertions. +- **FILE-001**: `src/ConfigProvider.php` - config key migration, `log_runtime` removal, listener registration update, method rename. +- **FILE-002**: `src/Runtime.php` - **deleted**. +- **FILE-003**: `src/Event/LogEvent.php` - refactored: remove Laminas EM inheritance, implement PSR-14 `StoppableEventInterface`, remove `new ConfigProvider()` call, update constructor. +- **FILE-004**: `src/Container/LogFactory.php` - config key migration, `process_uuid`/`process_translation` flag guards. +- **FILE-005**: `src/Container/MezzioErrorHandlerDelegator.php` - config key migration. +- **FILE-006**: `src/Handler/LaminasDbHandler.php` - move `Sql` to constructor, rename column `userIdentifier` → `user_identifier`. +- **FILE-007**: `src/Handler/LaminasDbHandlerFactory.php` - config key migration, explicit `\Laminas\Db\Adapter\AdapterInterface::class` resolution. +- **FILE-008**: `src/Handler/PhpDbHandlerFactory.php` - config key migration, explicit `\PhpDb\Adapter\AdapterInterface::class` resolution, inline comment on config structure. +- **FILE-009**: `src/Listener/Psr3LogLaminasListener.php` - remove `AbstractController` identifier; add `@deprecated` annotation. +- **FILE-010**: `src/Listener/Psr3LogPsr14Listener.php` - **new file**. +- **FILE-011**: `src/Listener/Psr3LogPsr14ListenerFactory.php` - **new file**. +- **FILE-012**: `src/Middleware/MonologMiddleware.php` - configurable auth attribute key; inject config. +- **FILE-013**: `src/Middleware/MonologMiddlewareFactory.php` - pass config to `MonologMiddleware`. +- **FILE-014**: `test/integration/TestFixtures/mysql.sql` - rename `userIdentifier` column to `user_identifier`. +- **FILE-015**: `phpunit.xml.dist` - update PHPUnit schema URL to 13.0. +- **FILE-016**: `composer.json` - move `phly/phly-event-dispatcher` from `require-dev` to `require`. +- **FILE-017**: `docs/Project_Architecture_Blueprint.md` - update for 0.1.0 scope (separate update, see blueprint). +- **FILE-018**: `phpcs.xml` - **deleted**. PHP_CodeSniffer is no longer used; code style is enforced entirely by `.php-cs-fixer.dist.php`. +- **FILE-019**: `psalm.xml.dist` - **deleted**. Psalm is replaced by PHPStan as the sole static analysis tool. +- **FILE-020**: `psalm-baseline.xml` - **deleted**. Psalm baseline is no longer relevant after removing `vimeo/psalm`. +- **FILE-021**: `src/Container/ListenerProviderAggregateFactory.php` - **new file**. Builds `ListenerProviderAggregate` from `'listeners'` and `'listener_providers'` config keys, compatible with `webware/commandbus-event` conventions. +- **FILE-022**: `test/unit/ConfigProviderTest.php` - extended with PSR-14 alias, listener key, and `getListeners()` format assertions. --- ## 6. Testing -- **TEST-001**: Unit test for `LogEvent` — verify `isPropagationStopped()` returns `false` by default; returns `true` after `stopPropagation()`; all accessor methods return correct values. ✅ `test/unit/Event/LogEventTest.php` -- **TEST-002**: Unit test for `Psr3LogPsr14Listener` — verify `__invoke(LogEvent)` calls `$logger->log()` with the correct level, message, and context; verify `withName()` is called when the channel differs from `LogChannel::App`; verify renamed logger is used for the actual `log()` call. ✅ `test/unit/Listener/Psr3LogPsr14ListenerTest.php` -- **TEST-003**: Unit test for `LogFactory` — verify that when `process_uuid = false`, `RamseyUuidProcessor` is NOT pushed; when `process_translation = false`, `LaminasI18nProcessor` is NOT pushed. -- **TEST-004**: Unit test for `LaminasDbHandlerFactory` and `PhpDbHandlerFactory` — verify each resolves its own adapter FQCN and reads config under `LoggerInterface::class`. -- **TEST-005**: Unit test for `MonologMiddleware` — verify the auth attribute key is read from config, with correct fallback to `UserInterface::class`. -- **TEST-006**: Integration test for `LaminasDbHandler` — verify the `user_identifier` column (snake_case) is populated correctly after the column rename. -- **TEST-007**: Integration test for `PhpDbHandler` — verify `context` JSON column is populated and `user_identifier` is set. +- **TEST-001**: Unit test for `LogEvent` - verify `isPropagationStopped()` returns `false` by default; returns `true` after `stopPropagation()`; all accessor methods return correct values. ✅ `test/unit/Event/LogEventTest.php` +- **TEST-002**: Unit test for `Psr3LogPsr14Listener` - verify `__invoke(LogEvent)` calls `$logger->log()` with the correct level, message, and context; verify `withName()` is called when the channel differs from `LogChannel::App`; verify renamed logger is used for the actual `log()` call. ✅ `test/unit/Listener/Psr3LogPsr14ListenerTest.php` +- **TEST-003**: Unit test for `LogFactory` - verify that when `process_uuid = false`, `RamseyUuidProcessor` is NOT pushed; when `process_translation = false`, `LaminasI18nProcessor` is NOT pushed. +- **TEST-004**: Unit test for `LaminasDbHandlerFactory` and `PhpDbHandlerFactory` - verify each resolves its own adapter FQCN and reads config under `LoggerInterface::class`. +- **TEST-005**: Unit test for `MonologMiddleware` - verify the auth attribute key is read from config, with correct fallback to `UserInterface::class`. +- **TEST-006**: Integration test for `LaminasDbHandler` - verify the `user_identifier` column (snake_case) is populated correctly after the column rename. +- **TEST-007**: Integration test for `PhpDbHandler` - verify `context` JSON column is populated and `user_identifier` is set. - **TEST-008**: Verify static analysis (`phpstan`) passes with zero new errors on all modified files. - **TEST-009**: Verify `php-cs-fixer --dry-run` reports no violations on all new and modified files against the `.php-cs-fixer.dist.php` ruleset (`@Webware/coding-standard-1.0`). diff --git a/test/unit/Http/Middleware/Container/MonologMiddlewareFactoryTest.php b/test/unit/Http/Middleware/Container/MonologMiddlewareFactoryTest.php index 54fedb1..5894848 100644 --- a/test/unit/Http/Middleware/Container/MonologMiddlewareFactoryTest.php +++ b/test/unit/Http/Middleware/Container/MonologMiddlewareFactoryTest.php @@ -52,7 +52,7 @@ public function invokeFallsBackToUserInterfaceClassWhenNoConfig(): void $factory = new MonologMiddlewareFactory(); $middleware = $factory($container); - // The default auth_attribute should be UserInterface::class — verify the + // The default auth_attribute should be UserInterface::class - verify the // middleware was constructed without throwing. $this->assertInstanceOf(MonologMiddleware::class, $middleware); }