ContextualBindingTest.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. <?php
  2. namespace Illuminate\Tests\Container;
  3. use Illuminate\Config\Repository;
  4. use Illuminate\Container\Container;
  5. use PHPUnit\Framework\TestCase;
  6. class ContextualBindingTest extends TestCase
  7. {
  8. public function testContainerCanInjectDifferentImplementationsDependingOnContext()
  9. {
  10. $container = new Container;
  11. $container->bind(IContainerContextContractStub::class, ContainerContextImplementationStub::class);
  12. $container->when(ContainerTestContextInjectOne::class)->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStub::class);
  13. $container->when(ContainerTestContextInjectTwo::class)->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStubTwo::class);
  14. $one = $container->make(ContainerTestContextInjectOne::class);
  15. $two = $container->make(ContainerTestContextInjectTwo::class);
  16. $this->assertInstanceOf(ContainerContextImplementationStub::class, $one->impl);
  17. $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $two->impl);
  18. /*
  19. * Test With Closures
  20. */
  21. $container = new Container;
  22. $container->bind(IContainerContextContractStub::class, ContainerContextImplementationStub::class);
  23. $container->when(ContainerTestContextInjectOne::class)->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStub::class);
  24. $container->when(ContainerTestContextInjectTwo::class)->needs(IContainerContextContractStub::class)->give(function ($container) {
  25. return $container->make(ContainerContextImplementationStubTwo::class);
  26. });
  27. $one = $container->make(ContainerTestContextInjectOne::class);
  28. $two = $container->make(ContainerTestContextInjectTwo::class);
  29. $this->assertInstanceOf(ContainerContextImplementationStub::class, $one->impl);
  30. $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $two->impl);
  31. }
  32. public function testContextualBindingWorksForExistingInstancedBindings()
  33. {
  34. $container = new Container;
  35. $container->instance(IContainerContextContractStub::class, new ContainerImplementationStub);
  36. $container->when(ContainerTestContextInjectOne::class)->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStubTwo::class);
  37. $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $container->make(ContainerTestContextInjectOne::class)->impl);
  38. }
  39. public function testContextualBindingWorksForNewlyInstancedBindings()
  40. {
  41. $container = new Container;
  42. $container->when(ContainerTestContextInjectOne::class)->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStubTwo::class);
  43. $container->instance(IContainerContextContractStub::class, new ContainerImplementationStub);
  44. $this->assertInstanceOf(
  45. ContainerContextImplementationStubTwo::class,
  46. $container->make(ContainerTestContextInjectOne::class)->impl
  47. );
  48. }
  49. public function testContextualBindingWorksOnExistingAliasedInstances()
  50. {
  51. $container = new Container;
  52. $container->instance('stub', new ContainerImplementationStub);
  53. $container->alias('stub', IContainerContextContractStub::class);
  54. $container->when(ContainerTestContextInjectOne::class)->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStubTwo::class);
  55. $this->assertInstanceOf(
  56. ContainerContextImplementationStubTwo::class,
  57. $container->make(ContainerTestContextInjectOne::class)->impl
  58. );
  59. }
  60. public function testContextualBindingWorksOnNewAliasedInstances()
  61. {
  62. $container = new Container;
  63. $container->when(ContainerTestContextInjectOne::class)->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStubTwo::class);
  64. $container->instance('stub', new ContainerImplementationStub);
  65. $container->alias('stub', IContainerContextContractStub::class);
  66. $this->assertInstanceOf(
  67. ContainerContextImplementationStubTwo::class,
  68. $container->make(ContainerTestContextInjectOne::class)->impl
  69. );
  70. }
  71. public function testContextualBindingWorksOnNewAliasedBindings()
  72. {
  73. $container = new Container;
  74. $container->when(ContainerTestContextInjectOne::class)->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStubTwo::class);
  75. $container->bind('stub', ContainerContextImplementationStub::class);
  76. $container->alias('stub', IContainerContextContractStub::class);
  77. $this->assertInstanceOf(
  78. ContainerContextImplementationStubTwo::class,
  79. $container->make(ContainerTestContextInjectOne::class)->impl
  80. );
  81. }
  82. public function testContextualBindingWorksForMultipleClasses()
  83. {
  84. $container = new Container;
  85. $container->bind(IContainerContextContractStub::class, ContainerContextImplementationStub::class);
  86. $container->when([ContainerTestContextInjectTwo::class, ContainerTestContextInjectThree::class])->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStubTwo::class);
  87. $this->assertInstanceOf(
  88. ContainerContextImplementationStub::class,
  89. $container->make(ContainerTestContextInjectOne::class)->impl
  90. );
  91. $this->assertInstanceOf(
  92. ContainerContextImplementationStubTwo::class,
  93. $container->make(ContainerTestContextInjectTwo::class)->impl
  94. );
  95. $this->assertInstanceOf(
  96. ContainerContextImplementationStubTwo::class,
  97. $container->make(ContainerTestContextInjectThree::class)->impl
  98. );
  99. }
  100. public function testContextualBindingDoesntOverrideNonContextualResolution()
  101. {
  102. $container = new Container;
  103. $container->instance('stub', new ContainerContextImplementationStub);
  104. $container->alias('stub', IContainerContextContractStub::class);
  105. $container->when(ContainerTestContextInjectTwo::class)->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStubTwo::class);
  106. $this->assertInstanceOf(
  107. ContainerContextImplementationStubTwo::class,
  108. $container->make(ContainerTestContextInjectTwo::class)->impl
  109. );
  110. $this->assertInstanceOf(
  111. ContainerContextImplementationStub::class,
  112. $container->make(ContainerTestContextInjectOne::class)->impl
  113. );
  114. }
  115. public function testContextuallyBoundInstancesAreNotUnnecessarilyRecreated()
  116. {
  117. ContainerTestContextInjectInstantiations::$instantiations = 0;
  118. $container = new Container;
  119. $container->instance(IContainerContextContractStub::class, new ContainerImplementationStub);
  120. $container->instance(ContainerTestContextInjectInstantiations::class, new ContainerTestContextInjectInstantiations);
  121. $this->assertEquals(1, ContainerTestContextInjectInstantiations::$instantiations);
  122. $container->when(ContainerTestContextInjectOne::class)->needs(IContainerContextContractStub::class)->give(ContainerTestContextInjectInstantiations::class);
  123. $container->make(ContainerTestContextInjectOne::class);
  124. $container->make(ContainerTestContextInjectOne::class);
  125. $container->make(ContainerTestContextInjectOne::class);
  126. $container->make(ContainerTestContextInjectOne::class);
  127. $this->assertEquals(1, ContainerTestContextInjectInstantiations::$instantiations);
  128. }
  129. public function testContainerCanInjectSimpleVariable()
  130. {
  131. $container = new Container;
  132. $container->when(ContainerInjectVariableStub::class)->needs('$something')->give(100);
  133. $instance = $container->make(ContainerInjectVariableStub::class);
  134. $this->assertEquals(100, $instance->something);
  135. $container = new Container;
  136. $container->when(ContainerInjectVariableStub::class)->needs('$something')->give(function ($container) {
  137. return $container->make(ContainerConcreteStub::class);
  138. });
  139. $instance = $container->make(ContainerInjectVariableStub::class);
  140. $this->assertInstanceOf(ContainerConcreteStub::class, $instance->something);
  141. }
  142. public function testContextualBindingWorksWithAliasedTargets()
  143. {
  144. $container = new Container;
  145. $container->bind(IContainerContextContractStub::class, ContainerContextImplementationStub::class);
  146. $container->alias(IContainerContextContractStub::class, 'interface-stub');
  147. $container->alias(ContainerContextImplementationStub::class, 'stub-1');
  148. $container->when(ContainerTestContextInjectOne::class)->needs('interface-stub')->give('stub-1');
  149. $container->when(ContainerTestContextInjectTwo::class)->needs('interface-stub')->give(ContainerContextImplementationStubTwo::class);
  150. $one = $container->make(ContainerTestContextInjectOne::class);
  151. $two = $container->make(ContainerTestContextInjectTwo::class);
  152. $this->assertInstanceOf(ContainerContextImplementationStub::class, $one->impl);
  153. $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $two->impl);
  154. }
  155. public function testContextualBindingWorksForNestedOptionalDependencies()
  156. {
  157. $container = new Container;
  158. $container->when(ContainerTestContextInjectTwoInstances::class)->needs(ContainerTestContextInjectTwo::class)->give(function () {
  159. return new ContainerTestContextInjectTwo(new ContainerContextImplementationStubTwo);
  160. });
  161. $resolvedInstance = $container->make(ContainerTestContextInjectTwoInstances::class);
  162. $this->assertInstanceOf(
  163. ContainerTestContextWithOptionalInnerDependency::class,
  164. $resolvedInstance->implOne
  165. );
  166. $this->assertNull($resolvedInstance->implOne->inner);
  167. $this->assertInstanceOf(
  168. ContainerTestContextInjectTwo::class,
  169. $resolvedInstance->implTwo
  170. );
  171. $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $resolvedInstance->implTwo->impl);
  172. }
  173. public function testContextualBindingWorksForVariadicDependencies()
  174. {
  175. $container = new Container;
  176. $container->when(ContainerTestContextInjectVariadic::class)->needs(IContainerContextContractStub::class)->give(function ($c) {
  177. return [
  178. $c->make(ContainerContextImplementationStub::class),
  179. $c->make(ContainerContextImplementationStubTwo::class),
  180. ];
  181. });
  182. $resolvedInstance = $container->make(ContainerTestContextInjectVariadic::class);
  183. $this->assertCount(2, $resolvedInstance->stubs);
  184. $this->assertInstanceOf(ContainerContextImplementationStub::class, $resolvedInstance->stubs[0]);
  185. $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $resolvedInstance->stubs[1]);
  186. }
  187. public function testContextualBindingWorksForVariadicDependenciesWithNothingBound()
  188. {
  189. $container = new Container;
  190. $resolvedInstance = $container->make(ContainerTestContextInjectVariadic::class);
  191. $this->assertCount(0, $resolvedInstance->stubs);
  192. }
  193. public function testContextualBindingWorksForVariadicAfterNonVariadicDependencies()
  194. {
  195. $container = new Container;
  196. $container->when(ContainerTestContextInjectVariadicAfterNonVariadic::class)->needs(IContainerContextContractStub::class)->give(function ($c) {
  197. return [
  198. $c->make(ContainerContextImplementationStub::class),
  199. $c->make(ContainerContextImplementationStubTwo::class),
  200. ];
  201. });
  202. $resolvedInstance = $container->make(ContainerTestContextInjectVariadicAfterNonVariadic::class);
  203. $this->assertCount(2, $resolvedInstance->stubs);
  204. $this->assertInstanceOf(ContainerContextImplementationStub::class, $resolvedInstance->stubs[0]);
  205. $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $resolvedInstance->stubs[1]);
  206. }
  207. public function testContextualBindingWorksForVariadicAfterNonVariadicDependenciesWithNothingBound()
  208. {
  209. $container = new Container;
  210. $resolvedInstance = $container->make(ContainerTestContextInjectVariadicAfterNonVariadic::class);
  211. $this->assertCount(0, $resolvedInstance->stubs);
  212. }
  213. public function testContextualBindingWorksForVariadicDependenciesWithoutFactory()
  214. {
  215. $container = new Container;
  216. $container->when(ContainerTestContextInjectVariadic::class)->needs(IContainerContextContractStub::class)->give([
  217. ContainerContextImplementationStub::class,
  218. ContainerContextImplementationStubTwo::class,
  219. ]);
  220. $resolvedInstance = $container->make(ContainerTestContextInjectVariadic::class);
  221. $this->assertCount(2, $resolvedInstance->stubs);
  222. $this->assertInstanceOf(ContainerContextImplementationStub::class, $resolvedInstance->stubs[0]);
  223. $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $resolvedInstance->stubs[1]);
  224. }
  225. public function testContextualBindingGivesTagsForArrayWithNoTagsDefined()
  226. {
  227. $container = new Container;
  228. $container->when(ContainerTestContextInjectArray::class)->needs('$stubs')->giveTagged('stub');
  229. $resolvedInstance = $container->make(ContainerTestContextInjectArray::class);
  230. $this->assertCount(0, $resolvedInstance->stubs);
  231. }
  232. public function testContextualBindingGivesTagsForVariadicWithNoTagsDefined()
  233. {
  234. $container = new Container;
  235. $container->when(ContainerTestContextInjectVariadic::class)->needs(IContainerContextContractStub::class)->giveTagged('stub');
  236. $resolvedInstance = $container->make(ContainerTestContextInjectVariadic::class);
  237. $this->assertCount(0, $resolvedInstance->stubs);
  238. }
  239. public function testContextualBindingGivesTagsForArray()
  240. {
  241. $container = new Container;
  242. $container->tag([
  243. ContainerContextImplementationStub::class,
  244. ContainerContextImplementationStubTwo::class,
  245. ], ['stub']);
  246. $container->when(ContainerTestContextInjectArray::class)->needs('$stubs')->giveTagged('stub');
  247. $resolvedInstance = $container->make(ContainerTestContextInjectArray::class);
  248. $this->assertCount(2, $resolvedInstance->stubs);
  249. $this->assertInstanceOf(ContainerContextImplementationStub::class, $resolvedInstance->stubs[0]);
  250. $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $resolvedInstance->stubs[1]);
  251. }
  252. public function testContextualBindingGivesTagsForVariadic()
  253. {
  254. $container = new Container;
  255. $container->tag([
  256. ContainerContextImplementationStub::class,
  257. ContainerContextImplementationStubTwo::class,
  258. ], ['stub']);
  259. $container->when(ContainerTestContextInjectVariadic::class)->needs(IContainerContextContractStub::class)->giveTagged('stub');
  260. $resolvedInstance = $container->make(ContainerTestContextInjectVariadic::class);
  261. $this->assertCount(2, $resolvedInstance->stubs);
  262. $this->assertInstanceOf(ContainerContextImplementationStub::class, $resolvedInstance->stubs[0]);
  263. $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $resolvedInstance->stubs[1]);
  264. }
  265. public function testContextualBindingGivesValuesFromConfigOptionalValueNull()
  266. {
  267. $container = new Container;
  268. $container->singleton('config', function () {
  269. return new Repository([
  270. 'test' => [
  271. 'username' => 'laravel',
  272. 'password' => 'hunter42',
  273. ],
  274. ]);
  275. });
  276. $container
  277. ->when(ContainerTestContextInjectFromConfigIndividualValues::class)
  278. ->needs('$username')
  279. ->giveConfig('test.username');
  280. $container
  281. ->when(ContainerTestContextInjectFromConfigIndividualValues::class)
  282. ->needs('$password')
  283. ->giveConfig('test.password');
  284. $resolvedInstance = $container->make(ContainerTestContextInjectFromConfigIndividualValues::class);
  285. $this->assertSame('laravel', $resolvedInstance->username);
  286. $this->assertSame('hunter42', $resolvedInstance->password);
  287. $this->assertNull($resolvedInstance->alias);
  288. }
  289. public function testContextualBindingGivesValuesFromConfigOptionalValueSet()
  290. {
  291. $container = new Container;
  292. $container->singleton('config', function () {
  293. return new Repository([
  294. 'test' => [
  295. 'username' => 'laravel',
  296. 'password' => 'hunter42',
  297. 'alias' => 'lumen',
  298. ],
  299. ]);
  300. });
  301. $container
  302. ->when(ContainerTestContextInjectFromConfigIndividualValues::class)
  303. ->needs('$username')
  304. ->giveConfig('test.username');
  305. $container
  306. ->when(ContainerTestContextInjectFromConfigIndividualValues::class)
  307. ->needs('$password')
  308. ->giveConfig('test.password');
  309. $container
  310. ->when(ContainerTestContextInjectFromConfigIndividualValues::class)
  311. ->needs('$alias')
  312. ->giveConfig('test.alias');
  313. $resolvedInstance = $container->make(ContainerTestContextInjectFromConfigIndividualValues::class);
  314. $this->assertSame('laravel', $resolvedInstance->username);
  315. $this->assertSame('hunter42', $resolvedInstance->password);
  316. $this->assertSame('lumen', $resolvedInstance->alias);
  317. }
  318. public function testContextualBindingGivesValuesFromConfigWithDefault()
  319. {
  320. $container = new Container;
  321. $container->singleton('config', function () {
  322. return new Repository([
  323. 'test' => [
  324. 'password' => 'hunter42',
  325. ],
  326. ]);
  327. });
  328. $container
  329. ->when(ContainerTestContextInjectFromConfigIndividualValues::class)
  330. ->needs('$username')
  331. ->giveConfig('test.username', 'DEFAULT_USERNAME');
  332. $container
  333. ->when(ContainerTestContextInjectFromConfigIndividualValues::class)
  334. ->needs('$password')
  335. ->giveConfig('test.password');
  336. $resolvedInstance = $container->make(ContainerTestContextInjectFromConfigIndividualValues::class);
  337. $this->assertSame('DEFAULT_USERNAME', $resolvedInstance->username);
  338. $this->assertSame('hunter42', $resolvedInstance->password);
  339. $this->assertNull($resolvedInstance->alias);
  340. }
  341. public function testContextualBindingGivesValuesFromConfigArray()
  342. {
  343. $container = new Container;
  344. $container->singleton('config', function () {
  345. return new Repository([
  346. 'test' => [
  347. 'username' => 'laravel',
  348. 'password' => 'hunter42',
  349. 'alias' => 'lumen',
  350. ],
  351. ]);
  352. });
  353. $container
  354. ->when(ContainerTestContextInjectFromConfigArray::class)
  355. ->needs('$settings')
  356. ->giveConfig('test');
  357. $resolvedInstance = $container->make(ContainerTestContextInjectFromConfigArray::class);
  358. $this->assertSame('laravel', $resolvedInstance->settings['username']);
  359. $this->assertSame('hunter42', $resolvedInstance->settings['password']);
  360. $this->assertSame('lumen', $resolvedInstance->settings['alias']);
  361. }
  362. }
  363. interface IContainerContextContractStub
  364. {
  365. //
  366. }
  367. class ContainerContextNonContractStub
  368. {
  369. //
  370. }
  371. class ContainerContextImplementationStub implements IContainerContextContractStub
  372. {
  373. //
  374. }
  375. class ContainerContextImplementationStubTwo implements IContainerContextContractStub
  376. {
  377. //
  378. }
  379. class ContainerTestContextInjectInstantiations implements IContainerContextContractStub
  380. {
  381. public static $instantiations;
  382. public function __construct()
  383. {
  384. static::$instantiations++;
  385. }
  386. }
  387. class ContainerTestContextInjectOne
  388. {
  389. public $impl;
  390. public function __construct(IContainerContextContractStub $impl)
  391. {
  392. $this->impl = $impl;
  393. }
  394. }
  395. class ContainerTestContextInjectTwo
  396. {
  397. public $impl;
  398. public function __construct(IContainerContextContractStub $impl)
  399. {
  400. $this->impl = $impl;
  401. }
  402. }
  403. class ContainerTestContextInjectThree
  404. {
  405. public $impl;
  406. public function __construct(IContainerContextContractStub $impl)
  407. {
  408. $this->impl = $impl;
  409. }
  410. }
  411. class ContainerTestContextInjectTwoInstances
  412. {
  413. public $implOne;
  414. public $implTwo;
  415. public function __construct(ContainerTestContextWithOptionalInnerDependency $implOne, ContainerTestContextInjectTwo $implTwo)
  416. {
  417. $this->implOne = $implOne;
  418. $this->implTwo = $implTwo;
  419. }
  420. }
  421. class ContainerTestContextWithOptionalInnerDependency
  422. {
  423. public $inner;
  424. public function __construct(ContainerTestContextInjectOne $inner = null)
  425. {
  426. $this->inner = $inner;
  427. }
  428. }
  429. class ContainerTestContextInjectArray
  430. {
  431. public $stubs;
  432. public function __construct(array $stubs)
  433. {
  434. $this->stubs = $stubs;
  435. }
  436. }
  437. class ContainerTestContextInjectVariadic
  438. {
  439. public $stubs;
  440. public function __construct(IContainerContextContractStub ...$stubs)
  441. {
  442. $this->stubs = $stubs;
  443. }
  444. }
  445. class ContainerTestContextInjectVariadicAfterNonVariadic
  446. {
  447. public $other;
  448. public $stubs;
  449. public function __construct(ContainerContextNonContractStub $other, IContainerContextContractStub ...$stubs)
  450. {
  451. $this->other = $other;
  452. $this->stubs = $stubs;
  453. }
  454. }
  455. class ContainerTestContextInjectFromConfigIndividualValues
  456. {
  457. public $username;
  458. public $password;
  459. public $alias = null;
  460. public function __construct($username, $password, $alias = null)
  461. {
  462. $this->username = $username;
  463. $this->password = $password;
  464. $this->alias = $alias;
  465. }
  466. }
  467. class ContainerTestContextInjectFromConfigArray
  468. {
  469. public $settings;
  470. public function __construct($settings)
  471. {
  472. $this->settings = $settings;
  473. }
  474. }