Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. Builder.php

class Builder

Same name in this branch
  1. 11.1.x vendor/phpunit/phpunit/src/TextUI/Configuration/Builder.php \PHPUnit\TextUI\Configuration\Builder
  2. 11.1.x vendor/phpunit/phpunit/src/TextUI/Configuration/Cli/Builder.php \PHPUnit\TextUI\CliArguments\Builder

@internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage

@psalm-import-type TestType from \SebastianBergmann\CodeCoverage\CodeCoverage

Hierarchy

  • class \SebastianBergmann\CodeCoverage\Node\Builder

Expanded class hierarchy of Builder

1 file declares its use of Builder
CodeCoverage.php in vendor/phpunit/php-code-coverage/src/CodeCoverage.php

File

vendor/phpunit/php-code-coverage/src/Node/Builder.php, line 33

Namespace

SebastianBergmann\CodeCoverage\Node
View source
final class Builder {
    private readonly FileAnalyser $analyser;
    public function __construct(FileAnalyser $analyser) {
        $this->analyser = $analyser;
    }
    public function build(CodeCoverage $coverage) : Directory {
        $data = clone $coverage->getData();
        // clone because path munging is destructive to the original data
        $commonPath = $this->reducePaths($data);
        $root = new Directory($commonPath, null);
        $this->addItems($root, $this->buildDirectoryStructure($data), $coverage->getTests());
        return $root;
    }
    
    /**
     * @psalm-param array<string, TestType> $tests
     */
    private function addItems(Directory $root, array $items, array $tests) : void {
        foreach ($items as $key => $value) {
            $key = (string) $key;
            if (str_ends_with($key, '/f')) {
                $key = substr($key, 0, -2);
                $filename = $root->pathAsString() . DIRECTORY_SEPARATOR . $key;
                if (is_file($filename)) {
                    $root->addFile(new File($key, $root, $value['lineCoverage'], $value['functionCoverage'], $tests, $this->analyser
                        ->classesIn($filename), $this->analyser
                        ->traitsIn($filename), $this->analyser
                        ->functionsIn($filename), $this->analyser
                        ->linesOfCodeFor($filename)));
                }
            }
            else {
                $child = $root->addDirectory($key);
                $this->addItems($child, $value, $tests);
            }
        }
    }
    
    /**
     * Builds an array representation of the directory structure.
     *
     * For instance,
     *
     * <code>
     * Array
     * (
     *     [Money.php] => Array
     *         (
     *             ...
     *         )
     *
     *     [MoneyBag.php] => Array
     *         (
     *             ...
     *         )
     * )
     * </code>
     *
     * is transformed into
     *
     * <code>
     * Array
     * (
     *     [.] => Array
     *         (
     *             [Money.php] => Array
     *                 (
     *                     ...
     *                 )
     *
     *             [MoneyBag.php] => Array
     *                 (
     *                     ...
     *                 )
     *         )
     * )
     * </code>
     *
     * @psalm-return array<string, array<string, array{lineCoverage: array<int, int>, functionCoverage: array<string, array<int, int>>}>>
     */
    private function buildDirectoryStructure(ProcessedCodeCoverageData $data) : array {
        $result = [];
        foreach ($data->coveredFiles() as $originalPath) {
            $path = explode(DIRECTORY_SEPARATOR, $originalPath);
            $pointer =& $result;
            $max = count($path);
            for ($i = 0; $i < $max; $i++) {
                $type = '';
                if ($i === $max - 1) {
                    $type = '/f';
                }
                $pointer =& $pointer[$path[$i] . $type];
            }
            $pointer = [
                'lineCoverage' => $data->lineCoverage()[$originalPath] ?? [],
                'functionCoverage' => $data->functionCoverage()[$originalPath] ?? [],
            ];
        }
        return $result;
    }
    
    /**
     * Reduces the paths by cutting the longest common start path.
     *
     * For instance,
     *
     * <code>
     * Array
     * (
     *     [/home/sb/Money/Money.php] => Array
     *         (
     *             ...
     *         )
     *
     *     [/home/sb/Money/MoneyBag.php] => Array
     *         (
     *             ...
     *         )
     * )
     * </code>
     *
     * is reduced to
     *
     * <code>
     * Array
     * (
     *     [Money.php] => Array
     *         (
     *             ...
     *         )
     *
     *     [MoneyBag.php] => Array
     *         (
     *             ...
     *         )
     * )
     * </code>
     */
    private function reducePaths(ProcessedCodeCoverageData $coverage) : string {
        if (empty($coverage->coveredFiles())) {
            return '.';
        }
        $commonPath = '';
        $paths = $coverage->coveredFiles();
        if (count($paths) === 1) {
            $commonPath = dirname($paths[0]) . DIRECTORY_SEPARATOR;
            $coverage->renameFile($paths[0], basename($paths[0]));
            return $commonPath;
        }
        $max = count($paths);
        for ($i = 0; $i < $max; $i++) {
            // strip phar:// prefixes
            if (str_starts_with($paths[$i], 'phar://')) {
                $paths[$i] = substr($paths[$i], 7);
                $paths[$i] = str_replace('/', DIRECTORY_SEPARATOR, $paths[$i]);
            }
            $paths[$i] = explode(DIRECTORY_SEPARATOR, $paths[$i]);
            if (empty($paths[$i][0])) {
                $paths[$i][0] = DIRECTORY_SEPARATOR;
            }
        }
        $done = false;
        $max = count($paths);
        while (!$done) {
            for ($i = 0; $i < $max - 1; $i++) {
                if (!isset($paths[$i][0]) || !isset($paths[$i + 1][0]) || $paths[$i][0] !== $paths[$i + 1][0]) {
                    $done = true;
                    break;
                }
            }
            if (!$done) {
                $commonPath .= $paths[0][0];
                if ($paths[0][0] !== DIRECTORY_SEPARATOR) {
                    $commonPath .= DIRECTORY_SEPARATOR;
                }
                for ($i = 0; $i < $max; $i++) {
                    array_shift($paths[$i]);
                }
            }
        }
        $original = $coverage->coveredFiles();
        $max = count($original);
        for ($i = 0; $i < $max; $i++) {
            $coverage->renameFile($original[$i], implode(DIRECTORY_SEPARATOR, $paths[$i]));
        }
        return substr($commonPath, 0, -1);
    }

}

Members

Title Sort descending Modifiers Object type Summary
Builder::$analyser private property
Builder::addItems private function @psalm-param array&lt;string, TestType&gt; $tests
Builder::build public function
Builder::buildDirectoryStructure private function Builds an array representation of the directory structure.
Builder::reducePaths private function Reduces the paths by cutting the longest common start path.
Builder::__construct public function

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal