Are rename() and unlink() asynchronous functions? - php

I have strong reason to believe that both functions rename() and unlink() are asynchronous, which, from my understanding, means that when the functions are called, the code below them are continued before it finishes its procedures on the filesystem. This is a problem for the internet app I'll explain below, because later code depends on these changes to already be set in stone. So, is there a way to make both synchronous, so that the code reader freezes when it hits these functions, until all of its tasks are fully carried out on the filesystem?
Here is the code in delete-image.php, which is called by ajax from another admin-images.php(the latter will not be shown):
`
foreach ($dirScan as $key => $value) {
$fileParts = explode('.', $dirScan[$key]);
if (isset($fileParts[1])) {
if ((!($fileParts[1] == "gif") && !($fileParts[1] == "jpg")) && (!($fileParts[1] == "png") && !($fileParts[1] == "jpg"))) {
unset($dirScan[$key]);
}
} else {
unset($dirScan[$key]);
}
}
$dirScan = array_values($dirScan);
// for thumbnail
$file = 'galleries/' . $currentGal . '/' . $currentDir . "/" . $dirScan[$imageNum - 1];
unlink($file);
for ($i = ($imageNum - 1) + 1; $i < count($dirScan); $i++) {
$thisFile = 'galleries/' . $currentGal . '/' . $currentDir . '/' . $dirScan[$i];
$thisSplitFileName = explode('.', $dirScan[$i]);
$newName = 'galleries/' . $currentGal . '/' . $currentDir . "/" . ($thisSplitFileName[0] - 1) . "." . $thisSplitFileName[1];
rename($thisFile, $newName);
}
// for large image
$fileParts = explode('.', $dirScan[$imageNum - 1]);
$file = 'galleries/' . $currentGal . '/' . $currentDir . "/large/" . $fileParts[0] . "Large." . $fileParts[1];
unlink($file);
for ($i = ($imageNum - 1) + 1; $i < count($dirScan); $i++) {
$thisSplitFileName = explode('.', $dirScan[$i]);
$thisFile = 'galleries/' . $currentGal . '/' . $currentDir . '/large/' . $thisSplitFileName[0] . "Large." . $thisSplitFileName[1];
$newName = 'galleries/' . $currentGal . '/' . $currentDir . "/large/" . ($thisSplitFileName[0] - 1) . "Large." . $thisSplitFileName[1];
rename($thisFile, $newName);
}
sleep(1);
echo 'deleted ' . $dirScan[$imageNum - 1] . " successfully!";
} else {
echo "please set the post data";
} ?>`
After this script returns its completed text, admin-images.php triggers a new function which populates an image table from these renamed and trimmed files. Sometimes it displays old names and files that were suppose to be deleted, and a simple page refresh gets rid of them. This seems to suggest that the above php script is running through all the code and spitting out echoed text to the mainfile before it completes its file-system manipulation (All of this other code is long and complicated, and hopefully unnecessary for the discussion at hand).
You'll notice, I've tried a sleep() function to halt the php script to hopefully give it time to finish. This is an ineligent, and problematic way of doing things, because I have to put a large amount of time to insure it works every-time, but I don't want the user to wait longer than she / he has to.

Mind that file-systems often use caches to reduce the load. Normally you won't notice, but sometimes you need to clear the cache if you need to have the real information. Check the configuration of your file-system if your issue is file-system related.
PHP itself uses a cache as well for some file-operations, so clear that, too.
See clearstatcache to clear the PHP stat cache.
Take note that this is a "view" issue, the file is actually deleted on disk, but PHP might still return it's there (until you clear the cache).

I suppose they are not asynchronous, because they return a result telling if the operation was successful or not.
I believe the problem happens because when you run scandir after making the modifications, it may be using "cached" data, from memory, instead of re-scanning the file system.

rename() is not, but unlink() is asynchronous on Windows.
Because there seems to be no way of waiting for a pending delete to finish, this answer suggests to rename a file before deleting it. PHP does not seem to do that, so you can assume it's asynchronous.

To use any file operation you are required to use the $_SERVER["DOCUMENT_ROOT"] to make that work. In case you wont do it.. the real operation wont work properly. Also in case you are using the Linux Server then you will be required to set the permissions for the folders in which you want to perform the file operation.
And mind it both the operations are synchronous they are not asynchronous. It also depends on the type of the server or the OS that you are using.

Related

PHP ftp_nlist returning false when retrieving large directory?

What I'm trying to do:
Use PHP ftp_nlist to retrieve the contents of a directory on the FTP server
The problem:
For directories that contain a lot of files (the one I encountered the problem on has nearly 40 thousand files, and no subfolders), the ftp_nlist function is returning false. For directories that are not as large, the ftp_nlist function returns an array of filenames as expected.
What I've tried:
Enabling passive mode (it already was enabled, but I see it as a common suggestion)
Adding ftp_set_option($conn_id, FTP_USEPASVADDRESS, false); after my ftp_login
using ftp_chdir, although my folder names never have spaces anyways
echoing error_get_last() after ftp_nlist returns false. The error show seems unrelated, but is shown below.
My code:
In case it is useful, here is the function I have created. What it is supposed to do is...
take in $fm (filemaker, unrelated to this problem)
take in $FTPConnectionID (the ftp connection I established in the prior to the function call)
take in $FolderPath (the path of the folder on the FTP server for which I want to list files/subfolders recursively - ex: "SomeFolder/Testing")
take in $TextFile (I am writing the paths of every file found on the FTP server to a text file, which was created prior to calling the function)
function createAuditFile($fm, $FTPConnectionID, $FolderPath, $TextFile) {
echo "createAuditFile called for " . $FolderPath . "\n";
//Get the contents of the given path. Will include files and folders.
$FolderContents = ftp_nlist($FTPConnectionID, $FolderPath);
if($FolderContents == false) {
echo "Couldn't get " . $FolderPath . "\n";
echo "ERROR: " . print_r(error_get_last()) . "\n";
} else {
print_r($FolderContents);
}
//Loop through the array, call this function recursively if a folder is found.
if(is_array($FolderContents)) {
foreach($FolderContents as $Content) {
//Create a varaible for the folder path
$ContentPath = $FolderPath . "/" . $Content;
//Call the function recursively if a folder is found
if(pathinfo($Content, PATHINFO_EXTENSION) == "") {
createAuditFile($fm, $FTPConnectionID, $ContentPath, $TextFile);
echo "Recursive call for " . $ContentPath . "\n";
//If a file is found, add the file ftp path to our array
} else {
echo "Writing to file: " . $ContentPath . "\n";
fwrite($TextFile, $ContentPath . "\n");
}
}
}
}
I can provide other code if needed, but I think my question is less of a coding issue, and more of an understanding ftp_nlist issue. I've been stuck on this for hours, so any help is appreciated. And like I said, this function works just fine for most folder paths passed to it, the problem is when there are tens of thousands of files within the folder. Thank you!

Creating SYMLINK with PHP on Hostgator jailed shell - Get Perm Denied error

so I come to you with this problem:
GIVENS:
I have Hostgator as the ISP.
I'm using PHP 5.5
The LINUX box is CENTOS
Shared Hosting Environment
I am a professional coder and experienced with LAMP for many years
PROBLEMS:
I'm NOT familiar with Jailed Shell but have an idea
I've tried the script and have been searching for an answer
Still Stuck...
Here's my current code:
function getMyFakeDir($myfile) {
$target = "";
$link = 'content/purchased-items/link';
symlink($target, $link);
echo "READ LINK: ". readlink($link);
return readlink($link);
}
Here's the called to the function:
$linkText = getMyFakeDir('SomePDFThatTheUserCanDownload.pdf');
Then I pass that "$linkText" var to PHPMailer and wala!!! The user clicks to download through the Symlink and I've written a code to make it expire after 24 hours. Yeah, I got that from PHP.net.
So, basically that's my problem....
Here's the error:
Warning: symlink(): Permission denied in /homeSomewhere/someMasterDir/public_html/webServices/somePHPFile.php on line 654
This is link 654 from above:
symlink($target, $link);
Thanks...
Figured it out.... Simple LOGIC!
First I had the PATHS all wrong. Here's the CORRECTED CODE:
//Generate Symbolic Link that blows away a fake directory each time
//A symbolic Link is created to "THIS" file below
$filename = $myfile;
//This is ANY directory on the server...
//A randomly named directory is created here...STEP 1
$downloaddir = "/home/someHostDir/public_html/sldktrulwiu2555ivd0fjvdfgdfgdfgdf/";
//Any directory NOT accessible by a browser - This is one level up
$safedir = "/home/someHostDir/content/purchased-items/";
//This is the equivalent of the $downloaddir and the browser is REDIRECTED here and the download begins
$downloadURL = "http://www.theSomeDomain.com/sldktrulwiu2555ivd0fjvdfgdfgdfgdf/"; //THIS IS THE FAKE DIRECTORY (Which I simply type gobbligook and created that mess above)
$letters = 'abcdefghijklmnopqrstuvwxyz';
srand((double) microtime() * 1000000);
$string = '';
for ($i = 1; $i <= rand(4, 12); $i++) {
$q = rand(1, 24);
$string = $string . $letters[$q];
}
$handle = opendir($downloaddir);
while ($dir = readdir($handle)) {
if (is_dir($downloaddir . $dir)) {
if ($dir != "." && $dir != "..") {
#unlink($downloaddir . $dir . "/" . $filename);
#rmdir($downloaddir . $dir);
}
}
}
closedir($handle);
mkdir($downloaddir . $string, 0777);
symlink($safedir . $filename, $downloaddir . $string . "/" . $filename);
//Header("Location: " . $downloadURL . $string . "/" . $filename);
That just about does it.
The result is a Dynamic directory inside the "/sldktrulwiu2555ivd0fjvdfgdfgdfgdf/" directory like this: "dfsgss/" and a "shortcut" to the REAL LOCATION where the files are. THEN when the file is downloaded, and the page returns to the INDEX.HTML, the code BLOWS OUT the FAKE directory Never more to be seen again. Thus, if the user wants to go back, they get the horrible 404 PAGE error that the file / has been moved or deleted. BUMMER.
That's it folks. Thanks for your assistance, it was you all the helped me release my error. TEAM WORK!

Warning: move_uploaded_file(): it is moved but the Filesize stays at zero

I have been using an upload script on my server, like below
$newname = time() . '_' . $_FILES[$file]["name"];
if (strtolower(end(explode('.', $_FILES[$file]["name"]))) != 'pdf' AND $file != "damage_attachment_damageform_1" AND $file != "damage_attachment_damageform_2" AND $file != "damage_attachment_damageform_3" AND $file != "damage_attachment_damageform_4") {
if (move_uploaded_file($_FILES[$file]["tmp_name"], $_SERVER['DOCUMENT_ROOT'] . '/components/com_fleet/uploads/docs/' . $newname)) {
$images[] = $_SERVER['DOCUMENT_ROOT'] . '/components/com_fleet/uploads/docs/' . $newname;
$docs[] = $_SERVER['DOCUMENT_ROOT'] . '/components/com_fleet/uploads/docs/' . $newname;
} else {
die();
}
}
It uploads an image fine, but since a few days a get a Warning: move_uploaded_file(): Unable to move error. Ive seen these a dozen of times while learning to program, so I did all the usual stuff, check paths, the $_FILES[$file]["error"] and check all the right CHMODs. All is fine, path is spot-on, chmod is too, no errors etc...
1 extra weird thing I noticed the file does get written to the right /docs map but its Filesize is empty, and move_upload_file still sends false...
What am I forgetting? CHOWN maybe? And how can I solve that, I dont have SSH access or something.
Graa after an hour I now found out what was wrong, server Disk Quota was exceeded. Maybe people can still benefit from my problems...

Second instance of script runs the exact same code as the first one

This script is supposed to write log files using file locks etc to make sure that scripts running at the same time don't have any read/write complications. I got it off someone on php.net. When I tried to run it twice at the same time, I noticed that it completely ignored the lock file. However, when I ran them consecutively, the lock file worked just fine.
That doesn't make any sense whatsoever. The script just checks if a file exists, and acts based on that. Whether another script is running or not, shouldn't influence it at all. I double checked to make sure the lock file was created in both cases; it was.
So I started to do some testing.
First instance started at 11:21:00 outputs:
Started at: 2012-04-12 11:21:00
Checking if weblog/20120412test.txt.1.wlock exists
Got lock: weblog/20120412test.txt.1.wlock
log file not exists, make new
log file was either appended to or create anew
Wrote: 2012-04-12 11:21:00 xx.xx.xx.xxx "testmsg"
1
Second instance started at 11:21:03 outputs:
Started at: 2012-04-12 11:21:00
Checking if weblog/20120412test.txt.1.wlock exists
Got lock: weblog/20120412test.txt.1.wlock
log file not exists, make new
log file was either appended to or create anew
Wrote: 2012-04-12 11:21:00 xx.xx.xx.xxx "testmsg"
1
So there are two things wrong here. The timestamp, and the fact that the script sais the lock file doesn't exist even though it most certainly does.
It's almost as if the second instance of the script simply outputs what the first one did.
<?php
function Weblog_debug($input)
{
echo $input."<br/>";
}
function Weblog($directory, $logfile, $message)
{
// Created 15 september 2010: Mirco Babin
$curtime = time();
$startedat = date('Y-m-d',$curtime) . "\t" . date('H:i:s', $curtime) . "\t";
Weblog_debug("Started at: $startedat");
$logfile = date('Ymd',$curtime) . $logfile;
//Set directory correctly
if (!isset($directory) || $directory === false)
$directory = './';
if (substr($directory,-1) !== '/')
$directory = $directory . '/';
$count = 1;
while(1)
{
//*dir*/*file*.*count*
$logfilename = $directory . $logfile . '.' . $count;
//*dir*/*file*.*count*.lock
$lockfile = $logfilename . '.wlock';
$lockhandle = false;
Weblog_debug("Checking if $lockfile exists");
if (!file_exists($lockfile))
{
$lockhandle = #fopen($lockfile, 'xb'); //lock handle true if lock file opened
Weblog_debug("Got lock: $lockfile");
}
if ($lockhandle !== false) break; //break loop if we got lock
$count++;
if ($count > 100) return false;
}
//log file exists, append
if (file_exists($logfilename))
{
Weblog_debug("log file exists, append");
$created = false;
$loghandle = #fopen($logfilename, 'ab');
}
//log file not exists, make new
else
{
Weblog_debug("log file not exists, make new");
$loghandle = #fopen($logfilename, 'xb');
if ($loghandle !== false) //Did we make it?
{
$created = true;
$str = '#version: 1.0' . "\r\n" .
'#Fields: date time c-ip x-msg' . "\r\n";
fwrite($loghandle,$str);
}
}
//was log file either appended to or create anew?
if ($loghandle !== false)
{
Weblog_debug("log file was either appended to or create anew");
$str = date('Y-m-d',$curtime) . "\t" .
date('H:i:s', $curtime) . "\t" .
(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '-') . "\t" .
'"' . str_replace('"', '""', $message) . '"' . "\r\n";
fwrite($loghandle,$str);
Weblog_debug("Wrote: $str");
fclose($loghandle);
//Only chmod if new file
if ($created) chmod($logfilename,0644); // Read and write for owner, read for everybody else
$result = true;
}
else
{
Weblog_debug("log file was not appended to or create anew");
$result = false;
}
/**
Sleep & disable unlinking of lock file, both for testing purposes.
*/
//Sleep for 10sec to allow other instance(s) of script to run while this one still in progress.
sleep(10);
//fclose($lockhandle);
//#unlink($lockfile);
return $result;
}
echo Weblog("weblog", "test.txt", "testmsg");
?>
UPDATE:
Here's a simple script that just shows the timestamp. I tried it on a different host so I don't think it's a problem with my server;
<?php
function Weblog_debug($input)
{
echo $input."<br/>";
}
$curtime = time();
$startedat = date('Y-m-d',$curtime) . "\t" . date('H:i:s', $curtime) . "\t";
Weblog_debug("Started at: $startedat");
$timediff = time() - $curtime;
while($timediff < 5)
{
$timediff = time() - $curtime;
}
Weblog_debug("OK");
?>
Again, if I start the second instance of the script while the first is in the while loop, the second script will state it started at the same time as the first.
I can't fricking believe this myself, but it turns out this is just a "feature" in Opera. The script works as intended in Firefox. I kinda wish I tested that before I went all berserk on this but there ya go.

Checking to see if file exist in multiple locations

I have a two part question...
For my file check I need to look to see if the file is present in $filechk or $dirchk.
How can I use a wildcard on the file extension $filename.* when doing a file check?
I'm using is_file because I read that it's twice as fast when checking if a file exists.
code
$filechk1 = "/temp/files/" . $data[0] . ".doc";
$filechk2 = "/temp/files/" . $data[1] . ".doc";
$dirchk1 = "/temp/files/" . $IDname . $data[0] . ".doc";
$dirchk2 = "/temp/files/" . $IDname . $data[1] . ".doc";
if(is_file($filechk1) && ($filechk2)){
...
}
else { ... }
you should get a list of all of the files in the directory and then check the file extensions - is_file is for a single file only.
To check a number of files, just do a separate is_file() or file_exists() - the speed difference between the two is hardly relevant if you're doing this on one or two files.
For a wildcard search, do a glob().
$files = glob("/path/to/directory/*.doc");
print_r($files);

Categories