Skip to content

Commit

Permalink
feature #26300 [PropertyInfo] Implement "Collection" types in PhpDocE…
Browse files Browse the repository at this point in the history
…xtractor (popy-dev)

This PR was squashed before being merged into the 4.2-dev branch (closes #26300).

Discussion
----------

[PropertyInfo] Implement "Collection" types in PhpDocExtractor

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #26299
| License       | MIT
| Doc PR        | todo

Here's a proposition of implementation of my feature request #26299 :
I added few tests covering the requested feature, and I had to change a few things in the ```Symfony\Component\PropertyInfo\Util\PhpDocTypeHelper``` class (createType no longer gets a string, but a ```phpDocumentor\Reflection\Type``` instance) to be able to detect properly Collections and their subtypes.

Of course a simpler implementation is possible, without changing the PhpDocTypeHelper internal behaviour, by matching the input string against ```/^([^>]+)</``` and extracting the classname alone.

Commits
-------

12bafe4 [PropertyInfo] Implement \"Collection\" types in PhpDocExtractor
  • Loading branch information
fabpot committed Jun 26, 2018
2 parents 2b9c142 + 12bafe4 commit 1a3d445
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 10 deletions.
Expand Up @@ -14,6 +14,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\PropertyInfo\Type;
use phpDocumentor\Reflection\Types\Collection;

/**
* @author Kévin Dunglas <dunglas@gmail.com>
Expand Down Expand Up @@ -77,6 +78,39 @@ public function typesProvider()
);
}

/**
* @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'));
Expand Down
Expand Up @@ -42,6 +42,9 @@ public function testGetProperties()
'Guid',
'array',
'emptyVar',
'iteratorCollection',
'iteratorCollectionWithKey',
'nestedIterators',
'foo',
'foo2',
'foo3',
Expand Down Expand Up @@ -79,6 +82,9 @@ public function testGetPropertiesWithCustomPrefixes()
'Guid',
'array',
'emptyVar',
'iteratorCollection',
'iteratorCollectionWithKey',
'nestedIterators',
'foo',
'foo2',
'foo3',
Expand Down Expand Up @@ -107,6 +113,9 @@ public function testGetPropertiesWithNoPrefixes()
'Guid',
'array',
'emptyVar',
'iteratorCollection',
'iteratorCollectionWithKey',
'nestedIterators',
'foo',
'foo2',
'foo3',
Expand Down
15 changes: 15 additions & 0 deletions src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php
Expand Up @@ -75,6 +75,21 @@ class Dummy extends ParentDummy
*/
public $emptyVar;

/**
* @var \Iterator<string>
*/
public $iteratorCollection;

/**
* @var \Iterator<integer,string>
*/
public $iteratorCollectionWithKey;

/**
* @var \Iterator<integer,\Iterator<integer,string>>
*/
public $nestedIterators;

public static function getStatic()
{
}
Expand Down
36 changes: 26 additions & 10 deletions src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php
Expand Up @@ -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;
Expand Down Expand Up @@ -39,7 +40,7 @@ public function getTypes(DocType $varType): array
$nullable = true;
}

$type = $this->createType((string) $varType, $nullable);
$type = $this->createType($varType, $nullable);
if (null !== $type) {
$types[] = $type;
}
Expand All @@ -49,16 +50,15 @@ public function getTypes(DocType $varType): array

$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) {
Expand All @@ -74,8 +74,24 @@ public function getTypes(DocType $varType): array
/**
* 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;
Expand Down

0 comments on commit 1a3d445

Please sign in to comment.