Skip to content

Reorganize namespaces and expose decoded server primitives.#56

Merged
gustavofreze merged 27 commits into
mainfrom
feature/develop
May 16, 2026
Merged

Reorganize namespaces and expose decoded server primitives.#56
gustavofreze merged 27 commits into
mainfrom
feature/develop

Conversation

@gustavofreze
Copy link
Copy Markdown
Member

No description provided.

gustavofreze and others added 26 commits May 14, 2026 22:52
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.
Copilot AI review requested due to automatic review settings May 16, 2026 12:14
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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, client Request/Response, Transport implementations) plus new transport-focused exception types.
  • Replace the old Headers interface with a concrete Headers value object and introduce Headerable for 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).

Comment thread src/Internal/Server/Stream/Stream.php
Comment thread src/Internal/Server/Stream/Stream.php
Comment thread src/Client/Transports/NetworkTransport.php
Comment thread src/Client/Transports/NetworkTransport.php
Comment thread composer.json
Comment thread tests/Unit/HeadersTest.php
Comment thread tests/Unit/Client/Transports/InMemoryTransportTest.php
Comment thread tests/Unit/Client/Transports/NetworkTransportTest.php
Comment thread tests/Unit/Client/Transports/NetworkTransportTest.php
@gustavofreze gustavofreze merged commit 5fcaf2b into main May 16, 2026
6 checks passed
@gustavofreze gustavofreze deleted the feature/develop branch May 16, 2026 16:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants