php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #49874 ftell() and fseek() inconsistency when using stream filters
Submitted: 2009-10-14 11:39 UTC Modified: 2013-10-24 08:19 UTC
Votes:8
Avg. Score:4.2 ± 0.8
Reproduced:5 of 5 (100.0%)
Same Version:1 (20.0%)
Same OS:2 (40.0%)
From: jketterl at chipxonio dot de Assigned:
Status: Open Package: Filesystem function related
PHP Version: 5.5.4 OS: linux (ubuntu)
Private report: No CVE-ID: None
View Add Comment Developer Edit
Anyone can comment on a bug. Have a simpler test case? Does it work for you on a different platform? Let us know!
Just going to say 'Me too!'? Don't clutter the database with that please — but make sure to vote on the bug!
Your email address:
MUST BE VALID
Solve the problem:
22 - 13 = ?
Subscribe to this entry?

 
 [2009-10-14 11:39 UTC] jketterl at chipxonio dot de
Description:
------------
exact php version: PHP 5.2.11-0.dotdeb.1 with Suhosin-Patch 0.9.7 (cli) (built: Sep 20 2009 09:41:43)
this bug is also be filter-/stream-related. i just believe it might be easier to fix on the filesystem side, that's why i chose that category.

when using a php stream filter to convert input from utf-16 into iso8859 (or most probably from any 2byte-encoded charset into any single-byte-encode charset) the ftell() and fseek() functions start to behave inconsistently.

more precisely: fseek() jumps to exact offsets ignoring the 2byte-encoding, whereas ftell() seems to return the number of bytes read *after* the filter has been applied. thus it is not possible to fseek() back to a certain offset that has been stored with ftell() before.

the content of the testfile used in the code examples is as follows:
Line 01
Line 02
Line 03
Line 04

Reproduce code:
---------------
$file = 'test.csv';

$fp = fopen($file, 'r');
stream_filter_append($fp, 'convert.iconv.utf16/iso8859-15');
$line = fgets($fp);
var_dump($line);
$line = fgets($fp);
var_dump($line);
fclose($fp);

$fp = fopen($file, 'r');
stream_filter_append($fp, 'convert.iconv.utf16/iso8859-15');
$line = fgets($fp);
var_dump($line);
fseek($fp, ftell($fp)); // this shouldn't move anything - but it does...
$line = fgets($fp);
var_dump($line);
fclose($fp);

Expected result:
----------------
string(8) "Line 01
"
string(8) "Line 02
"
string(8) "Line 01
"
string(8) "Line 02
"

Actual result:
--------------
string(8) "Line 01
"
string(8) "Line 02
"
string(8) "Line 01
"
string(4) " 01
"

Patches

Add a Patch

Pull Requests

Pull requests:

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2009-10-14 16:40 UTC] Sjoerd@php.net
Thank you for your bug report. Does your test.csv file start with a BOM? You can determine this by viewing the file in a hex editor. If it starts with fffe or feff, it has a BOM (byte order mark).
 [2009-10-15 06:54 UTC] jketterl at chipxonio dot de
thanks for having a look

i tried with and without. the challenge is to get it working without, because that's the worst case my app has to deal with, but the BOM doesn't seem to solve this.

$ hexdump test-with-bom.csv
0000000 feff 004c 0069 006e 0065 0020 0030 0031
0000010 000a 004c 0069 006e 0065 0020 0030 0032
0000020 000a 004c 0069 006e 0065 0020 0030 0033
0000030 000a 004c 0069 006e 0065 0020 0030 0034
0000040 000a
0000042

$ php test.php
string(8) "Line 01
"
string(8) "Line 02
"
string(8) "Line 01
"
string(5) "e 01
"

i also tried opening the file including the BOM without a stream filter, but that just resulted in php reading in two extra chars (the BOM converted in some way i guess) on the beginning of the first line.

i thought i'd attach the sample files to this bug, but it seems like i can't. i've uploaded them here instead: http://www.djmacgyver.net/tmp/php-ftell/
 [2013-10-24 08:19 UTC] yohgaki@php.net
-PHP Version: 5.2.11 +PHP Version: 5.5.4
 [2013-10-24 08:19 UTC] yohgaki@php.net
It seems 5.5 has this problem still

string(8) "Line 01
"
string(8) "Line 02
"
string(8) "Line 01
"
string(5) "e 01
"
[yohgaki@dev php-5.4]$ php -v
PHP 5.5.4 (cli) (built: Sep 19 2013 13:06:40)
 [2017-08-01 12:06 UTC] paul dot smith at deepseaplc dot com
I'm also having this problem with the zlib.deflate filter in PHP 5.6.20.

I attempted to append a user filter after zlib.deflate with the intention of totalling the datalen of the buckets. The filter() function is called but stream_bucket_make_writeable($in) returns null. I suspect this is related to the ftell problem.
 [2018-07-08 10:14 UTC] php at bohwaz dot net
Same issue with zlib:

<?php

$fh = fopen('php://temp', 'w+');

fwrite($fh, '123');
var_dump(ftell($fh));

stream_filter_append($fh, 'zlib.deflate', STREAM_FILTER_WRITE, 9);

fwrite($fh, 'abc');

var_dump(ftell($fh));

fseek($fh, 0);
var_dump(fread($fh, 1024));

fclose($fh);

?>

Will return:

int(3)
int(3)
string(3) "123"

See: https://3v4l.org/VAfmR
 [2020-02-06 05:52 UTC] phofstetter at sensational dot ch
This is an inherent problem with all filters that keep internal state (which is probably all but the simplest): There's no way a filter can learn about seeking on the underlying stream and thus it can't reset its internal state.

It's a tricky problem though: For any seek() but a seek() going to the beginning of the stream, it's pretty much impossible to correctly rebuild the internal filter state after seeking on the stream.

I think what should happen in order to save other people the trouble in the future is to mark streams with filters attached as non-seekable.
 [2020-10-20 17:06 UTC] cmb@php.net
The following pull request has been associated:

Patch Name: Disallow seeking on filtered streams
On GitHub:  https://github.com/php/php-src/pull/6359
Patch:      https://github.com/php/php-src/pull/6359.patch
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Mar 28 20:01:28 2024 UTC