Application.php 12.8 KB
Newer Older
w  
Qiang Xue committed
1 2 3 4 5
<?php
/**
 * Application class file.
 *
 * @link http://www.yiiframework.com/
Qiang Xue committed
6
 * @copyright Copyright &copy; 2008 Yii Software LLC
w  
Qiang Xue committed
7 8 9
 * @license http://www.yiiframework.com/license/
 */

10 11
namespace yii\base;

Qiang Xue committed
12
use Yii;
Qiang Xue committed
13
use yii\util\FileHelper;
.  
Qiang Xue committed
14

w  
Qiang Xue committed
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
/**
 * Application is the base class for all application classes.
 *
 * An application serves as the global context that the user request
 * is being processed. It manages a set of application components that
 * provide specific functionalities to the whole application.
 *
 * The core application components provided by Application are the following:
 * <ul>
 * <li>{@link getErrorHandler errorHandler}: handles PHP errors and
 *   uncaught exceptions. This application component is dynamically loaded when needed.</li>
 * <li>{@link getSecurityManager securityManager}: provides security-related
 *   services, such as hashing, encryption. This application component is dynamically
 *   loaded when needed.</li>
 * <li>{@link getStatePersister statePersister}: provides global state
 *   persistence method. This application component is dynamically loaded when needed.</li>
 * <li>{@link getCache cache}: provides caching feature. This application component is
 *   disabled by default.</li>
 * </ul>
 *
Qiang Xue committed
35
 * Application will undergo the following life cycles when processing a user request:
w  
Qiang Xue committed
36 37 38 39
 * <ol>
 * <li>load application configuration;</li>
 * <li>set up class autoloader and error handling;</li>
 * <li>load static application components;</li>
40
 * <li>{@link beforeRequest}: preprocess the user request; `beforeRequest` event raised.</li>
w  
Qiang Xue committed
41
 * <li>{@link processRequest}: process the user request;</li>
42
 * <li>{@link afterRequest}: postprocess the user request; `afterRequest` event raised.</li>
w  
Qiang Xue committed
43 44 45 46 47
 * </ol>
 *
 * Starting from lifecycle 3, if a PHP error or an uncaught exception occurs,
 * the application will switch to its error handling logic and jump to step 6 afterwards.
 *
w  
Qiang Xue committed
48 49
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
w  
Qiang Xue committed
50
 */
Qiang Xue committed
51
class Application extends Module
w  
Qiang Xue committed
52
{
Qiang Xue committed
53 54
	const EVENT_BEFORE_REQUEST = 'beforeRequest';
	const EVENT_AFTER_REQUEST = 'afterRequest';
w  
Qiang Xue committed
55
	/**
Qiang Xue committed
56
	 * @var string the application name.
w  
Qiang Xue committed
57 58
	 */
	public $name = 'My Application';
Qiang Xue committed
59
	/**
Qiang Xue committed
60
	 * @var string the version of this application.
Qiang Xue committed
61 62
	 */
	public $version = '1.0';
w  
Qiang Xue committed
63
	/**
Qiang Xue committed
64
	 * @var string the charset currently used for the application.
w  
Qiang Xue committed
65 66
	 */
	public $charset = 'UTF-8';
Qiang Xue committed
67 68 69 70 71
	/**
	 * @var string the language that is meant to be used for end users.
	 * @see sourceLanguage
	 */
	public $language = 'en_US';
w  
Qiang Xue committed
72 73
	/**
	 * @var string the language that the application is written in. This mainly refers to
Qiang Xue committed
74
	 * the language that the messages and view files are written in.
.  
Qiang Xue committed
75
	 * @see language
w  
Qiang Xue committed
76
	 */
Qiang Xue committed
77
	public $sourceLanguage = 'en_US';
Qiang Xue committed
78
	/**
Qiang Xue committed
79
	 * @var array IDs of the components that need to be loaded when the application starts.
Qiang Xue committed
80
	 */
Qiang Xue committed
81
	public $preload = array();
Qiang Xue committed
82 83 84 85
	/**
	 * @var Controller the currently active controller instance
	 */
	public $controller;
Qiang Xue committed
86 87 88 89 90
	/**
	 * @var mixed the layout that should be applied for views in this application. Defaults to 'main'.
	 * If this is false, layout will be disabled.
	 */
	public $layout = 'main';
w  
Qiang Xue committed
91

Qiang Xue committed
92
	// todo
Qiang Xue committed
93 94
	public $localeDataPath = '@yii/i18n/data';

w  
Qiang Xue committed
95 96 97 98 99 100
	private $_runtimePath;
	private $_ended = false;
	private $_language;

	/**
	 * Constructor.
Qiang Xue committed
101
	 * @param string $id the ID of this application. The ID should uniquely identify the application from others.
Qiang Xue committed
102 103
	 * @param string $basePath the base path of this application. This should point to
	 * the directory containing all application logic, template and data.
Qiang Xue committed
104
	 * @param array $config name-value pairs that will be used to initialize the object properties
w  
Qiang Xue committed
105
	 */
Qiang Xue committed
106
	public function __construct($id, $basePath, $config = array())
w  
Qiang Xue committed
107
	{
Qiang Xue committed
108
		Yii::$app = $this;
Qiang Xue committed
109
		$this->id = $id;
.  
Qiang Xue committed
110
		$this->setBasePath($basePath);
Qiang Xue committed
111 112 113 114 115 116

		if (YII_ENABLE_ERROR_HANDLER) {
			set_exception_handler(array($this, 'handleException'));
			set_error_handler(array($this, 'handleError'), error_reporting());
		}

Qiang Xue committed
117
		$this->registerDefaultAliases();
w  
Qiang Xue committed
118
		$this->registerCoreComponents();
Qiang Xue committed
119

Qiang Xue committed
120
		Component::__construct($config);
.  
Qiang Xue committed
121
	}
w  
Qiang Xue committed
122

.  
Qiang Xue committed
123
	/**
Qiang Xue committed
124 125
	 * Initializes the application by loading components declared in [[preload]].
	 * If you override this method, make sure the parent implementation is invoked.
.  
Qiang Xue committed
126 127 128
	 */
	public function init()
	{
w  
Qiang Xue committed
129 130 131 132 133
		$this->preloadComponents();
	}

	/**
	 * Terminates the application.
.  
Qiang Xue committed
134
	 * This method replaces PHP's exit() function by calling [[afterRequest()]] before exiting.
w  
Qiang Xue committed
135
	 * @param integer $status exit status (value 0 means normal exit while other values mean abnormal exit).
.  
Qiang Xue committed
136
	 * @param boolean $exit whether to exit the current request.
w  
Qiang Xue committed
137 138 139 140
	 * It defaults to true, meaning the PHP's exit() function will be called at the end of this method.
	 */
	public function end($status = 0, $exit = true)
	{
.  
Qiang Xue committed
141 142 143
		if (!$this->_ended) {
			$this->_ended = true;
			$this->afterRequest();
Qiang Xue committed
144 145
		}
		if ($exit) {
w  
Qiang Xue committed
146
			exit($status);
Qiang Xue committed
147
		}
w  
Qiang Xue committed
148 149
	}

Qiang Xue committed
150 151 152 153 154 155 156 157 158 159 160 161 162
	/**
	 * Runs the application.
	 * This is the main entrance of an application.
	 * @return integer the exit status (0 means normal, non-zero values mean abnormal)
	 */
	public function run()
	{
		$this->beforeRequest();
		$status = $this->processRequest();
		$this->afterRequest();
		return $status;
	}

w  
Qiang Xue committed
163
	/**
Qiang Xue committed
164
	 * Raises the [[EVENT_BEFORE_REQUEST]] event right BEFORE the application processes the request.
w  
Qiang Xue committed
165
	 */
.  
Qiang Xue committed
166
	public function beforeRequest()
w  
Qiang Xue committed
167
	{
Qiang Xue committed
168
		$this->trigger(self::EVENT_BEFORE_REQUEST);
w  
Qiang Xue committed
169 170
	}

Qiang Xue committed
171
	/**
Qiang Xue committed
172
	 * Raises the [[EVENT_AFTER_REQUEST]] event right AFTER the application processes the request.
Qiang Xue committed
173
	 */
Qiang Xue committed
174
	public function afterRequest()
Qiang Xue committed
175
	{
Qiang Xue committed
176
		$this->trigger(self::EVENT_AFTER_REQUEST);
Qiang Xue committed
177 178
	}

w  
Qiang Xue committed
179
	/**
Qiang Xue committed
180
	 * Processes the request.
Qiang Xue committed
181
	 * Child classes should override this method with actual request processing logic.
Qiang Xue committed
182
	 * @return integer the exit status of the controller action (0 means normal, non-zero values mean abnormal)
Qiang Xue committed
183 184 185 186 187 188
	 */
	public function processRequest()
	{
		return 0;
	}

w  
Qiang Xue committed
189 190 191 192 193 194
	/**
	 * Returns the directory that stores runtime files.
	 * @return string the directory that stores runtime files. Defaults to 'protected/runtime'.
	 */
	public function getRuntimePath()
	{
Qiang Xue committed
195
		if ($this->_runtimePath !== null) {
w  
Qiang Xue committed
196 197
			$this->setRuntimePath($this->getBasePath() . DIRECTORY_SEPARATOR . 'runtime');
		}
Qiang Xue committed
198
		return $this->_runtimePath;
w  
Qiang Xue committed
199 200 201 202 203
	}

	/**
	 * Sets the directory that stores runtime files.
	 * @param string $path the directory that stores runtime files.
Qiang Xue committed
204
	 * @throws InvalidConfigException if the directory does not exist or is not writable
w  
Qiang Xue committed
205 206 207
	 */
	public function setRuntimePath($path)
	{
Qiang Xue committed
208 209
		$p = FileHelper::ensureDirectory($path);
		if (is_writable($p)) {
Qiang Xue committed
210
			$this->_runtimePath = $p;
Qiang Xue committed
211 212
		} else {
			throw new InvalidConfigException("Runtime path must be writable by the Web server process: $path");
Qiang Xue committed
213
		}
w  
Qiang Xue committed
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
	}

	/**
	 * Returns the time zone used by this application.
	 * This is a simple wrapper of PHP function date_default_timezone_get().
	 * @return string the time zone used by this application.
	 * @see http://php.net/manual/en/function.date-default-timezone-get.php
	 */
	public function getTimeZone()
	{
		return date_default_timezone_get();
	}

	/**
	 * Sets the time zone used by this application.
	 * This is a simple wrapper of PHP function date_default_timezone_set().
	 * @param string $value the time zone used by this application.
	 * @see http://php.net/manual/en/function.date-default-timezone-set.php
	 */
	public function setTimeZone($value)
	{
		date_default_timezone_set($value);
	}

Qiang Xue committed
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
	//	/**
	//	 * Returns the security manager component.
	//	 * @return SecurityManager the security manager application component.
	//	 */
	//	public function getSecurityManager()
	//	{
	//		return $this->getComponent('securityManager');
	//	}
	//
	//	/**
	//	 * Returns the locale instance.
	//	 * @param string $localeID the locale ID (e.g. en_US). If null, the {@link getLanguage application language ID} will be used.
	//	 * @return CLocale the locale instance
	//	 */
	//	public function getLocale($localeID = null)
	//	{
	//		return CLocale::getInstance($localeID === null ? $this->getLanguage() : $localeID);
	//	}
	//
	//	/**
	//	 * @return CNumberFormatter the locale-dependent number formatter.
	//	 * The current {@link getLocale application locale} will be used.
	//	 */
	//	public function getNumberFormatter()
	//	{
	//		return $this->getLocale()->getNumberFormatter();
	//	}
	//
	//	/**
	//	 * Returns the locale-dependent date formatter.
	//	 * @return CDateFormatter the locale-dependent date formatter.
	//	 * The current {@link getLocale application locale} will be used.
	//	 */
	//	public function getDateFormatter()
	//	{
	//		return $this->getLocale()->getDateFormatter();
	//	}
	//
w  
Qiang Xue committed
276 277 278

	/**
	 * Returns the database connection component.
Qiang Xue committed
279
	 * @return \yii\db\Connection the database connection
w  
Qiang Xue committed
280 281 282 283 284 285 286 287
	 */
	public function getDb()
	{
		return $this->getComponent('db');
	}

	/**
	 * Returns the error handler component.
.  
Qiang Xue committed
288
	 * @return ErrorHandler the error handler application component.
w  
Qiang Xue committed
289 290 291 292 293 294
	 */
	public function getErrorHandler()
	{
		return $this->getComponent('errorHandler');
	}

Qiang Xue committed
295 296 297 298 299 300 301 302 303
	/**
	 * Returns the application theme.
	 * @return Theme the theme that this application is currently using.
	 */
	public function getTheme()
	{
		return $this->getComponent('theme');
	}

w  
Qiang Xue committed
304 305
	/**
	 * Returns the cache component.
.  
Qiang Xue committed
306
	 * @return \yii\caching\Cache the cache application component. Null if the component is not enabled.
w  
Qiang Xue committed
307 308 309 310 311 312 313 314
	 */
	public function getCache()
	{
		return $this->getComponent('cache');
	}

	/**
	 * Returns the request component.
.  
Qiang Xue committed
315
	 * @return Request the request component
w  
Qiang Xue committed
316 317 318 319 320 321
	 */
	public function getRequest()
	{
		return $this->getComponent('request');
	}

Qiang Xue committed
322 323 324 325 326 327 328 329 330
	/**
	 * Returns the view renderer.
	 * @return ViewRenderer the view renderer used by this application.
	 */
	public function getViewRenderer()
	{
		return $this->getComponent('viewRenderer');
	}

Qiang Xue committed
331 332 333 334 335 336 337 338 339
	/**
	 * Returns the internationalization (i18n) component
	 * @return \yii\i18n\I18N the internationalization component
	 */
	public function getI18N()
	{
		return $this->getComponent('i18n');
	}

Qiang Xue committed
340 341 342 343 344
	/**
	 * Sets default path aliases.
	 */
	public function registerDefaultAliases()
	{
Qiang Xue committed
345
		Yii::$aliases['@app'] = $this->getBasePath();
Qiang Xue committed
346 347
		Yii::$aliases['@entry'] = dirname($_SERVER['SCRIPT_FILENAME']);
		Yii::$aliases['@www'] = '';
Qiang Xue committed
348 349
	}

w  
Qiang Xue committed
350 351 352 353
	/**
	 * Registers the core application components.
	 * @see setComponents
	 */
.  
Qiang Xue committed
354
	public function registerCoreComponents()
w  
Qiang Xue committed
355
	{
.  
Qiang Xue committed
356 357 358 359
		$this->setComponents(array(
			'errorHandler' => array(
				'class' => 'yii\base\ErrorHandler',
			),
Qiang Xue committed
360 361
			'i18n' => array(
				'class' => 'yii\i18n\I18N',
w  
Qiang Xue committed
362 363
			),
			'securityManager' => array(
.  
Qiang Xue committed
364
				'class' => 'yii\base\SecurityManager',
w  
Qiang Xue committed
365
			),
.  
Qiang Xue committed
366
		));
w  
Qiang Xue committed
367
	}
Qiang Xue committed
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406

	/**
	 * Handles PHP execution errors such as warnings, notices.
	 *
	 * This method is used as a PHP error handler. It will simply raise an `ErrorException`.
	 *
	 * @param integer $code the level of the error raised
	 * @param string $message the error message
	 * @param string $file the filename that the error was raised in
	 * @param integer $line the line number the error was raised at
	 * @throws \ErrorException the error exception
	 */
	public function handleError($code, $message, $file, $line)
	{
		if (error_reporting() !== 0) {
			throw new \ErrorException($message, 0, $code, $file, $line);
		}
	}

	/**
	 * Handles uncaught PHP exceptions.
	 *
	 * This method is implemented as a PHP exception handler. It requires
	 * that constant YII_ENABLE_ERROR_HANDLER be defined true.
	 *
	 * @param \Exception $exception exception that is not caught
	 */
	public function handleException($exception)
	{
		// disable error capturing to avoid recursive errors while handling exceptions
		restore_error_handler();
		restore_exception_handler();

		try {
			$this->logException($exception);

			if (($handler = $this->getErrorHandler()) !== null) {
				$handler->handle($exception);
			} else {
Qiang Xue committed
407
				$this->renderException($exception);
Qiang Xue committed
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
			}

			$this->end(1);

		} catch(\Exception $e) {
			// exception could be thrown in end() or ErrorHandler::handle()
			$msg = (string)$e;
			$msg .= "\nPrevious exception:\n";
			$msg .= (string)$exception;
			$msg .= "\n\$_SERVER = " . var_export($_SERVER, true);
			error_log($msg);
			exit(1);
		}
	}

Qiang Xue committed
423 424 425 426 427 428
	/**
	 * Renders an exception without using rich format.
	 * @param \Exception $exception the exception to be rendered.
	 */
	public function renderException($exception)
	{
Qiang Xue committed
429
		if ($exception instanceof Exception && ($exception instanceof UserException || !YII_DEBUG)) {
Qiang Xue committed
430 431 432 433 434 435 436 437 438 439 440
			$message = $exception->getName() . ': ' . $exception->getMessage();
		} else {
			$message = YII_DEBUG ? (string)$exception : 'Error: ' . $exception->getMessage();
		}
		if (PHP_SAPI) {
			echo $message . "\n";
		} else {
			echo '<pre>' . htmlspecialchars($message, ENT_QUOTES, $this->charset) . '</pre>';
		}
	}

Qiang Xue committed
441 442 443 444 445 446 447 448 449 450 451 452 453
	// todo: to be polished
	protected function logException($exception)
	{
		$category = get_class($exception);
		if ($exception instanceof HttpException) {
			/** @var $exception HttpException */
			$category .= '\\' . $exception->statusCode;
		} elseif ($exception instanceof \ErrorException) {
			/** @var $exception \ErrorException */
			$category .= '\\' . $exception->getSeverity();
		}
		Yii::error((string)$exception, $category);
	}
w  
Qiang Xue committed
454
}