7

I see people using flock like this:

if (!$fp = fopen($file_name, 'wb'))  
{  
    return FALSE;  
}  

if (flock($fp, LOCK_EX))  
{  
    fwrite($fp, serialize($data));  
    flock($fp, LOCK_UN);  
}

Also this:

if (!$fp = @fopen($file_name, 'rb'))  
{  
    return FALSE;  
}  

flock($fp, LOCK_SH);  

$data = '';  

if (filesize($file_name) > 0)  
{  
    $data = unserialize(fread($fp, filesize($file_name)));  
}  

But isn't there a chance that someone else will edit the file between the fopen call and the flock call? and the same question for fread


EDIT:
To clarify why I'm asking this... I'm basing my question on the code here, In a mysql caching situation, what's to stop 20 people from all being able to access the file at the same time if they all can get in between the fopen and flock?

Is that code foolproof?

pilcrow
  • 56,591
  • 13
  • 94
  • 135
qwertymk
  • 34,200
  • 28
  • 121
  • 184

2 Answers2

6

You ask:

isn't there a chance that someone else will edit the file between the fopen call and the flock call? and the same question for fread

Yes, no, maybe. Short answer: assume "yes" and act carefully.

Yes, in that traditional flock()-based locking is merely advisory, so other processes (or even the same process) are free to disregard the locks. In practice, this is not a problem, as flock() is used by well-behaved client code — you don't read until you get a LOCK_SH, and you don't write unless you've obtained a LOCK_EX — on application-specific files.

No, in that PHP's implementation of flock() may be mandatory on certain operating systems, per the documentation, which might also require support from the filesystem (e.g., as with the mand option under Linux). So, other processes could not disregard those locks.

Maybe, in that the streams subsystem in PHP 5 implements some locking bookkeeping beyond that provided by the operating system. This may, for example, prevent the same process (but not another) from disregarding its own otherwise advisory locks. The behavior might surprise some. Even so, this kind of locking would not be mandatory between unrelated processes.

For portability, just assume the weakest semantics (the "yes" above) and restrict flock()ing to well-behaved code on application-specific lockfiles chosen in advance.

pilcrow
  • 56,591
  • 13
  • 94
  • 135
  • In case that cache is used only by that particular script, nothing will happen. If someone will whack the file from, say, shell - sure, bad things will happen. – favoretti Aug 05 '12 at 20:59
  • Yes, but it is not the case that LOCK_EX, for example, somehow forces `fread` to "return nothing", as another comment stated. – pilcrow Aug 05 '12 at 21:30
  • If you read the comments on the PHP's site to flock(), it actually does, despite the fact that locking is advisory. That being said, mandatory locking on, for instance, Linux can only be achieved while working with filesystems that explicitly support it and are mounted as such. – favoretti Aug 05 '12 at 21:35
  • Cool, an upvote for your answer, much more elaborate than mine :) – favoretti Aug 06 '12 at 06:45
2

First snippet is fool-proof, if you can't get a lock on the file, you don't write. If someone else edited the file between fopen() and flock(), your file handle will point to the latest incarnation, since fopen() binds to a stream, rather than to a 'snapshot'.

Second example is not guaranteed to work, because the return value of flock() is not checked, so if you haven't acquired the lock, the subsequent code will be executed anyway.

[edit] removed statement that reader lock doesn't matter, it actually does, as explained in comments below :)

favoretti
  • 29,299
  • 4
  • 48
  • 61
  • I'm basing my question on the code [**here,**](http://www.jongales.com/blog/2009/02/18/simple-file-based-php-cache-class/) In a mysql caching situation, what's to stop 20 people from all being able to access the file at the same time in that case based on the code in that class? – qwertymk Aug 05 '12 at 20:38
  • For reading - nothing, which is most likely what you want, concurrent cache read access, rather than exclusive one. For writing, exactly that acquired lock will stop other people from writing to the cache. What's exactly bothering you with that code? – favoretti Aug 05 '12 at 20:41
  • I guess a minor issue, that if 100,000 people all try to set the same cache file and 20,000 get called between the fopen and flock, there will be 19,999 unneeded fwrites, also some of those people will have done a check to see if the cache is fresh and will have missed the new filetime that it gets. Is that even a problem? – qwertymk Aug 05 '12 at 20:45
  • The only problem with the code in your example is that it doesn't check whether shared lock was acquired. If there were writes between fopen() and acquisition of a shared reader lock (that will lock file for writing), subsequent fread() will return the latest state of the file, because file handle is a reference to a file stream. While you're doing a locked read, there can be no writes, as flock() in the write section won't be able to acquire writing lock. – favoretti Aug 05 '12 at 20:50
  • Also, if file was locked for writing while you tried to read, fread() will return nothing. That's why you want to check whether shared lock acquisition also succeeded. – favoretti Aug 05 '12 at 20:52
  • So there's nothing with the technique in the code in the link? – qwertymk Aug 05 '12 at 20:56
  • Aside of what I mentioned, nothing that I can see that can go wrong. – favoretti Aug 05 '12 at 20:58