Skip to content

Commit 3250966

Browse files
author
Kirill Nesmeyanov
committed
Add object type builder
1 parent adb960c commit 3250966

18 files changed

+455
-300
lines changed

bench/composer.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818
"phpdocumentor/reflection-docblock": "^5.4",
1919
"phpdocumentor/type-resolver": "^1.8",
2020
"symfony/cache": "^5.4|^6.0|^7.0",
21-
"symfony/property-access": "^7.1",
22-
"symfony/serializer": "^7.1",
21+
"symfony/property-access": "^5.4|^6.0|^7.1",
22+
"symfony/serializer": "^5.4|^6.0|^7.1",
2323
"type-lang/phpdoc": "^1.0",
24-
"type-lang/phpdoc-standard-tags": "^1.0"
24+
"type-lang/phpdoc-standard-tags": "^1.0",
25+
"symfony/var-dumper": "^7.1"
2526
},
2627
"scripts": {
2728
"bench": "phpbench run --report=grouped"

example/03.types/04.custom-platform.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use TypeLang\Mapper\Mapper;
66
use TypeLang\Mapper\Platform\GrammarFeature;
77
use TypeLang\Mapper\Platform\PlatformInterface;
8-
use TypeLang\Mapper\Type\Builder\ObjectTypeBuilder;
8+
use TypeLang\Mapper\Type\Builder\ClassTypeBuilder;
99

1010
require __DIR__ . '/../../vendor/autoload.php';
1111

@@ -26,7 +26,7 @@ public function getTypes(): iterable
2626
{
2727
// The platform will only support objects, that is,
2828
// references to existing classes.
29-
yield new ObjectTypeBuilder();
29+
yield new ClassTypeBuilder();
3030
}
3131

3232
public function isFeatureSupported(GrammarFeature $feature): bool

src/Mapper.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ private function createTypeRepository(PlatformInterface $platform): TypeReposito
6262
delegate: TypeRepository::createFromPlatform(
6363
platform: $platform,
6464
parser: $this->parser,
65-
)
65+
),
6666
);
6767

6868
if (($tracer = $this->config->getTracer()) !== null) {
@@ -189,4 +189,18 @@ private function getType(mixed $value, ?string $type): TypeInterface
189189

190190
return $this->types->getTypeByDefinition($type);
191191
}
192+
193+
/**
194+
* @param class-string|object $class
195+
* @throws TypeNotFoundException
196+
* @throws \Throwable
197+
*/
198+
public function warmup(string|object $class): void
199+
{
200+
if (\is_object($class)) {
201+
$class = $class::class;
202+
}
203+
204+
$this->types->getTypeByDefinition($class);
205+
}
192206
}

src/Platform/StandardPlatform.php

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ public function getTypes(): iterable
3131
{
3232
// Adds support for the "mixed" type
3333
yield new Builder\SimpleTypeBuilder('mixed', Type\MixedType::class);
34-
yield new Builder\SimpleTypeBuilder('array-key', Type\ArrayKeyType::class);
3534

3635
// Adds support for the "bool" type
3736
yield new Builder\SimpleTypeBuilder(['bool', 'boolean'], Type\BoolType::class);
@@ -40,13 +39,26 @@ public function getTypes(): iterable
4039
yield new Builder\SimpleTypeBuilder('string', Type\StringType::class);
4140

4241
// Adds support for the "int" type
43-
yield new Builder\SimpleTypeBuilder(['int', 'integer'], Type\IntType::class);
42+
yield new Builder\IntRangeTypeBuilder(['int', 'integer']);
4443

4544
// Adds support for the "float" type
4645
yield new Builder\SimpleTypeBuilder(['float', 'double', 'real'], Type\FloatType::class);
4746

4847
// Adds support for the "array" type
49-
yield new Builder\SimpleTypeBuilder('array', Type\ArrayType::class);
48+
yield new Builder\ArrayTypeBuilder([
49+
'array',
50+
'iterable',
51+
\Iterator::class,
52+
\Generator::class,
53+
\Traversable::class,
54+
\IteratorAggregate::class,
55+
]);
56+
57+
// Adds support for the "object" type
58+
yield new Builder\ObjectTypeBuilder([
59+
'object',
60+
\stdClass::class,
61+
]);
5062

5163
// Adds support for the "?T" statement
5264
yield new Builder\NullableTypeBuilder();
@@ -79,7 +91,7 @@ public function getTypes(): iterable
7991
yield new Builder\UnitEnumTypeBuilder();
8092

8193
// Adds support for the "Path\To\Class" statement
82-
yield new Builder\ObjectTypeBuilder($this->driver);
94+
yield new Builder\ClassTypeBuilder($this->driver);
8395
}
8496

8597
public function isFeatureSupported(GrammarFeature $feature): bool

src/Type/ArrayType.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,15 @@ public function __construct(
3131

3232
public function match(mixed $value, Context $context): bool
3333
{
34-
if (!\is_array($value)) {
34+
if (!\is_iterable($value)) {
3535
return false;
3636
}
3737

38+
// Force return true if the iterator does not allow rewinding.
39+
if ($value instanceof \Generator) {
40+
return true;
41+
}
42+
3843
foreach ($value as $key => $item) {
3944
$entrance = $context->enter($item, new ArrayIndexEntry($key));
4045

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TypeLang\Mapper\Type\Builder;
6+
7+
use TypeLang\Mapper\Mapping\Driver\DriverInterface;
8+
use TypeLang\Mapper\Mapping\Driver\ReflectionDriver;
9+
use TypeLang\Mapper\Runtime\Parser\TypeParserInterface;
10+
use TypeLang\Mapper\Runtime\Repository\TypeRepositoryInterface;
11+
use TypeLang\Mapper\Type\ClassType;
12+
use TypeLang\Mapper\Type\ClassType\ClassInstantiator\ClassInstantiatorInterface;
13+
use TypeLang\Mapper\Type\ClassType\ClassInstantiator\ReflectionClassInstantiator;
14+
use TypeLang\Mapper\Type\ClassType\PropertyAccessor\PropertyAccessorInterface;
15+
use TypeLang\Mapper\Type\ClassType\PropertyAccessor\ReflectionPropertyAccessor;
16+
use TypeLang\Parser\Node\Stmt\NamedTypeNode;
17+
use TypeLang\Parser\Node\Stmt\TypeStatement;
18+
19+
/**
20+
* Creates an {@see ClassType} from a type name containing a reference to an
21+
* existing class.
22+
*
23+
* @template T of object
24+
* @template-extends Builder<NamedTypeNode, ClassType<T>>
25+
*/
26+
class ClassTypeBuilder extends Builder
27+
{
28+
public function __construct(
29+
protected readonly DriverInterface $driver = new ReflectionDriver(),
30+
protected readonly PropertyAccessorInterface $accessor = new ReflectionPropertyAccessor(),
31+
protected readonly ClassInstantiatorInterface $instantiator = new ReflectionClassInstantiator(),
32+
) {}
33+
34+
/**
35+
* Returns {@see true} if the type contains a reference to an existing class.
36+
*/
37+
public function isSupported(TypeStatement $statement): bool
38+
{
39+
if (!$statement instanceof NamedTypeNode) {
40+
return false;
41+
}
42+
43+
/** @var non-empty-string $name */
44+
$name = $statement->name->toString();
45+
46+
if (!\class_exists($name)) {
47+
return false;
48+
}
49+
50+
$reflection = new \ReflectionClass($name);
51+
52+
return $reflection->isInstantiable();
53+
}
54+
55+
public function build(
56+
TypeStatement $statement,
57+
TypeRepositoryInterface $types,
58+
TypeParserInterface $parser,
59+
): ClassType {
60+
$this->expectNoShapeFields($statement);
61+
$this->expectNoTemplateArguments($statement);
62+
63+
/** @var class-string<T> $class */
64+
$class = $statement->name->toString();
65+
66+
$metadata = $this->driver->getClassMetadata(
67+
class: new \ReflectionClass($class),
68+
types: $types,
69+
parser: $parser,
70+
);
71+
72+
return new ClassType(
73+
metadata: $metadata,
74+
accessor: $this->accessor,
75+
instantiator: $this->instantiator,
76+
);
77+
}
78+
}

src/Type/Builder/ObjectTypeBuilder.php

Lines changed: 3 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4,54 +4,16 @@
44

55
namespace TypeLang\Mapper\Type\Builder;
66

7-
use TypeLang\Mapper\Mapping\Driver\DriverInterface;
8-
use TypeLang\Mapper\Mapping\Driver\ReflectionDriver;
97
use TypeLang\Mapper\Runtime\Parser\TypeParserInterface;
108
use TypeLang\Mapper\Runtime\Repository\TypeRepositoryInterface;
119
use TypeLang\Mapper\Type\ObjectType;
12-
use TypeLang\Mapper\Type\ObjectType\ObjectInstantiator\ObjectInstantiatorInterface;
13-
use TypeLang\Mapper\Type\ObjectType\ObjectInstantiator\ReflectionObjectInstantiator;
14-
use TypeLang\Mapper\Type\ObjectType\PropertyAccessor\PropertyAccessorInterface;
15-
use TypeLang\Mapper\Type\ObjectType\PropertyAccessor\ReflectionPropertyAccessor;
16-
use TypeLang\Parser\Node\Stmt\NamedTypeNode;
1710
use TypeLang\Parser\Node\Stmt\TypeStatement;
1811

1912
/**
20-
* Creates an {@see ObjectType} from a type name containing a reference to an
21-
* existing class.
22-
*
23-
* @template T of object
24-
* @template-extends Builder<NamedTypeNode, ObjectType<T>>
13+
* @template-extends NamedTypeBuilder<ObjectType>
2514
*/
26-
class ObjectTypeBuilder extends Builder
15+
class ObjectTypeBuilder extends NamedTypeBuilder
2716
{
28-
public function __construct(
29-
protected readonly DriverInterface $driver = new ReflectionDriver(),
30-
protected readonly PropertyAccessorInterface $accessor = new ReflectionPropertyAccessor(),
31-
protected readonly ObjectInstantiatorInterface $instantiator = new ReflectionObjectInstantiator(),
32-
) {}
33-
34-
/**
35-
* Returns {@see true} if the type contains a reference to an existing class.
36-
*/
37-
public function isSupported(TypeStatement $statement): bool
38-
{
39-
if (!$statement instanceof NamedTypeNode) {
40-
return false;
41-
}
42-
43-
/** @var non-empty-string $name */
44-
$name = $statement->name->toString();
45-
46-
if (!\class_exists($name)) {
47-
return false;
48-
}
49-
50-
$reflection = new \ReflectionClass($name);
51-
52-
return $reflection->isInstantiable();
53-
}
54-
5517
public function build(
5618
TypeStatement $statement,
5719
TypeRepositoryInterface $types,
@@ -60,19 +22,6 @@ public function build(
6022
$this->expectNoShapeFields($statement);
6123
$this->expectNoTemplateArguments($statement);
6224

63-
/** @var class-string<T> $class */
64-
$class = $statement->name->toString();
65-
66-
$metadata = $this->driver->getClassMetadata(
67-
class: new \ReflectionClass($class),
68-
types: $types,
69-
parser: $parser,
70-
);
71-
72-
return new ObjectType(
73-
metadata: $metadata,
74-
accessor: $this->accessor,
75-
instantiator: $this->instantiator,
76-
);
25+
return new ObjectType();
7726
}
7827
}

src/Type/ClassType.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TypeLang\Mapper\Type;
6+
7+
use TypeLang\Mapper\Mapping\Metadata\ClassMetadata;
8+
use TypeLang\Mapper\Type\ClassType\ClassInstantiator\ClassInstantiatorInterface;
9+
use TypeLang\Mapper\Type\ClassType\ClassTypeDenormalizer;
10+
use TypeLang\Mapper\Type\ClassType\ClassTypeNormalizer;
11+
use TypeLang\Mapper\Type\ClassType\PropertyAccessor\PropertyAccessorInterface;
12+
13+
/**
14+
* @template T of object
15+
* @template-extends AsymmetricType<ClassTypeNormalizer<T>, ClassTypeDenormalizer<T>>
16+
*/
17+
class ClassType extends AsymmetricType
18+
{
19+
/**
20+
* @param ClassMetadata<T> $metadata
21+
*/
22+
public function __construct(
23+
ClassMetadata $metadata,
24+
PropertyAccessorInterface $accessor,
25+
ClassInstantiatorInterface $instantiator,
26+
) {
27+
parent::__construct(
28+
normalizer: new ClassTypeNormalizer(
29+
metadata: $metadata,
30+
accessor: $accessor,
31+
),
32+
denormalizer: new ClassTypeDenormalizer(
33+
metadata: $metadata,
34+
accessor: $accessor,
35+
instantiator: $instantiator,
36+
),
37+
);
38+
}
39+
}

src/Type/ObjectType/ObjectInstantiator/ObjectInstantiatorInterface.php renamed to src/Type/ClassType/ClassInstantiator/ClassInstantiatorInterface.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
declare(strict_types=1);
44

5-
namespace TypeLang\Mapper\Type\ObjectType\ObjectInstantiator;
5+
namespace TypeLang\Mapper\Type\ClassType\ClassInstantiator;
66

77
use TypeLang\Mapper\Mapping\Metadata\ClassMetadata;
88

9-
interface ObjectInstantiatorInterface
9+
interface ClassInstantiatorInterface
1010
{
1111
/**
1212
* @template T of object

src/Type/ObjectType/ObjectInstantiator/ReflectionObjectInstantiator.php renamed to src/Type/ClassType/ClassInstantiator/ReflectionClassInstantiator.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
declare(strict_types=1);
44

5-
namespace TypeLang\Mapper\Type\ObjectType\ObjectInstantiator;
5+
namespace TypeLang\Mapper\Type\ClassType\ClassInstantiator;
66

77
use TypeLang\Mapper\Mapping\Metadata\ClassMetadata;
88

9-
final class ReflectionObjectInstantiator implements ObjectInstantiatorInterface
9+
final class ReflectionClassInstantiator implements ClassInstantiatorInterface
1010
{
1111
public function instantiate(ClassMetadata $class): object
1212
{

0 commit comments

Comments
 (0)