Skip to content

exif_read_data() cannot read smaller stream wrapper chunk sizes #10834

Closed
ThePHPF/thephp.foundation
#90
@dotpointer

Description

@dotpointer

Description

exif_read_data() outputs the warning below when it reads from a stream wrapper source and the stream chunk size that is set by stream_set_chunk_size() is too small. What is considered a too small chunk size seems to depend on the file size and maybe the EXIF data size inside it. On a 271 KB JPEG it was 113, but on a 1,1M JPEG was 7397. Sometimes even the default chunk size is too small and it needs to be adjusted to work.

The example stream wrapper class is a modified version of the VariableStream example from the manual:
https://www.php.net/manual/en/stream.streamwrapper.example-1.php

I have tested this on Debian Linux 11 with PHP 7.4.33 and on the same system in a Docker environment with PHP 8.1.16, the bug is in both versions.

I found JPEG files with EXIF data on the Lorem Picsum page, but I have tried other sources too with the same error:
https://picsum.photos/1920/1080

If there is something wrong with the example code please tell me how to correct it so I can learn something please. :blush I have tried different variants of this code like writing to the stream before reading it using cURL calls which makes no difference and also passing a fopen() file handle that reads image file directly which makes the bug disappear.

The PHP code:

<?php

error_reporting(E_ALL);
ini_set('display_errors', 1);

# set the path to a (JPEG) image file with EXIF data
# a big file like +1 MB is recommended
define('IMAGE_FILE', './example.jpg');

class VariableStream {
  var $data;
  var $position;

  function stream_close() {
    # echo "Stream close\n";
    return true;
  }

  function stream_eof() {
    # echo 'Stream eof - '.($this->position >= strlen($this->data ? 'Yes' : 'No'))."\n";
    return $this->position >= strlen($this->data);
  }

  function stream_open($path, $mode, $options, &$opened_path) {
    # echo "Stream opened\n";
    $this->position = 0;
    $this->data = file_get_contents(IMAGE_FILE);
    return true;
  }

  function stream_seek($offset, $whence) {
    # echo "Stream seek $offset $whence\n";
    switch ($whence) {
      case SEEK_SET:
        if ($offset < strlen($this->data) && $offset >= 0) {
              $this->position = $offset;
              return true;
        } else {
              return false;
        }
        break;
      case SEEK_CUR:
        if ($offset >= 0) {
              $this->position += $offset;
              return true;
        } else {
              return false;
        }
        break;
      case SEEK_END:
        if (strlen($this->data) + $offset >= 0) {
              $this->position = strlen($this->data) + $offset;
              return true;
        } else {
              return false;
        }
        break;
      default:
        return false;
    }
  }

  function stream_read($count) {
    # echo "Stream read\n";
    $ret = substr($this->data, $this->position, $count);
    $this->position += strlen($ret);
    return $ret;
  }

  function stream_tell() {
    # echo 'Stream tell: '.$this->position."\n";
    return $this->position;
  }
}

stream_wrapper_register('var', 'VariableStream')
  or die('Failed to register protocol');

$fp = fopen('var://myvar', 'rb');

# try different chunk sizes here to find the threshold
# where the warnings appear and disappear
stream_set_chunk_size($fp, 8000);

# error appear after this line
$headers = exif_read_data($fp);

var_dump($headers);

fclose($fp);

?>

Resulted in this output:

Warning: exif_read_data(): Error reading from file: got=x004C(=76) != itemlen-2=x2838(=10296) in example.php on line 86

Warning: exif_read_data(): Invalid JPEG file in example.php on line 86
bool(false)

But I expected this output instead:

No warnings but the EXIF header data outputted as an array by var_dump.

PHP Version

PHP 8.1.16

Operating System

Debian 11

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions