The StreamReader class
The StreamReader class is helpful when reading files.
The foremost advantage of streaming files instead of reading them into memory at
once, by something like file_get_contents(), is of course that the streaming
method will consume far less memory. And if you only want to read a chunk of
bytes from the file there's no need to read the complete file into memory.
A good example of when the StreamReader class comes
in handy is when you want to read binary data. Lets say I want to verify that an
image I have really is a PNG image.
The first eight bytes of a PNG
image is a signature that always is the same. They look like 137 80 78 71 13
10 26 10 in decimal form.
So to verify that our image really is a PNG image we can read the first eight bytes, unpack them to decimal form and verify that they match the PNG signature.
I will be working on this image:

- <?php
- require_once 'PLib.php';
- PLib::Import('IO.StreamReader');
- $image = 'png24.png';
- //! The PNG signature
- $pngsig = array(
- 1 => 137,
- 2 => 80,
- 3 => 78,
- 4 => 71,
- 5 => 13,
- 6 => 10,
- 7 => 26,
- 8 => 10
- );
- //! Create a stream for the image
- $reader = new StreamReader($img);
- //! Read the first eight bytes and unpack them.
- //! Hopefully this will result in an array identical to the $pngsig
- $sig = unpack('C*', $reader->Read(8));
- if ($sig === $pngsig)
- echo "It's a PNG image";
- else
- echo "It's not a PNG image!";
- ?>
Not too difficult!
We can also use the method ReadBlock to easily read a block
of bytes. For instance, lets read 4 bytes from byte 12 (13, 14, 15, 16) in the
PNG image to check if that's really where the PNG image header (ihdr) begins
and if so then read the next 13 bytes - which is the actual header - to
determine the width, height, bit depth, colour type and so on of the image:
- <?php
- require_once 'PLib.php';
- PLib::Import('IO.StreamReader');
- $image = 'png24.png';
- $reader = new StreamReader($image);
- //! Move to byte 12 and read four bytes.
- $ihdr = $reader->ReadBlock(12, 4);
- if ($ihdr != 'IHDR')
- die('Malformed PNG image!');
- //! Since we read up to and including byte 16 previously that means the file
- //! pointer in the StreamReader object has moved to the 16th byte so we can
- //! just read the next 13 bytes.
- $ihdr = $reader->Read(13);
- //! Format for unpacking the header data
- $ihdrFormat = 'Nwidth/' .
- 'Nheight/' .
- 'Cbitdepth/' .
- 'Ccolourtype/' .
- 'CcompressionMethod/' .
- 'CfilterMethod/' .
- 'CinterlaceMethod';
- $ihdr = unpack($ihdrFormat, $ihdr);
- print_r($ihdr);
- //! The result is
- Array
- (
- [width] => 191
- [height] => 69
- [bitdepth] => 8
- [colourtype] => 6
- [compressionMethod] => 0
- [filterMethod] => 0
- [interlaceMethod] => 0
- )
- ?>
If you want to know more about the PNG format you can check out the specification at W3C.
Line by line
Another handy method of StreamReader is
ReadLine.
A common way to read a file line by line is to do like this:
- <?php
- $lines = file('thefile.txt');
- foreach ($lines as $line) {
- // Do some stuff
- }
- ?>
This is really easy but it can have a major impact on the memory consumption if
you happen to deal with a really large file. Since the
StreamReader class
will stream the file line by line the memory consumption will be far far less.
In the following example I will list the file /etc/mime.types which contains
a list of mimetypes and their file extensions (if one or more is associated
with the mimetype).
- <?php
- require_once 'PLib.php';
- PLib::Import('IO.StreamReader');
- $reader = new StreamReader('/etc/mime.types');
- echo "<table>\n<tr><th>Mimetype</th><th>Extension</th></tr>\n";
- while (($line = $reader->ReadLine()) !== false) {
- $line = trim($line);
- //! Skip empty lines and comments
- if (empty($line) || $line[0] == '#')
- continue;
- preg_match('/(.[^ \t]*)[ \t]*(.*)$/s', $line, $m);
- echo "<tr><td>{$m[1]}</td><td>{$m[2]}</td></tr>\n";
- }
- echo "</table>\n";
- ?>
Here's the result of the code above