Skip to content

Commit 4e51324

Browse files
committed
Add missing tests for the resolving callbacks on the container
+ bug fix related to multiple calls to resolving callbacks
1 parent d329797 commit 4e51324

File tree

2 files changed

+197
-4
lines changed

2 files changed

+197
-4
lines changed

src/Illuminate/Container/Container.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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
*
@@ -234,6 +241,7 @@ public function bind($abstract, $concrete = null, $shared = false)
234241
// bound into this container to the abstract type and we will just wrap it
235242
// up inside its own Closure to give us more convenience when extending.
236243
if (! $concrete instanceof Closure) {
244+
$this->boundToClassName[$abstract] = null;
237245
$concrete = $this->getClosure($abstract, $concrete);
238246
}
239247

@@ -1011,9 +1019,11 @@ protected function fireResolvingCallbacks($abstract, $object)
10111019
{
10121020
$this->fireCallbackArray($object, $this->globalResolvingCallbacks);
10131021

1014-
$this->fireCallbackArray(
1015-
$object, $this->getCallbacksForType($abstract, $object, $this->resolvingCallbacks)
1016-
);
1022+
if (! interface_exists($abstract) || (interface_exists($abstract) && ! array_key_exists($abstract, $this->boundToClassName))) {
1023+
$this->fireCallbackArray(
1024+
$object, $this->getCallbacksForType($abstract, $object, $this->resolvingCallbacks)
1025+
);
1026+
}
10171027

10181028
$this->fireAfterResolvingCallbacks($abstract, $object);
10191029
}
@@ -1137,7 +1147,7 @@ public function forgetExtenders($abstract)
11371147
*/
11381148
protected function dropStaleInstances($abstract)
11391149
{
1140-
unset($this->instances[$abstract], $this->aliases[$abstract]);
1150+
unset($this->instances[$abstract], $this->aliases[$abstract], $this->boundToClassName[$abstract]);
11411151
}
11421152

11431153
/**

tests/Container/ContainerTest.php

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,189 @@ public function testResolvingCallbacksShouldBeFiredWhenCalledWithAliases()
10221022
$this->assertEquals('taylor', $instance->name);
10231023
}
10241024

1025+
public function testResolvingCallbacksAreCalledOnceForImplementation()
1026+
{
1027+
$container = new Container;
1028+
1029+
$callCounter = 0;
1030+
$container->resolving(IContainerContractStub::class, function ($some) use (&$callCounter) {
1031+
$callCounter++;
1032+
});
1033+
1034+
$container->bind(IContainerContractStub::class, ContainerImplementationStub::class);
1035+
1036+
$container->make(ContainerImplementationStub::class);
1037+
$this->assertEquals(1, $callCounter);
1038+
1039+
$container->make(ContainerImplementationStub::class);
1040+
$this->assertEquals(2, $callCounter);
1041+
}
1042+
1043+
public function testResolvingCallbacksAreCalledOnceForImplementation2()
1044+
{
1045+
$container = new Container;
1046+
1047+
$callCounter = 0;
1048+
$container->resolving(IContainerContractStub::class, function ($some) use (&$callCounter) {
1049+
$callCounter++;
1050+
});
1051+
1052+
$container->bind(IContainerContractStub::class, function () {
1053+
return new ContainerImplementationStub;
1054+
});
1055+
1056+
$container->make(IContainerContractStub::class);
1057+
$this->assertEquals(1, $callCounter);
1058+
1059+
$container->make(ContainerImplementationStub::class);
1060+
$this->assertEquals(2, $callCounter);
1061+
1062+
$container->make(ContainerImplementationStub::class);
1063+
$this->assertEquals(3, $callCounter);
1064+
1065+
$container->make(IContainerContractStub::class);
1066+
$this->assertEquals(4, $callCounter);
1067+
}
1068+
1069+
public function testRebindingDoesNotAffectResolvingCallbacks()
1070+
{
1071+
$container = new Container;
1072+
1073+
$callCounter = 0;
1074+
$container->resolving(IContainerContractStub::class, function ($some) use (&$callCounter) {
1075+
$callCounter++;
1076+
});
1077+
1078+
$container->bind(IContainerContractStub::class, ContainerImplementationStub::class);
1079+
$container->bind(IContainerContractStub::class, function () {
1080+
return new ContainerImplementationStub;
1081+
});
1082+
1083+
$container->make(IContainerContractStub::class);
1084+
$this->assertEquals(1, $callCounter);
1085+
1086+
$container->make(ContainerImplementationStub::class);
1087+
$this->assertEquals(2, $callCounter);
1088+
1089+
$container->make(ContainerImplementationStub::class);
1090+
$this->assertEquals(3, $callCounter);
1091+
1092+
$container->make(IContainerContractStub::class);
1093+
$this->assertEquals(4, $callCounter);
1094+
}
1095+
1096+
public function testRebindingDoesNotAffectMultipleResolvingCallbacks()
1097+
{
1098+
$container = new Container;
1099+
1100+
$callCounter = 0;
1101+
1102+
$container->resolving(IContainerContractStub::class, function ($some) use (&$callCounter) {
1103+
$callCounter++;
1104+
});
1105+
1106+
$container->resolving(ContainerImplementationStubTwo::class, function ($some) use (&$callCounter) {
1107+
$callCounter++;
1108+
});
1109+
1110+
$container->bind(IContainerContractStub::class, ContainerImplementationStub::class);
1111+
1112+
// it should call the callback for interface
1113+
$container->make(IContainerContractStub::class);
1114+
$this->assertEquals(1, $callCounter);
1115+
1116+
// it should call the callback for interface
1117+
$container->make(ContainerImplementationStub::class);
1118+
$this->assertEquals(2, $callCounter);
1119+
1120+
// should call the callback for the interface it implements
1121+
// plus the callback for ContainerImplementationStubTwo.
1122+
$container->make(ContainerImplementationStubTwo::class);
1123+
$this->assertEquals(4, $callCounter);
1124+
}
1125+
1126+
public function testResolvingCallbacksAreCalledForInterfaces()
1127+
{
1128+
$container = new Container;
1129+
1130+
$callCounter = 0;
1131+
$container->resolving(IContainerContractStub::class, function ($some) use (&$callCounter) {
1132+
$callCounter++;
1133+
});
1134+
1135+
$container->bind(IContainerContractStub::class, ContainerImplementationStub::class);
1136+
1137+
$container->make(IContainerContractStub::class);
1138+
1139+
$this->assertEquals(1, $callCounter);
1140+
}
1141+
1142+
public function testResolvingCallbacksAreCalledForConcretesWhenAttachedOnInterface()
1143+
{
1144+
$container = new Container;
1145+
1146+
$callCounter = 0;
1147+
$container->resolving(ContainerImplementationStub::class, function ($some) use (&$callCounter) {
1148+
$callCounter++;
1149+
});
1150+
1151+
$container->bind(IContainerContractStub::class, ContainerImplementationStub::class);
1152+
1153+
$container->make(IContainerContractStub::class);
1154+
$this->assertEquals(1, $callCounter);
1155+
1156+
$container->make(ContainerImplementationStub::class);
1157+
$this->assertEquals(2, $callCounter);
1158+
}
1159+
1160+
public function testResolvingCallbacksAreCalledForConcretesWhenAttachedOnConcretes()
1161+
{
1162+
$container = new Container;
1163+
1164+
$callCounter = 0;
1165+
$container->resolving(ContainerImplementationStub::class, function ($some) use (&$callCounter) {
1166+
$callCounter++;
1167+
});
1168+
1169+
$container->bind(IContainerContractStub::class, ContainerImplementationStub::class);
1170+
1171+
$container->make(IContainerContractStub::class);
1172+
$this->assertEquals(1, $callCounter);
1173+
1174+
$container->make(ContainerImplementationStub::class);
1175+
$this->assertEquals(2, $callCounter);
1176+
}
1177+
1178+
public function testResolvingCallbacksAreCalledForConcretesWithNoBinding()
1179+
{
1180+
$container = new Container;
1181+
1182+
$callCounter = 0;
1183+
$container->resolving(ContainerImplementationStub::class, function ($some) use (&$callCounter) {
1184+
$callCounter++;
1185+
});
1186+
1187+
$container->make(ContainerImplementationStub::class);
1188+
$this->assertEquals(1, $callCounter);
1189+
$container->make(ContainerImplementationStub::class);
1190+
$this->assertEquals(2, $callCounter);
1191+
}
1192+
1193+
public function testResolvingCallbacksAreCalledForInterFacesWithNoBinding()
1194+
{
1195+
$container = new Container;
1196+
1197+
$callCounter = 0;
1198+
$container->resolving(IContainerContractStub::class, function ($some) use (&$callCounter) {
1199+
$callCounter++;
1200+
});
1201+
1202+
$container->make(ContainerImplementationStub::class);
1203+
$this->assertEquals(1, $callCounter);
1204+
$container->make(ContainerImplementationStub::class);
1205+
$this->assertEquals(2, $callCounter);
1206+
}
1207+
10251208
public function testMakeWithMethodIsAnAliasForMakeMethod()
10261209
{
10271210
$mock = $this->getMockBuilder(Container::class)

0 commit comments

Comments
 (0)