Best way to replace file on server - php

I have to write script in PHP which will be dynamicly replace some files on server from time to time. Easy thing, but the problem is that I want to avoid situation when user request this file during replacing. Then he could get uncompleted file or even error.
Best solution to me is block access to my site during replacing by e.g. setting .htaccess redirecting all requests to page with information about short break. But normally .htaccess file already exist, so there may be situation when server gets uncomplited .htaccess file.
Is there any way to solve it?
Edit: Thank you so much for all answers, guys. You are briliant.
#ircmaxell Your idea sounds great for me. I read what dudes from PHP.net wrote and I don't know if I understand all correctly.
So, tell me: If I do all steps you wrote and add apc.file_update_protection to my php.ini, there will be no way to get uncompleted file by user by any time? There will be always one, correct file? Are you sure at 100% ?
It is very important to me coz these replacements will be very often and there is big chance to request file during renaming.

Here's something that's easy, and will work on any local filesystem on linux:
Upload (or write) the file to a temporary filename
Move the file (using the mv (move) command, either in FTP, or command line, etc, or the rename command in PHP) to overwrite the existing one.
When you execute the mv command, it basically deletes the old file pointer, and writes the new one. Since it's being done at the filesystem level, it's an atomic operation. So the client can't get an old file...
APC recommends doing this to prevent these very issues from cropping up...
Also note that you could use rsync to do it as well (since it basically does this behind the scenes)...

Doesn't this work already? I never tested for this specifically but I've done what you're doing and that problem never showed up.
It seems like an easy thing for an operating system to
Upload / write to a temporary file
When writing is done, block access to the original file (make the request for the file wait)
Delete the file, rename the temporary one and remove any locks
I'm fairly sure this is what an OS should do for copying. If you're writing the file contents yourself with PHP you'll just have to do this yourself...

Try railless Capistrano or a method they use:
in a directory you have two things:
A folder containering folders, each subfolder is a release
A soft link to the current release folder
When you upload the new file, do the upload making a new release folder. Check to see that no one is currently running the current release (this might be a little tricky assuming you dont have a crazy number of users you could probably do it with a db entry) and then rewrite the softlink to point to the newest release.

maybe do try it like this:
delete file and save it's path
ln -nfs movedfilepath pathtosorrypage.html
upload file to some temporary folder on the server
remove symlink
mv newfile movedfilepath

Option 1: If you have a lot of users and this replacing is done not so frequent, you can set up a maintenance on the site (block access) and have no one log in after a certain time, and finally cut off everyone who is logged in when you're about to do the replacement.
Option 2: If the file replacing is done frequently (in which case you shouldn't do the maintenance every day), have it done by code. Have two of the same files (same folder if you want). Then, by code, when you're about to replace the file, have it just give the copy, while you replace the one you want. You can do it with a simple IF.
Pseudo-code:
if (replaceTime - 15 seconds <= currentTime <= replaceTime + 15 seconds){
// allows 30 seconds for another script to bring in the new image into 'myImage.jpg'
<img src="/myFiles/myOldImage.jpg" />
} else {
<img src="/myFiles/myImage.jpg" />
}
No need to update any database or manually move/copy/rename a file.
After replaceTime + 15 has passed:
copyFileTo("myImage.jpg","myOldImage.jpg");
// Now you have the copy ready for the next time to replace

Related

php - What will happen if I overwrite the file itself when it is executing (using ZipArchive)

So as described in question itself, I want to replace the file from which a zip archive is opened and then which is overwriting files with new version.
If still my question is not clear then the thing I want to do is I want to get a zip file from a server and then unzip using CLASS "ZipArchive" and then over write everyfile which is in Zip to destination location, the problem will be the php file by which this thing is happening will gonna be overwritten.
So will the php generate the error or the process will go whatever we want?
On Linux files are not usually locked (see https://unix.stackexchange.com/questions/147392/what-is-advisory-locking-on-files-that-unix-systems-typically-employs) so you can do whatever you want with that file. PHP works with that file in memory so you can overwrite it during it's execution.
But if you will run the script multiple times while the first one is in progress it might load incomplete version and then it will throw some error so it might be wise to make sure that won't happen (using locks) or try to do some more atomic approach.
Windows locks files so I assume you won't be able to extract files the same way there.

PHP - Is file being written to?

My mailserver writes to a file every minute, this is fine and I'm happy for it to do that.
However on my WebServer, I want to check if that file is currently being written to and if it isn't, show the user a download link.
Is there any way I can do this..
For example: run a loop that will keep looking until the file is no longer being written to then, show a download link to the file?
I've read about flock() but I don't think this will help as another process / os is actually creating the file!
Your writting script/app/process should write lock file (empty file like filename.lock before it starts writting to main file, and then it shall remove when done. It's regular locking approach but the your script will just need to check if filename.lock is present or not. If it is, then file is being written to.
You can only acquire a read or write lock if no-one else is currently writing. You shouldn't have to do this.
Also, when the user downloads the file it could be the file has changed in the mean time. Are you sure you've got the right mental image of what you want?

php: check if a file is open

I'm writing a document managment system. One of the features that has been requested is that users be able to cause the system to copy certain files to a location that both the users and php have access to. From here the user will be able to directly edit the files. What needs to happen is that the edited file must be automatically saved to the document management system as a new version. My question is with regards to this last point.
I have a page that allows the user to view all versions of a specific file. I was thinking that what would be cool would be to have things that when this page is accessed by anyone then php checks if there is an associated file that was edited and is now closed and simply move it to the right place and give it version info. Would this be possible? For example if a user is editing a file using MS Word, would php be able to know if that file is in use? If yes, how?
Another alternative is to just grab all files that were edited periodically (during the witching hour, for example) and also have a handy 'synchronise' button that users can click to signal that they are done.
here's some stuff I've tried:
flock: i thought it mich return false for files that are in use. mistaken
fstat: doesn't return anything useful as far as I can tell
unlink: I thought I might make a copy of the file then try unlink the original(user edited one). it turns out unlink works on stuff that is open
any ideas?
Also, it needs to work on windows and linux...
Here's some clarification for them what need: if andrew were to click the 'edit' button corresponding to a word file then the word file would be copied to some location. Andrew would then edit it using MS word, save his changes (possible more than once) and close it. That is all I want Andrew to do. I want my code to see that the file is closed then do stuff with it
You can create a file "filename.lock" (or "filename.lck") for each file open.
And you delete the file "filename.lock" (or "filename.lck") when is unlock.
And you can check if file is locked when the file "filename.lock" (or "filename.lck") exists.
If you're running under unix OS, you can implement a strategy like that:
Write a bash script like this lsof | grep /absolute/path/to/file.txt
You can also parameterize that
Recall that script from php
<?php
$result=shell_exec("myScriptPath");
echo($result);
?>
Remember that bash script will return status 0 if no one has file open, 256 (1) otherwise

Apache: Returns broken file if requested during file copy

On my webserver I generate an animated gif every 15 minutes. The result gets written into a temporary directory, then I copy the gif (some MBs) into the htdocs directory via php:copy(). However, if there's an http request for the file during the copy process, Apache will return only a portion of the file, which results in a broken animation.
How can I solve this? Using rename() instead of copy()? Or is there a way to instruct Apache to wait for filesystem operations before delivering a file (shouldn't this be default anyway?)?
Definitely use rename instead of copy. Rename (mv, essentially), just changes file pointers around, so even if it's not truly atomic (I haven't been able to find a conclusive source, but I suspect it is) it'll be so fast that the chances of someone loading a page in that interval is virtually zero.
For more info, see:
MV vs. RM
This comment from the PHP manual
Hope this helps!
You need to copy into a temporary filename and then rename the temporary. Then the server will never see an incomplete file.

How to efficiently monitor a directory for changes on linux?

I am working with Magento, and there is a function that merges CSS and Javascript into one big file.
Regardless the pros and cons of that, there is the following problem:
The final file gets cached at multiple levels that include but are not limited to:
Amazon CloudFront
Proxy servers
Clients browser cache
Magento uses an MD5 sum of the concatenated css filenames to generate a new filename for the merged css file. So that every page that has a distinct set of css files gets a proper merged css file.
To work around the caching issue, I also included the file modification timestamps into that hash, so that a new hash is generated, everytime a css file is modified.
So the full advantages of non revalidative caching score, but if something gets changed, its visible instantly, because the resource link has changed.
So far so good:
Only problem is, that the filenames that are used to generate the has, are only the ones that would normally be directly referenced in the HTML-Head block, and don't include css imports inside those files.
So changes in files that are imported inside css files don't result in a new hash.
No I really don't want to recursively parse all out imports and scan them or something like that.
I rather thought about a directory based solution. Is there anything to efficiently monitor the "last change inside a directory" on a file system basis?
We are using ext4.
Or maybe is there another way, maybe with the find command, that does all the job based on inode indexes?
Something like that?
I have seen a lot of programs that instantly "see" changes without scanning whole filesystems. I believe there are also sort of "file manipulation watch" daemons available under linux.
The problem is that the css directory is pretty huge.
Can anyone point me in the right direction?
I suggest you use php-independent daemon to modify change date of your main css file when one of dependent php files are modified. You can use dnotify for it, something like:
dnotify -a -r -b -s /path/to/imported/css/files/ -e touch /path/to/main/css/file;
It will execute 'touch' on main css file each time one of the files in other folder are modified (-a -r -b -s = any access/recursive directory lookup/run in background/no output). Or you can do any other action and test for it from PHP.
If you use the command
ls -ltr `find . -type f `
It will give you a long listing of all files with the newest at the bottom.
Try to have a look to inotify packages that will allows you to be notified eah time a modification occurs in a directory.
InotifyTools
php-inotify
I've never used it, but apparently there is inotify support for PHP.
(inotify would be the most efficient way to get notifications under Linux)

Categories