Created
June 16, 2021 06:04
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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