Hi,
I have some problems with the stream API. The methods stream_tell and
stream_seek works not as expected in some cases.
Before reading the next lines, please look at the short
gist(https://gist.github.com/1176641).
First example:
$fp = fopen('mfx://test1', 'w');
fwrite($fp, '12345678');
fseek($fp, -1, SEEK_CUR);
fclose($fp);
// stream_seek: $offset = 7
If you call fseek with the arguments (-1, SEEK_CUR) then the $offset
parameter in the method stream_seek is 7. It seems that the internal API
takes the written bytes returned by fwrite and then it subtracts the
argument (-1) from it before passing it to stream_seek. For the
constants SEEK_SET
and SEEK_END, the passed value is the same as defined
for the fseek call.
The second example:
$fp = fopen('mfx://test2', 'w');
fwrite($fp, '12345678');
fread($fp, 2);
fseek($fp, 1, SEEK_CUR);
fclose($fp);
For this example the stream_seek method gets never be called. The
difference here is that fread is called before fseek.
The third example:
$fp = fopen('mfx://test3', 'w');
fwrite($fp, '12345678');
fread($fp, 3);
ftell($fp);
fclose($fp);
For this example the stream_tell method gets never be called. It is
documented(http://www.php.net/manual/en/streamwrapper.stream-tell.php)
that the stream_tell method is called in response to ftell()
. But it
seems that this method is only be called internally by the stream API.
There exists a Bug report at https://bugs.php.net/bug.php?id=30157
In one of the comments Pierre says: There is no bug but a feature
request which seems to be very discutable.
So with these words, I start the discussion.
Christian
On Sun, Aug 28, 2011 at 3:29 PM, Christian Kaps
christian.kaps@mohiva.com wrote:
In one of the comments Pierre says: There is no bug but a feature
request which seems to be very discutable.
The key part of the comments there are in Wez ones much more than
mine, which mainly refer to his comment about why it works this way
and why the provided patch is not a good idea.
--
Pierre
@pierrejoye | http://blog.thepimp.net | http://www.libgd.org
On Sun, 28 Aug 2011 14:29:28 +0100, Christian Kaps
christian.kaps@mohiva.com wrote:
I have some problems with the stream API. The methods stream_tell and
stream_seek works not as expected in some cases.Before reading the next lines, please look at the short
gist(https://gist.github.com/1176641).First example:
$fp = fopen('mfx://test1', 'w');
fwrite($fp, '12345678');
fseek($fp, -1, SEEK_CUR);
fclose($fp);
// stream_seek: $offset = 7If you call fseek with the arguments (-1, SEEK_CUR) then the $offset
parameter in the method stream_seek is 7. It seems that the internal API
takes the written bytes returned by fwrite and then it subtracts the
argument (-1) from it before passing it to stream_seek. For the
constantsSEEK_SET
and SEEK_END, the passed value is the same as defined
for the fseek call.
SEEK_CUR
seeks are internally converted to SEEK_SET
seeks. It's been this
way since at least 2002:
http://svn.php.net/viewvc/php/php-src/trunk/main/streams.c?annotate=96547&pathrev=96547#l582
The first thing you should know is that PHP keeps track internally of the
position of the stream. That's why PHP knows how to convert SEEK_CUR
-1 to
SEEK_SET
7.
The seek type conversion is an arguable decision, but changing this has
some risks. Consider that some some streams might not respond to SEEK_CUR
seeks or that they do so defectively. The only advantages I see is that it
could mitigate the problems of an inaccurate internal position (though
this happens mostly with internal code that casts the stream into e.g. a
FILE* and then manipulates the pointer); it would also save stream
implementations that only support SEEK_CUR
from having to reconvert the
SEEK_SET
to SEEK_CUR.
The second example:
$fp = fopen('mfx://test2', 'w');
fwrite($fp, '12345678');
fread($fp, 2);
fseek($fp, 1, SEEK_CUR);
fclose($fp);For this example the stream_seek method gets never be called. The
difference here is that fread is called before fseek.
No bug here. This is by design. PHP doesn't read only two bytes from the
stream, it reads an entire chunk. Once PHP has data buffered and you tell
it to skip one byte, it can just advance its internal pointer on the
buffered data; no need to actually call fseek.
The third example:
$fp = fopen('mfx://test3', 'w');
fwrite($fp, '12345678');
fread($fp, 3);
ftell($fp);
fclose($fp);For this example the stream_tell method gets never be called. It is
documented(http://www.php.net/manual/en/streamwrapper.stream-tell.php)
that the stream_tell method is called in response toftell()
. But it
seems that this method is only be called internally by the stream API.There exists a Bug report at https://bugs.php.net/bug.php?id=30157
In one of the comments Pierre says: There is no bug but a feature
request which seems to be very discutable.
Again, as the comment on the bug report says, this is by design. PHP keeps
track internally of the position, so it can just return the information it
has.
But yes, the documentation is wrong in this respect. stream_tell is only
called after a seek in order to determine where the seek ended up. In the
C standard library, you're allowed to seek past the end of the file and
then write, zeroing everything in between the end of the position sought
to (or failing to write). In PHP, this convention doesn't apply; you're
not always allowed to seek to any position.
--
Gustavo Lopes
Hi Gustavo,
thanks for your detailed explanation.
Nevertheless it might be useful to document these inconsistent(from the
userland point of view) behavior. Please see my comments inline.
Am 28.08.2011 17:22, schrieb Gustavo Lopes:
On Sun, 28 Aug 2011 14:29:28 +0100, Christian Kaps
christian.kaps@mohiva.com wrote:I have some problems with the stream API. The methods stream_tell and
stream_seek works not as expected in some cases.Before reading the next lines, please look at the short
gist(https://gist.github.com/1176641).First example:
$fp = fopen('mfx://test1', 'w');
fwrite($fp, '12345678');
fseek($fp, -1, SEEK_CUR);
fclose($fp);
// stream_seek: $offset = 7If you call fseek with the arguments (-1, SEEK_CUR) then the $offset
parameter in the method stream_seek is 7. It seems that the internal API
takes the written bytes returned by fwrite and then it subtracts the
argument (-1) from it before passing it to stream_seek. For the
constantsSEEK_SET
and SEEK_END, the passed value is the same as defined
for the fseek call.
SEEK_CUR
seeks are internally converted toSEEK_SET
seeks. It's been
this way since at least 2002:http://svn.php.net/viewvc/php/php-src/trunk/main/streams.c?annotate=96547&pathrev=96547#l582
The first thing you should know is that PHP keeps track internally of
the position of the stream. That's why PHP knows how to convertSEEK_CUR
-1 toSEEK_SET
7.The seek type conversion is an arguable decision, but changing this has
some risks. Consider that some some streams might not respond to
SEEK_CUR
seeks or that they do so defectively. The only advantages I see
is that it could mitigate the problems of an inaccurate internal
position (though this happens mostly with internal code that casts the
stream into e.g. a FILE* and then manipulates the pointer); it would
also save stream implementations that only supportSEEK_CUR
from having
to reconvert theSEEK_SET
to SEEK_CUR.
This behavior should be documented. The possible values for the "whence"
parameter of the "stream_seek" method should only be SEEK_SET
and
SEEK_END. And there should be a hint that the SEEK_CUR
seeks are
internally converted to SEEK_SET
seeks.
The second example:
$fp = fopen('mfx://test2', 'w');
fwrite($fp, '12345678');
fread($fp, 2);
fseek($fp, 1, SEEK_CUR);
fclose($fp);For this example the stream_seek method gets never be called. The
difference here is that fread is called before fseek.No bug here. This is by design. PHP doesn't read only two bytes from the
stream, it reads an entire chunk. Once PHP has data buffered and you
tell it to skip one byte, it can just advance its internal pointer on
the buffered data; no need to actually call fseek.
It should be documented that, in some circumstances, the "stream_seek"
method isn't called in response to fseek.
The third example:
$fp = fopen('mfx://test3', 'w');
fwrite($fp, '12345678');
fread($fp, 3);
ftell($fp);
fclose($fp);For this example the stream_tell method gets never be called. It is
documented(http://www.php.net/manual/en/streamwrapper.stream-tell.php)
that the stream_tell method is called in response toftell()
. But it
seems that this method is only be called internally by the stream API.There exists a Bug report at https://bugs.php.net/bug.php?id=30157
In one of the comments Pierre says: There is no bug but a feature
request which seems to be very discutable.Again, as the comment on the bug report says, this is by design. PHP
keeps track internally of the position, so it can just return the
information it has.But yes, the documentation is wrong in this respect. stream_tell is only
called after a seek in order to determine where the seek ended up. In
the C standard library, you're allowed to seek past the end of the file
and then write, zeroing everything in between the end of the position
sought to (or failing to write). In PHP, this convention doesn't apply;
you're not always allowed to seek to any position.
The documentation for "stream_tell" is wrong. This method gets only be
called internally.
What do you think about my suggestions?
Christian
Hi Gustavo,
thanks for your detailed explanation.
Nevertheless it might be useful to document these inconsistent(from the
userland point of view) behavior. Please see my comments inline.Am 28.08.2011 17:22, schrieb Gustavo Lopes:
On Sun, 28 Aug 2011 14:29:28 +0100, Christian Kaps
christian.kaps@mohiva.com wrote:I have some problems with the stream API. The methods stream_tell and
stream_seek works not as expected in some cases.Before reading the next lines, please look at the short
gist(https://gist.github.com/1176641).First example:
$fp = fopen('mfx://test1', 'w');
fwrite($fp, '12345678');
fseek($fp, -1, SEEK_CUR);
fclose($fp);
// stream_seek: $offset = 7If you call fseek with the arguments (-1, SEEK_CUR) then the $offset
parameter in the method stream_seek is 7. It seems that the internal API
takes the written bytes returned by fwrite and then it subtracts the
argument (-1) from it before passing it to stream_seek. For the
constantsSEEK_SET
and SEEK_END, the passed value is the same as defined
for the fseek call.
SEEK_CUR
seeks are internally converted toSEEK_SET
seeks. It's been
this way since at least 2002:http://svn.php.net/viewvc/php/php-src/trunk/main/streams.c?annotate=96547&pathrev=96547#l582
The first thing you should know is that PHP keeps track internally of
the position of the stream. That's why PHP knows how to convertSEEK_CUR
-1 toSEEK_SET
7.The seek type conversion is an arguable decision, but changing this has
some risks. Consider that some some streams might not respond to
SEEK_CUR
seeks or that they do so defectively. The only advantages I see
is that it could mitigate the problems of an inaccurate internal
position (though this happens mostly with internal code that casts the
stream into e.g. a FILE* and then manipulates the pointer); it would
also save stream implementations that only supportSEEK_CUR
from having
to reconvert theSEEK_SET
to SEEK_CUR.This behavior should be documented. The possible values for the "whence"
parameter of the "stream_seek" method should only beSEEK_SET
and
SEEK_END. And there should be a hint that theSEEK_CUR
seeks are
internally converted toSEEK_SET
seeks.
Indeed, it should be documented. Please file a doc bug report for it
(and the others) :)
-Hannes