Skip to content

Commit ee344a1

Browse files
authored
Added mixed type (#1678)
1 parent 1262761 commit ee344a1

10 files changed

Lines changed: 153 additions & 8 deletions

File tree

src/lib/types/src/Flow/Types/DSL/functions.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
EnumType,
2828
FloatType,
2929
IntegerType,
30+
MixedType,
3031
NullType,
3132
ObjectType,
3233
ResourceType,
@@ -276,6 +277,12 @@ function type_null() : NullType
276277
return new NullType();
277278
}
278279

280+
#[DocumentationDSL(module: Module::TYPES, type: DSLType::TYPE)]
281+
function type_mixed() : MixedType
282+
{
283+
return new MixedType();
284+
}
285+
279286
#[DocumentationDSL(module: Module::TYPES, type: DSLType::TYPE)]
280287
function type_positive_integer() : PositiveIntegerType
281288
{

src/lib/types/src/Flow/Types/Exception/InvalidTypeException.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
final class InvalidTypeException extends InvalidArgumentException
1010
{
11-
private function __construct(string $message, ?\Throwable $previous = null)
11+
public function __construct(string $message, ?\Throwable $previous = null)
1212
{
1313
parent::__construct($message, 0, $previous);
1414
}

src/lib/types/src/Flow/Types/Type/Logical/OptionalType.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
namespace Flow\Types\Type\Logical;
66

7-
use Flow\Types\Exception\InvalidArgumentException;
7+
use Flow\Types\Exception\InvalidTypeException;
8+
use Flow\Types\Type\{Native\MixedType, Type, TypeFactory};
89
use Flow\Types\Type\Native\UnionType;
9-
use Flow\Types\Type\{Type, TypeFactory};
1010

1111
/**
1212
* @template T
@@ -18,16 +18,20 @@
1818
/**
1919
* @param Type<T> $base
2020
*
21-
* @throws InvalidArgumentException
21+
* @throws InvalidTypeException
2222
*/
2323
public function __construct(private Type $base)
2424
{
25+
if ($base instanceof MixedType) {
26+
throw new InvalidTypeException('Optional type cannot be created from MixedType, mixed is a standalone type');
27+
}
28+
2529
if ($base instanceof UnionType) {
26-
throw new InvalidArgumentException('Optional type cannot be created from a union type');
30+
throw new InvalidTypeException('Optional type cannot be created from a union type');
2731
}
2832

2933
if ($base instanceof self) {
30-
throw new InvalidArgumentException('Optional type cannot be created from an optional type');
34+
throw new InvalidTypeException('Optional type cannot be created from an optional type');
3135
}
3236
}
3337

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Flow\Types\Type\Native;
6+
7+
use Flow\Types\Type\Type;
8+
9+
/**
10+
* @implements Type<mixed>
11+
*/
12+
final class MixedType implements Type
13+
{
14+
public function assert(mixed $value) : mixed
15+
{
16+
return $value;
17+
}
18+
19+
public function cast(mixed $value) : mixed
20+
{
21+
return $value;
22+
}
23+
24+
public function isValid(mixed $value) : bool
25+
{
26+
return true;
27+
}
28+
29+
public function normalize() : array
30+
{
31+
return [
32+
'type' => 'mixed',
33+
];
34+
}
35+
36+
public function toString() : string
37+
{
38+
return 'mixed';
39+
}
40+
}

src/lib/types/src/Flow/Types/Type/Native/UnionType.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
*/
2525
public function __construct(private Type $left, private Type $right)
2626
{
27+
if ($left instanceof MixedType || $right instanceof MixedType) {
28+
throw new InvalidTypeException('UnionType cannot be mixed with MixedType, mixed is a standalone type');
29+
}
30+
2731
$types = [];
2832

2933
if ($this->left instanceof self) {

src/lib/types/src/Flow/Types/Type/TypeFactory.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
type_float,
1313
type_integer,
1414
type_json,
15+
type_mixed,
1516
type_non_empty_string,
1617
type_null,
1718
type_object,
@@ -74,6 +75,7 @@ public static function fromArray(array $data) : Type
7475
/** @phpstan-ignore argument.type */
7576
'optional' => OptionalType::fromArray($data),
7677
'scalar' => type_scalar(),
78+
'mixed' => type_mixed(),
7779
default => throw new InvalidArgumentException("Unknown type '{$data['type']}'"),
7880
};
7981
}

src/lib/types/tests/Flow/Types/Tests/Unit/Type/Logical/OptionalTypeTest.php

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44

55
namespace Flow\Types\Tests\Unit\Type\Logical;
66

7-
use function Flow\Types\DSL\{type_float, type_from_array, type_integer, type_optional, type_string, type_union};
7+
use function Flow\Types\DSL\{type_float,
8+
type_from_array,
9+
type_integer,
10+
type_mixed,
11+
type_optional,
12+
type_string,
13+
type_union};
814
use Flow\Types\Type\Logical\OptionalType;
915
use PHPUnit\Framework\Attributes\DataProvider;
1016
use PHPUnit\Framework\TestCase;
@@ -40,12 +46,33 @@ public function test_creating_optional_type_from_another_optional_type() : void
4046
type_optional(type_optional(type_float()));
4147
}
4248

49+
public function test_creating_optional_type_from_mixed_type() : void
50+
{
51+
$this->expectExceptionMessage('Optional type cannot be created from MixedType, mixed is a standalone type');
52+
53+
type_optional(type_mixed());
54+
}
55+
56+
public function test_creating_optional_type_from_optional_type() : void
57+
{
58+
$this->expectExceptionMessage('Optional type cannot be created from an optional type');
59+
60+
type_optional(type_optional(type_float()));
61+
}
62+
4363
public function test_creating_optional_type_from_union_type() : void
4464
{
4565
$this->expectExceptionMessage('Optional type cannot be created from a union type');
4666
type_optional(type_union(type_float(), type_string()));
4767
}
4868

69+
public function test_creating_optional_type_from_union_type_with_mixed() : void
70+
{
71+
$this->expectExceptionMessage('Optional type cannot be created from a union type');
72+
73+
type_optional(type_union(type_float(), type_integer()));
74+
}
75+
4976
public function test_normalizing_optional_type() : void
5077
{
5178
self::assertEquals(
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Flow\Types\Tests\Unit\Type\Native;
6+
7+
use function Flow\Types\DSL\{type_from_array, type_mixed};
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use PHPUnit\Framework\TestCase;
10+
11+
final class MixedTypeTest extends TestCase
12+
{
13+
public static function mixed_data_provider() : \Generator
14+
{
15+
yield [null, null];
16+
yield [true, true];
17+
yield [false, false];
18+
yield [0, 0];
19+
yield [1, 1];
20+
yield [0.0, 0.0];
21+
yield [1.0, 1.0];
22+
yield ['string', 'string'];
23+
yield [[], []];
24+
yield [[1], [1]];
25+
yield [[1 => 2], [1 => 2]];
26+
yield [(object) [], (object) []];
27+
yield [(object) ['a' => 'b'], (object) ['a' => 'b']];
28+
}
29+
30+
#[DataProvider('mixed_data_provider')]
31+
public function test_assert_is_valid_and_cast(mixed $value, mixed $expected) : void
32+
{
33+
self::assertEquals($expected, type_mixed()->assert($value));
34+
self::assertEquals($expected, type_mixed()->cast($value));
35+
self::assertTrue(type_mixed()->isValid($value));
36+
}
37+
38+
public function test_normalize() : void
39+
{
40+
self::assertEquals(['type' => 'mixed'], type_mixed()->normalize());
41+
42+
self::assertEquals(
43+
type_mixed(),
44+
type_from_array(type_mixed()->normalize())
45+
);
46+
}
47+
48+
public function test_to_string() : void
49+
{
50+
self::assertEquals('mixed', type_mixed()->toString());
51+
}
52+
}

src/lib/types/tests/Flow/Types/Tests/Unit/Type/Native/UnionTypeTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
type_float,
99
type_from_array,
1010
type_integer,
11+
type_mixed,
1112
type_null,
1213
type_optional,
1314
type_string,
@@ -145,6 +146,14 @@ public function test_types() : void
145146
);
146147
}
147148

149+
public function test_union_with_mixed_type() : void
150+
{
151+
$this->expectException(InvalidTypeException::class);
152+
$this->expectExceptionMessage('UnionType cannot be mixed with MixedType, mixed is a standalone type');
153+
154+
type_union(type_integer(), type_mixed());
155+
}
156+
148157
/**
149158
* @param UnionType<mixed, mixed> $type
150159
*/

web/landing/resources/dsl.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)