I'm really new to php. I decided to make a counter based off a script I've seen. I've made changes to it. I'm trying to figure out how to reset the counter.
$userCount = file_get_contents("count.txt");
$userCount = trim($userCount);
$userCount = $userCount + 1;
$countReset = $userCount - $userCount;
$file = fopen("count.txt","w+");
fwrite($file,$userCount);
fclose($file);
print "The number of visitors is: $userCount";
if ($userCount < 20){
echo 'not yet';
}
else {
echo 'done!';
}
if ($userCount > 40){
fwrite($file,$countReset);
fclose($file);
}
I tried subtracting the counter from itself to get it back to 0.
$countReset = $userCount - $userCount;
However, it doesn't seem to work. the counter itself works, but I am unable to get it to reset back to 0.
This is just an impression like script I'm doing as a way to learn php. Also, sorry about the format, struggling with this post editor.
Any help with the script?
You've closed the file before trying to write to it again. Before your second fwrite, add:
$file = fopen("count.txt","w+");
Try to simply set value 0 to the var:
$countReset = 0;
Couldn't you just edit the count.txt file?
Doing it in PHP, you could do
fwrite($file,'0');
EDIT: Like CanSpice said, you shouldn't close the file before you're done with it. Remove the first fclose, and it should work.
I wouldn't mix fopen() and file_get_content() functions in this context, either use fopen(), fread() and fwrite() or use file_get_contents() and file_put_contents().
If you need just reset counter and you don't need previous value, than use:
file_put_contents('count.txt', '0');
If you need update value you may use either:
$count = file_get_contents( 'count.txt');
$count++;
// Reset?
if( $reset){
$count = 0;
}
file_put_contents( 'count.txt', "$count");
Or rather:
$fp = fopen( 'count.txt', 'r+') or die( 'Cannot use counter');
$count = trim( fread( $fp, 1024));
$count++;
// Reset?
if( $reset){
$count = 0;
}
ftruncate( $fp, 0);
fseek( 0, SEEK_SET)
fwrite( $fp, "$count");
fclose( $fp);
Here are the manual pages for ftruncate() and fseek() + you should probably study flock() so two scripts wouldn't overwrite the content at the same time.
/****************************************************************************
* read the current count from the counter file, increment
* it by 1, and re-save the new value off to the file
***************************************************************************/
function getAndIncrementCount(){
// attempt to open the file
// a+ means keep the contents so we can read it at least once,
// but allow us to overwrite the value once we increment it
if (($fHandle = fopen('count.txt','a+')) !== FALSE){
// read in the count (also cast to an int so if there is
// an invalid (or absent) value it will default to a 0
$count = (int) fread($fHandle, 100);
// increase the counter
$count++;
// go back to the beginning of the file
fseek($fHandle, 0);
// re-write the new count back to the file
fwrite($fHandle, $count);
// cose the file now that we're done with it
fclose($fHandle);
// return back the count
return $count;
}
// we couldn't get to the file, so return an error flag
return FALSE;
}
/****************************************************************************
* write the specified value to the counter file
***************************************************************************/
function setCount($count = 0){
// attempt to open the file with over-write permissions
// w+ will open the file and clear it
if (($fHandle = fopen('count.txt','w+')){
// write the counter to the file
fwrite($fHandle, $count);
// close the file now that we're done
fclose($fHandle);
// return the newly saved count
return $count;
}
// we couldn't get to the file, so return an error flag
return FALSE;
}
And applied in practice:
$userCount = getAndIncrementCount();
echo "The number of visitors is: {$userCount}";
if ($userCount < 20){
echo "Not Yet";
}else{
echo "Done!";
}
if ($userCount > 40){
setCount(0);
}
It's because you are not rewriting the file contents but adding to them when you use the fwrite second time, so $countReset gets appended to the content already in the file. Try this:
$userCount = file_get_contents("count.txt");
$userCount = $userCount + 1;
$countReset = $userCount - $userCount;
file_put_contents("count.txt", $userCount);
print "The number of visitors is: $userCount\n";
if ($userCount < 20){
echo 'not yet';
}
else {
echo 'done!';
}
if ($userCount > 40){
file_put_contents("count.txt", $countReset);
}
Related
I developed a very simple counter in PHP. It works as expected but occasionally it resets to zero. No idea why. I suspect it could be related to concurrent visitors but I have no idea how to prevent that in case I am correct. Here is the code:
function updateCounter($logfile) {
$count = (int)file_get_contents($logfile);
$file = fopen($logfile, 'w');
if (flock($file, LOCK_EX)) {
$count++ ;
fwrite($file, $count);
flock($file, LOCK_UN);
}
fclose($file);
return number_format((float)$count, 0, ',', '.') ;
}
Thank you in advance.
file_get_contents on a locked file will probably get a "false" (== 0) and the logfile is probably unlocked again, when it comes to writing.
A classic race condition...
As file_get_contents() can return false accessing a previously locked file, the consequent fwrite() may write a zero or 1, resetting our counter to zero.
So we try to read the counter file after the locking has been succeeded for us.
function updateCounter($logfile) {
//$count = (int)file_get_contents($logfile);
if(file_exists($logfile)) {
$mode = 'r+';
} else {
$mode = 'w+';
}
//
$file = fopen($logfile, $mode);
//
if (flock($file, LOCK_EX)) {
//
// read counter file:
//
$count = (int) fgets($file);
$count++ ;
//
// point to the beginning of the file:
//
rewind($file);
fwrite($file, $count);
flock($file, LOCK_UN);
}
fclose($file);
return number_format((float)$count, 0, ',', '.') ;
}
//
$logfile = "counter.log";
echo updateCounter($logfile);
Please see usernotes on https://www.php.net/manual/en/function.flock.php .
I would append a character into the file and use strlen on the file contents to get the hits. Please note that your file will get big overtime but this can be easily solved with a cronjob that sums it up and cache it into another readonly file.
You can also use !is_writeable and check if its locked and if so you can miss the hit or wait with a while loop until its writable. Tricky but it works. It depends how valuable each hit will be and how much effort you would like to invest in this counter.
I managed to make this PHP counter without database, it is very basic as it increments the visits in a .txt file:
$counter_file = ("count.txt");
$fp = fopen($counter_file, "r");
$count = fread($fp, 1024);
fclose($fp);
$count = $count +1;
$fp = fopen($counter_file, "w");
fwrite($fp, $count);
fclose($fp);
But this counter fails on a distant server, when the visits are too fast. It goes back to 0.
What can explain this behaviour and how to make sure the counter will never go back to 0?
Edit: This script seams to be more robust. It uses flock as #ghopst suggested.
$counter_file = ("count.txt");
$handle = fopen($counter_file,"r+");
//Lock File, error if unable to lock
if(flock($handle, LOCK_EX)) {
$count = fread($handle, filesize($counter_file));
$count = $count + 1;
ftruncate($handle, 0);
rewind($handle);
fwrite($handle, $count);
flock($handle, LOCK_UN);
} else {
echo "Could not Lock File!";
}
fclose($handle);
It's down to the file system, your code makes a request to open, read, close, open, write and close the file for each visitor. If the file is being written it is locked against being written to by another instance, it's a behavior of the file system. Perhaps it would be better to have a simple database table with a autoincrement column and just insert a row for each visit then delete it , then you could just select the top row to return a value.
Try this version:
<?php
$counter_file = ("count.txt");
$count = #file_get_contents($counter_file);
$count = $count ? intval($count) + 1 : 1;
file_put_contents($counter_file, $count);
Text file in question is named fp.txt and contains 01, 02, 03, 04, 05, ...10 on each line.
01
02
...
10
Code:
<?php
//test file for testing fseek etc
$file = "fp.txt";
$fp = fopen($file, "r+") or die("Couldn't open ".$file);
$count = 0;
while(!(feof($fp))){ // till the end of file
$text = fgets($fp, 1024);
$count++;
$dice = rand(1,2); // just to make/alter the if condition randomly
echo "Dice=".$dice." Count=".$count." Text=".$text."<br />";
if ($dice == 1){
fseek($fp, -1024, SEEK_CUR);
}
}
fclose($fp);
?>
So, because of fseek($fp, -1024, SEEK_CUR); is not working properly. What I want is that If Dice == 1, set the file-pointer to previous line i.e. one line up than the current one. But I think negative value is setting the file pointer to end of file, and thus ending the while loop before the actual end of file.
Desired Output is:
Dice=2 Count=1 Text=01
Dice=2 Count=2 Text=02
Dice=2 Count=3 Text=03
Dice=1 Count=4 Text=03
Dice=2 Count=5 Text=04
Dice=2 Count=6 Text=05
Dice=2 Count=7 Text=06
Dice=1 Count=8 Text=06
Dice=1 Count=9 Text=06
Dice=2 Count=10 Text=07
.... //and so on until Text is 10 (Last Line)
Dice=2 Count=n Text=10
Note that whenever dice is 2, text is same as previous one. Now it is just stopping at the first occurrence of Dice=1
So basically my question is How to move/relocate file-pointer to previous line?
Please note that dice=rand(1,2) is just for example. In the actual code, $text is a string and if condition is to be true when string does not contain a particular text.
EDIT:
Solved, both samples (#hakre 's & mine) are working as desired.
You read out a line from the file, but only forward to the next line when the dice is not 1.
Consider to use the SplFileObject for that, which offers an interface that is better for your scenario I'd say:
$file = new SplFileObject("fp.txt");
$count = 0;
$file->rewind();
while ($file->valid())
{
$count++;
$text = $file->current();
$dice = rand(1,2); // just to make alter the if condition randomly
echo "Dice=".$dice." Count=".$count." Text=".$text."<br />";
if ($dice != 1)
{
$file->next();
}
}
<?php
$file = "fp.txt";
$fp = fopen($file, "r+") or die("Couldn't open ".$file);
$eof = FALSE; //end of file status
$count = 0;
while(!(feof($fp))){ // till the end of file
$current = ftell($fp);
$text = fgets($fp, 1024);
$count++;
$dice = rand(1,2); // just to alter the if condition randomly
if ($dice == 2){
fseek($fp, $current, SEEK_SET);
}
echo "Dice=".$dice." Count=".$count." Text=".$text."<br />";
}
fclose($fp);
?>
This sample is also working as required.
The changes are:
* Addition of "$current = ftell($fp);" after while loop.
* Modification of fseek line in if condition.
* checking for dice==2 instead of dice==1
$done=0;
$filename = "raw_urls.txt";
if(! ($fhandle = fopen($filename, "r")))
{ echo "File failed to open";
Exit; }
//
// main loop reads sitemap url list
//
while($url_full_raw = fgets($fhandle,4096))
{
print (mysql_error());
$url_full= preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $url_full_raw);
if(strlen($url_full) > 3)
{
$url_stat++;
// Echo ' tp1 Url from sitemap:',$url_stat,' - ' ,$url_full,'<br>';
$end_st = strlen($url_full)-29;
$s_url= substr($url_full,29,$end_st);
//Echo 'short:',$s_url,'<br>';
$url_full_raw= '';
}
else{
$done++;
Echo '----------- short string ---------------';
}
//
// Check for url
//
$res1=sql("SELECT * FROM `spy3` WHERE `Landingpage` LIKE '%$s_url%' LIMIT 0, 30 ",$o);
if($row=mysql_fetch_array($res1))
{
$lp=$row[6];
$found++;
// Echo '------->Url from sitemap:',$url_full,'<br>';
}
else{
Echo 'Not Found:-> ',$s_url,'<br>';
$nfound++;
}
sql("insert into sitemap (url, stat_url,nf, s_recno)
values (
'$url_full',
'$lp',
'$nfound',
'$url_stat'
)", $o);
print (mysql_error());
$found=0;
$nfound=0;
}
So the code works great. Except for one problem, after about 130 lines, it stops. It exits the program with no error. Yes full error reporting is on. PHP.ini memory is huge.
If I edit the txt file and take out some lines, no difference. I have been working on this for many hours.
Try doing it like they do in their example... with !== false. i.e,
while(($url_full_raw = fgets($fhandle,4096))!==false) {
I'm guessing your content is evaluating to false for whatever reason. That just happens to be at 130 lines (throw the 130 lines into a text file and see if the file size is close to 4 KB).
Also, you might want to fix your formatting for next time. Makes it very hard for us to read and help you.
I had asked a question earlier( How to keep this counter from reseting at 100,000? ), and now have a follow-up question.
I have another version of the counter in question that can be told to reset at a certain number, and I would like to make sure that this second version does not have the same problem as the first.
What I have coded now is:
$reset = '10';
$filename4 = "$some_variable/$filename3.txt";
// Open our file in append-or-create mode.
$fh = fopen($filename4, "a+");
if (!$fh)
die("unable to create file");
if ($reset == 'default'){
// Before doing anything else, get an exclusive lock on the file.
// This will prevent anybody else from reading or writing to it.
flock($fh, LOCK_EX);
// Place the pointer at the start of the file.
fseek($fh, 0);
// Read one line from the file, then increment the number.
// There should only ever be one line.
$current = 1 + intval(trim(fgets($fh)));
// Now we can reset the pointer again, and truncate the file to zero length.
fseek($fh, 0);
ftruncate($fh, 0);
// Now we can write out our line.
fwrite($fh, $current . "\n");
// And we're done. Closing the file will also release the lock.
fclose($fh);
}
else {
$current = trim(file_get_contents($filename4)) + 1;
if($current >= $reset) {
$new = '0';
fwrite(fopen($filename4, 'w'), $new);
}
else {
fwrite(fopen($filec, 'w'), $current);
}
}
echo $current;
I did not want to assume I know what changes to make to this code, so I post another question. EDIT- What changes should I make here to avoid not getting an exclusive lock on the file if $reset is not equal to default? What is the correct way to code this? Would this work?:
$filename4 = "$some_variable/$filename3.txt";
// Open our file in append-or-create mode.
$fh = fopen($filename4, "a+");
if (!$fh)
die("unable to create file");
// Before doing anything else, get an exclusive lock on the file.
// This will prevent anybody else from reading or writing to it.
flock($fh, LOCK_EX);
// Place the pointer at the start of the file.
fseek($fh, 0);
if ($reset == 'default'){
// Read one line from the file, then increment the number.
// There should only ever be one line.
$current = 1 + intval(trim(fgets($fh)));
} else {
// Read one line from the file, then increment the number.
// There should only ever be one line.
$current = 1 + intval(trim(fgets($fh)));
if($current >= $reset) {
$current = '0';
}
else {
// Read one line from the file, then increment the number.
// There should only ever be one line.
$current = 1 + intval(trim(fgets($fh)));
}
}
// Now we can reset the pointer again, and truncate the file to zero length.
fseek($fh, 0);
ftruncate($fh, 0);
// Now we can write out our line.
fwrite($fh, $current . "\n");
// And we're done. Closing the file will also release the lock.
fclose($fh);
echo $current;
EDIT - This seems to be working for me:
$reset = "default";
$filename4 = "counter.txt";
// Open our file in append-or-create mode.
$fh = fopen($filename4, "a+");
if (!$fh)
die("unable to create file");
// Before doing anything else, get an exclusive lock on the file.
// This will prevent anybody else from reading or writing to it.
flock($fh, LOCK_EX);
// Place the pointer at the start of the file.
fseek($fh, 0);
// Read one line from the file, then increment the number.
// There should only ever be one line.
$current = 1 + intval(trim(fgets($fh)));
if ($reset == 'default'){
$new = $current;
} else {
if($current >= ($reset + '1')) {
$new = '1';
}
else {
$new = $current;
}
}
// Now we can reset the pointer again, and truncate the file to zero length.
fseek($fh, 0);
ftruncate($fh, 0);
// Now we can write out our line.
fwrite($fh, $new . "\n");
// And we're done. Closing the file will also release the lock.
fclose($fh);
echo $new;
Does this look right?
if($current >= $reset) {
// here is where you are setting the counter back to zero. comment out
// these lines.
//$new = '0';
//fwrite(fopen($filename4, 'w'), $new);
}
If you simply want a counter that doesn't get reset, try:
$filename4 = "counter.txt";
// Open our file in append-or-create mode.
$fh = fopen($filename4, "a+");
if (!$fh)
die("unable to create file");
// Before doing anything else, get an exclusive lock on the file.
// This will prevent anybody else from reading or writing to it.
flock($fh, LOCK_EX);
// Place the pointer at the start of the file.
fseek($fh, 0);
// Read one line from the file to get current count.
// There should only ever be one line.
$current = intval(trim(fgets($fh)));
// Increment
$new = $current++;
// Now we can reset the pointer again, and truncate the file to zero length.
fseek($fh, 0);
ftruncate($fh, 0);
// Now we can write out our line.
fwrite($fh, $new . "\n");
// And we're done. Closing the file will also release the lock.
fclose($fh);
echo $new;
The best way I can see to do this would be to open the file for reading with a lock other than exclusive. you can then perform your required checks and if the the count exceeds the $reset value, you can close the the file, open it again but this time with the exclusive lock for writing.
Another way would simply not to use an exclusive lock.
You could look into very good flatfile classes out there which have tested locking mechanisms.
file_put_contents is already atomic. There is no need for ten lines of file locking code.
<?php
$fn = "$filename3.txt";
$reset = 0; // 0 is equivalent to "default"
//$reset = 10000000;
$count = file_get_contents($fn);
$count = ($reset && ($count >= $reset)) ? (0) : ($count + 1);
file_put_contents($fn, $count, LOCK_EX);
echo $count;
No idea if this is any help, since your question is still opaque. I will not answer comments.