PimpleTest.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. <?php
  2. /*
  3. * This file is part of Pimple.
  4. *
  5. * Copyright (c) 2009 Fabien Potencier
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to deal
  9. * in the Software without restriction, including without limitation the rights
  10. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. * copies of the Software, and to permit persons to whom the Software is furnished
  12. * to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in all
  15. * copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. * THE SOFTWARE.
  24. */
  25. namespace Pimple\Tests;
  26. use PHPUnit\Framework\TestCase;
  27. use Pimple\Container;
  28. /**
  29. * @author Igor Wiedler <igor@wiedler.ch>
  30. */
  31. class PimpleTest extends TestCase
  32. {
  33. public function testWithString()
  34. {
  35. $pimple = new Container();
  36. $pimple['param'] = 'value';
  37. $this->assertEquals('value', $pimple['param']);
  38. }
  39. public function testWithClosure()
  40. {
  41. $pimple = new Container();
  42. $pimple['service'] = function () {
  43. return new Fixtures\Service();
  44. };
  45. $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']);
  46. }
  47. public function testServicesShouldBeDifferent()
  48. {
  49. $pimple = new Container();
  50. $pimple['service'] = $pimple->factory(function () {
  51. return new Fixtures\Service();
  52. });
  53. $serviceOne = $pimple['service'];
  54. $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne);
  55. $serviceTwo = $pimple['service'];
  56. $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo);
  57. $this->assertNotSame($serviceOne, $serviceTwo);
  58. }
  59. public function testShouldPassContainerAsParameter()
  60. {
  61. $pimple = new Container();
  62. $pimple['service'] = function () {
  63. return new Fixtures\Service();
  64. };
  65. $pimple['container'] = function ($container) {
  66. return $container;
  67. };
  68. $this->assertNotSame($pimple, $pimple['service']);
  69. $this->assertSame($pimple, $pimple['container']);
  70. }
  71. public function testIsset()
  72. {
  73. $pimple = new Container();
  74. $pimple['param'] = 'value';
  75. $pimple['service'] = function () {
  76. return new Fixtures\Service();
  77. };
  78. $pimple['null'] = null;
  79. $this->assertTrue(isset($pimple['param']));
  80. $this->assertTrue(isset($pimple['service']));
  81. $this->assertTrue(isset($pimple['null']));
  82. $this->assertFalse(isset($pimple['non_existent']));
  83. }
  84. public function testConstructorInjection()
  85. {
  86. $params = ['param' => 'value'];
  87. $pimple = new Container($params);
  88. $this->assertSame($params['param'], $pimple['param']);
  89. }
  90. public function testOffsetGetValidatesKeyIsPresent()
  91. {
  92. $this->expectException(\Pimple\Exception\UnknownIdentifierException::class);
  93. $this->expectExceptionMessage('Identifier "foo" is not defined.');
  94. $pimple = new Container();
  95. echo $pimple['foo'];
  96. }
  97. /**
  98. * @group legacy
  99. */
  100. public function testLegacyOffsetGetValidatesKeyIsPresent()
  101. {
  102. $this->expectException(\InvalidArgumentException::class);
  103. $this->expectExceptionMessage('Identifier "foo" is not defined.');
  104. $pimple = new Container();
  105. echo $pimple['foo'];
  106. }
  107. public function testOffsetGetHonorsNullValues()
  108. {
  109. $pimple = new Container();
  110. $pimple['foo'] = null;
  111. $this->assertNull($pimple['foo']);
  112. }
  113. public function testUnset()
  114. {
  115. $pimple = new Container();
  116. $pimple['param'] = 'value';
  117. $pimple['service'] = function () {
  118. return new Fixtures\Service();
  119. };
  120. unset($pimple['param'], $pimple['service']);
  121. $this->assertFalse(isset($pimple['param']));
  122. $this->assertFalse(isset($pimple['service']));
  123. }
  124. /**
  125. * @dataProvider serviceDefinitionProvider
  126. */
  127. public function testShare($service)
  128. {
  129. $pimple = new Container();
  130. $pimple['shared_service'] = $service;
  131. $serviceOne = $pimple['shared_service'];
  132. $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne);
  133. $serviceTwo = $pimple['shared_service'];
  134. $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo);
  135. $this->assertSame($serviceOne, $serviceTwo);
  136. }
  137. /**
  138. * @dataProvider serviceDefinitionProvider
  139. */
  140. public function testProtect($service)
  141. {
  142. $pimple = new Container();
  143. $pimple['protected'] = $pimple->protect($service);
  144. $this->assertSame($service, $pimple['protected']);
  145. }
  146. public function testGlobalFunctionNameAsParameterValue()
  147. {
  148. $pimple = new Container();
  149. $pimple['global_function'] = 'strlen';
  150. $this->assertSame('strlen', $pimple['global_function']);
  151. }
  152. public function testRaw()
  153. {
  154. $pimple = new Container();
  155. $pimple['service'] = $definition = $pimple->factory(function () {
  156. return 'foo';
  157. });
  158. $this->assertSame($definition, $pimple->raw('service'));
  159. }
  160. public function testRawHonorsNullValues()
  161. {
  162. $pimple = new Container();
  163. $pimple['foo'] = null;
  164. $this->assertNull($pimple->raw('foo'));
  165. }
  166. public function testFluentRegister()
  167. {
  168. $pimple = new Container();
  169. $this->assertSame($pimple, $pimple->register($this->getMockBuilder('Pimple\ServiceProviderInterface')->getMock()));
  170. }
  171. public function testRawValidatesKeyIsPresent()
  172. {
  173. $this->expectException(\Pimple\Exception\UnknownIdentifierException::class);
  174. $this->expectExceptionMessage('Identifier "foo" is not defined.');
  175. $pimple = new Container();
  176. $pimple->raw('foo');
  177. }
  178. /**
  179. * @group legacy
  180. */
  181. public function testLegacyRawValidatesKeyIsPresent()
  182. {
  183. $this->expectException(\InvalidArgumentException::class);
  184. $this->expectExceptionMessage('Identifier "foo" is not defined.');
  185. $pimple = new Container();
  186. $pimple->raw('foo');
  187. }
  188. /**
  189. * @dataProvider serviceDefinitionProvider
  190. */
  191. public function testExtend($service)
  192. {
  193. $pimple = new Container();
  194. $pimple['shared_service'] = function () {
  195. return new Fixtures\Service();
  196. };
  197. $pimple['factory_service'] = $pimple->factory(function () {
  198. return new Fixtures\Service();
  199. });
  200. $pimple->extend('shared_service', $service);
  201. $serviceOne = $pimple['shared_service'];
  202. $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne);
  203. $serviceTwo = $pimple['shared_service'];
  204. $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo);
  205. $this->assertSame($serviceOne, $serviceTwo);
  206. $this->assertSame($serviceOne->value, $serviceTwo->value);
  207. $pimple->extend('factory_service', $service);
  208. $serviceOne = $pimple['factory_service'];
  209. $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne);
  210. $serviceTwo = $pimple['factory_service'];
  211. $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo);
  212. $this->assertNotSame($serviceOne, $serviceTwo);
  213. $this->assertNotSame($serviceOne->value, $serviceTwo->value);
  214. }
  215. public function testExtendDoesNotLeakWithFactories()
  216. {
  217. if (\extension_loaded('pimple')) {
  218. $this->markTestSkipped('Pimple extension does not support this test');
  219. }
  220. $pimple = new Container();
  221. $pimple['foo'] = $pimple->factory(function () {
  222. return;
  223. });
  224. $pimple['foo'] = $pimple->extend('foo', function ($foo, $pimple) {
  225. return;
  226. });
  227. unset($pimple['foo']);
  228. $p = new \ReflectionProperty($pimple, 'values');
  229. $p->setAccessible(true);
  230. $this->assertEmpty($p->getValue($pimple));
  231. $p = new \ReflectionProperty($pimple, 'factories');
  232. $p->setAccessible(true);
  233. $this->assertCount(0, $p->getValue($pimple));
  234. }
  235. public function testExtendValidatesKeyIsPresent()
  236. {
  237. $this->expectException(\Pimple\Exception\UnknownIdentifierException::class);
  238. $this->expectExceptionMessage('Identifier "foo" is not defined.');
  239. $pimple = new Container();
  240. $pimple->extend('foo', function () {
  241. });
  242. }
  243. /**
  244. * @group legacy
  245. */
  246. public function testLegacyExtendValidatesKeyIsPresent()
  247. {
  248. $this->expectException(\InvalidArgumentException::class);
  249. $this->expectExceptionMessage('Identifier "foo" is not defined.');
  250. $pimple = new Container();
  251. $pimple->extend('foo', function () {
  252. });
  253. }
  254. public function testKeys()
  255. {
  256. $pimple = new Container();
  257. $pimple['foo'] = 123;
  258. $pimple['bar'] = 123;
  259. $this->assertEquals(['foo', 'bar'], $pimple->keys());
  260. }
  261. /** @test */
  262. public function settingAnInvokableObjectShouldTreatItAsFactory()
  263. {
  264. $pimple = new Container();
  265. $pimple['invokable'] = new Fixtures\Invokable();
  266. $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['invokable']);
  267. }
  268. /** @test */
  269. public function settingNonInvokableObjectShouldTreatItAsParameter()
  270. {
  271. $pimple = new Container();
  272. $pimple['non_invokable'] = new Fixtures\NonInvokable();
  273. $this->assertInstanceOf('Pimple\Tests\Fixtures\NonInvokable', $pimple['non_invokable']);
  274. }
  275. /**
  276. * @dataProvider badServiceDefinitionProvider
  277. */
  278. public function testFactoryFailsForInvalidServiceDefinitions($service)
  279. {
  280. $this->expectException(\Pimple\Exception\ExpectedInvokableException::class);
  281. $this->expectExceptionMessage('Service definition is not a Closure or invokable object.');
  282. $pimple = new Container();
  283. $pimple->factory($service);
  284. }
  285. /**
  286. * @group legacy
  287. * @dataProvider badServiceDefinitionProvider
  288. */
  289. public function testLegacyFactoryFailsForInvalidServiceDefinitions($service)
  290. {
  291. $this->expectException(\InvalidArgumentException::class);
  292. $this->expectExceptionMessage('Service definition is not a Closure or invokable object.');
  293. $pimple = new Container();
  294. $pimple->factory($service);
  295. }
  296. /**
  297. * @dataProvider badServiceDefinitionProvider
  298. */
  299. public function testProtectFailsForInvalidServiceDefinitions($service)
  300. {
  301. $this->expectException(\Pimple\Exception\ExpectedInvokableException::class);
  302. $this->expectExceptionMessage('Callable is not a Closure or invokable object.');
  303. $pimple = new Container();
  304. $pimple->protect($service);
  305. }
  306. /**
  307. * @group legacy
  308. * @dataProvider badServiceDefinitionProvider
  309. */
  310. public function testLegacyProtectFailsForInvalidServiceDefinitions($service)
  311. {
  312. $this->expectException(\InvalidArgumentException::class);
  313. $this->expectExceptionMessage('Callable is not a Closure or invokable object.');
  314. $pimple = new Container();
  315. $pimple->protect($service);
  316. }
  317. /**
  318. * @dataProvider badServiceDefinitionProvider
  319. */
  320. public function testExtendFailsForKeysNotContainingServiceDefinitions($service)
  321. {
  322. $this->expectException(\Pimple\Exception\InvalidServiceIdentifierException::class);
  323. $this->expectExceptionMessage('Identifier "foo" does not contain an object definition.');
  324. $pimple = new Container();
  325. $pimple['foo'] = $service;
  326. $pimple->extend('foo', function () {
  327. });
  328. }
  329. /**
  330. * @group legacy
  331. * @dataProvider badServiceDefinitionProvider
  332. */
  333. public function testLegacyExtendFailsForKeysNotContainingServiceDefinitions($service)
  334. {
  335. $this->expectException(\InvalidArgumentException::class);
  336. $this->expectExceptionMessage('Identifier "foo" does not contain an object definition.');
  337. $pimple = new Container();
  338. $pimple['foo'] = $service;
  339. $pimple->extend('foo', function () {
  340. });
  341. }
  342. /**
  343. * @group legacy
  344. * @expectedDeprecation How Pimple behaves when extending protected closures will be fixed in Pimple 4. Are you sure "foo" should be protected?
  345. */
  346. public function testExtendingProtectedClosureDeprecation()
  347. {
  348. $pimple = new Container();
  349. $pimple['foo'] = $pimple->protect(function () {
  350. return 'bar';
  351. });
  352. $pimple->extend('foo', function ($value) {
  353. return $value.'-baz';
  354. });
  355. $this->assertSame('bar-baz', $pimple['foo']);
  356. }
  357. /**
  358. * @dataProvider badServiceDefinitionProvider
  359. */
  360. public function testExtendFailsForInvalidServiceDefinitions($service)
  361. {
  362. $this->expectException(\Pimple\Exception\ExpectedInvokableException::class);
  363. $this->expectExceptionMessage('Extension service definition is not a Closure or invokable object.');
  364. $pimple = new Container();
  365. $pimple['foo'] = function () {
  366. };
  367. $pimple->extend('foo', $service);
  368. }
  369. /**
  370. * @group legacy
  371. * @dataProvider badServiceDefinitionProvider
  372. */
  373. public function testLegacyExtendFailsForInvalidServiceDefinitions($service)
  374. {
  375. $this->expectException(\InvalidArgumentException::class);
  376. $this->expectExceptionMessage('Extension service definition is not a Closure or invokable object.');
  377. $pimple = new Container();
  378. $pimple['foo'] = function () {
  379. };
  380. $pimple->extend('foo', $service);
  381. }
  382. public function testExtendFailsIfFrozenServiceIsNonInvokable()
  383. {
  384. $this->expectException(\Pimple\Exception\FrozenServiceException::class);
  385. $this->expectExceptionMessage('Cannot override frozen service "foo".');
  386. $pimple = new Container();
  387. $pimple['foo'] = function () {
  388. return new Fixtures\NonInvokable();
  389. };
  390. $foo = $pimple['foo'];
  391. $pimple->extend('foo', function () {
  392. });
  393. }
  394. public function testExtendFailsIfFrozenServiceIsInvokable()
  395. {
  396. $this->expectException(\Pimple\Exception\FrozenServiceException::class);
  397. $this->expectExceptionMessage('Cannot override frozen service "foo".');
  398. $pimple = new Container();
  399. $pimple['foo'] = function () {
  400. return new Fixtures\Invokable();
  401. };
  402. $foo = $pimple['foo'];
  403. $pimple->extend('foo', function () {
  404. });
  405. }
  406. /**
  407. * Provider for invalid service definitions.
  408. */
  409. public function badServiceDefinitionProvider()
  410. {
  411. return [
  412. [123],
  413. [new Fixtures\NonInvokable()],
  414. ];
  415. }
  416. /**
  417. * Provider for service definitions.
  418. */
  419. public function serviceDefinitionProvider()
  420. {
  421. return [
  422. [function ($value) {
  423. $service = new Fixtures\Service();
  424. $service->value = $value;
  425. return $service;
  426. }],
  427. [new Fixtures\Invokable()],
  428. ];
  429. }
  430. public function testDefiningNewServiceAfterFreeze()
  431. {
  432. $pimple = new Container();
  433. $pimple['foo'] = function () {
  434. return 'foo';
  435. };
  436. $foo = $pimple['foo'];
  437. $pimple['bar'] = function () {
  438. return 'bar';
  439. };
  440. $this->assertSame('bar', $pimple['bar']);
  441. }
  442. public function testOverridingServiceAfterFreeze()
  443. {
  444. $this->expectException(\Pimple\Exception\FrozenServiceException::class);
  445. $this->expectExceptionMessage('Cannot override frozen service "foo".');
  446. $pimple = new Container();
  447. $pimple['foo'] = function () {
  448. return 'foo';
  449. };
  450. $foo = $pimple['foo'];
  451. $pimple['foo'] = function () {
  452. return 'bar';
  453. };
  454. }
  455. /**
  456. * @group legacy
  457. */
  458. public function testLegacyOverridingServiceAfterFreeze()
  459. {
  460. $this->expectException(\RuntimeException::class);
  461. $this->expectExceptionMessage('Cannot override frozen service "foo".');
  462. $pimple = new Container();
  463. $pimple['foo'] = function () {
  464. return 'foo';
  465. };
  466. $foo = $pimple['foo'];
  467. $pimple['foo'] = function () {
  468. return 'bar';
  469. };
  470. }
  471. public function testRemovingServiceAfterFreeze()
  472. {
  473. $pimple = new Container();
  474. $pimple['foo'] = function () {
  475. return 'foo';
  476. };
  477. $foo = $pimple['foo'];
  478. unset($pimple['foo']);
  479. $pimple['foo'] = function () {
  480. return 'bar';
  481. };
  482. $this->assertSame('bar', $pimple['foo']);
  483. }
  484. public function testExtendingService()
  485. {
  486. $pimple = new Container();
  487. $pimple['foo'] = function () {
  488. return 'foo';
  489. };
  490. $pimple['foo'] = $pimple->extend('foo', function ($foo, $app) {
  491. return "$foo.bar";
  492. });
  493. $pimple['foo'] = $pimple->extend('foo', function ($foo, $app) {
  494. return "$foo.baz";
  495. });
  496. $this->assertSame('foo.bar.baz', $pimple['foo']);
  497. }
  498. public function testExtendingServiceAfterOtherServiceFreeze()
  499. {
  500. $pimple = new Container();
  501. $pimple['foo'] = function () {
  502. return 'foo';
  503. };
  504. $pimple['bar'] = function () {
  505. return 'bar';
  506. };
  507. $foo = $pimple['foo'];
  508. $pimple['bar'] = $pimple->extend('bar', function ($bar, $app) {
  509. return "$bar.baz";
  510. });
  511. $this->assertSame('bar.baz', $pimple['bar']);
  512. }
  513. }