8000 [TypeInfo] Add `accept` method · symfony/symfony@b41f120 · GitHub
[go: up one dir, main page]

Skip to content

Commit b41f120

Browse files
committed
[TypeInfo] Add accept method
1 parent 78f4d9a commit b41f120

16 files changed

+260
-0
lines changed

src/Symfony/Component/TypeInfo/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
7.3
5+
---
6+
7+
* Add `Type::accept()` method
8+
49
7.2
510
---
611

src/Symfony/Component/TypeInfo/Tests/Type/BackedEnumTypeTest.php

+8
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,12 @@ public function testToString()
2929
{
3030
$this->assertSame(DummyBackedEnum::class, (string) new BackedEnumType(DummyBackedEnum::class, Type::int()));
3131
}
32+
33+
public function testAccept()
34+
{
35+
$type = new BackedEnumType(DummyBackedEnum::class, Type::int());
36+
37+
$this->assertFalse($type->accept('string'));
38+
$this->assertTrue($type->accept(DummyBackedEnum::ONE));
39+
}
3240
}

src/Symfony/Component/TypeInfo/Tests/Type/BuiltinTypeTest.php

+44
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,48 @@ public function testIsNullable()
3939
$this->assertTrue((new BuiltinType(TypeIdentifier::MIXED))->isNullable());
4040
$this->assertFalse((new BuiltinType(TypeIdentifier::INT))->isNullable());
4141
}
42+
43+
public function testAccept()
44+
{
45+
$this->assertFalse((new BuiltinType(TypeIdentifier::ARRAY))->accept('string'));
46+
$this->assertTrue((new BuiltinType(TypeIdentifier::ARRAY))->accept([]));
47+
48+
$this->assertFalse((new BuiltinType(TypeIdentifier::BOOL))->accept('string'));
49+
$this->assertTrue((new BuiltinType(TypeIdentifier::BOOL))->accept(true));
50+
51+
$this->assertFalse((new BuiltinType(TypeIdentifier::CALLABLE))->accept('string'));
52+
$this->assertTrue((new BuiltinType(TypeIdentifier::CALLABLE))->accept('strtoupper'));
53+
54+
$this->assertFalse((new BuiltinType(TypeIdentifier::FALSE))->accept('string'));
55+
$this->assertTrue((new BuiltinType(TypeIdentifier::FALSE))->accept(false));
56+
57+
$this->assertFalse((new BuiltinType(TypeIdentifier::FLOAT))->accept('string'));
58+
$this->assertTrue((new BuiltinType(TypeIdentifier::FLOAT))->accept(1.23));
59+
60+
$this->assertFalse((new BuiltinType(TypeIdentifier::INT))->accept('string'));
61+
$this->assertTrue((new BuiltinType(TypeIdentifier::INT))->accept(123));
62+
63+
$this->assertFalse((new BuiltinType(TypeIdentifier::ITERABLE))->accept('string'));
64+
$this->assertTrue((new BuiltinType(TypeIdentifier::ITERABLE))->accept([]));
65+
66+
$this->assertTrue((new BuiltinType(TypeIdentifier::MIXED))->accept('string'));
67+
68+
$this->assertFalse((new BuiltinType(TypeIdentifier::NULL))->accept('string'));
69+
$this->assertTrue((new BuiltinType(TypeIdentifier::NULL))->accept(null));
70+
71+
$this->assertFalse((new BuiltinType(TypeIdentifier::OBJECT))->accept('string'));
72+
$this->assertTrue((new BuiltinType(TypeIdentifier::OBJECT))->accept(new \stdClass()));
73+
74+
$this->assertFalse((new BuiltinType(TypeIdentifier::RESOURCE))->accept('string'));
75+
$this->assertTrue((new BuiltinType(TypeIdentifier::RESOURCE))->accept(fopen('php://temp', 'r')));
76+
77+
$this->assertFalse((new BuiltinType(TypeIdentifier::STRING))->accept(123));
78+
$this->assertTrue((new BuiltinType(TypeIdentifier::STRING))->accept('string'));
79+
80+
$this->assertFalse((new BuiltinType(TypeIdentifier::TRUE))->accept('string'));
81+
$this->assertTrue((new BuiltinType(TypeIdentifier::TRUE))->accept(true));
82+
83+
$this->assertFalse((new BuiltinType(TypeIdentifier::NEVER))->accept('string'));
84+
$this->assertFalse((new BuiltinType(TypeIdentifier::VOID))->accept('string'));
85+
}
4286
}

src/Symfony/Component/TypeInfo/Tests/Type/CollectionTypeTest.php

+37
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,41 @@ public function testToString()
8888
$type = new CollectionType(new GenericType(Type::builtin(TypeIdentifier::ARRAY), Type::string(), Type::bool()));
8989
$this->assertEquals('array<string,bool>', (string) $type);
9090
}
91+
92+
public function testAccept()
93+
{
94+
$type = new CollectionType(Type::generic(Type::builtin(TypeIdentifier::ARRAY), Type::string(), Type::bool()));
95+
96+
$this->assertFalse($type->accept(new \ArrayObject(['foo' => true, 'bar' => true])));
97+
98+
$this->assertTrue($type->accept(['foo' => true, 'bar' => true]));
99+
$this->assertFalse($type->accept(['foo' => true, 'bar' => 123]));
100+
$this->assertFalse($type->accept([1 => true]));
101+
102+
$type = new CollectionType(Type::generic(Type::builtin(TypeIdentifier::ARRAY), Type::int(), Type::bool()));
103+
104+
$this->assertTrue($type->accept([1 => true]));
105+
$this->assertFalse($type->accept(['foo' => true]));
106+
107+
$type = new CollectionType(Type::generic(Type::builtin(TypeIdentifier::ARRAY), Type::int(), Type::bool()), isList: true);
108+
109+
$this->assertTrue($type->accept([0 => true, 1 => false]));
110+
$this->assertFalse($type->accept([0 => true, 2 => false]));
111+
112+
$type = new CollectionType(Type::generic(Type::builtin(TypeIdentifier::ITERABLE), Type::string(), Type::bool()));
113+
114+
$this->assertTrue($type->accept(new \ArrayObject(['foo' => true, 'bar' => true])));
115+
$this->assertFalse($type->accept(new \ArrayObject(['foo' => true, 'bar' => 123])));
116+
$this->assertFalse($type->accept(new \ArrayObject([1 => true])));
117+
118+
$type = new CollectionType(Type::generic(Type::builtin(TypeIdentifier::ITERABLE), Type::int(), Type::bool()));
119+
120+
$this->assertTrue($type->accept(new \ArrayObject([1 => true])));
121+
$this->assertFalse($type->accept(new \ArrayObject(['foo' => true])));
122+
123+
$type = new CollectionType(Type::generic(Type::builtin(TypeIdentifier::ITERABLE), Type::int(), Type::bool()), isList: true);
124+
125+
$this->assertTrue($type->accept(new \ArrayObject([0 => true, 1 => false])));
126+
$this->assertFalse($type->accept(new \ArrayObject([0 => true, 2 => false])));
127+
}
91128
}

src/Symfony/Component/TypeInfo/Tests/Type/EnumTypeTest.php

+8
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,12 @@ public function testToString()
2121
{
2222
$this->assertSame(DummyEnum::class, (string) new EnumType(DummyEnum::class));
2323
}
24+
25+
public function testAccept()
26+
{
27+
$type = new EnumType(DummyEnum::class);
28+
29+
$this->assertFalse($type->accept('string'));
30+
$this->assertTrue($type->accept(DummyEnum::ONE));
31+
}
2432
}

src/Symfony/Component/TypeInfo/Tests/Type/GenericTypeTest.php

+8
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,12 @@ public function testWrappedTypeIsSatisfiedBy()
4545
$type = new GenericType(Type::builtin(TypeIdentifier::ITERABLE), Type::bool());
4646
$this->assertFalse($type->wrappedTypeIsSatisfiedBy(static fn (Type $t): bool => 'array' === (string) $t));
4747
}
48+
49+
public function testAccept()
50+
{
51+
$type = new GenericType(Type::object(self::class), Type::string());
52+
53+
$this->assertFalse($type->accept('string'));
54+
$this->assertTrue($type->accept($this));
55+
}
4856
}

src/Symfony/Component/TypeInfo/Tests/Type/IntersectionTypeTest.php

+18
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,22 @@ public function testToString()
7777
$type = new IntersectionType(Type::object(\DateTime::class), Type::object(\Iterator::class), Type::object(\Stringable::class));
7878
$this->assertSame(\sprintf('%s&%s&%s', \DateTime::class, \Iterator::class, \Stringable::class), (string) $type);
7979
}
80+
81+
public function testAccept()
82+
{
83+
$type = new IntersectionType(Type::object(\Traversable::class), Type::object(\Countable::class));
84+
85+
$traversableAndCountable = new \ArrayObject();
86+
87+
$countable = new class implements \Countable {
88+
public function count(): int
89+
{
90+
return 1;
91+
}
92+
};
93+
94+
$this->assertFalse($type->accept('string'));
95+
$this->assertFalse($type->accept($countable));
96+
$this->assertTrue($type->accept($traversableAndCountable));
97+
}
8098
}

src/Symfony/Component/TypeInfo/Tests/Type/NullableTypeTest.php

+9
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,13 @@ public function testWrappedTypeIsSatisfiedBy()
4141
$type = new NullableType(Type::string());
4242
$this->assertFalse($type->wrappedTypeIsSatisfiedBy(static fn (Type $t): bool => 'int' === (string) $t));
4343
}
44+
45+
public function testAccept()
46+
{
47+
$type = new NullableType(Type::int());
48+
49+
$this->assertFalse($type->accept('string'));
50+
$this->assertTrue($type->accept(123));
51+
$this->assertTrue($type->accept(null));
52+
}
4453
}

src/Symfony/Component/TypeInfo/Tests/Type/ObjectTypeTest.php

+8
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,12 @@ public function testIsIdentifiedBy()
3535

3636
$this->assertTrue((new ObjectType(self::class))->isIdentifiedBy('array', 'object'));
3737
}
38+
39+
public function testAccept()
40+
{
41+
$this->assertFalse((new ObjectType(self::class))->accept('string'));
42+
$this->assertFalse((new ObjectType(self::class))->accept(new \stdClass()));
43+
$this->assertTrue((new ObjectType(TestCase::class))->accept($this));
44+
$this->assertTrue((new ObjectType(self::class))->accept($this));
45+
}
3846
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\TypeInfo\Tests\Type;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\TypeInfo\Type\BuiltinType;
16+
use Symfony\Component\TypeInfo\Type\TemplateType;
17+
use Symfony\Component\TypeInfo\TypeIdentifier;
18+
19+
class TemplateTypeTest extends TestCase
20+
{
21+
public function testAccept()
22+
{
23+
$this->assertFalse((new TemplateType('T', new BuiltinType(TypeIdentifier::BOOL)))->accept('string'));
24+
$this->assertTrue((new TemplateType('T', new BuiltinType(TypeIdentifier::BOOL)))->accept(true));
25+
}
26+
}

src/Symfony/Component/TypeInfo/Tests/Type/UnionTypeTest.php

+9
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,13 @@ public function testToString()
8585
$type = new UnionType(Type::int(), Type::string(), Type::intersection(Type::object(\DateTime::class), Type::object(\Iterator::class)));
8686
$this->assertSame(\sprintf('(%s&%s)|int|string', \DateTime::class, \Iterator::class), (string) $type);
8787
}
88+
89+
public function testAccept()
90+
{
91+
$type = new UnionType(Type::int(), Type::bool());
92+
93+
$this->assertFalse($type->accept('string'));
94+
$this->assertTrue($type->accept(123));
95+
$this->assertTrue($type->accept(false));
96+
}
8897
}

src/Symfony/Component/TypeInfo/Type.php

+17
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,21 @@ public function isNullable(): bool
5656
{
5757
return false;
5858
}
59+
60+
public function accept(mixed $value): bool
61+
{
62+
$specification = static function (Type $type) use (&$specification, $value): bool {
63+
if ($type instanceof WrappingTypeInterface) {
64+
return $type->wrappedTypeIsSatisfiedBy($specification);
65+
}
66+
67+
if ($type instanceof CompositeTypeInterface) {
68+
return $type->composedTypesAreSatisfiedBy($specification);
69+
}
70+
71+
return $type->accept($value);
72+
};
73+
74+
return $this->isSatisfiedBy($specification);
75+
}
5976
}

src/Symfony/Component/TypeInfo/Type/BuiltinType.php

+20
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,26 @@ public function isNullable(): bool
6262
return \in_array($this->typeIdentifier, [TypeIdentifier::NULL, TypeIdentifier::MIXED]);
6363
}
6464

65+
public function accept(mixed $value): bool
66+
{
67+
return match ($this->typeIdentifier) {
68+
TypeIdentifier::ARRAY => \is_array($value),
69+
TypeIdentifier::BOOL => \is_bool($value),
70+
TypeIdentifier::CALLABLE => \is_callable($value),
71+
TypeIdentifier::FALSE => false === $value,
72+
TypeIdentifier::FLOAT => \is_float($value),
73+
TypeIdentifier::INT => \is_int($value),
74+
TypeIdentifier::ITERABLE => is_iterable($value),
75+
TypeIdentifier::MIXED => true,
76+
TypeIdentifier::NULL => null === $value,
77+
TypeIdentifier::OBJECT => \is_object($value),
78+
TypeIdentifier::RESOURCE => \is_resource($value),
79+
TypeIdentifier::STRING => \is_string($value),
80+
TypeIdentifier::TRUE => true === $value,
81+
default => false,
82+
};
83+
}
84+
6585
public function __toString(): string
6686
{
6787
return $this->typeIdentifier->value;

src/Symfony/Component/TypeInfo/Type/CollectionType.php

+33
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,39 @@ public function wrappedTypeIsSatisfiedBy(callable $specification): bool
9292
return $this->getWrappedType()->isSatisfiedBy($specification);
9393
}
9494

95+
public function accept(mixed $value): bool
96+
{
97+
if (!parent::accept($value)) {
98+
return false;
99+
}
100+
101+
// array is not a list
102+
if ($this->isList() && \is_array($value) && !array_is_list($value)) {
103+
return false;
104+
}
105+
106+
$keyType = $this->getCollectionKeyType();
107+
$valueType = $this->getCollectionValueType();
108+
109+
$i = 0;
110+
111+
foreach ($value as $k => $v) {
112+
// key or value do not match
113+
if (!$keyType->accept($k) || !$valueType->accept($v)) {
114+
return false;
115+
}
116+
117+
// iterable is not a list
118+
if ($this->isList() && !\is_array($value) && $i !== $k) {
119+
return false;
120+
}
121+
122+
++$i;
123+
}
124+
125+
return true;
126+
}
127+
95128
public function __toString(): string
96129
{
97130
return (string) $this->type;

src/Symfony/Component/TypeInfo/Type/NullableType.php

+5
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,9 @@ public function isNullable(): bool
5959
{
6060
return true;
6161
}
62+
63+
public function accept(mixed $value): bool
64+
{
65+
return null === $value || parent::accept($value);
66+
}
6267
}

src/Symfony/Component/TypeInfo/Type/ObjectType.php

+5
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ public function isIdentifiedBy(TypeIdentifier|string ...$identifiers): bool
6666
return false;
6767
}
6868

69+
public function accept(mixed $value): bool
70+
{
71+
return $value instanceof $this->className;
72+
}
73+
6974
public function __toString(): string
7075
{
7176
return $this->className;

0 commit comments

Comments
 (0)
0