Skip to content

Commit ab64380

Browse files
committed
Add tryFromMixed method to float classes, refine convertMixedToString calls, update tests, and restore UndefinedStandard functionality.
1 parent 0cdd3cc commit ab64380

File tree

10 files changed

+184
-70
lines changed

10 files changed

+184
-70
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"composer validate --strict",
4747
"@cs",
4848
"@sca",
49+
"@usage",
4950
"@test",
5051
"@type",
5152
"@coverage",

src/Abstract/Undefined/UndefinedType.php

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -25,61 +25,4 @@
2525
*/
2626
abstract readonly class UndefinedType extends AbstractType implements UndefinedTypeInterface
2727
{
28-
public static function create(): static
29-
{
30-
return new static();
31-
}
32-
33-
public static function fromString(string $value): static
34-
{
35-
return new static();
36-
}
37-
38-
/**
39-
* @throws UndefinedTypeException
40-
*/
41-
public function toInt(): never
42-
{
43-
throw new UndefinedTypeException('UndefinedType cannot be converted to integer.');
44-
}
45-
46-
/**
47-
* @throws UndefinedTypeException
48-
*/
49-
public function toFloat(): never
50-
{
51-
throw new UndefinedTypeException('UndefinedType cannot be converted to float.');
52-
}
53-
54-
/**
55-
* @throws UndefinedTypeException
56-
*/
57-
public function toString(): never
58-
{
59-
throw new UndefinedTypeException('UndefinedType cannot be converted to string.');
60-
}
61-
62-
/**
63-
* @throws UndefinedTypeException
64-
*/
65-
public function value(): never
66-
{
67-
throw new UndefinedTypeException('UndefinedType has no value.');
68-
}
69-
70-
/**
71-
* @throws UndefinedTypeException
72-
*/
73-
public function __toString(): string
74-
{
75-
throw new UndefinedTypeException('UndefinedType cannot be converted to string.');
76-
}
77-
78-
/**
79-
* @throws UndefinedTypeException
80-
*/
81-
public function jsonSerialize(): never
82-
{
83-
throw new UndefinedTypeException('UndefinedType cannot be serialized for Json.');
84-
}
8528
}

src/Float/FloatPositive.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ public static function tryFromString(string $value): static|Undefined
6161
public static function tryFromMixed(mixed $value): static|Undefined
6262
{
6363
try {
64-
return static::fromString((string) $value);
64+
return static::fromString(
65+
static::convertMixedToString($value)
66+
);
6567
} catch (TypeException) {
6668
return Undefined::create();
6769
}

src/Float/FloatStandard.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ public static function fromFloat(float $value): static
3737
return new static($value);
3838
}
3939

40+
public static function tryFromMixed(mixed $value): static|Undefined
41+
{
42+
try {
43+
return static::fromString(
44+
static::convertMixedToString($value)
45+
);
46+
} catch (TypeException) {
47+
return Undefined::create();
48+
}
49+
}
50+
4051
public static function tryFromString(string $value): static|Undefined
4152
{
4253
try {

src/Undefined/UndefinedStandard.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace PhpTypedValues\Undefined;
66

77
use PhpTypedValues\Abstract\Undefined\UndefinedType;
8+
use PhpTypedValues\Exception\UndefinedTypeException;
89

910
/**
1011
* Base implementation for a special "UndefinedStandard" typed value.
@@ -20,4 +21,61 @@
2021
*/
2122
readonly class UndefinedStandard extends UndefinedType
2223
{
24+
public static function create(): static
25+
{
26+
return new static();
27+
}
28+
29+
public static function fromString(string $value): static
30+
{
31+
return new static();
32+
}
33+
34+
/**
35+
* @throws UndefinedTypeException
36+
*/
37+
public function toInt(): never
38+
{
39+
throw new UndefinedTypeException('UndefinedType cannot be converted to integer.');
40+
}
41+
42+
/**
43+
* @throws UndefinedTypeException
44+
*/
45+
public function toFloat(): never
46+
{
47+
throw new UndefinedTypeException('UndefinedType cannot be converted to float.');
48+
}
49+
50+
/**
51+
* @throws UndefinedTypeException
52+
*/
53+
public function toString(): never
54+
{
55+
throw new UndefinedTypeException('UndefinedType cannot be converted to string.');
56+
}
57+
58+
/**
59+
* @throws UndefinedTypeException
60+
*/
61+
public function value(): never
62+
{
63+
throw new UndefinedTypeException('UndefinedType has no value.');
64+
}
65+
66+
/**
67+
* @throws UndefinedTypeException
68+
*/
69+
public function __toString(): string
70+
{
71+
throw new UndefinedTypeException('UndefinedType cannot be converted to string.');
72+
}
73+
74+
/**
75+
* @throws UndefinedTypeException
76+
*/
77+
public function jsonSerialize(): never
78+
{
79+
throw new UndefinedTypeException('UndefinedType cannot be serialized for Json.');
80+
}
2381
}

src/Usage/Primitive/Float.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
echo FloatType::fromString('2.71828')->toString() . PHP_EOL;
3131
echo Double::fromString('2.71828')->toString() . PHP_EOL;
3232
echo Positive::fromString('2.8')->toString() . PHP_EOL;
33+
echo FloatStandard::tryFromMixed('2.8')->toString() . PHP_EOL;
3334

3435
// PositiveFloat usage
3536
testPositiveFloat(FloatNonNegative::fromFloat(0.5)->value());

src/Usage/Primitive/Integer.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@
4444
echo Integer::tryFromInt(127)->toString() . PHP_EOL;
4545
echo IntegerNonNegative::tryFromString('10')->toString() . PHP_EOL;
4646
echo IntegerPositive::tryFromString('10')->toString() . PHP_EOL;
47-
echo IntegerWeekDay::tryFromString('10')->toString() . PHP_EOL;
48-
echo IntegerTiny::tryFromString('10')->toString() . PHP_EOL;
47+
echo IntegerWeekDay::tryFromString('5')->toString() . PHP_EOL;
48+
echo IntegerTiny::tryFromString('1')->toString() . PHP_EOL;
4949

5050
/**
5151
* Artificial functions.

tests/Unit/Abstract/AbstractTypeTest.php

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,21 @@
55
use PhpTypedValues\Abstract\AbstractType;
66
use PhpTypedValues\Exception\TypeException;
77

8+
abstract readonly class AbstractTypeTest extends AbstractType
9+
{
10+
public static function convert(mixed $value): string
11+
{
12+
return self::convertMixedToString($value);
13+
}
14+
}
15+
816
it('convertMixedToString casts scalars and null to string', function (): void {
9-
expect(AbstractType::convertMixedToString(123))->toBe('123')
10-
->and(AbstractType::convertMixedToString(1.5))->toBe('1.5')
11-
->and(AbstractType::convertMixedToString('foo'))->toBe('foo')
12-
->and(AbstractType::convertMixedToString(true))->toBe('1')
13-
->and(AbstractType::convertMixedToString(false))->toBe('')
14-
->and(AbstractType::convertMixedToString(null))->toBe('');
17+
expect(AbstractTypeTest::convert(123))->toBe('123')
18+
->and(AbstractTypeTest::convert(1.5))->toBe('1.5')
19+
->and(AbstractTypeTest::convert('foo'))->toBe('foo')
20+
->and(AbstractTypeTest::convert(true))->toBe('1')
21+
->and(AbstractTypeTest::convert(false))->toBe('')
22+
->and(AbstractTypeTest::convert(null))->toBe('');
1523
});
1624

1725
it('convertMixedToString accepts Stringable objects', function (): void {
@@ -22,22 +30,22 @@ public function __toString(): string
2230
}
2331
};
2432

25-
expect(AbstractType::convertMixedToString($obj))->toBe('3.5');
33+
expect(AbstractTypeTest::convert($obj))->toBe('3.5');
2634
});
2735

2836
it('convertMixedToString throws TypeException for non-stringable objects, arrays and resources', function (): void {
2937
// array
30-
expect(fn() => AbstractType::convertMixedToString(['x']))
38+
expect(fn() => AbstractTypeTest::convert(['x']))
3139
->toThrow(TypeException::class, 'Value cannot be cast to string');
3240

3341
// stdClass (non-stringable)
34-
expect(fn() => AbstractType::convertMixedToString(new stdClass()))
42+
expect(fn() => AbstractTypeTest::convert(new stdClass()))
3543
->toThrow(TypeException::class, 'Value cannot be cast to string');
3644

3745
// resource
3846
$res = fopen('php://memory', 'r');
3947
try {
40-
expect(fn() => AbstractType::convertMixedToString($res))
48+
expect(fn() => AbstractTypeTest::convert($res))
4149
->toThrow(TypeException::class, 'Value cannot be cast to string');
4250
} finally {
4351
\is_resource($res) && fclose($res);

tests/Unit/Float/FloatPositiveTest.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,51 @@
9898
it('jsonSerialize returns float', function (): void {
9999
expect(FloatPositive::tryFromString('1.1')->jsonSerialize())->toBeFloat();
100100
});
101+
102+
it('__toString mirrors toString and value', function (): void {
103+
$v = FloatPositive::fromFloat(3.14);
104+
105+
expect((string) $v)
106+
->toBe('3.14')
107+
->and($v->toString())
108+
->toBe('3.14')
109+
->and($v->value())
110+
->toBe(3.14);
111+
});
112+
113+
it('tryFromMixed covers positive, non-positive, and non-numeric inputs', function (): void {
114+
// Positive inputs
115+
$fromNumericString = FloatPositive::tryFromMixed('1.2');
116+
$fromInt = FloatPositive::tryFromMixed(3);
117+
$fromFloat = FloatPositive::tryFromMixed(2.5);
118+
119+
// Non-positive inputs
120+
$zero = FloatPositive::tryFromMixed(0);
121+
$negative = FloatPositive::tryFromMixed(-1);
122+
123+
// Non-numeric inputs
124+
$fromArray = FloatPositive::tryFromMixed([1]);
125+
$fromNull = FloatPositive::tryFromMixed(null);
126+
127+
// Stringable object
128+
$stringable = new class {
129+
public function __toString(): string
130+
{
131+
return '1.23';
132+
}
133+
};
134+
$fromStringable = FloatPositive::tryFromMixed($stringable);
135+
136+
expect($fromNumericString)->toBeInstanceOf(FloatPositive::class)
137+
->and($fromNumericString->value())->toBe(1.2)
138+
->and($fromInt)->toBeInstanceOf(FloatPositive::class)
139+
->and($fromInt->value())->toBe(3.0)
140+
->and($fromFloat)->toBeInstanceOf(FloatPositive::class)
141+
->and($fromFloat->value())->toBe(2.5)
142+
->and($zero)->toBeInstanceOf(Undefined::class)
143+
->and($negative)->toBeInstanceOf(Undefined::class)
144+
->and($fromArray)->toBeInstanceOf(Undefined::class)
145+
->and($fromNull)->toBeInstanceOf(Undefined::class)
146+
->and($fromStringable)->toBeInstanceOf(FloatPositive::class)
147+
->and($fromStringable->value())->toBe(1.23);
148+
});

tests/Unit/Float/FloatStandardTest.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,45 @@
4040
it('jsonSerialize returns float', function (): void {
4141
expect(FloatStandard::tryFromString('1.1')->jsonSerialize())->toBeFloat();
4242
});
43+
44+
it('__toString mirrors toString and value', function (): void {
45+
$v = FloatStandard::fromFloat(3.14);
46+
47+
expect((string) $v)
48+
->toBe('3.14')
49+
->and($v->toString())
50+
->toBe('3.14')
51+
->and($v->value())
52+
->toBe(3.14);
53+
});
54+
55+
it('tryFromMixed covers numeric, non-numeric, and stringable inputs', function (): void {
56+
// Numeric inputs
57+
$fromNumericString = FloatStandard::tryFromMixed('1.2');
58+
$fromInt = FloatStandard::tryFromMixed(3);
59+
$fromFloat = FloatStandard::tryFromMixed(2.5);
60+
61+
// Non-numeric inputs
62+
$fromArray = FloatStandard::tryFromMixed([1]);
63+
$fromNull = FloatStandard::tryFromMixed(null);
64+
65+
// Stringable object
66+
$stringable = new class {
67+
public function __toString(): string
68+
{
69+
return '1.23';
70+
}
71+
};
72+
$fromStringable = FloatStandard::tryFromMixed($stringable);
73+
74+
expect($fromNumericString)->toBeInstanceOf(FloatStandard::class)
75+
->and($fromNumericString->value())->toBe(1.2)
76+
->and($fromInt)->toBeInstanceOf(FloatStandard::class)
77+
->and($fromInt->value())->toBe(3.0)
78+
->and($fromFloat)->toBeInstanceOf(FloatStandard::class)
79+
->and($fromFloat->value())->toBe(2.5)
80+
->and($fromArray)->toBeInstanceOf(Undefined::class)
81+
->and($fromNull)->toBeInstanceOf(Undefined::class)
82+
->and($fromStringable)->toBeInstanceOf(FloatStandard::class)
83+
->and($fromStringable->value())->toBe(1.23);
84+
});

0 commit comments

Comments
 (0)