PHP Logosebastianbergmann/php-code-coverage

The `sebastianbergmann/php-code-coverage` library is a fundamental PHP package used for collecting and reporting code coverage information. It is developed and maintained by Sebastian Bergmann, the creator of PHPUnit, and is a core component often utilized during the testing phase of PHP applications.

What is Code Coverage?
Code coverage measures the extent to which the source code of a program is executed when a particular test suite runs. It helps developers understand which parts of their code are being tested and, more importantly, which parts are not. High code coverage often correlates with higher quality and more reliable software, though it's important to note that coverage alone doesn't guarantee the effectiveness of tests.

How it Works:
`php-code-coverage` typically relies on PHP extensions like Xdebug or PCOV (a newer, faster alternative) to instrument the PHP code. When these extensions are active and `php-code-coverage` is running:
1. It hooks into the PHP execution engine.
2. As your application's code is executed (e.g., by your unit tests), it records which lines, branches, or functions are invoked.
3. After the execution, it processes this raw data.
4. It then generates various types of reports (e.g., HTML, Cobertura XML, Clover XML, Text) that visualize the coverage statistics, showing which lines were executed and which were missed.

Key Features and Benefits:
* Identifies Untested Code: Helps pinpoint areas of your codebase that lack sufficient test coverage, guiding where new tests are needed.
* Improves Test Quality: By revealing blind spots, it encourages more thorough and effective testing strategies.
* Supports Multiple Reporting Formats: Provides flexibility in how coverage data is presented, allowing integration with various CI/CD tools and dashboards.
* Integration with PHPUnit: Seamlessly integrates with PHPUnit, making it the de facto standard for code coverage reporting in the PHP ecosystem.
* Performance: Supports both Xdebug (traditional) and PCOV (performance-optimized for modern PHP versions) for data collection.

Installation:
The library is typically installed as a development dependency via Composer:
`composer require sebastianbergmann/php-code-coverage --dev`

While most commonly used through PHPUnit's configuration, it can also be used programmatically to collect coverage for any PHP script, as demonstrated in the example.

Example Code

```php
<?php

// Assuming you have 'composer install' run and a 'vendor/autoload.php' exists.
require __DIR__ . '/vendor/autoload.php';

use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlReportFacade;
use SebastianBergmann\CodeCoverage\Driver\XdebugDriver;
use SebastianBergmann\CodeCoverage\Driver\PcovDriver;
use SebastianBergmann\CodeCoverage\Driver\Driver;

// --- Create a dummy class to be covered ---
// Save this content to 'src/MyCalculator.php'
// (You would need to create a 'src' directory)
namespace MyApp\Services;

class MyCalculator
{
    public function add(int $a, int $b): int
    {
        return $a + $b;
    }

    public function subtract(int $a, int $b): int
    {
        return $a - $b;
    }

    public function multiply(int $a, int $b): int
    {
        return $a * $b;
    }

    public function divide(int $a, int $b): float
    {
        if ($b === 0) {
            throw new \InvalidArgumentException("Cannot divide by zero.");
        }
        return $a / $b;
    }

    public function isPositive(int $number): bool
    {
        if ($number > 0) {
            return true;
        } else {
            return false;
        }
    }
}

// --- Main script to collect and report coverage ---

// 1. Initialize CodeCoverage
// The driver is automatically detected based on available extensions (PCOV then Xdebug)
$coverage = new CodeCoverage();

// 2. Configure filters (Crucial for focusing on your own code)
// Add the directory containing your application code to the whitelist
// This ensures only files in 'src' are analyzed for coverage.
$coverage->addDirectoryToWhitelist(__DIR__ . '/src');

// Optionally, exclude vendor directories, tests, etc., from coverage analysis.
// $coverage->excludeDirectory(__DIR__ . '/vendor');
// $coverage->excludeDirectory(__DIR__ . '/tests');

// 3. Start collecting coverage data
// The string 'MyManualCoverageRun' is an identifier for this data collection.
$coverage->start('MyManualCoverageRun');

// 4. Execute the code you want to cover
// Instantiate and call methods of your class
$calculator = new MyApp\Services\MyCalculator();

echo "Add: " . $calculator->add(5, 3) . PHP_EOL;       // Covers add()
echo "Subtract: " . $calculator->subtract(10, 4) . PHP_EOL; // Covers subtract()
echo "IsPositive (true): " . var_export($calculator->isPositive(7), true) . PHP_EOL; // Covers true branch of isPositive()

try {
    echo "Divide: " . $calculator->divide(10, 2) . PHP_EOL; // Covers divide() for non-zero divisor
} catch (\InvalidArgumentException $e) {
    echo "Error: " . $e->getMessage() . PHP_EOL;
}

// To demonstrate uncovered code, we intentionally don't call:
// - MyCalculator::multiply()
// - MyCalculator::isPositive() for false branch (e.g., $calculator->isPositive(-5))
// - MyCalculator::divide() with zero divisor (which throws an exception, but the line to throw is covered if the condition is met)

// 5. Stop collecting coverage data
$coverage->stop();

// 6. Generate a report (e.g., an HTML report)
$writer = new HtmlReportFacade();
$reportDir = __DIR__ . '/coverage-report'; // Directory where the HTML report will be saved

// Create the report directory if it doesn't exist
if (!is_dir($reportDir)) {
    mkdir($reportDir, 0777, true);
}

$writer->process($coverage, $reportDir); // Process the collected coverage data and write the report

echo "\nCode coverage report successfully generated at: " . realpath($reportDir) . PHP_EOL;
echo "Open '" . realpath($reportDir) . "/index.html' in your browser to view the report.\n";

?>
```