Request::fromGlobals now accepts $_POST and chooses how to fill body()
based on Content-Type: JSON via json_decode, urlencoded via $_POST
(POST) or parse_str (PUT/PATCH/...), multipart via $_POST. Anything
else, or empty Content-Type, yields []. Charset suffix on JSON is
honoured.
Body parse failures throw HttpException(400) directly from fromGlobals
("Invalid JSON body" / "JSON body must be an object or array") so the
controller never sees garbage. Top-level JSON list ([1,2,3]) is
accepted as a valid body.
HttpApplication moves Request::fromGlobals into dispatch's try block
so the new HttpException maps to a 400 response uniformly. OPTIONS
preflight reads the method directly from $server and skips body
parsing entirely. The opportunistic catch (\JsonException) in dispatch
is gone — controller-level json_decode errors now correctly produce
500 instead of being mislabelled as "Invalid JSON body".
Adds Request::input(key, default) for per-field access alongside the
existing body() that returns the full array.
Tests: tests/Http/RequestTest.php (14 cases) covering JSON
object/list/scalar/null/malformed/charset/empty, urlencoded POST/PUT,
multipart, missing Content-Type, GET, and input() with/without default.
Suite: 34 tests, 55 assertions, all green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>