Operating system: Fedora 39 Workstation x86_64 (but the examples also work on RHEL, Alma Linux, Rocky Linux, CentOS Stream version 8 or 9)

3rd party repositories: rpmfusion (to listen to music while I work) and of course remi

# dnf install http://rpms.remirepo.net/fedora/remi-release-39.rpm
# dnf config-manager --set-enabled remi

For RHEL, Alma, Rocky, or CentOS, instead, read the Configuration Wizard instructions.

 

Installation of the PHP versions

I use the Software Collections which allows installing multiple versions, in parallel, without affecting the base system, PHP versions 7.4 to 8.4 are available in my repository, so:

# dnf install php php-php-fpm php-mbstring php-mysqlnd ...
# dnf install php74 php74-php-fpm php74-php-mbstring php74-php-mysqlnd ...
# dnf install php80 php80-php-fpm php80-php-mbstring php80-php-mysqlnd ...
# dnf install php81 php81-php-fpm php81-php-mbstring php81-php-mysqlnd ...
# dnf install php82 php82-php-fpm php82-php-mbstring php82-php-mysqlnd ...
# dnf install php83 php83-php-fpm php83-php-mbstring php83-php-mysqlnd ...
# dnf install php84 php84-php-fpm php84-php-mbstring php84-php-mysqlnd ...

 

Web environment configuration

 

PHP FastCGI Process Manager

I haven't used mod_php for a long time, as it only allows a single version, but FPM, which is now used by default.

Because of the drop-in configuration files order, the highest installed version will be used by default, and all FPM services will start automatically when the web server service is started.

I'm not running a production server, but a development workstation, so, to reduce the load, I change the FPM pool configuration to use the "ondemand" mode. Each version uses a specific socket file.

For example, for PHP 7.4, in the  /etc/opt/remi/php74/php-fpm.d/www.conf file:

listen = /var/opt/remi/php74/run/php-fpm/www.sock
pm = ondemand

And I enable the service:

# systemctl enable --now php74-php-fpm

And apply the same for each version.

Apache

I create a virtual host for each PHP version.

In the /etc/hosts file, IP aliases declaration:

127.0.0.1 php74scl php80scl php81scl php82scl php83scl php84scl

I create a configuration file, with an alias to the git repositories in which I'm used to work, and the virtual hosts: /etc/httpd/conf.d/remi.conf

    <VirtualHost *:80>
        ServerName localhost
        <FilesMatch \.php$>
            SetHandler "proxy:unix:/var/run/php-fpm/www.sock|fcgi://localhost"
        </FilesMatch>
    </VirtualHost>

    <VirtualHost *:80>
        ServerName php74scl
        <FilesMatch \.php$>
            SetHandler "proxy:unix:/var/opt/remi/php74/run/php-fpm/www.sock|fcgi://php74scl"
        </FilesMatch>
    </VirtualHost>

    <VirtualHost *:80>
        ServerName php80scl
        <FilesMatch \.php$>
            SetHandler "proxy:unix:/var/opt/remi/php80/run/php-fpm/www.sock|fcgi://php80scl"
        </FilesMatch>
    </VirtualHost>

    <VirtualHost *:80>
        ServerName php81scl
        <FilesMatch \.php$>
            SetHandler "proxy:unix:/var/opt/remi/php81/run/php-fpm/www.sock|fcgi://php81scl"
        </FilesMatch>
    </VirtualHost>

    <VirtualHost *:80>
        ServerName php82scl
        <FilesMatch \.php$>
            SetHandler "proxy:unix:/var/opt/remi/php82/run/php-fpm/www.sock|fcgi://php82scl"
        </FilesMatch>
    </VirtualHost>

    <VirtualHost *:80>
        ServerName php83scl
        <FilesMatch \.php$>
            SetHandler "proxy:unix:/var/opt/remi/php83/run/php-fpm/www.sock|fcgi://php83scl"
        </FilesMatch>
    </VirtualHost>

    <VirtualHost *:80>
        ServerName php84scl
        <FilesMatch \.php$>
            SetHandler "proxy:unix:/var/opt/remi/php84/run/php-fpm/www.sock|fcgi://php84scl"
        </FilesMatch>
    </VirtualHost>

And then, I can use the http://php74scl/, http://php80scl/, http://php81scl/, http://php82scl/, http://php83scl/ and http://php84scl/ addresses in my browser. Each host serves the same pages but with the selected version. All hosts work simultaneously.

 

Command line

To switch from one version to another, I simply select the desired version using the environment modules:

$ module load php81
$ ...
$ module unload php81

Working on PHP code

Of course, I use the development tools available in the repository which are designed to work with the available PHP version.

# dnf install composer phpunit10 phpunit11 phpcompatinfo phpcs ...

For example :

$ git clone https://github.com/vendor/project.git
$ cd project
$ composer install
$ module load php74
$ vendor/bin/phpunit --verbose
$ module load php84
$ vendor/bin/phpunit --verbose
$ ...

Working on PHP extensions

Of course, the development packages need to be installed:

# dnf install php74-php-devel php80-php-devel php81-php-devel php82-php-devel php83-php-devel php84-php-devel

For example :

$ cd /work/GIT/rpminfo
$ module load php84
$ phpize
$ ./configure
$ make
$ make test

 

Conclusion

I think this configuration, which seems simple, is the ideal solution for a developer who needs various PHP versions but wants to be focused on his work, and take benefit of the large set of available packages in a quite full-featured distribution, with the benefits of available Software Collections in my repository.

I could also have used Docker... but this solution seems more simple, indeed, I sometimes use Docker for PHP 5.3.