Skip to content

Commit f53313a

Browse files
committed
Add missing tests for the resolving callbacks on the container
+ bug fix related to multiple calls to "resolving" and "afterResolving" callbacks
1 parent d329797 commit f53313a

File tree

2 files changed

+368
-3
lines changed

2 files changed

+368
-3
lines changed

src/Illuminate/Container/Container.php

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,8 @@ protected function getClosure($abstract, $concrete)
261261
return $container->build($concrete);
262262
}
263263

264-
return $container->make($concrete, $parameters);
264+
// To prevent extra call to resolving callbacks, we make the object silently.
265+
return $container->resolveSilent($concrete, $parameters);
265266
};
266267
}
267268

@@ -633,6 +634,19 @@ public function get($id)
633634
* @return mixed
634635
*/
635636
protected function resolve($abstract, $parameters = [])
637+
{
638+
$this->resolveSilent($abstract, $parameters, false);
639+
}
640+
641+
/**
642+
* Resolve the given type from the container.
643+
*
644+
* @param string $abstract
645+
* @param array $parameters
646+
* @param bool $silent
647+
* @return mixed
648+
*/
649+
private function resolveSilent($abstract, $parameters = [], $silent = false)
636650
{
637651
$abstract = $this->getAlias($abstract);
638652

@@ -674,8 +688,9 @@ protected function resolve($abstract, $parameters = [])
674688
$this->instances[$abstract] = $object;
675689
}
676690

677-
$this->fireResolvingCallbacks($abstract, $object);
678-
691+
if (! $silent) {
692+
$this->fireResolvingCallbacks($abstract, $object);
693+
}
679694
// Before returning, we will also set the resolved flag to "true" and pop off
680695
// the parameter overrides for this build. After those two things are done
681696
// we will be ready to return back the fully constructed class instance.

tests/Container/ContainerTest.php

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,356 @@ 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 () 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 testGlobalResolvingCallbacksAreCalledOnceForImplementation()
1044+
{
1045+
$container = new Container;
1046+
1047+
$callCounter = 0;
1048+
$container->resolving(function ($some) use (&$callCounter) {
1049+
$callCounter++;
1050+
});
1051+
1052+
$container->bind(IContainerContractStub::class, ContainerImplementationStub::class);
1053+
1054+
$container->make(ContainerImplementationStub::class);
1055+
$this->assertEquals(1, $callCounter);
1056+
1057+
$container->make(IContainerContractStub::class);
1058+
$this->assertEquals(2, $callCounter);
1059+
}
1060+
1061+
public function testAfterResolvingCallbacksAreCalledOnceForImplementation()
1062+
{
1063+
$container = new Container;
1064+
1065+
$callCounter = 0;
1066+
$container->afterResolving(IContainerContractStub::class, function ($some) use (&$callCounter) {
1067+
$callCounter++;
1068+
});
1069+
1070+
$container->bind(IContainerContractStub::class, ContainerImplementationStub::class);
1071+
1072+
$container->make(ContainerImplementationStub::class);
1073+
$this->assertEquals(1, $callCounter);
1074+
1075+
$container->make(IContainerContractStub::class);
1076+
$this->assertEquals(2, $callCounter);
1077+
}
1078+
1079+
public function testResolvingCallbacksAreCalledOnceForSingletonConcretes()
1080+
{
1081+
$container = new Container;
1082+
1083+
$callCounter = 0;
1084+
$container->resolving(IContainerContractStub::class, function () use (&$callCounter) {
1085+
$callCounter++;
1086+
});
1087+
1088+
$container->bind(IContainerContractStub::class, ContainerImplementationStub::class);
1089+
$container->bind(ContainerImplementationStub::class);
1090+
1091+
$container->make(ContainerImplementationStub::class);
1092+
$this->assertEquals(1, $callCounter);
1093+
1094+
$container->make(ContainerImplementationStub::class);
1095+
$this->assertEquals(2, $callCounter);
1096+
1097+
$container->make(IContainerContractStub::class);
1098+
$this->assertEquals(3, $callCounter);
1099+
}
1100+
1101+
public function testResolvingCallbacksCanStillBeAddedAfterTheFirstResolution()
1102+
{
1103+
$container = new Container;
1104+
1105+
$container->bind(IContainerContractStub::class, ContainerImplementationStub::class);
1106+
1107+
$container->make(ContainerImplementationStub::class);
1108+
1109+
$callCounter = 0;
1110+
$container->resolving(IContainerContractStub::class, function () use (&$callCounter) {
1111+
$callCounter++;
1112+
});
1113+
1114+
$container->make(ContainerImplementationStub::class);
1115+
$this->assertEquals(1, $callCounter);
1116+
}
1117+
1118+
public function testResolvingCallbacksAreCanceledWhenInterfaceGetsBoundToSomeOtherConcrete()
1119+
{
1120+
$container = new Container;
1121+
1122+
$container->bind(IContainerContractStub::class, ContainerImplementationStub::class);
1123+
1124+
$callCounter = 0;
1125+
$container->resolving(ContainerImplementationStub::class, function () use (&$callCounter) {
1126+
$callCounter++;
1127+
});
1128+
1129+
$container->make(IContainerContractStub::class);
1130+
$this->assertEquals(1, $callCounter);
1131+
1132+
$container->bind(IContainerContractStub::class, ContainerImplementationStubTwo::class);
1133+
$container->make(IContainerContractStub::class);
1134+
$this->assertEquals(1, $callCounter);
1135+
}
1136+
1137+
public function testResolvingCallbacksAreCalledOnceForStringAbstractions()
1138+
{
1139+
$container = new Container;
1140+
1141+
$callCounter = 0;
1142+
$container->resolving('foo', function ($some) use (&$callCounter) {
1143+
$callCounter++;
1144+
});
1145+
1146+
$container->bind('foo', ContainerImplementationStub::class);
1147+
1148+
$container->make('foo');
1149+
$this->assertEquals(1, $callCounter);
1150+
1151+
$container->make('foo');
1152+
$this->assertEquals(2, $callCounter);
1153+
}
1154+
1155+
public function testResolvingCallbacksAreCalledOnceForImplementation2()
1156+
{
1157+
$container = new Container;
1158+
1159+
$callCounter = 0;
1160+
$container->resolving(IContainerContractStub::class, function () use (&$callCounter) {
1161+
$callCounter++;
1162+
});
1163+
1164+
$container->bind(IContainerContractStub::class, function () {
1165+
return new ContainerImplementationStub;
1166+
});
1167+
1168+
$container->make(IContainerContractStub::class);
1169+
$this->assertEquals(1, $callCounter);
1170+
1171+
$container->make(ContainerImplementationStub::class);
1172+
$this->assertEquals(2, $callCounter);
1173+
1174+
$container->make(ContainerImplementationStub::class);
1175+
$this->assertEquals(3, $callCounter);
1176+
1177+
$container->make(IContainerContractStub::class);
1178+
$this->assertEquals(4, $callCounter);
1179+
}
1180+
1181+
public function testRebindingDoesNotAffectResolvingCallbacks()
1182+
{
1183+
$container = new Container;
1184+
1185+
$callCounter = 0;
1186+
$container->resolving(IContainerContractStub::class, function () use (&$callCounter) {
1187+
$callCounter++;
1188+
});
1189+
1190+
$container->bind(IContainerContractStub::class, ContainerImplementationStub::class);
1191+
$container->bind(IContainerContractStub::class, function () {
1192+
return new ContainerImplementationStub;
1193+
});
1194+
1195+
$container->make(IContainerContractStub::class);
1196+
$this->assertEquals(1, $callCounter);
1197+
1198+
$container->make(ContainerImplementationStub::class);
1199+
$this->assertEquals(2, $callCounter);
1200+
1201+
$container->make(ContainerImplementationStub::class);
1202+
$this->assertEquals(3, $callCounter);
1203+
1204+
$container->make(IContainerContractStub::class);
1205+
$this->assertEquals(4, $callCounter);
1206+
}
1207+
1208+
public function testParametersPassedIntoResolvingCallbacks()
1209+
{
1210+
$container = new Container;
1211+
1212+
$container->resolving(IContainerContractStub::class, function ($obj, $app) use ($container) {
1213+
$this->assertInstanceOf(IContainerContractStub::class, $obj);
1214+
$this->assertInstanceOf(ContainerImplementationStubTwo::class, $obj);
1215+
$this->assertSame($container, $app);
1216+
});
1217+
1218+
$container->afterResolving(IContainerContractStub::class, function ($obj, $app) use ($container) {
1219+
$this->assertInstanceOf(IContainerContractStub::class, $obj);
1220+
$this->assertInstanceOf(ContainerImplementationStubTwo::class, $obj);
1221+
$this->assertSame($container, $app);
1222+
});
1223+
1224+
$container->afterResolving(function ($obj, $app) use ($container) {
1225+
$this->assertInstanceOf(IContainerContractStub::class, $obj);
1226+
$this->assertInstanceOf(ContainerImplementationStubTwo::class, $obj);
1227+
$this->assertSame($container, $app);
1228+
});
1229+
1230+
$container->bind(IContainerContractStub::class, ContainerImplementationStubTwo::class);
1231+
$container->make(IContainerContractStub::class);
1232+
}
1233+
1234+
public function testResolvingCallbacksAreCallWhenRebindHappenForResolvedAbstract()
1235+
{
1236+
$container = new Container;
1237+
1238+
$callCounter = 0;
1239+
$container->resolving(IContainerContractStub::class, function () use (&$callCounter) {
1240+
$callCounter++;
1241+
});
1242+
1243+
$container->bind(IContainerContractStub::class, ContainerImplementationStub::class);
1244+
1245+
$container->make(IContainerContractStub::class);
1246+
$this->assertEquals(1, $callCounter);
1247+
1248+
$container->bind(IContainerContractStub::class, ContainerImplementationStubTwo::class);
1249+
$this->assertEquals(2, $callCounter);
1250+
1251+
$container->make(ContainerImplementationStubTwo::class);
1252+
$this->assertEquals(3, $callCounter);
1253+
1254+
$container->bind(IContainerContractStub::class, function () {
1255+
return new ContainerImplementationStubTwo();
1256+
});
1257+
$this->assertEquals(4, $callCounter);
1258+
1259+
$container->make(IContainerContractStub::class);
1260+
$this->assertEquals(5, $callCounter);
1261+
}
1262+
1263+
public function testRebindingDoesNotAffectMultipleResolvingCallbacks()
1264+
{
1265+
$container = new Container;
1266+
1267+
$callCounter = 0;
1268+
1269+
$container->resolving(IContainerContractStub::class, function () use (&$callCounter) {
1270+
$callCounter++;
1271+
});
1272+
1273+
$container->resolving(ContainerImplementationStubTwo::class, function () use (&$callCounter) {
1274+
$callCounter++;
1275+
});
1276+
1277+
$container->bind(IContainerContractStub::class, ContainerImplementationStub::class);
1278+
1279+
// it should call the callback for interface
1280+
$container->make(IContainerContractStub::class);
1281+
$this->assertEquals(1, $callCounter);
1282+
1283+
// it should call the callback for interface
1284+
$container->make(ContainerImplementationStub::class);
1285+
$this->assertEquals(2, $callCounter);
1286+
1287+
// should call the callback for the interface it implements
1288+
// plus the callback for ContainerImplementationStubTwo.
1289+
$container->make(ContainerImplementationStubTwo::class);
1290+
$this->assertEquals(4, $callCounter);
1291+
}
1292+
1293+
public function testResolvingCallbacksAreCalledForInterfaces()
1294+
{
1295+
$container = new Container;
1296+
1297+
$callCounter = 0;
1298+
$container->resolving(IContainerContractStub::class, function () use (&$callCounter) {
1299+
$callCounter++;
1300+
});
1301+
1302+
$container->bind(IContainerContractStub::class, ContainerImplementationStub::class);
1303+
1304+
$container->make(IContainerContractStub::class);
1305+
1306+
$this->assertEquals(1, $callCounter);
1307+
}
1308+
1309+
public function testResolvingCallbacksAreCalledForConcretesWhenAttachedOnInterface()
1310+
{
1311+
$container = new Container;
1312+
1313+
$callCounter = 0;
1314+
$container->resolving(ContainerImplementationStub::class, function () use (&$callCounter) {
1315+
$callCounter++;
1316+
});
1317+
1318+
$container->bind(IContainerContractStub::class, ContainerImplementationStub::class);
1319+
1320+
$container->make(IContainerContractStub::class);
1321+
$this->assertEquals(1, $callCounter);
1322+
1323+
$container->make(ContainerImplementationStub::class);
1324+
$this->assertEquals(2, $callCounter);
1325+
}
1326+
1327+
public function testResolvingCallbacksAreCalledForConcretesWhenAttachedOnConcretes()
1328+
{
1329+
$container = new Container;
1330+
1331+
$callCounter = 0;
1332+
$container->resolving(ContainerImplementationStub::class, function () use (&$callCounter) {
1333+
$callCounter++;
1334+
});
1335+
1336+
$container->bind(IContainerContractStub::class, ContainerImplementationStub::class);
1337+
1338+
$container->make(IContainerContractStub::class);
1339+
$this->assertEquals(1, $callCounter);
1340+
1341+
$container->make(ContainerImplementationStub::class);
1342+
$this->assertEquals(2, $callCounter);
1343+
}
1344+
1345+
public function testResolvingCallbacksAreCalledForConcretesWithNoBinding()
1346+
{
1347+
$container = new Container;
1348+
1349+
$callCounter = 0;
1350+
$container->resolving(ContainerImplementationStub::class, function () use (&$callCounter) {
1351+
$callCounter++;
1352+
});
1353+
1354+
$container->make(ContainerImplementationStub::class);
1355+
$this->assertEquals(1, $callCounter);
1356+
$container->make(ContainerImplementationStub::class);
1357+
$this->assertEquals(2, $callCounter);
1358+
}
1359+
1360+
public function testResolvingCallbacksAreCalledForInterFacesWithNoBinding()
1361+
{
1362+
$container = new Container;
1363+
1364+
$callCounter = 0;
1365+
$container->resolving(IContainerContractStub::class, function () use (&$callCounter) {
1366+
$callCounter++;
1367+
});
1368+
1369+
$container->make(ContainerImplementationStub::class);
1370+
$this->assertEquals(1, $callCounter);
1371+
$container->make(ContainerImplementationStub::class);
1372+
$this->assertEquals(2, $callCounter);
1373+
}
1374+
10251375
public function testMakeWithMethodIsAnAliasForMakeMethod()
10261376
{
10271377
$mock = $this->getMockBuilder(Container::class)

0 commit comments

Comments
 (0)