Without a doubt the biggest “gotcha” when working with files in PHP is permissions. Simply put in order for any PHP script to write to a file the Apache needs to have privileges to that file. This can be a bit of an issue in a shared hosting environment especially. Your webhost maybe running Apache under one user and for manipulating files and directories you are required to use FTP or shell which operates under a different user. So how can you make sure you have the correct permissions? First port of call is definitely your webhost. They should indicate in their support information the permissions required in order for your PHP scripts to write to a file on the server. For most it will be 777, which means EVERYONE has read/write/execute access - so be weary sensitive files may be accessible!
How to set permissions on your webserver:
1. Using FTP:
Probably the easiest way of setting permissions is through an FTP Application (Eg Core FTP & Filezilla). Usually it’s simply a matter of right-clicking and entering the numeric permission (777) or checking the correct boxes for “Owner” “Group” and “Other” (all for 777). You may also be able to recursively set permissions on any child files.
2. Through Linux Shell:
Linux shell is a little more advanced, offering a few more options but not all webhosts offer shell access. If you do have shell access you can run the chmod command followed by the numeric permission like so:
$ chmod 777 uploads
Additional options are set by passing -flags. For example for recursive permission setting of the uploads directory:
$ chmod -R 777 uploads
3. In PHP script:
A number of PHP functions allow you to set file permissions on the fly. However, to do this Apache needs to have permission to write to the parent directory in the first place! Here are some examples:
Creating a directory with 777 permissions -
// Under Linux
mkdir('/absolute/path/to/uploads/images', 0777); // beginning '/' is important!
// Under Windows
mkdir('E:/absolute/path/to/uploads/images', 0777); // windows has driver name instead of beginning slash
// Creating a directory recursively
// (see below)
(note: mkdir() actually sets directories to 777 by default)
Setting an image file to 777 so we can manipulate it with PHP’s various image functions -
chmod('/absolute/path/to/uploads/images/pic_thumb.jpg', 0777);
Getting around open_basedir restrictions
One little speedhump I discovered recently was the effect of open_basedir restrictions on directory creation. I was creating a directory tree recursively by passing the absolute path, then splitting it up into directories and creating each directory incrementally based on whether is existed or not.
$folder = preg_split( "/[/]/" , '/absolute/path/to/uploads/images' );
// append beginning slash for Linux
$mkfolder = strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' ? '' : DIRECTORY_SEPARATOR;
for( $i=0 ; isset( $folder[$i] ) ; $i++ )
{
if(!strlen(trim($folder[$i])))continue;
$mkfolder .= $folder[$i];
if( !is_dir( $mkfolder ) ){
mkdir( "$mkfolder" , 0777);
}
$mkfolder .= DIRECTORY_SEPARATOR;
}
Problem was being an absolute path some of the directories were outside of the webroot and PHP was unable to run any checks on the directories due to permissions! The solution was to do the checks using shell instead via PHP’s exec() function so i created by own is_dir function open_basedir_is_dir():
// open_basedir safe is_dir function
function open_basedir_is_dir($dir) {
// get base_dir settings
$open_basedir = @ini_get('open_basedir');
// if open_basedir has not been set we'll use normal check
if (empty($open_basedir)) {
return is_dir($dir);
} else {
// run shell command
exec("ls -dl $dir", $tmp_cmd);
// if nothing set assuming dir doesn't exist, or if not "d" flag its not a dir
// [d] directory [l] link [-] regular file)
return (isset($tmp_cmd[0]) && substr($tmp_cmd[0], 0, 1) == 'd');
}
}
$folder = preg_split( "/[/]/" , '/absolute/path/to/uploads/images' );
// append beginning slash for Linux
$mkfolder = strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' ? '' : DIRECTORY_SEPARATOR;
for( $i=0 ; isset( $folder[$i] ) ; $i++ )
{
if(!strlen(trim($folder[$i])))continue;
$mkfolder .= $folder[$i];
if( !open_base_dir_is_dir( $mkfolder ) ){
mkdir( "$mkfolder" , 0777);
}
$mkfolder .= DIRECTORY_SEPARATOR;
}