diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 32fc4f3e6..c83f5b75e 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -120,11 +120,6 @@ parameters: count: 1 path: src/Support/DataConfig.php - - - message: "#^Call to method Illuminate\\\\Support\\\\Collection\\<\\(int\\|string\\),string\\|null\\>\\:\\:isEmpty\\(\\) will always evaluate to false\\.$#" - count: 1 - path: src/Support/Validation/RuleDenormalizer.php - - message: "#^Call to an undefined method DateTimeInterface\\:\\:setTimezone\\(\\)\\.$#" count: 1 diff --git a/src/Support/Annotations/CollectionAnnotationReader.php b/src/Support/Annotations/CollectionAnnotationReader.php index 9187c3855..835000481 100644 --- a/src/Support/Annotations/CollectionAnnotationReader.php +++ b/src/Support/Annotations/CollectionAnnotationReader.php @@ -4,7 +4,7 @@ use Iterator; use IteratorAggregate; -use phpDocumentor\Reflection\DocBlock\Tags\Generic; +use phpDocumentor\Reflection\DocBlock\Tag; use phpDocumentor\Reflection\DocBlockFactory; use phpDocumentor\Reflection\TypeResolver; use phpDocumentor\Reflection\Types\Context; @@ -84,9 +84,7 @@ protected function isIterable(ReflectionClass $class): bool protected function getCollectionReturnType(ReflectionClass $class): ?array { $docBlockFactory = DocBlockFactory::createInstance(); - $this->context = $this->contextResolver->execute($class); - $docComment = $class->getDocComment(); if ($docComment === false) { @@ -96,50 +94,65 @@ protected function getCollectionReturnType(ReflectionClass $class): ?array $docBlock = $docBlockFactory->create($docComment, $this->context); $templateTypes = []; - $keyType = null; - $valueType = null; foreach ($docBlock->getTags() as $tag) { - if (! $tag instanceof Generic) { + if (! $tag instanceof Tag) { continue; } - if ($tag->getName() === 'template') { - $description = $tag->getDescription(); + $tagName = $tag->getName(); + $description = (string) $tag; - if (preg_match('/^(\w+)\s+of\s+([^\s]+)/', $description, $matches)) { - $templateTypes[$matches[1]] = $this->resolve($matches[2]); - } + if ($tagName === 'template') { + $this->processTemplateTag($description, $templateTypes); continue; } - if ($tag->getName() === 'extends') { - $description = $tag->getDescription(); - - if (preg_match('/<\s*([^,\s]+)?\s*(?:,\s*([^>\s]+))?\s*>/', $description, $matches)) { - if (count($matches) === 3) { - $keyType = $templateTypes[$matches[1]] ?? $this->resolve($matches[1]); - $valueType = $templateTypes[$matches[2]] ?? $this->resolve($matches[2]); - } else { - $keyType = null; - $valueType = $templateTypes[$matches[1]] ?? $this->resolve($matches[1]); - } - - $keyType = $keyType ? explode('|', $keyType)[0] : null; - $valueType = explode('|', $valueType)[0]; - - return [ - 'keyType' => $keyType, - 'valueType' => $valueType, - ]; - } + if ($tagName === 'extends') { + return $this->processExtendsTag($description, $templateTypes); } } return null; } + private function processTemplateTag(string $description, array &$templateTypes): void + { + // The pattern matches strings like "T of SomeType", capturing "T" as the template name + // and "SomeType" as the type associated with the template. + if (preg_match('/^(\w+)\s+of\s+([^\s]+)/', $description, $matches)) { + $templateTypes[$matches[1]] = $this->resolve($matches[2]); + } + } + + private function processExtendsTag(string $description, array $templateTypes): ?array + { + // The pattern matches strings like "" or "", + // capturing "KeyType" and "ValueType" or just "ValueType" if no key type is specified. + if (preg_match('/<\s*([^,\s]+)?\s*(?:,\s*([^>\s]+))?\s*>/', $description, $matches)) { + $keyType = null; + $valueType = null; + + if (count($matches) === 3) { + $keyType = $templateTypes[class_basename($matches[1])] ?? $this->resolve($matches[1]); + $valueType = $templateTypes[class_basename($matches[2])] ?? $this->resolve($matches[2]); + } else { + $valueType = $templateTypes[class_basename($matches[1])] ?? $this->resolve($matches[1]); + } + + $keyType = $keyType ? explode('|', $keyType)[0] : null; + $valueType = explode('|', $valueType)[0]; + + return [ + 'keyType' => $keyType, + 'valueType' => $valueType, + ]; + } + + return null; + } + protected function resolve(string $type): ?string { $type = (string) $this->typeResolver->resolve($type, $this->context);