DEV Community

Esteban
Esteban

Posted on

2

Validating data in PHP objects using magic methods

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';
Enter fullscreen mode Exit fullscreen mode

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!

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (7)

Collapse
 
suckup_de profile image
Lars Moelleken

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...

Collapse
 
fr0gs profile image
Esteban

They are nice, but they force you to introduce an additional library.

Collapse
 
suckup_de profile image
Lars Moelleken

No, they are just plain (immutable) php objects. 😊

Collapse
 
klabauterjan profile image
JanWennrich

For the given example (and probably in general), using getters and setters is a cleaner approach

Collapse
 
fr0gs profile image
Esteban

Can you provide an example ?

Collapse
 
klabauterjan profile image
JanWennrich • Edited

Sure, here is my example:

<?php

class User {
   private string $firstName;
   private string $email;

   public function setFirstName(string $firstName): void
   {
        if (strlen($firstName) > 25) {
            throw new Exception('Firstname is longer than 25 characters');
        }

        $this->firstName = $firstName;
   }

   public function setEmail(string $email): void
   {
        $this->email = filter_var($email, FILTER_VALIDATE_EMAIL);
   }

   public function getFirstName(): string
   {
        return $this->firstName;
   }

   public function getEmail(): string
   {
        return $this->email;
   }
}


$user = new User();
$user->setFirstName('Ahhhh I am longer than 25 characters nooooooo'); // throws Exception
$user->setEmail('esteban@gmail.com');
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
fr0gs profile image
Esteban

Good point :P

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay