skip to Main Content

PHP 8 has introduced an amazing code fallback tool,
Null Safe Operator, eg:

$country = $session?->user?->getAddress()?->country;

It prevents you create a lot of comparisons of all whole object tree, Null Coalesce Operator not plays well here (PHP 7.x or Earlier) beacuse above code has an method which will throw an exception because their main class is null. Here, Null Safe Operator prevents an exception.

Well, there are some hack method to emulate this behaviour into earlier versions of PHP (<= 7.X)?

Fallback to some generic class with magic methods where ever returns null can be handful.

2

Answers


  1. Chosen as BEST ANSWER

    We can create a black holed class to instead of throw an exception return Null if we call an undefined method of a generic class which will acts as our fallback.

        <?php
        // _7 - for brevity write and 7 is closer to question mark ;)
        class _7  {
            public function __call($method, $args) return null;
        }
    
    
    $myDate = (DateTime::createFromFormat('d/m/Y','05/04/1989') ?: new _7)->format('Y-m-d') ?? 'Bad date';
    //'1989-04-05' - because expected date format is provided to Datetime 
    
    $myDate = (DateTime::createFromFormat('d/m/Y','1989') ?: new _7)->format('Y-m-d') ?? 'Bad date';
    //'Bad date' - Very cool! No exceptions here, successfull fallback;
    

    Important! This approach only works with PHP >= 7.0, i will collect info to work with 5.x soon as possible.


  2. To emulate null safe operator, you can take inspiration from the option type. The idea is simple – you wrap the value in an object, as you suggested, and have a magic method handling. Now, the magic method will either return $this – e.g. the same Option instance, if this is already a null, or call the method and wrap the result in an Option, to allow further chaining.

    The challenge you face with PHP will be where to terminate, e.g. where to return the original value, and not the wrapper. If you can afford an explicit method call at the end of the chain, it becomes straightforward.

    It would look something like (not tested, written for illustrative purposes)

    class Option {
        protected $value;
    
        public function __construct($value)
        {
            $this->value = $value;
        }
    
        public function __call($methodName, $args) {
            if (is_null($this->value)) {
                return $this;
            }
            return new Option($this->value->$methodName($args));
        }
    
        public function __get($propertyName) {
            if (is_null($this->value)) {
                return $this;
            }
            return new Option($this->value->$propertyName);
        }
    
        public function get() {
            return $this->value;
        }
    }
    

    So you will do:

    $country = new Option($session)->user->getAddress()->country->get();
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search