PHP making blank file when I try to increment my variable? - php

I recently made a PHP program to count how many users are online using a program I created. It works like this:
Client sends register request to server (registerMember.php)
Server checks if the user is already online
If the user isn't already online, increment a number in a file
Write the persons username to a text file
My problem is that a lot of people would register in at the same time, and that would create a blank file.
$file_handle = fopen("registeredMembers", "r");
while (!feof($file_handle)) {
$line = fgets($file_handle);
$line++;
$fp = fopen('registeredMembers', 'w');
fwrite($fp, $line);
fclose($fp);
}
This is happening because there are two requests happening at the same time, and before the program writes the incremented version of the variable, another request has read a blank file before writing. Is there a better way I could be doing this? Any help would be greatly appreciated!

Try blocking the file with flock after you open it (see the example inside).

Related

How can I prevent a race condition when using a file based counter in php?

I have a small web-page that delivers different content to a user based on a %3 (modulo 3) of a counter. The counter is read in from a file with php, at which point the counter is incremented and written back into the file over the old value.
I am trying to get an even distribution of the content in question which is why I have this in place.
I am worried that if two users access the page at a similar time then they could either both be served the same data or that one might fail to increment the counter since the other is currently writing to the file.
I am fairly new to web-dev so I am not sure how to approach this without mutex's. I considered having only one open and close and doing all of the operations inside of it but I am trying to minimize time where in which a user could fail to access the file. (hence why the read and write are in separate opens)
What would be the best way to implement a sort of mutual exclusion so that only one person will access the file at a time and create a queue for access if multiple overlapping requests for the file come in? The primary goal is to preserve the ratio of the content that is being shown to users which involves keeping the counter consistent.
The code is as follows :
<?php
session_start();
$counterName = "<path/to/file>";
$file = fopen($counterName, "r");
$currCount = fread($file, filesize($counterName));
fclose($file);
$newCount = $currCount + 1;
$file = fopen($counterName,'w');
if(fwrite($file, $newCount) === FALSE){
echo "Could not write to the file";
}
fclose($file);
?>
Just in case anyone finds themselves with the same issue, I was able to fix the problem by adding in
flock($fp, LOCK_EX | LOCK_NB) before writing into the file as per the documentation for php's flock function. I have read that it is not the most reliable, but for what I am doing it was enough.
Documentation here for convenience.
https://www.php.net/manual/en/function.flock.php

php - How do you delete entry from .html file?

I have a file, online.html. The file is supposed to store the names of all the current users who are online on the site. The document might look something like this:
<p>python-b5</p>
<p>Other user</p>
They are added with code like this:
$fp = fopen("online.html", 'a');
fwrite($fp, "<p>" . $_SESSION ['name'] . "</p>");
fclose($fp);
When the user logs out, I would like to delete the entry in online.html for their user. However, I can't figure out how to accomplish it. How would I delete the entry?
The idea of using an HTML file as a database will have serious problems.
There are two big problems.
Making sure that only one process updates the file at any given time.
If more than one process updates the file at the same time, the file will get corrupted or it won't record all the updates.
Finding the right line to delete. You will need a way to unambiguously identify the line to delete.
Assuming you have some sort of user id, you could put that into the html.
<!-- BEGIN -->
<p data-id="1">Promethus</p>
<p data-id="2">Orpheus</p>
<p data-id="99">Tantalus</p>
<p data-id="11895">Marcus</p>
<!-- END -->
Delete the line.
// WARNING ! This function can ONLY be called by one process at a time.
function deleteLine($file, $id)
{
$tmpfile = $file . '.tmp';
$fp = fopen($file, 'r');
$fpout = fopen($tmpfile, 'w');
while ( $line = fgets($fp, 1000) ) {
// only write lines that are not the id we are searching for
if ( ! preg_match("/data-id=\"$id\"/", $line) {
fwrite($fpout, $line . "\n");
}
}
fclose($fp); fclose($fpout);
rename($tmpfile, $file);
}
why do u want to write the online status of users as a static file?
Normally you provide some kind of last_activity timestamp in your users table which can be updated on every request.
Then you can say every user that has the last_activity timestamp within the last 5-10 minutes is treated as online - otherwise he's offline.
If you really want to keep that static file structure you can use the DOMDocument php extension to parse the DOM of the static file in the page and find the entry you want to remove.

How to make flock block if other process locked the file?

I have a JavaScript application which posts messages to server. I have to gather those messages on server side and analyse them later, so I'm simply writing them to file. The problem is, when I open the file for reading, ie. in Notepad, messages are not being written. Since flock() is blocking and the locks should be mandatory on Windows, I expected the script to simply wait until I close the file and then save all pending messages, but it doesn't seem to work this way. Is there a way to make sure that all messages will finally be saved to the file, even if other process got exclusive access to it? I can't lose any message, even if someone opens the file for reading or copies it. Can I achieve it with PHP, or maybe I should rather send messages to database instead? PHP version is 7.0.4, my script looks like this:
$f = fopen('log.csv', 'a+');
flock($f, LOCK_EX);
$text = date('Y-m-d H:i:s'). ";" .htmlspecialchars($_POST["message"]). PHP_EOL;
clearstatcache();
fwrite($f, $text);
fflush($f);
flock($f, LOCK_UN);
fclose($f);
?>
flock returns true if successful or false if failed.
while(!flock($f, LOCK_EX)) {
sleep(5);
}
This won't fix the problem of your script timing out if another process has it locked for a long time. In that case, you might want to close the file and try opening a different file name.

fwrite to create new pages

I'm very new to php and so far in everything I have wanted to do I have been able to search the web for a solution. And after hours of reading what I could find and still not being able to figure it out I come to you..
Here is what I'm doing I'm creating a form where someone can upload some information and then that information populates in to an inventory page as long as well as there own page, and create a more in depth full page for each submission based on a template. All form information is stored in a mysql database.
I have gotten all if to work except the last piece. I'm trying to use fwrite to do this. I'll post what I have for that section. I do know that I'm missing some major steps here. Any guidance or a point in the right direction would be nice.
Thanks!
<?php
// For use in creating individual page
$tpl_file = "properties.php"; // template
$tpl_path = "pages/"; // where template is stored
$submissions_path = "new-pages/"; // where new file will be stored
$fp = fopen($submissions_path, "w");
fwrite($fp, $tpl_file);
fclose($fp);
?>
You're opening a directory using a file operation, which will not work. fopen() works on FILES, not directories.
Most likely what you're after is
$path = $submissions_path . $tpl_path . $tpl_file;
$fp = fopen($path, 'w') or die("Failed open $path for writing");
Note the added or die() business - it is always a good idea to verify that a file system operation succeeded (or failed) before you proceed. Assuming success will bite you in the rump at some point.

How to show random content every 15 minutes - php

Ok so i have a .txt file with a bunch of urls. I got a script that gets 1 of the lines randomly. I then included this into another page.
However I want the url to change every 15 minutes. So I'm guessing I'm gonna need to use a cron, however I'm not sure how I should put it all into place.
I found if you include a file, it's still going to give a random output so I'm guessing if I run the cron and the include file it's going to get messy.
So what I'm thinking is I have a script that randomly selects a url from my initial text file then it saves it to another .txt file and I include that file on the final page.
I just found this which is sort of in the right direction:
Include php code within echo from a random text
I'm not the best with writing php (can understand it perfectly) so all help is appreciated!
So what I'm thinking is I have a
script that randomly selects a url
from my initial text file then it
saves it to another .txt file and I
include that file on the final page.
That's pretty much what I would do.
To re-generate that file, though, you don't necessarily need a cron.
You could use the following idea :
If the file has been modified less that 15 minutes ago (which you can find out using filemtime() and comparing it with time())
then, use what in the file
else
re-generate the file, randomly choosing one URL from the big file
and use the newly generated file
This way, no need for a cron : the first user that arrives more than 15 minutes after the previous modification of the file will re-generate it, with a new URL.
Alright so I sorta solved my own question:
<?php
// load the file that contain thecode
$adfile = "urls.txt";
$ads = array();
// one line per code
$fh = fopen($adfile, "r");
while(!feof($fh)) {
$line = fgets($fh, 10240);
$line = trim($line);
if($line != "") {
$ads[] = $line;
}
}
// randomly pick an code
$num = count($ads);
$idx = rand(0, $num-1);
$f = fopen("output.txt", "w");
fwrite($f, $ads[$idx]);
fclose($f);
?>
However is there anyway I can delete the chosen line once it has been picked?

Categories