Skip to content

Instantly share code, notes, and snippets.

@ghabxph
Created June 16, 2021 06:04
Show Gist options
  • Save ghabxph/492b5b33fe4c15c8aab399b791ac4b08 to your computer and use it in GitHub Desktop.
Save ghabxph/492b5b33fe4c15c8aab399b791ac4b08 to your computer and use it in GitHub Desktop.
Class responsible for dynamically providing classes. Inspired from Laravel app()->make() class provider
<?php
/**
* Class responsible for dynamically providing classes.
* - Inspired from Laravel app()->make() class provider
*
* @author Gabriel Lucernas Pascual <me@ghabxph.info>
* @since 2018.07.23
*/
class DynamicDependencyProvider
{
/**
* Singleton instance of this class
*
* @var DynamicDependencyProvider
*/
private static $oInstance;
/**
* Binded classes
*
* @var array
*/
private static $aBoundClasses;
/**
* Array of classes whose dependencies are specified manually
*
* @var array
*/
private static $aClassDependencies;
/**
* Classes that are bound to a singleton instance
* @var array
*/
private static $aSingletons;
/**
* Current class to reflect
*
* @var string
*/
private $sCurrentClass;
/**
* Class's Reflection
*
* @var ReflectionClass
*/
private $oClassReflection;
/**
* Class's dependencies
*
* @var array
*/
private $aDependencies;
/**
* Implements a concrete implementation of a certain class
*
* @param string $sClass
* @param string $sConcrete
*/
public static function bind(string $sClass, string $sConcrete)
{
self::$aBoundClasses[$sClass] = $sConcrete;
}
/**
* Binds class to a singleton
*
* @param string $sClass
* @param $oSingleton
*/
public static function singleton(string $sClass, $oSingleton)
{
self::$aSingletons[$sClass] = $oSingleton;
}
/**
* Sets class dependencies specifically
*
* @param string $sClass
* @param array $aDependencies
*/
public static function setClassDependencies(string $sClass, array $aDependencies)
{
self::$aClassDependencies[$sClass] = $aDependencies;
}
/**
* Returns the singleton instance of this class
*
* @return DynamicDependencyProvider
*/
public static function getInstance()
{
if (self::$oInstance === null) {
self::$oInstance = new DynamicDependencyProvider();
}
return self::$oInstance;
}
/**
* Provides the desired class
*
* @param string $sClass
* @return mixed
* @throws ReflectionException
*/
public static function provide(string $sClass)
{
$oInstance = self::getInstance();
return $oInstance->doProvide($sClass);
}
/**
* Provides the desired class
*
* @param string $sClass
* @return mixed
* @throws ReflectionException
*/
public function doProvide(string $sClass)
{
$this->setCurrentClass($sClass);
if (isset(self::$aSingletons[$sClass]) === true) {
return self::$aSingletons[$sClass];
}
$this->getClassReflection()
->getClassDependencies();
return new $sClass(...$this->aDependencies);
}
/**
* Seeks final class implementation through bound classes, then sets the current final class
*
* @param string $sClass
* @return $this
*/
private function setCurrentClass(string $sClass)
{
$this->sCurrentClass = (isset(self::$aBoundClasses[$sClass]) === false) ? $sClass : self::$aBoundClasses[$sClass];
if ($sClass !== $this->sCurrentClass) {
return $this->setCurrentClass($this->sCurrentClass);
}
return $this;
}
/**
* Provides the dependencies of the specified class
*
* @return DynamicDependencyProvider
* @throws ReflectionException
*/
private function getClassDependencies()
{
$aParameters = $this->oClassReflection->getConstructor()->getParameters();
$this->aDependencies = (isset(self::$aClassDependencies[$this->sCurrentClass]) === true) ? self::$aClassDependencies[$this->sCurrentClass] : [];
foreach ($aParameters as $iKey => $oParameter) {
$this->aDependencies[$iKey] = $this->provideIfParameterNotYetExist($iKey, $oParameter);
}
return $this;
}
/**
* Provide parameter if parameter is not yet set by default
*
* @param int $iKey
* @param ReflectionParameter $oParameter
* @return mixed
* @throws ReflectionException
*/
private function provideIfParameterNotYetExist(int $iKey, ReflectionParameter $oParameter)
{
if (isset(self::$aClassDependencies[$this->sCurrentClass]) && isset(self::$aClassDependencies[$this->sCurrentClass][$iKey])) {
return (new DynamicDependencyProvider())->doProvide(self::$aClassDependencies[$this->sCurrentClass][$iKey]);
}
return ($oParameter->getClass() === null) ? $oParameter->getDefaultValue() : (new DynamicDependencyProvider())->doProvide($oParameter->getClass()->name);
}
/**
* Retrieves current class's reflection
*
* @return DynamicDependencyProvider
* @throws ReflectionException
*/
private function getClassReflection()
{
$this->oClassReflection = new ReflectionClass($this->sCurrentClass);
return $this;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment