Long time contributor to open source
Equip, League, Kohana, DOMPDF, etc …
4587 contributions to 270 repositories and counting
It's not just for the British!
An immutable object is an object whose state cannot be modified after it is created.
<?php
final class LoginRequest
{
private $username;
private $password;
public function __construct($username, $password)
{
$this->username = $username;
$this->password = $password;
}
public function __get($key)
{
return $this->$key;
}
}
But isn't using magic methods slow?!
Relatively speaking, yes.
In the real world, don't worry about it.
<?php
final class LoginRequest
{
private $username;
private $password;
public function __construct($username, $password)
{
$this->username = $username;
$this->password = $password;
}
public function __get($key)
{
return $this->$key;
}
}
<?php
$login = new LoginRequest(
$input['username'],
$input['password']
);
if (!$this->authorization->isValid($login)) {
throw AuthorizationException::invalidLogin($login);
}
// Carry on, lovely person!
<?php
function isValid(LoginRequest $login)
{
if (!$login->username || !$login->password) {
return false;
}
$user = $this->users->findBy([
'username' => $login->username,
]);
if (!$user) {
return false;
}
return password_verify($login->password, $user->password);
}
Given this code…
$widget = new Widget([
'price' => 5.00,
'color' => 'yellow',
'quantity' => 3,
]);
$cart->add($widget);
What is the state of $widget
?
Unless the $widget
is immutable we have to make assumptions.
What if the cart did this?
function add($item) {
$this->items[$item->id] = $item;
$item->in_cart = true;
}
A silent modification just happened.
This is relatively safe but what if there was a bug?
A junior developer might come along and do something silly.
A bug could be something trivial…
function add($item) {
$this->items[$item->id] = $item;
$item->quantity = 1;
}
The quantity just became corrupted!
If we want to allow the cart to modify items…
function add($item) {
$item = $item->withInCart();
$this->items[$item->id] = $item;
return $item;
}
use Zend\Diactoros\ServerRequestFactory;
$request = ServerRequestFactory::fromGlobals();
if ($request->getMethod() === 'GET') {
return $this->fetchResource($request);
}
if ($request->getMethod() === 'POST') {
return $this->createResource($request);
}
if ($request->getMethod() === 'PUT') {
return $this->updateResource($request);
}
$query = $request->getUri()->getQuery();
// the query is a string, NOT an array, so we need to parse it
parse_str($query, $params);
use League\Uri\Components\Query;
$query = new Query($request->getUri()->getQuery());
// easier to access things
if ($query->hasKey('q')) {
return $this->searchFor($query->getValue('q'));
}
$body = (string) $request->getBody();
// or read it as a stream
$body = $request->getBody();
while ($chunk = $body->read(1024)) {
$this->writeUpload($chunk);
}
use Zend\Diactoros\Response;
use Zend\Diactoros\Stream;
$body = new Stream(json_encode($output));
$response = new Response;
$response = $response->withHeader('Content-Type', 'application/json');
$response = $response->withBody($body);
A number of different packages in this space.
use ValueObjects\Web\EmailAddress;
$email = new EmailAddress('me@example.com');
echo $email->toNative(); // me@example.com
// throws InvalidNativeArgumentException
$email = new EmailAddress('blah');
use ValueObjects\Person\Age;
$age = new Age(29);
echo $age->toNative(); // 29
// throws InvalidNativeArgumentException
$age = new Age(-1);
use SebastianBergmann\Money\Currency;
use SebastianBergmann\Money\Money;
$currency = new Currency('USD');
$price = new Money(100, $currency);
$discount = new Money(5, $currency);
$discounted = $price->subtract($discount);
var_dump($price === $discounted); // false
echo $discounted->getAmount(); // 90
use DataValues\QuantityValue;
use DataValues\Geo\Values\LatLongValue;
$quantity = QuantityValue::newFromNumber(5);
$coords = new LatLongValue(32.962747, -96.827570); // you are here!
Quality package covering a number of interesting types.
A number of different packages in this space, too.
use Equip\Structure\Dictionary;
$dict = new Dictionary([
'username' => 'shadowhand',
'password' => 'too-secure-for-you',
]);
echo $dict['username']; // shadowhand
$dict = $dict->withValue('username', 'tron');
echo $dict['username']; // tron
// If unsure if something exists?
var_dump($dict->getValue('famous')); // null
use Equip\Structure\Set;
use Equip\Structure\OrderedList;
$set = new Set([1, 5, 100, 20, 1]);
var_dump($set->toArray()); // [1, 5, 100, 20]
$set = $set->withValues([5, 100]);
var_dump($set->hasValue(5)); // true
var_dump($set->hasValue(20)); // false
$list = new OrderedList($set->toArray());
Guzzle 6
league/period
ramsey/uuid
estvoyage/value
DateTimeImmutable
Definitely!
And I hope you think so too!