1: | <?php declare(strict_types = 1); |
2: | |
3: | namespace PHPStan\Type; |
4: | |
5: | 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 callable(Type $type, callable(Type): Type $traverse): Type $cb |
35: | */ |
36: | public static function map(Type $type, callable $cb): Type |
37: | { |
38: | $self = new self($cb); |
39: | |
40: | return $self->mapInternal($type); |
41: | } |
42: | |
43: | /** @param callable(Type $type, callable(Type): Type $traverse): Type $cb */ |
44: | private function __construct(callable $cb) |
45: | { |
46: | $this->cb = $cb; |
47: | } |
48: | |
49: | /** @internal */ |
50: | public function mapInternal(Type $type): Type |
51: | { |
52: | return ($this->cb)($type, [$this, 'traverseInternal']); |
53: | } |
54: | |
55: | /** @internal */ |
56: | public function traverseInternal(Type $type): Type |
57: | { |
58: | return $type->traverse([$this, 'mapInternal']); |
59: | } |
60: | |
61: | } |
62: |