Skip to content

Commit

Permalink
Merge pull request #15 from researchgate/lazy-loading
Browse files Browse the repository at this point in the history
Added lazy loading to reduce dependency tree
  • Loading branch information
Darijusch authored Nov 13, 2018
2 parents a579a60 + 6e660fe commit 1e86b3d
Show file tree
Hide file tree
Showing 13 changed files with 493 additions and 96 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.idea
/build/
/vendor/
/composer.phar
Expand Down
6 changes: 5 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@
"psr/log": "~1.0.0"
},
"require-dev": {
"phpunit/phpunit": "~7.0.0"
"phpunit/phpunit": "~7.0.0",
"ocramius/proxy-manager": "^2.1.0"
},
"suggest": {
"ocramius/proxy-manager": "For lazy loading"
},
"autoload": {
"psr-0": {
Expand Down
45 changes: 43 additions & 2 deletions src/rg/injektor/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,36 @@ class Configuration {
*/
private $factoryPath;

/**
* @var bool
*/
private $lazyLoading;

/**
* @var bool
*/
private $lazyServices;

/**
* @var bool
*/
private $lazySingletons;

/**
* @param string $configurationFilePath
* @param string $factoryPath
* @param bool $lazyLoading
* @param bool $lazyServices
* @param bool $lazySingletons
*/
public function __construct($configurationFilePath = null, $factoryPath = '') {
public function __construct($configurationFilePath = null, $factoryPath = '', $lazyLoading = false, $lazyServices = false, $lazySingletons = false) {
if ($configurationFilePath) {
$this->addConfigFile($configurationFilePath);
}
$this->factoryPath = $factoryPath;
$this->lazyLoading = $lazyLoading;
$this->lazyServices = $lazyServices;
$this->lazySingletons = $lazySingletons;
}

/**
Expand Down Expand Up @@ -96,4 +117,24 @@ public function getFactoryPath() {
return $this->factoryPath;
}

}
/**
* @return bool
*/
public function isLazyLoading() {
return $this->lazyLoading;
}

/**
* @return bool
*/
public function isLazyServices() {
return $this->lazyServices;
}

/**
* @return bool
*/
public function isLazySingletons() {
return $this->lazySingletons;
}
}
164 changes: 156 additions & 8 deletions src/rg/injektor/DependencyInjectionContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
namespace rg\injektor;

use Doctrine\Common\Annotations\PhpParser;
use ProxyManager\Configuration as ProxyManagerConfiguration;
use ProxyManager\Factory\LazyLoadingValueHolderFactory;
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
use ProxyManager\Proxy\LazyLoadingInterface;
use Psr\Log\LoggerInterface;
use rg\injektor\annotations\Named;

Expand Down Expand Up @@ -53,6 +57,16 @@ class DependencyInjectionContainer {
*/
private $logger;

/**
* @var bool
*/
private $supportsLazyLoading = false;

/**
* @var LazyLoadingValueHolderFactory|null
*/
private $lazyProxyFactory;

/**
* used for injection loop detection
*
Expand All @@ -71,6 +85,7 @@ public function __construct(Configuration $config = null) {
}

$this->annotationReader = new SimpleAnnotationReader();
$this->supportsLazyLoading = class_exists('ProxyManager\Configuration');
}

/**
Expand Down Expand Up @@ -197,14 +212,24 @@ public function getInstanceOfClass($fullClassName, array $constructorArguments =
$instance = $classReflection->getMethod('getInstance')->invokeArgs(null, $constructorArguments);
} else {
$constructorArguments = $this->getConstructorArguments($classReflection, $classConfig, $constructorArguments);
if ($constructorArguments) {
$instance = $classReflection->newInstanceArgs($constructorArguments);
} else {
$instance = $classReflection->newInstanceArgs(array());
}
}
$instanceConstructor = function () use ($classReflection, $constructorArguments, $isConfiguredAsSingleton, $isConfiguredAsService, $singletonKey, $fullClassName) {
$instance = $classReflection->newInstanceArgs($constructorArguments ? $constructorArguments : []);

$this->log('Created instance [' . spl_object_hash($instance) . '] of class [' . get_class($instance) . ']');
if ($isConfiguredAsSingleton) {
$this->log('Added singleton instance [' . spl_object_hash($instance) . '] of class [' . get_class($instance) . '], Singleton Key: [' . $singletonKey . ']');
$this->instances[$singletonKey] = $instance;
}
if ($isConfiguredAsService) {
$this->log('Added service instance [' . spl_object_hash($instance) . '] of class [' . $fullClassName . ']');
$this->instances[$fullClassName] = $instance;
}

$instance = $this->injectProperties($classReflection, $instance);

return $instance;
};
$instance = $this->createNewInstance($classConfig, $classReflection, $instanceConstructor);
}

if ($isConfiguredAsSingleton) {
$this->log('Added singleton instance [' . spl_object_hash($instance) . '] of class [' . get_class($instance) . '], Singleton Key: [' . $singletonKey . ']');
Expand All @@ -217,13 +242,51 @@ public function getInstanceOfClass($fullClassName, array $constructorArguments =

$instance = $this->injectProperties($classReflection, $instance);

$this->log('Created instance [' . spl_object_hash($instance) . '] of class [' . get_class($instance) . ']');

if ($this->iterationDepth > 0) {
$this->iterationDepth--;
}

return $instance;
}

/**
* @param array $classConfig
* @param \ReflectionClass $classReflection
* @param callable $instanceConstructor
*
* @return object
*/
private function createNewInstance(array $classConfig, \ReflectionClass $classReflection, $instanceConstructor) {
if ($this->supportsLazyLoading && $this->config->isLazyLoading() && $this->isConfiguredAsLazy($classConfig, $classReflection)) {
return $this->wrapInstanceWithLazyProxy($classReflection->getName(), $instanceConstructor);
} else {
return $instanceConstructor();
}
}

/**
* @param string $className
* @param callable $instanceConstructor
*
* @return \ProxyManager\Proxy\VirtualProxyInterface
*/
private function wrapInstanceWithLazyProxy($className, $instanceConstructor) {
$proxyParameters = [

];
return $this->getLazyProxyFactory()->createProxy(
$className,
function (&$wrappedObject, LazyLoadingInterface $proxy) use ($instanceConstructor) {
$proxy->setProxyInitializer(null);
$wrappedObject = $instanceConstructor();
return true;
},
$proxyParameters
);
}

/**
* @param string $fullClassName
* @param array $constructorArguments
Expand Down Expand Up @@ -295,6 +358,10 @@ private function getDefaultArguments($classConfig, $defaultConstructorArguments)
* @throws InjectionException
*/
private function injectProperties($classReflection, $instance) {
if ($instance instanceof \ProxyManager\Proxy\LazyLoadingInterface) {
return $instance; // Not inject into lazy proxies
}

$properties = $this->getInjectableProperties($classReflection);
foreach ($properties as $property) {
$this->injectProperty($property, $instance);
Expand Down Expand Up @@ -419,7 +486,15 @@ public function getClassReflection($fullClassName) {
*/
public function getProvidedConfiguredClass($classConfig, \ReflectionClass $classReflection, $name = null, $additionalArgumentsForProvider = array()) {
if ($namedAnnotation = $this->getProviderClassName($classConfig, $classReflection, $name)) {
return $this->getRealClassInstanceFromProvider($namedAnnotation->getClassName(), $classReflection->name, array_merge($namedAnnotation->getParameters(), $additionalArgumentsForProvider));
$instanceConstructor = function () use ($namedAnnotation, $classReflection, $additionalArgumentsForProvider) {
return $this->getRealClassInstanceFromProvider($namedAnnotation->getClassName(), $classReflection->name, array_merge($namedAnnotation->getParameters(), $additionalArgumentsForProvider));
};

if ($this->supportsLazyLoading && $this->config->isLazyLoading() && $this->isConfiguredAsLazy($classConfig, $classReflection)) {
return $this->wrapInstanceWithLazyProxy($classReflection->name, $instanceConstructor);
} else {
return $instanceConstructor();
}
}

return null;
Expand Down Expand Up @@ -599,6 +674,51 @@ public function isConfiguredAsService(array $classConfig, \ReflectionClass $clas
return strpos($classComment, '@service') !== false;
}

/**
* @param array $classConfig
* @param \ReflectionClass $classReflection
* @return bool
*/
public function isConfiguredAsLazy(array $classConfig, \ReflectionClass $classReflection) {
// Force no lazy loading
if ($this->isConfiguredAsNoLazy($classConfig, $classReflection)) {
return false;
}

// Lazy services
if ($this->config->isLazyServices() && $this->isConfiguredAsService($classConfig, $classReflection)) {
return true;
}

// Lazy singletons
if ($this->config->isLazySingletons() && $this->isConfiguredAsSingleton($classConfig, $classReflection)) {
return true;
}

if (isset($classConfig['lazy'])) {
return (bool) $classConfig['lazy'];
}

$classComment = $classReflection->getDocComment();

return strpos($classComment, '@lazy') !== false;
}

/**
* @param array $classConfig
* @param \ReflectionClass $classReflection
* @return bool
*/
public function isConfiguredAsNoLazy(array $classConfig, \ReflectionClass $classReflection) {
if (isset($classConfig['noLazy'])) {
return (bool) $classConfig['noLazy'];
}

$classComment = $classReflection->getDocComment();

return strpos($classComment, '@noLazy') !== false;
}

/**
* @param object $object
* @param string $methodName
Expand Down Expand Up @@ -805,6 +925,27 @@ private function getImplementingClassBecauseOfName($argumentClass, $classConfig,
return $classConfig['named'][$name];
}

/**
* @return \ProxyManager\Configuration
*/
private function getLazyProxyFactoryConfiguration() {
$config = new ProxyManagerConfiguration();
$config->setGeneratorStrategy(new EvaluatingGeneratorStrategy());
return $config;
}

/**
* @return LazyLoadingValueHolderFactory|null
*/
private function getLazyProxyFactory() {
if ($this->supportsLazyLoading && !$this->lazyProxyFactory) {
$config = $this->getLazyProxyFactoryConfiguration();
$this->lazyProxyFactory = new LazyLoadingValueHolderFactory($config);
}

return $this->lazyProxyFactory;
}

/**
* @param $docComment
* @return bool
Expand All @@ -822,4 +963,11 @@ protected function log($string) {
}
}

/**
* @return bool
*/
public function supportsLazyLoading()
{
return $this->supportsLazyLoading;
}
}
9 changes: 8 additions & 1 deletion src/rg/injektor/FactoryDependencyInjectionContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ public function getProxyClassName($fullClassName) {
return self::$prefix . $this->getStrippedClassName($fullClassName) . 'Proxy';
}

/**
* @param $fullClassName
* @return string
*/
public function getLazyProxyClassName($fullClassName) {
return self::$prefix . $this->getStrippedClassName($fullClassName) . 'Lazy';
}

/**
* @param string $fullClassName
* @return string
Expand Down Expand Up @@ -114,5 +122,4 @@ protected function factoryClassExists($fullFactoryClassName, $factoryClassName)

return false;
}

}
20 changes: 20 additions & 0 deletions src/rg/injektor/generators/CreateInstanceMethod.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php
/*
* This file is part of rg\injektor.
*
* (c) ResearchGate GmbH <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace rg\injektor\generators;

class CreateInstanceMethod extends \Zend\Code\Generator\MethodGenerator {

public function __construct() {
parent::__construct('createInstance', [], self::FLAG_PRIVATE | self::FLAG_STATIC);

$parameter = new \Zend\Code\Generator\ParameterGenerator('parameters', 'array', array());
$this->setParameter($parameter);
}
}
Loading

0 comments on commit 1e86b3d

Please sign in to comment.