PHP is_dir defective - php

Strange behaviour, exentially:
(the name of the folder depends on the date - the purpose is a hit counter of the website, broken down by day)
if (!is_dir($folder)) { // first access in the day
mkdir($folder);
}
Well: on the server in internet all works well.
But when i try in local, with the server simulator of Easy PHP, happens that:
(a) The first time, no problem. The folder doesn't exists and it is created.
(b) subsequently, for example to a page refresh, the program flow again goes in the IF (!!!) generating the error (at line of mkdir) of kind: "Warning: mkdir(): No such file or directory in [...]".
All parent part of the directory $folder exists.
Thanks
.

Try using a recursive directory creation function:
function mkdir_r($dirName, $rights = 0777)
{
$dirs = explode(DIRECTORY_SEPARATOR , $dirName);
$dir = '';
if (strpos($dirs[count($dirs) - 1], '.')) {
array_pop($dirs);
}
foreach ($dirs as $part) {
$dir .= $part . DIRECTORY_SEPARATOR ;
if (!is_dir($dir) && strlen($dir) > 0) {
mkdir($dir, $rights);
}
}
}
This way all directories up to the directry you wanted to create are created if they don't exist.
mkdir doesn't work recursively unfortunately.

If anyone faces the issue; Use the native clearstatcache() function after you delete the file.
I'm quoting the interesting part of the original documentation
You should also note that PHP doesn't cache information about non-existent files. So, if you call file_exists() on a file that doesn't exist, it will return false until you create the file. If you create the file, it will return true even if you then delete the file. However unlink() clears the cache automatically.
For further information here is the documentation page: https://www.php.net/manual/en/function.clearstatcache.php

Related

Copying folder to other folder with PHP

I'm trying to move folder to other folder, with all it's files. Both folders are in root directory. Tried a lot of ways, and always get no result.
Here is my latest atempt:
$source = "template/"
$dest = "projects/"
function copyr($source, $dest){
if (is_link($source)) {
return symlink(readlink($source), $dest);
}
if (is_file($source)) {
return copy($source, $dest);
}
if (!is_dir($dest)) {
mkdir($dest);
}
$dir = dir($source);
while (false !== $entry = $dir->read()) {
// Skip pointers
if ($entry == '.' || $entry == '..') {
continue;
}
copyr("$source/$entry", "$dest/$entry");
}
$dir->close();
return true;
}
Need professional glance to tell me, where I'm getting it wrong?
EDIT:
Sorry for wrong tags.
Problem is - nothing is happening. Nothing is being copied. No error messages. Simply nothing happens.
File structure:
I suggest to try and do the following
How do you run the scrips? Do you open page in browser or run script in command line? If you open page in browser this might be an issue with permissions, paths (relative and not absolute) and errors not shown but logged.
Use absolute folder paths instead of relative paths. For example /var/www/project/template.
Apply realpath() function to all paths and check (output) the result. If path is wrong (folder does not exist, separators are wrong etc) you will get empty result from the function.
Make sure to use DIRECTORY_SEPARATOR instead of / if you run your script on Windows. I can not check if / works on Windows now but potentially this might be an issue. For example
copyr($source.DIRECTORY_SEPARATOR.$entry", $dest.DIRECTORY_SEPARATOR.$entry);
Check warnings and errors. If you do not have permission you should get warning like this
PHP Warning: mkdir(): Permission denied
You may need to enable warnings and errors if they are disabled. Try for example to make an obvious mistake with name and check if you get any error message.
Try to use tested solution from one of the answers. For example xcopy function.
Try to add debug messages or run your script in debugger step by step. Check what is happening, what is executed etc. You can add debug output near any operator like (just an idea):
echo 'Creating directory '.$name.' ... ';
mkdir($name);
echo (is_dir($name) ? 'created' : 'failed').PHP_EOL;

rmdir no such file directory error although directory exist

If use this function to remove a directory + all files inside.
function delete_files($target)
{
if(is_dir($target))
{
$files = glob($target . '*', GLOB_MARK);
foreach($files as $file)
{
delete_files($file);
}
rmdir($target);
}
elseif(is_file($target))
{
unlink($target);
}
}
delete_files($directory);
But whenever I do this, I get this error message:
Warning: rmdir(directory/12) [function.rmdir]: No such file or directory
in delete_files.php
"directory/12" is the correct name of the directory I wanted to delete. I don't understand why it says that it does not exist because it does! Weirdly though, even though I got the error message, the directory DID get deleted.
So I added a line of code print_n($files); before the for-loop and it game me two arrays -- one containing the directory ("directory/12") and the other containing all the files of the directory ("directory/12/01.gif", "directory/12/02.gif" etc). So I figured the directory must have gotten deleted in the for-loop and removed the line rmdir($target) and tried it again. This time, all the files within the directory got deleted but the directory itself remained.
So apparently, rmdir DOES indeed remove the directory correctly. But then, why does it give me the error message beforehand that it doesn't exist?
It will work if you append a slash to the directory name.
Explanation: When you initially call the function as delete_files("directory/12"), the parameters passed to the glob() call will look like this:
$files = glob("directory/12*", GLOB_MARK);
Assuming that you have no other files in directory/ with names beginning with 12, this will just return "directory/12/" (with a slash appended because of GLOB_MARK). The function will then recursively call itself with that parameter, resulting in the top-level directory being processed twice.
Of course, if you did happen to have some other file or directory named, say, directory/123, then it would also get deleted, which is presumably not what you want.
To fix this properly, you should make sure your function can properly handle directories even if they get passed in without a trailing slash. The simplest way to do that would be to always append the slash to directory names before globbing them, like this:
$files = glob($target . '/*');
However, note that this could still fail (albeit less destructively) if your directory happened to contain some files not matched by *, such as dotfiles, since they would not get deleted, causing the subsequent rmdir() to fail because the directory will not be empty.
A more robust solution would be to use scandir() instead of glob(), like this:
$files = array_diff( scandir($target), array('.', '..') );
foreach ($files as $file) {
delete_files("$target/$file");
}
(The array_diff() is needed to eliminate the special . and .. directory entries, which would cause the code to recurse forever if they weren't excluded.)
One remaining potential failure mode is that this code will happily follow symlinks to directories and try to delete everything in the directory they point to (and then fail to remove the link itself, because rmdir() can't remove symlinks). To fix this issue, you'll probably want to replace the is_dir($target) test with !is_link($target) && is_dir($target).
All put together, the resulting code would look like this:
function delete_files($target)
{
if(!is_link($target) && is_dir($target))
{
// it's a directory; recursively delete everything in it
$files = array_diff( scandir($target), array('.', '..') );
foreach($files as $file) {
delete_files("$target/$file");
}
rmdir($target);
}
else
{
// probably a normal file or a symlink; either way, just unlink() it
unlink($target);
}
}
delete_files($directory);
Ps. See also How do I recursively delete a directory and its entire contents (files + sub dirs) in PHP?
cause you call it twice the first time it works the second time it gives a error.
I can't prove it, but with recursive code like that it is the problem.

Forcing file-creation in public directory?

Here's an idea. I'm trying to create file from PHP script. File may be created in any public place on server (in public root or in its subdirectories). Bottom of line, I want to write function like this (it must work on both unix and windows servers).
function create_file($path, $filename, $content, $overwrite=false) {
# body
...
}
Parameters:
$path - file-path (empty or ends with /)
$filename - any valid file-name (requires dot+extension)
$content - file-content (anything)
$overwrite - if set true, existing file will be overwritten (default. false).
Result:
Function returns TRUE and creates file with content, or FALSE if file-creation was not possible for any reason.
Description of problem:
I expect this function to return true and create file $filename on path $path, or false if it wasn't possible for any reason. Also if file was successfully opened for writing, need to put $content in it.
Argument $path is relative path to public directory, and $filename is any valid filename. Of course, I want to avoid creating files if path points outside of public directory. Function may be called from subdirectory-scripts like this example
# php script: /scripts/test.php
create_file('../data/', 'test.info', 'some contents');
# result file: /data/test.info
What have I tried so far?
I’ve tried doing this with fopen() and fwrite() functions and that works on some servers, and doesn’t work on some. I guess there’s problem with writing privileges and chmod() but to be honest I’m not very familiar with chmod attributes. Also I couldn't check if $path points outside of server's public directory.
In short, I want this function to create file and return TRUE if file doesn't exist, or file exists and $owerwrite=true. Otherwise, nothing happens and function returns FALSE.
Additionally, I would like to know reason why file can't be created on some path (just in theory). Incorrect path/filename is only thing I have on my mind and I'm sure there's more about this problem.
Thanks in advance for any sample/example/suggestion.
update code
So far I have this code...
function create_file($path, $filename, $content, $overwrite=false) {
$reldir = explode('/', trim(str_replace('\\', '/', dirname($_SERVER['PHP_SELF'])), '/'));
$dirdepth = sizeof($reldir);
$rpadd = $dirdepth > 0 ? '(\.\./){0,' . $dirdepth . '}' : '';
$ptpath = '#^/?' . $rpadd . '([a-z0-9]\w*/)*$#i';
$ptname = '/^[a-z]\w*\.[a-z0-9]+$/i';
if ($res = preg_match($ptpath, $path) && preg_match($ptname, $filename)) {
$res = false;
if ($overwrite === true || !file_exists($path.$filename)) {
if ($f = #fopen($path.$filename, 'w')) {
fwrite($f, $content);
fclose($f);
$res = true;
}
}
}
return $res;
}
Some suggestions:
set the owner of the web server document root: chown -R apache:apache /var/www (i suppose /var/www is your document root and that the web server apache runs with user apache). Set the privilegies of the document root like this in order to have all directories under document look with privilegies 755 (only owner which is user apache can write in folders /var/www and sub folders)
Block paths that point out of your /var/www document root: you are under the issue known as http://en.wikipedia.org/wiki/Directory_traversal_attack. What about if the $path is something like: /var/www/../../../etc/passwd?
Basename php function can help you identifying this kind of malignous paths. Look this post: php directory traversal issue
To check wheter a file already exists or not: http://php.net/manual/en/function.file-exists.php
All file functions in php will not work properly in two circumstances
If you don't have enough user privileges for applications, then
You may not used/passed the arguements/parameters correctly .

PHP Count through different directories and see which ones have 0 files in them

I have the following folder structure:
images/photo-gallery/2e/
72/
rk/
u3/
va/
yk/
... and so on. Basically, each time an image is uploaded it hashes the name and then creates a folder with the first two letters. So inside of 2e is 2e0gpw1p.jpg
Here's the thing... if I delete an image, it will delete the file but it will keep the folder that it's in. Now when I have a TON of images uploaded, that will be fine since a lot of images will share the same folder.. but until then, I will end up having a bunch of empty directories.
What I want to do is search through the photo-gallery folder and go through each directory and see which folders are empty.. if there are any empty folders then it will remove it.
I know how to do that for a single directory, like the 2e folder. But how would I do it for all the folders inside the photo-gallery folder?
The PHP function rmdir() will throw a warning if the directory is not empty, so you can use it on non-empty directories without risking deleting them. Combine that with scandir() and array_slice (to remove . and ..), and you can do this:
foreach(array_slice(scandir('images/photo-gallery'),2) as $dir) {
#rmdir('images/photo-gallery/' . $dir); // use # to silence the warning
}
while you could do with with php, i'm inclined to use the os for such a task. Of course you can call the below with php
find <parent-dir> -depth -type d -empty -exec rmdir -v {} \;
PLEASE READ THIS WARNING I DID NOT TEST BUT HAVE USED SIMILAR CODE DOZENS OF TIMES. FAMILURIZE YOURSELF WITH THIS AND DO NOT USE IF YOU DO NOT UNDERSTAND WHAT IT IS DOING THIS COULD POTENTIALLY WIPE YOUR SITE FROM THE SERVER.
EDIT BACKUP EVERYTHING BEFORE TRYING THIS YOUR FIRST TIME THE PATH IS VERY VERY IMPORTANT!
Ok with that said this is quite easy :)
<?php
function recursiveDelete($path){
$ignore = array(
'cgi-bin',
'.',
'..'
); // Directories to ignore
$dh = opendir($path); // Open the directory
while(false !== ($file = readdir($dh))){ // Loop through the directory
if(!in_array($file, $ignore)){ // Check that this file is not to be ignored
if(is_dir($path."/".$file)){ // Its a directory, keep going
if(!iterator_count(new DirectoryIterator($path."/".$file)))
rmdir($path."/".$file); // its empty delete it
} else {
recursiveDelete($path."/".$file);// Recursive call to self
}
}
}
}
closedir($dh); // All Done close the directory
}
// WARNING IMPROPERLY USED YOU CAN DUMP YOUR ENTIRE SERVER USE WITH CAUTION!!!!
// I WILL NOT BE HELD RESPONSIBLE FOR MISUSE
recursiveDelete('/some/directoy/path/to/your/gallery');
?>

file_exists() returns false, but the file DOES exist

I'm having a very weird issue with file_exists(). I'm using this function to check if 2 different files in the same folders do exist. I've double-checked, they BOTH do exist.
echo $relative . $url['path'] . '/' . $path['filename'] . '.jpg';
Result: ../../images/example/001-001.jpg
echo $relative . $url['path'] . '/' . $path['filename'] . '.' . $path['extension'];
Result: ../../images/example/001-001.PNG
Now let's use file_exists() on these:
var_dump(file_exists($relative . $url['path'] . '/' . $path['filename'] . '.jpg'));
Result: bool(false)
var_dump(file_exists($relative . $url['path'] . '/' . $path['filename'] . '.' . $path['extension']));
Result: bool(true)
I don't get it - both of these files do exist. I'm running Windows, so it's not related to a case-sensitive issue. Safe Mode is off.
What might be worth mentioning though is that the .png one is uploaded by a user via FTP, while the .jpg one is created using a script. But as far as I know, that shouldn't make a difference.
Any tips?
Thanks
file_exists() just doesn't work with HTTP addresses.
It only supports filesystem paths (and FTP, if you're using PHP5.)
Please note:
Works :
if (file_exists($_SERVER['DOCUMENT_ROOT']."/folder/test.txt")
echo "file exists";
Does not work:
if (file_exists("www.mysite.com/folder/test.txt")
echo "file exists";
Results of the file_exists() are cached, so try using clearstatcache(). If that not helped, recheck names - they might be similar, but not same.
I found that what works for me to check if a file exists (relative to the current php file it is being executed from) is this piece of code:
$filename = 'myfile.jpg';
$file_path_and_name = dirname(__FILE__) . DIRECTORY_SEPARATOR . "{$filename}";
if ( file_exists($file_path_and_name) ){
// file exists. Do some magic...
} else {
// file does not exists...
}
Just my $.02: I just had this problem and it was due to a space at the end of the file name. It's not always a path problem - although that is the first thing I check - always. I could cut and paste the file name into a shell window using the ls -l command and of course that locates the file because the command line will ignore the space where as file_exists does not. Very frustrating indeed and nearly impossible to locate were it not for StackOverflow.
HINT: When outputting debug statements enclose values with delimiters () or [] and that will show a space pretty clearly. And always remember to trim your input.
It's because of safe mode. You can turn it off or include the directory in safe_mode_include_dir. Or change file ownership / permissions for those files.
php.net: file_exists()
php.net: safe mode
Try using DIRECTORY_SEPARATOR instead of '/' as separator. Windows uses a different separator for file system paths (backslash) than Linux and Unix systems.
A very simple trick is here that worked for me.
When I write following line, than it returns false.
if(file_exists('/my-dreams-files/'.$_GET['article'].'.html'))
And when I write with removing URL starting slash, then it returns true.
if(file_exists('my-dreams-files/'.$_GET['article'].'.html'))
I have a new reason this happens - I am using PHP inside a Docker container with a mounted volume for the codebase which resides on my local host machine.
I was getting file_exists == FALSE (inside Composer autoload), but if I copied the filepath into terminal - it did exist! I tried the clearstatche(), checked safe-mode was OFF.
Then I remembered the Docker volume mapping: the absolute path on my local host machine certainly doesn't exist inside the Docker container - which is PHP's perspective on the world.
(I keep forgetting I'm using Docker, because I've made shell functions which wrap the docker run commands so nicely...)
It can also be a permission problem on one of the parent folders or the file itself.
Try to open a session as the user running your webserver and cd into it. The folder must be accessible by this user and the file must be readable.
If not, php will return that the file doesn't exist.
have you tried manual entry. also your two extensions seem to be in different case
var_dump(file_exists('../../images/example/001-001.jpg'));
var_dump(file_exists('../../images/example/001-001.PNG'));
A custom_file_exists() function inspired by #Timur, #Brian, #Doug and #Shahar previous answers:
function custom_file_exists($file_path=''){
$file_exists=false;
//clear cached results
//clearstatcache();
//trim path
$file_dir=trim(dirname($file_path));
//normalize path separator
$file_dir=str_replace('/',DIRECTORY_SEPARATOR,$file_dir).DIRECTORY_SEPARATOR;
//trim file name
$file_name=trim(basename($file_path));
//rebuild path
$file_path=$file_dir."{$file_name}";
//If you simply want to check that some file (not directory) exists,
//and concerned about performance, try is_file() instead.
//It seems like is_file() is almost 2x faster when a file exists
//and about the same when it doesn't.
$file_exists=is_file($file_path);
//$file_exists=file_exists($file_path);
return $file_exists;
}
This answer may be a bit hacky, but its been working for me -
$file = 'path/to/file.jpg';
$file = $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST'].'/'.$file;
$file_headers = #get_headers($file);
if($file_headers[0] == 'HTTP/1.1 404 Not Found') {
$exists = false;
}else{
$exists = true;
}
apparently $_SERVER['REQUEST_SCHEME'] is a bit dicey to use with IIS 7.0 + PHP 5.3 so you could probably look for a better way to add in the protocol.
I found this answer here http://php.net/manual/en/function.file-exists.php#75064
I spent the last two hours wondering what was wrong with my if statement: file_exists($file) was returning false, however I could call include($file) with no problem.
It turns out that I didn't realize that the php include_path value I had set in the .htaccess file didn't carry over to file_exists, is_file, etc.
Thus:
<?PHP
// .htaccess php_value include_path '/home/user/public_html/';
// includes lies in /home/user/public_html/includes/
//doesn't work, file_exists returns false
if ( file_exists('includes/config.php') )
{
include('includes/config.php');
}
//does work, file_exists returns true
if ( file_exists('/home/user/public_html/includes/config.php') )
{
include('includes/config.php');
}
?>
Just goes to show that "shortcuts for simplicity" like setting the include_path in .htaccess can just cause more grief in the long run.
In my case, the problem was a misconception of how file_exists() behaves with symbolic links and .. ("dotdot" or double period) parent dir references. In that regard, it differs from functions like require, include or even mkdir().
Given this directory structure:
/home/me/work/example/
www/
/var/www/example.local/
tmp/
public_html -> /home/me/work/example/www/
file_exists('/var/www/example.local/public_html/../tmp/'); would return FALSE even though the subdir exists as we see, because the function traversed up into /home/me/work/example/ which does not have that subdir.
For this reason, I have created this function:
/**
* Resolve any ".." ("dotdots" or double periods) in a given path.
*
* This is especially useful for avoiding the confusing behavior `file_exists()`
* shows with symbolic links.
*
* #param string $path
*
* #return string
*/
function resolve_dotdots( string $path ) {
if (empty($path)) {
return $path;
}
$source = array_reverse(explode(DIRECTORY_SEPARATOR, $path));
$balance = 0;
$parts = array();
// going backwards through the path, keep track of the dotdots and "work
// them off" by skipping a part. Only take over the respective part if the
// balance is at zero.
foreach ($source as $part) {
if ($part === '..') {
$balance++;
} else if ($balance > 0) {
$balance--;
} else {
array_push($parts, $part);
}
}
// special case: path begins with too many dotdots, references "outside
// knowledge".
if ($balance > 0) {
for ($i = 0; $i < $balance; $i++) {
array_push($parts, '..');
}
}
$parts = array_reverse($parts);
return implode(DIRECTORY_SEPARATOR, $parts);
}
I just encountered this same problem and I solved it in a mysterious way. After inserting a a filepath I copied from Windows File explorer. file_exists() keeps returning false continuously, but if I copy same path from VSCode editor it works perfectly.
After dumping variables with var_dump($path); I noticed something mysterious.
For path I copied from file explorer it shows length 94.
For path I copied from VSCode Editor it shows length 88.
Both path look same length on my code Editor.
My suggestion: if string contain hidden characters, it may fail and not work.

Categories