Skip to content

Commit 30fc37c

Browse files
Deprecate PropertyTypeIterables's Collections in 1.x in favor of Traversables (#45)
1 parent ea3cf43 commit 30fc37c

File tree

6 files changed

+100
-40
lines changed

6 files changed

+100
-40
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
Similarly, `getDeserializeFormat(): ?string` is deprecated in favor of `getDeserializeFormats(): ?array`
99
* Added `PropertyTypeIterable`, which generalizes `PropertyTypeArray` to allow merging Collection informations like one would with arrays, including between interfaces and concrete classes
1010
* Deprecated `PropertyTypeArray`, please prefer using `PropertyTypeIterable` instead
11+
* `PropertyTypeArray::isCollection()` and `PropertyTypeArray::getCollectionClass()` are deprecated, including in its child classes, in favor of `isTraversable()` and `getTraversableClass()`
1112
* Added a model parser `VisibilityAwarePropertyAccessGuesser` that tries to guess getter and setter methods for non-public properties.
1213

1314
# 1.1.0

src/Metadata/PropertyTypeArray.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,41 @@ public function getSubType(): PropertyType
6363
return $this->subType;
6464
}
6565

66+
/**
67+
* @deprecated Please prefer using {@link isTraversable}
68+
*/
6669
public function isCollection(): bool
6770
{
6871
return $this->isCollection;
6972
}
7073

74+
/**
75+
* @deprecated Please prefer using {@link getTraversableClass}
76+
*
77+
* @return class-string<Collection>|null
78+
*/
7179
public function getCollectionClass(): ?string
7280
{
7381
return $this->isCollection() ? Collection::class : null;
7482
}
7583

84+
public function isTraversable(): bool
85+
{
86+
return $this->isCollection;
87+
}
88+
89+
/**
90+
* @return class-string<\Traversable>
91+
*/
92+
public function getTraversableClass(): string
93+
{
94+
if (!$this->isTraversable()) {
95+
throw new \UnexpectedValueException("Iterable type '{$this}' is not traversable.");
96+
}
97+
98+
return \Traversable::class;
99+
}
100+
76101
/**
77102
* Goes down the type until it is not an array or hashmap anymore.
78103
*/

src/Metadata/PropertyTypeClass.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public function merge(PropertyType $other): PropertyType
6868
if ($other instanceof PropertyTypeUnknown) {
6969
return new self($this->className, $nullable);
7070
}
71-
if (is_a($this->getClassName(), Collection::class, true) && (($other instanceof PropertyTypeIterable) && $other->isCollection())) {
71+
if (is_a($this->getClassName(), Collection::class, true) && (($other instanceof PropertyTypeIterable) && $other->isTraversable())) {
7272
return $other->merge($this);
7373
}
7474
if (is_a($this->getClassName(), \DateTimeInterface::class, true) && ($other instanceof PropertyTypeDateTime)) {

src/Metadata/PropertyTypeIterable.php

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,52 +17,78 @@ final class PropertyTypeIterable extends PropertyTypeArray
1717
/**
1818
* @var string
1919
*/
20-
private $collectionClass;
20+
private $traversableClass;
2121

2222
/**
23-
* @param class-string<\Traversable>|null $collectionClass
23+
* @param class-string<\Traversable>|null $traversableClass
2424
*/
25-
public function __construct(PropertyType $subType, bool $hashmap, bool $nullable, string $collectionClass = null)
25+
public function __construct(PropertyType $subType, bool $hashmap, bool $nullable, string $traversableClass = null)
2626
{
27-
parent::__construct($subType, $hashmap, $nullable, null != $collectionClass);
27+
parent::__construct($subType, $hashmap, $nullable, null != $traversableClass);
2828

29-
$this->collectionClass = $collectionClass;
29+
$this->traversableClass = $traversableClass;
3030
}
3131

3232
public function __toString(): string
3333
{
3434
if ($this->subType instanceof PropertyTypeUnknown) {
35-
return 'array'.($this->isCollection() ? '|\\'.$this->collectionClass : '');
35+
return 'array'.($this->isTraversable() ? '|\\'.$this->traversableClass : '');
3636
}
3737

3838
$array = $this->isHashmap() ? '[string]' : '[]';
39-
if ($this->isCollection()) {
39+
if ($this->isTraversable()) {
4040
$collectionType = $this->isHashmap() ? ', string' : '';
41-
$array .= sprintf('|\\%s<%s%s>', $this->collectionClass, $this->subType, $collectionType);
41+
$array .= sprintf('|\\%s<%s%s>', $this->traversableClass, $this->subType, $collectionType);
4242
}
4343

4444
return ((string) $this->subType).$array.AbstractPropertyType::__toString();
4545
}
4646

47+
/**
48+
* @deprecated Please prefer using {@link getTraversableClass}
49+
*
50+
* @return class-string<Collection>|null
51+
*/
4752
public function getCollectionClass(): ?string
4853
{
49-
return $this->collectionClass;
54+
return $this->isCollection() ? null : $this->traversableClass;
5055
}
5156

57+
/**
58+
* @deprecated Please prefer using {@link isTraversable}
59+
*/
5260
public function isCollection(): bool
5361
{
54-
return null != $this->getCollectionClass();
62+
return (null != $this->traversableClass) && is_a($this->traversableClass, Collection::class, true);
63+
}
64+
65+
/**
66+
* @return class-string<\Traversable>
67+
*/
68+
public function getTraversableClass(): string
69+
{
70+
if (!$this->isTraversable()) {
71+
throw new \UnexpectedValueException("Iterable type '{$this}' is not traversable.");
72+
}
73+
74+
return $this->traversableClass;
75+
}
76+
77+
public function isTraversable(): bool
78+
{
79+
return null != $this->traversableClass;
5580
}
5681

5782
public function merge(PropertyType $other): PropertyType
5883
{
5984
$nullable = $this->isNullable() && $other->isNullable();
85+
$thisTraversableClass = $this->isTraversable() ? $this->getTraversableClass() : null;
6086

6187
if ($other instanceof PropertyTypeUnknown) {
62-
return new self($this->subType, $this->isHashmap(), $nullable, $this->getCollectionClass());
88+
return new self($this->subType, $this->isHashmap(), $nullable, $thisTraversableClass);
6389
}
64-
if ($this->isCollection() && (($other instanceof PropertyTypeClass) && is_a($other->getClassName(), Collection::class, true))) {
65-
return new self($this->getSubType(), $this->isHashmap(), $nullable, $this->findCommonCollectionClass($this->getCollectionClass(), $other->getClassName()));
90+
if ($this->isTraversable() && (($other instanceof PropertyTypeClass) && is_a($other->getClassName(), \Traversable::class, true))) {
91+
return new self($this->getSubType(), $this->isHashmap(), $nullable, $this->findCommonCollectionClass($thisTraversableClass, $other->getClassName()));
6692
}
6793
if (!$other instanceof parent) {
6894
throw new \UnexpectedValueException(sprintf('Can\'t merge type %s with %s, they must be the same or unknown', self::class, \get_class($other)));
@@ -77,13 +103,9 @@ public function merge(PropertyType $other): PropertyType
77103
throw new \UnexpectedValueException(sprintf('Can\'t merge type %s with %s, can\'t change hashmap into plain array', self::class, \get_class($other)));
78104
}
79105

80-
if ($other->isCollection()) {
81-
$otherCollectionClass = ($other instanceof self) ? $other->getCollectionClass() : Collection::class;
82-
} else {
83-
$otherCollectionClass = null;
84-
}
106+
$otherTraversableClass = $other->isTraversable() ? $other->getTraversableClass() : null;
85107
$hashmap = $this->isHashmap() || $other->isHashmap();
86-
$commonClass = $this->findCommonCollectionClass($this->getCollectionClass(), $otherCollectionClass);
108+
$commonClass = $this->findCommonCollectionClass($thisTraversableClass, $otherTraversableClass);
87109

88110
if ($other->getSubType() instanceof PropertyTypeUnknown) {
89111
return new self($this->getSubType(), $hashmap, $nullable, $commonClass);
@@ -99,7 +121,7 @@ public function merge(PropertyType $other): PropertyType
99121
* Find the most derived class that doesn't deny both class hints, meaning the most derived
100122
* between left and right if one is a child of the other
101123
*/
102-
protected function findCommonCollectionClass(?string $left, ?string $right): ?string
124+
private function findCommonCollectionClass(?string $left, ?string $right): ?string
103125
{
104126
if (null === $right) {
105127
return $left;
@@ -115,6 +137,6 @@ protected function findCommonCollectionClass(?string $left, ?string $right): ?st
115137
return $right;
116138
}
117139

118-
throw new \UnexpectedValueException("Collection classes '{$left}' and '{$right}' do not match.");
140+
throw new \UnexpectedValueException("Traversable classes '{$left}' and '{$right}' do not match.");
119141
}
120142
}

tests/Metadata/PropertyTypeIterableTest.php

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,19 @@ public function testDefaultListIsNotCollection(): void
3131
$subType = new PropertyTypePrimitive('int', false);
3232
$list = new PropertyTypeIterable($subType, false, false);
3333

34-
$this->assertFalse($list->isCollection());
34+
$this->assertFalse($list->isTraversable());
35+
}
36+
37+
/**
38+
* @deprecated This only checks the behaviour of deprecated class {@see PropertyTypeArray}
39+
*/
40+
public function testDefaultTraversableClassIsTraversableInterface(): void
41+
{
42+
$subType = new PropertyTypePrimitive('int', false);
43+
$collection = new PropertyTypeArray($subType, false, false, true);
44+
45+
$this->assertTrue($collection->isTraversable());
46+
$this->assertSame(\Traversable::class, $collection->getTraversableClass());
3547
}
3648

3749
/**
@@ -51,9 +63,9 @@ public function testExplicitCollectionClassIsKept(): void
5163
$subType = new PropertyTypePrimitive('int', false);
5264
$collection = new PropertyTypeIterable($subType, false, false, ArrayCollection::class);
5365

54-
$this->assertTrue($collection->isCollection());
55-
$this->assertNotSame(Collection::class, $collection->getCollectionClass());
56-
$this->assertSame(ArrayCollection::class, $collection->getCollectionClass());
66+
$this->assertTrue($collection->isTraversable());
67+
$this->assertNotSame(\Traversable::class, $collection->getTraversableClass());
68+
$this->assertSame(ArrayCollection::class, $collection->getTraversableClass());
5769
}
5870

5971
public function testMergeListWithClassCollection(): void
@@ -74,9 +86,9 @@ public function testMergeDefaultCollectionListWithClassCollection(): void
7486
$result = $defaultCollection->merge($typeHintedCollection);
7587
$this->assertInstanceOf(PropertyTypeArray::class, $result);
7688
/* @var PropertyTypeIterable $result */
77-
$this->assertTrue($result->isCollection());
89+
$this->assertTrue($result->isTraversable());
7890
$this->assertSame((string) $defaultCollection->getSubType(), (string) $result->getSubType());
79-
$this->assertSame(Collection::class, $result->getCollectionClass());
91+
$this->assertSame(\Traversable::class, $result->getTraversableClass());
8092
}
8193

8294
public function testMergeExplicitCollectionListWithClassCollection(): void
@@ -88,9 +100,9 @@ public function testMergeExplicitCollectionListWithClassCollection(): void
88100
$result = $explicitCollection->merge($typeHintedCollection);
89101
$this->assertInstanceOf(PropertyTypeIterable::class, $result);
90102
/* @var PropertyTypeIterable $result */
91-
$this->assertTrue($result->isCollection());
103+
$this->assertTrue($result->isTraversable());
92104
$this->assertSame((string) $explicitCollection->getSubType(), (string) $result->getSubType());
93-
$this->assertSame(ArrayCollection::class, $result->getCollectionClass());
105+
$this->assertSame(ArrayCollection::class, $result->getTraversableClass());
94106
}
95107

96108
public function testMergeExplicitCollectionListWithDefaultCollection(): void
@@ -102,9 +114,9 @@ public function testMergeExplicitCollectionListWithDefaultCollection(): void
102114
$result = $explicitCollection->merge($defaultCollection);
103115
$this->assertInstanceOf(PropertyTypeIterable::class, $result);
104116
/* @var PropertyTypeIterable $result */
105-
$this->assertTrue($result->isCollection());
117+
$this->assertTrue($result->isTraversable());
106118
$this->assertSame((string) $explicitCollection->getSubType(), (string) $result->getSubType());
107-
$this->assertSame(ArrayCollection::class, $result->getCollectionClass());
119+
$this->assertSame(ArrayCollection::class, $result->getTraversableClass());
108120
}
109121

110122
public function testMergeDefaultCollectionListWithExplicitCollection(): void
@@ -116,7 +128,7 @@ public function testMergeDefaultCollectionListWithExplicitCollection(): void
116128
$result = $defaultCollection->merge($explicitCollection);
117129
$this->assertInstanceOf(PropertyTypeArray::class, $result);
118130
/* @var PropertyTypeIterable $result */
119-
$this->assertTrue($result->isCollection());
131+
$this->assertTrue($result->isTraversable());
120132
$this->assertSame((string) $explicitCollection->getSubType(), (string) $result->getSubType());
121133
$this->assertSame(Collection::class, $result->getCollectionClass());
122134
}
@@ -130,9 +142,9 @@ public function testMergeClassCollectionWithExplicitCollectionList(): void
130142
$result = $typeHintedCollection->merge($explicitCollection);
131143
$this->assertInstanceOf(PropertyTypeIterable::class, $result);
132144
/* @var PropertyTypeIterable $result */
133-
$this->assertTrue($result->isCollection());
145+
$this->assertTrue($result->isTraversable());
134146
$this->assertSame((string) $explicitCollection->getSubType(), (string) $result->getSubType());
135-
$this->assertSame(ArrayCollection::class, $result->getCollectionClass());
147+
$this->assertSame(ArrayCollection::class, $result->getTraversableClass());
136148
}
137149

138150
public function testMergeListAndCollection(): void
@@ -144,8 +156,8 @@ public function testMergeListAndCollection(): void
144156
foreach ([$list->merge($collection), $collection->merge($list)] as $result) {
145157
$this->assertInstanceOf(PropertyTypeIterable::class, $result);
146158
/* @var PropertyTypeIterable $result */
147-
$this->assertTrue($result->isCollection());
148-
$this->assertSame(Collection::class, $result->getCollectionClass());
159+
$this->assertTrue($result->isTraversable());
160+
$this->assertSame(Collection::class, $result->getTraversableClass());
149161
$this->assertSame((string) $list->getSubType(), (string) $result->getSubType());
150162
}
151163
}
@@ -159,8 +171,8 @@ public function testMergeListWithExplicitCollectionClass(): void
159171
foreach ([$list->merge($collection), $collection->merge($list)] as $result) {
160172
$this->assertInstanceOf(PropertyTypeIterable::class, $result);
161173
/* @var PropertyTypeIterable $result */
162-
$this->assertTrue($result->isCollection());
163-
$this->assertSame(ArrayCollection::class, $result->getCollectionClass());
174+
$this->assertTrue($result->isTraversable());
175+
$this->assertSame(ArrayCollection::class, $result->getTraversableClass());
164176
$this->assertSame((string) $list->getSubType(), (string) $result->getSubType());
165177
}
166178
}

tests/TypeParser/PhpTypeParserTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ public function testPropertyTypeArrayIsCollection(string $rawType): void
166166
{
167167
$type = $this->parser->parseAnnotationType($rawType, new \ReflectionClass($this));
168168
self::assertInstanceOf(PropertyTypeIterable::class, $type);
169-
self::assertTrue($type->isCollection());
169+
self::assertTrue($type->isTraversable());
170170
}
171171

172172
public function testMultiType(): void

0 commit comments

Comments
 (0)