php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #21641 stream_read() always passed count of 8192
Submitted: 2003-01-14 14:59 UTC Modified: 2003-01-14 15:38 UTC
From: web-php-bugs at sklar dot com Assigned:
Status: Not a bug Package: Filesystem function related
PHP Version: 4CVS-2003-01-14 (stable) OS: Linux 2.4.18
Private report: No CVE-ID: None
 [2003-01-14 14:59 UTC] web-php-bugs at sklar dot com
The stream_read() method of a class registered with stream_register_wrapper() is always passed 8192 as a count of bytes to read no matter what the second argument is to an fread() on that stream. This is reproduceable with the following class and test code:

<?php
class Stream_File {
    var $fn;
    var $fp;

    function stream_open($path, $mode, $options, &$opened_path)
    {
        $url = parse_url($path);
        $this->fp = fopen($this->fn = $url['path'],$mode);
        return ($this->fp ? true : false);
    }

    function stream_close()
    {
        fclose($this->fp);
    }

    function stream_read($count)
    {
        error_log("stream_read: $count ");
        return fread($this->fp,$count);
    }
    
    function stream_write($data)
    {
        return fwrite($this->fp,$data);
    }
    function stream_eof()
    {
        return feof($this->fp);
    }
    function stream_tell()
    {
        return ftell($this->fp);
    }
    function stream_seek($offset,$whence)
    {
        return fseek($this->fp,$offset,$whence);
    }
    function stream_flush()
    {
        return fflush($this->fp);
    }
}

stream_register_wrapper('filetest', 'Stream_File') or die("Can't register filetest on Stream_File");

$fp = fopen("filetest://localhost/tmp/hello","w+");
fwrite($fp, "testing\n");
rewind($fp);
$data = fread($fp, 10);
echo "$data\n";
rewind($fp);
$data = fread($fp,32000);
echo "$data\n";
fclose($fp);
?>

Each time fread() is called on the stream, the error_log() call in stream_read() prints:

stream_read: 8192

instead of "stream_read: 10" or "stream_read: 32000"

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2003-01-14 15:09 UTC] wez@php.net
This is not a bug; PHP uses an internal read buffer 8192 bytes in length in order to be more efficient.
You should note that fread() returns the correct number of bytes to your script.
 [2003-01-14 15:20 UTC] web-php-bugs at sklar dot com
However, this causes problems for smaller values when the function that stream_read() is calling doesn't like it when the byte count is larger than its size. Consider this code (from the shared memory stream wrapper I am working on)

    function stream_read($count)
    {
        $data = shmop_read($this->shm,$this->pos,$count);
        $this->pos += strlen($data);
        return $data;
    }

If the shared memory segment that $this->shm points to is less than 8192 bytes long, then shmop_read() complains that the count is out of range. Here's some sample code if you want to try (thanks to Xavier Noguer [xnoguer@rezebra.com] who reported this to me):

<?php

class Stream_SHM {
    var $pos = 0;
    var $shm_key;
    var $size = 16384; // default size: 16k
    var $shm;

    function stream_open($path, $mode, $options, &$opened_path)
    {
        $url = parse_url($path);
        $this->shm_key = $url['host'];
        if ((! intval($this->shm_key)) && (! preg_match('/^0x[0-9a-f]+$/i',
                                                        $this->shm_key))) {
            if ($options & STREAM_REPORT_ERRORS) {
                trigger_error("Stream_SHM::stream_open: $this->shm_key is not a valid shm key.",E_USER_ERROR);
            }
            return false;
        }
        if (intval($url['port'])) {
            $this->size = intval($url['port']);
        }
        if (($mode != 'a') && ($mode != 'c') && 
            ($mode != 'w') && ($mode != 'n')) {
            if ($options & STREAM_REPORT_ERRORS) {
                trigger_error("Stream_SHM::stream_open: $mode is not a valid mode (must be one of: a c n w)",E_USER_ERROR);
            }
            return false;
        }
        if (! ($this->shm = shmop_open($this->shm_key,$mode,0600,$this->size))) {
            if ($options & STREAM_REPORT_ERRORS) {
                trigger_error('Stream_SHM::stream_open: shmop_open() failed');
            }
            return false;
        }
        $this->size = shmop_size($this->shm);
        return true;
    }

    function stream_close()
    {
        shmop_close($this->shm);
    }

    function stream_read($count)
    {
        $data = shmop_read($this->shm,$this->pos,$count);
        $this->pos += strlen($data);
        return $data;
    }

    function stream_write($data)
    {
        $count = shmop_write($this->shm,$data,$this->pos);
        $this->pos += $count;
        return $count;
    }
    function stream_eof()
    {
        return ($this->pos == ($this->size - 1));
    }
    function stream_tell()
    {
        return $this->pos;
    }
    function stream_seek($offset,$whence)
    {
        switch ($whence) {
        case SEEK_SET:
            if (($offset >= 0) && ($offset < $this->size)) {
                $this->pos = $offset;
                return true;
            } else {
                return false;
            }
            break;
        case SEEK_CUR:
            if (($offset >= 0) && (($this->pos + $offset) < $this->size)) {
                $this->pos += $offset;
                return true;
            } else {
                return false;
            }
            break;
        case SEEK_END:
            if (($this->size + $offset) >= 0) {
                $this->pos = $this->size + $offset;
                return true;
            } else {
                return false;
            }
            break;
        default:
            return false;
        }
    }

    function stream_flush()
    {
        return true;
    }
}
  stream_register_wrapper('shm', 'Stream_SHM') 
     or die("Can't register shm on Stream_SHM");


  echo "\nUsing shm://0x0000:8192/\n";
  $mem = fopen("shm://0x0000:8192/","c");
  fwrite($mem, "hola\n");
  rewind($mem);
  $data = fread($mem, 4);
  echo "$data\n";
  fclose($mem);

  echo "\nUsing shm://0x0000:8191/\n";
  $mem = fopen("shm://0x0000:8191/","c");
  fwrite($mem, "hola\n");
  rewind($mem);
     $data = fread($mem, 4);
  echo "$data\n";
  fclose($mem);

  echo "\nUsing shmop_open() with size = 8191\n"; //shm://0x0000:8191/";
  $mem = shmop_open(0x0000, "c", 0600, 8191);
  $shm_bytes_written = shmop_write($mem, "hola", 0);
  $data = shmop_read($mem, 0, 4);
  echo "$data\n";
  shmop_close($mem);
?>

The first example, with the stream and an 8192 byte segment, works fine. The last example, without the stream and with an 8191 byte segment, works fine. The middle example, however, with the stream and an 8191 byte segment fails, because the call to shmop_read() throws a "count is out of range" error.
 [2003-01-14 15:25 UTC] wez@php.net
Not a bug.
Your stream should keep track of the maximum size available, and is responsible for keeping within range of the functions it calls.
You already have a size and a position, you should be able to determine the maximum readable size using that information.
If you don't want to see an error, you can use the @ operator.

 [2003-01-14 15:31 UTC] web-php-bugs at sklar dot com
I don't understand. In the shared memory example, how am I supposed to be able to read just four bytes from the stream? When I pass 4 to fread(), stream_open() gets 8192 $count. I can do the math just fine in stream_open() to trim that to 8191 (or whatever's left between the current position and the size of the segment), but that assumes that the fread() call was to read the rest of the segment. It's not. It's only for four bytes.
 [2003-01-14 15:38 UTC] wez@php.net
function stream_read($count)
{
    if ($count + $this->pos > $this->size) {
        $count = $this->size - $this->pos;
    }
    $data = shmop_read($this->shm,$this->pos,$count);
    $this->pos += strlen($data);
    return $data;
}

 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 28 19:01:28 2024 UTC