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

Breadcrumb

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

class DataProviderHelper

Hierarchy

  • class \PHPStan\Rules\PHPUnit\DataProviderHelper

Expanded class hierarchy of DataProviderHelper

File

vendor/phpstan/phpstan-phpunit/src/Rules/PHPUnit/DataProviderHelper.php, line 25

Namespace

PHPStan\Rules\PHPUnit
View source
class DataProviderHelper {
    
    /**
     * Reflection provider.
     *
     * @var ReflectionProvider
     */
    private $reflectionProvider;
    
    /**
     * The file type mapper.
     *
     * @var FileTypeMapper
     */
    private $fileTypeMapper;
    
    /** @var bool */
    private $phpunit10OrNewer;
    public function __construct(ReflectionProvider $reflectionProvider, FileTypeMapper $fileTypeMapper, bool $phpunit10OrNewer) {
        $this->reflectionProvider = $reflectionProvider;
        $this->fileTypeMapper = $fileTypeMapper;
        $this->phpunit10OrNewer = $phpunit10OrNewer;
    }
    
    /**
     * @return iterable<array{ClassReflection|null, string, int}>
     */
    public function getDataProviderMethods(Scope $scope, ClassMethod $node, ClassReflection $classReflection) : iterable {
        $docComment = $node->getDocComment();
        if ($docComment !== null) {
            $methodPhpDoc = $this->fileTypeMapper
                ->getResolvedPhpDoc($scope->getFile(), $classReflection->getName(), $scope->isInTrait() ? $scope->getTraitReflection()
                ->getName() : null, $node->name
                ->toString(), $docComment->getText());
            foreach ($this->getDataProviderAnnotations($methodPhpDoc) as $annotation) {
                $dataProviderValue = $this->getDataProviderAnnotationValue($annotation);
                if ($dataProviderValue === null) {
                    // Missing value is already handled in NoMissingSpaceInMethodAnnotationRule
                    continue;
                }
                $dataProviderMethod = $this->parseDataProviderAnnotationValue($scope, $dataProviderValue);
                $dataProviderMethod[] = $node->getLine();
                (yield $dataProviderValue => $dataProviderMethod);
            }
        }
        if (!$this->phpunit10OrNewer) {
            return;
        }
        foreach ($node->attrGroups as $attrGroup) {
            foreach ($attrGroup->attrs as $attr) {
                $dataProviderMethod = null;
                if ($attr->name
                    ->toLowerString() === 'phpunit\\framework\\attributes\\dataprovider') {
                    $dataProviderMethod = $this->parseDataProviderAttribute($attr, $classReflection);
                }
                elseif ($attr->name
                    ->toLowerString() === 'phpunit\\framework\\attributes\\dataproviderexternal') {
                    $dataProviderMethod = $this->parseDataProviderExternalAttribute($attr);
                }
                if ($dataProviderMethod === null) {
                    continue;
                }
                yield from $dataProviderMethod;
            }
        }
    }
    
    /**
     * @return array<PhpDocTagNode>
     */
    private function getDataProviderAnnotations(?ResolvedPhpDocBlock $phpDoc) : array {
        if ($phpDoc === null) {
            return [];
        }
        $phpDocNodes = $phpDoc->getPhpDocNodes();
        $annotations = [];
        foreach ($phpDocNodes as $docNode) {
            $annotations = array_merge($annotations, $docNode->getTagsByName('@dataProvider'));
        }
        return $annotations;
    }
    
    /**
     * @return list<IdentifierRuleError> errors
     */
    public function processDataProvider(string $dataProviderValue, ?ClassReflection $classReflection, string $methodName, int $lineNumber, bool $checkFunctionNameCase, bool $deprecationRulesInstalled) : array {
        if ($classReflection === null) {
            return [
                RuleErrorBuilder::message(sprintf('@dataProvider %s related class not found.', $dataProviderValue))->line($lineNumber)
                    ->identifier('phpunit.dataProviderClass')
                    ->build(),
            ];
        }
        try {
            $dataProviderMethodReflection = $classReflection->getNativeMethod($methodName);
        } catch (MissingMethodFromReflectionException $missingMethodFromReflectionException) {
            return [
                RuleErrorBuilder::message(sprintf('@dataProvider %s related method not found.', $dataProviderValue))->line($lineNumber)
                    ->identifier('phpunit.dataProviderMethod')
                    ->build(),
            ];
        }
        $errors = [];
        if ($checkFunctionNameCase && $methodName !== $dataProviderMethodReflection->getName()) {
            $errors[] = RuleErrorBuilder::message(sprintf('@dataProvider %s related method is used with incorrect case: %s.', $dataProviderValue, $dataProviderMethodReflection->getName()))
                ->line($lineNumber)
                ->identifier('method.nameCase')
                ->build();
        }
        if (!$dataProviderMethodReflection->isPublic()) {
            $errors[] = RuleErrorBuilder::message(sprintf('@dataProvider %s related method must be public.', $dataProviderValue))->line($lineNumber)
                ->identifier('phpunit.dataProviderPublic')
                ->build();
        }
        if ($deprecationRulesInstalled && $this->phpunit10OrNewer && !$dataProviderMethodReflection->isStatic()) {
            $errors[] = RuleErrorBuilder::message(sprintf('@dataProvider %s related method must be static in PHPUnit 10 and newer.', $dataProviderValue))->line($lineNumber)
                ->identifier('phpunit.dataProviderStatic')
                ->build();
        }
        return $errors;
    }
    private function getDataProviderAnnotationValue(PhpDocTagNode $phpDocTag) : ?string {
        if (preg_match('/^[^ \\t]+/', (string) $phpDocTag->value, $matches) !== 1) {
            return null;
        }
        return $matches[0];
    }
    
    /**
     * @return array{ClassReflection|null, string}
     */
    private function parseDataProviderAnnotationValue(Scope $scope, string $dataProviderValue) : array {
        $parts = explode('::', $dataProviderValue, 2);
        if (count($parts) <= 1) {
            return [
                $scope->getClassReflection(),
                $dataProviderValue,
            ];
        }
        if ($this->reflectionProvider
            ->hasClass($parts[0])) {
            return [
                $this->reflectionProvider
                    ->getClass($parts[0]),
                $parts[1],
            ];
        }
        return [
            null,
            $dataProviderValue,
        ];
    }
    
    /**
     * @return array<string, array{(ClassReflection|null), string, int}>|null
     */
    private function parseDataProviderExternalAttribute(Attribute $attribute) : ?array {
        if (count($attribute->args) !== 2) {
            return null;
        }
        $methodNameArg = $attribute->args[1]->value;
        if (!$methodNameArg instanceof String_) {
            return null;
        }
        $classNameArg = $attribute->args[0]->value;
        if ($classNameArg instanceof ClassConstFetch && $classNameArg->class instanceof Name) {
            $className = $classNameArg->class
                ->toString();
        }
        elseif ($classNameArg instanceof String_) {
            $className = $classNameArg->value;
        }
        else {
            return null;
        }
        $dataProviderClassReflection = null;
        if ($this->reflectionProvider
            ->hasClass($className)) {
            $dataProviderClassReflection = $this->reflectionProvider
                ->getClass($className);
            $className = $dataProviderClassReflection->getName();
        }
        return [
            sprintf('%s::%s', $className, $methodNameArg->value) => [
                $dataProviderClassReflection,
                $methodNameArg->value,
                $attribute->getLine(),
            ],
        ];
    }
    
    /**
     * @return array<string, array{(ClassReflection|null), string, int}>|null
     */
    private function parseDataProviderAttribute(Attribute $attribute, ClassReflection $classReflection) : ?array {
        if (count($attribute->args) !== 1) {
            return null;
        }
        $methodNameArg = $attribute->args[0]->value;
        if (!$methodNameArg instanceof String_) {
            return null;
        }
        return [
            $methodNameArg->value => [
                $classReflection,
                $methodNameArg->value,
                $attribute->getLine(),
            ],
        ];
    }

}

Members

Title Sort descending Modifiers Object type Summary
DataProviderHelper::$fileTypeMapper private property * The file type mapper.
*
*
DataProviderHelper::$phpunit10OrNewer private property @var bool
DataProviderHelper::$reflectionProvider private property * Reflection provider.
*
*
DataProviderHelper::getDataProviderAnnotations private function *
DataProviderHelper::getDataProviderAnnotationValue private function
DataProviderHelper::getDataProviderMethods public function *
DataProviderHelper::parseDataProviderAnnotationValue private function *
DataProviderHelper::parseDataProviderAttribute private function *
DataProviderHelper::parseDataProviderExternalAttribute private function *
DataProviderHelper::processDataProvider public function *
DataProviderHelper::__construct public function
RSS feed
Powered by Drupal