Typed value objects for PHP. Build precise, immutable, and validated data for DTOs, Value Objects, and Entities.
- Use V2 for PHP 8.2+:
composer require georgii-web/php-typed-values:^2- Use V1 for PHP 7.4:
composer require georgii-web/php-typed-values:^1- Strong typing for scalars with runtime validation
- Immutable and self‑documenting values
- Safer constructors for your DTOs/VOs/Entities
- Great fit for static analysis (Psalm/PHPStan)
use TypedValues\Integer\IntegerPositive;
$id = IntegerPositive::fromString('123');Instead of spreading validation across an application
$id = (int) '123';
if ($id <= 0) {
throw new InvalidArgumentException('Invalid ID');
}use TypedValues\Integer\IntegerPositive;
readonly class Id extends IntegerPositive {}
Id::fromInt(123);use TypedValues\Integer\IntegerPositive;
use TypedValues\String\StringNonEmpty;
use TypedValues\Float\FloatPositive;
use TypedValues\Undefined\Alias\Undefined; // represents an intentionally missing value
final readonly class Profile
{
public function __construct(
private IntegerPositive $id,
private StringNonEmpty|Undefined $firstName,
private FloatPositive|Undefined $height,
) {}
public static function fromScalars(
int $id,
?string $firstName,
string|float|int|null $height = null,
): self {
return new self(
IntegerPositive::fromInt($id), // early fail (must be valid)
StringNonEmpty::tryFromMixed($firstName), // late fail (maybe undefined)
$height !== null
? FloatPositive::fromString((string) $height) // early fail if provided
: Undefined::create(), // late fail when accessed
);
}
public function getFirstName(): StringNonEmpty|Undefined { return $this->firstName; }
public function getHeight(): FloatPositive|Undefined { return $this->height; }
}Profile::fromScalars(id: 0, firstName: 'Alice', height: '172.5'); // throws exception$profile = Profile::fromScalars(id: 101, firstName: '', height: '172.5'); // created
$profile->getFirstName()->value(); // throws an exception on access the Undefined valueIdeal for partial data handling (e.g., requests where only specific fields, like ID, are required), allowing access to valid fields without failing on missing ones.
Profile::fromScalars(id: 101, firstName: 'Alice', height: -1); // invalid provided value -> early fail
$profile = Profile::fromScalars(id: 101, firstName: 'Alice', height: null); // value omitted -> created, fails only on access
$profile->getHeight()->value(); // throws an exception on access the Undefined value- Static analysis friendly (Psalm/PHPStan-ready types)
- Strict types with
declare(strict_types=1); - Validation on construction; no invalid state
- Immutable, readonly objects
- No external runtime dependencies
- Easy to extend with your own types and composites
- Objects vs scalars:
- ~3× slower for large arrays of objects
- ~2× higher memory usage
- Use value objects for domain boundaries, validation, and clarity
- Use raw scalars in hot loops or large data processing paths
- Development guide:
docs/DEVELOP.md - Usage examples in
src/Usageandtests/Unit
MIT