Will md5(file_contents_as_string) equal md5_file(/path/to/file)? - php

If I do:
<?php echo md5(file_get_contents("/path/to/file")) ?>
...will this always produce the same hash as:
<?php echo md5_file("/path/to/file") ?>

Yes they return the same:
var_dump(md5(file_get_contents(__FILE__)));
var_dump(md5_file(__FILE__));
which returns this in my case:
string(32) "4d2aec3ae83694513cb9bde0617deeea"
string(32) "4d2aec3ae83694513cb9bde0617deeea"
Edit:
Take a look at the source code of both functions: https://github.com/php/php-src/blob/master/ext/standard/md5.c (Line 47 & 76). They both use the same functions to generate the hash except that the md5_file() function opens the file first.
2nd Edit:
Basically the md5_file() function generates the hash based on the file contents, not on the file meta data like the filename. This is the same way md5sum on Linux systems work.
See this example:
pr#testumgebung:~# echo foobar > foo.txt
pr#testumgebung:~# md5sum foo.txt
14758f1afd44c09b7992073ccf00b43d foo.txt
pr#testumgebung:~# mv foo.txt bar.txt
pr#testumgebung:~# md5sum bar.txt
14758f1afd44c09b7992073ccf00b43d bar.txt

md5_file command just hashs the content of a file with md5.
If you refer to the old md5_file PHP implementation (but the principle is still the same) source :
function php_compat_md5_file($filename, $raw_output = false)
{
// ...
// removed protections
if ($fsize = #filesize($filename)) {
$data = fread($fh, $fsize);
} else {
$data = '';
while (!feof($fh)) {
$data .= fread($fh, 8192);
}
}
fclose($fh);
// Return
$data = md5($data);
if ($raw_output === true) {
$data = pack('H*', $data);
}
return $data;
}
So if you hash with md5 any string or content, you will always get the same result as md5_file (for the same encoding and file content).
In that case, if you hash by md5 the content of a file with file_get_content() or if you use md5_file or even if you use md5 command with the same content as your file content, you will always get the same result.
By example, you could change the file name of a file, and for two different files, with the same content, they will produce the same md5 hash.
By example:
Considering two files containing "stackoverflow" (without the quotes) named 1.txt and 2.txt
md5_file("1.txt");
md5_file("2.txt");
would output
73868cb1848a216984dca1b6b0ee37bc
You will have the exact same result if you md5("stackoverflow") or if you md5(file_get_contents("1.txt")) or md5(file_get_contents("1.txt")).

based on the file contents, not on the file metadata like the BOM or filename
That's not correct about BOM.
BOM is a part of file content, you can see its three bytes in any non-unicode file editor.

Yes, I tried it for several times.
In my case, result for:
<?php echo md5(file_get_contents("1.php")) ?>
<br/>
<?php echo md5_file("1.php") ?>
Produce output as:
660d4e394937c10cd1c16a98f44457c2
660d4e394937c10cd1c16a98f44457c2
Which seems equivalent on both lines.

Related

Open a text file, search for integer and then display text after a character

I have a multi-line text file that contains a number and url
59=folder/image.png
63=folder/image2.png
etc...
I want to search the file for "59" and then echo the url in img src
If the text file doesn't contain "59", I want to echo an alternative image
There's a problem because the png filename is also an integer
I have tried this with code that doesn't make sense, as pointed out by a user below.
You say you want to "search the file for 59", but surely actually you only want to search the beginning of the line. That would solve the issue with having numbers in the filename.
Your code so far doesn't make a lot of sense, it isn't searching for 59 at all, anywhere in the text.
This will do the job correctly:
$search_ID = "59"; //Note: this is now a string, so we can check its length, and match that in the file
$data = file_get_contents('text_file.txt'); //$contents was redundant, just start by calling it $data
$result = "";
if (substr($data, 0, strlen($search_ID)) == $search_ID)
{
$result = substr($data, strpos($data, "=")+1);
}
else
{
$result = "something else"; //replace this with whatever alternative output you need
}
Demo: http://sandbox.onlinephpfunctions.com/code/fb413cd66670001fe6e665b9e10b55285d1440b1
P.S. this code assumes the file only ever has one line. It wasn't quite clear from your question where there was the potential for it to have multiple lines in the same format, or not.

PHP File Handling (Download Counter) Reading file data as a number, writing it as that plus 1

I'm trying to make a download counter in a website for a video game in PHP, but for some reason, instead of incrementing the contents of the downloadcount.txt file by 1, it takes the number, increments it, and appends it to the end of the file. How could I just make it replace the file contents instead of appending it?
Here's the source:
<?php
ob_start();
$newURL = 'versions/v1.0.0aplha/Dungeon1UP.zip';
//header('Location: '.$newURL);
//increment download counter
$file = fopen("downloadcount.txt", "w+") or die("Unable to open file!");
$content = fread($file,filesize("downloadcount.txt"));
echo $content;
$output = (int) $content + 1;
//$output = 'test';
fwrite($file, $output);
fclose($file);
ob_end_flush();
?>
The number in the file is supposed to increase by one every time, but instead, it gives me numbers like this: 101110121011101310111012101110149.2233720368548E+189.2233720368548E+189.2233720368548E+18
As correctly pointed out in one of the comments, for your specific case you can use fseek ( $file, 0 ) right before writing, such as:
fseek ( $file, 0 );
fwrite($file, $output);
Or even simpler you can rewind($file) before writing, this will ensure that the next write happens at byte 0 - ie the start of the file.
The reason why the file gets appended it is because you're opening the file in append and truncate mode, that is "w+". You have to open it in readwrite mode in case you do not want to reset the contents, just "r+" on your fopen, such as:
fopen("downloadcount.txt", "r+")
Just make sure the file exists before writing!
Please see fopen modes here:
https://www.php.net/manual/en/function.fopen.php
And working code here:
https://bpaste.net/show/iasj
It will be much simpler to use file_get_contents/file_put_contents:
// update with more precise path to file:
$content = file_get_contents(__DIR__ . "/downloadcount.txt");
echo $content;
$output = (int) $content + 1;
// by default `file_put_contents` overwrites file content
file_put_contents(__DIR__ . "/downloadcount.txt", $output);
That appending should just be a typecasting problem, but I would not encourage you to handle counts the file way. In order to count the number of downloads for a file, it's better to make a database update of a row using transactions to handle concurrency properly, as doing it the file way could compromise accuracy.
You can get the content, check if the file has data. If not initialise to 0 and then just replace the content.
$fileContent = file_get_contents("downloadcount.txt");
$content = (!empty($fileContent) ? $fileContent : 0);
$content++;
file_put_contents('downloadcount.txt', $content);
Check $str or directly content inside the file

Delete content stored in an array from a log file in php

I extracted some data from a log file and put it in an array (lets call it $line_content). I copied the 1st 15 lines from the array into another array ($line_content15). I want to delete/remove these 15 lines from the log file. How should I do it? I tried to use str_replace, like in this code snippet:
file_put_contents($filename, str_replace($line_content15 . "\r\n", "",
file_get_contents($filename)));
Any input would be helpful. Thank you!
As #user3783243 commented, I needed to implode the array and use
array_splice($imploded_content,$initial_line_number,$last_line_number);.
In this case,
$initial_line_number=0 and $last_line_number=15.
Update:
I also need to change permission for PHP to access and modify the file.
Given that log files often get very large then trying to do this in memory is not a good solution. Also, given that log files are usually part of one's audit trail, they should not be modified. But assuming there is a valid reason.....
function delete_lines($fname, $startline, $endline)
{
$tmp=tmpfile();
$in=fopen($fname, 'r+');
if (!flock($in, LOCK_EX, $wouldblock) || $wouldblock) {
trigger_error("Unable to lock file");
return false;
}
for ($x=0; $x<$startline; $x++) {
fputs($tmp, fgets($in));
}
for ($x=0; $x<($endline-$startline); $x++) {
fgets($in);
}
while (!feof($in)) {
fputs($tmp, fgets($in));
}
fseek($tmp,0);
fseek($in, 0);
$newsize=0;
while (!feof($tmp)) {
$newsize+=fputs($in, fgets($tmp));
}
ftruncate($in,$newsize);
fclose($in);
fclose($tmp);
return true;
}
You may want to add additional error handling in the above. This can be implemented with a single open file - but it can become messy quickly.

Cut string and gunzip using PHP

I have a router config export file, which contains a header of 20 bytes, followed by zlib compressed data. Once uncompressed it should contain plain xml content.
My code strips the first 20 bytes, and decompresses the file. The exported data is still binary. I used file_get_contents and file_put_contents first, but assumed (wrongly) it wasn't binary-safe. I've tried to change the 20 in anything from 1-1000 without avail.
<?
$fp_orig = fopen('config.cfg', "rb");
$data_orig = fread($fp_orig, filesize('config.cfg'));
fclose($fp_orig);
$bytes = 20; // tried 1-1000
$data_gz = substr($data_orig,$bytes);
$fp_gz = fopen('config.cfg.gz', 'w');
fwrite($fp_gz, $data_gz);
fclose($fp_gz);
$fp_gz = gzopen('config.cfg.gz', 'rb');
$fp_xml = fopen('config.cfg.xml', 'wb');
while(!gzeof($fp_gz))
{
fwrite($fp_xml, gzread($fp_gz, 4096));
}
fclose($fp_xml);
gzclose($fp_gz);
echo file_get_contents('config.cfg.xml'); // gives binary data
?>
I'm not particularly looking for turnkey working code, but rather a push into the right direction.

Contingency plan for fopen error in php

I'm writing a PHP app that has a 'control panel' that writes a prefs file with certain variables. On every POST, if the file doesn't exist, it is created. If it does exist, it is unlinked and a new file is touched with the same filename and new variables. This file is then included on another page with displays content based on the variables inside it.
$file = "phpsettings.php";
if (!file_exists($file)) {
touch($file);
$handle = fopen ($file, 'r+');
$str = "<?php \$pref1 = \"$mypref\"; ?>";
} else {
unlink($file);
touch($file);
$handle = fopen ($file, 'r+');
$str = "<?php \$pref1 = \"$mypref\"; ?>";
}
fwrite ($handle, $str);
fclose ($handle);
Is this a safe way of writing preferences, provided this file will be overwritten many times per day? What is a good way of both alerting the user of this control panel if the file wasn't saved correctly, and in that case, what would be a good contingency plan to avoid breaking the page this prefs file is included on short of defining a default set of variables to fill if !(file_exists)?
If you store your settings in an array, you can serialize() them and write to a text file, rather than writing raw php to a php file and including it.
If you're not sanitising your input for those preferences, and say $mypref1 represents someone's name, there's nothing stopping them from filling this out in the form field:
\"; echo \"PWNED
and your resulting PHP will become
<?php \$pref1 = \"$mypref\"; echo \"PWNED\"; ?>
So firstly, storing your preferences in an array and using serialize() is much safer:
$prefs = array('mypref1' => 'somethingorother');
$handle = fopen ($file, 'w');
fwrite($handle, serialize($prefs));
fclose($h);
// example code demonstrating unserialization
$prefs2 = unserialize(file_get_contents($file));
var_dump($prefs == $prefs2); // should output "(bool) true"
In your question, you also mention that if the file does exist, it is unlinked. You can simply truncate it to zero length by passing "w" as the second argument to fopen - you don't need to manually delete it. This should set the mtime anyway, negating the need for the call to touch().
If the values being written to the file are preferences, surely each preference could have a default, unless there are hundreds? array_merge will allow you to overwrite on a per-key basis, so if you do something like this:
// array of defaults
$prefs = array(
'mypref1' => 'pants',
'mypref2' => 'socks',
);
if (file_exists($file)) {
// if this fails, an E_NOTICE is raised. are you checking your server error
// logs regularly?
if ($userprefs = unserialize(file_get_contents($file))) {
$prefs = array_merge($prefs, $userprefs);
}
}
If the issue is that there are heaps, and you don't want to have to initialise them all, you could have a get_preference method which just wraps an isset call to the prefs array.
function get_preference($name, &$prefs) {
if (isset($pref[$name]))
return $pref[$name];
return null;
}
var_dump(get_preference('mypref1', $prefs));
Beyond all of the questions this raises though, the reality is that with your app, in the unlikely event that something does go wrong with the fopen, it should be regarded as a serious failure anyway, and the handful of users you're likely to have making use of this feature are going to be contacting you pretty darn quick if something goes wrong.
It is always better to store your users state in a session and only persist that state when needed.
Why not just use the truncation capabilities of fopen()? I believe instead of "r+", you'll need to pass "w+"... Then if the file exists, it will be truncated, if it doesn't you'll just create a new file. So the code becomes:
$file = "phpsettings.php";
$handle = fopen( $file, 'w+' );
$str = "<?php \$pref1 = \"$mypref\"; ?>";
fwrite ($handle, $str);
fclose ($handle);

Categories