PHP fread file pointer position - php

I wanted to know how fread function moves the file pointer inside the file.
lets consider the following scenario:
<?php
$file=fopen(binary.txt,rb);
fread($file,0x594);
function(fread($file,0x1a8), ....); // some function w/ first argument as fread O/P
?>
brief overview of the code:
it will open a binary file in read only mode. I wanted to know if my understanding is correct or not:
The first invocation of the fread function will move the file pointer to position 0x594.
Since the position of the first byte in the binary file is considered 0, and fread function is reading 0x594 bytes, so what will be the new position of file pointer?
0x593 or 0x594?
The second fread function will start reading from the previous file pointer position. So, everytime, there is an invocation of fread function, the position of file pointer is preserved?
Which means, in a sequence of fread function invocations, each fread function starts reading bytes from the position of the file pointer set by the previous fread function?
in this case, it will start reading bytes from position, 0x594 to (0x594+0x1a8) or 0x73c ?
thanks.

You can investigate this yourself, using ftell(). The current position of the file pointer is an inherent part of the file infrastructure, and PHP is simply riding on top of libc/glibc's implementation of fopen/fread/etc...
However, consider this:
$fh = fopen('somefile.txt', 'r');
the file pointer will now be a position zero, because no data has been read.
$data = fread($fh, 500);
file pointer will now be at position 500, because it's read positions 0->499 (500 bytes) as part of the previous fread call.
$data = fread($fh, 0); // makes no sense to read 0 bytes, but hey...
still at position 500
$data = fread($fh, 1); // now at 501
$data = fread($fh, 2); // now at 503
etc...
Basically, use ftell() to check for yourself. ftell() is used to retrieve the CURRENT LOCATION of the filepointer, so you can use remember where you are. You can then use rewind(), fseek(), etc... to move the pointer all over, then jump right back to where you were without losing place:
$old_loc = ftell($fh); // 503
fseek($fh, 9999);
fseek($fh, 20000); // jump around a bit
fseek($fh, $old_loc); // back to 503, ready to resume reading where we left off.

Related

What the correct way to get size of binary data in php?

I've read a part of file and now want to make sure the part is the right size. How can I do it in php?
$part = fread($file, 1024);
return some_function($part) == 1024;
I've read the examples, but a I doubt to use strlen in cause of Null-terminated string, that might be inside the binary data in $part. In this way strlen returns size from start of part and until first null-byte is presented in data.
As stated in the PHP manual, strlen returns the number of bytes in the string, not the character length.
In PHP, a null byte in a string does NOT count as the end of the string, and any null bytes are included in the length of the string.
So strlen can be used for binary data, no matter if the data is from a file or some other source.
According to PHP documentation of fread() function you may use the construction using filesize() as shown in the first example:
<?php
// get contents of a file into a string
$filename = "/usr/local/something.txt";
$handle = fopen($filename, "r");
$contents = fread($handle, filesize($filename));
fclose($handle);
?>
Update: to find a size of the file can be used function stat() without opening or fstat() on opened file.

byte position: file_get_contents vs fopen

I need some data from a specific byte in range in a binary file.
(concatenated jpegs, don't ask...)
So I have a offset and length data from an external API.
(I would guess that those are byte positions)
What works is the following:
$fileData = file_get_contents($binaryFile);
$imageData = substr($fileData, $offset, $length);
But i would rather not load the full file into memory and therefor tried fopen:
$handle = fopen($binaryFile, 'rb');
fseek($handle, $offset);
$imageData = fgets($handle, $length);
But that doesn't work. The data chunk is no valid image data.
So i assume i got the position wrong with fopen.
Any ideas on how the positions differ in substr vs fopen?
You wrote
The data chunk is no valid image data
"image data" - but in your code you call fgets() to read that data. That's wrong, as image is binary data, not a text file, so you don't want it read it by lines (docs):
fgets — Gets line from file pointer
This means fgets() would stop reading from file once it finds what it considers line end marker, which usually means stopping earlier and reading less than $length as there's pretty low chance such byte is not in the binary sequence.
So fgets() wrong method to use and this is the main issue. Instead you should pick less smart fread() (which does not know about lines and stuff, and just reads what you told). Finally you should fclose() the handle when you done. And naturally you should always check for errors, starting from fopen():
if ($handle = fopen($binaryFile, 'rb')) {
if (fseek($handle, $offset) === 0) {
$imageData = fread($handle, $length);
if ($imageData === false) {
// error handling - failed to read the data
}
} else {
// error handling - seek failed
}
fclose($handle);
} else {
// error handling - can't open file
}
So always use right tool for the task, and if you are unsure what given method/function does, there's always not-that-bad documentation to peek.
You can use file_get_contents, too. See this simple line:
imageData = file_get_contents($binaryFile, null, null, 0, $length);
And here the documentation of file_get_contents.

fopen() with r+ acts like a?

This is some of the weirdest stuff I've ever honestly seen.
$filename = "/etc/httpd/conf/httpd.conf";
$handle = fopen($filename, "r+");
$size = filesize($filename);
$contents = fread($handle, $size);
fwrite($handle,$contents);
fclose($handle);
Shouldn't this read the contents of the file then write it again? Basically leaving the file unmodified? At this moment this piece of code does exactly what append does, it duplicates the contents of the file adding $contents at the end of the file, and I have no idea why. I tried changing the string a little before writing it and that's how I found out it writes to the end, not the beginning.
Your fread() is advancing the file pointer such that by the time you call fwrite, your file pointer is at the end and your write appends to the file from there.
If you want to write to the file starting at the beginning after reading it all in then:
rewind($handle);

PHP & stress tests - load large file into memory

I have to stress test an URL (PHP script) using Apache Benchmark. But for each request, I need a different set of data to be processed, and the URL to remain the same. So inside that PHP script I need to read a 3.000.000 lines file and pick a random one. It means that for each ab request, I need to read that file, then get a random line, then process it.
What method do you recommend?
I was thinking to somehow load that file in memory once (and be available for all requests) and then get a random line from it.
In other words, I need to read one random line from a large file without "feeling" it.
Thank you!
$fh = fopen($file, 'r');
$stats = fstat($fh);
// jump to random location within file
fseek($fh, mt_rand(0, $stats['size'] - 1));
// check where you are
$chr = fread($fh, 1);
// while you're not at a newline and not at the start of the file
while ($chr != "\n" && ftell($fh) > 0) {
// go back one character (plus an additional one you've just read)
fseek($fh, -2, SEEK_CUR);
$chr = fread($fh, 1);
}
// you're just past a newline now, go read the whole next line
$line = fgets($fh);

PHP using fwrite and fread with input stream

I'm looking for the most efficient way to write the contents of the PHP input stream to disk, without using much of the memory that is granted to the PHP script. For example, if the max file size that can be uploaded is 1 GB but PHP only has 32 MB of memory.
define('MAX_FILE_LEN', 1073741824); // 1 GB in bytes
$hSource = fopen('php://input', 'r');
$hDest = fopen(UPLOADS_DIR.'/'.$MyTempName.'.tmp', 'w');
fwrite($hDest, fread($hSource, MAX_FILE_LEN));
fclose($hDest);
fclose($hSource);
Does fread inside an fwrite like the above code shows mean that the entire file will be loaded into memory?
For doing the opposite (writing a file to the output stream), PHP offers a function called fpassthru which I believe does not hold the contents of the file in the PHP script's memory.
I'm looking for something similar but in reverse (writing from input stream to file). Thank you for any assistance you can give.
Yep - fread used in that way would read up to 1 GB into a string first, and then write that back out via fwrite. PHP just isn't smart enough to create a memory-efficient pipe for you.
I would try something akin to the following:
$hSource = fopen('php://input', 'r');
$hDest = fopen(UPLOADS_DIR . '/' . $MyTempName . '.tmp', 'w');
while (!feof($hSource)) {
/*
* I'm going to read in 1K chunks. You could make this
* larger, but as a rule of thumb I'd keep it to 1/4 of
* your php memory_limit.
*/
$chunk = fread($hSource, 1024);
fwrite($hDest, $chunk);
}
fclose($hSource);
fclose($hDest);
If you wanted to be really picky, you could also unset($chunk); within the loop after fwrite to absolutely ensure that PHP frees up the memory - but that shouldn't be necessary, as the next loop will overwrite whatever memory is being used by $chunk at that time.

Categories