@@ -133,6 +133,13 @@ class Container implements ArrayAccess, ContainerContract
133133 */
134134 protected $ afterResolvingCallbacks = [];
135135
136+ /**
137+ * All the abstract keys which are bound with class names.
138+ *
139+ * @var array
140+ */
141+ protected $ boundToClassName = [];
142+
136143 /**
137144 * Define a contextual binding.
138145 *
@@ -222,6 +229,7 @@ public function isAlias($name)
222229 public function bind ($ abstract , $ concrete = null , $ shared = false )
223230 {
224231 $ this ->dropStaleInstances ($ abstract );
232+ $ this ->isConcreteClassName ($ abstract , $ concrete );
225233
226234 // If no concrete type was given, we will simply set the concrete type to the
227235 // abstract type. After that, the concrete type to be registered as shared
@@ -674,7 +682,12 @@ protected function resolve($abstract, $parameters = [])
674682 $ this ->instances [$ abstract ] = $ object ;
675683 }
676684
677- $ this ->fireResolvingCallbacks ($ abstract , $ object );
685+ // To prevent extra call to resolving callbacks, we don't fire "resolving" callbacks
686+ // for interfaces bound with class path syntax, since the callback will be called
687+ // later, in a recursive call to make() when the bounded concrete is resolving.
688+ if (! $ this ->isAnInterfaceBoundedWithClassName ($ abstract )) {
689+ $ this ->fireResolvingCallbacks ($ abstract , $ object );
690+ }
678691
679692 // Before returning, we will also set the resolved flag to "true" and pop off
680693 // the parameter overrides for this build. After those two things are done
@@ -1269,4 +1282,32 @@ public function __set($key, $value)
12691282 {
12701283 $ this [$ key ] = $ value ;
12711284 }
1285+
1286+ /**
1287+ * Determine if the abstract is both an interface and is bounded to a class path (not a closure).
1288+ *
1289+ * @param string $abstract
1290+ * @return bool
1291+ */
1292+ protected function isAnInterfaceBoundedWithClassName ($ abstract )
1293+ {
1294+ return interface_exists ($ abstract ) && array_key_exists ($ abstract , $ this ->boundToClassName );
1295+ }
1296+
1297+ /**
1298+ * Detects whether the concrete param is a closure or a class path.
1299+ *
1300+ * @param string $abstract
1301+ * @param \Closure|string|null $concrete
1302+ * @return void
1303+ */
1304+ protected function isConcreteClassName ($ abstract , $ concrete )
1305+ {
1306+ if (! ($ concrete instanceof Closure)) {
1307+ $ this ->boundToClassName [$ abstract ] = null ;
1308+ } else {
1309+ // Needed when rebinding happens.
1310+ unset($ this ->boundToClassName [$ abstract ]);
1311+ }
1312+ }
12721313}
0 commit comments