A man is sitting on a dune covered with dry grass. He appears pensive or perhaps even sad, as he rests his head on his arm with one hand while looking down.

Over the last few months, I've been asked time and again: "Sebastian, which PHP extension do you use to collect code coverage data?" This question may sound simple, but answering it requires a little history lesson.

The story of code coverage for PHP starts with Xdebug 1.2 in April 2003, but actually it starts earlier, because Derick Rethans had to develop the feature first before it could find its way into a release. Back then, collecting code coverage data with Xdebug was very slow and the initial version of the report that PHPUnit generated from this data was very ugly.

From April to December 2006, I lived in Norway and wrote my thesis at eZ Systems, where Derick was also working at the time. We worked together on the Open Source project eZ Components, for which we had tests, of course. Without code coverage analysis, we could run these tests in a few minutes. With code coverage analysis, it took half a day. Unfortunately, this was far too slow to be of any real practical use.

Xdebug 2

With the release of Xdebug 2 in 2007, code coverage analysis became significantly faster and therefore more useful. Since then, Derick has continued to improve the code coverage functionality. For example, Xdebug 2.6 introduced a filter mechanism that speeds up the code coverage analysis.

In a previous [article](/articles/faster-code-coverage), I already wrote about this filter mechanism, which incidentally has become superfluous due to other developments. In a previous article, I already wrote about this filter mechanism, which incidentally has become superfluous due to other developments.

I presented the improvements we have made to PHPUnit to generate code coverage reports faster in another article.

PCOV

Of course, the performance of a functionality such as code coverage analysis could always be better: developers want to spend less time waiting for the tests to run and it is also better for the planet if the CPU consumes less power in the process.

This was also the motivation for Joe Watkins, who introduced PCOV in 2019 as an alternative for collecting code coverage data. Since then, PHPUnit has supported PCOV in addition to Xdebug.

Unfortunately, PCOV is currently not maintained. The last version was released in 2021, but can be used with current PHP versions. However, it cannot be compiled without a patch for the upcoming PHP 8.4.

In contrast to Xdebug, PCOV only supports line coverage. This software metric measures whether every executable line has been executed. Since version 2.3, Xdebug also supports path coverage. This measures whether every possible execution path in a function or method was executed during the execution of the test suite.

For everyday use, line coverage is completely sufficient for me. In recent years, I have used PCOV for this because it was significantly faster than Xdebug, especially compared to Xdebug 2.

I do not use path coverage regularly when I run my tests by hand during programming, nor in my CI pipelines. I only use it when I want to look at the execution paths of a specific method in detail. Therefore, in recent years I have only ever loaded Xdebug when I wanted to look at the path coverage.

Back to the question ...

As I was happy with PCOV, I didn't really give Xdebug 3, which was released at the end of 2020, a chance in daily use. I have now made up for this, inspired by the question mentioned at the beginning, which I have been asked more frequently recently.

Sebastian, which PHP extension do you use to collect code coverage data?

Now I can answer that question!

After five years of PCOV, I am returning to Xdebug. There are two reasons for this: the configuration setting xdebug.mode introduced with Xdebug 3 and the currently uncertain future of PCOV.

Xdebug 3

How Xdebug works can be configured since version 3 via the setting `xdebug.mode` or the environment variable `XDEBUG_MODE`.

I have set xdebug.mode=off in my PHP configuration file. This means that Xdebug is only loaded, but does nothing by default. There is no overhead just because Xdebug is loaded.

If I want to have a code coverage report, I invoke PHPUnit with XDEBUG_MODE=coverage ./tools/phpunit instead of ./tools/phpunit.

I advise against activating the code coverage functionality of Xdebug in the PHP configuration file with xdebug.mode=coverage. The following benchmark will hopefully make it clear why.

Benchmark

When neither Xdebug nor PCOV are loaded, the tests are executed in 14.031 seconds. When Xdebug is loaded but disabled with xdebug.mode=off, there is no significant overhead (14.076 seconds).

The setting xdebug.mode=debug activates Xdebug's step debugger. With this configuration, the tests are executed in 31.928 seconds.

The setting xdebug.mode=develop activates Xdebug's development utilities. With this configuration, the tests are executed in 37.593 seconds.

The setting xdebug.mode=coverage activates Xdebug's code coverage functionality. With this configuration, the tests are executed in 53.028 seconds, which is approximately 3.8 times slower. When PHPUnit collects line coverage data with Xdebug, the tests take 53.488 seconds to execute. It seems to make no difference whether PHPUnit collects Xdebug code coverage data or not.

When PHPUnit collects path coverage data with Xdebug, the tests take 146.811 seconds to run. This is approximately 2.7 times slower than when only line coverage data is collected with Xdebug.

If the PCOV extension is loaded, the tests are executed in 18.844 seconds. If PHPUnit collects line coverage data with PCOV, the tests take 18.862 seconds to execute. It seems to make no difference whether PHPUnit lets PCOV collect code coverage data or not.

Collecting data for a code coverage report with line coverage is approximately 2.8 times slower with Xdebug (53.488 seconds) than with PCOV (18.862 seconds). However, Xdebug is actively developed by Derick and provides more accurate data than PCOV.

I ran this benchmark with PHP 8.5.0, Xdebug 3.5.0, PCOV 1.0.12, and PHPUnit 12.5.2 on an Apple MacBook Air M3 (2024) running macOS 26.1. I used the test suite from my Raytracer project.

The measurements were performed using the hyperfine tool. PHPUnit 12.5.2 was minimally modified to collect code coverage data but not generate code coverage reports. For this benchmark, we are ultimately interested in the time required to execute the tests, not the time required to generate a code coverage report.

Update from December 11, 2025: Unfortunately, the SVG graphic file with the bar chart was not useful because it only embedded a bitmap graphic instead of a real, scalable vector graphic. This bitmap graphic was not easily readable in the website's "dark mode". I took this as an opportunity to rerun the benchmark with the latest versions of PHP, PCOV, and Xdebug in order to create a basis for a more readable bar chart.

I have over 35 years' experience developing software, including almost 30 years working with PHP. I have also been developing PHPUnit for over 25 years. The knowledge I have gained during this time is reflected in my articles, but this is just the tip of the iceberg.

If you and your team would like to benefit from my experience through consulting and coaching, I look forward to hearing from you. Send me a message.