Skip to content

Fix: Return empty content for multipart requests to match PHP-FPM behavior#119

Merged
s2x merged 1 commit intomasterfrom
fix/multipart-empty-content
Apr 12, 2026
Merged

Fix: Return empty content for multipart requests to match PHP-FPM behavior#119
s2x merged 1 commit intomasterfrom
fix/multipart-empty-content

Conversation

@s2x
Copy link
Copy Markdown
Collaborator

@s2x s2x commented Apr 12, 2026

Summary

For multipart/form-data requests, getContent() now returns empty string to match PHP-FPM behavior where php://input is not available.

Changes

  • Modified RequestConverter::toSymfonyRequest() to pass empty content for multipart requests
  • Improved content-type detection using stripos() instead of preg_match()
  • Added 3 new tests for content behavior across different content types

Benefits

  • Prevents duplicate memory usage (file data was stored twice in memory)
  • Prevents data exposure via logging getContent()
  • Consistent behavior with PHP-FPM

Testing

  • All 25 RequestConverter tests pass
  • Tests cover multipart, JSON, and form-urlencoded content types

Closes #68

@s2x s2x force-pushed the fix/multipart-empty-content branch from 09a91c0 to 6c1e0dc Compare April 12, 2026 10:07
// JSON and other content types should leave POST bag empty (like PHP-FPM)
$contentType = $rawRequest->header('content-type', '');
$isFormData = preg_match('/^(application\/x-www-form-urlencoded|multipart\/form-data)\b/i', (string) $contentType);
$isFormUrlEncoded = stripos((string) $contentType, 'application/x-www-form-urlencoded') !== false;
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[Suggestion] stripos() matches the substring anywhere in the string, whereas the original preg_match used ^ (start anchor) and \b (word boundary). In practice false positives are extremely unlikely, but this is a behavioral regression from stricter to looser matching.

Consider using str_starts_with() to preserve the original anchored behavior:

Suggested change
$isFormUrlEncoded = stripos((string) $contentType, 'application/x-www-form-urlencoded') !== false;
$ct = strtolower((string) $rawRequest->header('content-type', ''));
$isFormUrlEncoded = str_starts_with($ct, 'application/x-www-form-urlencoded');
$isMultipart = str_starts_with($ct, 'multipart/form-data');

— coder-model via Qwen Code /review


// For multipart requests, pass empty content to match PHP-FPM behavior
// (php://input is not available for multipart - body is consumed during $_POST/$_FILES parsing)
$content = $isMultipart ? '' : $rawRequest->rawBody();
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[Suggestion] Before this change, getContent() on a multipart request returned the full raw body. Now it returns ''. Any application code, middleware, or custom parser reading raw multipart data will silently break.

The behavior is correct for PHP-FPM compatibility, but this should be documented as a breaking change in CHANGELOG.md with a migration note so existing users are aware.

— coder-model via Qwen Code /review

…avior

For multipart/form-data requests, getContent() now returns empty string to match
PHP-FPM behavior where php://input is not available. This prevents:
- Duplicate memory usage (file data stored twice)
- Data exposure via getContent() logging
- Behavioral inconsistency with PHP-FPM

Also improves content-type detection using stripos() instead of preg_match().
@s2x s2x force-pushed the fix/multipart-empty-content branch from 6c1e0dc to 750544f Compare April 12, 2026 10:45
@s2x s2x merged commit 167b8c0 into master Apr 12, 2026
21 checks passed
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.

RequestConverter — Multipart form-data raw body exposed via getContent(), inconsistent with PHP-FPM

1 participant