Advanced PHP configuration
From DreamHost
| The instructions provided in this article or section are considered advanced. You are expected to be knowledgeable in the UNIX shell. |
Introduction
This article is intended to bundle up some of the more complex scenarios of PHP configuration into a single place.
If you follow the steps outlined here you will optionally end up with:
- The ability to have a local php binary with a custom php.ini file and to keep that binary and ini file synced with the current Dreamhost php.ini
- The ability to share that local php custom install amongst all your domains in your home directory
- For PHP 5, the ability to wrap that custom install in a fastcgi wrapper
- The ability to run PHP 4 and PHP 5 in a single domain (in different directories)
Instructions
These instructions will walk through steps to setup PHP5, with options along the way to also set up PHP4 at the same time.
Step 1 - Set up local PHP directory
First SSH into your home directory, then issue the following commands relevant to your target version/s of PHP.
PHP 4
mkdir -m 771 ~/php
PHP 5
mkdir -m 771 ~/php5
PHP 4 and 5
Run both the above commands.
Step 2 (optonal) - Create FastCGI wrapper
PHP 5 only.
PHP 5
cat > ~/php5/php5-wrapper.fcgi << "EOF" #!/bin/bash export PHP_FCGI_CHILDREN=3 exec ./php5.cgi EOF
Then set it to be executable:
chmod 0750 ~/php5/php5-wrapper.fcgi
Step 3 - Create php-update shell script
Create a new file for the shell script.
PHP 4
touch ~/php/php-update.sh
PHP 5
touch ~/php5/php-update.sh
PHP 4 and 5
Copy the following code into a the new file using a text editor.
#!/bin/bash
# CLI script for updating a local copy of php in Dreamhost accounts.
#
# Copyright (c) 2008, Sam Bauers
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the copyright holder nor the names of any
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# @author Sam Bauers
# @version 0.1.7
# @copyright Sam Bauers, 2008
# @license BSD
echo
echo "=========================================================="
echo "php-update for Dreamhost version 0.1.7"
echo "Copyright (C) 2006 Sam Bauers"
echo "----------------------------------------------------------"
echo "php-update for Dreamhost comes with ABSOLUTELY NO WARRANTY"
echo "This is free software, and you are welcome to redistribute"
echo "it under certain conditions."
echo
echo "See source code for details."
echo "=========================================================="
echo
UPDATEDIR=$(dirname $0)
$UPDATEDIR/php-update.php
if [[ -e "$UPDATEDIR/php.cgi" ]]
then
echo
echo "----------------------------------------------------------"
echo "Finally - attempting to make php.cgi executable"
echo "----------------------------------------------------------"
chmod 0750 $UPDATEDIR/php.cgi
fi
if [[ -e "$UPDATEDIR/php5.cgi" ]]
then
echo
echo "----------------------------------------------------------"
echo "Finally - attempting to make php5.cgi executable"
echo "----------------------------------------------------------"
chmod 0750 $UPDATEDIR/php5.cgi
fi
Step 4 - Create php-update PHP script
Create a new file for the PHP script.
PHP 4
touch ~/php/php-update.php
PHP 5
touch ~/php5/php-update.php
PHP 4 and 5
Copy the following code into a the new file using a text editor.
#!/usr/local/php5/bin/php -q
<?php
// Note "-q" in shebang used to suppress header output
/**
* CLI script for updating a local copy of php in Dreamhost accounts.
*
* Copyright (c) 2008, Sam Bauers
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the copyright holder nor the names of any
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @author Sam Bauers
* @version 0.1.7
* @copyright Sam Bauers, 2008
* @license BSD
*/
// Determine the version of PHP to update (4 or 5) based on the directory
$directories = explode('/', dirname(__FILE__));
$directories = array_reverse($directories);
$directory = $directories[0];
switch ($directory) {
case 'php':
$php = 'php';
break;
case 'php5':
default:
$php = 'php5';
break;
}
// The full paths to the source cgi and ini
$sourcecgi = '/dh/cgi-system/' . $php . '.cgi';
$sourceini = '/etc/' . $php . '/cgi/php.ini';
// Get the HOME environment variable
$home = $_SERVER['HOME'];
// The full paths to the target cgi and ini
$targetcgi = $home . '/' . $php . '/' . $php . '.cgi';
$targetini = $home . '/' . $php . '/php.ini';
// The full path to the ini file kept for comparison with the source ini
$unmodifiedini = $targetini . '.unmodified';
$updatetargetini = FALSE;
// Modifications to be made to the ini file after it is updated
// - If you need to change these then you can do so here, then
// delete your target ini files and run the script again
$inimodifications = array(
'output_buffering = 0',
'post_max_size = 100M',
'upload_max_filesize = 100M'
);
// Update the CGI
echo("\n");
echo('----------------------------------------------------------' . "\n");
echo('Attempting CGI update' . "\n");
echo('----------------------------------------------------------' . "\n");
if (!file_exists($sourcecgi)) {
echo('!!! SOURCE CGI does not exist - aborting CGI update' . "\n");
} else {
echo('--> SOURCE CGI exists' . "\n");
if (!file_exists($targetcgi)) {
echo(' --> TARGET CGI does not exist - make the initial copy' . "\n");
if (copy($sourcecgi, $targetcgi)) {
echo(' --> SOURCE CGI copied to TARGET CGI' . "\n");
} else {
echo(' !!! copy from SOURCE CGI to TARGET CGI failed - check permissions on target directory' . "\n");
}
} else {
echo('--> TARGET CGI exists' . "\n");
echo('--> compare source to target using md5' . "\n");
$sourcecgimd5 = md5_file($sourcecgi);
$targetcgimd5 = md5_file($targetcgi);
if ($sourcecgimd5 !== $targetcgimd5) {
echo(' --> they are different - update the TARGET CGI' . "\n");
if (copy($sourcecgi, $targetcgi)) {
echo(' --> SOURCE CGI copied to TARGET CGI' . "\n");
/*
if (chmod($targetcgi, 0750)) {
echo(' --> execute permission set on TARGET CGI' . "\n");
} else {
echo(' !!! could not set execute permission on TARGET CGI - try manual chmod' . "\n");
}
*/
} else {
echo(' !!! copy from SOURCE CGI to TARGET CGI failed - check permissions on target directory' . "\n");
}
} else {
echo(' --> they are the same - take no action' . "\n");
}
}
}
echo('----------------------------------------------------------' . "\n");
echo("\n");
// Update the INI
echo('----------------------------------------------------------' . "\n");
echo('Attempting INI update' . "\n");
echo('----------------------------------------------------------' . "\n");
if (!file_exists($sourceini)) {
echo('!!! SOURCE INI does not exist - aborting INI update' . "\n");
} else {
echo('--> SOURCE INI exists' . "\n");
if (!file_exists($unmodifiedini)) {
echo(' --> UNMODIFIED INI does not exist - make the initial copy' . "\n");
if (copy($sourceini, $unmodifiedini)) {
echo(' --> SOURCE INI copied to UNMODIFIED INI' . "\n");
$updatetargetini = TRUE;
} else {
echo(' !!! copy from SOURCE INI to UNMODIFIED INI failed - check permissions on target directory' . "\n");
}
} else {
echo('--> UNMODIFIED INI exists' . "\n");
echo('--> compare source to unmodified using md5' . "\n");
$sourceinimd5 = md5_file($sourceini);
$unmodifiedinimd5 = md5_file($unmodifiedini);
if ($sourceinimd5 !== $unmodifiedinimd5) {
echo(' --> they are different - update the UNMODIFIED INI' . "\n");
if (copy($sourceini, $unmodifiedini)) {
echo(' --> SOURCE INI copied to UNMODIFIED INI' . "\n");
$updatetargetini = TRUE;
} else {
echo(' !!! copy from SOURCE INI to UNMODIFIED INI failed - check permissions on target directory' . "\n");
}
} else {
echo(' --> they are the same - take no action' . "\n");
}
}
}
echo('----------------------------------------------------------' . "\n");
// Modify the target INI if required
if ($updatetargetini) {
echo("\n");
echo('----------------------------------------------------------' . "\n");
echo('Attempting INI modification' . "\n");
echo('----------------------------------------------------------' . "\n");
if (copy($unmodifiedini, $targetini)) {
echo('--> UNMODIFIED INI copied to TARGET INI' . "\n");
if (count($inimodifications) <= 0) {
echo(' !!! no modifications specified' . "\n");
} else {
echo(' --> attempt to append modifications to TARGET INI' . "\n");
$appendstring = "\n\n\n";
$appendstring .= ';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;' . "\n";
$appendstring .= '; Dreamhost php-update script ;' . "\n";
$appendstring .= ';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;' . "\n";
$appendstring .= ';' . "\n";
$appendstring .= '; Script location -> ' . __FILE__ . "\n";
$appendstring .= "\n";
$appendecho = ' --> appended the following ini settings:' . "\n";
foreach ($inimodifications as $iniline) {
$appendstring .= $iniline . "\n";
$appendecho .= ' * ' . $iniline . "\n";
}
$appendedbytes = file_put_contents($targetini, $appendstring, FILE_APPEND);
if ($appendedbytes) {
echo(' --> modification successful - appended ' . $appendedbytes . ' bytes to TARGET INI' . "\n");
echo($appendecho);
} else {
echo(' !!! modification failed - check permissions on TARGET INI' . "\n");
}
}
} else {
echo('!!! copy from UNMODIFIED INI to TARGET INI failed' . "\n");
}
echo('----------------------------------------------------------' . "\n");
}
?>
Step 5 - make the php-update scripts executable
The scripts need to be executable to run.
PHP 4
chmod 0700 ~/php/php-update.sh chmod 0700 ~/php/php-update.php
PHP 5
chmod 0700 ~/php5/php-update.sh chmod 0700 ~/php5/php-update.php
PHP 4 and 5
Run both the above commands.
Step 6 - run the php-update script
Execute the php-update shell script to create your local versions.
If you want to make any customisations to the php.ini, they should be set in the php-update.php file as described on line 62 (version 0.1.4) prior to execution. If you don't setup your customisations in the php script, then they will be overwritten if Dreamhost's source php.ini file changes.
If you change your customisations at any time, you will need to delete the files php.ini and php.ini.unmodified, and then execute the php-update shell script again.
PHP 4
cd ~/php ./php-update.sh
After the script has executed read the output and act on any errors. If there were no errors then check the file listing.
ls -lh ~/php
The output should be like:
-rw-r--r-- 1 username pg123456 18K 2004-09-23 06:31 LICENSE -rwx------ 1 username pg123456 7.4K 2004-09-23 06:31 php-update.php -rwx------ 1 username pg123456 2.0K 2004-09-23 06:36 php-update.sh -rwxr-x--- 1 username pg123456 3.3M 2004-09-23 06:36 php.cgi -rw-rw-r-- 1 username pg123456 22K 2004-09-23 06:36 php.ini -rw-rw-r-- 1 username pg123456 22K 2004-09-23 06:36 php.ini.unmodified
PHP 5
cd ~/php5 ./php-update.sh
After the script has executed read the output and act on any errors. If there were no errors then check the file listing.
ls -lh ~/php5
The output should be like:
-rwx------ 1 username pg123456 7.4K 2004-09-23 06:31 php-update.php -rwx------ 1 username pg123456 2.0K 2004-09-23 06:31 php-update.sh -rw-rw-r-- 1 username pg123456 45K 2004-09-23 06:38 php.ini -rw-rw-r-- 1 username pg123456 45K 2004-09-23 06:38 php.ini.unmodified -rwxr-x--- 1 username pg123456 53 2004-09-23 06:31 php5-wrapper.fcgi <--- *** FastCGI ONLY *** -rwxr-x--- 1 username pg123456 5.4M 2004-09-23 06:38 php5.cgi
PHP 4 and 5
Run both the above commands.
Step 7 - periodically update the php binary and ini files
The php-update script should be run at some interval to check if there is a newer version of the Dreamhost php binary and ini files.
The script actually checks whether the files have changed and only copies them across to your local version if they have.
Add the script to crontab and check for a new file weekly.
To edit your crontab file type:
crontab -e
Paste the following line/s into the editor that appears:
PHP 4
@weekly /bin/bash $HOME/php/php-update.sh
PHP 5
@weekly /bin/bash $HOME/php5/php-update.sh
PHP 4 and 5
@weekly /bin/bash $HOME/php/php-update.sh @weekly /bin/bash $HOME/php5/php-update.sh
Close the editor by pressing CTRL-x, follow the prompts it gives you to save and exit.
Step 8 - alias the local php directories into your website domains
You can alias this local install into the base directory of as many websites as you like.
Let's assume the website is setup at ~/example.com
PHP 4
cd ~/example.com ln -s ~/php .
PHP 5
cd ~/example.com ln -s ~/php5 .
PHP 4 and 5
cd ~/example.com ln -s ~/php . ln -s ~/php5 .
Step 9 - setup .htaccess files
The .htaccess file will point php scripts in your domain/s to the local versions we have setup.
Assume that you are still working on the domain example.com and that it has not got an .htaccess file setup already. If it does, then you simply need to edit the existing file and not run these commands.
PHP 4
cat > ~/example.com/.htaccess << "EOF" Options +ExecCGI AddHandler php-cgi .php Action php-cgi /php/php.cgi EOF
PHP 5
cat > ~/example.com/.htaccess << "EOF" Options +ExecCGI AddHandler php5-cgi .php Action php5-cgi /php5/php5.cgi EOF
PHP 4 and 5
This method only allows running of one of the two versions in the base directory, then you can over-ride that version on a per directory basis using another .htaccess file.
These instructions will setup PHP 5 for the whole site and then setup PHP 4 in the subdirectory "testPHP4".
Create the .htaccess file for PHP 5 in the site root directory
cat > ~/example.com/.htaccess << "EOF" Options +ExecCGI AddHandler php5-cgi .php Action php5-cgi /php5/php5.cgi EOF
Create a test file for PHP 5
cat > ~/example.com/info.php << "EOF" <?php phpinfo(); ?> EOF
Create the PHP 4 test directory
mkdir ~/example.com/testPHP4
Create the .htaccess file for PHP 4 in the PHP 4 test directory
cat > ~/example.com/testPHP4/.htaccess << "EOF" AddHandler php-cgi .php Action php-cgi /php/php.cgi EOF
Create a test file for PHP 4 in the PHP 4 directory
cat > ~/example.com/testPHP4/info.php << "EOF" <?php phpinfo(); ?> EOF
Visiting the address http://example.com/info.php will show the PHP 5 info page.
Visiting the address http://example.com/testPHP4/info.php will show the PHP 4 info page.
Step 10 - Secure php.ini and php cgi
Protect your new php.ini and php cgi's
In the file ~/example.com/.htaccess add
<FilesMatch "^php5?\.(ini|cgi)$"> Order Deny,Allow Deny from All Allow from env=REDIRECT_STATUS </FilesMatch>
OR: For even better protection, protect the entire directory instead.
Create the file ~/php5/.htaccess and add:
<FilesMatch ".*"> Order Deny,Allow Deny from All Allow from env=REDIRECT_STATUS </FilesMatch>
Step 11 (optonal) - FastCGI setup
PHP 5 only.
PHP 5
To use FastCGI with PHP 5 you need to modify any .htaccess that specify use of the php5.cgi binary this:
Remove the following lines from the relevant .htaccess files...
AddHandler php5-cgi .php Action php5-cgi /php5/php5.cgi
And replace them with these...
AddHandler php5-fastcgi .php Action php5-fastcgi /php5/php5-wrapper.fcgi
Be careful not to wreck any earlier FastCGI settings in your .htaccess file.
Also make sure that FastCGI is enabled for this domain in the Dreamhost Control Panel.
See also
References
Much of the information used to produce this set of instructions came from the following pages:
Thanks go to their authors and contributors.

