Simple Dic With Object Typehint

Following the raging success of PHP 7.0 additions scalar type hints and return types, there is also place for object typehint and return type. That feature can replace double is_object() validation inside function/method. I decided to write an RFC:Object-typehint.

Why do we need this?

Function and method typehints allow to declare expected sort of scalar type or class/interface but there is no way to declare any object type. Function and method return type allow to declare expected sort of scalar type or class/interface but there is same situation impossible to declare any object type. Those two (typehint and return type) should exists both for consistency, allowing typehint should able to declare same return type.

How does it help in DIC

In all well known IoC pattern and it’s implementations like Dependency Injection Container - The DependencyInjection component allows you to standardize and centralize the way objects are constructed in your application.

There are also many other implementations like Pimple or Zend\DI.

The main functions of simple DIC is setting service definitions and retrieving already prepared services, even complex instances with their internal dependencies.

Let’s have a look on a simple interface.

interface ServiceContainer {
  /**
   * Set service definition
   * @param string $id Service identifier
   * @param object $service Service object or closure
   */
  public function set(string $id, object $service);
  /**
   * Retrieve service object from container
   * @param string $id Service identifier
   * @return
   */
  public function get(string $id) : object;
}

I’ve putted here additional type-hint and return-type to protect any implementation to avoid implementing non-object services.

Why an object type-hint? Because in PHP we can catch here any objects which also are closures. They are simply instances of \Closure class and this kind of magic is made under the hood when we type for eg.:

$outerScopeVariable = true;
$closure = function () use ($outerScopeVariable) {
    // my impl...
};

As we know many DIC implementations use closures for lazy loading/instantiating our services.

With additional return-type we’re sure that PHP will do the magic of result validation - those results are validated against function/method declaration return-type on runtime with returned value.

Simple DIC implementation may look like this.

class Container implements ServiceContainer {
  /** @var object[] */
  private $services = [];
  /**
   * {@inheritdoc}
   */
  public function set(string $id, object $service) {
    $this->services[$id] = $service;
  }
  /**
   * {@inheritdoc}
   */
  public function get(string $id) : object {
    // some assertion
    $service = $this->services[$id];
    if ($service instanceof Closure) {
      return $service();
    }
    return $service;
  }
}

This peace of code gives us partial functionality because any get() method call can return new new instances each time, but that was not purpose of this example. The significant improvement is object type-hint and return-type.

Usage

Simple usage could look like this one.

$container = new Container();
$container->set('std', new stdClass());
$container->set('lazy', function () use ($container) : object {
  return $container->get('std');
});

var_dump(
    $container->get('std'),
    $container->get('lazy')
);

PHP 8.0: Object Initializer Proposal

September 26, 2019
Concept of the Object Initializer Expression as a simplification of object instantiation and properties initialization
class object initializer

Inner Classes in PHP Concept

November 24, 2016
Concept of Inner Classes in PHP - idea and thoughts about the way of implementing them inside PHP
class object

Why Object Types are useful in Hydrators and Extractors

August 29, 2016
Object Type-hint described on examples based on Hydration pattern from authored RFC
dependency-injection hydration object
comments powered by Disqus