feat(http): introduce HTTP kernel with Request/Response objects
Replaces the PSR-7 request with symfony/http-foundation and adds a real HTTP
kernel, so the application can run in a long-running runtime without leaking
state between requests. Covers stages 0-3a of the migration plan plus stage 2c
for the downloads and library modules.
Request (stage 1):
- Johncms\Http\Request extends HttpFoundation Request with nine explicit
accessors (body/bodyInt/bodyList/bodyInts/hasBody/queryParam/queryInt/
queryInts/isPost); filterVar() is gone.
- RequestFactory configures trusted proxies, headers and hosts. Default is to
trust no proxy: a non-empty default is both unsafe behind docker port
publishing and impossible to override, because configs merge recursively.
- UploadedFileDTO/Mapper remove the HTTP type from Application and Domain.
- TrimStringsMiddleware preserves the historical always-trim behaviour.
- Johncms\System\Http\* is removed; guzzlehttp/psr7 is dropped.
Response and control flow (stage 2):
- redirect(), pageNotFound() and checkRedirect() throw instead of calling exit;
ExceptionResponseFactory turns those into responses. MethodNotAllowedException
replaces a 405 body that used to be served with status 200.
- ResponseNormalizer accepts Response|string|null, so controllers migrate one at
a time. Render no longer swallows template errors into the page body.
- downloads and library no longer emit exit/header()/http_response_code():
DownloadArticleController streams with its headers preserved 1:1, and each
module error renderer moved to Response together with its controllers.
Kernel (stage 3a):
- Johncms\Http\Kernel implements HttpKernelInterface and maps redirect, 404,
405, 400 and 500 centrally. public/index.php is down to 38 lines.
- Request became synthetic so the kernel can publish the current one.
- New tests/Functional suite boots the app once and drives real requests.
Static analysis gate (stage 1.5):
- PHPStan level 5 replaces psalm, which excluded 20 of 21 modules, and runs in
verify.sh between cs-check and test.
Fixes found while doing the above:
- Files could not be downloaded at all and screenshot upload was dead: both
compared queryParam() to null, which is never true. Same defect hit album and
admin more quietly. PHPStan had reported all four as notIdentical.alwaysTrue,
but the stage 1.5 baseline had accepted them.
- Album validation errors fataled on a readonly property redeclaration, JSON
posts to news comments fataled on getContent(), and the 10.x installer could
not run at all because stage 1d never reached public/install/.
- Rating::addVote() redirected to an unvalidated Referer; it now keeps only the
path and query, and rejects paths starting with // or /\.
BREAKING: sessions are unaffected here, but Johncms\System\Http\* is gone and
installations behind a proxy must now set http.trusted_proxies explicitly, or
getIp() returns the proxy address. A cached container dump must be deleted.
kernel, so the application can run in a long-running runtime without leaking
state between requests. Covers stages 0-3a of the migration plan plus stage 2c
for the downloads and library modules.
Request (stage 1):
- Johncms\Http\Request extends HttpFoundation Request with nine explicit
accessors (body/bodyInt/bodyList/bodyInts/hasBody/queryParam/queryInt/
queryInts/isPost); filterVar() is gone.
- RequestFactory configures trusted proxies, headers and hosts. Default is to
trust no proxy: a non-empty default is both unsafe behind docker port
publishing and impossible to override, because configs merge recursively.
- UploadedFileDTO/Mapper remove the HTTP type from Application and Domain.
- TrimStringsMiddleware preserves the historical always-trim behaviour.
- Johncms\System\Http\* is removed; guzzlehttp/psr7 is dropped.
Response and control flow (stage 2):
- redirect(), pageNotFound() and checkRedirect() throw instead of calling exit;
ExceptionResponseFactory turns those into responses. MethodNotAllowedException
replaces a 405 body that used to be served with status 200.
- ResponseNormalizer accepts Response|string|null, so controllers migrate one at
a time. Render no longer swallows template errors into the page body.
- downloads and library no longer emit exit/header()/http_response_code():
DownloadArticleController streams with its headers preserved 1:1, and each
module error renderer moved to Response together with its controllers.
Kernel (stage 3a):
- Johncms\Http\Kernel implements HttpKernelInterface and maps redirect, 404,
405, 400 and 500 centrally. public/index.php is down to 38 lines.
- Request became synthetic so the kernel can publish the current one.
- New tests/Functional suite boots the app once and drives real requests.
Static analysis gate (stage 1.5):
- PHPStan level 5 replaces psalm, which excluded 20 of 21 modules, and runs in
verify.sh between cs-check and test.
Fixes found while doing the above:
- Files could not be downloaded at all and screenshot upload was dead: both
compared queryParam() to null, which is never true. Same defect hit album and
admin more quietly. PHPStan had reported all four as notIdentical.alwaysTrue,
but the stage 1.5 baseline had accepted them.
- Album validation errors fataled on a readonly property redeclaration, JSON
posts to news comments fataled on getContent(), and the 10.x installer could
not run at all because stage 1d never reached public/install/.
- Rating::addVote() redirected to an unvalidated Referer; it now keeps only the
path and query, and rejects paths starting with // or /\.
BREAKING: sessions are unaffected here, but Johncms\System\Http\* is gone and
installations behind a proxy must now set http.trusted_proxies explicitly, or
getIp() returns the proxy address. A cached container dump must be deleted.