Reorganize namespaces and expose decoded server primitives.#56
Merged
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…d applyTo Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… HttpRequestInvalid Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nses::from Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ic factory Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ch, delete fromJsonError Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…d factory param Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…UserAgent section Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- phpstan: drop ignoreErrors, include tests/, keep level 9. - phpcs: introduce phpcs.xml and run against src/ and tests/. - infection: remove ProtectedVisibility override so the default mutator set is enforced. - composer: declare psr/http-server-handler and psr/http-server-middleware used by the PSR-15 test drivers. - README: fix code blocks that mixed named with trailing positional arguments (fatal under PHP 8.5) and drop the fictional keyed argument on CacheControl::fromResponseDirectives' variadic. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Every public exception is now final, extends RuntimeException or LogicException, and implements HttpException directly. None extend another library exception, eliminating the protected constructor problem at the root. - HttpException carries url(), reason(), method() — all documented. - Every message lives in a class constant (REASON / REASON_TEMPLATE) and is built via positional sprintf when interpolation is needed. - Internal Server exceptions stop using named arguments on the native exception parent and centralize their messages in constants. - NetworkTransport keeps the catch order NetworkExceptionInterface → RequestExceptionInterface → ClientExceptionInterface and now folds the JSON depth literal at the json_encode call site. - SynthesizedResponseHasNoRaw exposes a private constructor and a ::create() factory; Client\Response uses it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Headers: get() keeps nullable return; mergedWith() now takes Headers not arrays; fromArray() removed (callers build Headers directly). - Client\Response: with() accepts ?Headers $headers = null; raw() moves after the single-line accessors so members ascend by body size. - Client\Request: query parameter is nullable with a null default; the variadic is renamed from headerables to headers throughout. - Internal\Client\Url::compose() returns the composed string directly; the Url value object disappears. RequestResolver consumes the string. - Server\Response delegates to InternalResponse using named arguments end-to-end. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Cookie/Stream/Uri constructor parameter lists ascend by name length with alphabetical tiebreak. - Code enum carries PHPDoc on the isError/isSuccess instance methods. - Internal namespace is fully PHPDoc-free: Uri, RouteParameterResolver, Directives, and the inline @var hint in Stream are gone. - Stream is rewritten so resource state is narrowed by inline is_resource() checks; PHPStan level 9 no longer needs ignoreErrors. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename every test method to test<Method>When<Condition>Then<Outcome>.
- Move the PSR-18 client stubs out of HttpTest and NetworkTransportTest
into reusable tests/Fixtures/Client/{CapturingClient,ThrowingClient}.
- Replace PHPUnit createStub(ServerRequestInterface/StreamInterface)
with real nyholm/psr7 ServerRequest instances wherever the test
exercises the public boundary.
- Switch tests to Headers::from()/new Headers() (no fromArray); the
RequestResolver now passes a Headers instance, so withMergedHeaders
test wiring is updated accordingly.
- Update exception tests to the flat hierarchy (each exception is a
direct HttpException implementor) and assert against the templated
message via assertStringContainsString.
- Internal\Client\UrlTest asserts on the returned string from
Url::compose() now that the Url value object is gone.
- Bring driver tests (Laminas, Slim) onto real ServerRequest instances
and stop using the fictional `noCache:` named argument on
CacheControl::fromResponseDirectives' variadic.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Overview lists every public primitive at the root namespace, including MimeType, Charset, SameSite, ResponseCacheDirectives, and UserAgent. - Error handling section explains that the hierarchy is now flat and drops the "base class for the others below" claim from the table. - Recommend catching the umbrella HttpException once readers have caught the specific failure modes they care about. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PHPStan level 9 (src/ + tests/) is green without ignoreErrors: - Headerable returns array<string, string|list<string>>; concrete Headerables narrow their @return so PHPStan agrees with the contract. - Headers::applyTo is templated over MessageInterface; the PSR-7 request type survives the round-trip. - Internal classes carry the minimum array-shape annotations PHPStan level 9 needs (Body, Attribute, QueryParameters, ResponseHeaders). - Stream stops depending on StreamMetaData, stores mode/seekable directly, and narrows the resource on every operation. - StreamFactory tolerates json_encode(false), drops the @internal- flavoured PHPDoc that used to suppress mixed casts. - Server\Response keeps a positional spread (PHP rejects named arguments followed by `...`); the rest stays named. - InternalResponse passes Header values through PSR-7's string|list contract, eliminating the previous defensive cast layer. Mutation tests reach 100% MSI / 100% covered MSI by: - Deleting unreachable defaults (`MAX_JSON_DEPTH`, `array_values` wrappers, `0` exception code) and inlining lone-use literals. - Adding focused assertions on the surfaces that previously left mutants alive: HttpNetworkFailed/HttpRequestInvalid getCode === 0, MalformedPath getMessage, the SynthesizedResponseHasNoRaw message, Cookie exception messages, the UserAgent single-string header fallback, and the Stream length/byte-count boundaries. - Wrapping the unwritable fwrite branch in a ternary so PHPStan and Infection both see a single throw path. 312 tests / 541 assertions / 0 phpcs warnings / 0 phpstan errors / 100% MSI / 100% Covered MSI on PHP 8.5. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Promote Attribute and Body out of Internal/Shared into the public src root, move DecodedRequest, QueryParameters, and Uri under Server/Decoded so consumers can depend on them without crossing the Internal boundary, and split path-validation failures into dedicated PathContainsScheme and PathContainsControlChars exceptions. Add TransportFailure and UserAgentProductIsEmpty for the previously generic transport and user agent error paths.
There was a problem hiding this comment.
Pull request overview
This PR restructures the library’s public surface by splitting server primitives into a TinyBlocks\Http\Server\* concept group, introducing a PSR-18 client facade (Http + transports), and adding new typed wrappers for decoded request data and header composition.
Changes:
- Move server request/response builders into
TinyBlocks\Http\Server\*and introduce decoded request primitives (DecodedRequest,Uri,QueryParameters). - Add a PSR-18 client facade (
Http,HttpBuilder, clientRequest/Response,Transportimplementations) plus new transport-focused exception types. - Replace the old
Headersinterface with a concreteHeadersvalue object and introduceHeaderablefor consistent header contributors.
Reviewed changes
Copilot reviewed 132 out of 133 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Unit/UserAgentTest.php | Adds unit coverage for new UserAgent. |
| tests/Unit/ThrowingClient.php | Adds PSR-18 throwing client test double. |
| tests/Unit/Server/ResponseWithCookiesTest.php | Updates server response cookie tests for new namespace. |
| tests/Unit/Server/ProtocolVersionTest.php | Updates protocol version test for server response. |
| tests/Unit/SameSiteTest.php | Moves SameSite test into Unit namespace and renames test. |
| tests/Unit/PsrRequestException.php | Adds PSR-18 request exception test double. |
| tests/Unit/PsrNetworkException.php | Adds PSR-18 network exception test double. |
| tests/Unit/PsrClientException.php | Adds PSR-18 client exception test double. |
| tests/Unit/HttpBuilderTest.php | Adds builder tests for Http. |
| tests/Unit/HeadersTest.php | Adds tests for new Headers value object. |
| tests/Unit/FailingTransport.php | Adds transport test double producing HttpExceptions. |
| tests/Unit/CodeTest.php | Expands Code behavior coverage and renames tests. |
| tests/Unit/Client/Transports/NetworkTransportTest.php | Adds unit coverage for PSR-18 network transport wrapper. |
| tests/Unit/Client/Transports/InMemoryTransportTest.php | Adds unit coverage for in-memory transport. |
| tests/Unit/Client/ResponseTest.php | Adds unit coverage for client Response wrapper/synthesis. |
| tests/Unit/Client/RequestTest.php | Adds unit coverage for client Request value object. |
| tests/Unit/CapturingClient.php | Adds capturing PSR-18 client test double. |
| tests/Models/Products.php | Normalizes iterable input to a list (drops keys). |
| tests/Internal/Stream/StreamTest.php | Removes tests for old internal stream implementation. |
| tests/Internal/Stream/StreamFactoryTest.php | Removes tests for old internal stream factory. |
| tests/Internal/Request/RouteParameterResolverTest.php | Removes tests for old internal request resolver. |
| tests/Drivers/Slim/SlimTest.php | Updates Slim integration test for server response + real request. |
| tests/Drivers/Middleware.php | Switches to positional args for PSR middleware handler call. |
| tests/Drivers/Laminas/LaminasTest.php | Updates Laminas integration test for server response + real request. |
| src/UserAgent.php | Introduces UserAgent header value object. |
| src/Server/Responses.php | Moves response factory interface into Server concept group and updates Headerable usage. |
| src/Server/Response.php | Adds public server Response facade backed by internal response implementation. |
| src/Server/Request.php | Adds public server Request wrapper with decode + typed method access. |
| src/Server/Decoded/Uri.php | Adds decoded URI accessor with route param resolution. |
| src/Server/Decoded/QueryParameters.php | Adds typed query parameter wrapper. |
| src/Server/Decoded/DecodedRequest.php | Adds decoded request aggregate (uri + body). |
| src/ResponseCacheDirectives.php | Re-points cache-control internals to Internal\Server\* and adds method docs. |
| src/Response.php | Removes old top-level Response facade. |
| src/Request.php | Removes old top-level Request wrapper. |
| src/Internal/Stream/StreamMetaData.php | Removes old internal stream metadata type. |
| src/Internal/Stream/Stream.php | Removes old internal stream implementation. |
| src/Internal/Server/Stream/StreamFactory.php | Moves/refactors stream factory under server internals. |
| src/Internal/Server/Stream/Stream.php | Adds new internal stream implementation. |
| src/Internal/Server/Response/ResponseHeaders.php | Moves/refactors response header aggregation. |
| src/Internal/Server/Response/ProtocolVersion.php | Moves/refactors protocol version wrapper. |
| src/Internal/Server/Response/InternalResponse.php | Moves/refactors PSR-7 response implementation for server facade. |
| src/Internal/Server/Request/RouteParameterResolver.php | Moves/refactors framework-agnostic route param resolution. |
| src/Internal/Server/Request/Decoder.php | Moves decoder to server internals and switches body decoding entrypoint. |
| src/Internal/Server/Exceptions/SameSiteNoneRequiresSecure.php | Introduces server-internal cookie invariant exception. |
| src/Internal/Server/Exceptions/NonWritableStream.php | Introduces server-internal stream exception. |
| src/Internal/Server/Exceptions/NonSeekableStream.php | Introduces server-internal stream exception. |
| src/Internal/Server/Exceptions/NonReadableStream.php | Introduces server-internal stream exception. |
| src/Internal/Server/Exceptions/MissingResourceStream.php | Introduces server-internal stream exception. |
| src/Internal/Server/Exceptions/CookieValueIsInvalid.php | Introduces server-internal cookie invariant exception. |
| src/Internal/Server/Exceptions/CookieNameIsInvalid.php | Introduces server-internal cookie invariant exception. |
| src/Internal/Server/Exceptions/ConflictingLifetimeAttributes.php | Introduces server-internal cookie invariant exception. |
| src/Internal/Server/Cookies/CookieValue.php | Moves cookie value validation into server internals. |
| src/Internal/Server/Cookies/CookieName.php | Moves cookie name validation into server internals. |
| src/Internal/Server/CacheControl/Directives.php | Moves cache-control directives enum into server internals. |
| src/Internal/Server/CacheControl/CacheControlDirective.php | Moves cache-control directive trait into server internals. |
| src/Internal/Request/Uri.php | Removes old internal URI wrapper. |
| src/Internal/Request/QueryParameters.php | Removes old internal query parameter wrapper. |
| src/Internal/Request/DecodedRequest.php | Removes old internal decoded request type. |
| src/Internal/Request/Body.php | Removes old internal request body decoder. |
| src/Internal/Exceptions/SameSiteNoneRequiresSecure.php | Removes old internal cookie invariant exception. |
| src/Internal/Exceptions/NonWritableStream.php | Removes old internal stream exception. |
| src/Internal/Exceptions/NonSeekableStream.php | Removes old internal stream exception. |
| src/Internal/Exceptions/NonReadableStream.php | Removes old internal stream exception. |
| src/Internal/Exceptions/MissingResourceStream.php | Removes old internal stream exception. |
| src/Internal/Exceptions/InvalidResource.php | Removes old internal invalid-resource exception. |
| src/Internal/Exceptions/CookieValueIsInvalid.php | Removes old internal cookie invariant exception. |
| src/Internal/Exceptions/CookieNameIsInvalid.php | Removes old internal cookie invariant exception. |
| src/Internal/Exceptions/ConflictingLifetimeAttributes.php | Removes old internal cookie invariant exception. |
| src/Internal/Client/Url.php | Adds internal URL composition + path validation for client facade. |
| src/Internal/Client/RequestResolver.php | Adds internal request resolver (base URL + defaults merge). |
| src/Internal/Client/Exceptions/PathContainsScheme.php | Adds internal path validation exception. |
| src/Internal/Client/Exceptions/PathContainsControlChars.php | Adds internal path validation exception. |
| src/Internal/Client/Cursor.php | Adds internal cursor for in-memory transport queue. |
| src/HttpBuilder.php | Adds fluent builder for Http. |
| src/Http.php | Adds PSR-18 client facade entrypoint. |
| src/Headers.php | Replaces Headers interface with concrete case-insensitive header collection. |
| src/Headerable.php | Introduces Headerable contributor interface. |
| src/Exceptions/UserAgentProductIsEmpty.php | Adds public exception for invalid user-agent product. |
| src/Exceptions/TransportFailure.php | Adds public interface for transport failures. |
| src/Exceptions/SynthesizedResponseHasNoRaw.php | Adds public exception for synthesized client responses. |
| src/Exceptions/NoMoreResponses.php | Adds public exception for in-memory transport exhaustion. |
| src/Exceptions/MalformedPath.php | Adds public exception for unsafe request path composition. |
| src/Exceptions/HttpRequestInvalid.php | Adds public exception wrapping PSR-18 request exceptions. |
| src/Exceptions/HttpRequestFailed.php | Adds public exception wrapping PSR-18 client exceptions. |
| src/Exceptions/HttpNetworkFailed.php | Adds public exception wrapping PSR-18 network exceptions. |
| src/Exceptions/HttpException.php | Adds shared public exception marker interface. |
| src/Exceptions/HttpConfigurationInvalid.php | Adds public exception for missing builder configuration. |
| src/Cookie.php | Migrates cookie internals and aligns with Headerable. |
| src/ContentType.php | Migrates to Headerable and adjusts header rendering behavior. |
| src/Code.php | Adds instance helpers (isSuccess/isError) and simplifies validation. |
| src/Client/Transports/NetworkTransport.php | Adds PSR-18 transport wrapper translating exceptions. |
| src/Client/Transports/InMemoryTransport.php | Adds deterministic in-memory transport for tests. |
| src/Client/Transport.php | Adds client transport abstraction. |
| src/Client/Response.php | Adds client response wrapper with typed body + raw access. |
| src/Client/Request.php | Adds client request value object with header merge support. |
| src/Charset.php | Minor formatting/template cleanup. |
| src/CacheControl.php | Migrates to Headerable and clarifies directive construction. |
| src/Body.php | Introduces shared typed body decoding for requests/responses. |
| src/Attribute.php | Promotes Attribute to public API for typed scalar/array coercions. |
| SECURITY.md | Adds standard security policy document. |
| phpunit.xml | Tightens PHPUnit failure flags and aligns output paths to reports/. |
| phpstan.neon.dist | Switches to level: max, includes tests, and refines ignores. |
| phpcs.xml | Adds PHPCS ruleset file for PSR-12. |
| Makefile | Aligns targets/scripts with new composer tooling and reports/ paths. |
| infection.json.dist | Aligns Infection output paths to reports/. |
| composer.json | Adds PSR-18 deps, tooling scripts reshape, and metadata keywords. |
| .gitignore | Updates ignore patterns to match tooling outputs and caches. |
| .github/workflows/ci.yml | Aligns CI with canonical job chain + resolved PHP version. |
| .github/PULL_REQUEST_TEMPLATE.md | Adds canonical PR template. |
| .github/ISSUE_TEMPLATE/feature_request.md | Adds canonical feature request template. |
| .github/ISSUE_TEMPLATE/bug_report.md | Adds canonical bug report template. |
| .github/copilot-instructions.md | Updates repo-level Copilot instructions wording. |
| .gitattributes | Adds export-ignore entries (including reports + cache dirs). |
| .editorconfig | Adds max_line_length = 120. |
| .claude/rules/php-library-commits.md | Adds conventional commit guidance. |
| .claude/rules/php-library-architecture.md | Adds canonical architecture rules. |
| .claude/rules/github-workflows.md | Removes old workflow rules file. |
| .claude/CLAUDE.md | Updates CLAUDE entrypoint to reference scoped rule files. |
Comments suppressed due to low confidence (2)
src/Internal/Server/Response/InternalResponse.php:72
- InternalResponse::getHeaderLine() calls $this->getHeader(name: $name) using a named argument, but getHeader() is part of the PSR-7 ResponseInterface contract, which does not standardize parameter names. Prefer positional invocation ($this->getHeader($name)) to avoid coupling to parameter names.
tests/Unit/Client/Transports/NetworkTransportTest.php:209 - This test uses try/catch to assert on exception messages via getMessage(), but the repo's testing rules prohibit try/catch for message assertions. Prefer expectException(HttpRequestFailed::class) plus expectExceptionMessage/expectExceptionMessageMatches, reserving try/catch for asserting custom accessors (url/method/reason/getPrevious).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.