Facade.php 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. <?php
  2. namespace Illuminate\Support\Facades;
  3. use Closure;
  4. use Illuminate\Database\Eloquent\Model;
  5. use Illuminate\Support\Arr;
  6. use Illuminate\Support\Js;
  7. use Illuminate\Support\Number;
  8. use Illuminate\Support\Str;
  9. use Illuminate\Support\Testing\Fakes\Fake;
  10. use Mockery;
  11. use Mockery\LegacyMockInterface;
  12. use RuntimeException;
  13. abstract class Facade
  14. {
  15. /**
  16. * The application instance being facaded.
  17. *
  18. * @var \Illuminate\Contracts\Foundation\Application|null
  19. */
  20. protected static $app;
  21. /**
  22. * The resolved object instances.
  23. *
  24. * @var array
  25. */
  26. protected static $resolvedInstance;
  27. /**
  28. * Indicates if the resolved instance should be cached.
  29. *
  30. * @var bool
  31. */
  32. protected static $cached = true;
  33. /**
  34. * Run a Closure when the facade has been resolved.
  35. *
  36. * @param \Closure $callback
  37. * @return void
  38. */
  39. public static function resolved(Closure $callback)
  40. {
  41. $accessor = static::getFacadeAccessor();
  42. if (static::$app->resolved($accessor) === true) {
  43. $callback(static::getFacadeRoot(), static::$app);
  44. }
  45. static::$app->afterResolving($accessor, function ($service, $app) use ($callback) {
  46. $callback($service, $app);
  47. });
  48. }
  49. /**
  50. * Convert the facade into a Mockery spy.
  51. *
  52. * @return \Mockery\MockInterface
  53. */
  54. public static function spy()
  55. {
  56. if (! static::isMock()) {
  57. $class = static::getMockableClass();
  58. return tap($class ? Mockery::spy($class) : Mockery::spy(), function ($spy) {
  59. static::swap($spy);
  60. });
  61. }
  62. }
  63. /**
  64. * Initiate a partial mock on the facade.
  65. *
  66. * @return \Mockery\MockInterface
  67. */
  68. public static function partialMock()
  69. {
  70. $name = static::getFacadeAccessor();
  71. $mock = static::isMock()
  72. ? static::$resolvedInstance[$name]
  73. : static::createFreshMockInstance();
  74. return $mock->makePartial();
  75. }
  76. /**
  77. * Initiate a mock expectation on the facade.
  78. *
  79. * @return \Mockery\Expectation
  80. */
  81. public static function shouldReceive()
  82. {
  83. $name = static::getFacadeAccessor();
  84. $mock = static::isMock()
  85. ? static::$resolvedInstance[$name]
  86. : static::createFreshMockInstance();
  87. return $mock->shouldReceive(...func_get_args());
  88. }
  89. /**
  90. * Initiate a mock expectation on the facade.
  91. *
  92. * @return \Mockery\Expectation
  93. */
  94. public static function expects()
  95. {
  96. $name = static::getFacadeAccessor();
  97. $mock = static::isMock()
  98. ? static::$resolvedInstance[$name]
  99. : static::createFreshMockInstance();
  100. return $mock->expects(...func_get_args());
  101. }
  102. /**
  103. * Create a fresh mock instance for the given class.
  104. *
  105. * @return \Mockery\MockInterface
  106. */
  107. protected static function createFreshMockInstance()
  108. {
  109. return tap(static::createMock(), function ($mock) {
  110. static::swap($mock);
  111. $mock->shouldAllowMockingProtectedMethods();
  112. });
  113. }
  114. /**
  115. * Create a fresh mock instance for the given class.
  116. *
  117. * @return \Mockery\MockInterface
  118. */
  119. protected static function createMock()
  120. {
  121. $class = static::getMockableClass();
  122. return $class ? Mockery::mock($class) : Mockery::mock();
  123. }
  124. /**
  125. * Determines whether a mock is set as the instance of the facade.
  126. *
  127. * @return bool
  128. */
  129. protected static function isMock()
  130. {
  131. $name = static::getFacadeAccessor();
  132. return isset(static::$resolvedInstance[$name]) &&
  133. static::$resolvedInstance[$name] instanceof LegacyMockInterface;
  134. }
  135. /**
  136. * Get the mockable class for the bound instance.
  137. *
  138. * @return string|null
  139. */
  140. protected static function getMockableClass()
  141. {
  142. if ($root = static::getFacadeRoot()) {
  143. return get_class($root);
  144. }
  145. }
  146. /**
  147. * Hotswap the underlying instance behind the facade.
  148. *
  149. * @param mixed $instance
  150. * @return void
  151. */
  152. public static function swap($instance)
  153. {
  154. static::$resolvedInstance[static::getFacadeAccessor()] = $instance;
  155. if (isset(static::$app)) {
  156. static::$app->instance(static::getFacadeAccessor(), $instance);
  157. }
  158. }
  159. /**
  160. * Determines whether a "fake" has been set as the facade instance.
  161. *
  162. * @return bool
  163. */
  164. protected static function isFake()
  165. {
  166. $name = static::getFacadeAccessor();
  167. return isset(static::$resolvedInstance[$name]) &&
  168. static::$resolvedInstance[$name] instanceof Fake;
  169. }
  170. /**
  171. * Get the root object behind the facade.
  172. *
  173. * @return mixed
  174. */
  175. public static function getFacadeRoot()
  176. {
  177. return static::resolveFacadeInstance(static::getFacadeAccessor());
  178. }
  179. /**
  180. * Get the registered name of the component.
  181. *
  182. * @return string
  183. *
  184. * @throws \RuntimeException
  185. */
  186. protected static function getFacadeAccessor()
  187. {
  188. throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
  189. }
  190. /**
  191. * Resolve the facade root instance from the container.
  192. *
  193. * @param string $name
  194. * @return mixed
  195. */
  196. protected static function resolveFacadeInstance($name)
  197. {
  198. if (isset(static::$resolvedInstance[$name])) {
  199. return static::$resolvedInstance[$name];
  200. }
  201. if (static::$app) {
  202. if (static::$cached) {
  203. return static::$resolvedInstance[$name] = static::$app[$name];
  204. }
  205. return static::$app[$name];
  206. }
  207. }
  208. /**
  209. * Clear a resolved facade instance.
  210. *
  211. * @param string $name
  212. * @return void
  213. */
  214. public static function clearResolvedInstance($name)
  215. {
  216. unset(static::$resolvedInstance[$name]);
  217. }
  218. /**
  219. * Clear all of the resolved instances.
  220. *
  221. * @return void
  222. */
  223. public static function clearResolvedInstances()
  224. {
  225. static::$resolvedInstance = [];
  226. }
  227. /**
  228. * Get the application default aliases.
  229. *
  230. * @return \Illuminate\Support\Collection
  231. */
  232. public static function defaultAliases()
  233. {
  234. return collect([
  235. 'App' => App::class,
  236. 'Arr' => Arr::class,
  237. 'Artisan' => Artisan::class,
  238. 'Auth' => Auth::class,
  239. 'Blade' => Blade::class,
  240. 'Broadcast' => Broadcast::class,
  241. 'Bus' => Bus::class,
  242. 'Cache' => Cache::class,
  243. 'Config' => Config::class,
  244. 'Cookie' => Cookie::class,
  245. 'Crypt' => Crypt::class,
  246. 'Date' => Date::class,
  247. 'DB' => DB::class,
  248. 'Eloquent' => Model::class,
  249. 'Event' => Event::class,
  250. 'File' => File::class,
  251. 'Gate' => Gate::class,
  252. 'Hash' => Hash::class,
  253. 'Http' => Http::class,
  254. 'Js' => Js::class,
  255. 'Lang' => Lang::class,
  256. 'Log' => Log::class,
  257. 'Mail' => Mail::class,
  258. 'Notification' => Notification::class,
  259. 'Number' => Number::class,
  260. 'Password' => Password::class,
  261. 'Process' => Process::class,
  262. 'Queue' => Queue::class,
  263. 'RateLimiter' => RateLimiter::class,
  264. 'Redirect' => Redirect::class,
  265. 'Request' => Request::class,
  266. 'Response' => Response::class,
  267. 'Route' => Route::class,
  268. 'Schema' => Schema::class,
  269. 'Session' => Session::class,
  270. 'Storage' => Storage::class,
  271. 'Str' => Str::class,
  272. 'URL' => URL::class,
  273. 'Validator' => Validator::class,
  274. 'View' => View::class,
  275. 'Vite' => Vite::class,
  276. ]);
  277. }
  278. /**
  279. * Get the application instance behind the facade.
  280. *
  281. * @return \Illuminate\Contracts\Foundation\Application|null
  282. */
  283. public static function getFacadeApplication()
  284. {
  285. return static::$app;
  286. }
  287. /**
  288. * Set the application instance.
  289. *
  290. * @param \Illuminate\Contracts\Foundation\Application|null $app
  291. * @return void
  292. */
  293. public static function setFacadeApplication($app)
  294. {
  295. static::$app = $app;
  296. }
  297. /**
  298. * Handle dynamic, static calls to the object.
  299. *
  300. * @param string $method
  301. * @param array $args
  302. * @return mixed
  303. *
  304. * @throws \RuntimeException
  305. */
  306. public static function __callStatic($method, $args)
  307. {
  308. $instance = static::getFacadeRoot();
  309. if (! $instance) {
  310. throw new RuntimeException('A facade root has not been set.');
  311. }
  312. return $instance->$method(...$args);
  313. }
  314. }