163 lines
4.6 KiB
PHP
163 lines
4.6 KiB
PHP
<?php declare(strict_types=1);
|
|
/*
|
|
* This file is part of phpunit/php-code-coverage.
|
|
*
|
|
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
namespace SebastianBergmann\CodeCoverage\Driver;
|
|
|
|
use const XDEBUG_CC_BRANCH_CHECK;
|
|
use const XDEBUG_CC_DEAD_CODE;
|
|
use const XDEBUG_CC_UNUSED;
|
|
use const XDEBUG_FILTER_CODE_COVERAGE;
|
|
use const XDEBUG_PATH_INCLUDE;
|
|
use function explode;
|
|
use function extension_loaded;
|
|
use function getenv;
|
|
use function in_array;
|
|
use function ini_get;
|
|
use function phpversion;
|
|
use function version_compare;
|
|
use function xdebug_get_code_coverage;
|
|
use function xdebug_info;
|
|
use function xdebug_set_filter;
|
|
use function xdebug_start_code_coverage;
|
|
use function xdebug_stop_code_coverage;
|
|
use SebastianBergmann\CodeCoverage\Data\RawCodeCoverageData;
|
|
use SebastianBergmann\CodeCoverage\Filter;
|
|
|
|
/**
|
|
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
|
*
|
|
* @see https://xdebug.org/docs/code_coverage#xdebug_get_code_coverage
|
|
*
|
|
* @psalm-type XdebugLinesCoverageType = array<int, int>
|
|
* @psalm-type XdebugBranchCoverageType = array{
|
|
* op_start: int,
|
|
* op_end: int,
|
|
* line_start: int,
|
|
* line_end: int,
|
|
* hit: int,
|
|
* out: array<int, int>,
|
|
* out_hit: array<int, int>,
|
|
* }
|
|
* @psalm-type XdebugPathCoverageType = array{
|
|
* path: array<int, int>,
|
|
* hit: int,
|
|
* }
|
|
* @psalm-type XdebugFunctionCoverageType = array{
|
|
* branches: array<int, XdebugBranchCoverageType>,
|
|
* paths: array<int, XdebugPathCoverageType>,
|
|
* }
|
|
* @psalm-type XdebugFunctionsCoverageType = array<string, XdebugFunctionCoverageType>
|
|
* @psalm-type XdebugPathAndBranchesCoverageType = array{
|
|
* lines: XdebugLinesCoverageType,
|
|
* functions: XdebugFunctionsCoverageType,
|
|
* }
|
|
* @psalm-type XdebugCodeCoverageWithoutPathCoverageType = array<string, XdebugLinesCoverageType>
|
|
* @psalm-type XdebugCodeCoverageWithPathCoverageType = array<string, XdebugPathAndBranchesCoverageType>
|
|
*/
|
|
final class XdebugDriver extends Driver
|
|
{
|
|
/**
|
|
* @throws XdebugNotAvailableException
|
|
* @throws XdebugNotEnabledException
|
|
*/
|
|
public function __construct(Filter $filter)
|
|
{
|
|
$this->ensureXdebugIsAvailable();
|
|
$this->ensureXdebugCodeCoverageFeatureIsEnabled();
|
|
|
|
if (!$filter->isEmpty()) {
|
|
xdebug_set_filter(
|
|
XDEBUG_FILTER_CODE_COVERAGE,
|
|
XDEBUG_PATH_INCLUDE,
|
|
$filter->files(),
|
|
);
|
|
}
|
|
}
|
|
|
|
public function canCollectBranchAndPathCoverage(): bool
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public function canDetectDeadCode(): bool
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public function start(): void
|
|
{
|
|
$flags = XDEBUG_CC_UNUSED;
|
|
|
|
if ($this->detectsDeadCode() || $this->collectsBranchAndPathCoverage()) {
|
|
$flags |= XDEBUG_CC_DEAD_CODE;
|
|
}
|
|
|
|
if ($this->collectsBranchAndPathCoverage()) {
|
|
$flags |= XDEBUG_CC_BRANCH_CHECK;
|
|
}
|
|
|
|
xdebug_start_code_coverage($flags);
|
|
}
|
|
|
|
public function stop(): RawCodeCoverageData
|
|
{
|
|
$data = xdebug_get_code_coverage();
|
|
|
|
xdebug_stop_code_coverage();
|
|
|
|
if ($this->collectsBranchAndPathCoverage()) {
|
|
/* @var XdebugCodeCoverageWithPathCoverageType $data */
|
|
return RawCodeCoverageData::fromXdebugWithPathCoverage($data);
|
|
}
|
|
|
|
/* @var XdebugCodeCoverageWithoutPathCoverageType $data */
|
|
return RawCodeCoverageData::fromXdebugWithoutPathCoverage($data);
|
|
}
|
|
|
|
public function nameAndVersion(): string
|
|
{
|
|
return 'Xdebug ' . phpversion('xdebug');
|
|
}
|
|
|
|
/**
|
|
* @throws XdebugNotAvailableException
|
|
*/
|
|
private function ensureXdebugIsAvailable(): void
|
|
{
|
|
if (!extension_loaded('xdebug')) {
|
|
throw new XdebugNotAvailableException;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @throws XdebugNotEnabledException
|
|
*/
|
|
private function ensureXdebugCodeCoverageFeatureIsEnabled(): void
|
|
{
|
|
if (version_compare(phpversion('xdebug'), '3.1', '>=')) {
|
|
if (!in_array('coverage', xdebug_info('mode'), true)) {
|
|
throw new XdebugNotEnabledException;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
$mode = getenv('XDEBUG_MODE');
|
|
|
|
if ($mode === false || $mode === '') {
|
|
$mode = ini_get('xdebug.mode');
|
|
}
|
|
|
|
if ($mode === false ||
|
|
!in_array('coverage', explode(',', $mode), true)) {
|
|
throw new XdebugNotEnabledException;
|
|
}
|
|
}
|
|
}
|