ChatGPT解决这个技术问题 Extra ChatGPT

Create a folder if it doesn't already exist

I've run into a few cases with WordPress installs with Bluehost where I've encountered errors with my WordPress theme because the uploads folder wp-content/uploads was not present.

Apparently the Bluehost cPanel WordPress installer does not create this folder, though HostGator does.

So I need to add code to my theme that checks for the folder and creates it otherwise.

if (!file_exists('path/to/directory')) { mkdir('path/to/directory', 0777, true); }

r
reformed

Try this, using mkdir:

if (!file_exists('path/to/directory')) {
    mkdir('path/to/directory', 0777, true);
}

Note that 0777 is already the default mode for directories and may still be modified by the current umask.


You missed the 'recursive' flag - see Satish's answer.
is_dir() is bit faster, than file_exists()
@YuryPliashkou Yeah maybe, but it doesn’t work if there’s already a file with that name.
question here: so if there'd be a file named 'directory' in path/to , is_dir would return true, but file_exists would return false?
file_exists — Checks whether a file or directory exists is_file — Tells whether the filename is a regular file is_dir — Tells whether the filename is a directory
r
reformed

Here is the missing piece. You need to pass 'recursive' flag as third argument (boolean true) in mkdir call like this:

mkdir('path/to/directory', 0755, true);

the 'recursive' flag being the boolean 3rd argument true
P
Peter Mortensen

Here is something a bit more universal since this comes up on Google. While the details are more specific, the title of this question is more universal.

/**
 * recursively create a long directory path
 */
function createPath($path) {
    if (is_dir($path)) 
        return true;
    $prev_path = substr($path, 0, strrpos($path, '/', -2) + 1 );
    $return = createPath($prev_path);
    return ($return && is_writable($prev_path)) ? mkdir($path) : false;
}

This will take a path, possibly with a long chain of uncreated directories, and keep going up one directory until it gets to an existing directory. Then it will attempt to create the next directory in that directory, and continue till it's created all the directories. It returns true if successful.

It could be improved by providing a stopping level so it just fails if it goes beyond the user folder or something and by including permissions.


@phazei I get a call to undefined function because of the line $return = createPath($prev_path);
Thanks @phazei :)
P
Peter Mortensen

Use a helper function like this:

function makeDir($path)
{
     $ret = mkdir($path); // use @mkdir if you want to suppress warnings/errors
     return $ret === true || is_dir($path);
}

It will return true if the directory was successfully created or already exists, and false if the directory couldn't be created.

A better alternative is this (shouldn't give any warnings):

function makeDir($path)
{
     return is_dir($path) || mkdir($path);
}

If you remove the @ and replace it by a proper is_dir check, my upvote is yours :) Bonus points for checking whether the parent directory is_writable() for a watertight helper function.
Using @ to suppress the errors is a performance hit. Better to check it doesn't already exist like Gumbo
Regardless of error suppression, I'm inclined to -1 for the first example. The second is so much better that the first is pointless.
This is difficult to read code just for the point of putting it on 1 line. The accepted answer is much clearer.
P
Peter Mortensen

A faster way to create a folder:

if (!is_dir('path/to/directory')) {
    mkdir('path/to/directory', 0777, true);
}

This would give error if there is a file named 'directory' at that path.
P
Peter Mortensen

Recursively create the directory path:

function makedirs($dirpath, $mode=0777) {
    return is_dir($dirpath) || mkdir($dirpath, $mode, true);
}

Inspired by Python's os.makedirs()


In what way is it recursive? Not in the function calls (it doesn't call itself)? What is the principle of operation? Why does work? Does mkdir do it recursively? Can you elaborate a little bit in your answer? (But without "Edit:", "Update:", or similar - the answer should appear as if it was written today.)
There is some explanation in Satish Gadhave's answer.
W
WP Punk

The best way is to use the wp_mkdir_p function. This function will recursively create a folder with the correct permissions.

Also, you can skip folder exists condition because the function returns:

true when the directory was created or existed before

false if you can't create the directory.

Example:

$path = 'path/to/directory';
if ( wp_mkdir_p( $path ) ) {
    // Directory exists or was created.
}

More: https://developer.wordpress.org/reference/functions/wp_mkdir_p/


Worth mentioning that wp_mkdir_p() will also return true if directory already exists, so you don't need any additional file_exists() checks. Also, it will do its best to set proper directory permissions. I'd always go with this function instead of reinventing the wheel.
P
Peter Mortensen

Within WordPress, there's also the very handy function wp_mkdir_p which will recursively create a directory structure.

Source for reference:

function wp_mkdir_p( $target ) {
    $wrapper = null;

    // Strip the protocol
    if( wp_is_stream( $target ) ) {
        list( $wrapper, $target ) = explode( '://', $target, 2 );
    }

    // From php.net/mkdir user contributed notes
    $target = str_replace( '//', '/', $target );

    // Put the wrapper back on the target
    if( $wrapper !== null ) {
        $target = $wrapper . '://' . $target;
    }

    // Safe mode fails with a trailing slash under certain PHP versions.
    $target = rtrim($target, '/'); // Use rtrim() instead of untrailingslashit to avoid formatting.php dependency.
    if ( empty($target) )
        $target = '/';

    if ( file_exists( $target ) )
        return @is_dir( $target );

    // We need to find the permissions of the parent folder that exists and inherit that.
    $target_parent = dirname( $target );
    while ( '.' != $target_parent && ! is_dir( $target_parent ) ) {
        $target_parent = dirname( $target_parent );
    }

    // Get the permission bits.
    if ( $stat = @stat( $target_parent ) ) {
        $dir_perms = $stat['mode'] & 0007777;
    } else {
        $dir_perms = 0777;
    }

    if ( @mkdir( $target, $dir_perms, true ) ) {

        // If a umask is set that modifies $dir_perms, we'll have to re-set the $dir_perms correctly with chmod()
        if ( $dir_perms != ( $dir_perms & ~umask() ) ) {
            $folder_parts = explode( '/', substr( $target, strlen( $target_parent ) + 1 ) );
            for ( $i = 1; $i <= count( $folder_parts ); $i++ ) {
                @chmod( $target_parent . '/' . implode( '/', array_slice( $folder_parts, 0, $i ) ), $dir_perms );
            }
        }

        return true;
    }

    return false;
}

Re "recursively create a directory structure": Does this suggest vanilla PHP functions can not do it?
P
Peter Mortensen

I needed the same thing for a login site. I needed to create a directory with two variables.

The $directory is the main folder where I wanted to create another sub-folder with the users license number.

include_once("../include/session.php");

$lnum = $session->lnum; // Users license number from sessions
$directory = uploaded_labels; // Name of directory that folder is being created in

if (!file_exists($directory . "/" . $lnum)) {
    mkdir($directory . "/" . $lnum, 0777, true);
}

A
Andreas

This is the most up-to-date solution without error suppression:

if (!is_dir('path/to/directory')) {
    mkdir('path/to/directory');
}

U
Usman Ahmed

For your specific question about WordPress, use the following code:

if (!is_dir(ABSPATH . 'wp-content/uploads')) wp_mkdir_p(ABSPATH . 'wp-content/uploads');

Function Reference: WordPress wp_mkdir_p. ABSPATH is the constant that returns WordPress working directory path.

There is another WordPress function named wp_upload_dir(). It returns the upload directory path and creates a folder if doesn't already exists.

$upload_path = wp_upload_dir();

The following code is for PHP in general.

if (!is_dir('path/to/directory')) mkdir('path/to/directory', 0777, true);

Function reference: PHP is_dir()


For the WordPress use, the more secure way to use is wp_get_upload_dir() function.
@IvijanStefanStipić Can you explain why do you think that my code is insecure? I intentionally didn't use wp_get_upload_dir() function. So the user could create any folder he wants, not just uploads folder.
That function return base director and base url of the upload folder. If someone use security plugin or CDN and want to change that, it will relay to that function. I develop WP pligins arround 8 years and trust me, in the production you need to think in advance. You just need to add your custom folder and create if not exists.
Why is it different in WordPress than vanilla PHP? Is it strictly necessary? More secure? (Not rhetorical questions.)
N
Nikunj Kathrotiya
$upload = wp_upload_dir();
$upload_dir = $upload['basedir'];
$upload_dir = $upload_dir . '/newfolder';
if (! is_dir($upload_dir)) {
   mkdir( $upload_dir, 0700 );
}

P
Peter Mortensen

If you want to avoid the file_exists vs. is_dir problem, I would suggest you to look here.

I tried this and it only creates the directory if the directory does not exist. It does not care if there is a file with that name.

/* Creates the directory if it does not exist */
$path_to_directory = 'path/to/directory';
if (!file_exists($path_to_directory) && !is_dir($path_to_directory)) {
    mkdir($path_to_directory, 0777, true);
}

P
Peter Mortensen

To create a folder if it doesn't already exist

Considering the question's environment.

WordPress.

Webhosting server.

Assuming it's Linux, not Windows running PHP.

And quoting from: mkdir

bool mkdir ( string $pathname [, int $mode = 0777 [, bool $recursive = FALSE [, resource $context ]]] )

The manual says that the only required parameter is the $pathname!

So, we can simply code:

<?php
    error_reporting(0); 

    if(!mkdir('wp-content/uploads')){
        // Todo
    }
?>

Explanation:

We don't have to pass any parameter or check if the folder exists or even pass the mode parameter unless needed; for the following reasons:

The command will create the folder with 0755 permission (the shared hosting folder's default permission) or 0777, the command's default.

mode is ignored on Windows hosting running PHP.

Already the mkdir command has a built-in checker for if the folder exists; so we need to check the return only True|False ; and it’s not an error; it’s a warning only, and Warning is disabled on the hosting servers by default.

As per speed, this is faster if warning disabled.

This is just another way to look into the question and not claiming a better or most optimal solution.

It was tested on PHP 7, production server, and Linux


M
Mayur Kukadiya
if (!is_dir('path_directory')) {
    @mkdir('path_directory');
}

With error suppression, there is no need in checking that directory exists
it's better to handle errors than suppress them. If this fails, you will never know why from this, and will have to research it
In highly concurrent/multithreaded environments it is advisable to suppress the error. A race condition might occur in which two or more threads will evaluate is_dir() to false and will try to create the directory. The first thread will be able to create it without any problem, but the other threads will fail to do so, because the directory already exists. To avoid missing an actually failed directory creation you should check the existence of the directory after the call to @mkdir() again.
U
ULIDU THEERAKE

Here you go.

if (!is_dir('path/to/directory')) {
    if (!mkdir('path/to/directory', 0777, true) && !is_dir('path/to/directory')) {
        throw new \RuntimeException(sprintf('Directory "%s" was not created', 'path/to/directory'));
    }
}

PHPStorm (with PHP Inspections) gives exactly this suggestion ;-) btw. you can merge the outer if into the inner: if (!is_dir(...) && !mkdir(...) && !is_dir(...)) ...
@aProgger If looks better when you separate them. a lot of programmers won't merge the if statements.
It's a matter of taste. I would merge them and even kick the braces, to get an one liner. But I am curious. Would you seperate only the first !is_dir or the last one too?
Wow, out of all the answers this is the only robust one with meaningful error-handling. Congratulations, you win my Internet-points.
s
simhumileco

You can try also:

$dirpath = "path/to/dir";
$mode = "0764";
is_dir($dirpath) || mkdir($dirpath, $mode, true);

J
Jens Törnell

As a complement to current solutions, a utility function.

function createDir($path, $mode = 0777, $recursive = true) {
  if(file_exists($path)) return true;
  return mkdir($path, $mode, $recursive);
}

createDir('path/to/directory');

It returns true if already exists or successfully created. Else it returns false.


P
Peter Mortensen

We should always modularise our code and I've written the same check it below...

We first check the directory. If the directory is absent, we create the directory.

$boolDirPresents = $this->CheckDir($DirectoryName);

if (!$boolDirPresents) {
        $boolCreateDirectory = $this->CreateDirectory($DirectoryName);
        if ($boolCreateDirectory) {
        echo "Created successfully";
      }
  }

function CheckDir($DirName) {
    if (file_exists($DirName)) {
        echo "Dir Exists<br>";
        return true;
    } else {
        echo "Dir Not Absent<br>";
        return false;
    }
}
     
function CreateDirectory($DirName) {
    if (mkdir($DirName, 0777)) {
        return true;
    } else {
        return false;
    }
}

N
Nasir Khan

You first need to check if directory exists file_exists('path_to_directory')

Then use mkdir(path_to_directory) to create a directory

mkdir( string $pathname [, int $mode = 0777 [, bool $recursive = FALSE [, resource $context ]]] ) : bool

More about mkdir() here

Full code here:

$structure = './depth1/depth2/depth3/';
if (!file_exists($structure)) {
    mkdir($structure);
}