Like many others, I was quite excited to see PHP 5.3 come out and immediately wanted to start using it. Unfortunately reality set in in the form of legacy sites running 5.2 (or older) and the need to maintain them. My solution is to have multiple versions of PHP running which can be configured on a per virtual host basis, specifically PHP 5.2.10 & 5.3 with Apache 2.2.12 and MySQL 5.1.36. I compiled the software on a Mac, but the instructions should work with little or no modification on most *nix flavors.
Disclaimer: This is what my dev setup is on my local machines (MBP & Mac Pro) and what works for me. I make no guarantees that this will work for you or that you won’t ruin your OS doing this.
Prerequisites
Since we are compiling from source, we’re gonna need gcc and make among other programs. They can all be downloaded for free with the Apple Developer Tools.
I like putting what I compile in a parent directory “/opt” for clarity and to not affect any built in OS X files. To do this:
sudo mkdir -p /opt/src sudo chmod g+w /opt/src cd /opt/src
Wget is going to be used to retrieve tarballs without having to leave the command line. It’s quite simple to install and will get your feet wet if you’ve never compiled software from source before. To install wget, you’re gonna have to download via browser the first time.
Download: http://ftp.gnu.org/gnu/wget/wget-latest.tar.bz2
Then in Terminal:
cp ~/Downloads/wget-latest.tar.bz2 . tar xvjf wget-latest.tar.bz2 cd wget-1.11.4/ ./configure --prefix=/opt make sudo make install
Remaining steps from here will be done via Terminal.
Now we want to add /opt/bin to our PATH so we don’t have to type it out every time. Open up ‘~/.profile’ with your favorite text editor *cough*VI*cough* and prepend it to the variable. In my case it looks like this:
export PATH="/opt/bin:$PATH"
Finally source the file to apply the changes:
. ~/.profile
You only have to this once. Next time you open Terminal it will be sourced automatically.
Still with me? Then on to…
Installing MySQL
Download and install:
cd /opt/src wget http://dev.mysql.com/get/Downloads/MySQL-5.1/mysql-5.1.36.tar.gz/from/http://mysql.he.net/ tar xvzf mysql-5.1.36.tar.gz cd mysql-5.1.36 CC=gcc CFLAGS="-O3 -fno-omit-frame-pointer" CXX=gcc \ CXXFLAGS="-O3 -fno-omit-frame-pointer -felide-constructors -fno-exceptions -fno-rtti" \ ./configure --prefix=/opt/mysql \ --localstatedir=/opt/mysql/data \ --with-extra-charsets=complex \ --enable-thread-safe-client \ --enable-local-infile \ --enable-assembler \ --with-mysqld-ldflags=-all-static \ --with-plugins=innobase make sudo make install
Create some symlinks from /opt/bin to MySQL’s bin:
sudo ln -s /opt/mysql/bin/* /opt/bin/.
Initialize the system tables and run it for the first time:
sudo mysql_install_db --user=mysql sudo mysqld_safe --user=mysql --log &
SECURE THE ROOT ACCOUNT AND REMOVE ANONYMOUS ACCESS:
mysql -u root
mysql> delete from mysql.user where User != 'root';
mysql> update mysql.user set Password=PASSWORD('supersecretpassword') where User='root';
mysql> flush privileges;
mysql> exit
Now you should need the password to connect as root, and anonymous access should be disabled.
Installing Apache
Download and install:
cd /opt/src wget http://apache.mirror.facebook.net/httpd/httpd-2.2.12.tar.bz2 tar xvjf httpd-2.2.12.tar.bz2 cd httpd-2.2.12 ./configure --prefix=/opt/apache2 \ --enable-mods-shared=all \ --enable-ssl \ --with-mpm=worker \ --enable-suexec \ --with-suexec-bin=/opt/apache2/bin \ --with-suexec-caller=_www \ --with-suexec-docroot=/projects make sudo make install sudo ln -s /opt/apache2/bin/* /opt/bin/.
We’ll come back to the Apache config after installing PHP.
Installing mod_fastcgi
Download and install:
cd /opt/src wget http://www.fastcgi.com/dist/mod_fastcgi-SNAP-0811090952.tar.gz tar xvzf mod_fastcgi-SNAP-0811090952.tar.gz cd mod_fastcgi-SNAP-0811090952.tar.gz cp Makefile.AP2 Makefile make top_dir=/opt/apache2 sudo make install top_dir=/opt/apache2
Create a tmp folder for FastCGI:
sudo mkdir -p /opt/tmp/fcgi sudo chmod -R 0777 /opt/tmp/fcgi
Create the config file for mod_fastcgi at “/opt/apache2/conf/extra/httpd-fastcgi.conf”:
LoadModule fastcgi_module modules/mod_fastcgi.so FastCgiIpcDir /opt/tmp/fcgi AddHandler fastcgi-script .fcgi FastCgiConfig -autoUpdate -singleThreshold 100 -killInterval 300 AddType application/x-httpd-php .php ScriptAlias /fastcgi/ /opt/apache2/cgi-bin/ <Directory "/opt/apache2/cgi-bin"> Options ExecCGI SetHandler fastcgi-script Order allow,deny Allow from all </Directory>
Install PHP 5.2.10
Download and install:
cd /opt/src wget http://us.php.net/get/php-5.2.10.tar.bz2/from/us2.php.net/mirror tar xvjf php-5.2.10.tar.bz2 cd php-5.2.10 ./configure --prefix=/opt/php5.2 \ --with-config-file-path=/opt/php5.2 \ --with-mysqli=/opt/bin/mysql_config \ --with-mysql=/opt/mysql \ --with-curl \ --enable-cli \ --enable-fastcgi \ --enable-discard-path \ --enable-force-cgi-redirect make sudo make install
Copy php.ini to your install dir:
sudo cp /opt/src/php-5.2.10/php.ini-dist /opt/php5.2/php.ini
Optional, but highly recommended. Open php.ini, change error_reporting to strict and turn magic quotes off:
error_reporting = E_ALL | E_STRICT magic_quotes_gpc = Off
Finally, create a PHP fcgi “wrapper” script at “/opt/apache2/cgi-bin/php52.fcgi” (you will have to use sudo):
#!/bin/sh PHP_FCGI_CHILDREN=1 export PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS=5000 export PHP_FCGI_MAX_REQUESTS exec /opt/php5.2/bin/php-cgi
Set the wrapper to be executable:
sudo chmod +x /opt/apache2/cgi-bin/php52.fcgi
Installing PHP 5.3
Download and install:
cd /opt/src wget http://us3.php.net/get/php-5.3.0.tar.bz2/from/us2.php.net/mirror tar xvjf php-5.3.0.tar.bz2 cd php-5.3.0 ./configure --prefix=/opt/php5.3 \ --with-config-file-path=/opt/php5.3 \ --with-curl \ --enable-cli \ --with-mysql=mysqlnd \ --with-mysqli=mysqlnd \ --with-pdo-mysql=mysqlnd make sudo make install
Copy over php.ini:
sudo cp /opt/src/php-5.3.0/php.ini-development /opt/php5.3/php.ini
And create the wrapped like we did for 5.2, but with a different name “/opt/apache2/cgi-bin/php53.fcgi”:
#!/bin/sh PHP_FCGI_CHILDREN=1 export PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS=5000 export PHP_FCGI_MAX_REQUESTS exec /opt/php5.3/bin/php-cgi
Lastly, make it executable:
sudo chmod +x /opt/apache2/cgi-bin/php53.fcgi
Tying it all together
Now we’ve got our MAMP2 stack ready to go, just need to setup our Apache config to use it.
Open up “/opt/apache2/conf/httpd.conf” and make the following changes:
- Change “User” and “Group” to “_www”
- Add index.php to “DirectoryIndex”. Should look something like “DirectoryIndex index.php index.html”
- Add permissions to your docroot. (I keep all my sites under “/projects”, just replace that with whatever you use, i.e. “/var/www”, “~/sites”):
<Directory "/projects"> Options Indexes FollowSymLinks AllowOverride All Order allow,deny Allow from all </Directory>
- Uncomment “Include conf/extra/httpd-vhosts.conf”
- Add “Include conf/extra/httpd-fastcgi.conf”
Set up virtual hosts. In “/opt/apache2/conf/extra/httpd-vhosts.conf”, delete everything below the “NameVirtualHost” directive and add:
<VirtualHost *:80> ServerAdmin you@domain.com DocumentRoot "/projects/site1" ServerName site1 ErrorLog "logs/site1-error_log" CustomLog "logs/site1-access_log" common Action application/x-httpd-php /fastcgi/php52.fcgi </VirtualHost> <VirtualHost *:80> ServerAdmin you@domain.com DocumentRoot "/projects/site2" ServerName site2 ErrorLog "logs/site2-error_log" CustomLog "logs/site2-access_log" common Action application/x-httpd-php /fastcgi/php53.fcgi </VirtualHost>
For testing purposes, let’s set up a simple PHP file in each VHost. For “site1″ the path to the file will be “/projects/site1/index.php” and look like this:
<h2>Site 1</h2> <?php phpinfo(); ?>
For “site2″ it will be at “/projects/site2/index.php” and look like this:
<h2>Site 2</h2> <?php phpinfo(); ?>
The last step is to set up your hosts file (sudo vim /etc/hosts) so you can get to the sites. Add the following entry
127.0.0.1 site1 site2
Now fire up your favorite browser and point it at “http://site1″ and “http://site2″ respectively. You should see something like this:
That’s it!
Please respond in the comments if you find any glaring errors or security issues, or just to say this helped!
Next post will be how to set up MySQL and Apache to start automatically via launchd.
References:
http://dev.mysql.com/doc/refman/5.1/en/index.html
http://httpd.apache.org/docs/2.2/
http://www.php.net/manual/en/
http://www.fastcgi.com/docs/faq.html
http://timdorr.com/archives/2006/02/php4-and-php4-s.php






Very nicely done Robert. BTW, have you ever thought of switching out apache for lighttpd or nginx? One of these days I want to try them out on an Amazon instance. Supposed to be WAAY faster than apache.
I haven’t used Nginx, but I have used Lighty quite a bit. I think the advantage of using Lighty over Apache is that it’s smaller and faster for serving up static files. As far as FastCGI is concerned, I personally haven’t seen a huge difference in performance.
In all honesty, I don’t really prefer one over the other. They both work great, I tend to stick to Apache though because it is more widely used and sometimes you don’t get to choose what the http server is.
This is a great idea, and it really helped me along, but I’m having a problem I can’t solve. I’m running the latest version of Leopard on a Mac Mini. For one particular webmail application, I need to drop back to PHP 5.2 (installed at the usual /usr/bin/php). For everything else I’m using PHP 5.3 (installed in /opt/local/bin/php).
To force the redirect for the /mail subdirectory, I created a .htaccess file containing the following:
AddHandler x-httpd-php php
Action x-httpd-php /usr/bin/php
The server is actually picking it up as expected, but it seems to be doing the wrong thing with it. Rather than executing “/usr/bin/php mail/index.php” (note the space between “php” and “mail”, it’s instead executing “/usr/bin/php/mail/index.php” — jamming everything together into a single string and trying to execute that. Since the path “/usr/bin/php/mail/index.php” doesn’t exist, I’m getting a 404.
Any ideas?
It’s really hard to say without knowing more about the setup or which app it is. The first place I would check is the webmail script that executes the command.
[...] found this one after I wrote up this tutorial at http://cuadradevelopment.com. It’s a bit different, but should work as [...]
Great write up. Thanks!
I only have one strange problem – the full php configuration I used does not show up, only a partial configuration appears.
FYI, I’m using Snow Leopard’s build in Apache, for simplicity, php 5.2.12 and php 5.3.1.
After I comment out the php make install LoadModule php5_module in httpd.conf, in the result phpinfo() output I get (php 5.3.1):
Configure Command ‘./configure’ ‘–prefix=/opt/php5.3′ ‘–with-config-file-path=/opt/php5.3′ ‘–with-curl’ ‘–enable-cli’ ‘–with-mysql=mysqlnd’ ‘–with-mysqli=mysqlnd’ ‘–with-pdo-mysql=mysqlnd’
However if I reactive LoadModule php5_module in httpd.conf and restart the server (forcing the direct use of php) I get:
Command ‘./configure’ ‘–prefix=/opt/php5.3′ ‘–with-config-file-path=/opt/php5.3′ ‘–with-curl’ ‘–enable-cli’ ‘–with-mysql=mysqlnd’ ‘–with-mysqli=mysqlnd’ ‘–with-pdo-mysql=mysqlnd’ ‘–with-zlib’ ‘–with-zlib-dir=/usr’ ‘–with-apxs2=/usr/sbin/apxs’ ‘–with-ldap=/usr’ ‘–with-iodbc=/usr’ ‘–with-curl=/usr’ ‘–with-mysql-sock=/tmp’ ‘–with-openssl=/usr’ ‘–with-xmlrpc’ ‘–with-pear’ ‘–with-iconv-dir=/usr/local’ ‘–with-gd’ ‘–with-jpeg-dir=/usr/local/lib’ ‘–with-png-dir=/usr/X11R6′ ‘–with-freetype-dir=/usr/X11R6′ ‘–with-xpm-dir=/usr/X11R6′ ‘–with-sqlite’ ‘–with-pdo-sqlite’ ‘–with-ldap=/usr’ ‘–with-ldap-sasl=/usr’ ‘–with-bz2=/usr’ ‘–enable-exif’ ‘–enable-ftp’ ‘–enable-sockets’ ‘–enable-mbstring’ ‘–enable-mbregex’ ‘–enable-soap’ ‘–enable-bcmath’ ‘–enable-dba’ ‘–enable-gd-native-ttf’
The result is roughly the same for php 5.2.12.
No errors in the error log. Just missing most of my php configuration, which, as you can imagine, hamstrings the application!
Any ideas?
I like your weblog very much. Will bookmark. Keep up to briliant posting on it. Thank you