Read and write to a file while keeping lock - php

I am making a simple page load counter by storing the current count in a file. This is how I want to do this:
Lock the file (flock)
Read the current count (fread)
Increment it (++)
Write new count (fwrite)
Unlock file/close it (flock/fclose)
Can this be done without losing the lock?
As I understand it, the file can't be written to without losing the lock. The only way I have come up with to tackle this, is to write a character using "r+" mode, and then counting characters.

As said, you could use FLock. A simple example would be:
//Open the File Stream
$handle = fopen("file.txt","r+");
//Lock File, error if unable to lock
if(flock($handle, LOCK_EX)) {
$count = fread($handle, filesize("file.txt")); //Get Current Hit Count
$count = $count + 1; //Increment Hit Count by 1
ftruncate($handle, 0); //Truncate the file to 0
rewind($handle); //Set write pointer to beginning of file
fwrite($handle, $count); //Write the new Hit Count
flock($handle, LOCK_UN); //Unlock File
} else {
echo "Could not Lock File!";
}
//Close Stream
fclose($handle);

I believe you can achieve this using flock. Open a pointer to your file, flock it, read the data, write the data, then close (close automatically unlocks).
http://php.net/flock

Yes, you have to use rewind before ftruncate. Otherwise, the old content of the file is only refilled with zeros.
The working sequence is:
fopen
flock LOCK_EX
fread filesize
rewind
ftruncate 0
fwrite
flock LOCK_UN
fclose

Related

Is it possible to write to file without closing its reading state? PHP

I was curious to do a test. My question is if is it possible to open file for both reading and writing, so if I have more read-write operations to do on one file I do not need to close the reading status, read, open for write status, write and so on in a loop.
$filename = "test.txt";
$handle = fopen($filename, "rwb");
fseek( $handle , 15360 );
$contents = fread($handle, 51200);
$start = microtime (true);
fseek( $handle , 1 );
fwrite ( $handle , $contents );
fclose($handle);
This test does not work. I expected, I will read the data and move the fseek pointer to begin of the file either 1 or 0 position and then I will write the data. But this action failed for some reason with a result 0 (int) bytes written. Hence my question is, is it possible to do it? Or I need to close file for reading first?
As a related sub-question - is it possible that more users can read or write from files simultaneously from different position. As this should simulate database read/write operations. You know how mysql works - more users can write same table - same file any time. I know this is not problem in C/C++ but is it possible to do it in php?
You can create multiple file handlers on the same file. Just fopen() it twice, one with read only, the other with read/write. Although I'm not sure why you'd want to do so unless you're reading and writing from two different point in the file.
$filename = "test.txt";
$rw_handle = fopen($filename, "c+"); //open for read/write, allow fseek
$r_handle = fopen($filename, "r");
If you want to have multiple processes reading and writing a file from different locations, you'll want to file lock with flock()

Simple download counter sometimes resets to 0 [duplicate]

I am making a simple page load counter by storing the current count in a file. This is how I want to do this:
Lock the file (flock)
Read the current count (fread)
Increment it (++)
Write new count (fwrite)
Unlock file/close it (flock/fclose)
Can this be done without losing the lock?
As I understand it, the file can't be written to without losing the lock. The only way I have come up with to tackle this, is to write a character using "r+" mode, and then counting characters.
As said, you could use FLock. A simple example would be:
//Open the File Stream
$handle = fopen("file.txt","r+");
//Lock File, error if unable to lock
if(flock($handle, LOCK_EX)) {
$count = fread($handle, filesize("file.txt")); //Get Current Hit Count
$count = $count + 1; //Increment Hit Count by 1
ftruncate($handle, 0); //Truncate the file to 0
rewind($handle); //Set write pointer to beginning of file
fwrite($handle, $count); //Write the new Hit Count
flock($handle, LOCK_UN); //Unlock File
} else {
echo "Could not Lock File!";
}
//Close Stream
fclose($handle);
I believe you can achieve this using flock. Open a pointer to your file, flock it, read the data, write the data, then close (close automatically unlocks).
http://php.net/flock
Yes, you have to use rewind before ftruncate. Otherwise, the old content of the file is only refilled with zeros.
The working sequence is:
fopen
flock LOCK_EX
fread filesize
rewind
ftruncate 0
fwrite
flock LOCK_UN
fclose

php remove duplicate if same mobile phone number is in .csv file

I have a separate php script file that saves to file a number csv values via a html contact form.
I would like a maximum of 2 duplicate rows based on mobile phone entries in csv file,
any more and I would want the current record deleted.
I am using the $_GET()(no $_POST() functions) function to record all entries, and then save to file.
Im just having issues with deleting duplicates if the mobile number is already TWICE in the file.
Any help would be greatly appreciated.
**ADDED MORE CODE AND COMMENT BELOW**
I have edited the code, but I am still running into trouble with removing duplicates, let alone check for 2 dupes first. I will do the sanitize and better code 'after' I have some function (help!).
Thanks again for your help :)
<?php
$filename = "input.csv";
$csv_output .= "\n";$title=$_GET[title];$fname=$_GET[fname];
$sname=$_GET[sname];$notes=$_GET[notes];$mobile=$_GET[mobile];
$string="$title,$fname,$sname,$mobile,$notes,$csv_output";
$file = fopen($filename, "c");
// see details on the 'c' mode here http://us3.php.net/manual/en/function.fopen.php - it will create a file if it does not exist.
// Now acquire an exclusive via flock() - http://us3.php.net/manual/en/function.flock.php
flock($file, LOCK_EX); // this will block till some other reader/writer has released the lock.
$stat = fstat($file)
if($stat['size'] == 0) {
// file created for the first time
fwrite($file, "Title,First Name,Last Name,MobileNumber,Notes\n$string");
flock($file, LOCK_UN);
fclose($file);
return;
}
// File not empty - scan thru line by line via fgets(), and detect duplicates
// If a duplicate is detected, just flock($file, LOCK_UN), close the file and return - ///// no need to fwrite the line.
while (($buffer = fgets($file, 2188)) !== false) {
if(!stripos($buffer, ",$mobile,") {
$mobile .= $buffer;
}
else {
flock($file, LOCK_UN);
fclose($file);
return;
}
}
?>
Are you running this on a Linux/Unix system? If so, the way you have accessed the file will lead to race-conditions and possible corruption of the file.
You need to ensure that the write to the file is done in a serialized manner if multiple processes are attempting to write to the same file.
As you don't want to explore other alternatives like a db (even key-value file-based dbs), a pseudo-code approach is:
$file = fopen($filename, "c"); // see details on the 'c' mode here http://us3.php.net/manual/en/function.fopen.php - it will create a file if it does not exist.
// Now acquire a exclusive via flock() - http://us3.php.net/manual/en/function.flock.php
flock($file, LOCK_EX); // this will block till some other reader/writer has released the lock.
$stat = fstat($file)
if($stat['size'] == 0)
{
// file created for the first time
fwrite($file, "Title,First Name,Last Name,MobileNumber,Notes\n$string");
flock($file, LOCK_UN);
fclose($file);
return;
}
// File not empty - scan thru line by line via fgets(), and detect duplicates
// If a duplicate is detected, just flock($file, LOCK_UN), close the file and return - no need to fwrite the line.
// Otherwise fwrite() the line
.
.
flock($file, LOCK_UN);
fclose($file);
You can fill in the details in the middle part - hope you got the gist of it.
You could potentially make it more 'scalable' by initially grabbing a read lock (this will allow multiple readers to run concurrently, and only the writer will block). Once the read portion is done, you need to release the lock, and if a write needs to be done (i.e. no duplicates detected), then grab a writer lock etc...
Clearly this is not a ideal solution but if your file contents are going to be small, it may suffice.
Stating the obvious, you would need to do better error handling with all file-based operations.
A tangential point: you should also sanitize the data from $_GET before going to the core logic to catch for invalid inputs.
Hope this helps.

fopen(file,w+) truncates the file before I can check if it's locked with flock()

I have a function which receives a filename and a json object to write to a text file.
The object is updated and needs to entirely replace the current contents of the file. Each site visitor has their own file. Multiple rapid changes create a situation where the file is truncated by fopen(file,w+), then not written to as it's locked. End result is empty file.
I'm sure there's a standard simply way to do this as it's such a usual activity. Ideally what I'm looking for is a way to check if a file has a lock before truncating the file with fopen in w+ mode or a way to switch modes.
It seems strange that you would have to truncate the file with fopen() to get a file handle to pass to flock() to check if it's locked -- but you just truncated it, so what's the point?
Here's the function I have so far:
function updateFile($filename, $jsonFileData) {
$fp = fopen($filename,"w+");
if (flock($fp, LOCK_EX)) {
fwrite($fp, $jsonFileData);
flock($fp, LOCK_UN);
fclose($fp);
return true;
} else {
fclose($fp);
return false;
}
}
Example #1 from the PHP manual will do what you want with a slight modification. Use the "c" mode to open the file for writing, create it if it doesn't exist, and don't truncate it.
$fp = fopen("/tmp/lock.txt", "c");
if (flock($fp, LOCK_EX)) { // acquire an exclusive lock
ftruncate($fp, 0); // truncate file
fwrite($fp, "Write something here\n");
fflush($fp); // flush output before releasing the lock
flock($fp, LOCK_UN); // release the lock
} else {
echo "Couldn't get the lock!";
}
fclose($fp);
Full description of the "c" mode:
Open the file for writing. If the file does not exist, it is created. If it exists, it is neither truncated (as opposed to 'w'), nor the call to this function fails (as is the case with 'x'). The file pointer is positioned on the beginning of the file. This may be useful if it's desired to get an advisory lock (see flock()) before attempting to modify the file, as using 'w' could truncate the file before the lock was obtained (if truncation is desired, ftruncate() can be used after the lock is requested).
It doesn't look like you need it, but there's also a corresponding "c+" mode if you want to both read and write.

PHP writing to file without truncation

I am creating a flatfile database, and I am trying to solve the problem of multiple edits being made at the same time. I understand I need to truncate the file for editing and deleting rows but for adding rows this is not necessary.
So if I were to use fopen($file, 'a') to write to a file, and multiple people where to open the file and write to it, would they all be able to write to the file simultaneously?
Without truncating the file people shouldn't be overwriting each other right?
It's better to use some kind of helper for this.
PHP function flock (File LOCK)
//Open the File Stream
$handle = fopen($file,"a");
//Lock File, error if unable to lock
if(flock($handle, LOCK_EX)) {
$data;
// do anything to fill variable $data
fwrite($handle, $data); //Write the $data into file
flock($handle, LOCK_UN); //Unlock File
} else {
echo "Could not Lock File!";
}
//Close Stream
fclose($handle);
PHP file write threading issues
Read and write to a file while keeping lock

Categories