3, $type instanceof CompositeTraversableType => 2, $type instanceof ScalarType, $type instanceof NullType => 1, default => 0, }; } /** * Sorting the scalar types by priority: int, float, string, bool. */ public static function scalarTypePriority(ScalarType $type): int { return match (true) { $type instanceof IntegerType => 4, $type instanceof FloatType => 3, $type instanceof StringType => 2, $type instanceof BooleanType => 1, default => 0, }; } /** * @return list */ public static function traverseRecursively(Type $type): array { $types = []; if ($type instanceof CompositeType) { foreach ($type->traverse() as $subType) { $types = [...$types, $subType, ...self::traverseRecursively($subType)]; } } return $types; } /** * @param non-empty-array $vacantTypes */ public static function assignVacantTypes(Type $type, array $vacantTypes): Type { try { return self::doAssignVacantTypes($type, $vacantTypes); } catch (InvalidType $exception) { return new UnresolvableType($type->toString(), $exception->getMessage()); } } /** * @param non-empty-array $vacantTypes */ private static function doAssignVacantTypes(Type $type, array $vacantTypes): Type { if ($type instanceof VacantType && isset($vacantTypes[$type->symbol()])) { return $vacantTypes[$type->symbol()]; } if ($type instanceof CompositeType) { return $type->replace( static fn (Type $subType) => self::doAssignVacantTypes($subType, $vacantTypes), ); } return $type; } }