1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type;
4:
5: use PHPStan\Turbo\ShadowedByTurboExtension;
6:
7: #[ShadowedByTurboExtension(implementation: __DIR__ . '/../../turbo-ext/src/TypeTraverser.cpp')]
8: final class TypeTraverser
9: {
10:
11: /** @var callable(Type $type, callable(Type): Type $traverse): Type */
12: private $cb;
13:
14: /**
15: * Map a Type recursively
16: *
17: * For every Type instance, the callback can return a new Type, and/or
18: * decide to traverse inner types or to ignore them.
19: *
20: * The following example converts constant strings to objects, while
21: * preserving unions and intersections:
22: *
23: * TypeTraverser::map($type, function (Type $type, callable $traverse): Type {
24: * if ($type instanceof UnionType || $type instanceof IntersectionType) {
25: * // Traverse inner types
26: * return $traverse($type);
27: * }
28: * if ($type instanceof ConstantStringType) {
29: * // Replaces the current type, and don't traverse
30: * return new ObjectType($type->getValue());
31: * }
32: * // Replaces the current type, and don't traverse
33: * return new MixedType();
34: * });
35: *
36: * @api
37: * @param TypeTraverserCallable|callable(Type $type, callable(Type): Type $traverse): Type $cb
38: */
39: public static function map(Type $type, TypeTraverserCallable|callable $cb): Type
40: {
41: $self = new self($cb);
42:
43: return $self->mapInternal($type);
44: }
45:
46: /** @param TypeTraverserCallable|callable(Type $type, callable(Type): Type $traverse): Type $cb */
47: private function __construct(TypeTraverserCallable|callable $cb)
48: {
49: if ($cb instanceof TypeTraverserCallable) {
50: $this->cb = static fn (Type $type, callable $traverse): Type => $cb->traverse($type, $traverse);
51: } else {
52: $this->cb = $cb;
53: }
54: }
55:
56: /** @internal */
57: public function mapInternal(Type $type): Type
58: {
59: return ($this->cb)($type, [$this, 'traverseInternal']);
60: }
61:
62: /** @internal */
63: public function traverseInternal(Type $type): Type
64: {
65: return $type->traverse([$this, 'mapInternal']);
66: }
67:
68: }
69: