Skip to content

Commit 4cce51a

Browse files
committed
Add TrueStandard and FalseStandard classes with tryFromString and tryFromInt methods
- Implemented `TrueStandard` and `FalseStandard` to represent literal boolean values. - Added `tryFromString` and `tryFromInt` methods returning `Undefined` for invalid inputs. - Updated `Usage` examples to demonstrate new methods and their behavior for both branches. - Included comprehensive unit tests to validate `tryFromString`, `tryFromInt`, `fromString`, `fromInt`, and `__toString` behaviors.
1 parent 5b55d8b commit 4cce51a

File tree

5 files changed

+296
-0
lines changed

5 files changed

+296
-0
lines changed

src/Bool/FalseStandard.php

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpTypedValues\Bool;
6+
7+
use PhpTypedValues\Abstract\Bool\BoolType;
8+
use PhpTypedValues\Exception\BoolTypeException;
9+
use PhpTypedValues\Exception\TypeException;
10+
use PhpTypedValues\Undefined\Alias\Undefined;
11+
12+
use function sprintf;
13+
use function strtolower;
14+
use function trim;
15+
16+
/**
17+
* Represents a literal boolean false value.
18+
*
19+
* Accepts common false-like representations in factories:
20+
* - Strings: "false", "0", "no", "off", "n" (case-insensitive)
21+
* - Ints: 0
22+
*
23+
* @psalm-immutable
24+
*/
25+
readonly class FalseStandard extends BoolType
26+
{
27+
protected false $value;
28+
29+
/**
30+
* @throws BoolTypeException
31+
*/
32+
public function __construct(bool $value)
33+
{
34+
if ($value !== false) {
35+
throw new BoolTypeException('Expected false literal, got "true"');
36+
}
37+
38+
$this->value = false;
39+
}
40+
41+
public static function tryFromString(string $value): static|Undefined
42+
{
43+
try {
44+
return static::fromString($value);
45+
} catch (TypeException) {
46+
return Undefined::create();
47+
}
48+
}
49+
50+
public static function tryFromInt(int $value): static|Undefined
51+
{
52+
try {
53+
return static::fromInt($value);
54+
} catch (TypeException) {
55+
return Undefined::create();
56+
}
57+
}
58+
59+
/**
60+
* @throws BoolTypeException
61+
*/
62+
public static function fromString(string $value): static
63+
{
64+
$v = strtolower(trim($value));
65+
if ($v === 'false' || $v === '0' || $v === 'no' || $v === 'off' || $v === 'n') {
66+
return new static(false);
67+
}
68+
69+
throw new BoolTypeException(sprintf('Expected string representing false, got "%s"', $value));
70+
}
71+
72+
/**
73+
* @throws BoolTypeException
74+
*/
75+
public static function fromInt(int $value): static
76+
{
77+
if ($value === 0) {
78+
return new static(false);
79+
}
80+
81+
throw new BoolTypeException(sprintf('Expected int "0" for false, got "%s"', $value));
82+
}
83+
84+
public function value(): bool
85+
{
86+
return $this->value;
87+
}
88+
}

src/Bool/TrueStandard.php

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpTypedValues\Bool;
6+
7+
use PhpTypedValues\Abstract\Bool\BoolType;
8+
use PhpTypedValues\Exception\BoolTypeException;
9+
use PhpTypedValues\Exception\TypeException;
10+
use PhpTypedValues\Undefined\Alias\Undefined;
11+
12+
use function sprintf;
13+
use function strtolower;
14+
use function trim;
15+
16+
/**
17+
* Represents a literal boolean true value.
18+
*
19+
* Accepts common true-like representations in factories:
20+
* - Strings: "true", "1", "yes", "on", "y" (case-insensitive)
21+
* - Ints: 1
22+
*
23+
* @psalm-immutable
24+
*/
25+
readonly class TrueStandard extends BoolType
26+
{
27+
protected bool $value;
28+
29+
/**
30+
* @throws BoolTypeException
31+
*/
32+
public function __construct(bool $value)
33+
{
34+
if ($value !== true) {
35+
throw new BoolTypeException('Expected true literal, got "false"');
36+
}
37+
38+
$this->value = true;
39+
}
40+
41+
public static function tryFromString(string $value): static|Undefined
42+
{
43+
try {
44+
return static::fromString($value);
45+
} catch (TypeException) {
46+
return Undefined::create();
47+
}
48+
}
49+
50+
public static function tryFromInt(int $value): static|Undefined
51+
{
52+
try {
53+
return static::fromInt($value);
54+
} catch (TypeException) {
55+
return Undefined::create();
56+
}
57+
}
58+
59+
/**
60+
* @throws BoolTypeException
61+
*/
62+
public static function fromString(string $value): static
63+
{
64+
$v = strtolower(trim($value));
65+
if ($v === 'true' || $v === '1' || $v === 'yes' || $v === 'on' || $v === 'y') {
66+
return new static(true);
67+
}
68+
69+
throw new BoolTypeException(sprintf('Expected string representing true, got "%s"', $value));
70+
}
71+
72+
/**
73+
* @throws BoolTypeException
74+
*/
75+
public static function fromInt(int $value): static
76+
{
77+
if ($value === 1) {
78+
return new static(true);
79+
}
80+
81+
throw new BoolTypeException(sprintf('Expected int "1" for true, got "%s"', $value));
82+
}
83+
84+
public function value(): bool
85+
{
86+
return $this->value;
87+
}
88+
}

src/Usage/Boolean.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
use PhpTypedValues\Abstract\Bool\BoolTypeInterface;
44
use PhpTypedValues\Bool\Alias\Boolean;
55
use PhpTypedValues\Bool\BoolStandard;
6+
use PhpTypedValues\Bool\FalseStandard;
7+
use PhpTypedValues\Bool\TrueStandard;
68
use PhpTypedValues\Exception\UndefinedTypeException;
9+
use PhpTypedValues\Undefined\Alias\Undefined;
710

811
/**
912
* Boolean.
@@ -23,6 +26,27 @@
2326
// Ensure interface method usage is visible to Psalm
2427
echo (testBool(BoolStandard::fromBool(true)) ? 'true' : 'false') . \PHP_EOL;
2528

29+
// true/false literal usages (and try* to reference both branches for Psalm)
30+
$t1 = TrueStandard::tryFromString('yes');
31+
if (!($t1 instanceof Undefined)) {
32+
echo $t1->toString() . \PHP_EOL;
33+
}
34+
35+
$t2 = TrueStandard::tryFromInt(1);
36+
if (!($t2 instanceof Undefined)) {
37+
echo $t2->toString() . \PHP_EOL;
38+
}
39+
40+
$f1 = FalseStandard::tryFromString('off');
41+
if (!($f1 instanceof Undefined)) {
42+
echo $f1->toString() . \PHP_EOL;
43+
}
44+
45+
$f2 = FalseStandard::tryFromInt(0);
46+
if (!($f2 instanceof Undefined)) {
47+
echo $f2->toString() . \PHP_EOL;
48+
}
49+
2650
/**
2751
* Exercise BoolTypeInterface::value() for Psalm.
2852
*/
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use PhpTypedValues\Bool\FalseStandard;
6+
use PhpTypedValues\Exception\BoolTypeException;
7+
use PhpTypedValues\Undefined\Alias\Undefined;
8+
9+
it('constructs only with false and exposes value/toString', function (): void {
10+
$f = new FalseStandard(false);
11+
expect($f->value())->toBeFalse()
12+
->and($f->toString())->toBe('false')
13+
->and((string) $f)->toBe('false');
14+
15+
expect(fn() => new FalseStandard(true))
16+
->toThrow(BoolTypeException::class, 'Expected false literal, got "true"');
17+
});
18+
19+
it('fromString accepts false-like values only', function (): void {
20+
expect(FalseStandard::fromString('false')->value())->toBeFalse()
21+
->and(FalseStandard::fromString(' NO ')->value())->toBeFalse()
22+
->and(FalseStandard::fromString('off')->value())->toBeFalse()
23+
->and(FalseStandard::fromString('0')->value())->toBeFalse();
24+
25+
expect(fn() => FalseStandard::fromString('true'))
26+
->toThrow(BoolTypeException::class, 'Expected string representing false, got "true"');
27+
});
28+
29+
it('fromInt accepts only 0', function (): void {
30+
expect(FalseStandard::fromInt(0)->value())->toBeFalse();
31+
32+
expect(fn() => FalseStandard::fromInt(1))
33+
->toThrow(BoolTypeException::class, 'Expected int "0" for false, got "1"');
34+
});
35+
36+
it('tryFromString/tryFromInt return Undefined for non-false inputs', function (): void {
37+
$ok = FalseStandard::tryFromString('n');
38+
$badStr = FalseStandard::tryFromString('yes');
39+
$okI = FalseStandard::tryFromInt(0);
40+
$badI = FalseStandard::tryFromInt(2);
41+
42+
expect($ok)->toBeInstanceOf(FalseStandard::class)
43+
->and($ok->value())->toBeFalse()
44+
->and($okI)->toBeInstanceOf(FalseStandard::class)
45+
->and($okI->value())->toBeFalse()
46+
->and($badStr)->toBeInstanceOf(Undefined::class)
47+
->and($badI)->toBeInstanceOf(Undefined::class);
48+
});
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use PhpTypedValues\Bool\TrueStandard;
6+
use PhpTypedValues\Exception\BoolTypeException;
7+
use PhpTypedValues\Undefined\Alias\Undefined;
8+
9+
it('constructs only with true and exposes value/toString', function (): void {
10+
$t = new TrueStandard(true);
11+
expect($t->value())->toBeTrue()
12+
->and($t->toString())->toBe('true')
13+
->and((string) $t)->toBe('true');
14+
15+
expect(fn() => new TrueStandard(false))
16+
->toThrow(BoolTypeException::class, 'Expected true literal, got "false"');
17+
});
18+
19+
it('fromString accepts true-like values only', function (): void {
20+
expect(TrueStandard::fromString('true')->value())->toBeTrue()
21+
->and(TrueStandard::fromString(' YES ')->value())->toBeTrue()
22+
->and(TrueStandard::fromString('on')->value())->toBeTrue()
23+
->and(TrueStandard::fromString('1')->value())->toBeTrue();
24+
25+
expect(fn() => TrueStandard::fromString('false'))
26+
->toThrow(BoolTypeException::class, 'Expected string representing true, got "false"');
27+
});
28+
29+
it('fromInt accepts only 1', function (): void {
30+
expect(TrueStandard::fromInt(1)->value())->toBeTrue();
31+
32+
expect(fn() => TrueStandard::fromInt(0))
33+
->toThrow(BoolTypeException::class, 'Expected int "1" for true, got "0"');
34+
});
35+
36+
it('tryFromString/tryFromInt return Undefined for non-true inputs', function (): void {
37+
$ok = TrueStandard::tryFromString('y');
38+
$badStr = TrueStandard::tryFromString('no');
39+
$okI = TrueStandard::tryFromInt(1);
40+
$badI = TrueStandard::tryFromInt(0);
41+
42+
expect($ok)->toBeInstanceOf(TrueStandard::class)
43+
->and($ok->value())->toBeTrue()
44+
->and($okI)->toBeInstanceOf(TrueStandard::class)
45+
->and($okI->value())->toBeTrue()
46+
->and($badStr)->toBeInstanceOf(Undefined::class)
47+
->and($badI)->toBeInstanceOf(Undefined::class);
48+
});

0 commit comments

Comments
 (0)