Application.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\base;
  8. use Yii;
  9. /**
  10. * Application is the base class for all application classes.
  11. *
  12. * For more details and usage information on Application, see the [guide article on applications](guide:structure-applications).
  13. *
  14. * @property-read \yii\web\AssetManager $assetManager The asset manager application component. This property
  15. * is read-only.
  16. * @property-read \yii\rbac\ManagerInterface $authManager The auth manager application component. Null is
  17. * returned if auth manager is not configured. This property is read-only.
  18. * @property string $basePath The root directory of the application.
  19. * @property-read \yii\caching\CacheInterface $cache The cache application component. Null if the component is
  20. * not enabled. This property is read-only.
  21. * @property-write array $container Values given in terms of name-value pairs. This property is write-only.
  22. * @property-read \yii\db\Connection $db The database connection. This property is read-only.
  23. * @property-read \yii\web\ErrorHandler|\yii\console\ErrorHandler $errorHandler The error handler application
  24. * component. This property is read-only.
  25. * @property-read \yii\i18n\Formatter $formatter The formatter application component. This property is
  26. * read-only.
  27. * @property-read \yii\i18n\I18N $i18n The internationalization application component. This property is
  28. * read-only.
  29. * @property-read \yii\log\Dispatcher $log The log dispatcher application component. This property is
  30. * read-only.
  31. * @property-read \yii\mail\MailerInterface $mailer The mailer application component. This property is
  32. * read-only.
  33. * @property-read \yii\web\Request|\yii\console\Request $request The request component. This property is
  34. * read-only.
  35. * @property-read \yii\web\Response|\yii\console\Response $response The response component. This property is
  36. * read-only.
  37. * @property string $runtimePath The directory that stores runtime files. Defaults to the "runtime"
  38. * subdirectory under [[basePath]].
  39. * @property-read \yii\base\Security $security The security application component. This property is read-only.
  40. * @property string $timeZone The time zone used by this application.
  41. * @property-read string $uniqueId The unique ID of the module. This property is read-only.
  42. * @property-read \yii\web\UrlManager $urlManager The URL manager for this application. This property is
  43. * read-only.
  44. * @property string $vendorPath The directory that stores vendor files. Defaults to "vendor" directory under
  45. * [[basePath]].
  46. * @property-read View|\yii\web\View $view The view application component that is used to render various view
  47. * files. This property is read-only.
  48. *
  49. * @author Qiang Xue <qiang.xue@gmail.com>
  50. * @since 2.0
  51. */
  52. abstract class Application extends Module
  53. {
  54. /**
  55. * @event Event an event raised before the application starts to handle a request.
  56. */
  57. const EVENT_BEFORE_REQUEST = 'beforeRequest';
  58. /**
  59. * @event Event an event raised after the application successfully handles a request (before the response is sent out).
  60. */
  61. const EVENT_AFTER_REQUEST = 'afterRequest';
  62. /**
  63. * Application state used by [[state]]: application just started.
  64. */
  65. const STATE_BEGIN = 0;
  66. /**
  67. * Application state used by [[state]]: application is initializing.
  68. */
  69. const STATE_INIT = 1;
  70. /**
  71. * Application state used by [[state]]: application is triggering [[EVENT_BEFORE_REQUEST]].
  72. */
  73. const STATE_BEFORE_REQUEST = 2;
  74. /**
  75. * Application state used by [[state]]: application is handling the request.
  76. */
  77. const STATE_HANDLING_REQUEST = 3;
  78. /**
  79. * Application state used by [[state]]: application is triggering [[EVENT_AFTER_REQUEST]]..
  80. */
  81. const STATE_AFTER_REQUEST = 4;
  82. /**
  83. * Application state used by [[state]]: application is about to send response.
  84. */
  85. const STATE_SENDING_RESPONSE = 5;
  86. /**
  87. * Application state used by [[state]]: application has ended.
  88. */
  89. const STATE_END = 6;
  90. /**
  91. * @var string the namespace that controller classes are located in.
  92. * This namespace will be used to load controller classes by prepending it to the controller class name.
  93. * The default namespace is `app\controllers`.
  94. *
  95. * Please refer to the [guide about class autoloading](guide:concept-autoloading.md) for more details.
  96. */
  97. public $controllerNamespace = 'app\\controllers';
  98. /**
  99. * @var string the application name.
  100. */
  101. public $name = 'My Application';
  102. /**
  103. * @var string the charset currently used for the application.
  104. */
  105. public $charset = 'UTF-8';
  106. /**
  107. * @var string the language that is meant to be used for end users. It is recommended that you
  108. * use [IETF language tags](http://en.wikipedia.org/wiki/IETF_language_tag). For example, `en` stands
  109. * for English, while `en-US` stands for English (United States).
  110. * @see sourceLanguage
  111. */
  112. public $language = 'en-US';
  113. /**
  114. * @var string the language that the application is written in. This mainly refers to
  115. * the language that the messages and view files are written in.
  116. * @see language
  117. */
  118. public $sourceLanguage = 'en-US';
  119. /**
  120. * @var Controller the currently active controller instance
  121. */
  122. public $controller;
  123. /**
  124. * @var string|bool the layout that should be applied for views in this application. Defaults to 'main'.
  125. * If this is false, layout will be disabled.
  126. */
  127. public $layout = 'main';
  128. /**
  129. * @var string the requested route
  130. */
  131. public $requestedRoute;
  132. /**
  133. * @var Action the requested Action. If null, it means the request cannot be resolved into an action.
  134. */
  135. public $requestedAction;
  136. /**
  137. * @var array the parameters supplied to the requested action.
  138. */
  139. public $requestedParams;
  140. /**
  141. * @var array list of installed Yii extensions. Each array element represents a single extension
  142. * with the following structure:
  143. *
  144. * ```php
  145. * [
  146. * 'name' => 'extension name',
  147. * 'version' => 'version number',
  148. * 'bootstrap' => 'BootstrapClassName', // optional, may also be a configuration array
  149. * 'alias' => [
  150. * '@alias1' => 'to/path1',
  151. * '@alias2' => 'to/path2',
  152. * ],
  153. * ]
  154. * ```
  155. *
  156. * The "bootstrap" class listed above will be instantiated during the application
  157. * [[bootstrap()|bootstrapping process]]. If the class implements [[BootstrapInterface]],
  158. * its [[BootstrapInterface::bootstrap()|bootstrap()]] method will be also be called.
  159. *
  160. * If not set explicitly in the application config, this property will be populated with the contents of
  161. * `@vendor/yiisoft/extensions.php`.
  162. */
  163. public $extensions;
  164. /**
  165. * @var array list of components that should be run during the application [[bootstrap()|bootstrapping process]].
  166. *
  167. * Each component may be specified in one of the following formats:
  168. *
  169. * - an application component ID as specified via [[components]].
  170. * - a module ID as specified via [[modules]].
  171. * - a class name.
  172. * - a configuration array.
  173. * - a Closure
  174. *
  175. * During the bootstrapping process, each component will be instantiated. If the component class
  176. * implements [[BootstrapInterface]], its [[BootstrapInterface::bootstrap()|bootstrap()]] method
  177. * will be also be called.
  178. */
  179. public $bootstrap = [];
  180. /**
  181. * @var int the current application state during a request handling life cycle.
  182. * This property is managed by the application. Do not modify this property.
  183. */
  184. public $state;
  185. /**
  186. * @var array list of loaded modules indexed by their class names.
  187. */
  188. public $loadedModules = [];
  189. /**
  190. * Constructor.
  191. * @param array $config name-value pairs that will be used to initialize the object properties.
  192. * Note that the configuration must contain both [[id]] and [[basePath]].
  193. * @throws InvalidConfigException if either [[id]] or [[basePath]] configuration is missing.
  194. */
  195. public function __construct($config = [])
  196. {
  197. Yii::$app = $this;
  198. static::setInstance($this);
  199. $this->state = self::STATE_BEGIN;
  200. $this->preInit($config);
  201. $this->registerErrorHandler($config);
  202. Component::__construct($config);
  203. }
  204. /**
  205. * Pre-initializes the application.
  206. * This method is called at the beginning of the application constructor.
  207. * It initializes several important application properties.
  208. * If you override this method, please make sure you call the parent implementation.
  209. * @param array $config the application configuration
  210. * @throws InvalidConfigException if either [[id]] or [[basePath]] configuration is missing.
  211. */
  212. public function preInit(&$config)
  213. {
  214. if (!isset($config['id'])) {
  215. throw new InvalidConfigException('The "id" configuration for the Application is required.');
  216. }
  217. if (isset($config['basePath'])) {
  218. $this->setBasePath($config['basePath']);
  219. unset($config['basePath']);
  220. } else {
  221. throw new InvalidConfigException('The "basePath" configuration for the Application is required.');
  222. }
  223. if (isset($config['vendorPath'])) {
  224. $this->setVendorPath($config['vendorPath']);
  225. unset($config['vendorPath']);
  226. } else {
  227. // set "@vendor"
  228. $this->getVendorPath();
  229. }
  230. if (isset($config['runtimePath'])) {
  231. $this->setRuntimePath($config['runtimePath']);
  232. unset($config['runtimePath']);
  233. } else {
  234. // set "@runtime"
  235. $this->getRuntimePath();
  236. }
  237. if (isset($config['timeZone'])) {
  238. $this->setTimeZone($config['timeZone']);
  239. unset($config['timeZone']);
  240. } elseif (!ini_get('date.timezone')) {
  241. $this->setTimeZone('UTC');
  242. }
  243. if (isset($config['container'])) {
  244. $this->setContainer($config['container']);
  245. unset($config['container']);
  246. }
  247. // merge core components with custom components
  248. foreach ($this->coreComponents() as $id => $component) {
  249. if (!isset($config['components'][$id])) {
  250. $config['components'][$id] = $component;
  251. } elseif (is_array($config['components'][$id]) && !isset($config['components'][$id]['class'])) {
  252. $config['components'][$id]['class'] = $component['class'];
  253. }
  254. }
  255. }
  256. /**
  257. * {@inheritdoc}
  258. */
  259. public function init()
  260. {
  261. $this->state = self::STATE_INIT;
  262. $this->bootstrap();
  263. }
  264. /**
  265. * Initializes extensions and executes bootstrap components.
  266. * This method is called by [[init()]] after the application has been fully configured.
  267. * If you override this method, make sure you also call the parent implementation.
  268. */
  269. protected function bootstrap()
  270. {
  271. if ($this->extensions === null) {
  272. $file = Yii::getAlias('@vendor/yiisoft/extensions.php');
  273. $this->extensions = is_file($file) ? include $file : [];
  274. }
  275. foreach ($this->extensions as $extension) {
  276. if (!empty($extension['alias'])) {
  277. foreach ($extension['alias'] as $name => $path) {
  278. Yii::setAlias($name, $path);
  279. }
  280. }
  281. if (isset($extension['bootstrap'])) {
  282. $component = Yii::createObject($extension['bootstrap']);
  283. if ($component instanceof BootstrapInterface) {
  284. Yii::debug('Bootstrap with ' . get_class($component) . '::bootstrap()', __METHOD__);
  285. $component->bootstrap($this);
  286. } else {
  287. Yii::debug('Bootstrap with ' . get_class($component), __METHOD__);
  288. }
  289. }
  290. }
  291. foreach ($this->bootstrap as $mixed) {
  292. $component = null;
  293. if ($mixed instanceof \Closure) {
  294. Yii::debug('Bootstrap with Closure', __METHOD__);
  295. if (!$component = call_user_func($mixed, $this)) {
  296. continue;
  297. }
  298. } elseif (is_string($mixed)) {
  299. if ($this->has($mixed)) {
  300. $component = $this->get($mixed);
  301. } elseif ($this->hasModule($mixed)) {
  302. $component = $this->getModule($mixed);
  303. } elseif (strpos($mixed, '\\') === false) {
  304. throw new InvalidConfigException("Unknown bootstrapping component ID: $mixed");
  305. }
  306. }
  307. if (!isset($component)) {
  308. $component = Yii::createObject($mixed);
  309. }
  310. if ($component instanceof BootstrapInterface) {
  311. Yii::debug('Bootstrap with ' . get_class($component) . '::bootstrap()', __METHOD__);
  312. $component->bootstrap($this);
  313. } else {
  314. Yii::debug('Bootstrap with ' . get_class($component), __METHOD__);
  315. }
  316. }
  317. }
  318. /**
  319. * Registers the errorHandler component as a PHP error handler.
  320. * @param array $config application config
  321. */
  322. protected function registerErrorHandler(&$config)
  323. {
  324. if (YII_ENABLE_ERROR_HANDLER) {
  325. if (!isset($config['components']['errorHandler']['class'])) {
  326. echo "Error: no errorHandler component is configured.\n";
  327. exit(1);
  328. }
  329. $this->set('errorHandler', $config['components']['errorHandler']);
  330. unset($config['components']['errorHandler']);
  331. $this->getErrorHandler()->register();
  332. }
  333. }
  334. /**
  335. * Returns an ID that uniquely identifies this module among all modules within the current application.
  336. * Since this is an application instance, it will always return an empty string.
  337. * @return string the unique ID of the module.
  338. */
  339. public function getUniqueId()
  340. {
  341. return '';
  342. }
  343. /**
  344. * Sets the root directory of the application and the @app alias.
  345. * This method can only be invoked at the beginning of the constructor.
  346. * @param string $path the root directory of the application.
  347. * @property string the root directory of the application.
  348. * @throws InvalidArgumentException if the directory does not exist.
  349. */
  350. public function setBasePath($path)
  351. {
  352. parent::setBasePath($path);
  353. Yii::setAlias('@app', $this->getBasePath());
  354. }
  355. /**
  356. * Runs the application.
  357. * This is the main entrance of an application.
  358. * @return int the exit status (0 means normal, non-zero values mean abnormal)
  359. */
  360. public function run()
  361. {
  362. try {
  363. $this->state = self::STATE_BEFORE_REQUEST;
  364. $this->trigger(self::EVENT_BEFORE_REQUEST);
  365. $this->state = self::STATE_HANDLING_REQUEST;
  366. $response = $this->handleRequest($this->getRequest());
  367. $this->state = self::STATE_AFTER_REQUEST;
  368. $this->trigger(self::EVENT_AFTER_REQUEST);
  369. $this->state = self::STATE_SENDING_RESPONSE;
  370. $response->send();
  371. $this->state = self::STATE_END;
  372. return $response->exitStatus;
  373. } catch (ExitException $e) {
  374. $this->end($e->statusCode, isset($response) ? $response : null);
  375. return $e->statusCode;
  376. }
  377. }
  378. /**
  379. * Handles the specified request.
  380. *
  381. * This method should return an instance of [[Response]] or its child class
  382. * which represents the handling result of the request.
  383. *
  384. * @param Request $request the request to be handled
  385. * @return Response the resulting response
  386. */
  387. abstract public function handleRequest($request);
  388. private $_runtimePath;
  389. /**
  390. * Returns the directory that stores runtime files.
  391. * @return string the directory that stores runtime files.
  392. * Defaults to the "runtime" subdirectory under [[basePath]].
  393. */
  394. public function getRuntimePath()
  395. {
  396. if ($this->_runtimePath === null) {
  397. $this->setRuntimePath($this->getBasePath() . DIRECTORY_SEPARATOR . 'runtime');
  398. }
  399. return $this->_runtimePath;
  400. }
  401. /**
  402. * Sets the directory that stores runtime files.
  403. * @param string $path the directory that stores runtime files.
  404. */
  405. public function setRuntimePath($path)
  406. {
  407. $this->_runtimePath = Yii::getAlias($path);
  408. Yii::setAlias('@runtime', $this->_runtimePath);
  409. }
  410. private $_vendorPath;
  411. /**
  412. * Returns the directory that stores vendor files.
  413. * @return string the directory that stores vendor files.
  414. * Defaults to "vendor" directory under [[basePath]].
  415. */
  416. public function getVendorPath()
  417. {
  418. if ($this->_vendorPath === null) {
  419. $this->setVendorPath($this->getBasePath() . DIRECTORY_SEPARATOR . 'vendor');
  420. }
  421. return $this->_vendorPath;
  422. }
  423. /**
  424. * Sets the directory that stores vendor files.
  425. * @param string $path the directory that stores vendor files.
  426. */
  427. public function setVendorPath($path)
  428. {
  429. $this->_vendorPath = Yii::getAlias($path);
  430. Yii::setAlias('@vendor', $this->_vendorPath);
  431. Yii::setAlias('@bower', $this->_vendorPath . DIRECTORY_SEPARATOR . 'bower');
  432. Yii::setAlias('@npm', $this->_vendorPath . DIRECTORY_SEPARATOR . 'npm');
  433. }
  434. /**
  435. * Returns the time zone used by this application.
  436. * This is a simple wrapper of PHP function date_default_timezone_get().
  437. * If time zone is not configured in php.ini or application config,
  438. * it will be set to UTC by default.
  439. * @return string the time zone used by this application.
  440. * @see https://secure.php.net/manual/en/function.date-default-timezone-get.php
  441. */
  442. public function getTimeZone()
  443. {
  444. return date_default_timezone_get();
  445. }
  446. /**
  447. * Sets the time zone used by this application.
  448. * This is a simple wrapper of PHP function date_default_timezone_set().
  449. * Refer to the [php manual](https://secure.php.net/manual/en/timezones.php) for available timezones.
  450. * @param string $value the time zone used by this application.
  451. * @see https://secure.php.net/manual/en/function.date-default-timezone-set.php
  452. */
  453. public function setTimeZone($value)
  454. {
  455. date_default_timezone_set($value);
  456. }
  457. /**
  458. * Returns the database connection component.
  459. * @return \yii\db\Connection the database connection.
  460. */
  461. public function getDb()
  462. {
  463. return $this->get('db');
  464. }
  465. /**
  466. * Returns the log dispatcher component.
  467. * @return \yii\log\Dispatcher the log dispatcher application component.
  468. */
  469. public function getLog()
  470. {
  471. return $this->get('log');
  472. }
  473. /**
  474. * Returns the error handler component.
  475. * @return \yii\web\ErrorHandler|\yii\console\ErrorHandler the error handler application component.
  476. */
  477. public function getErrorHandler()
  478. {
  479. return $this->get('errorHandler');
  480. }
  481. /**
  482. * Returns the cache component.
  483. * @return \yii\caching\CacheInterface the cache application component. Null if the component is not enabled.
  484. */
  485. public function getCache()
  486. {
  487. return $this->get('cache', false);
  488. }
  489. /**
  490. * Returns the formatter component.
  491. * @return \yii\i18n\Formatter the formatter application component.
  492. */
  493. public function getFormatter()
  494. {
  495. return $this->get('formatter');
  496. }
  497. /**
  498. * Returns the request component.
  499. * @return \yii\web\Request|\yii\console\Request the request component.
  500. */
  501. public function getRequest()
  502. {
  503. return $this->get('request');
  504. }
  505. /**
  506. * Returns the response component.
  507. * @return \yii\web\Response|\yii\console\Response the response component.
  508. */
  509. public function getResponse()
  510. {
  511. return $this->get('response');
  512. }
  513. /**
  514. * Returns the view object.
  515. * @return View|\yii\web\View the view application component that is used to render various view files.
  516. */
  517. public function getView()
  518. {
  519. return $this->get('view');
  520. }
  521. /**
  522. * Returns the URL manager for this application.
  523. * @return \yii\web\UrlManager the URL manager for this application.
  524. */
  525. public function getUrlManager()
  526. {
  527. return $this->get('urlManager');
  528. }
  529. /**
  530. * Returns the internationalization (i18n) component.
  531. * @return \yii\i18n\I18N the internationalization application component.
  532. */
  533. public function getI18n()
  534. {
  535. return $this->get('i18n');
  536. }
  537. /**
  538. * Returns the mailer component.
  539. * @return \yii\mail\MailerInterface the mailer application component.
  540. */
  541. public function getMailer()
  542. {
  543. return $this->get('mailer');
  544. }
  545. /**
  546. * Returns the auth manager for this application.
  547. * @return \yii\rbac\ManagerInterface the auth manager application component.
  548. * Null is returned if auth manager is not configured.
  549. */
  550. public function getAuthManager()
  551. {
  552. return $this->get('authManager', false);
  553. }
  554. /**
  555. * Returns the asset manager.
  556. * @return \yii\web\AssetManager the asset manager application component.
  557. */
  558. public function getAssetManager()
  559. {
  560. return $this->get('assetManager');
  561. }
  562. /**
  563. * Returns the security component.
  564. * @return \yii\base\Security the security application component.
  565. */
  566. public function getSecurity()
  567. {
  568. return $this->get('security');
  569. }
  570. /**
  571. * Returns the configuration of core application components.
  572. * @see set()
  573. */
  574. public function coreComponents()
  575. {
  576. return [
  577. 'log' => ['class' => 'yii\log\Dispatcher'],
  578. 'view' => ['class' => 'yii\web\View'],
  579. 'formatter' => ['class' => 'yii\i18n\Formatter'],
  580. 'i18n' => ['class' => 'yii\i18n\I18N'],
  581. 'mailer' => ['class' => 'yii\swiftmailer\Mailer'],
  582. 'urlManager' => ['class' => 'yii\web\UrlManager'],
  583. 'assetManager' => ['class' => 'yii\web\AssetManager'],
  584. 'security' => ['class' => 'yii\base\Security'],
  585. ];
  586. }
  587. /**
  588. * Terminates the application.
  589. * This method replaces the `exit()` function by ensuring the application life cycle is completed
  590. * before terminating the application.
  591. * @param int $status the exit status (value 0 means normal exit while other values mean abnormal exit).
  592. * @param Response $response the response to be sent. If not set, the default application [[response]] component will be used.
  593. * @throws ExitException if the application is in testing mode
  594. */
  595. public function end($status = 0, $response = null)
  596. {
  597. if ($this->state === self::STATE_BEFORE_REQUEST || $this->state === self::STATE_HANDLING_REQUEST) {
  598. $this->state = self::STATE_AFTER_REQUEST;
  599. $this->trigger(self::EVENT_AFTER_REQUEST);
  600. }
  601. if ($this->state !== self::STATE_SENDING_RESPONSE && $this->state !== self::STATE_END) {
  602. $this->state = self::STATE_END;
  603. $response = $response ?: $this->getResponse();
  604. $response->send();
  605. }
  606. if (YII_ENV_TEST) {
  607. throw new ExitException($status);
  608. }
  609. exit($status);
  610. }
  611. /**
  612. * Configures [[Yii::$container]] with the $config.
  613. *
  614. * @param array $config values given in terms of name-value pairs
  615. * @since 2.0.11
  616. */
  617. public function setContainer($config)
  618. {
  619. Yii::configure(Yii::$container, $config);
  620. }
  621. }