PHP Values is a tool for creating immutable value objects in PHP. A value object intakes a raw value, transforms it, validates it, and can be used consistently and dependably across your application.
For instance, suppose you need an email address when creating a user. You can write it more traditionally like this:
public function createUser(string $email)
{
// perform sanitation and validation on email before using
}
But it's more optimal to write it like this:
use RyanWhitman\PhpValues\Email;
public function createUser(Email $email)
{
// email has already been sanitized and validated and is ready for use
}
You should install the package via composer:
composer require ryanwhitman/php-values
Start by creating a Value class. For instance, a Value class for an email address:
<?php
namespace App\Values;
use RyanWhitman\PhpValues\Value;
use RyanWhitman\PhpValues\Concerns\Stringable;
class Email extends Value
{
use Stringable;
protected function transform(string $email): string
{
return filter_var($email, FILTER_SANITIZE_EMAIL);
}
protected function validate(string $email): bool
{
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
}
Now, you're ready to use the value:
<?php
use App\Values\Email;
// Valid email address
Email::from('[email protected]'); // instance of Email
Email::from('[email protected]')->get(); // [email protected]
Email::getFrom('[email protected]'); // [email protected]
(string) Email::from('[email protected]'); // [email protected]
Email::isValid('[email protected]'); // true
// Valid email address (with imperfections)
Email::getFrom(' email @example.com '); // [email protected]
Email::isValid(' email @example.com '); // true
// Invalid email address
Email::from('non-email'); // throws exception
Email::tryFrom('non-email'); // null
Email::isValid('non-email'); // false
To create a new Value class, extend the RyanWhitman\PhpValues\Value
class. From there, define a transform
method (optional) and a validate
method (mandatory). Upon instantiation, the transform
method receives the raw input and transforms it, as needed. Then, the validate
method receives the transformed value and returns true
or false
. If validation passes, the object is ready for use. If validation passes, InvalidValueException
is thrown. Note: 2 try
static methods exist that catch the exception and return null
.
The transform
method is an optional method called during instantiation. It receives the input value and, when defined, should return a sanitized/transformed version of the value. The transform method is not defined in the base abstract Value class to allow for proper typing in sub-classes.
protected function transform(string $email): string
{
return filter_var($email, FILTER_SANITIZE_EMAIL);
}
The validate
method is called during instantiation. It receives the transformed value and should return true or false. The validate method is not defined in the base abstract Value class to allow for proper typing in sub-classes.
protected function validate(string $email): bool
{
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
Suppose you're creating a Value class for a person's name. You'll likely want to remove all superfluous whitespace. You could, of course, simply call another Value class within your transform
method, but you can also define a $baseValues
property to automatically run other Value classes:
<?php
namespace App\Values;
use RyanWhitman\PhpValues\SquishedString;
use RyanWhitman\PhpValues\Value;
class Name extends Value
{
protected array $baseValues = [
SquishedString::class,
];
// ...
}
The from
static method will return a Value instance when validation passes and will throw an exception when validation fails.
Email::from('[email protected]'); // instance of Email
Email::from('non-email'); // throws InvalidValueException
The getFrom
static method is a shortcut for ::from($value)->get()
.
Email::getFrom('[email protected]'); // [email protected]
Email::getFrom('non-email'); // throws InvalidValueException
The tryFrom
static method will return a Value instance when validation passes and null
when validation fails.
Email::tryFrom('[email protected]'); // instance of Email
Email::tryFrom('non-email'); // null
The tryGetFrom
static method is a shortcut for ::tryFrom($value)->get()
.
Email::tryGetFrom('[email protected]'); // [email protected]
Email::tryGetFrom('non-email'); // null
The isValid
static method will return true or false.
Email::isValid('[email protected]'); // true
Email::isValid('non-email'); // false
The getOrigValue
method returns the original input value (before transformation).
Email::from('e m [email protected]')->getOrigValue(); // e m [email protected]
The get
method returns the transformed and validated value.
Email::from('e m [email protected]')->get(); // [email protected]
As mentioned above, the getFrom
and tryGetFrom
static methods are shortcuts for ::from($value)->get()
and ::tryFrom($value)->get()
, respectively. You may add the ShortcutMethod
annotation/attribute to your custom get methods to add the same shortcut capabilities. Shortcut methods must be defined using camelCase and start with get
(e.g. getFormatted
).
Using a doctrine annotation in PHP 7.4+:
use RyanWhitman\PhpValues\Annotations\ShortcutMethod;
/**
* @ShortcutMethod
*/
public function getFormatted()
{
// ...
}
Using an attribute in PHP 8.0+:
use RyanWhitman\PhpValues\Attributes\ShortcutMethod;
#[ShortcutMethod]
public function getFormatted()
{
// ...
}
After adding the ShortcutMethod
annotation/attribute to the getFormatted
method, for example, the following will work:
::getFormattedFrom($value)
::tryGetFormattedFrom($value)
The Stringable
trait simply defines the __toString()
magic method with (string) $this->get()
.
PHP Values will throw 1 of 2 exceptions:
RyanWhitman\PhpValues\Exceptions\InvalidValueException
will be thrown when either a TypeError
occurs (e.g. an array is needed but a string is provided) or when validation fails. This exception is useful as it indicates the raw input is invalid. RyanWhitman\PhpValues\Exceptions\Exception
is thrown when something else goes wrong (e.g. a validate
method is not defined). Note: The try
methods only catch InvalidValueException
.
composer test
Please see CONTRIBUTING for details.
If you discover any security-related issues, please email [email protected] instead of using the issue tracker.
The MIT License (MIT). Please have a look at License File for more information.