-
Notifications
You must be signed in to change notification settings - Fork 8k
Fix stream filter seeking resetting write filter state on read seeks #21587
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
nicolas-grekas
wants to merge
1
commit into
php:master
Choose a base branch
from
nicolas-grekas:fix-write-filter
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+102
−52
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,55 +1,51 @@ | ||
| --TEST-- | ||
| bzip2.compress filter with seek to start | ||
| bzip2.compress write filter is not reset on seek | ||
| --EXTENSIONS-- | ||
| bz2 | ||
| --FILE-- | ||
| <?php | ||
| /* Write filters are not reset on stream seek; seeking only affects the | ||
| * stream's read/write position, not the filter pipeline state. */ | ||
|
|
||
| $file = __DIR__ . '/bz2_filter_seek_compress.bz2'; | ||
|
|
||
| $text1 = 'Short text.'; | ||
| $text2 = 'This is a much longer text that will completely overwrite the previous compressed data in the file.'; | ||
| $text = 'Hello, World!'; | ||
|
|
||
| $fp = fopen($file, 'w+'); | ||
| stream_filter_append($fp, 'bzip2.compress', STREAM_FILTER_WRITE); | ||
| $filter = stream_filter_append($fp, 'bzip2.compress', STREAM_FILTER_WRITE); | ||
|
|
||
| fwrite($fp, $text); | ||
|
|
||
| fwrite($fp, $text1); | ||
| fflush($fp); | ||
| /* Remove the filter to finalize compression cleanly before seeking */ | ||
| stream_filter_remove($filter); | ||
|
|
||
| $size1 = ftell($fp); | ||
| echo "Size after first write: $size1\n"; | ||
| $size = ftell($fp); | ||
| echo "Size after write: $size\n"; | ||
|
|
||
| /* Seek to start succeeds; write filters no longer block seeking */ | ||
| $result = fseek($fp, 0, SEEK_SET); | ||
| echo "Seek to start: " . ($result === 0 ? "SUCCESS" : "FAILURE") . "\n"; | ||
|
|
||
| fwrite($fp, $text2); | ||
| fflush($fp); | ||
|
|
||
| $size2 = ftell($fp); | ||
| echo "Size after second write: $size2\n"; | ||
| echo "Second write is larger: " . ($size2 > $size1 ? "YES" : "NO") . "\n"; | ||
|
|
||
| /* Seek to middle also succeeds */ | ||
| $result = fseek($fp, 50, SEEK_SET); | ||
| echo "Seek to middle: " . ($result === 0 ? "SUCCESS" : "FAILURE") . "\n"; | ||
|
|
||
| fclose($fp); | ||
|
|
||
| /* Verify the compressed output is still valid */ | ||
| $fp = fopen($file, 'r'); | ||
| stream_filter_append($fp, 'bzip2.decompress', STREAM_FILTER_READ); | ||
| $content = stream_get_contents($fp); | ||
| fclose($fp); | ||
|
|
||
| echo "Decompressed content matches text2: " . ($content === $text2 ? "YES" : "NO") . "\n"; | ||
| echo "Decompressed content matches: " . ($content === $text ? "YES" : "NO") . "\n"; | ||
| ?> | ||
| --CLEAN-- | ||
| <?php | ||
| @unlink(__DIR__ . '/bz2_filter_seek_compress.bz2'); | ||
| ?> | ||
| --EXPECTF-- | ||
| Size after first write: 40 | ||
| Size after write: %d | ||
| Seek to start: SUCCESS | ||
| Size after second write: 98 | ||
| Second write is larger: YES | ||
|
|
||
| Warning: fseek(): Stream filter bzip2.compress is seekable only to start position in %s on line %d | ||
| Seek to middle: FAILURE | ||
| Decompressed content matches text2: YES | ||
| Seek to middle: SUCCESS | ||
| Decompressed content matches: YES |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| --TEST-- | ||
| Dechunk write filter state must survive stream seek | ||
| --FILE-- | ||
| <?php | ||
| /* The dechunk filter is commonly used as a write filter on php://temp buffers. | ||
| * The buffer is written to (through the filter) and then seeked to re-read | ||
| * the already-decoded output. Seeking the stream must NOT reset the write | ||
| * filter state, otherwise multi-chunk transfers break. */ | ||
|
|
||
| $buffer = fopen('php://temp', 'w+'); | ||
| stream_filter_append($buffer, 'dechunk', STREAM_FILTER_WRITE); | ||
|
|
||
| /* Write first chunk */ | ||
| fwrite($buffer, "5\r\nHello\r\n"); | ||
|
|
||
| /* Read back decoded data; this seeks to offset 0 internally */ | ||
| $data = stream_get_contents($buffer, -1, 0); | ||
| var_dump($data); | ||
|
|
||
| /* Write second chunk; filter must still be in the correct state */ | ||
| fwrite($buffer, "7\r\n, World\r\n"); | ||
|
|
||
| /* Read all decoded data from the beginning */ | ||
| $data = stream_get_contents($buffer, -1, 0); | ||
| var_dump($data); | ||
|
|
||
| /* Write final (terminating) chunk */ | ||
| fwrite($buffer, "0\r\n\r\n"); | ||
|
|
||
| /* Read complete decoded output */ | ||
| $data = stream_get_contents($buffer, -1, 0); | ||
| var_dump($data); | ||
|
|
||
| fclose($buffer); | ||
|
|
||
| /* Also verify that incomplete chunked transfer is still detected: | ||
| * writing a non-chunk byte after the filter has been reset by a | ||
| * seek should not produce output. */ | ||
| $buffer = fopen('php://temp', 'w+'); | ||
| stream_filter_append($buffer, 'dechunk', STREAM_FILTER_WRITE); | ||
|
|
||
| fwrite($buffer, "5\r\nHello\r\n"); | ||
| $data = stream_get_contents($buffer, -1, 0); | ||
| var_dump($data); | ||
|
|
||
| /* The transfer is still in progress (no terminating 0-chunk seen). | ||
| * Verify incomplete state is preserved by checking ftell: the decoded | ||
| * write position should reflect only the 5 bytes written so far. */ | ||
| var_dump(ftell($buffer)); | ||
|
|
||
| fclose($buffer); | ||
| ?> | ||
| --EXPECT-- | ||
| string(5) "Hello" | ||
| string(12) "Hello, World" | ||
| string(12) "Hello, World" | ||
| string(5) "Hello" | ||
| int(5) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,55 +1,52 @@ | ||
| --TEST-- | ||
| zlib.deflate filter with seek to start | ||
| zlib.deflate write filter is not reset on seek | ||
| --EXTENSIONS-- | ||
| zlib | ||
| --FILE-- | ||
| <?php | ||
| /* Write filters are not reset on stream seek; seeking only affects the | ||
| * stream's read/write position, not the filter pipeline state. This ensures | ||
| * seeking a stream with write filters does not disrupt the filter state. */ | ||
|
|
||
| $file = __DIR__ . '/zlib_filter_seek_deflate.zlib'; | ||
|
|
||
| $text1 = 'Short text.'; | ||
| $text2 = 'This is a much longer text that will completely overwrite the previous compressed data in the file.'; | ||
| $text = 'Hello, World!'; | ||
|
|
||
| $fp = fopen($file, 'w+'); | ||
| stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE); | ||
| $filter = stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE); | ||
|
|
||
| fwrite($fp, $text); | ||
|
|
||
| fwrite($fp, $text1); | ||
| fflush($fp); | ||
| /* Remove the filter to finalize compression cleanly before seeking */ | ||
| stream_filter_remove($filter); | ||
|
|
||
| $size1 = ftell($fp); | ||
| echo "Size after first write: $size1\n"; | ||
| $size = ftell($fp); | ||
| echo "Size after write: $size\n"; | ||
|
|
||
| /* Seek to start succeeds; write filters no longer block seeking */ | ||
| $result = fseek($fp, 0, SEEK_SET); | ||
| echo "Seek to start: " . ($result === 0 ? "SUCCESS" : "FAILURE") . "\n"; | ||
|
|
||
| fwrite($fp, $text2); | ||
| fflush($fp); | ||
|
|
||
| $size2 = ftell($fp); | ||
| echo "Size after second write: $size2\n"; | ||
| echo "Second write is larger: " . ($size2 > $size1 ? "YES" : "NO") . "\n"; | ||
|
|
||
| /* Seek to middle also succeeds */ | ||
| $result = fseek($fp, 50, SEEK_SET); | ||
| echo "Seek to middle: " . ($result === 0 ? "SUCCESS" : "FAILURE") . "\n"; | ||
|
|
||
| fclose($fp); | ||
|
|
||
| /* Verify the compressed output is still valid */ | ||
| $fp = fopen($file, 'r'); | ||
| stream_filter_append($fp, 'zlib.inflate', STREAM_FILTER_READ); | ||
| $content = stream_get_contents($fp); | ||
| fclose($fp); | ||
|
|
||
| echo "Decompressed content matches text2: " . ($content === $text2 ? "YES" : "NO") . "\n"; | ||
| echo "Decompressed content matches: " . ($content === $text ? "YES" : "NO") . "\n"; | ||
| ?> | ||
| --CLEAN-- | ||
| <?php | ||
| @unlink(__DIR__ . '/zlib_filter_seek_deflate.zlib'); | ||
| ?> | ||
| --EXPECTF-- | ||
| Size after first write: %d | ||
| Size after write: %d | ||
| Seek to start: SUCCESS | ||
| Size after second write: %d | ||
| Second write is larger: YES | ||
|
|
||
| Warning: fseek(): Stream filter zlib.deflate is seekable only to start position in %s on line %d | ||
| Seek to middle: FAILURE | ||
| Decompressed content matches text2: YES | ||
| Seek to middle: SUCCESS | ||
| Decompressed content matches: YES | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.