phpphp-7.3

Does php have a way to stop the call chain if a null value is encoutered?


I would like to do self::container()->get($path); but self::container() can return null.

Is there a quicker way to avoid the Call to a member function get() on null error when chaining function calls and some could return null instead of an object ?

Is there a better way than the ugly workaround of mocking the expected object/member?

public static function getDependency($path) {
 return self::container() ??
  (new class{public function get($path){return null;}})->get($path);
}

What I am after is something like the null-conditional member access operator (?.) in C#


Solution

  • As of PHP 8.0, you have Nullsafe methods and properties and you can do:

    self::container()?->get($path);

    Otherwise, you have the original answer below as it targeted PHP 7.3:

    Short answer: no, there is no such thing in PHP 7.3.

    I would avoid doing magic like the one you suggested.

    Doing:

    <?php
    public static function getDependency($path) {
        $container = self::container();
        if ($container instanceof ContainerInterface) {
            return $container->get($path);
        }
    }
    

    would be easier to read/understand.

    Now, regarding null, it has been described by its own creator (Tony Hoare) "The Billion Dollar Mistake".

    A better approach would be that self::container() would have as return type ContainerInterface without the possibility of being null. Trying to returning a null, it would throw a TypeError, which could potentially be caught. This way, the call to ->get() would never happen as the exception would be thrown before.

    Allowing self::container() to return something like ContainerInterface|null would result in all callers to implement a logic as the one you proposed which would also lead to (lot of) duplicated code.

    For the very same reason, you would probably be safer to have a specific return type for your dependency:

    <?php
    public static function getServiceFoo($path): ServicFoo {
        $container = self::container();
        if (!$container instanceof ContainerInterface) {
            throw new RuntimeException("Not a valid container received");
        }
    
        return $container->get($path);
    }
    

    Otherwise you will be exposed to the same issue on getServiceFoo() that you already have on self::container().