Skip to content

Commit 302249f

Browse files
committed
Refactor exception classes and improve type handling
- Updated exception class names for greater specificity (e.g., `StringTypeException` to `UuidStringTypeException`, `JsonTypeException` to `JsonStringTypeException`). - Enhanced `BoolStandard` to include extended true/false string aliases (`yes`, `no`, `on`, `off`, etc.). - Introduced `tryFromInt` and `tryFromString` methods in `BoolStandard` for non-throwing parsing with `UndefinedStandard` fallback. - Refined integer validation for stricter checking with updated exception messages in `IntType`. - Adjusted exception throwing for UUID v4 and v7 constructors to use refined exception classes with detailed messages. - Updated tests for compatibility with refined exceptions and added cases for expanded `BoolStandard` behavior.
1 parent d357396 commit 302249f

File tree

14 files changed

+121
-41
lines changed

14 files changed

+121
-41
lines changed

src/Abstract/Integer/IntType.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ protected static function assertIntegerString(string $value): void
2121
// Strict check, avoid unexpected string conversion
2222
$convertedValue = (string) ((int) $value);
2323
if ($value !== $convertedValue) {
24-
throw new IntegerTypeException(sprintf('String "%s" has no valid integer value', $value));
24+
throw new IntegerTypeException(sprintf('String "%s" has no valid strict integer value', $value));
2525
}
2626
}
2727

src/Abstract/Undefined/UndefinedType.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616
abstract readonly class UndefinedType implements UndefinedTypeInterface
1717
{
18-
public static function create(): self
18+
public static function create(): static
1919
{
2020
return new static();
2121
}

src/Bool/BoolStandard.php

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
use PhpTypedValues\Abstract\Bool\BoolType;
88
use PhpTypedValues\Exception\BoolTypeException;
9+
use PhpTypedValues\Exception\TypeException;
10+
use PhpTypedValues\Undefined\UndefinedStandard;
911

1012
use function sprintf;
1113

@@ -25,19 +27,37 @@ public function __construct(bool $value)
2527
$this->value = $value;
2628
}
2729

30+
public static function tryFromString(string $value): self|UndefinedStandard
31+
{
32+
try {
33+
return static::fromString($value);
34+
} catch (TypeException) {
35+
return UndefinedStandard::create();
36+
}
37+
}
38+
39+
public static function tryFromInt(int $value): self|UndefinedStandard
40+
{
41+
try {
42+
return static::fromInt($value);
43+
} catch (TypeException) {
44+
return UndefinedStandard::create();
45+
}
46+
}
47+
2848
/**
2949
* @throws BoolTypeException
3050
*/
3151
public static function fromString(string $value): static
3252
{
33-
$lowerCaseValue = strtolower($value);
53+
$lowerCaseValue = strtolower(trim($value));
3454

35-
if ($lowerCaseValue === 'true' || $lowerCaseValue === '1') {
55+
if ($lowerCaseValue === 'true' || $lowerCaseValue === '1' || $lowerCaseValue === 'yes' || $lowerCaseValue === 'on' || $lowerCaseValue === 'y') {
3656
$boolValue = true;
37-
} elseif ($lowerCaseValue === 'false' || $lowerCaseValue === '0') {
57+
} elseif ($lowerCaseValue === 'false' || $lowerCaseValue === '0' || $lowerCaseValue === 'no' || $lowerCaseValue === 'off' || $lowerCaseValue === 'n') {
3858
$boolValue = false;
3959
} else {
40-
throw new BoolTypeException(sprintf('Expected string "true"\"1" or "false"\"0", got "%s"', $value));
60+
throw new BoolTypeException(sprintf('Expected string "true" or "false", got "%s"', $value));
4161
}
4262

4363
return new static($boolValue);

src/Exception/JsonTypeException.php renamed to src/Exception/JsonStringTypeException.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44

55
namespace PhpTypedValues\Exception;
66

7-
class JsonTypeException extends TypeException
7+
class JsonStringTypeException extends StringTypeException
88
{
99
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpTypedValues\Exception;
6+
7+
class UuidStringTypeException extends StringTypeException
8+
{
9+
}

src/String/Json.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
use JsonException;
1010
use PhpTypedValues\Abstract\String\StrType;
11-
use PhpTypedValues\Exception\JsonTypeException;
11+
use PhpTypedValues\Exception\JsonStringTypeException;
1212

1313
use function json_decode;
1414
use function sprintf;
@@ -25,7 +25,7 @@
2525
protected string $value;
2626

2727
/**
28-
* @throws JsonTypeException
28+
* @throws JsonStringTypeException
2929
*/
3030
public function __construct(string $value)
3131
{
@@ -35,7 +35,7 @@ public function __construct(string $value)
3535
}
3636

3737
/**
38-
* @throws JsonTypeException
38+
* @throws JsonStringTypeException
3939
*/
4040
public static function fromString(string $value): static
4141
{
@@ -66,7 +66,7 @@ public function toArray(): array
6666
}
6767

6868
/**
69-
* @throws JsonTypeException
69+
* @throws JsonStringTypeException
7070
*/
7171
protected static function assertJsonString(string $value): void
7272
{
@@ -78,7 +78,7 @@ protected static function assertJsonString(string $value): void
7878
*/
7979
json_decode(json: $value, flags: JSON_THROW_ON_ERROR);
8080
} catch (JsonException $e) {
81-
throw new JsonTypeException(sprintf('String "%s" has no valid JSON value', $value), 0, $e);
81+
throw new JsonStringTypeException(sprintf('String "%s" has no valid JSON value', $value), 0, $e);
8282
}
8383
}
8484
}

src/String/StringUuidV4.php

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

77
use PhpTypedValues\Abstract\String\StrType;
8-
use PhpTypedValues\Exception\StringTypeException;
8+
use PhpTypedValues\Exception\UuidStringTypeException;
99

1010
use function preg_match;
1111
use function sprintf;
@@ -24,7 +24,7 @@
2424
protected string $value;
2525

2626
/**
27-
* @throws StringTypeException
27+
* @throws UuidStringTypeException
2828
*/
2929
public function __construct(string $value)
3030
{
@@ -35,18 +35,18 @@ public function __construct(string $value)
3535
// Format: xxxxxxxx-xxxx-4xxx-[89ab]xxx-xxxxxxxxxxxx (hex, case-insensitive)
3636
if ($normalized === '') {
3737
// Provide a distinct message for empty input to ensure mutation testing can distinguish the branch
38-
throw new StringTypeException(sprintf('Expected non-empty UUID v4 (xxxxxxxx-xxxx-4xxx-[89ab]xxx-xxxxxxxxxxxx), got "%s"', $value));
38+
throw new UuidStringTypeException(sprintf('Expected non-empty UUID v4 (xxxxxxxx-xxxx-4xxx-[89ab]xxx-xxxxxxxxxxxx), got "%s"', $value));
3939
}
4040

4141
if (preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/', $normalized) !== 1) {
42-
throw new StringTypeException(sprintf('Expected UUID v4 (xxxxxxxx-xxxx-4xxx-[89ab]xxx-xxxxxxxxxxxx), got "%s"', $value));
42+
throw new UuidStringTypeException(sprintf('Expected UUID v4 (xxxxxxxx-xxxx-4xxx-[89ab]xxx-xxxxxxxxxxxx), got "%s"', $value));
4343
}
4444

4545
$this->value = $normalized;
4646
}
4747

4848
/**
49-
* @throws StringTypeException
49+
* @throws UuidStringTypeException
5050
*/
5151
public static function fromString(string $value): static
5252
{

src/String/StringUuidV7.php

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

77
use PhpTypedValues\Abstract\String\StrType;
8-
use PhpTypedValues\Exception\StringTypeException;
8+
use PhpTypedValues\Exception\UuidStringTypeException;
99

1010
use function preg_match;
1111
use function sprintf;
@@ -24,7 +24,7 @@
2424
protected string $value;
2525

2626
/**
27-
* @throws StringTypeException
27+
* @throws UuidStringTypeException
2828
*/
2929
public function __construct(string $value)
3030
{
@@ -33,20 +33,20 @@ public function __construct(string $value)
3333

3434
if ($normalized === '') {
3535
// Distinct message for empty input
36-
throw new StringTypeException(sprintf('Expected non-empty UUID v7 (xxxxxxxx-xxxx-7xxx-[89ab]xxx-xxxxxxxxxxxx), got "%s"', $value));
36+
throw new UuidStringTypeException(sprintf('Expected non-empty UUID v7 (xxxxxxxx-xxxx-7xxx-[89ab]xxx-xxxxxxxxxxxx), got "%s"', $value));
3737
}
3838

3939
// RFC 4122-style UUID v7:
4040
// xxxxxxxx-xxxx-7xxx-[89ab]xxx-xxxxxxxxxxxx (hex, case-insensitive)
4141
if (preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/', $normalized) !== 1) {
42-
throw new StringTypeException(sprintf('Expected UUID v7 (xxxxxxxx-xxxx-7xxx-[89ab]xxx-xxxxxxxxxxxx), got "%s"', $value));
42+
throw new UuidStringTypeException(sprintf('Expected UUID v7 (xxxxxxxx-xxxx-7xxx-[89ab]xxx-xxxxxxxxxxxx), got "%s"', $value));
4343
}
4444

4545
$this->value = $normalized;
4646
}
4747

4848
/**
49-
* @throws StringTypeException
49+
* @throws UuidStringTypeException
5050
*/
5151
public static function fromString(string $value): static
5252
{

src/typeUsageTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,14 @@
9696
/**
9797
* Boolean.
9898
*/
99+
$undefinedType1 = BoolStandard::tryFromInt(2);
100+
$undefinedType2 = BoolStandard::tryFromString('test');
101+
try {
102+
echo $undefinedType1->value();
103+
echo $undefinedType2->value();
104+
} catch (UndefinedTypeException $e) {
105+
// suppress
106+
}
99107
echo BoolStandard::fromString('true')->toString() . \PHP_EOL;
100108
echo BoolStandard::fromInt(1)->toString() . \PHP_EOL;
101109
echo BoolStandard::fromBool(true)->toString() . \PHP_EOL;

tests/Unit/Bool/BoolStandardTest.php

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,22 @@
2626
->and(BoolStandard::fromString('TrUe')->toString())->toBe('true');
2727
});
2828

29+
it('parses extended true/false string aliases', function (): void {
30+
// true-like
31+
expect(BoolStandard::fromString('1')->value())->toBeTrue()
32+
->and(BoolStandard::fromString('yes')->value())->toBeTrue()
33+
->and(BoolStandard::fromString('on')->value())->toBeTrue()
34+
->and(BoolStandard::fromString('Y')->value())->toBeTrue()
35+
// false-like
36+
->and(BoolStandard::fromString('0')->value())->toBeFalse()
37+
->and(BoolStandard::fromString('no')->value())->toBeFalse()
38+
->and(BoolStandard::fromString('off')->value())->toBeFalse()
39+
->and(BoolStandard::fromString('N')->value())->toBeFalse();
40+
});
41+
2942
it('throws on invalid string values', function (): void {
30-
expect(fn() => BoolStandard::fromString('yes'))
31-
->toThrow(BoolTypeException::class, 'Expected string "true"\"1" or "false"\"0", got "yes"');
43+
expect(fn() => BoolStandard::fromString('yes1'))
44+
->toThrow(BoolTypeException::class, 'Expected string "true" or "false", got "yes1"');
3245
});
3346

3447
it('parses valid integer values 1/0', function (): void {
@@ -42,3 +55,33 @@
4255
expect(fn() => BoolStandard::fromInt(-1))
4356
->toThrow(BoolTypeException::class, 'Expected int "1" or "0", got "-1"');
4457
});
58+
59+
it('tryFromString returns Undefined on invalid input and BoolStandard on valid', function (): void {
60+
$ok = BoolStandard::tryFromString('true');
61+
$fail = BoolStandard::tryFromString('maybe');
62+
63+
expect($ok)->toBeInstanceOf(BoolStandard::class)
64+
->and($ok->value())->toBeTrue();
65+
66+
// Undefined type instance indicates failure without throwing
67+
expect($fail::class)->toBe(PhpTypedValues\Undefined\UndefinedStandard::class);
68+
});
69+
70+
it('tryFromInt returns Undefined on invalid input and BoolStandard on valid', function (): void {
71+
$one = BoolStandard::tryFromInt(1);
72+
$zero = BoolStandard::tryFromInt(0);
73+
$bad = BoolStandard::tryFromInt(3);
74+
75+
expect($one)->toBeInstanceOf(BoolStandard::class)
76+
->and($one->value())->toBeTrue()
77+
->and($zero)->toBeInstanceOf(BoolStandard::class)
78+
->and($zero->value())->toBeFalse()
79+
->and($bad::class)->toBe(PhpTypedValues\Undefined\UndefinedStandard::class);
80+
});
81+
82+
it('parses string values with surrounding whitespace', function (): void {
83+
expect(BoolStandard::fromString(' true ')->value())->toBeTrue()
84+
->and(BoolStandard::fromString("\tFALSE \n")->value())->toBeFalse()
85+
->and(BoolStandard::fromString(' on ')->value())->toBeTrue()
86+
->and(BoolStandard::fromString(' off ')->value())->toBeFalse();
87+
});

0 commit comments

Comments
 (0)