-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
Lazy services - service proxies #7527
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f503ad8
67aef45
f4a19c7
d2760f1
4a13f82
a6a6572
35fdded
bec7774
468e92e
4ecd5ad
11a1da9
5870fed
695e3c5
c5a5af0
29899ec
b5d0298
cda390b
1eb4cf7
b417969
1e24767
450635a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,10 @@ | |
|
||
namespace Symfony\Component\DependencyInjection; | ||
|
||
use ProxyManager\Configuration; | ||
use ProxyManager\Factory\LazyLoadingValueHolderFactory; | ||
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy; | ||
use ProxyManager\Proxy\LazyLoadingInterface; | ||
use Symfony\Component\DependencyInjection\Compiler\Compiler; | ||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | ||
use Symfony\Component\DependencyInjection\Compiler\PassConfig; | ||
|
@@ -222,15 +226,32 @@ public function setResources(array $resources) | |
* @api | ||
*/ | ||
public function addObjectResource($object) | ||
{ | ||
if ($this->trackResources) { | ||
$this->addClassResource(new \ReflectionClass($object)); | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Adds the given class hierarchy as resources. | ||
* | ||
* @param \ReflectionClass $class | ||
* | ||
* @return ContainerBuilder The current instance | ||
* | ||
* @api | ||
*/ | ||
public function addClassResource(\ReflectionClass $class) | ||
{ | ||
if (!$this->trackResources) { | ||
return $this; | ||
} | ||
|
||
$parent = new \ReflectionObject($object); | ||
do { | ||
$this->addResource(new FileResource($parent->getFileName())); | ||
} while ($parent = $parent->getParentClass()); | ||
$this->addResource(new FileResource($class->getFileName())); | ||
} while ($class = $class->getParentClass()); | ||
|
||
return $this; | ||
} | ||
|
@@ -417,8 +438,10 @@ public function has($id) | |
* | ||
* @return object The associated service | ||
* | ||
* @throws InvalidArgumentException if the service is not defined | ||
* @throws LogicException if the service has a circular reference to itself | ||
* @throws InvalidArgumentException when no definitions are available | ||
* @throws InactiveScopeException when the current scope is not active | ||
* @throws LogicException when a circular dependency is detected | ||
* @throws \Exception | ||
* | ||
* @see Reference | ||
* | ||
|
@@ -584,6 +607,12 @@ public function compile() | |
foreach ($this->compiler->getPassConfig()->getPasses() as $pass) { | ||
$this->addObjectResource($pass); | ||
} | ||
|
||
foreach ($this->definitions as $definition) { | ||
if ($definition->isLazy() && ($class = $definition->getClass()) && class_exists($class)) { | ||
$this->addClassResource(new \ReflectionClass($class)); | ||
} | ||
} | ||
} | ||
|
||
$this->compiler->compile($this); | ||
|
@@ -865,20 +894,51 @@ public function findDefinition($id) | |
* | ||
* @param Definition $definition A service definition instance | ||
* @param string $id The service identifier | ||
* @param Boolean $tryProxy Whether to try proxying the service with a lazy proxy | ||
* | ||
* @return object The service described by the service definition | ||
* | ||
* @throws RuntimeException When the scope is inactive | ||
* @throws RuntimeException When the factory definition is incomplete | ||
* @throws RuntimeException When the service is a synthetic service | ||
* @throws InvalidArgumentException When configure callable is not callable | ||
* | ||
* @internal this method is public because of PHP 5.3 limitations, do not use it explicitly in your code | ||
*/ | ||
private function createService(Definition $definition, $id) | ||
public function createService(Definition $definition, $id, $tryProxy = true) | ||
{ | ||
if ($definition->isSynthetic()) { | ||
throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.', $id)); | ||
} | ||
|
||
if ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we move this whole block of code into a new service? This adds a hidden dependency to ZendFramework/ThirdParty code inside the ContainerBuilder that is not replaceable. This violates both Single Responsibilty and Dependency Inversion Principle here, not keeping the code against abstract concepts. I say introduce a |
||
$tryProxy | ||
&& ($className = $definition->getClass()) | ||
&& $definition->isLazy() | ||
&& class_exists('ProxyManager\\Factory\\LazyLoadingValueHolderFactory') | ||
) { | ||
$config = new Configuration(); | ||
|
||
$config->setGeneratorStrategy(new EvaluatingGeneratorStrategy()); | ||
|
||
$factory = new LazyLoadingValueHolderFactory($config); | ||
$container = $this; | ||
$proxy = $factory->createProxy( | ||
$className, | ||
function (& $wrappedInstance, LazyLoadingInterface $proxy) use ($container, $definition, $id) { | ||
$proxy->setProxyInitializer(null); | ||
|
||
$wrappedInstance = $container->createService($definition, $id, false); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would the call to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, this has also a test for it, see $this->assertSame($foo1, $builder->get('foo1'), 'The same proxy is retrieved after initialization'); |
||
|
||
return true; | ||
} | ||
); | ||
|
||
$this->shareService($definition, $proxy, $id); | ||
|
||
return $proxy; | ||
} | ||
|
||
$parameterBag = $this->getParameterBag(); | ||
|
||
if (null !== $definition->getFile()) { | ||
|
@@ -903,16 +963,9 @@ private function createService(Definition $definition, $id) | |
$service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments); | ||
} | ||
|
||
if (self::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) { | ||
if (self::SCOPE_CONTAINER !== $scope && !isset($this->scopedServices[$scope])) { | ||
throw new InactiveScopeException($id, $scope); | ||
} | ||
|
||
$this->services[$lowerId = strtolower($id)] = $service; | ||
|
||
if (self::SCOPE_CONTAINER !== $scope) { | ||
$this->scopedServices[$scope][$lowerId] = $service; | ||
} | ||
if ($tryProxy || !$definition->isLazy()) { | ||
// share only if proxying failed, or if not a proxy | ||
$this->shareService($definition, $service, $id); | ||
} | ||
|
||
foreach ($definition->getMethodCalls() as $call) { | ||
|
@@ -1057,4 +1110,28 @@ private function callMethod($service, $call) | |
|
||
call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->resolveValue($call[1]))); | ||
} | ||
|
||
/** | ||
* Shares a given service in the container | ||
* | ||
* @param Definition $definition | ||
* @param mixed $service | ||
* @param string $id | ||
* | ||
* @throws InactiveScopeException | ||
*/ | ||
private function shareService(Definition $definition, $service, $id) | ||
{ | ||
if (self::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) { | ||
if (self::SCOPE_CONTAINER !== $scope && !isset($this->scopedServices[$scope])) { | ||
throw new InactiveScopeException($id, $scope); | ||
} | ||
|
||
$this->services[$lowerId = strtolower($id)] = $service; | ||
|
||
if (self::SCOPE_CONTAINER !== $scope) { | ||
$this->scopedServices[$scope][$lowerId] = $service; | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not passing a class name and building the ReflectionClass internally ?
addObjectResource
expects an object, not a ReflectionObjectThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strict typing. This disallows unexisting classes upfront