I've been working with PHP lately and when deserializing a model I wanted to have a generic way of adding validations for its properties, so I wondered whether there was a way for hooking into an object's properties while getting/setting them, much like the Proxy object in javascript.
I found that magic methods in PHP would do the trick. Magic methods overwrite the object's default functionality when the event that triggers them happens. The most commonly used is the __construct()
method, the constructor for the object.
Using the __get
and __set
magic methods, we can hook into the property when getting & setting it and run our custom logic. Hence, creating a bunch of validators to verify we're not assigning them any forbidden values:
class User {
public $allowedProperties = [ 'firstName', 'email' ];
private $firstName;
private $email;
public function __set($propertyName, $value) {
if (!in_array($propertyName, $this->allowedProperties)) {
throw new Exception('Property: ' . $propertyName . ' not supported');
}
$validator = $propertyName . 'Validator';
$isPropertyValueValid = $this->$validator($value);
if (!$isPropertyValueValid) {
throw new Exception('Value: ' . $value . ' for: ' . $propertyName);
}
$this->$propertyName = $value;
}
public function __get($propertyName) {
if (!in_array($propertyName, $this->allowedProperties)) {
throw new Exception('Property: ' . $propertyName . ' not supported');
}
return $this->$propertyName;
}
private function firstNameValidator($value) {
return is_string($value) && strlen($value) < 25;
}
private function emailValidator($value) {
return filter_var($value, FILTER_VALIDATE_EMAIL);
}
}
$user = new User();
$user->firstName = 'Ahhhh I am longer than 25 characters nooooooo'; // throw Exception
$user->email = 'esteban@gmail.com';
It is important to point out that by using this strategy we'll lose any static analysis we have, so it's a choice to make.
Have fun!
Top comments (7)
What do you think of value objects like First Name or Email that will validate itself, so that you do not need any magic?
e.g.: github.com/voku/value_objects/blob...
They are nice, but they force you to introduce an additional library.
No, they are just plain (immutable) php objects. 😊
For the given example (and probably in general), using getters and setters is a cleaner approach
Can you provide an example ?
Sure, here is my example:
Good point :P