Skip to content

Commit a342595

Browse files
committed
update union
1 parent 07267c3 commit a342595

File tree

2 files changed

+180
-1
lines changed

2 files changed

+180
-1
lines changed

Tests/UnionTest.php

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Nejcc\PhpDatatypes\Tests;
5+
6+
use DateTime;
7+
use InvalidArgumentException;
8+
use Nejcc\PhpDatatypes\Composite\Union\Union;
9+
use PHPUnit\Framework\TestCase;
10+
use stdClass;
11+
12+
class UnionTest extends TestCase
13+
{
14+
/**
15+
* Test the constructor sets the allowed types.
16+
*/
17+
public function testConstructorSetsAllowedTypes()
18+
{
19+
$union = new Union(['int', 'string']);
20+
$this->assertInstanceOf(Union::class, $union);
21+
}
22+
23+
/**
24+
* Test that setting a valid value works.
25+
*/
26+
public function testSetValueWithValidType()
27+
{
28+
$union = new Union(['int', 'string']);
29+
$union->setValue(123);
30+
$this->assertEquals(123, $union->getValue());
31+
32+
$union->setValue('hello');
33+
$this->assertEquals('hello', $union->getValue());
34+
}
35+
36+
/**
37+
* Test that setting an invalid type throws an exception.
38+
*/
39+
public function testSetValueWithInvalidTypeThrowsException()
40+
{
41+
$union = new Union(['int', 'string']);
42+
43+
$this->expectException(InvalidArgumentException::class);
44+
$union->setValue(1.5); // float is not allowed
45+
}
46+
47+
/**
48+
* Test that object type validation works.
49+
*/
50+
public function testSetValueWithObjectType()
51+
{
52+
$union = new Union([DateTime::class]);
53+
$date = new DateTime();
54+
55+
$union->setValue($date);
56+
$this->assertEquals($date, $union->getValue());
57+
}
58+
59+
/**
60+
* Test that dynamically adding allowed types works.
61+
*/
62+
public function testAddAllowedType()
63+
{
64+
$union = new Union(['int']);
65+
$union->addAllowedType('float');
66+
67+
$union->setValue(1.5);
68+
$this->assertEquals(1.5, $union->getValue());
69+
}
70+
71+
/**
72+
* Test that isType works for scalar types.
73+
*/
74+
public function testIsTypeWithScalarTypes()
75+
{
76+
$union = new Union(['int', 'string']);
77+
$union->setValue(123);
78+
79+
$this->assertTrue($union->isType('int'));
80+
$this->assertFalse($union->isType('string'));
81+
}
82+
83+
/**
84+
* Test that isType works for object types.
85+
*/
86+
public function testIsTypeWithObjectTypes()
87+
{
88+
$union = new Union([DateTime::class]);
89+
$date = new DateTime();
90+
91+
$union->setValue($date);
92+
$this->assertTrue($union->isType(DateTime::class));
93+
}
94+
95+
/**
96+
* Test that validateType correctly handles invalid object types.
97+
*/
98+
public function testValidateTypeThrowsExceptionForInvalidObjectType()
99+
{
100+
$union = new Union([DateTime::class]);
101+
102+
$this->expectException(InvalidArgumentException::class);
103+
$union->setValue(new stdClass()); // stdClass is not allowed
104+
}
105+
}

src/Composite/Union/Union.php

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,48 @@
77
final class Union
88
{
99
/**
10+
* The current value of the union.
11+
*
1012
* @var mixed
1113
*/
1214
private mixed $value;
1315

1416
/**
17+
* The allowed types for this union.
18+
*
1519
* @var array
1620
*/
1721
private array $allowedTypes;
1822

1923
/**
24+
* A mapping of PHP shorthand types to their gettype() equivalents.
25+
*
26+
* @var array
27+
*/
28+
private static array $typeMap = [
29+
'int' => 'integer',
30+
'float' => 'double',
31+
'bool' => 'boolean',
32+
];
33+
34+
/**
35+
* Create a new Union instance with allowed types.
36+
*
2037
* @param array $allowedTypes
38+
* @return void
2139
*/
2240
public function __construct(array $allowedTypes)
2341
{
2442
$this->allowedTypes = $allowedTypes;
2543
}
2644

2745
/**
46+
* Set the value of the union, validating the type.
47+
*
2848
* @param mixed $value
2949
* @return void
50+
*
51+
* @throws InvalidArgumentException
3052
*/
3153
public function setValue(mixed $value): void
3254
{
@@ -35,6 +57,8 @@ public function setValue(mixed $value): void
3557
}
3658

3759
/**
60+
* Get the current value of the union.
61+
*
3862
* @return mixed
3963
*/
4064
public function getValue(): mixed
@@ -43,15 +67,65 @@ public function getValue(): mixed
4367
}
4468

4569
/**
70+
* Validate the type of the value against allowed types.
71+
*
4672
* @param mixed $value
4773
* @return void
74+
*
75+
* @throws InvalidArgumentException
4876
*/
4977
private function validateType(mixed $value): void
5078
{
5179
$type = gettype($value);
5280

81+
// If the type is in the type map, convert to the shorthand notation
82+
$shorthandType = array_search($type, self::$typeMap, true);
83+
if ($shorthandType) {
84+
$type = $shorthandType;
85+
}
86+
87+
// If it's a class, validate using 'instanceof' for better OOP support.
88+
foreach ($this->allowedTypes as $allowedType) {
89+
if (class_exists($allowedType) && $value instanceof $allowedType) {
90+
return;
91+
}
92+
}
93+
94+
// Check against the allowed types array.
95+
if (!in_array($type, $this->allowedTypes, true)) {
96+
throw new InvalidArgumentException(
97+
"Invalid type: $type. Allowed types: " . implode(', ', $this->allowedTypes)
98+
);
99+
}
100+
}
101+
102+
/**
103+
* Determine if the current value is of a specific type.
104+
*
105+
* @param string $type
106+
* @return bool
107+
*/
108+
public function isType(string $type): bool
109+
{
110+
$actualType = gettype($this->value);
111+
112+
// Map to shorthand if applicable
113+
$shorthandType = array_search($actualType, self::$typeMap, true);
114+
$actualType = $shorthandType ?: $actualType;
115+
116+
return $actualType === $type || $this->value instanceof $type;
117+
}
118+
119+
/**
120+
* Add a new type to the allowed types of the union.
121+
*
122+
* @param string $type
123+
* @return void
124+
*/
125+
public function addAllowedType(string $type): void
126+
{
53127
if (!in_array($type, $this->allowedTypes, true)) {
54-
throw new InvalidArgumentException("Invalid type: $type. Allowed types: " . implode(', ', $this->allowedTypes));
128+
$this->allowedTypes[] = $type;
55129
}
56130
}
57131
}

0 commit comments

Comments
 (0)