flow-php/types
Flow PHP type system library with typed value objects and type definitions for consistent, safe data handling across the Flow ecosystem. Designed for ETL pipelines, it helps enforce data contracts and reduce runtime type errors.
Installation
composer require flow-php/types
Ensure your project uses PHP 8.3+, 8.4+, or 8.5+.
First Use Case: Strongly Typed Collections
Replace loose arrays with flow-php/types' Collection or Map:
use Flow\Types\Collection;
use Flow\Types\Map;
$users = new Collection([
new User('Alice', 30),
new User('Bob', 25),
]);
Key Entry Points
src/Types/ (core classes like Collection, Map, Result, Option)src/ValueObjects/ (immutable value objects like Email, Uuid, Money)ETL Pipelines
Use Result for error handling:
use Flow\Types\Result;
function validateEmail(string $email): Result<Email> {
return Email::tryFrom($email)
->mapErr(fn($e) => new ValidationError($e->getMessage()));
}
map(), mapErr(), and flatMap() for functional error handling.Immutable Data
Replace mutable objects with value objects (e.g., Money, Email):
$amount = new Money(100, 'USD');
$amount->add(new Money(50, 'USD')); // Returns new instance, original unchanged.
->withX() methods for "copy-with" updates.Type-Safe Config
Define configs as Map:
$config = new Map([
'database' => new Map(['host' => 'localhost', 'port' => 5432]),
'cache' => new Map(['driver' => 'redis']),
]);
->get('database.host') with runtime type checks.Collection/Map into controllers/services:
public function __construct(
private Collection $users,
private Map $settings
) {}
Option for nullable fields:
$user = new User(
name: 'Alice',
email: Option::some(new Email('alice@example.com')),
);
Result for API errors:
return response()->json($result->match(
fn($data) => ['success' => true, 'data' => $data],
fn($error) => ['success' => false, 'error' => $error->getMessage()]
));
Performance Overhead
Email, Money) are immutable and may create copies on modification.->equals() for comparisons.Static Analysis Conflicts
Collection/Map as "non-traversable."ArrayAccess or use @method PHPDoc annotations:
/** @method mixed offsetGet(string $offset) */
Serialization Quirks
Collection/Map rely on __serialize()/__unserialize().JsonSerializable for JSON APIs:
$data = json_encode($collection->toArray());
instanceof checks or is_a() for runtime validation:
if ($value instanceof Email) { ... }
.phpstan.neon:
includes:
- vendor/flow-php/types/extension.neon
AbstractValueObject:
class Domain extends AbstractValueObject {
public function __construct(private string $value) {}
public function getValue(): string { return $this->value; }
}
trait FilterableCollection {
public function filterByAge(int $age): static {
return $this->filter(fn($user) => $user->getAge() >= $age);
}
}
match() for domain-specific logic:
$result->match(
fn($data) => logger()->info("Success: {$data}"),
fn($error) => logger()->error("Failed: {$error}")
);
Map::withDefaults() for optional configs:
$defaults = new Map(['timeout' => 30]);
$config = new Map(['timeout' => 60], $defaults);
$email = Email::tryFrom(env('USER_EMAIL'))
->unwrapOrThrow(new InvalidArgumentException('Email missing!'));
How can I help you explore Laravel packages today?