Quickly switching between PCOV and Xdebug

Berlin, Germany

There are at least two PHP extensions I need during the development of a PHP library or application: PCOV and Xdebug.

PCOV is relatively new. Built by by Joe Watkins, it provides fast code coverage for use with phpunit/phpunit. Xdebug has been around for a while. Built by Derick Rethans, it is a development tool that can not only be used for collecting code coverage, but also provides a single-step debugger for use with IDEs, is equipped with a profiler, upgrades var_dump(), and more. Since it does more things differently, it is slower at collecting code coverage.

Both are great tools and have their place. When I want to collect code coverage with recent versions of phpunit/phpunit, I use PCOV. When I work on a project with older versions of phpunit/phpunit, I use Xdebug to collect code coverage. When I want to debug production code while running tests in PhpStorm, I use Xdebug.

There are drawbacks, however. PCOV does not play well with Xdebug, so I only want one of them to be enabled at a time. Xdebug adds significant overhead when running other tasks in PHP, so I prefer to disable Xdebug when I do not need it.

Before I can enable or disable PCOV and Xdebug, I need to install and configure them. I am going to demonstrate the installation of both on PHP 7.4.

Installation of PCOV

Run

$ pecl install pcov

and take note of last few lines that are shown when the installation process is finished and the location of pcov.so is revealed (we need it in a moment):

Build process completed successfully
Installing '/usr/local/Cellar/php/7.4.6/pecl/20190902/pcov.so'
install ok: channel://pecl.php.net/pcov-1.0.6
Extension pcov enabled in php.ini

Configuration of PCOV

Run

$ php --ini

to show the names of configuration files loaded by PHP:

Configuration File (php.ini) Path: /usr/local/etc/php/7.4
Loaded Configuration File:         /usr/local/etc/php/7.4/php.ini
Scan for additional .ini files in: /usr/local/etc/php/7.4/conf.d
Additional .ini files parsed:      /usr/local/etc/php/7.4/conf.d/ext-opcache.ini

Open /usr/local/etc/php/7.4/php.ini and remove

extension="pcov.so"

Create a file /usr/local/etc/php/7.4/conf.d/ext-pcov.ini, but use the exact location of pcov.so we noted earlier:

extension="/usr/local/Cellar/php/7.4.5_2/pecl/20190902/pcov.so"

Run

$ php --ini

again and observe that ext-pcov.ini has been loaded:

Configuration File (php.ini) Path: /usr/local/etc/php/7.4
Loaded Configuration File:         /usr/local/etc/php/7.4/php.ini
Scan for additional .ini files in: /usr/local/etc/php/7.4/conf.d
Additional .ini files parsed:      /usr/local/etc/php/7.4/conf.d/ext-opcache.ini,
/usr/local/etc/php/7.4/conf.d/ext-pcov.ini

Installation of Xdebug

Run

$ pecl install xdebug

and take note of the location of xdebug.so:

Build process completed successfully
Installing '/usr/local/Cellar/php/7.4.6/pecl/20190902/xdebug.so'
install ok: channel://pecl.php.net/xdebug-2.9.5
Extension xdebug enabled in php.ini

Configuration of Xdebug

Run

$ php --ini

to show the names of configuration files loaded by PHP:

Configuration File (php.ini) Path: /usr/local/etc/php/7.4
Loaded Configuration File:         /usr/local/etc/php/7.4/php.ini
Scan for additional .ini files in: /usr/local/etc/php/7.4/conf.d
Additional .ini files parsed:      /usr/local/etc/php/7.4/conf.d/ext-opcache.ini,
/usr/local/etc/php/7.4/conf.d/ext-pcov.ini

Open /usr/local/etc/php/7.4/php.ini and remove

zend_extension="xdebug.so"

Create a file /usr/local/etc/php/7.4/conf.d/ext-xdebug.ini, but use the exact location of xdebug.so we noted earlier:

zend_extension="/usr/local/Cellar/php/7.4.6/pecl/20190902/xdebug.so"

Run

$ php --ini

again and observe that ext-xdebug.ini has been loaded:

Configuration File (php.ini) Path: /usr/local/etc/php/7.4
Loaded Configuration File:         /usr/local/etc/php/7.4/php.ini
Scan for additional .ini files in: /usr/local/etc/php/7.4/conf.d
Additional .ini files parsed:      /usr/local/etc/php/7.4/conf.d/ext-opcache.ini,
/usr/local/etc/php/7.4/conf.d/ext-pcov.ini,
/usr/local/etc/php/7.4/conf.d/ext-xdebug.ini

Disabling and enabling PCOV and Xdebug

Now that we have installed PCOV and Xdebug, we need a way for disabling and enabling them.

The easiest way to disable an extension is to prevent PHP from loading the corresponding configuration file. Since PHP is looking for files ending with a .ini extension, renaming the file seems smart.

In Switching between PHP versions when using Homebrew, I collect PHP versions previously installed with Homebrew.

Again, I would like to have a command that works regardless of which version of PHP I currently use. I would also prefer to use a short command, especially when I am going to run them often. To achieve this, I have added the following to my .zhsrc:

# Toggle extension
function toggle()
{
    name=$1
    displayName=${2:-$1}

    for phpVersion in ${installedPhpVersions[*]}; do
        config="/usr/local/etc/php/${phpVersion}/conf.d/ext-${name}.ini"

        if [[ -f "${config}" ]]; then
            mv "${config}" "${config}.bak"
        elif [[ -f "${config}.bak" ]]; then
            mv "${config}.bak" "${config}"
        fi
    done

    php -v

    if [[ $(php -m | grep ${name}) ]]; then
        echo "${displayName} extension has been enabled."
    else
        echo "${displayName} extension has been disabled."
    fi
}

This function expects a single argument, the name of an extension, and optionally a second argument, the display name. The function iterates over all installed PHP versions, and either adds a .bak extension to the name of a matching configuration file, or removes it - effectively disabling or enabling the corresponding extension.

Now I could run

$ toggle pcov

or

$ toggle xdebug

but to be honest, that is still too much work. I have added the following to .zshrc instead:

# Toggle PCOVV
function p () {
    toggle pcov PCOV
}

# Toggle Xdebug
function x () {
    toggle xdebug Xdebug
}

Thanks to these small functions, I can run

$ p

and

$ x

to quickly disable or enable PCOV and Xdebug.