php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #65581 stream_get_contents does not seek with a stream wrapper
Submitted: 2013-08-29 08:28 UTC Modified: 2013-08-29 13:41 UTC
From: ivan dot enderlin at hoa-project dot net Assigned: jpauli (profile)
Status: Not a bug Package: *General Issues
PHP Version: master-Git-2013-08-29 (Git) OS:
Private report: No CVE-ID: None
 [2013-08-29 08:28 UTC] ivan dot enderlin at hoa-project dot net
Description:
------------
When calling stream_get_contents() with $offset >= ftell() *through a stream wrapper*, the internal pointer of the stream is moved but stream_get_contents() throws an error: "Failed to seek to position $offset in the stream".

While monitoring the source code (ext/standard/streamsfuncs.c, at line 404, no kidding ;-)), it appears that it is a normal behaviour since seek_res is set to -1 at line 426 (with the code bellow). I did not understand why (I looked at php_stream_seek implementation but I was not able to understand).

Change fopen('foo://bar', 'rb') for fopen(__FILE__, 'rb') and the issue disappears. That's why I think it is related to stream wrapper, but I cannot ensure that.

Test script:
---------------
<?php

class StreamWrapper {

    protected $_stream = null;

    public function stream_open ( $path, $mode, $options, &$openedPath ) {

        $this->_stream = fopen(__FILE__, $mode);

        return true;
    }

    public function stream_seek ( $offset, $whence = SEEK_SET ) {

        var_dump('seek to ' . $offset);

        return fseek($this->_stream, $offset, $whence);
    }

    public function stream_read ( $count ) {

        return fread($this->_stream, $count);
    }

    public function stream_stat ( ) {

        return fstat($this->_stream);
    }

    public function stream_eof ( ) {

        return feof($this->_stream);
    }
}

stream_wrapper_register('foo', 'StreamWrapper');

$a = fopen('foo://bar', 'rb');
var_dump(stream_get_contents($a, 30, 4));


Expected result:
----------------
no error

Actual result:
--------------
error

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2013-08-29 08:40 UTC] ivan dot enderlin at hoa-project dot net
Oh also the bug disappears with:

    fseek($a, 4);
    stream_get_contents($a, 30);
 [2013-08-29 13:31 UTC] jpauli@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: jpauli
 [2013-08-29 13:31 UTC] jpauli@php.net
Thank you for taking the time to write to us, but this is not
a bug. Please double-check the documentation available at
http://www.php.net/manual/ and the instructions on how to report
a bug at http://bugs.php.net/how-to-report.php

This is because your stream_seek() function does not return the right value.
stream_seek() should return TRUE if the seek has successed, you return the value 
of fseek(), which is 0 on success.

Additionally, you have to implement stream_tell() so that the underlying layer 
can ask for it and know where the stream position is.
stream_tell() is called just after stream_seek() has successed, thus it is 
needed, even if you never call ftell() on your own stream, the underlying layer 
does.

Correct code is :

<?php

class StreamWrapper {

    protected $_stream = null;

    public function stream_open ( $path, $mode, $options, &$openedPath ) {

        $this->_stream = fopen(__FILE__, $mode);

        return true;
    }

    public function stream_seek ( $offset, $whence = SEEK_SET ) {

        var_dump('seek to ' . $offset);

        if (fseek($this->_stream, $offset, $whence) == 0) {
            return true;
        }
        return false;
    }

    public function stream_tell()
    {
        return ftell($this->_stream);
    }

    public function stream_read ( $count ) {

        return fread($this->_stream, $count);
    }

    public function stream_stat ( ) {

        return fstat($this->_stream);
    }

    public function stream_eof ( ) {

        return feof($this->_stream);
    }
}

stream_wrapper_register('foo', 'StreamWrapper');

$a = fopen('foo://bar', 'rb');
var_dump(stream_get_contents($a, 30, 4));
 [2013-08-29 13:36 UTC] ivan dot enderlin at hoa-project dot net
Oh yes, I forgot that tell() returns 0 on success… That's solved my issue. Thanks!
 [2013-08-29 13:41 UTC] rasmus@php.net
-Status: Closed +Status: Not a bug
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Nov 05 01:01:29 2024 UTC