BaseApplication.php 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice.
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 3.3.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Http;
  17. use Cake\Console\CommandCollection;
  18. use Cake\Controller\ControllerFactory;
  19. use Cake\Core\ConsoleApplicationInterface;
  20. use Cake\Core\Container;
  21. use Cake\Core\ContainerApplicationInterface;
  22. use Cake\Core\ContainerInterface;
  23. use Cake\Core\Exception\MissingPluginException;
  24. use Cake\Core\HttpApplicationInterface;
  25. use Cake\Core\Plugin;
  26. use Cake\Core\PluginApplicationInterface;
  27. use Cake\Core\PluginCollection;
  28. use Cake\Core\PluginInterface;
  29. use Cake\Event\EventDispatcherInterface;
  30. use Cake\Event\EventDispatcherTrait;
  31. use Cake\Event\EventManager;
  32. use Cake\Event\EventManagerInterface;
  33. use Cake\Routing\RouteBuilder;
  34. use Cake\Routing\Router;
  35. use Cake\Routing\RoutingApplicationInterface;
  36. use Closure;
  37. use Psr\Http\Message\ResponseInterface;
  38. use Psr\Http\Message\ServerRequestInterface;
  39. /**
  40. * Base class for full-stack applications
  41. *
  42. * This class serves as a base class for applications that are using
  43. * CakePHP as a full stack framework. If you are only using the Http or Console libraries
  44. * you should implement the relevant interfaces directly.
  45. *
  46. * The application class is responsible for bootstrapping the application,
  47. * and ensuring that middleware is attached. It is also invoked as the last piece
  48. * of middleware, and delegates request/response handling to the correct controller.
  49. */
  50. abstract class BaseApplication implements
  51. ConsoleApplicationInterface,
  52. ContainerApplicationInterface,
  53. EventDispatcherInterface,
  54. HttpApplicationInterface,
  55. PluginApplicationInterface,
  56. RoutingApplicationInterface
  57. {
  58. use EventDispatcherTrait;
  59. /**
  60. * @var string Contains the path of the config directory
  61. */
  62. protected string $configDir;
  63. /**
  64. * Plugin Collection
  65. *
  66. * @var \Cake\Core\PluginCollection
  67. */
  68. protected PluginCollection $plugins;
  69. /**
  70. * Controller factory
  71. *
  72. * @var \Cake\Http\ControllerFactoryInterface|null
  73. */
  74. protected ?ControllerFactoryInterface $controllerFactory = null;
  75. /**
  76. * Container
  77. *
  78. * @var \Cake\Core\ContainerInterface|null
  79. */
  80. protected ?ContainerInterface $container = null;
  81. /**
  82. * Constructor
  83. *
  84. * @param string $configDir The directory the bootstrap configuration is held in.
  85. * @param \Cake\Event\EventManagerInterface|null $eventManager Application event manager instance.
  86. * @param \Cake\Http\ControllerFactoryInterface|null $controllerFactory Controller factory.
  87. */
  88. public function __construct(
  89. string $configDir,
  90. ?EventManagerInterface $eventManager = null,
  91. ?ControllerFactoryInterface $controllerFactory = null
  92. ) {
  93. $this->configDir = rtrim($configDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
  94. $this->plugins = Plugin::getCollection();
  95. $this->_eventManager = $eventManager ?: EventManager::instance();
  96. $this->controllerFactory = $controllerFactory;
  97. }
  98. /**
  99. * @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to set in your App Class
  100. * @return \Cake\Http\MiddlewareQueue
  101. */
  102. abstract public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue;
  103. /**
  104. * @inheritDoc
  105. */
  106. public function pluginMiddleware(MiddlewareQueue $middleware): MiddlewareQueue
  107. {
  108. foreach ($this->plugins->with('middleware') as $plugin) {
  109. $middleware = $plugin->middleware($middleware);
  110. }
  111. return $middleware;
  112. }
  113. /**
  114. * @inheritDoc
  115. */
  116. public function addPlugin($name, array $config = [])
  117. {
  118. if (is_string($name)) {
  119. $plugin = $this->plugins->create($name, $config);
  120. } else {
  121. $plugin = $name;
  122. }
  123. $this->plugins->add($plugin);
  124. return $this;
  125. }
  126. /**
  127. * Add an optional plugin
  128. *
  129. * If it isn't available, ignore it.
  130. *
  131. * @param \Cake\Core\PluginInterface|string $name The plugin name or plugin object.
  132. * @param array<string, mixed> $config The configuration data for the plugin if using a string for $name
  133. * @return $this
  134. */
  135. public function addOptionalPlugin(PluginInterface|string $name, array $config = [])
  136. {
  137. try {
  138. $this->addPlugin($name, $config);
  139. } catch (MissingPluginException) {
  140. // Do not halt if the plugin is missing
  141. }
  142. return $this;
  143. }
  144. /**
  145. * Get the plugin collection in use.
  146. *
  147. * @return \Cake\Core\PluginCollection
  148. */
  149. public function getPlugins(): PluginCollection
  150. {
  151. return $this->plugins;
  152. }
  153. /**
  154. * @inheritDoc
  155. */
  156. public function bootstrap(): void
  157. {
  158. require_once $this->configDir . 'bootstrap.php';
  159. // phpcs:ignore
  160. $plugins = @include_once $this->configDir . 'plugins.php';
  161. if (is_array($plugins)) {
  162. $this->plugins->addFromConfig($plugins);
  163. }
  164. }
  165. /**
  166. * @inheritDoc
  167. */
  168. public function pluginBootstrap(): void
  169. {
  170. foreach ($this->plugins->with('bootstrap') as $plugin) {
  171. $plugin->bootstrap($this);
  172. }
  173. }
  174. /**
  175. * {@inheritDoc}
  176. *
  177. * By default, this will load `config/routes.php` for ease of use and backwards compatibility.
  178. *
  179. * @param \Cake\Routing\RouteBuilder $routes A route builder to add routes into.
  180. * @return void
  181. */
  182. public function routes(RouteBuilder $routes): void
  183. {
  184. // Only load routes if the router is empty
  185. if (!Router::routes()) {
  186. $return = require $this->configDir . 'routes.php';
  187. if ($return instanceof Closure) {
  188. $return($routes);
  189. }
  190. }
  191. }
  192. /**
  193. * @inheritDoc
  194. */
  195. public function pluginRoutes(RouteBuilder $routes): RouteBuilder
  196. {
  197. foreach ($this->plugins->with('routes') as $plugin) {
  198. $plugin->routes($routes);
  199. }
  200. return $routes;
  201. }
  202. /**
  203. * Define the console commands for an application.
  204. *
  205. * By default, all commands in CakePHP, plugins and the application will be
  206. * loaded using conventions based names.
  207. *
  208. * @param \Cake\Console\CommandCollection $commands The CommandCollection to add commands into.
  209. * @return \Cake\Console\CommandCollection The updated collection.
  210. */
  211. public function console(CommandCollection $commands): CommandCollection
  212. {
  213. return $commands->addMany($commands->autoDiscover());
  214. }
  215. /**
  216. * @inheritDoc
  217. */
  218. public function pluginConsole(CommandCollection $commands): CommandCollection
  219. {
  220. foreach ($this->plugins->with('console') as $plugin) {
  221. $commands = $plugin->console($commands);
  222. }
  223. return $commands;
  224. }
  225. /**
  226. * Get the dependency injection container for the application.
  227. *
  228. * The first time the container is fetched it will be constructed
  229. * and stored for future calls.
  230. *
  231. * @return \Cake\Core\ContainerInterface
  232. */
  233. public function getContainer(): ContainerInterface
  234. {
  235. return $this->container ??= $this->buildContainer();
  236. }
  237. /**
  238. * Build the service container
  239. *
  240. * Override this method if you need to use a custom container or
  241. * want to change how the container is built.
  242. *
  243. * @return \Cake\Core\ContainerInterface
  244. */
  245. protected function buildContainer(): ContainerInterface
  246. {
  247. $container = new Container();
  248. $this->services($container);
  249. foreach ($this->plugins->with('services') as $plugin) {
  250. $plugin->services($container);
  251. }
  252. $event = $this->dispatchEvent('Application.buildContainer', ['container' => $container]);
  253. if ($event->getResult() instanceof ContainerInterface) {
  254. return $event->getResult();
  255. }
  256. return $container;
  257. }
  258. /**
  259. * Register application container services.
  260. *
  261. * @param \Cake\Core\ContainerInterface $container The Container to update.
  262. * @return void
  263. */
  264. public function services(ContainerInterface $container): void
  265. {
  266. }
  267. /**
  268. * Invoke the application.
  269. *
  270. * - Add the request to the container, enabling its injection into other services.
  271. * - Create the controller that will handle this request.
  272. * - Invoke the controller.
  273. *
  274. * @param \Psr\Http\Message\ServerRequestInterface $request The request
  275. * @return \Psr\Http\Message\ResponseInterface
  276. */
  277. public function handle(
  278. ServerRequestInterface $request
  279. ): ResponseInterface {
  280. $container = $this->getContainer();
  281. $container->add(ServerRequest::class, $request);
  282. $container->add(ContainerInterface::class, $container);
  283. $this->controllerFactory ??= new ControllerFactory($container);
  284. if (Router::getRequest() !== $request) {
  285. assert($request instanceof ServerRequest);
  286. Router::setRequest($request);
  287. }
  288. $controller = $this->controllerFactory->create($request);
  289. return $this->controllerFactory->invoke($controller);
  290. }
  291. }