Skip to content

Commit 026e096

Browse files
committed
Add UndefinedType support and restructure namespaces with alias classes and tests
- Introduced `UndefinedType` abstract structure and `UndefinedStandard` implementation with an interface and exceptions for "Undefined" states. - Added alias classes (`NotFound`, `NotSet`, `NotExist`, `Undefined`, `Unknown`) for flexible "Undefined" state representation. - Relocated `IntTiny` and `StringVarChar255` to `MariaDb` namespace for clarity and database-specific relevance. - Enhanced `BoolStandard` to accept "1" and "0" strings for boolean parsing, with updated exception messages. - Removed outdated `INSTALL.md`, simplifying documentation and enhancing examples across `README.md` and `USAGE.md`. - Added comprehensive tests for `Undefined` state aliases and updated `psalmTest.php` with usage examples.
1 parent 8b16678 commit 026e096

26 files changed

+339
-133
lines changed

README.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
A PHP 8.2 library of typed value objects for common PHP data types.
44

5+
Building blocks for a DTO's, ValueObjects, Entities, etc.
6+
57
[![Latest Version on Packagist](https://img.shields.io/packagist/v/georgii-web/php-typed-values.svg?style=flat-square)](https://packagist.org/packages/georgii-web/php-typed-values)
68
[![Tests](https://github.com/georgii-web/php-typed-values/actions/workflows/php.yml/badge.svg)](https://github.com/georgii-web/php-typed-values/actions/workflows/php.yml)
79
[![Total Downloads](https://img.shields.io/packagist/dt/georgii-web/php-typed-values.svg?style=flat-square)](https://packagist.org/packages/georgii-web/php-typed-values)
810

911
## Install
1012

11-
Using Composer:
12-
1313
```
1414
composer require georgii-web/php-typed-values
1515
```
@@ -37,7 +37,7 @@ if ($id <= 0) {
3737
```php
3838
readonly class Id extends PositiveInt {}
3939

40-
Id::fromString('123');
40+
Id::fromInt(123);
4141
```
4242

4343
#### 3. Create a composite value object from other typed values (nullable values example):
@@ -48,27 +48,33 @@ final class Profile
4848
public function __construct(
4949
public readonly PositiveInt $id,
5050
public readonly NonEmptyStr $firstName,
51-
public readonly ?NonEmptyStr $lastName,
51+
public readonly ?FloatNonNegative $height,
5252
) {}
5353

5454
public static function fromScalars(
5555
int $id,
5656
string $firstName,
57-
?string $lastName,
57+
string|float|int|null $height,
5858
): self {
5959
return new self(
6060
PositiveInt::fromInt($id),
6161
NonEmptyStr::fromString($firstName),
62-
$lastName !== null ? NonEmptyStr::fromString($lastName) : null,
62+
$height !== null ? FloatNonNegative::fromString((string) $height) : null,
6363
);
6464
}
65+
66+
public function getHeight(): FloatNonNegative|Undefined { // avoid using NULL, which could mean anything
67+
return $this->height ?? Undefined::create();
68+
}
6569
}
6670

6771
// Usage
68-
Profile::fromScalars(id: 101, firstName: 'Alice', lastName: 'Smith');
69-
Profile::fromScalars(id: 157, firstName: 'Tom', lastName: null);
72+
Profile::fromScalars(id: 101, firstName: 'Alice', height: '172.5');
73+
Profile::fromScalars(id: 157, firstName: 'Tom', height: null);
7074
// From array
7175
$profile = Profile::fromScalars(...[157, 'Tom', null]);
76+
// Accessing values
77+
$profile->getHeight(); // "172.5 \ Undefined" type class
7278
```
7379

7480

@@ -83,7 +89,6 @@ $profile = Profile::fromScalars(...[157, 'Tom', null]);
8389

8490
## More information
8591

86-
See [docs/INSTALL.md](docs/INSTALL.md) for installation instructions.
8792
See [docs/USAGE.md](docs/USAGE.md) for usage examples.
8893
See [docs/DEVELOP.md](docs/DEVELOP.md) for development details.
8994

docs/INSTALL.md

Lines changed: 0 additions & 45 deletions
This file was deleted.

docs/USAGE.md

Lines changed: 6 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,24 @@
11
Usage
22
=====
33

4-
This page shows concise examples for all available value objects and how to create your own.
5-
6-
Namespace
7-
---------
8-
9-
All classes live under the base namespace:
10-
11-
```
12-
PhpTypedValues
13-
```
14-
15-
Available types
16-
---------------
17-
18-
Integers (PhpTypedValues\Integer):
19-
20-
- IntegerBasic — any PHP integer
21-
- PositiveInt — positive integer (> 0)
22-
- NonNegativeInt — zero or positive integer (>= 0)
23-
- WeekDayInt — integer in range 1..7
24-
25-
Strings (PhpTypedValues\String):
26-
27-
- StringBasic — any PHP string
28-
- NonEmptyStr — non-empty string
29-
30-
Floats (PhpTypedValues\Float):
31-
32-
- FloatBasic — any PHP float
33-
- NonNegativeFloat — zero or positive float (>= 0)
34-
35-
DateTime (PhpTypedValues\DateTime):
36-
37-
- DateTimeAtom — immutable DateTime in RFC3339 (ATOM) format
38-
- DateTimeTimestamp — immutable DateTime represented as a Unix timestamp (seconds since epoch, UTC)
4+
This page shows concise examples for available value objects and how to create your own.
395

406
Static usage examples
417
---------------------
428

439
```php
44-
use PhpTypedValues\DateTime\DateTimeAtom;use PhpTypedValues\DateTime\Timestamp\TimestampSeconds;use PhpTypedValues\Float\FloatStandard;use PhpTypedValues\Float\FloatNonNegative;use PhpTypedValues\Integer\IntegerStandard;use PhpTypedValues\Integer\IntegerNonNegative;use PhpTypedValues\Integer\IntegerPositive;use PhpTypedValues\Integer\IntegerWeekDay;use PhpTypedValues\String\StringNonEmpty;use PhpTypedValues\String\StringStandard;
45-
46-
// Integers
4710
$any = IntegerStandard::fromInt(-10);
4811
$pos = IntegerPositive::fromInt(1);
4912
$nn = IntegerNonNegative::fromInt(0);
5013
$wd = IntegerWeekDay::fromInt(7); // 1..7
51-
52-
// From string (integers)
5314
$posFromString = IntegerPositive::fromString('123');
5415
$wdFromString = IntegerWeekDay::fromString('5');
55-
56-
// Strings
5716
$greeting = StringStandard::fromString('hello');
5817
$name = StringNonEmpty::fromString('Alice');
59-
60-
// Floats
6118
$price = FloatStandard::fromString('19.99');
6219
$ratio = FloatNonNegative::fromFloat(0.5); // >= 0
63-
64-
// DateTime (RFC 3339 / ATOM)
65-
$dt = DateTimeAtom::fromString('2025-01-02T03:04:05+00:00');
66-
67-
// DateTime (Unix timestamp, seconds)
68-
$unix = TimestampSeconds::fromString('1735787045');
20+
$dt = DateTimeAtom::fromString('2025-01-02T03:04:05+00:00'); // DateTime (RFC 3339 / ATOM)
21+
$unix = TimestampSeconds::fromString('1735787045'); // DateTime (Unix timestamp, seconds)
6922

7023
// Accessing value and string form
7124
$posValue = $pos->value(); // 1 (int)
@@ -81,21 +34,11 @@ Validation errors (static constructors)
8134
Invalid input throws an exception with a helpful message.
8235

8336
```php
84-
use PhpTypedValues\Integer\IntegerPositive;
85-
use PhpTypedValues\Integer\IntegerWeekDay;
86-
use PhpTypedValues\String\StringNonEmpty;
87-
use PhpTypedValues\Float\FloatNonNegative;
88-
use PhpTypedValues\DateTime\DateTimeAtom;
89-
9037
IntegerPositive::fromInt(0); // throws: must be > 0
9138
IntegerPositive::fromString('12.3'); // throws: String has no valid integer
92-
9339
IntegerWeekDay::fromInt(0); // throws: Value must be between 1 and 7
94-
9540
StringNonEmpty::fromString(''); // throws: Value must be a non-empty string
96-
9741
FloatNonNegative::fromString('abc'); // throws: String has no valid float
98-
9942
DateTimeAtom::fromString('not-a-date'); // throws: String has no valid datetime
10043
```
10144

@@ -106,12 +49,6 @@ If you want a domain-specific alias (e.g., UserId) that behaves like PositiveInt
10649

10750
```php
10851
<?php
109-
declare(strict_types=1);
110-
111-
namespace App\Domain;
112-
113-
use PhpTypedValues\Integer\IntegerPositive;
114-
11552
final class UserId extends IntegerPositive {}
11653

11754
// Usage
@@ -130,16 +67,16 @@ declare(strict_types=1);
13067
namespace App\Domain;
13168

13269
use PhpTypedValues\Abstract\Assert\Assert;
133-
use PhpTypedValues\Exception\FloatTypeException;
70+
use PhpTypedValues\Exception\IntegerTypeException;
13471
use PhpTypedValues\Abstract\Integer\IntType;
13572

136-
final class EvenPositiveInt extends IntType
73+
readonly class EvenPositiveInt extends IntType
13774
{
13875
/** @var positive-int */
13976
protected int $value;
14077

14178
/**
142-
* @throws FloatTypeException
79+
* @throws IntegerTypeException
14380
*/
14481
public function __construct(int $value)
14582
{
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpTypedValues\Abstract\Undefined;
6+
7+
use PhpTypedValues\Exception\UndefinedTypeException;
8+
9+
/**
10+
* Special type for "Undefined" \ "Unknown" state.
11+
* Use it to return type hints when you don't know the value.
12+
* Use it instead of NULL (could mean anything) to make your code more readable.
13+
*
14+
* @psalm-immutable
15+
*/
16+
abstract readonly class UndefinedType implements UndefinedTypeInterface
17+
{
18+
public static function create(): self
19+
{
20+
return new static();
21+
}
22+
23+
/**
24+
* @throws UndefinedTypeException
25+
*/
26+
public function toString(): void
27+
{
28+
throw new UndefinedTypeException('Undefined type cannot be converted to string.');
29+
}
30+
31+
/**
32+
* @throws UndefinedTypeException
33+
*/
34+
public function value(): void
35+
{
36+
throw new UndefinedTypeException('Undefined type has no value.');
37+
}
38+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpTypedValues\Abstract\Undefined;
6+
7+
/**
8+
* @psalm-immutable
9+
*/
10+
interface UndefinedTypeInterface
11+
{
12+
public static function create(): self;
13+
14+
public function value(): void;
15+
16+
public function toString(): void;
17+
}

src/Bool/BoolStandard.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ public static function fromString(string $value): static
3232
{
3333
$lowerCaseValue = strtolower($value);
3434

35-
if ($lowerCaseValue === 'true') {
35+
if ($lowerCaseValue === 'true' || $lowerCaseValue === '1') {
3636
$boolValue = true;
37-
} elseif ($lowerCaseValue === 'false') {
37+
} elseif ($lowerCaseValue === 'false' || $lowerCaseValue === '0') {
3838
$boolValue = false;
3939
} else {
40-
throw new BoolTypeException(sprintf('Expected string "true" or "false", got "%s"', $value));
40+
throw new BoolTypeException(sprintf('Expected string "true"\"1" or "false"\"0", got "%s"', $value));
4141
}
4242

4343
return new static($boolValue);
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 UndefinedTypeException extends TypeException
8+
{
9+
}

src/Integer/Alias/TinyInt.php

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

55
namespace PhpTypedValues\Integer\Alias;
66

7-
use PhpTypedValues\Integer\DB\IntTiny;
7+
use PhpTypedValues\Integer\MariaDb\IntTiny;
88

99
/**
1010
* Alias of IntTiny.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace PhpTypedValues\Integer\DB;
5+
namespace PhpTypedValues\Integer\MariaDb;
66

77
use PhpTypedValues\Abstract\Integer\IntType;
88
use PhpTypedValues\Exception\IntegerTypeException;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace PhpTypedValues\String\DB;
5+
namespace PhpTypedValues\String\MariaDb;
66

77
use PhpTypedValues\Abstract\String\StrType;
88
use PhpTypedValues\Exception\StringTypeException;

0 commit comments

Comments
 (0)