Description
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