Skip to content

Release/2.1.0 - #16

Merged
code-cfernandes merged 14 commits into
mainfrom
release/2.1.0
Jul 15, 2026
Merged

Release/2.1.0#16
code-cfernandes merged 14 commits into
mainfrom
release/2.1.0

Conversation

@code-cfernandes

Copy link
Copy Markdown
Member

Pull Request Template

📋 Descrição

Descreva resumidamente as mudanças feitas neste PR.

🎯 Tipo de Mudança

  • 🐛 Bug fix (mudança que corrige um bug)
  • ✨ Nova feature (mudança que adiciona funcionalidade)
  • 💥 Breaking change (mudança que causa incompatibilidade)
  • 📚 Documentação (mudanças apenas na documentação)
  • 🧹 Refactoring (mudanças que não corrigem bug nem adicionam feature)
  • ⚡ Performance (mudanças que melhoram a performance)
  • 🧪 Testes (adição ou correção de testes)

🧪 Como foi testado?

Descreva os testes que você executou para verificar suas mudanças.

  • Testes unitários passando
  • Testes funcionais passando
  • Testado manualmente
  • Exemplos funcionando

📝 Checklist

  • Meu código segue as diretrizes de estilo do projeto
  • Eu fiz uma auto-revisão do meu código
  • Comentei meu código, especialmente em partes difíceis de entender
  • Fiz mudanças correspondentes na documentação
  • Minhas mudanças não geram novos warnings
  • Adicionei testes que provam que minha correção é eficaz ou que minha feature funciona
  • Testes unitários novos e existentes passam localmente com minhas mudanças

🔗 Issues Relacionadas

Fixes #(número da issue)

📸 Screenshots (se aplicável)

Cole screenshots aqui para demonstrar as mudanças visuais.

📝 Notas Adicionais

Adicione qualquer informação adicional relevante para os reviewers.

…14 events

This bundles the work accumulated for the v2.1.0 pre-release, validated
together via the full CI suite (1067 tests, 0 failures):

Response emission model (bug fix):
- Response::json()/text()/html() no longer auto-emit. Emission now happens
  exactly once, at Application::run() — the only place that's guaranteed
  to run after the full middleware pipeline (including post-handler
  middleware) has finished. Previously, auto-emit inside the handler sent
  bytes before post-handler middleware could act on the response, and
  every request logged a spurious "body already sent" warning from the
  redundant emit() call in run().
- Application::run() now guards its emit() call with $response->isSent().
- set_exception_handler (registered in configureBasicErrorHandling() and
  configureErrorHandling()) previously discarded handleException()'s
  return value, relying entirely on the auto-emit side effect to get the
  error response to the client. Now emits explicitly — otherwise removing
  auto-emit would have silently swallowed error responses for exceptions
  that escape the handle()/run() flow entirely (e.g. bootstrap errors).
- disableAutoEmit() kept as a no-op for backward compatibility with
  existing callers.

v2.1.0 deprecation cycle (docs/technical/DEPRECATION_AND_REMOVAL_PLAN.md,
ITEM-001 through ITEM-008, all completed 2026-05-29):
- Core\Container, Request::getIp(), LoadShedder,
  Performance\RateLimitMiddleware and Str::startsWith/endsWith/contains
  marked @deprecated with trigger_error(E_USER_DEPRECATED), scheduled for
  removal in v3.0.0.
- Providers\Logger replaced by Logging\PsrLogger (PSR-3); Providers\Logger
  kept as a deprecated shim. Dead Logging\Logger/FileHandler/
  LogHandlerInterface removed.
- Providers\EventDispatcher and Providers\ListenerProvider deprecated in
  favor of Events\EventDispatcher (now PSR-14 compliant) and
  Events\ListenerProvider.

Other fixes captured in tasks/ (2026-05-29): Request body-parsing JSON
array fallback, HeaderRequest missing strict_types, Validator required
false-negative on integer zero, Psr7Pool header-iteration mutation on
reset, static cached-input leaking across requests, missing
CustomHeaderCollection import in Request.

Housekeeping: react/http moved from require to suggest (only needed by
the pivotphp/reactphp extension); obsolete v1.1.4 examples removed;
tests/Services/* merged into tests/Http/*; ContainerTestSimple removed
(superseded by ContainerTest).
…ch() task

Confirmed HookManager doesn't touch fire()/listen() at all — it manages its
own prioritized listeners and wires them into ListenerProvider directly via
PSR-14, dispatching through the Hook event class. fire()/listen() remain a
deliberately separate, lightweight string-based mechanism, but had zero test
coverage until now (grep confirmed no production caller either).

- Added tests/Events/EventDispatcherTest.php: covers dispatch() via
  ListenerProviderInterface, fire()/listen() independently, propagation
  stop on false return, and confirms the two listener sets don't leak
  into each other.
- Documented the dispatch(string,array) -> fire(string,array) rename
  (breaking since 2.0.0, no BC alias exists — dispatch() must stay
  PSR-14-only per EventDispatcherInterface) in CHANGELOG.md.
- Marked tasks/2026-05-29-event-dispatcher-*.md acceptance criteria
  resolved/explained.
Psr7Pool::resetServerRequest() received $serverParams but never applied it —
ServerRequestInterface (PSR-7) doesn't define a with*() method for server
params since they're meant to be read-only, so there was no way to reset
them on a reused instance. Headers were already being cleared/reapplied
correctly (previous fix), but server params (REMOTE_ADDR, HTTPS, auth data
injected by the SAPI, etc.) from the previous request could leak into the
next one served by the same pooled object — a real data-leak risk in
concurrent/pooled environments (Swoole, ReactPHP, FrankenPHP).

Added ServerRequest::withServerParams() (practical extension beyond the
formal PSR-7 interface, same pattern as the existing withCookieParams())
and wired it into resetServerRequest(). Covered by
Psr7PoolTest::testResetServerRequestDoesNotLeakHeadersOrServerParamsBetweenReuses().

Also updated docs/technical/INCONSISTENCIES_REPORT.md: marked C-01
(dual IoC containers) as resolved via the v2.1.0 deprecation cycle, C-02
(getCachedInput bypass in parseBody) as already fixed, and C-03 (this fix)
as resolved.
Audited all 6 high-impact items against current code:
- A-01 (three incompatible rate limiters): resolved — RateLimitMiddleware
  and LoadShedder already @deprecated v2.1.0 in favor of RateLimiter.
- A-02 (MiddlewareStack::warmupCommonPipelines() calling undefined
  Response::setHeader()): resolved — the method no longer exists.
- A-03 (Request::ip() vs getIp(), the latter unvalidated and spoofable via
  X-Forwarded-For): resolved — getIp() is deprecated and now delegates to
  ip(), inheriting its validation.
- A-04 (ApiDocumentationMiddleware instantiating the Express.js Response
  instead of a PSR-7 type): resolved — it uses Psr7Response directly now.
- A-05 (Validator::FILTER_VALIDATE_INT truthiness bug rejecting 0):
  resolved — already uses strict `=== false` comparison.
- A-06 (MiddlewareStack's 5 static properties unsafe under async servers):
  was undocumented — added an explicit warning to the class docblock
  covering which properties are affected, the practical impact under
  Swoole/ReactPHP/FrankenPHP, and that clearCache() must be called
  explicitly between requests in those environments. The recommendation
  only asked for documentation, not a refactor to instance state — that's
  a larger architectural change out of scope here.

Every item's report entry now has a Status note recording what was
verified and why. No behavior changes except A-06's docblock addition.
…r closure (B-04, B-05)

B-05: handleException() hardcoded `$statusCode === 404 ? 'Not Found' :
'Internal Server Error'` for the production (non-debug) error message —
any status other than 404 (403, 401, 400, 405, 503...) always showed
"Internal Server Error", discarding the exception's real HTTP semantics.
Response::error() already had a proper status->message map, just not
reusable. Extracted it into Response::defaultErrorMessage(), used by both
error() and Application::handleException(). Covered by
testExceptionHandlingProductionModeUsesStatusSpecificMessage() — it would
have failed against the old hardcoded ternary.

B-04: configureBasicErrorHandling() and configureErrorHandling() each
registered set_exception_handler() with an identical anonymous closure
(handleException() + isSent()-guarded emit()). Extracted into a public
handleUncaughtException() method, now unit-testable in isolation
(testHandleUncaughtExceptionEmitsErrorResponse()) instead of being an
anonymous closure nobody could exercise directly.

Also updated docs/technical/INCONSISTENCIES_REPORT.md: A-06 and A-07
were already fully resolved in earlier commits on this branch but the
status table still said "Pendente Sprint 4" / "Parcialmente Corrigido" —
corrected both entries, plus B-04 and B-05.
…-07)

ExtensionManager held a hard dependency on Application, but never actually
calls any Application-specific method — it only stores the reference and
passes it along to registered extension callbacks/classes, which are the
ones that use the real API surface.

Added Core\ApplicationInterface as a marker interface (intentionally
empty: there's no concrete method ExtensionManager itself needs) and made
Application implement it. ExtensionManager now type-hints against the
interface instead of the 1000+ line concrete class. Runtime behavior is
unchanged — the object passed to extensions is still the real Application
instance with its full API, only the declared type inside ExtensionManager
changed.
Http\Pool\PoolManager (instance-based, generic rent()/return()/borrow() on
arbitrary named pools) and Http\Psr7\Pool\PoolManager (100% static,
coordinates the specific PSR-7 pools: ResponsePool/HeaderPool/
OperationsCache) share a class name but not a purpose or a shape.

Full unification behind a single PoolManagerInterface — the report's
original recommendation — turns out to not be viable without rewriting
one of the two from scratch: PHP interfaces only cover instance methods,
and Http\Psr7\Pool\PoolManager has none (everything is static). Confirmed
neither class is actually used on the framework's hot path today (real
pooling goes through HttpPoolFacade/Psr7Pool), so a disproportionate
rewrite isn't justified just to satisfy an interface.

Documented the distinction explicitly in both classes' docblocks instead —
that removes the actual reported impact ("ambiguity in which pool to
use"). Full architectural unification is out of scope.
EventDispatcher and Logger were already moved out of Providers/ to
Events/ and Logging/ respectively, with deprecated shims left behind
(prior commits on this branch). Container is the one item from M-06
still in Providers/ — but it's the active, canonical container
(Application uses it exclusively; Core\Container is the deprecated one,
per C-01). Moving it now would be a third identity change for the same
class in a short span (Core\Container -> Providers\Container -> yet
another namespace) with no real benefit to consumers. Documented that
reasoning directly in the class docblock instead of moving it.
Multi-line addListener()/listen() calls didn't follow the project's
one-argument-per-line convention for multi-line calls. Caught by
composer cs:check while doing a final quality pass on this branch.
@code-cfernandes code-cfernandes self-assigned this Jul 15, 2026
Copilot AI review requested due to automatic review settings July 15, 2026 19:08
@code-cfernandes code-cfernandes added bug Something isn't working duplicate This issue or pull request already exists labels Jul 15, 2026
@github-actions

Copy link
Copy Markdown

🚀 Release Readiness Report - PivotPHP Core v2.0.0

✅ All Checks Passed!

  • Version: 2.0.0
  • PHPStan: Level 9, 0 errors
  • Tests: All tests passing
  • Code Style: PSR-12 compliant
  • PHP Compatibility: 8.1 - 8.4
  • Dependencies: All valid
  • Scripts: Consolidated and optimized

📦 Ready for Publication

The project is ready to be tagged and released!

Next Steps:

  1. Create a new tag: git tag -a v2.0.0 -m 'Release v2.0.0'
  2. Push the tag: git push origin v2.0.0
  3. The release workflow will automatically create a GitHub release
  4. Packagist will be automatically updated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 prepares the codebase for the “Release/2.1.0” line by tightening type-safety (strict_types=1), introducing/standardizing new PSR-aligned components (PSR-3 logger, PSR-14 events), improving request/response pooling correctness, and updating the test suite to reflect new deprecations and emission behavior.

Changes:

  • Add strict_types=1 across multiple core/util/http files and refine type handling in validator/utils/helpers.
  • Introduce PSR-3 Logging\PsrLogger, migrate service providers to prefer new Events\* and Logging\* components, and deprecate legacy provider classes.
  • Improve PSR-7 pooling safety (resetting headers/serverParams), remove Response auto-emit behavior, and update/relocate HTTP-related tests accordingly.

Reviewed changes

Copilot reviewed 83 out of 83 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/Support/StrTest.php Suppresses deprecation notices from deprecated Str methods under test.
tests/Support/HttpTestCase.php Adds a base HTTP test case that resets superglobals between tests.
tests/Services/ResponseTest.php Removes old service-layer response tests (moved to HTTP tests).
tests/Services/RequestTest.php Removes old service-layer request tests (moved to HTTP tests).
tests/Services/HeaderRequestTest.php Removes old service-layer header tests (moved to HTTP tests).
tests/Middleware/SimpleLoadShedderTest.php Suppresses deprecation notices for deprecated LoadShedder tests.
tests/Middleware/RateLimiterTest.php Ensures Response is in explicit test mode to avoid output side effects.
tests/Middleware/Performance/RateLimitMiddlewareTest.php Suppresses deprecation notices for deprecated rate-limit middleware tests.
tests/Middleware/Core/BaseMiddlewareTest.php Ensures Response is in explicit test mode to avoid output side effects.
tests/Http/ResponseTest.php Expands HTTP response tests and centralizes setup with explicit test mode.
tests/Http/RequestTest.php Expands HTTP request tests and adds superglobal resets for isolation.
tests/Http/Pool/Psr7PoolTest.php Adds regression test to prevent header/serverParams leakage across pooled request reuse.
tests/Http/HeaderRequestTest.php Expands header handling tests and manages $_SERVER fixtures in setup/teardown.
tests/Events/EventDispatcherTest.php Adds coverage for PSR-14 dispatch and the separate string-based fire()/listen() path.
tests/Core/RateLimitMiddlewareTestPsr15.php Updates import path for the deprecated PSR-15 rate-limit middleware class.
tests/Core/ContainerTestSimple.php Removes simplified/debug-only container test.
tests/Core/ContainerTest.php Suppresses deprecation notices when testing deprecated Core\Container.
tests/Core/ApplicationTest.php Adds tests for status-specific production error messages and uncaught-exception emission.
tasks/2026-05-29-validator-required-false-negative-integer-zero.md Adds task note about required falsy handling pitfalls.
tasks/2026-05-29-static-cached-input-isolacao-entre-requests.md Adds task note about request body cache isolation across requests/processes.
tasks/2026-05-29-request-missing-use-import-custom-header-collection.md Adds task note about missing import for CustomHeaderCollection.
tasks/2026-05-29-psrlogger-nome-hardcoded-express-php-log.md Adds task note about legacy “express-php” naming in logger defaults.
tasks/2026-05-29-psr7pool-reset-header-iteration-mutacao.md Adds task note about safe header reset patterns in pooled PSR-7 objects.
tasks/2026-05-29-parsebody-logic-bug-json-array-fallback.md Adds task note about JSON array handling consistency in request parsing.
tasks/2026-05-29-headerrequest-falta-strict-types.md Adds task note about strict types and typing expectations for HeaderRequest.
tasks/2026-05-29-event-dispatcher-fire-renomeacao-sem-retrocompatibilidade.md Adds task note documenting the dispatch/fire breaking change and mitigation via docs.
src/Validation/Validator.php Enables strict types and refines required/integer checks and message casting.
src/Utils/Utils.php Enables strict types and routes casing helpers through Support\Str.
src/Utils/Arr.php Enables strict types and adds union typing to only/except.
src/Support/Str.php Enables strict types and deprecates string helpers in favor of PHP natives.
src/Support/HookManager.php Updates docblock type annotation to the new Events\ListenerProvider.
src/Providers/LoggingServiceProvider.php Switches PSR logger binding to Logging\PsrLogger.
src/Providers/Logger.php Marks legacy provider logger as deprecated and emits deprecation warning.
src/Providers/ListenerProvider.php Marks legacy provider listener provider as deprecated and emits deprecation warning.
src/Providers/ExtensionManager.php Types extensions against new ApplicationInterface and avoids registering non-callables.
src/Providers/EventServiceProvider.php Binds PSR-14 interfaces to new Events\ListenerProvider and Events\EventDispatcher.
src/Providers/EventDispatcher.php Marks legacy provider event dispatcher as deprecated and emits deprecation warning.
src/Providers/Container.php Adds architectural note explaining why Container remains in Providers namespace.
src/Middleware/RateLimiter.php Updates default key generator to use Request::ip() (not deprecated getIp()).
src/Middleware/Performance/RateLimitMiddleware.php Marks as deprecated, emits deprecation warning, and hardens JSON body writing.
src/Middleware/MiddlewareStack.php Enables strict types, adds warning doc for async runtimes, and removes warmup method.
src/Middleware/LoadShedder.php Marks as deprecated, emits deprecation warning, and modernizes body creation.
src/Middleware/Http/ApiDocumentationMiddleware.php Switches to PSR-7 response/stream types and clarifies OpenAPI generation behavior.
src/Logging/PsrLogger.php Adds new PSR-3 logger implementation with file writing + fallback behavior.
src/Logging/LogHandlerInterface.php Removes legacy log handler interface.
src/Logging/Logger.php Removes legacy logging system.
src/Logging/FileHandler.php Removes legacy file handler.
src/Http/Response.php Enables strict types, removes auto test-environment detection and auto-emit, centralizes default error messages.
src/Http/Request.php Enables strict types, switches cached input to per-instance, introduces CustomHeaderCollection, improves PSR-7 parsedBody typing, deprecates getIp().
src/Http/Psr7/ServerRequest.php Adds withServerParams() for pooling resets and tightens URI path fallback.
src/Http/Psr7/Pool/PoolManager.php Clarifies purpose and distinguishes from generic pool manager.
src/Http/Psr7/Adapters/HeaderPoolAdapter.php Fixes non-array header value handling to use string casting.
src/Http/Pool/Psr7Pool.php Clears headers before reuse and resets serverParams; guards stream truncation during pooling.
src/Http/Pool/PoolManager.php Adds doc clarifying difference vs PSR-7 static pool manager.
src/Http/HeaderRequest.php Enables strict types and centralizes header camel-case conversion helper.
src/Http/CustomHeaderCollection.php Adds a concrete header collection for overriding headers in tests/custom requests.
src/Http/Adapters/Psr7PoolAdapter.php Ensures cookies param is always an array when calling Psr7Pool.
src/Http/Adapters/GlobalsToServerRequestAdapter.php Tightens URI path fallback and hardens uploaded file stream creation.
src/Events/ListenerProvider.php Adds new PSR-14 listener provider implementation.
src/Events/EventDispatcher.php Adds PSR-14 dispatcher implementation plus separate string-based event mechanism.
src/Core/Container.php Enables strict types, deprecates Core container, and hardens reflection type checks.
src/Core/ApplicationInterface.php Adds marker interface to decouple extensions from concrete Application type.
src/Core/Application.php Enables strict types, emits uncaught exceptions properly, improves middleware wrapping, and uses status-specific production messages.
src/aliases.php Guards class_alias calls with class_exists() for optional dependencies.
src/aliases-performance-tools.php Moves strict_types declaration to the proper location for the file.
README.md Updates version references, clarifies OpenAPI middleware behavior, fixes script paths.
phpstan.neon Replaces broad ignore patterns with narrower/specific ignores.
examples/README.md Updates examples documentation to v2.0.0 messaging and removes obsolete references.
examples/09-error-handling/enhanced-errors-v114.php Removes obsolete legacy example.
examples/07-advanced/performance-v1.1.3.php Removes obsolete legacy example.
examples/07-advanced/array-callables-v114.php Removes obsolete legacy example.
examples/05-performance/high-performance.php Updates performance example messaging to v2.0.0+.
examples/01-basics/hello-world.php Updates hello-world example messaging and labels to v2.0.0+.
composer.json Removes react/http from hard requirements; documents it under suggest for async integration.
CLAUDE.md Updates example commands and architecture notes to match current structure and deprecations.
CHANGELOG.md Documents the clarified dispatch() vs fire() breaking change context in Unreleased.
Comments suppressed due to low confidence (2)

src/Utils/Arr.php:169

  • Arr::only() currently breaks when $keys is passed as a string: it assigns func_get_args()[1] (still a string) and then immediately returns [] because $keys is not an array. Since the signature explicitly allows array|string, string input should be handled.
    src/Utils/Arr.php:195
  • Arr::except() currently treats a string $keys as "no keys" and returns the full array, because $keys stays a string and the method returns early when !is_array($keys). With the array|string signature, string input should be converted to a one-element array.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Http/CustomHeaderCollection.php
Comment thread src/Logging/PsrLogger.php Outdated
Comment thread src/Middleware/LoadShedder.php
Comment thread src/Http/Pool/Psr7Pool.php Outdated
Comment thread tests/Support/HttpTestCase.php
code-cfernandes and others added 3 commits July 15, 2026 16:14
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown

🚀 Release Readiness Report - PivotPHP Core v2.0.0

✅ All Checks Passed!

  • Version: 2.0.0
  • PHPStan: Level 9, 0 errors
  • Tests: All tests passing
  • Code Style: PSR-12 compliant
  • PHP Compatibility: 8.1 - 8.4
  • Dependencies: All valid
  • Scripts: Consolidated and optimized

📦 Ready for Publication

The project is ready to be tagged and released!

Next Steps:

  1. Create a new tag: git tag -a v2.0.0 -m 'Release v2.0.0'
  2. Push the tag: git push origin v2.0.0
  3. The release workflow will automatically create a GitHub release
  4. Packagist will be automatically updated

2 similar comments
@github-actions

Copy link
Copy Markdown

🚀 Release Readiness Report - PivotPHP Core v2.0.0

✅ All Checks Passed!

  • Version: 2.0.0
  • PHPStan: Level 9, 0 errors
  • Tests: All tests passing
  • Code Style: PSR-12 compliant
  • PHP Compatibility: 8.1 - 8.4
  • Dependencies: All valid
  • Scripts: Consolidated and optimized

📦 Ready for Publication

The project is ready to be tagged and released!

Next Steps:

  1. Create a new tag: git tag -a v2.0.0 -m 'Release v2.0.0'
  2. Push the tag: git push origin v2.0.0
  3. The release workflow will automatically create a GitHub release
  4. Packagist will be automatically updated

@github-actions

Copy link
Copy Markdown

🚀 Release Readiness Report - PivotPHP Core v2.0.0

✅ All Checks Passed!

  • Version: 2.0.0
  • PHPStan: Level 9, 0 errors
  • Tests: All tests passing
  • Code Style: PSR-12 compliant
  • PHP Compatibility: 8.1 - 8.4
  • Dependencies: All valid
  • Scripts: Consolidated and optimized

📦 Ready for Publication

The project is ready to be tagged and released!

Next Steps:

  1. Create a new tag: git tag -a v2.0.0 -m 'Release v2.0.0'
  2. Push the tag: git push origin v2.0.0
  3. The release workflow will automatically create a GitHub release
  4. Packagist will be automatically updated

…succeeded

Flagged by Copilot code review. The environment-header fallback checked
getallheaders() only to decide whether to *skip* merging — the merge loop
lived inside `if (empty($existingHeaders))`, so it only ever ran when
getallheaders() returned nothing. Whenever it returned real headers (the
normal case under Apache/PHP-FPM), they were fetched and then silently
discarded: Request::setHeaders() would end up with only the explicit
custom overrides, dropping Authorization, Cookie, and everything else
from the actual request.

Extracted the merge logic (skip keys already set by an explicit override)
into mergeMissingHeaders(), now called unconditionally with whichever
source has data — getallheaders() when non-empty, otherwise the parsed
$_SERVER['HTTP_*'] fallback (extracted into parseServerHeaders()). There's
now a single merge code path instead of two copies where only one worked,
so the bug can't be silently reintroduced by editing just one branch.

getallheaders() can't be meaningfully mocked in tests: CustomHeaderCollection
guards the call with function_exists('getallheaders'), which always resolves
against the global function regardless of any same-namespace override, and
the CLI SAPI PHPUnit runs under doesn't define it at all. Added
tests/Http/CustomHeaderCollectionTest.php covering mergeMissingHeaders()
directly via reflection (the exact logic that was broken) plus the real,
exercisable $_SERVER fallback path and override precedence.
@github-actions

Copy link
Copy Markdown

🚀 Release Readiness Report - PivotPHP Core v2.0.0

✅ All Checks Passed!

  • Version: 2.0.0
  • PHPStan: Level 9, 0 errors
  • Tests: All tests passing
  • Code Style: PSR-12 compliant
  • PHP Compatibility: 8.1 - 8.4
  • Dependencies: All valid
  • Scripts: Consolidated and optimized

📦 Ready for Publication

The project is ready to be tagged and released!

Next Steps:

  1. Create a new tag: git tag -a v2.0.0 -m 'Release v2.0.0'
  2. Push the tag: git push origin v2.0.0
  3. The release workflow will automatically create a GitHub release
  4. Packagist will be automatically updated

…Stream)

Flagged by Copilot code review. truncate() isn't part of StreamInterface
(PSR-7) — resetStream() guarded the call with method_exists() and, when
absent, fell through to write() anyway. write() only overwrites the bytes
matching the new content's length; if the new content is shorter than
whatever the reused stream held from its previous request/response,
the leftover trailing bytes stay in the stream. Same class of bug as the
serverParams pool leak (C-03) — data from one request leaking into the
next via a pooled object, just at the stream/body level this time.

The framework's own Stream implementation always has truncate(), so this
was latent (the pool never actually saw a stream without it) but real for
any other StreamInterface implementation the pool might be handed. Now
resetStream() only reuses the stream when both isWritable() and
method_exists(..., 'truncate') hold; otherwise it falls back to a fresh
Stream::createFromString($content), same as the non-writable case already
did.

Added WritableSeekableStreamWithoutTruncate to Psr7PoolTest — a minimal
StreamInterface implementation that's writable/seekable but deliberately
has no truncate(). Verified the new test fails without the fix (residual
bytes leak: 'Hi' + pool return('Original long content here') produced
'Hiiginal long content here') and passes with it.
@github-actions

Copy link
Copy Markdown

🚀 Release Readiness Report - PivotPHP Core v2.0.0

✅ All Checks Passed!

  • Version: 2.0.0
  • PHPStan: Level 9, 0 errors
  • Tests: All tests passing
  • Code Style: PSR-12 compliant
  • PHP Compatibility: 8.1 - 8.4
  • Dependencies: All valid
  • Scripts: Consolidated and optimized

📦 Ready for Publication

The project is ready to be tagged and released!

Next Steps:

  1. Create a new tag: git tag -a v2.0.0 -m 'Release v2.0.0'
  2. Push the tag: git push origin v2.0.0
  3. The release workflow will automatically create a GitHub release
  4. Packagist will be automatically updated

@code-cfernandes
code-cfernandes merged commit e3204a3 into main Jul 15, 2026
6 checks passed
@code-cfernandes
code-cfernandes deleted the release/2.1.0 branch July 15, 2026 19:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working duplicate This issue or pull request already exists

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants