diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php index af22aa9c91..127ebc6300 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php @@ -14,6 +14,7 @@ namespace Symfony\Component\PropertyInfo\Tests\PhpDocExtractor; use PHPUnit\Framework\TestCase; use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\PropertyInfo\Type; +use phpDocumentor\Reflection\Types\Collection; /** * @author Kévin Dunglas @@ -77,6 +78,39 @@ class PhpDocExtractorTest extends TestCase ); } + /** + * @dataProvider provideCollectionTypes + */ + public function testExtractCollection($property, array $type = null, $shortDescription, $longDescription) + { + if (!class_exists(Collection::class)) { + $this->markTestSkipped('Collections are not implemented in current phpdocumentor/type-resolver version'); + } + + $this->testExtract($property, $type, $shortDescription, $longDescription); + } + + public function provideCollectionTypes() + { + return array( + array('iteratorCollection', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Iterator', true, null, new Type(Type::BUILTIN_TYPE_STRING))), null, null), + array('iteratorCollectionWithKey', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Iterator', true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))), null, null), + array( + 'nestedIterators', + array(new Type( + Type::BUILTIN_TYPE_OBJECT, + false, + 'Iterator', + true, + new Type(Type::BUILTIN_TYPE_INT), + new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Iterator', true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING)) + )), + null, + null, + ), + ); + } + public function testParamTagTypeIsOmitted() { $this->assertNull($this->extractor->getTypes(OmittedParamTagTypeDocBlock::class, 'omittedType')); diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php index f5b47196ee..c20db3625e 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php @@ -42,6 +42,9 @@ class ReflectionExtractorTest extends TestCase 'Guid', 'array', 'emptyVar', + 'iteratorCollection', + 'iteratorCollectionWithKey', + 'nestedIterators', 'foo', 'foo2', 'foo3', @@ -79,6 +82,9 @@ class ReflectionExtractorTest extends TestCase 'Guid', 'array', 'emptyVar', + 'iteratorCollection', + 'iteratorCollectionWithKey', + 'nestedIterators', 'foo', 'foo2', 'foo3', @@ -107,6 +113,9 @@ class ReflectionExtractorTest extends TestCase 'Guid', 'array', 'emptyVar', + 'iteratorCollection', + 'iteratorCollectionWithKey', + 'nestedIterators', 'foo', 'foo2', 'foo3', diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php index afe09ef929..5c3b87c035 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php @@ -75,6 +75,21 @@ class Dummy extends ParentDummy */ public $emptyVar; + /** + * @var \Iterator + */ + public $iteratorCollection; + + /** + * @var \Iterator + */ + public $iteratorCollectionWithKey; + + /** + * @var \Iterator> + */ + public $nestedIterators; + public static function getStatic() { } diff --git a/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php b/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php index 939fb7c195..8ea3b9e732 100644 --- a/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php +++ b/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php @@ -12,6 +12,7 @@ namespace Symfony\Component\PropertyInfo\Util; use phpDocumentor\Reflection\Type as DocType; +use phpDocumentor\Reflection\Types\Collection; use phpDocumentor\Reflection\Types\Compound; use phpDocumentor\Reflection\Types\Null_; use Symfony\Component\PropertyInfo\Type; @@ -39,7 +40,7 @@ final class PhpDocTypeHelper $nullable = true; } - $type = $this->createType((string) $varType, $nullable); + $type = $this->createType($varType, $nullable); if (null !== $type) { $types[] = $type; } @@ -49,16 +50,15 @@ final class PhpDocTypeHelper $varTypes = array(); for ($typeIndex = 0; $varType->has($typeIndex); ++$typeIndex) { - $varTypes[] = (string) $varType->get($typeIndex); - } + $type = $varType->get($typeIndex); - // If null is present, all types are nullable - $nullKey = array_search(Type::BUILTIN_TYPE_NULL, $varTypes); - $nullable = false !== $nullKey; + // If null is present, all types are nullable + if ($type instanceof Null_) { + $nullable = true; + continue; + } - // Remove the null type from the type if other types are defined - if ($nullable && count($varTypes) > 1) { - unset($varTypes[$nullKey]); + $varTypes[] = $type; } foreach ($varTypes as $varType) { @@ -74,8 +74,24 @@ final class PhpDocTypeHelper /** * Creates a {@see Type} from a PHPDoc type. */ - private function createType(string $docType, bool $nullable): ?Type + private function createType(DocType $type, bool $nullable): ?Type { + $docType = (string) $type; + + if ($type instanceof Collection) { + list($phpType, $class) = $this->getPhpTypeAndClass((string) $type->getFqsen()); + + $key = $this->getTypes($type->getKeyType()); + $value = $this->getTypes($type->getValueType()); + + // More than 1 type returned means it is a Compound type, which is + // not handled by Type, so better use a null value. + $key = 1 === \count($key) ? $key[0] : null; + $value = 1 === \count($value) ? $value[0] : null; + + return new Type($phpType, $nullable, $class, true, $key, $value); + } + // Cannot guess if (!$docType || 'mixed' === $docType) { return null;