I just bought an Intel NUC (Next Unit of Computing) mini-PC to serve as my secondary, Windows-powered development machine (the primary is an Apple Mac Mini). I decided it would be a fun* weekend project to implement an Apache-MySQL-PHP web server without using a pre-packaged server like XAMPP, WAMPServer etc. My goal was to have the same sites run under different versions of PHP by just visiting a different URL on my browser.

* for extremely geek values of "fun"

We'll need to do four steps. Set up the PHP versions we want to use, set up MySQL, set up Apache (and tell it how to run PHP), create as many virtual hosts per site as PHP versions we want to test with. It's not very hard to do, but it's not for Joe and Jane Average. A working understanding of system administration is prerequisite to carrying out the rest of the instructions. If the terms "command line", "configuration file", "system service" or "FastCGI" make you cringe or, worse, draws a blank stare please stop reading now and save your time and your sanity by downloading XAMPP. You're welcome.

Disclaimer: the following configuration is only meant to be used as a local development server. Furthermore, these instructions were tested on a fairly vanilla Windows 8.1 installation on a brand new Intel NUC. I am documenting them in a blog post because, well, I will forget how I did it in a couple of months. If it works for you too, cool bananas!

Pro tip: One great application for editing configuration files for PHP, MySQL and Apache with full syntax highlighting is Notepad++. It's free of charge too.

Setting up PHP

Download the latest PHP 5.3, 5.4 and 5.5 versions from http://windows.php.net/download/ Choose the Non Thread Safe releases.

Create a new empty folder C:\PHP

Extract each version of PHP in a directory under C:\PHP, i.e. C:\PHP\5.3 for the latest PHP 5.3 version, C:\PHP\5.4 for the latest PHP 5.4 version, C:\PHP\5.5 for the latest PHP 5.5 version.

Inside each PHP version's directory copy php.ini-development to php.ini and edit to match your preferences. There is one important change which is mandatory. Find the line

; extension_dir = "ext"

and change it to

extension_dir = "C:\PHP\5.3\ext"

remembering to use the correct path per version number. Otherwise PHP won't start.

Press WIN-BREAK on your keyboard. Click on Advanced System Settings. Click on the Environment Variables button. In the top section ("User variables for MyUserName") double-click on the Path variable and append:

;C:\PHP\5.4

to add PHP 5.4 to your Windows path.

Setting up MySQL

Download the installer from MySQL community edition and install it following the instructions. Remember to enable TCP/IP networking when asked.

Press WIN-BREAK on your keyboard. Click on Advanced System Settings. Click on the Environment Variables button. In the top section ("User variables for MyUserName") double-click on the Path variable and append:

;C:\Program Files\MySQL\MySQL Server 5.6\bin

to add MySQL command lin programmes to your Windows path.

Setting up Apache

Download from Apache Lounge. Make sure you are downloading the VC11 binares. Download both the Apache package and the respective modules package. You also need to install the Microsoft VC11 redistributable package if it's not already installed on your PC.

Extract the Apache ZIP package. Copy the Apache24 directory to C:\ so that you now have a directory C:\Apache24

Enable PHP

Extract the modules ZIP package. Go into the mod_fcgid-x.y.z (x, y and z are numbers) folder. Go into the mod_fcgid folder. Copy the file mod_fcgid.so into the C:\Apache24\modules folder.

Edit the file C:\Apache24\conf\httpd.conf and change the following lines, removing the # in front of them:

# LoadModule include_module modules/mod_include.so
# LoadModule fcgid_module modules/mod_fcgid.so # LoadModule vhost_alias_module modules/mod_vhost_alias.so

Edit the file C:\Apache24\conf\extra\httpd-default.conf and add the following lines at the end:

FcgidInitialEnv PATH "c:/php/5.4.32;C:/WINDOWS/system32;C:/WINDOWS;C:/WINDOWS/System32/Wbem;"
FcgidInitialEnv SystemRoot "C:/Windows"
FcgidInitialEnv SystemDrive "C:"
FcgidInitialEnv TEMP "C:/WINDOWS/Temp"
FcgidInitialEnv TMP "C:/WINDOWS/Temp"
FcgidInitialEnv windir "C:/WINDOWS"
FcgidIOTimeout 64
FcgidConnectTimeout 16
FcgidMaxRequestsPerProcess 1000
FcgidMaxProcesses 50
FcgidMaxRequestLen 8131072
# Location of php.ini
FcgidInitialEnv PHPRC "c:/php/5.4.32"
FcgidInitialEnv PHP_FCGI_MAX_REQUESTS 1000
<Files ~ "\.php$">
AddHandler fcgid-script .php
FcgidWrapper "c:/php/5.4/php-cgi.exe" .php
Options +ExecCGI
order allow,deny
allow from all
deny from none
</Files>

 You may change c:/php/5.4 to c:/php/5.3 or c:/php/5.5 depending on the default version of PHP you want on your server.

Edit the fle C:\Apache24\conf\httpd.conf and change the line

# Include conf/extra/httpd-default.conf

to

Include conf/extra/httpd-default.conf

(just remove the leading hash sign)

Test PHP

Create a new file C:\Apache24\htdocs\phpinfo.php with the content

!--?php <?php phpinfo(); ?>

Open a command prompt (WIN-R, type cmd and press Enter) then

C:
cd C:\Apache24\Bin
httpd.exe

On your browser visit http://localhost/phpinfo.php You should see the PHP information page. If not, review all the instructions above, you missed a step.

Back to the command prompt, press CTRL-C to stop Apache. Close this command prompt.

Install Apache as a system service

Press the Windows key, type cmd, right clck on Command Prompt and choose Run as Administrator. This opens an "elevated" command prompt. Type this:

C:
cd C:\Apache24\Bin
httpd.exe -k install

Bonus: autostart Apache Monitor on boot

Open one Windows Explorer window of the folder C:\Apache24\bin

Open a second Windows Explorer window of the folder C:\Users\YourUserName\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup where YourUserName is, obviously, your username on Windows

Holding down the ALT key drag ApacheMonitor from the first window into the second.

This will auto-start the Apache Monitor tray icon on boot which monitors the status of the Apache server and lets you restart it easily.

The Virtual Hosts

Our goal is to have every site on our local server run under a different PHP version depending on the domain name we are using. The domain names we will be using are:

  • local.web for PHP 5.4
  • local53.web for PHP 5.3
  • local55.web for PHP 5.5

You need to change your hosts file to have Windows map these fake domains to your local server. Add these lines to the hosts file:

127.0.0.1 local.web www.local.web
127.0.0.1 local53.web www.local53.web
127.0.0.1 local55.web www.local55.web

We will also be implementing dynamic virtual hosts. This means that when you have a directory C:\Apache24\htdocs\foobar you will be able to access this site both as http://local.web/foobar and http://foobar.local.web  The latter is a better way to develop sites locally since most web scripts store image paths relative to the domain root. For this trick to work you'd need to add a few more lines to your hosts file:

127.0.0.1 foobar.local.web
127.0.0.1 foobar.local53.web
127.0.0.1 foobar.local55.web

Now the site in the directory C:\Apache24\htdocs\foobar will be accessible as http://foobar.local.web (PHP 5.4), http://foobar.local53.web (PHP 5.3) and http://foobar.local55.web (PHP 5.5). Cool! Let's make the changes in our server's configuration to make that happen.

Configure Apache for multiple PHP versions

Edit the C:\Apache24\conf\httpd.conf. Find the line

# Include conf/extra/httpd-vhosts.conf

and remove the leading hash sign so that it becomes

Include conf/extra/httpd-vhosts.conf

Now edit the C:\Apache24\conf\extra\httpd-vhosts.conf file and change its contents with:

# Default PHP virtual host (local.web)
<VirtualHost *:80>
 ServerAdmin This email address is being protected from spambots. You need JavaScript enabled to view it.
 DocumentRoot "c:/Apache24/htdocs"
 ServerName www.local.web
 #ErrorLog "logs/dummy-host.example.com-error.log"
 #CustomLog "logs/dummy-host.example.com-access.log" common
<!--</VirtualHost>
# Dynamic virtual hosts using vhost_alias, default PHP
<VirtualHost *:80>
	ServerAlias *.local.web
	UseCanonicalName Off
	VirtualDocumentRoot "c:/Apache24/htdocs/%1"
</VirtualHost>

# PHP 5.5 virtual host (local55.web)
<VirtualHost *:80>
	ServerAdmin This email address is being protected from spambots. You need JavaScript enabled to view it.
	DocumentRoot "c:/Apache24/htdocs"
	ServerName www.local55.web
	#ErrorLog "logs/dummy-host.example.com-error.log"
	#CustomLog "logs/dummy-host.example.com-access.log" common 
	
	<Directory "c:/Apache24/htdocs"> 
		<Files ~ "\.php$"> 
			AddHandler fcgid-script .php 
			FcgidWrapper "c:/php/5.5/php-cgi.exe" .php 
			Options +ExecCGI 
			order allow,deny 
			allow from all 
			deny from none 
		</Files> 
	</Directory> 
</VirtualHost>

# Dynamic virtual hosts using vhost_alias, PHP 5.5 
<VirtualHost *:80> 
	ServerAlias *.local55.web 
	UseCanonicalName Off 
	VirtualDocumentRoot "c:/Apache24/htdocs/%1" 
	
	<Directory "c:/Apache24/htdocs"> 
		<Files ~ "\.php$"> 
			AddHandler fcgid-script .php 
			FcgidWrapper "c:/php/5.5/php-cgi.exe" .php 
			Options +ExecCGI 
			order allow,deny 
			allow from all 
			deny from none 
		</Files> 
	</Directory> 
</VirtualHost>

# PHP 5.3 virtual host (local53.web) 
<VirtualHost *:80> 
	ServerAdmin This email address is being protected from spambots. You need JavaScript enabled to view it. 
	DocumentRoot "c:/Apache24/htdocs" 
	ServerName www.local53.web 
	#ErrorLog "logs/dummy-host.example.com-error.log" 
	#CustomLog "logs/dummy-host.example.com-access.log" common 
	
	<Directory "c:/Apache24/htdocs"> 
		<Files ~ "\.php$"> 
			AddHandler fcgid-script .php 
			FcgidWrapper "c:/php/5.3/php-cgi.exe" .php 
			Options +ExecCGI 
			order allow,deny 
			allow from all 
			deny from none 
		</Files> 
	</Directory> 
</VirtualHost>

# Dynamic virtual hosts using vhost_alias, PHP 5.3 
<VirtualHost *:80> 
	ServerAlias *.local53.web 
	UseCanonicalName Off 
	VirtualDocumentRoot "c:/Apache24/htdocs/%1" 
	
	<Directory "c:/Apache24/htdocs"> 
		<Files ~ "\.php$"> 
			AddHandler fcgid-script .php 
			FcgidWrapper "c:/php/5.3/php-cgi.exe" .php 
			Options +ExecCGI 
			order allow,deny 
			allow from all 
			deny from none 
		</Files> 
	</Directory> 
</VirtualHost>

Test the solution

We can now use three different domains to access the same content as different PHP versions. Let's try it:

  • http://www.local.web/phpinfo.php reports PHP 5.4
  • http://www.local53.web/phpinfo.php reports PHP 5.3
  • http://www.local55.web/phpinfo.php reports PHP 5.5

All three URLs load the file C:\Apache24\htdocs\phpinfo.php.

An exercise to the reader

You could even possibly have different sites use different PHP versions without using a special URL, just by adding the relevant  section to their .htaccess file. Can you see how to do it?

Hint: The <Directory> section in a virtual host configuration has the same effect as putting its contents inside a .htaccess file in that directory.