FoundationApplicationTest.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. <?php
  2. namespace Illuminate\Tests\Foundation;
  3. use Illuminate\Config\Repository;
  4. use Illuminate\Contracts\Support\DeferrableProvider;
  5. use Illuminate\Foundation\Application;
  6. use Illuminate\Foundation\Bootstrap\RegisterFacades;
  7. use Illuminate\Foundation\Events\LocaleUpdated;
  8. use Illuminate\Support\ServiceProvider;
  9. use Mockery as m;
  10. use PHPUnit\Framework\TestCase;
  11. use stdClass;
  12. class FoundationApplicationTest extends TestCase
  13. {
  14. protected function tearDown(): void
  15. {
  16. m::close();
  17. }
  18. public function testSetLocaleSetsLocaleAndFiresLocaleChangedEvent()
  19. {
  20. $app = new Application;
  21. $app['config'] = $config = m::mock(stdClass::class);
  22. $config->shouldReceive('set')->once()->with('app.locale', 'foo');
  23. $app['translator'] = $trans = m::mock(stdClass::class);
  24. $trans->shouldReceive('setLocale')->once()->with('foo');
  25. $app['events'] = $events = m::mock(stdClass::class);
  26. $events->shouldReceive('dispatch')->once()->with(m::type(LocaleUpdated::class));
  27. $app->setLocale('foo');
  28. }
  29. public function testServiceProvidersAreCorrectlyRegistered()
  30. {
  31. $provider = m::mock(ApplicationBasicServiceProviderStub::class);
  32. $class = get_class($provider);
  33. $provider->shouldReceive('register')->once();
  34. $app = new Application;
  35. $app->register($provider);
  36. $this->assertArrayHasKey($class, $app->getLoadedProviders());
  37. }
  38. public function testClassesAreBoundWhenServiceProviderIsRegistered()
  39. {
  40. $app = new Application;
  41. $app->register($provider = new class($app) extends ServiceProvider
  42. {
  43. public $bindings = [
  44. AbstractClass::class => ConcreteClass::class,
  45. ];
  46. });
  47. $this->assertArrayHasKey(get_class($provider), $app->getLoadedProviders());
  48. $instance = $app->make(AbstractClass::class);
  49. $this->assertInstanceOf(ConcreteClass::class, $instance);
  50. $this->assertNotSame($instance, $app->make(AbstractClass::class));
  51. }
  52. public function testSingletonsAreCreatedWhenServiceProviderIsRegistered()
  53. {
  54. $app = new Application;
  55. $app->register($provider = new class($app) extends ServiceProvider
  56. {
  57. public $singletons = [
  58. AbstractClass::class => ConcreteClass::class,
  59. ];
  60. });
  61. $this->assertArrayHasKey(get_class($provider), $app->getLoadedProviders());
  62. $instance = $app->make(AbstractClass::class);
  63. $this->assertInstanceOf(ConcreteClass::class, $instance);
  64. $this->assertSame($instance, $app->make(AbstractClass::class));
  65. }
  66. public function testServiceProvidersAreCorrectlyRegisteredWhenRegisterMethodIsNotFilled()
  67. {
  68. $provider = m::mock(ServiceProvider::class);
  69. $class = get_class($provider);
  70. $provider->shouldReceive('register')->once();
  71. $app = new Application;
  72. $app->register($provider);
  73. $this->assertArrayHasKey($class, $app->getLoadedProviders());
  74. }
  75. public function testServiceProvidersCouldBeLoaded()
  76. {
  77. $provider = m::mock(ServiceProvider::class);
  78. $class = get_class($provider);
  79. $provider->shouldReceive('register')->once();
  80. $app = new Application;
  81. $app->register($provider);
  82. $this->assertTrue($app->providerIsLoaded($class));
  83. $this->assertFalse($app->providerIsLoaded(ApplicationBasicServiceProviderStub::class));
  84. }
  85. public function testDeferredServicesMarkedAsBound()
  86. {
  87. $app = new Application;
  88. $app->setDeferredServices(['foo' => ApplicationDeferredServiceProviderStub::class]);
  89. $this->assertTrue($app->bound('foo'));
  90. $this->assertSame('foo', $app->make('foo'));
  91. }
  92. public function testDeferredServicesAreSharedProperly()
  93. {
  94. $app = new Application;
  95. $app->setDeferredServices(['foo' => ApplicationDeferredSharedServiceProviderStub::class]);
  96. $this->assertTrue($app->bound('foo'));
  97. $one = $app->make('foo');
  98. $two = $app->make('foo');
  99. $this->assertInstanceOf(stdClass::class, $one);
  100. $this->assertInstanceOf(stdClass::class, $two);
  101. $this->assertSame($one, $two);
  102. }
  103. public function testDeferredServicesCanBeExtended()
  104. {
  105. $app = new Application;
  106. $app->setDeferredServices(['foo' => ApplicationDeferredServiceProviderStub::class]);
  107. $app->extend('foo', function ($instance, $container) {
  108. return $instance.'bar';
  109. });
  110. $this->assertSame('foobar', $app->make('foo'));
  111. }
  112. public function testDeferredServiceProviderIsRegisteredOnlyOnce()
  113. {
  114. $app = new Application;
  115. $app->setDeferredServices(['foo' => ApplicationDeferredServiceProviderCountStub::class]);
  116. $obj = $app->make('foo');
  117. $this->assertInstanceOf(stdClass::class, $obj);
  118. $this->assertSame($obj, $app->make('foo'));
  119. $this->assertEquals(1, ApplicationDeferredServiceProviderCountStub::$count);
  120. }
  121. public function testDeferredServiceDontRunWhenInstanceSet()
  122. {
  123. $app = new Application;
  124. $app->setDeferredServices(['foo' => ApplicationDeferredServiceProviderStub::class]);
  125. $app->instance('foo', 'bar');
  126. $instance = $app->make('foo');
  127. $this->assertSame('bar', $instance);
  128. }
  129. public function testDeferredServicesAreLazilyInitialized()
  130. {
  131. ApplicationDeferredServiceProviderStub::$initialized = false;
  132. $app = new Application;
  133. $app->setDeferredServices(['foo' => ApplicationDeferredServiceProviderStub::class]);
  134. $this->assertTrue($app->bound('foo'));
  135. $this->assertFalse(ApplicationDeferredServiceProviderStub::$initialized);
  136. $app->extend('foo', function ($instance, $container) {
  137. return $instance.'bar';
  138. });
  139. $this->assertFalse(ApplicationDeferredServiceProviderStub::$initialized);
  140. $this->assertSame('foobar', $app->make('foo'));
  141. $this->assertTrue(ApplicationDeferredServiceProviderStub::$initialized);
  142. }
  143. public function testDeferredServicesCanRegisterFactories()
  144. {
  145. $app = new Application;
  146. $app->setDeferredServices(['foo' => ApplicationFactoryProviderStub::class]);
  147. $this->assertTrue($app->bound('foo'));
  148. $this->assertEquals(1, $app->make('foo'));
  149. $this->assertEquals(2, $app->make('foo'));
  150. $this->assertEquals(3, $app->make('foo'));
  151. }
  152. public function testSingleProviderCanProvideMultipleDeferredServices()
  153. {
  154. $app = new Application;
  155. $app->setDeferredServices([
  156. 'foo' => ApplicationMultiProviderStub::class,
  157. 'bar' => ApplicationMultiProviderStub::class,
  158. ]);
  159. $this->assertSame('foo', $app->make('foo'));
  160. $this->assertSame('foobar', $app->make('bar'));
  161. }
  162. public function testDeferredServiceIsLoadedWhenAccessingImplementationThroughInterface()
  163. {
  164. $app = new Application;
  165. $app->setDeferredServices([
  166. SampleInterface::class => InterfaceToImplementationDeferredServiceProvider::class,
  167. SampleImplementation::class => SampleImplementationDeferredServiceProvider::class,
  168. ]);
  169. $instance = $app->make(SampleInterface::class);
  170. $this->assertSame('foo', $instance->getPrimitive());
  171. }
  172. public function testEnvironment()
  173. {
  174. $app = new Application;
  175. $app['env'] = 'foo';
  176. $this->assertSame('foo', $app->environment());
  177. $this->assertTrue($app->environment('foo'));
  178. $this->assertTrue($app->environment('f*'));
  179. $this->assertTrue($app->environment('foo', 'bar'));
  180. $this->assertTrue($app->environment(['foo', 'bar']));
  181. $this->assertFalse($app->environment('qux'));
  182. $this->assertFalse($app->environment('q*'));
  183. $this->assertFalse($app->environment('qux', 'bar'));
  184. $this->assertFalse($app->environment(['qux', 'bar']));
  185. }
  186. public function testEnvironmentHelpers()
  187. {
  188. $local = new Application;
  189. $local['env'] = 'local';
  190. $this->assertTrue($local->isLocal());
  191. $this->assertFalse($local->isProduction());
  192. $this->assertFalse($local->runningUnitTests());
  193. $production = new Application;
  194. $production['env'] = 'production';
  195. $this->assertTrue($production->isProduction());
  196. $this->assertFalse($production->isLocal());
  197. $this->assertFalse($production->runningUnitTests());
  198. $testing = new Application;
  199. $testing['env'] = 'testing';
  200. $this->assertTrue($testing->runningUnitTests());
  201. $this->assertFalse($testing->isLocal());
  202. $this->assertFalse($testing->isProduction());
  203. }
  204. public function testDebugHelper()
  205. {
  206. $debugOff = new Application;
  207. $debugOff['config'] = new Repository(['app' => ['debug' => false]]);
  208. $this->assertFalse($debugOff->hasDebugModeEnabled());
  209. $debugOn = new Application;
  210. $debugOn['config'] = new Repository(['app' => ['debug' => true]]);
  211. $this->assertTrue($debugOn->hasDebugModeEnabled());
  212. }
  213. public function testMethodAfterLoadingEnvironmentAddsClosure()
  214. {
  215. $app = new Application;
  216. $closure = function () {
  217. //
  218. };
  219. $app->afterLoadingEnvironment($closure);
  220. $this->assertArrayHasKey(0, $app['events']->getListeners('bootstrapped: Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables'));
  221. }
  222. public function testBeforeBootstrappingAddsClosure()
  223. {
  224. $app = new Application;
  225. $closure = function () {
  226. //
  227. };
  228. $app->beforeBootstrapping(RegisterFacades::class, $closure);
  229. $this->assertArrayHasKey(0, $app['events']->getListeners('bootstrapping: Illuminate\Foundation\Bootstrap\RegisterFacades'));
  230. }
  231. public function testTerminationTests()
  232. {
  233. $app = new Application;
  234. $result = [];
  235. $callback1 = function () use (&$result) {
  236. $result[] = 1;
  237. };
  238. $callback2 = function () use (&$result) {
  239. $result[] = 2;
  240. };
  241. $callback3 = function () use (&$result) {
  242. $result[] = 3;
  243. };
  244. $app->terminating($callback1);
  245. $app->terminating($callback2);
  246. $app->terminating($callback3);
  247. $app->terminate();
  248. $this->assertEquals([1, 2, 3], $result);
  249. }
  250. public function testAfterBootstrappingAddsClosure()
  251. {
  252. $app = new Application;
  253. $closure = function () {
  254. //
  255. };
  256. $app->afterBootstrapping(RegisterFacades::class, $closure);
  257. $this->assertArrayHasKey(0, $app['events']->getListeners('bootstrapped: Illuminate\Foundation\Bootstrap\RegisterFacades'));
  258. }
  259. public function testTerminationCallbacksCanAcceptAtNotation()
  260. {
  261. $app = new Application;
  262. $app->terminating(ConcreteTerminator::class.'@terminate');
  263. $app->terminate();
  264. $this->assertEquals(1, ConcreteTerminator::$counter);
  265. }
  266. public function testBootingCallbacks()
  267. {
  268. $application = new Application;
  269. $counter = 0;
  270. $closure = function ($app) use (&$counter, $application) {
  271. $counter++;
  272. $this->assertSame($application, $app);
  273. };
  274. $closure2 = function ($app) use (&$counter, $application) {
  275. $counter++;
  276. $this->assertSame($application, $app);
  277. };
  278. $application->booting($closure);
  279. $application->booting($closure2);
  280. $application->boot();
  281. $this->assertEquals(2, $counter);
  282. }
  283. public function testBootedCallbacks()
  284. {
  285. $application = new Application;
  286. $counter = 0;
  287. $closure = function ($app) use (&$counter, $application) {
  288. $counter++;
  289. $this->assertSame($application, $app);
  290. };
  291. $closure2 = function ($app) use (&$counter, $application) {
  292. $counter++;
  293. $this->assertSame($application, $app);
  294. };
  295. $closure3 = function ($app) use (&$counter, $application) {
  296. $counter++;
  297. $this->assertSame($application, $app);
  298. };
  299. $application->booting($closure);
  300. $application->booted($closure);
  301. $application->booted($closure2);
  302. $application->boot();
  303. $this->assertEquals(3, $counter);
  304. $application->booted($closure3);
  305. $this->assertEquals(4, $counter);
  306. }
  307. public function testGetNamespace()
  308. {
  309. $app1 = new Application(realpath(__DIR__.'/fixtures/laravel1'));
  310. $app2 = new Application(realpath(__DIR__.'/fixtures/laravel2'));
  311. $this->assertSame('Laravel\\One\\', $app1->getNamespace());
  312. $this->assertSame('Laravel\\Two\\', $app2->getNamespace());
  313. }
  314. public function testCachePathsResolveToBootstrapCacheDirectory()
  315. {
  316. $app = new Application('/base/path');
  317. $ds = DIRECTORY_SEPARATOR;
  318. $this->assertSame('/base/path'.$ds.'bootstrap'.$ds.'cache/services.php', $app->getCachedServicesPath());
  319. $this->assertSame('/base/path'.$ds.'bootstrap'.$ds.'cache/packages.php', $app->getCachedPackagesPath());
  320. $this->assertSame('/base/path'.$ds.'bootstrap'.$ds.'cache/config.php', $app->getCachedConfigPath());
  321. $this->assertSame('/base/path'.$ds.'bootstrap'.$ds.'cache/routes-v7.php', $app->getCachedRoutesPath());
  322. $this->assertSame('/base/path'.$ds.'bootstrap'.$ds.'cache/events.php', $app->getCachedEventsPath());
  323. }
  324. public function testEnvPathsAreUsedForCachePathsWhenSpecified()
  325. {
  326. $app = new Application('/base/path');
  327. $_SERVER['APP_SERVICES_CACHE'] = '/absolute/path/services.php';
  328. $_SERVER['APP_PACKAGES_CACHE'] = '/absolute/path/packages.php';
  329. $_SERVER['APP_CONFIG_CACHE'] = '/absolute/path/config.php';
  330. $_SERVER['APP_ROUTES_CACHE'] = '/absolute/path/routes.php';
  331. $_SERVER['APP_EVENTS_CACHE'] = '/absolute/path/events.php';
  332. $this->assertSame('/absolute/path/services.php', $app->getCachedServicesPath());
  333. $this->assertSame('/absolute/path/packages.php', $app->getCachedPackagesPath());
  334. $this->assertSame('/absolute/path/config.php', $app->getCachedConfigPath());
  335. $this->assertSame('/absolute/path/routes.php', $app->getCachedRoutesPath());
  336. $this->assertSame('/absolute/path/events.php', $app->getCachedEventsPath());
  337. unset(
  338. $_SERVER['APP_SERVICES_CACHE'],
  339. $_SERVER['APP_PACKAGES_CACHE'],
  340. $_SERVER['APP_CONFIG_CACHE'],
  341. $_SERVER['APP_ROUTES_CACHE'],
  342. $_SERVER['APP_EVENTS_CACHE']
  343. );
  344. }
  345. public function testEnvPathsAreUsedAndMadeAbsoluteForCachePathsWhenSpecifiedAsRelative()
  346. {
  347. $app = new Application('/base/path');
  348. $_SERVER['APP_SERVICES_CACHE'] = 'relative/path/services.php';
  349. $_SERVER['APP_PACKAGES_CACHE'] = 'relative/path/packages.php';
  350. $_SERVER['APP_CONFIG_CACHE'] = 'relative/path/config.php';
  351. $_SERVER['APP_ROUTES_CACHE'] = 'relative/path/routes.php';
  352. $_SERVER['APP_EVENTS_CACHE'] = 'relative/path/events.php';
  353. $ds = DIRECTORY_SEPARATOR;
  354. $this->assertSame('/base/path'.$ds.'relative/path/services.php', $app->getCachedServicesPath());
  355. $this->assertSame('/base/path'.$ds.'relative/path/packages.php', $app->getCachedPackagesPath());
  356. $this->assertSame('/base/path'.$ds.'relative/path/config.php', $app->getCachedConfigPath());
  357. $this->assertSame('/base/path'.$ds.'relative/path/routes.php', $app->getCachedRoutesPath());
  358. $this->assertSame('/base/path'.$ds.'relative/path/events.php', $app->getCachedEventsPath());
  359. unset(
  360. $_SERVER['APP_SERVICES_CACHE'],
  361. $_SERVER['APP_PACKAGES_CACHE'],
  362. $_SERVER['APP_CONFIG_CACHE'],
  363. $_SERVER['APP_ROUTES_CACHE'],
  364. $_SERVER['APP_EVENTS_CACHE']
  365. );
  366. }
  367. public function testEnvPathsAreUsedAndMadeAbsoluteForCachePathsWhenSpecifiedAsRelativeWithNullBasePath()
  368. {
  369. $app = new Application;
  370. $_SERVER['APP_SERVICES_CACHE'] = 'relative/path/services.php';
  371. $_SERVER['APP_PACKAGES_CACHE'] = 'relative/path/packages.php';
  372. $_SERVER['APP_CONFIG_CACHE'] = 'relative/path/config.php';
  373. $_SERVER['APP_ROUTES_CACHE'] = 'relative/path/routes.php';
  374. $_SERVER['APP_EVENTS_CACHE'] = 'relative/path/events.php';
  375. $ds = DIRECTORY_SEPARATOR;
  376. $this->assertSame($ds.'relative/path/services.php', $app->getCachedServicesPath());
  377. $this->assertSame($ds.'relative/path/packages.php', $app->getCachedPackagesPath());
  378. $this->assertSame($ds.'relative/path/config.php', $app->getCachedConfigPath());
  379. $this->assertSame($ds.'relative/path/routes.php', $app->getCachedRoutesPath());
  380. $this->assertSame($ds.'relative/path/events.php', $app->getCachedEventsPath());
  381. unset(
  382. $_SERVER['APP_SERVICES_CACHE'],
  383. $_SERVER['APP_PACKAGES_CACHE'],
  384. $_SERVER['APP_CONFIG_CACHE'],
  385. $_SERVER['APP_ROUTES_CACHE'],
  386. $_SERVER['APP_EVENTS_CACHE']
  387. );
  388. }
  389. public function testEnvPathsAreAbsoluteInWindows()
  390. {
  391. $app = new Application(__DIR__);
  392. $app->addAbsoluteCachePathPrefix('C:');
  393. $_SERVER['APP_SERVICES_CACHE'] = 'C:\framework\services.php';
  394. $_SERVER['APP_PACKAGES_CACHE'] = 'C:\framework\packages.php';
  395. $_SERVER['APP_CONFIG_CACHE'] = 'C:\framework\config.php';
  396. $_SERVER['APP_ROUTES_CACHE'] = 'C:\framework\routes.php';
  397. $_SERVER['APP_EVENTS_CACHE'] = 'C:\framework\events.php';
  398. $this->assertSame('C:\framework\services.php', $app->getCachedServicesPath());
  399. $this->assertSame('C:\framework\packages.php', $app->getCachedPackagesPath());
  400. $this->assertSame('C:\framework\config.php', $app->getCachedConfigPath());
  401. $this->assertSame('C:\framework\routes.php', $app->getCachedRoutesPath());
  402. $this->assertSame('C:\framework\events.php', $app->getCachedEventsPath());
  403. unset(
  404. $_SERVER['APP_SERVICES_CACHE'],
  405. $_SERVER['APP_PACKAGES_CACHE'],
  406. $_SERVER['APP_CONFIG_CACHE'],
  407. $_SERVER['APP_ROUTES_CACHE'],
  408. $_SERVER['APP_EVENTS_CACHE']
  409. );
  410. }
  411. }
  412. class ApplicationBasicServiceProviderStub extends ServiceProvider
  413. {
  414. public function boot()
  415. {
  416. //
  417. }
  418. public function register()
  419. {
  420. //
  421. }
  422. }
  423. class ApplicationDeferredSharedServiceProviderStub extends ServiceProvider implements DeferrableProvider
  424. {
  425. public function register()
  426. {
  427. $this->app->singleton('foo', function () {
  428. return new stdClass;
  429. });
  430. }
  431. }
  432. class ApplicationDeferredServiceProviderCountStub extends ServiceProvider implements DeferrableProvider
  433. {
  434. public static $count = 0;
  435. public function register()
  436. {
  437. static::$count++;
  438. $this->app['foo'] = new stdClass;
  439. }
  440. }
  441. class ApplicationDeferredServiceProviderStub extends ServiceProvider implements DeferrableProvider
  442. {
  443. public static $initialized = false;
  444. public function register()
  445. {
  446. static::$initialized = true;
  447. $this->app['foo'] = 'foo';
  448. }
  449. }
  450. interface SampleInterface
  451. {
  452. public function getPrimitive();
  453. }
  454. class SampleImplementation implements SampleInterface
  455. {
  456. private $primitive;
  457. public function __construct($primitive)
  458. {
  459. $this->primitive = $primitive;
  460. }
  461. public function getPrimitive()
  462. {
  463. return $this->primitive;
  464. }
  465. }
  466. class InterfaceToImplementationDeferredServiceProvider extends ServiceProvider implements DeferrableProvider
  467. {
  468. public function register()
  469. {
  470. $this->app->bind(SampleInterface::class, SampleImplementation::class);
  471. }
  472. }
  473. class SampleImplementationDeferredServiceProvider extends ServiceProvider implements DeferrableProvider
  474. {
  475. public function register()
  476. {
  477. $this->app->when(SampleImplementation::class)->needs('$primitive')->give(function () {
  478. return 'foo';
  479. });
  480. }
  481. }
  482. class ApplicationFactoryProviderStub extends ServiceProvider implements DeferrableProvider
  483. {
  484. public function register()
  485. {
  486. $this->app->bind('foo', function () {
  487. static $count = 0;
  488. return ++$count;
  489. });
  490. }
  491. }
  492. class ApplicationMultiProviderStub extends ServiceProvider implements DeferrableProvider
  493. {
  494. public function register()
  495. {
  496. $this->app->singleton('foo', function () {
  497. return 'foo';
  498. });
  499. $this->app->singleton('bar', function ($app) {
  500. return $app['foo'].'bar';
  501. });
  502. }
  503. }
  504. abstract class AbstractClass
  505. {
  506. //
  507. }
  508. class ConcreteClass extends AbstractClass
  509. {
  510. //
  511. }
  512. class ConcreteTerminator
  513. {
  514. public static $counter = 0;
  515. public function terminate()
  516. {
  517. return self::$counter++;
  518. }
  519. }