How to install the RedHat Software Collections Library PHP 7.2 on CentOS 7

This is a functional reference guide for Enterprise Linux, specifically CentOS 7, that attempts to provide a straightforward procedure for producing a highly functional, stable, and modern LAMP 7 stack (i.e. HOWTO).

TL;DR

sudo yum clean all
sudo yum makecache
sudo yum -y install epel-release centos-release-scl

sudo yum -y install httpd24-mod_ssl libsodium
sudo yum -y install rh-php72 rh-php72-php rh-php72-php-bcmath rh-php72-php-cli rh-php72-php-common rh-php72-php-curl rh-php72-php-dba
sudo yum -y install rh-php72-php-embedded rh-php72-php-enchant rh-php72-php-fpm rh-php72-php-gd rh-php72-php-intl rh-php72-php-ldap
sudo yum -y install rh-php72-php-mbstring rh-php72-php-memcached rh-php72-php-mysqlnd rh-php72-php-odbc rh-php72-php-pdo
sudo yum -y install rh-php72-php-pear rh-php72-php-pecl-apcu rh-php72-php-pgsql rh-php72-php-process rh-php72-php-pspell
sudo yum -y install rh-php72-php-recode rh-php72-php-snmp rh-php72-php-soap rh-php72-php-xml rh-php72-php-xmlrpc rh-php72-php-zip
sudo yum -y install sclo-php72-php-imap sclo-php72-php-pecl-amqp sclo-php72-php-pecl-apcu-bc sclo-php72-php-sodium
sudo yum -y install sclo-php72-php-pecl-apfd sclo-php72-php-pecl-geoip sclo-php72-php-pecl-http sclo-php72-php-pecl-igbinary
sudo yum -y install sclo-php72-php-pecl-imagick sclo-php72-php-pecl-lzf sclo-php72-php-pecl-memcached sclo-php72-php-pecl-mongodb
sudo yum -y install sclo-php72-php-pecl-msgpack sclo-php72-php-pecl-propro sclo-php72-php-pecl-raphf sclo-php72-php-pecl-redis
sudo yum -y install sclo-php72-php-pecl-selinux sclo-php72-php-pecl-solr2 sclo-php72-php-pecl-uploadprogress sclo-php72-php-pecl-uuid
sudo yum -y install sclo-php72-php-pecl-xattr sclo-php72-php-pecl-xdebug sclo-php72-php-tidy

/opt/rh/rh-php72/root/usr/bin/php --ini

sudo systemctl stop httpd
sudo systemctl disable httpd

yum -y remove httpd httpd-tools php*

cat <<EOF > /etc/systemd/system/httpd.service
[Unit]
Description=The RedHat Software Collections Library (SCL) Apache HTTP Server
After=network.target remote-fs.target nss-lookup.target
Documentation=man:httpd(8)
Documentation=man:apachectl(8)

[Service]
Type=notify
EnvironmentFile=/etc/sysconfig/httpd
ExecStart=/opt/rh/httpd24/root/usr/sbin/httpd-scl-wrapper \$OPTIONS -DFOREGROUND
ExecReload=/opt/rh/httpd24/root/usr/sbin/httpd-scl-wrapper \$OPTIONS -k graceful
ExecStop=/bin/kill -WINCH \${MAINPID}
# We want systemd to give httpd some time to finish gracefully, but still want
# it to kill httpd after TimeoutStopSec if something went wrong during the
# graceful stop. Normally, Systemd sends SIGTERM signal right after the
# ExecStop, which would kill httpd. We are sending useless SIGCONT here to give
# httpd time to finish.
KillSignal=SIGCONT
PrivateTmp=true

[Install]
WantedBy=multi-user.target
EOF

cat <<EOF > /etc/sysconfig/httpd
OPTIONS="-d /etc/httpd"
LANG=C
EOF
chmod 644 /etc/sysconfig/httpd

cat <<EOF > /etc/logrotate.d/httpd
/var/log/httpd/*log {
    missingok
    notifempty
    sharedscripts
    compress
    postrotate
        /bin/systemctl reload httpd.service > /dev/null 2>/dev/null || true
    endscript
    su apache apache
}
EOF
chmod 644 /etc/logrotate.d/httpd

cat <<EOF > /etc/ld.so.conf.d/rh-php72.conf
/opt/rh/rh-php72/root/usr/lib64
/opt/rh/httpd24/root/usr/lib64
/opt/rh/rh-php72/root/usr/lib
/opt/rh/httpd24/root/usr/lib
EOF
ldconfig

if [ -d /etc/httpd ] && [ ! -d /etc/httpd.base ]; then (mv /etc/httpd /etc/httpd.base); fi
if [ ! -f /etc/httpd/conf/httpd.conf ]; then (rsync -avp /opt/rh/httpd24/root/etc/httpd/ /etc/httpd/); fi

for httpd24_conf in $(find /etc/httpd -type f | xargs grep -l "/opt/rh/httpd24/root"); do echo $httpd24_conf; sed -i -e 's#/opt/rh/httpd24/root/etc#/etc#g' -e 's#/opt/rh/httpd24/root/var/www#/var/www#g' $httpd24_conf; done

sudo systemctl daemon-reload
sudo systemctl enable httpd24-htcacheclean
sudo systemctl start httpd24-htcacheclean
sudo systemctl status httpd24-htcacheclean
sudo systemctl enable httpd
sudo systemctl start httpd
sudo systemctl status httpd

ln -s /opt/rh/rh-php72/root/usr/bin/php /usr/bin/php
ln -s /etc/opt/rh/rh-php72/php.ini /etc/php.ini

sudo systemctl daemon-reload
sudo systemctl status httpd

Install the CentOS EPEL & RedHat Software Collections Library (scl) release packages, clean, & rebuild the yum cache.

sudo yum clean all
sudo yum makecache
sudo yum install epel-release centos-release-scl

Install the latest RedHat SCL php 7.2 & corresponding, dependent httpd24 packages.

The following command installs the most commonly used php modules, as well.

sudo yum install httpd24-mod_ssl
sudo yum install rh-php72 rh-php72-php rh-php72-php-bcmath rh-php72-php-cli rh-php72-php-common rh-php72-php-curl rh-php72-php-dba
sudo yum install rh-php72-php-embedded rh-php72-php-enchant rh-php72-php-fpm rh-php72-php-gd rh-php72-php-intl rh-php72-php-ldap
sudo yum install rh-php72-php-mbstring rh-php72-php-memcached rh-php72-php-mysqlnd rh-php72-php-odbc rh-php72-php-pdo
sudo yum install rh-php72-php-pear rh-php72-php-pecl-apcu rh-php72-php-pgsql rh-php72-php-process rh-php72-php-pspell
sudo yum install rh-php72-php-recode rh-php72-php-snmp rh-php72-php-soap rh-php72-php-xml rh-php72-php-xmlrpc rh-php72-php-zip
sudo yum install sclo-php72-php-imap sclo-php72-php-pecl-amqp sclo-php72-php-pecl-apcu-bc sclo-php72-php-sodium
sudo yum install sclo-php72-php-pecl-apfd sclo-php72-php-pecl-geoip sclo-php72-php-pecl-http sclo-php72-php-pecl-igbinary
sudo yum install sclo-php72-php-pecl-imagick sclo-php72-php-pecl-lzf sclo-php72-php-pecl-memcached sclo-php72-php-pecl-mongodb
sudo yum install sclo-php72-php-pecl-msgpack sclo-php72-php-pecl-propro sclo-php72-php-pecl-raphf sclo-php72-php-pecl-redis
sudo yum install sclo-php72-php-pecl-selinux sclo-php72-php-pecl-solr2 sclo-php72-php-pecl-uploadprogress sclo-php72-php-pecl-uuid
sudo yum install sclo-php72-php-pecl-xattr sclo-php72-php-pecl-xdebug sclo-php72-php-tidy

Verify the php 7.2 installation.

At this point, there should be a fully functional httpd 2.4 with php 7.2 (using the mysqlnd driver, etc).

/opt/rh/rh-php72/root/usr/bin/php --ini
Configuration File (php.ini) Path: /etc/opt/rh/rh-php72
Loaded Configuration File:         /etc/opt/rh/rh-php72/php.ini
Scan for additional .ini files in: /etc/opt/rh/rh-php72/php.d
Additional .ini files parsed:      /etc/opt/rh/rh-php72/php.d/15-xdebug.ini,
/etc/opt/rh/rh-php72/php.d/20-bcmath.ini,
/etc/opt/rh/rh-php72/php.d/20-bz2.ini,
/etc/opt/rh/rh-php72/php.d/20-calendar.ini,
/etc/opt/rh/rh-php72/php.d/20-ctype.ini,
/etc/opt/rh/rh-php72/php.d/20-curl.ini,
/etc/opt/rh/rh-php72/php.d/20-dba.ini,
/etc/opt/rh/rh-php72/php.d/20-dom.ini,
/etc/opt/rh/rh-php72/php.d/20-enchant.ini,
/etc/opt/rh/rh-php72/php.d/20-exif.ini,
/etc/opt/rh/rh-php72/php.d/20-fileinfo.ini,
/etc/opt/rh/rh-php72/php.d/20-ftp.ini,
/etc/opt/rh/rh-php72/php.d/20-gd.ini,
/etc/opt/rh/rh-php72/php.d/20-gettext.ini,
/etc/opt/rh/rh-php72/php.d/20-iconv.ini,
/etc/opt/rh/rh-php72/php.d/20-imap.ini,
/etc/opt/rh/rh-php72/php.d/20-intl.ini,
/etc/opt/rh/rh-php72/php.d/20-json.ini,
/etc/opt/rh/rh-php72/php.d/20-ldap.ini,
/etc/opt/rh/rh-php72/php.d/20-mbstring.ini,
/etc/opt/rh/rh-php72/php.d/20-mysqlnd.ini,
/etc/opt/rh/rh-php72/php.d/20-odbc.ini,
/etc/opt/rh/rh-php72/php.d/20-pdo.ini,
/etc/opt/rh/rh-php72/php.d/20-pgsql.ini,
/etc/opt/rh/rh-php72/php.d/20-phar.ini,
/etc/opt/rh/rh-php72/php.d/20-posix.ini,
/etc/opt/rh/rh-php72/php.d/20-pspell.ini,
/etc/opt/rh/rh-php72/php.d/20-recode.ini,
/etc/opt/rh/rh-php72/php.d/20-shmop.ini,
/etc/opt/rh/rh-php72/php.d/20-simplexml.ini,
/etc/opt/rh/rh-php72/php.d/20-snmp.ini,
/etc/opt/rh/rh-php72/php.d/20-soap.ini,
/etc/opt/rh/rh-php72/php.d/20-sockets.ini,
/etc/opt/rh/rh-php72/php.d/20-sodium.ini,
/etc/opt/rh/rh-php72/php.d/20-sqlite3.ini,
/etc/opt/rh/rh-php72/php.d/20-sysvmsg.ini,
/etc/opt/rh/rh-php72/php.d/20-sysvsem.ini,
/etc/opt/rh/rh-php72/php.d/20-sysvshm.ini,
/etc/opt/rh/rh-php72/php.d/20-tidy.ini,
/etc/opt/rh/rh-php72/php.d/20-tokenizer.ini,
/etc/opt/rh/rh-php72/php.d/20-xml.ini,
/etc/opt/rh/rh-php72/php.d/20-xmlwriter.ini,
/etc/opt/rh/rh-php72/php.d/20-xsl.ini,
/etc/opt/rh/rh-php72/php.d/20-zip.ini,
/etc/opt/rh/rh-php72/php.d/30-mysqli.ini,
/etc/opt/rh/rh-php72/php.d/30-pdo_mysql.ini,
/etc/opt/rh/rh-php72/php.d/30-pdo_odbc.ini,
/etc/opt/rh/rh-php72/php.d/30-pdo_pgsql.ini,
/etc/opt/rh/rh-php72/php.d/30-pdo_sqlite.ini,
/etc/opt/rh/rh-php72/php.d/30-wddx.ini,
/etc/opt/rh/rh-php72/php.d/30-xmlreader.ini,
/etc/opt/rh/rh-php72/php.d/30-xmlrpc.ini,
/etc/opt/rh/rh-php72/php.d/40-amqp.ini,
/etc/opt/rh/rh-php72/php.d/40-apcu.ini,
/etc/opt/rh/rh-php72/php.d/40-apfd.ini,
/etc/opt/rh/rh-php72/php.d/40-geoip.ini,
/etc/opt/rh/rh-php72/php.d/40-igbinary.ini,
/etc/opt/rh/rh-php72/php.d/40-imagick.ini,
/etc/opt/rh/rh-php72/php.d/40-lzf.ini,
/etc/opt/rh/rh-php72/php.d/40-msgpack.ini,
/etc/opt/rh/rh-php72/php.d/40-propro.ini,
/etc/opt/rh/rh-php72/php.d/40-raphf.ini,
/etc/opt/rh/rh-php72/php.d/40-selinux.ini,
/etc/opt/rh/rh-php72/php.d/40-uploadprogress.ini,
/etc/opt/rh/rh-php72/php.d/40-uuid.ini,
/etc/opt/rh/rh-php72/php.d/40-xattr.ini,
/etc/opt/rh/rh-php72/php.d/50-apc.ini,
/etc/opt/rh/rh-php72/php.d/50-http.ini,
/etc/opt/rh/rh-php72/php.d/50-memcached.ini,
/etc/opt/rh/rh-php72/php.d/50-mongodb.ini,
/etc/opt/rh/rh-php72/php.d/50-solr.ini

Additional Steps

There are some things that I’ve found make using the RedHat PHP 7.2.x SCL packages and upgrading existing code bases a little easier. The following steps are OPTIONAL, but recommended.

To avoid potential conflict, ensure the CentOS base httpd is stopped, disabled, and completely removed from the system.

I’d strongly recommend removing the CentOS base php & httpd. Having the base packages on the system, with SCL packages, can cause confusion. Although, to be fair, it’s possible as long as both httpd installs are configured to use different interfaces and/or ports.

sudo systemctl stop httpd
sudo systemctl disable httpd

It’s also important to note that, by default and because, both base & SCL httpd packages are configured to bind to all interfaces on port 80 (& port 443 if mod_ssl is installed). As a result, only one at a time will start successfully. If you’re not doing this then I’m sure you know what I mean & how to fix that.

yum remove httpd httpd-tools php*

Create a custom systemd httpd.service file.

This will allow for a normal Enterprise Linux administrative ‘feel’ and retain functionality such as systemctl restart httpd. It will also override the default CentOS base httpd (if it exists on the system) and allow for local customizations. It’s helpful to me, sometimes.

The tendency for both httpd & httpd24 to use the same ports can be frustrating, especially for web developers who are unfamiliar with SCL. Ultimately, I’d recommend this and I do not recommend having both of the CentOS base httpd & SCL httpd24 on the same system. Nevertheless, if you do this then remember to run systemctl daemon-reload after creating /etc/systemd/system/httpd.service

/etc/systemd/system/httpd.service

cat <<EOF > /etc/systemd/system/httpd.service
[Unit]
Description=The RedHat Software Collections Library (SCL) Apache HTTP Server
After=network.target remote-fs.target nss-lookup.target
Documentation=man:httpd(8)
Documentation=man:apachectl(8)

[Service]
Type=notify
EnvironmentFile=/etc/sysconfig/httpd
ExecStart=/opt/rh/httpd24/root/usr/sbin/httpd-scl-wrapper \$OPTIONS -DFOREGROUND
ExecReload=/opt/rh/httpd24/root/usr/sbin/httpd-scl-wrapper \$OPTIONS -k graceful
ExecStop=/bin/kill -WINCH \${MAINPID}
# We want systemd to give httpd some time to finish gracefully, but still want
# it to kill httpd after TimeoutStopSec if something went wrong during the
# graceful stop. Normally, Systemd sends SIGTERM signal right after the
# ExecStop, which would kill httpd. We are sending useless SIGCONT here to give
# httpd time to finish.
KillSignal=SIGCONT
PrivateTmp=true

[Install]
WantedBy=multi-user.target
EOF

After creating or updating /etc/systemd/system/httpd.service then systemd needs to be reloaded. This, or a reboot, is mandatory every time a custom /etc/systemd service file is modified.

sudo systemctl daemon-reload # important!
sudo systemctl status httpd
● httpd.service - The RedHat Software Collections (SCL) Apache HTTP Server
   Loaded: loaded (/etc/systemd/system/httpd.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
     Docs: man:httpd(8)
           man:apachectl(8)

Create (or modify) the systemctl EnvironmentFile for httpd.service

Doing this will cause the SCL httpd24 to re-use the familiar /etc/httpd structure (rather than what’s in /opt/rh/httpd24/root/etc/httpd).

/etc/sysconfig/httpd

cat <<EOF > /etc/sysconfig/httpd
OPTIONS="-d /etc/httpd"
LANG=C
EOF
chmod 644 /etc/sysconfig/httpd

Copy the httpd24 files to /etc/httpd and modify them.

PAY MORE ATTENTION TO THE FINAL CONFIGURATIONS THAN IS OUTLINED HERE.

If using /etc/httpd, then be sure to update the contents of /etc/httpd to reflect what’s in /opt/rh/httpd24/root/etc/httpd. I typically move the old /etc/httpd out of the way, rsync the SCL configs, and then modify what’s in /etc/httpd because that preserves the integrity of the SCL packaged configs.

if [ -d /etc/httpd ] && [ ! -d /etc/httpd.base ]; then (mv /etc/httpd /etc/httpd.base); fi # artifacts may exist, e.q. conf.d/squid.conf
if [ ! -f /etc/httpd/conf/httpd.conf ]; then (rsync -avp /opt/rh/httpd24/root/etc/httpd/ /etc/httpd/); fi # don't accidentally overwrite httpd.conf (et al)

For new installs, I typically prefer /var/www as a starting point for DocumentRoot. It’s also important that the ServerRoot directive is changed in httpd.conf, to reference /etc/httpd, so that the correct config structure is used.

for httpd24_conf in $(find /etc/httpd -type f | xargs grep -l "/opt/rh/httpd24/root"); do echo $httpd24_conf; sed -i -e 's#/opt/rh/httpd24/root/etc#/etc#g' -e 's#/opt/rh/httpd24/root/var/www#/var/www#g' $httpd24_conf; done
```sh
sudo systemctl daemon-reload # can't hurt!
sudo systemctl enable httpd24-htcacheclean
sudo systemctl start httpd24-htcacheclean
sudo systemctl status httpd24-htcacheclean
sudo systemctl enable httpd # or, httpd24-http
sudo systemctl start httpd # or, httpd24-http
sudo systemctl status httpd # or, httpd24-http

This is mostly for any other binaries that may be installed, that depend on the shared SCL libraries. For them to always work properly with the RedHat SCL packages, then the following files should be configured manually. Anything compiled and linked with ld will find them.

Add the rh-php72 shared library directories to the ld linker search path via /etc/ld.so.conf.d/rh-php72.conf

/etc/ld.so.conf.d/rh-php72.conf

/opt/rh/rh-php72/root/usr/lib64
/opt/rh/httpd24/root/usr/lib64
/opt/rh/rh-php72/root/usr/lib
/opt/rh/httpd24/root/usr/lib

Prefix the rh-php72 binary directories to all login PATHs via /etc/profile.d/zzzz-scl.sh

This is may be helpful. Naming a profile.d include ‘zzzz-scl.sh’ will ensure it gets loaded last. Existing PATHs should be appended & preserved.

/etc/profile.d/zzzz-scl.sh

if [ -d /opt/rh/ ] && [ -r /opt/rh/ ]; then
    Rhscl_Roots=$(find /opt/rh/ -type f -name enable 2> /dev/null | sort -V)
    for Rhscl_Enable in $Rhscl_Roots; do
        if [ -r "$Rhscl_Enable" ]; then
            . "$Rhscl_Enable"
        else
            continue
        fi
        Rhscl_Root="$(dirname "$Rhscl_Enable")/root"
        Rhscl_Bins="usr/bin usr/sbin bin sbin"
        for Rhscl_Bin in $Rhscl_Bins; do
            if [ -d "$Rhscl_Root/$Rhscl_Bin" ]; then
                Scl_Path+="$Rhscl_Root/$Rhscl_Bin:"
            fi
        done
        unset Rhscl_Bin Rhscl_Bins  Rhscl_Enable Rhscl_Root
    done
    unset Rhscl_Roots
fi
export PATH_SCL=$Scl_Path
export PATH=${PATH_SCL}:$PATH

Caveats

The following information may be helpful.

Many packages, programs, & utilities (and people) expect the default CentOS 7 filesystem locations for associated files. The RedHat SCL packages relocate everything to /opt/rh/.

  • On CentOS 7 machines with only rh-php-7.2 installed, it may be easiest to use a symbolic link. However, it should not be done on machines that choose to have the default php and scl php co-exist on the same machine.

      ln -s /opt/rh/rh-php72/root/usr/bin/php /usr/bin/php
      ln -s /etc/opt/rh/rh-php72/php.ini /etc/php.ini
    
  • Best practice, for shell scripts in general, is to use the /usr/bin/env <processor> directives rather than hard coding something like #!/usr/bin/php. This is a good practice.

    #!/usr/bin/env php

  • Here’s a quick one liner to find all files from the current directory that contain /bin/php; they may need to be changed to #!/usr/bin/env php (rather than relying on a symbolic link).

      find . -type f | xargs grep -l "/bin/php"
    

Disclaimer

Many SCL packages are available and may have been installed if you’re blindly copy/pasting the commands in this guide. Some are ‘rh’ (maintained by RedHat), while others are ‘sclo’ (maintained by the Software Collections organization [not RedHat]). Some are required & others may not be needed.

Jumping into php 7 will enable some newer things to work, but if it isn’t broke, then why fix it? The reality is that php 5.4 has been around for a long time and by now is very stable. It’s still supported by RedHat and will be for many more years.

Or, if & when you’re paranoid about security; I’d recommend sticking with the CentOS 7 base php 5.4 & trust RedHat and the CentOS community. Long term stability and security updates are the main reason a lot of us use RedHat or derivitives like CentOS, anyway.

References