Part 4 – Installing PHP

October 30, 2024

!Series: Local Environment Apache PHP

In this section, we will be adding PHP to our demo websites—a different version for each site.

Downloading PHP

Since we’re going to have two different versions of PHP, it makes sense to have a new folder just for our PHP versions. I’ll create a new folder in the Development folder called PHP. All of our PHP versions will live inside that directory.

Screenshot of File Explorer

Development folder with the PHP folder added

Now we need the files to run PHP. I am going to be downloading a version of PHP 8 and a version of PHP 7, but you can use any two different versions. The current version will be available on the main downloads page of the PHP site. We want to get files from the Windows downloads.

Screenshot of browser

Windows downloads on PHP downloads page

You will have the option of downloading the thread safe or non-thread safe version. I don’t fully understand the details behind this at this point. As far as I can tell, you need to use thread safe version for running PHP in specific contexts. For our purposes, the non-thread safe version works fine. Download the ZIP file for the version of Windows you have (either x64 or x86).

Screenshot of browser

ZIP download of PHP 8.3 on PHP Windows downloads page

PHP 7 is out of support, so we will need to go to the archives page to download that. At the time of writing, that link is at the bottom of the left sidebar. (I assume it’s buried because using old versions of PHP poses a major security risk, and you probably shouldn’t do it for real websites.)

Screenshot of browser

Link to the PHP archives page

The archives page is pretty old-school, but we can scroll down to the last listed version of PHP 7, which was PHP 7.4.9. NTS stands for “not thread safe”, so I downloaded the file named php-7.4.9-nts-Win32-vc15-x64.zip.

Screenshot of browser

ZIP download of PHP 7.4.9 on PHP archive page

Once you’ve downloaded the files, extract them. I’m going to shorten the names a little bit to just include the version. Now, let’s move them into our PHP folder, which for me is C:\Development\PHP.

Screenshot of File Explorer

PHP folder contents

PHP Configuration

PHP comes with two different configuration files, one with the recommended settings for a development environment (called php.ini-development) and one with the recommended settings for production (called php.ini-production). We won’t change any of these settings at this point, but we do need to use one of the two files to be our configuration file. Because this is for a local development environment, we will use the development settings.

In each PHP folder, copy and paste the file php.ini-development, and then rename it to php.ini. PHP will automatically load settings from the file with that name when it starts.

Screenshot of File Explorer

Renamed php.ini file

Installing the FastCGI Module

When we first looked at the Apache main configuration file in Part 3, we mentioned that Apache uses modules to handle more complicated functionality. In order to run multiple versions of PHP, we will need to add an additional module. This module is called mod_fcgid.

Very generally, a Common Gateway Interface or CGI lets web servers like Apache connect with other programs. In our case, it will let Apache work with PHP. FastCGI is meant to be a faster way to handle that. The mod_fcgid module implements that for an Apache server.

This module isn’t included in the Apache binary by default, so we’ll need to download it. We want Windows files, so we’ll go back to the Apache Lounge download page. Scroll down to find the file for mod_fcgid, which for me is mod_fcgid-2.3.10-win64-VS17.zip (the version might be different for you). Download it and extract it.

Screenshot of web browser

ZIP file download for mod_fcgid on Apache Lounge

Before we go further, let’s look at the ReadMe file that came with the download. The ReadMe file includes instructions on how to install and include this module. It also includes an example configuration, which we will reference in a minute. For now, let’s copy mod_fcgid.so into our Apache modules folder, which for me is C:\Development\Apache24\modules.

Next, we need to load the module from our main Apache configuration file. Look for the section with the block of the LoadModule directives. Add this directive (which you can find in the ReadMe file for mod_fcgid):

LoadModule fcgid_module modules/mod_fcgid.so

Restart your Apache server and make sure that it still launches without errors. If it did, you’re ready to proceed.

Getting Ready for PHP

There’s a bit more setup we need to do before we can run PHP.

First of all, let’s define the folder where we’ve stored our PHP versions, like we did with the folder where we stored Apache and our other web files. I’ll define a new variable called PHP root and save the path to the PHP folder into it. Add this to httpd.conf:

Define PHPROOT "c:/Development/PHP"

Next, we need to tell Apache to recognize index.php as a valid index file. Look for DirectoryIndex in the httpd.conf file. It should look like this by default:

<IfModule dir_module>
    DirectoryIndex index.html
</IfModule>

This directive says that if a module labeled dir_module is available, then it will look for a file called index.html and use it as the index page.

We need Apache to recognize index.php as an index file as well, and we need it to take priority over index.html. (This means that if we have index.php and index.html in the same folder, index.php should be displayed.) Update your directive like this:

<IfModule dir_module>
    DirectoryIndex index.php index.html
</IfModule>

Let’s test if this worked. Let’s create another file in the test-1 folder, called index.php:

Screenshot of File Explorer

test-1 folder with index.php added

We aren’t running PHP yet, so let’s just add some simple text to our index.php file:

Hello from index.php

Restart Apache and refresh http://test-1.local/. (You may need to do a hard refresh.) You should see whatever text you added to index.php, which shows that even with both index.html and index.php available, Apache is using index.php as the index file.

Finally, let’s add some real PHP to our test-1 and test-2 folders. Add an index.php file to each folder with following code:

<?php
phpinfo();
?>

This function will display which version of PHP is running, so this will let us tell whether we’re actually running different versions of PHP on each site. You can also delete the index.html file from each folder.

With that done, we just need to run PHP to display our code.

Running PHP

Let’s go back to the ReadMe file for mod_fcgid. The exact details of every setting aren’t important right now, but we can get the general gist of this and we can see that most of these settings can be true for anything that is using the FastCGI module.

In the main configuration file, we’re going to set any settings that can apply to any site, regardless of what version of PHP it’s going to run. We only want these settings if the FastCGI module is available, so we’ll nest them inside an IfModule directive. Add this to the end of httpd.conf:

# FastCGI setup
<IfModule fcgid_module>
    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
    FcgidInitialEnv PHP_FCGI_MAX_REQUESTS 1000
</IfModule>

If you compare this to the ReadMe file, this is most of the configuration settings, except for the following:

  • FcgidInitialEnv PATH: Indicates the path to the PHP directory
  • FcgidInitialEnv PHPRC: Indicates where php.ini can be found
  • <Files ~ "\.php$>": Runs any nested code only if the files meet certain criteria (more on this later)

We didn’t set these files in httpd.conf because they need to be different for each site. For the rest of the settings, open httpd-vhosts.conf. Update your code to this:

<VirtualHost *:80>
    ServerName localhost
</VirtualHost>

<VirtualHost *:80>
    ServerName test-1.local
    DocumentRoot "${DOCROOT}/test-1"
    
    <Directory "${DOCROOT}/test-1/">
        Require all granted
    </Directory>
    
    <IfModule fcgid_module>
        FcgidInitialEnv PATH "${PHPROOT}/php-8.3.9"
        FcgidInitialEnv PHPRC "${PHPROOT}/php-8.3.9"
        <Files ~ "\.php$>"
            Options ExecCGI
            AddHandler fcgid-script .php
            FcgidWrapper "${PHPROOT}/php-8.3.9/php-cgi.exe" .php
        </Files>
    </IfModule>
    
</VirtualHost>

<VirtualHost *:80>
    ServerName test-2.local
    DocumentRoot "${DOCROOT}/test-2"
    
    <Directory "${DOCROOT}/test-2/">
        Require all granted
    </Directory>
    
    <IfModule fcgid_module>
        FcgidInitialEnv PATH "${PHPROOT}/php-7.4.9"
        FcgidInitialEnv PHPRC "${PHPROOT}/php-7.4.9"
        <Files ~ "\.php$>"
            Options ExecCGI
            AddHandler fcgid-script .php
            FcgidWrapper "${PHPROOT}/php-7.4.9/php-cgi.exe" .php
        </Files>
    </IfModule>
</VirtualHost>

Let’s look at what we’re doing with these changes:

  1. We only want this code to run if the FastCGI module is present, so we nest the rest of our directives in the IfModule directive
  2. FcgidInitialEnv PATH tells Apache where to find each version of PHP (note that test-1 directs to PHP 8.3.9 and test-2 directs to 7.4.9)
  3. FcgidInitialEnv PHPRC tells Apache where each php.ini file is
  4. The Files directive tells Apache to only run the following directives if a file ends in .php
  5. The Options ExecCGI directive explicitly allows CGI scripts to run (this isn’t in the ReadMe file but helps prevent conflicts if CGI scripts aren’t allowed globally)
  6. AddHandler tells Apache to process any files that end in .php with the FastCGI protocol
  7. FcgidWrapper tells Apache where to find the script that it needs to run on these files

Save your files, restart your server, and visit each page. Remember that you may need to do a hard refresh. You should see the “It works” fallback page when you visit localhost, PHP info that shows you’re running PHP 8 on test-1.local, and PHP info that shows you’re running version 7 on test-2.local.

Screenshot of browser

PHP info display showing PHP Version 8.3.9 on http://test-1.local/

Conclusion

That’s it for this section. It was a long one, but an important piece of the puzzle. In the next section, we’ll be adding a database to each of our virtual hosts.

Further Reading

Posted In: Tutorials