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