PHP Increment a counting variable in a text file - php

This seems simple but I can't figure it out.
file_get_contents('count.txt');
$variable_from_file++;
file_put_contents('count.txt', $variable_from_file);
There is only one line of data in count.txt and it is the hits counter. Is there a way to do this?

If you want to be sure no increments go uncounted (which is what CodeCaster is referring to, the script may load count.txt, increment it, while another file is doing the same, then save that, and then only one increment would have been done and not the proper two), you should use fopen.
$fp = fopen('count.txt', 'c+');
flock($fp, LOCK_EX);
$count = (int)fread($fp, filesize('count.txt'));
ftruncate($fp, 0);
fseek($fp, 0);
fwrite($fp, $count + 1);
flock($fp, LOCK_UN);
fclose($fp);
This will lock the file, preventing any others from reading or writing to it while the count is incremented (meaning others would have to wait before they can increment the value).

There is a slightly funnier way:
file_put_contents("count.txt",#file_get_contents("count.txt")+1);
file_get_contents reads the contents of the counter file.
# tells PHP to ignore the error of a missing file. The returned false will then be interpreted as the count of 0.
+1 will cause the string to be converted to a number.
file_put_contents then stores the new value in the counter file as a string.
On a very busy system you might want to obtain a file lock first to prevent simultaneous writes.
The OS file cache usually makes this method extremely fast.

$variable_from_file = (int)file_get_contents('count.txt');
But notice that this is not thread-safe.

Exactly like you did it should work fine. Just capture the data from file_get_contents(), and check if both of those functions were successful.
$var = file_get_contents('count.txt');
if ($var === false) {
die('Some error message.');
}
$var++;
if (file_put_contents('count.txt', $var) === false) {
die('Some error message.');
}

This works for me though
$count = intval(file_get_contents('count.txt'));
file_put_contents('count.txt', ++$count);
echo file_get_contents('count.txt');

Related

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);

Overwriting Text in a File When It's Already Open

I have a file that I'm opening for writing, like this:
if (!$idHandle = fopen($fileName, 'w')) {
echo "Cannot open file ($fileName)";
die();
}
I then enter a loop where I'm incrementing a variable, $beerId, during each iteration. I want to save that variable to the file I have open, so I use the following code:
if (fwrite($idHandle, $beerId) === FALSE) {
echo "Cannot write to file ($fileName)";
die();
}
$beerId++;
However, this ends up creating a massive string of every beerId I encounter. What I want is to have the file populated ONLY with the last id I left off on.
I realize I could put the write outside of the loop, but the script is volatile and likely to terminate prematurely with an error, so I want to have a reference to the last $beerId variable even in the event of an error that terminates the script before the loop is properly terminated.
You must go back to the beginning of the file because fwrite keeps track of where it is in the file. Use fseek. Opening and closing the file several times in a loop is expensive and I don't see a reason to do that in this case. You should of course close the file when you're done with it.
You should add this just before you write to the file:
fseek($idHandle, 0);
That will move you to the beginning of the file, since your incrementing values you won't have to worry about removing the previous value.
EDIT
In my answer above i assume that the id's encountered are incremented values, but you don't say that so, if for example you encounter id=10, and then encounter id=1
the above would still result in 10 in the file, to handle that just add some padding to the string that you're writing using str_pad:
str_pad($value_to_write, 10); //or whatever value is reasonable.
If you can, try memcache->increment().
http://php.net/manual/en/memcache.increment.php
Use $memcache->add('beer_id', 0); to initialize it to zero. Then fetch $beer_id like $memcache->get('beer_id') for an initial sanity check, and then $memcache->increment('beer_id'); for the next $beer_id.
Else, stick to file_get_contents() and file_put_contents():
http://php.net/manual/en/function.file-get-contents.php
http://php.net/manual/en/function.file-put-contents.php

How to save memory when reading a file in Php?

I have a 200kb file, what I use in multiple pages, but on each page I need only 1-2 lines of that file so how I can read only these lines what I need if I know the line number?
For example if I need only the 10th line, I don`t want to load in memory all the lines, just the 10th line.
Sorry for my bad english!
Try SplFileObject
echo memory_get_usage(), PHP_EOL; // 333200
$file = new SplFileObject('bible.txt'); // 996kb
$file->seek(5000); // jump to line 5000 (zero-based)
echo $file->current(), PHP_EOL; // output current line
echo memory_get_usage(), PHP_EOL; // 342984 vs 3319864 when using file()
For outputting the current line, you can either use current() or just echo $file. I find it clearer to use the method though. You can also use fgets(), but that would get the next line.
Of course, you only need the middle three lines. I've added the memory_get_usage calls just to prove this approach does eat almost no memory.
Unless you know the offset of the line, you will need to read every line up to that point. You can just throw away the old lines (that you don't want) by looping through the file with something like fgets(). (EDIT: Rather than fgets(), I would suggest #Gordon's solution)
Possibly a better solution would be to use a database, as the database engine will do the grunt work of storing the strings and allow you to (very efficiently) get a certain "line" (It wouldn't be a line but a record with an numeric ID, however it amounts to the same thing) without having to read the records before it.
Do the contents of the file change? If it's static, or relatively static, you can build a list of offsets where you want to read your data. For instance, if the file changes once a year, but you read it hundreds of times a day, then you can pre-compute the offsets of the lines you want and jump to them directly like this:
$offsets = array();
while ($line = fread($filehandle)) { .... find line 10 .... }
$offsets[10] = ftell($filehandle); // store line 10's location
.... find next line
$offsets[20] = ftell($filehandle);
and so on. Afterwards, you can trivially jump to that line's location like this:
$fh = fopen('file.txt', 'rb');
fseek($fh, $offsets[20]); // jump to line 20
But this could entirely be overkill. Try benchmarking the operations - compare how long it takes to do an oldfashioned "read 20 lines" versus precompute/jump.
<?php
$lines = array(1, 2, 10);
$handle = #fopen("/tmp/inputfile.txt", "r");
if ($handle) {
$i = 0;
while (!feof($handle)) {
$line = stream_get_line($handle, 1000000, "\n");
if (in_array($i, $lines)) {
echo $line;
$line = ''; // Don't forget to clean the buffer!
}
if ($i > end($lines)) {
break;
}
$i++;
}
fclose($handle);
}
?>
Just loop through them without storing, e.g.
$i = 1;
$file = fopen('file.txt', 'r');
while (!feof($file)) {
$line = fgets($file); // this gets whole line from the file;
if ($i == 10) {
break; // break on tenth line
}
$i ++;
}
The above example would keep memory for only the last line it got from the file, so this is the most memory efficient way to do it.
use fgets(). 10 times :-) in this case you will not store all 10 lines in the memory
Why are you only trying to load the first ten lines? Do you know that loading all those lines is in fact a problem?
If you haven't measured, then you don't know that it's a problem. Don't waste your time optimizing for non-problems. Chances are that any performance change you'll have in not loading the entire 200K file will be imperceptible, unless you know for a fact that loading that file is indeed a bottleneck.

Efficient flat file searching in PHP

I'd like to store 0 to ~5000 IP addresses in a plain text file, with an unrelated header at the top. Something like this:
Unrelated data
Unrelated data
----SEPARATOR----
1.2.3.4
5.6.7.8
9.1.2.3
Now I'd like to find if '5.6.7.8' is in that text file using PHP. I've only ever loaded an entire file and processed it in memory, but I wondered if there was a more efficient way of searching a text file in PHP. I only need a true/false if it's there.
Could anyone shed any light? Or would I be stuck with loading in the whole file first?
Thanks in advance!
5000 isn't a lot of records. You could easily do this:
$addresses = explode("\n", file_get_contents('filename.txt'));
and search it manually and it'll be quick.
If you were storing a lot more I would suggest storing them in a database, which is designed for that kind of thing. But for 5000 I think the full load plus brute force search is fine.
Don't optimize a problem until you have a problem. There's no point needlessly overcomplicating your solution.
I'm not sure if perl's command line tool needs to load the whole file to handle it, but you could do something similar to this:
<?php
...
$result = system("perl -p -i -e '5\.6\.7\.8' yourfile.txt");
if ($result)
....
else
....
...
?>
Another option would be to store the IP's in separate files based on the first or second group:
# 1.2.txt
1.2.3.4
1.2.3.5
1.2.3.6
...
# 5.6.txt
5.6.7.8
5.6.7.9
5.6.7.10
...
... etc.
That way you wouldn't necessarily have to worry about the files being so large you incur a performance penalty by loading the whole file into memory.
You could shell out and grep for it.
You might try fgets()
It reads a file line by line. I'm not sure how much more efficient this is though. I'm guessing that if the IP was towards the top of the file it would be more efficient and if the IP was towards the bottom it would be less efficient than just reading in the whole file.
You could use the GREP command with backticks in your on a Linux server. Something like:
$searchFor = '5.6.7.8';
$file = '/path/to/file.txt';
$grepCmd = `grep $searchFor $file`;
echo $grepCmd;
I haven't tested this personally, but there is a snippet of code in the PHP manual that is written for large file parsing:
http://www.php.net/manual/en/function.fgets.php#59393
//File to be opened
$file = "huge.file";
//Open file (DON'T USE a+ pointer will be wrong!)
$fp = fopen($file, 'r');
//Read 16meg chunks
$read = 16777216;
//\n Marker
$part = 0;
while(!feof($fp)) {
$rbuf = fread($fp, $read);
for($i=$read;$i > 0 || $n == chr(10);$i--) {
$n=substr($rbuf, $i, 1);
if($n == chr(10))break;
//If we are at the end of the file, just grab the rest and stop loop
elseif(feof($fp)) {
$i = $read;
$buf = substr($rbuf, 0, $i+1);
break;
}
}
//This is the buffer we want to do stuff with, maybe thow to a function?
$buf = substr($rbuf, 0, $i+1);
//Point marker back to last \n point
$part = ftell($fp)-($read-($i+1));
fseek($fp, $part);
}
fclose($fp);
The snippet was written by the original author: hackajar yahoo com
are you trying to compare the current IP with the text files listed IP's? the unrelated data wouldnt match anyway.
so just use strpos on the on the full file contents (file_get_contents).
<?php
$file = file_get_contents('data.txt');
$pos = strpos($file, $_SERVER['REMOTE_ADDR']);
if($pos === false) {
echo "no match for $_SERVER[REMOTE_ADDR]";
}
else {
echo "match for $_SERVER[REMOTE_ADDR]!";
}
?>

Blocking file-read in php?

I want to read everything from a textfile and echo it. But there might be more lines written to the text-file while I'm reading so I don't want the script to exit when it has reached the end of the file, instead I wan't it to wait forever for more lines. Is this possible in php?
this is just a guess, but try to pass through (passthru) a "tail -f" output.
but you will need to find a way to flush() your buffer.
IMHO a much nicer solution would be to build a ajax site.
read the contents of the file in to an array. store the number of lines in the session. print the content of the file.
start an ajax request every x seconds to a script which checks the file, if the line count is greater then the session count append the result to the page.
you could use popen() inststed:
$f = popen("tail -f /where/ever/your/file/is 2>&1", 'r');
while(!feof($f)) {
$buffer = fgets($f);
echo "$buffer\n";
flush();
sleep(1);
}
pclose($f)
the sleep is important, without it you will have 100% CPU time.
In fact, when you "echo" it, it goes to the buffer. So what you want is "appending" the new content if it's added while the browser is still receiving output. And this is not possible (but there are some approaches to this).
I solved it.
The trick was to use fopen and when eof is reached move the cursor to the previous position and continue reading from there.
<?php
$handle = fopen('text.txt', 'r');
$lastpos = 0;
while(true){
if (!feof($handle)){
echo fread($handle,8192);
flush();
$lastpos = ftell($handle);
}else{
fseek($handle,$lastpos);
}
}
?>
Still consumes pretty much cpu though, don't know how to solve that.
You may also use filemtime: you get latest modification timestamp, send the output and at the end compare again the stored filemtime with the current one.
Anyway, if you want the script go at the same time that the browser (or client), you should send the output using chunks (fread, flush), then check any changes at the end. If there are any changes, re-open the file and read from the latest position (you can get the position outside of the loop of while(!feof())).

Categories