2

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#

beppe9000
  • 1,056
  • 1
  • 13
  • 28

2 Answers2

3

With the upcoming PHP 8 the nullsafe_operator will be implemented and will allow to circuit break a chain of calls if null is returned by one of them

   $result = $o->returnNull()?->doSomething()
   $results === null // True
Maks3w
  • 6,014
  • 6
  • 37
  • 42
1

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

Patrick Allaert
  • 1,751
  • 18
  • 44