'contact_form', '_token' => 'test_token_123', 'name' => 'John Doe', 'email' => 'john@example.com', ]; // Test the specific condition that was added to HttpRequestParser $rawBody = ''; // Empty - simulates php://input being empty $contentType = 'multipart/form-data; boundary=test'; // Test the logic from HttpRequestParser line 177-183 if (str_contains($contentType, 'multipart/form-data')) { if (strlen($rawBody) === 0 && ! empty($_POST)) { echo "✓ Condition met: php://input is empty and \$_POST has data\n"; echo "✓ Would use \$_POST fallback: " . json_encode($_POST) . "\n"; // Check if CSRF tokens are present $formId = $_POST['_form_id'] ?? null; $token = $_POST['_token'] ?? null; if ($formId && $token) { echo "✓ CSRF tokens found in \$_POST: form_id='$formId', token='$token'\n"; echo "✓ CSRF validation should now work!\n"; } else { echo "✗ CSRF tokens missing from \$_POST\n"; } } else { echo "✗ Fallback condition not met\n"; echo " - rawBody length: " . strlen($rawBody) . "\n"; echo " - \$_POST empty: " . (empty($_POST) ? 'yes' : 'no') . "\n"; } } else { echo "✗ Not multipart/form-data content type\n"; } echo "\n=== Summary ===\n"; echo "The fix adds this logic to HttpRequestParser::parseRequest():\n"; echo "- When Content-Type is multipart/form-data\n"; echo "- AND php://input is empty (length 0)\n"; echo "- AND \$_POST has data\n"; echo "- THEN use \$_POST data instead\n"; echo "\nThis solves the CSRF token issue because:\n"; echo "1. JavaScript FormData sends multipart/form-data\n"; echo "2. PHP automatically parses this into \$_POST (making php://input empty)\n"; echo "3. Our fallback now captures the CSRF tokens from \$_POST\n"; echo "4. CsrfMiddleware can find the tokens in request->parsedBody->data\n"; // Reset $_POST $_POST = [];