klitsche/dog

View on GitHub
src/Enrichers/Clover/CloverEnricher.php

Summary

Maintainability
A
2 hrs
Test Coverage
F
0%
<?php

declare(strict_types=1);

namespace Klitsche\Dog\Enrichers\Clover;

use Klitsche\Dog\Elements\Class_;
use Klitsche\Dog\Elements\File;
use Klitsche\Dog\Elements\Interface_;
use Klitsche\Dog\Elements\Method;
use Klitsche\Dog\Elements\Trait_;
use Klitsche\Dog\Enrichers\DataAwareInterface;
use Klitsche\Dog\Enrichers\Enricher;
use Klitsche\Dog\Exceptions\EnricherException;
use Klitsche\Dog\ProjectInterface;
use Symfony\Component\Filesystem\Filesystem;

/**
 * Reads coverage clover xml files generated by tools like {@link https://phpunit.de/ phpunit} and
 * {@link https://codeception.com/ codeception} and enriches Project and Elements like File, Class, Trait and Method.
 */
class CloverEnricher extends Enricher
{
    protected \SimpleXMLElement $clover;

    public function prepare(): void
    {
        $filesystem = new Filesystem();
        $file = $this->config->getEnrichers()[$this->id]['file'] ?? '';
        if ($filesystem->isAbsolutePath($file) === false) {
            $file = $this->config->getWorkingDir() . '/' . $file;
        }

        if (file_exists($file) === false) {
            throw EnricherException::create($this, sprintf('File %s not found.', $file));
        }

        try {
            $this->clover = new \SimpleXMLElement($file, LIBXML_NOXMLDECL, true);
        } catch (\Throwable $exception) {
            throw EnricherException::create(
                $this,
                sprintf(
                    'Cannot parse file %s. Details: %s',
                    $file,
                    $exception->getMessage()
                )
            );
        }
    }

    public function enrich(DataAwareInterface $dataHolder): void
    {
        switch (true) {
            case $dataHolder instanceof ProjectInterface:
                $elements = $this->clover->xpath('//project');
                if (isset($elements[0]) === true) {
                    $this->addCoveredClasses($elements[0], $this->clover);
                    $dataHolder->setData($this->id, ProjectMetrics::createFromElement($elements[0]));
                }
                break;

            case $dataHolder instanceof File:
                $fileName = $dataHolder->getId();
                $elements = $this->clover->xpath('//file[@name="' . $fileName . '"]');
                if (isset($elements[0]) === true) {
                    $this->addCoveredClasses($elements[0], $elements[0]);
                    $dataHolder->setData($this->id, FileMetrics::createFromElement($elements[0]));
                }
                break;

            case $dataHolder instanceof Class_:
            case $dataHolder instanceof Trait_:
            case $dataHolder instanceof Interface_:
                $className = ltrim($dataHolder->getId(), '\\');
                $elements = $this->clover->xpath('//class[@name="' . $className . '"]');
                if (isset($elements[0]) === true) {
                    $dataHolder->setData($this->id, ClassMetrics::createFromElement($elements[0]));
                }
                break;

            case $dataHolder instanceof Method:
                $methodName = $dataHolder->getName();
                $fileName = ltrim($dataHolder->getFile()->getId(), '\\');

                // todo: only works for single classes/traits per file
                $elements = $this->clover->xpath(
                    '//file[@name="' . $fileName . '"]/line[@type="method" and @name="' . $methodName . '"]'
                );
                if (isset($elements[0]) === true) {
                    $dataHolder->setData($this->id, MethodMetrics::createFromElement($elements[0]));
                }
                break;
        }
    }

    private function addCoveredClasses(\SimpleXMLElement $element, \SimpleXMLElement $context): void
    {
        $coveredclasses = 0;
        foreach ($context->xpath('//class') as $class) {
            $methods = (int) $class->metrics['methods'];
            $coveredmethods = (int) $class->metrics['coveredmethods'];
            if ($methods > 0 && $methods === $coveredmethods) {
                $coveredclasses++;
            }
        }
        $element->metrics->addAttribute('coveredclasses', (string) $coveredclasses);
    }
}