Dependency Container I'm currently creating my own microframework for learning purpose and I want to make it more powerful by providing a way to register classes and instantiate them (once) only when needed. So here is my try :

&lt;?php


namespace Framework\Core;

use Exception;
use ReflectionClass;

/**
 * Class Container
 *
 * @package App\Core
 *
 * Depandancies injection and lazy loading, I guess ? (not sure how to call it for now)
 *
 */
class Container
{

    /**
     * @var array Contains all classes instance.
     */
    private static $instances = array();

    /**
     * @var array Contains all fully qualified classes name.
     */
    private static $classes = array();

    /**
     * @var string Should contain the name of the last registered class name.
     */
    private static $lastRegistered;

    /**
     * Container constructor.
     */
    private function __construct() {}

    /**
     * Prevents cloning.
     */
    private function __clone() {}

    /**
     * Prevents deserialization.
     */
    private function __wakeup() {}

    /**
     * Gets a class instance.
     *
     * @param $name
     * @param null $renew
     * @return mixed
     * @throws Exception
     */
    public static function get($name = NULL) {
        $name = (!$name) ? self::$lastRegistered : $name;
        if (self::$classes[$name]) {
            if (self::$instances[$name]) {
                return self::$instances[$name];
            } else {
                self::$instances[$name] = new self::$classes[$name];
                return self::$instances[$name];
            }
        } else {
            throw new Exception('The following class name could not be found: ' . $name);
        }
    }

    /**
     * Registers a class.
     *
     * @param $name
     * @param $class
     * @return $this|null
     * @throws Exception
     */
    public static function register($name, $class) {
        $reflection = new ReflectionClass($class);
        if (!$reflection-&gt;isInstantiable()) {
            throw new Exception('The provided class is not instantiable: ' . $class);
        }
        if (!in_array($name, self::$classes)) {
            self::$classes[$name] = $class;
        }
        self::$lastRegistered = $name;
        return __CLASS__;
    }

    /**
     * Returns an array of all registered classes.
     *
     * @return array
     */
    public static function getRegisteredClasses() {
        return self::$classes;
    }

    /**
     * Returns an array of all instantiated classes.
     *
     * @return array
     */
    public static function getInstantiatedClasses() {
        return self::$instances;
    }

}


So basically, what it does it that it allows you to register a class name as follow :

Container::register('config', \Framework\Core\Config::class);


And then you can get the class instance like this :

Container::get('config');


You can also register() and immediately get() the class instance like this :

Container::register('config', \Framework\Core\Config::class)::get();


I added this "feature" into my microframework in order to prevent instantiating a class more than one time.

The interesting part is that it instantiates the class only when you call the get() method, not when you register it. It allows me to register all framework dependancies at once when the app starts and then get their instances later.

Obvisouly, this class needs improvement in terms of "options". Because, what if I need to register() and get() a class that needs arguments to be passed into its constructor ? I'm aware about that and before making this class more "flexible" I just wanted to know if it's useful to have a "feature" like this in my microframework.



My question is :
How is this called ? Singleton ? Container ? Dependency injection ?
Also, can my microframework benefits from having this "feature" ?
Before addressing the main question, I would like to compare two of the primary methods: register and get. The register method is written well in terms of returning early.

  public static function register($name, $class) {
        $reflection = new ReflectionClass($class);
        if (!$reflection-&gt;isInstantiable()) {
            throw new Exception('The provided class is not instantiable: ' . $class);
        }
        if (!in_array($name, self::$classes)) {
            self::$classes[$name] = $class;
        }
        self::$lastRegistered = $name;
        return __CLASS__;
    }


With this method the indentation is kept to a single level. Because of this it is simple to read.
The get method could be re-worked to decrease indentation levels.

  public static function get($name = NULL)   {
          $name = (!$name) ? self::$lastRegistered : $name;
          if (self::$classes[$name]) {
              if (self::$instances[$name]) {
                  return self::$instances[$name];
              } else {
                  self::$instances[$name] = new self::$classes[$name];
                  return self::$instances[$name];
              }
          } else {
              throw new Exception('The following class name could not be found: ' . $name);
          }
      }


The ternary operator with null check can be simplified using a null coalescing operator:
 $name = $name ?? self::$lastRegistered;

And with PHP 7.4+ the null coalescing assignment operator is available:
  $name ??= self::$lastRegistered;

By returning early there is need for else - this can decrease indentation levels, which can result in ones that aren’t as long, as well as make the method shorter over-all:
public static function get($name = NULL) {
    $name = $name ?? self::$lastRegistered;
    if (!self::$classes[$name]) {
        throw new Exception('The following class name could not be found: ' . $name);
    }
    if (self::$instances[$name]) {
        return self::$instances[$name];
    }
    self::$instances[$name] = new self::$classes[$name];
    return self::$instances[$name];
}

In addition to requiring fewer lines of code, there is also no need for the else keyword.

How is this called ? Singleton ? Container ? Dependency injection ?

This appears to be a variation of the Singleton pattern. Typically a singleton class would have a private constructor and a static method like getInstance() that would return a static property after conditionally assigning that property to a newly instantiated object if it hadn’t been set.