diff --git a/src/Illuminate/Container/Container.php b/src/Illuminate/Container/Container.php index 98d8f0ae7900..9b273f367c7b 100755 --- a/src/Illuminate/Container/Container.php +++ b/src/Illuminate/Container/Container.php @@ -105,6 +105,13 @@ class Container implements ArrayAccess, ContainerContract */ protected $reboundCallbacks = []; + /** + * All of the global before resolving callbacks. + * + * @var \Closure[] + */ + protected $globalBeforeResolvingCallbacks = []; + /** * All of the global resolving callbacks. * @@ -119,6 +126,13 @@ class Container implements ArrayAccess, ContainerContract */ protected $globalAfterResolvingCallbacks = []; + /** + * All of the before resolving callbacks by class type. + * + * @var array[] + */ + protected $beforeResolvingCallbacks = []; + /** * All of the resolving callbacks by class type. * @@ -667,6 +681,10 @@ protected function resolve($abstract, $parameters = [], $raiseEvents = true) { $abstract = $this->getAlias($abstract); + if ($raiseEvents) { + $this->fireBeforeResolvingCallbacks($abstract, $parameters); + } + $concrete = $this->getContextualConcrete($abstract); $needsContextualBuild = ! empty($parameters) || ! is_null($concrete); @@ -1032,6 +1050,26 @@ protected function unresolvablePrimitive(ReflectionParameter $parameter) throw new BindingResolutionException($message); } + /** + * Register a new before resolving callback for all types. + * + * @param \Closure|string $abstract + * @param \Closure|null $callback + * @return void + */ + public function beforeResolving($abstract, Closure $callback = null) + { + if (is_string($abstract)) { + $abstract = $this->getAlias($abstract); + } + + if ($abstract instanceof Closure && is_null($callback)) { + $this->globalBeforeResolvingCallbacks[] = $abstract; + } else { + $this->beforeResolvingCallbacks[$abstract][] = $callback; + } + } + /** * Register a new resolving callback. * @@ -1072,6 +1110,39 @@ public function afterResolving($abstract, Closure $callback = null) } } + /** + * Fire all of the before resolving callbacks. + * + * @param string $abstract + * @param array $parameters + * @return void + */ + protected function fireBeforeResolvingCallbacks($abstract, $parameters = []) + { + $this->fireBeforeCallbackArray($abstract, $parameters, $this->globalBeforeResolvingCallbacks); + + foreach ($this->beforeResolvingCallbacks as $type => $callbacks) { + if ($type === $abstract || is_subclass_of($abstract, $type)) { + $this->fireBeforeCallbackArray($abstract, $parameters, $callbacks); + } + } + } + + /** + * Fire an array of callbacks with an object. + * + * @param string $abstract + * @param array $parameters + * @param array $callbacks + * @return void + */ + protected function fireBeforeCallbackArray($abstract, $parameters, array $callbacks) + { + foreach ($callbacks as $callback) { + $callback($abstract, $parameters, $this); + } + } + /** * Fire all of the resolving callbacks. * diff --git a/tests/Container/ResolvingCallbackTest.php b/tests/Container/ResolvingCallbackTest.php index c38eec52fa76..e8aeecb73a68 100644 --- a/tests/Container/ResolvingCallbackTest.php +++ b/tests/Container/ResolvingCallbackTest.php @@ -441,6 +441,45 @@ public function testAfterResolvingCallbacksAreCalledOnceForImplementation() $container->make(ResolvingContractStub::class); $this->assertEquals(2, $callCounter); } + + public function testBeforeResolvingCallbacksAreCalled() + { + // Given a call counter initialized to zero. + $container = new Container; + $callCounter = 0; + + // And a contract/implementation stub binding. + $container->bind(ResolvingContractStub::class, ResolvingImplementationStub::class); + + // When we add a before resolving callback that increment the counter by one. + $container->beforeResolving(ResolvingContractStub::class, function () use (&$callCounter) { + $callCounter++; + }); + + // Then resolving the implementation stub increases the counter by one. + $container->make(ResolvingImplementationStub::class); + $this->assertEquals(1, $callCounter); + + // And resolving the contract stub increases the counter by one. + $container->make(ResolvingContractStub::class); + $this->assertEquals(2, $callCounter); + } + + public function testGlobalBeforeResolvingCallbacksAreCalled() + { + // Given a call counter initialized to zero. + $container = new Container; + $callCounter = 0; + + // When we add a global before resolving callback that increment that counter by one. + $container->beforeResolving(function () use (&$callCounter) { + $callCounter++; + }); + + // Then resolving anything increases the counter by one. + $container->make(ResolvingImplementationStub::class); + $this->assertEquals(1, $callCounter); + } } interface ResolvingContractStub