Skip to content

fix: use file_get_contents to prevent large JS files (>8KB) from being truncated#208

Open
G4Zz0L1 wants to merge 3 commits intopestphp:4.xfrom
G4Zz0L1:fix/large-js-file-truncation-readableresourcestream
Open

fix: use file_get_contents to prevent large JS files (>8KB) from being truncated#208
G4Zz0L1 wants to merge 3 commits intopestphp:4.xfrom
G4Zz0L1:fix/large-js-file-truncation-readableresourcestream

Conversation

@G4Zz0L1
Copy link
Copy Markdown

@G4Zz0L1 G4Zz0L1 commented Apr 9, 2026

Problem

When running browser tests, all static JS files larger than 8192 bytes are truncated to exactly 8KB, causing SyntaxError: Unexpected end of input in Playwright. This makes assertNoJavaScriptErrors() fail on any page loading Livewire, Filament, or any Vite bundle.

Evidence

Capturing error details via window.__pestBrowser.jsErrors after waitForLoadState('networkidle'):

[
  { "message": "Uncaught SyntaxError: Unexpected end of input", "filename": ".../livewire.min.js", "lineno": 1, "colno": 8193 },
  { "message": "Uncaught SyntaxError: Unexpected end of input", "filename": ".../support.js",     "lineno": 1, "colno": 8193 },
  { "message": "Uncaught SyntaxError: Invalid or unexpected token", "filename": ".../tables.js", "lineno": 1, "colno": 8189 }
]

colno: 8193 = exactly 8192 bytes received — one AMPHP read chunk.

Root Cause

ReadableResourceStream sets the stream to non-blocking mode (stream_set_blocking($resource, false)). For php://temp in non-blocking mode, AMPHP's fiber event loop only delivers the first 8192-byte chunk and does not schedule further reads within the same request, so files >8KB are silently truncated.

Fix

Replace the fopen/fread/php://temp/ReadableResourceStream pipeline with file_get_contents and a plain string response body. The AMPHP Response accepts a string body and sends it in full, bypassing async read-chunk limitations entirely.

This also supersedes PR #203: the filesize() === 0 guard is no longer needed since file_get_contents on an empty file returns '' rather than crashing.

Fixes pestphp/pest#1664
Also fixes pestphp/pest#1635

ReadableResourceStream sets the php://temp stream to non-blocking mode,
causing AMPHP's fiber event loop to deliver only the first 8192-byte
chunk. Files larger than 8KB are silently truncated, producing
'SyntaxError: Unexpected end of input' in Playwright.

Replacing fopen/fread/php://temp/ReadableResourceStream with a plain
file_get_contents + string response body sends the full file content
in one shot, bypassing the async read-chunk limitation.

This also fixes the 0-byte fread crash from #1635 without needing a
separate filesize() guard.

Fixes pestphp/pest#1664
Copilot AI review requested due to automatic review settings April 9, 2026 10:34
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes truncation of JavaScript assets >8KB when served by the internal Laravel HTTP server during browser tests by switching from an AMPHP resource stream pipeline to a string body response.

Changes:

  • Replace fopen/ReadableResourceStream-based asset serving with file_get_contents() + string response body.
  • Keep JS URL rewriting but apply it directly to the in-memory string.
  • Add explicit Content-Length header based on the final rewritten content.

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

G4Zz0L1 added 2 commits April 9, 2026 12:41
…sets

Address review feedback: only use file_get_contents for .js files where
the php://temp + ReadableResourceStream truncation occurs. Non-JS assets
(images, fonts, CSS) continue to use ReadableResourceStream on the real
file handle, which is not affected by the 8KB chunk issue and is more
memory-efficient for large binary files.

Also adds an explanatory comment on why file_get_contents is used.
Adds a test that creates a JS file slightly above 8192 bytes with a
sentinel string at the end, then asserts the full content is returned.
Without the fix, ReadableResourceStream would truncate the response to
the first 8KB, causing the sentinel assertion to fail.
@G4Zz0L1
Copy link
Copy Markdown
Author

G4Zz0L1 commented Apr 9, 2026

Addressed both review comments:

Memory concern (r3057164015): Fixed — the file_get_contents + string body path is now limited exclusively to .js files. All other assets (images, fonts, CSS, videos, etc.) keep the original ReadableResourceStream path on the real file handle, which is unaffected by the php://temp non-blocking truncation.

Regression test (r3057164073): Added in tests/Unit/Drivers/Laravel/LaravelHttpServerTest.php — creates a JS file slightly over 8192 bytes with a sentinel string at the end and asserts the full response is returned via assertSee('END_SENTINEL'). Without the fix this test would fail because the sentinel falls after the first 8KB chunk.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants