CApplication.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990
  1. <?php
  2. /**
  3. * CApplication class file.
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright 2008-2013 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CApplication is the base class for all application classes.
  12. *
  13. * An application serves as the global context that the user request
  14. * is being processed. It manages a set of application components that
  15. * provide specific functionalities to the whole application.
  16. *
  17. * The core application components provided by CApplication are the following:
  18. * <ul>
  19. * <li>{@link getErrorHandler errorHandler}: handles PHP errors and
  20. * uncaught exceptions. This application component is dynamically loaded when needed.</li>
  21. * <li>{@link getSecurityManager securityManager}: provides security-related
  22. * services, such as hashing, encryption. This application component is dynamically
  23. * loaded when needed.</li>
  24. * <li>{@link getStatePersister statePersister}: provides global state
  25. * persistence method. This application component is dynamically loaded when needed.</li>
  26. * <li>{@link getCache cache}: provides caching feature. This application component is
  27. * disabled by default.</li>
  28. * <li>{@link getMessages messages}: provides the message source for translating
  29. * application messages. This application component is dynamically loaded when needed.</li>
  30. * <li>{@link getCoreMessages coreMessages}: provides the message source for translating
  31. * Yii framework messages. This application component is dynamically loaded when needed.</li>
  32. * <li>{@link getUrlManager urlManager}: provides URL construction as well as parsing functionality.
  33. * This application component is dynamically loaded when needed.</li>
  34. * <li>{@link getRequest request}: represents the current HTTP request by encapsulating
  35. * the $_SERVER variable and managing cookies sent from and sent to the user.
  36. * This application component is dynamically loaded when needed.</li>
  37. * <li>{@link getFormat format}: provides a set of commonly used data formatting methods.
  38. * This application component is dynamically loaded when needed.</li>
  39. * </ul>
  40. *
  41. * CApplication will undergo the following lifecycles when processing a user request:
  42. * <ol>
  43. * <li>load application configuration;</li>
  44. * <li>set up class autoloader and error handling;</li>
  45. * <li>load static application components;</li>
  46. * <li>{@link onBeginRequest}: preprocess the user request;</li>
  47. * <li>{@link processRequest}: process the user request;</li>
  48. * <li>{@link onEndRequest}: postprocess the user request;</li>
  49. * </ol>
  50. *
  51. * Starting from lifecycle 3, if a PHP error or an uncaught exception occurs,
  52. * the application will switch to its error handling logic and jump to step 6 afterwards.
  53. *
  54. * @property string $id The unique identifier for the application.
  55. * @property string $basePath The root directory of the application. Defaults to 'protected'.
  56. * @property string $runtimePath The directory that stores runtime files. Defaults to 'protected/runtime'.
  57. * @property string $extensionPath The directory that contains all extensions. Defaults to the 'extensions' directory under 'protected'.
  58. * @property string $language The language that the user is using and the application should be targeted to.
  59. * Defaults to the {@link sourceLanguage source language}.
  60. * @property string $timeZone The time zone used by this application.
  61. * @property CLocale $locale The locale instance.
  62. * @property string $localeDataPath The directory that contains the locale data. It defaults to 'framework/i18n/data'.
  63. * @property CNumberFormatter $numberFormatter The locale-dependent number formatter.
  64. * The current {@link getLocale application locale} will be used.
  65. * @property CDateFormatter $dateFormatter The locale-dependent date formatter.
  66. * The current {@link getLocale application locale} will be used.
  67. * @property CDbConnection $db The database connection.
  68. * @property CErrorHandler $errorHandler The error handler application component.
  69. * @property CSecurityManager $securityManager The security manager application component.
  70. * @property CStatePersister $statePersister The state persister application component.
  71. * @property CCache $cache The cache application component. Null if the component is not enabled.
  72. * @property CPhpMessageSource $coreMessages The core message translations.
  73. * @property CMessageSource $messages The application message translations.
  74. * @property CHttpRequest $request The request component.
  75. * @property CUrlManager $urlManager The URL manager component.
  76. * @property CController $controller The currently active controller. Null is returned in this base class.
  77. * @property string $baseUrl The relative URL for the application.
  78. * @property string $homeUrl The homepage URL.
  79. *
  80. * @author Qiang Xue <qiang.xue@gmail.com>
  81. * @package system.base
  82. * @since 1.0
  83. */
  84. abstract class CApplication extends CModule
  85. {
  86. /**
  87. * @var string the application name. Defaults to 'My Application'.
  88. */
  89. public $name='My Application';
  90. /**
  91. * @var string the charset currently used for the application. Defaults to 'UTF-8'.
  92. */
  93. public $charset='UTF-8';
  94. /**
  95. * @var string the language that the application is written in. This mainly refers to
  96. * the language that the messages and view files are in. Defaults to 'en_us' (US English).
  97. */
  98. public $sourceLanguage='en_us';
  99. private $_id;
  100. private $_basePath;
  101. private $_runtimePath;
  102. private $_extensionPath;
  103. private $_globalState;
  104. private $_stateChanged;
  105. private $_ended=false;
  106. private $_language;
  107. private $_homeUrl;
  108. /**
  109. * Processes the request.
  110. * This is the place where the actual request processing work is done.
  111. * Derived classes should override this method.
  112. */
  113. abstract public function processRequest();
  114. /**
  115. * Constructor.
  116. * @param mixed $config application configuration.
  117. * If a string, it is treated as the path of the file that contains the configuration;
  118. * If an array, it is the actual configuration information.
  119. * Please make sure you specify the {@link getBasePath basePath} property in the configuration,
  120. * which should point to the directory containing all application logic, template and data.
  121. * If not, the directory will be defaulted to 'protected'.
  122. */
  123. public function __construct($config=null)
  124. {
  125. Yii::setApplication($this);
  126. // set basePath at early as possible to avoid trouble
  127. if(is_string($config))
  128. $config=require($config);
  129. if(isset($config['basePath']))
  130. {
  131. $this->setBasePath($config['basePath']);
  132. unset($config['basePath']);
  133. }
  134. else
  135. $this->setBasePath('protected');
  136. Yii::setPathOfAlias('application',$this->getBasePath());
  137. Yii::setPathOfAlias('webroot',dirname($_SERVER['SCRIPT_FILENAME']));
  138. if(isset($config['extensionPath']))
  139. {
  140. $this->setExtensionPath($config['extensionPath']);
  141. unset($config['extensionPath']);
  142. }
  143. else
  144. Yii::setPathOfAlias('ext',$this->getBasePath().DIRECTORY_SEPARATOR.'extensions');
  145. if(isset($config['aliases']))
  146. {
  147. $this->setAliases($config['aliases']);
  148. unset($config['aliases']);
  149. }
  150. $this->preinit();
  151. $this->initSystemHandlers();
  152. $this->registerCoreComponents();
  153. $this->configure($config);
  154. $this->attachBehaviors($this->behaviors);
  155. $this->preloadComponents();
  156. $this->init();
  157. }
  158. /**
  159. * Runs the application.
  160. * This method loads static application components. Derived classes usually overrides this
  161. * method to do more application-specific tasks.
  162. * Remember to call the parent implementation so that static application components are loaded.
  163. */
  164. public function run()
  165. {
  166. if($this->hasEventHandler('onBeginRequest'))
  167. $this->onBeginRequest(new CEvent($this));
  168. register_shutdown_function(array($this,'end'),0,false);
  169. $this->processRequest();
  170. if($this->hasEventHandler('onEndRequest'))
  171. $this->onEndRequest(new CEvent($this));
  172. }
  173. /**
  174. * Terminates the application.
  175. * This method replaces PHP's exit() function by calling
  176. * {@link onEndRequest} before exiting.
  177. * @param integer $status exit status (value 0 means normal exit while other values mean abnormal exit).
  178. * @param boolean $exit whether to exit the current request. This parameter has been available since version 1.1.5.
  179. * It defaults to true, meaning the PHP's exit() function will be called at the end of this method.
  180. */
  181. public function end($status=0,$exit=true)
  182. {
  183. if($this->hasEventHandler('onEndRequest'))
  184. $this->onEndRequest(new CEvent($this));
  185. if($exit)
  186. exit($status);
  187. }
  188. /**
  189. * Raised right BEFORE the application processes the request.
  190. * @param CEvent $event the event parameter
  191. */
  192. public function onBeginRequest($event)
  193. {
  194. $this->raiseEvent('onBeginRequest',$event);
  195. }
  196. /**
  197. * Raised right AFTER the application processes the request.
  198. * @param CEvent $event the event parameter
  199. */
  200. public function onEndRequest($event)
  201. {
  202. if(!$this->_ended)
  203. {
  204. $this->_ended=true;
  205. $this->raiseEvent('onEndRequest',$event);
  206. }
  207. }
  208. /**
  209. * Returns the unique identifier for the application.
  210. * @return string the unique identifier for the application.
  211. */
  212. public function getId()
  213. {
  214. if($this->_id!==null)
  215. return $this->_id;
  216. else
  217. return $this->_id=sprintf('%x',crc32($this->getBasePath().$this->name));
  218. }
  219. /**
  220. * Sets the unique identifier for the application.
  221. * @param string $id the unique identifier for the application.
  222. */
  223. public function setId($id)
  224. {
  225. $this->_id=$id;
  226. }
  227. /**
  228. * Returns the root path of the application.
  229. * @return string the root directory of the application. Defaults to 'protected'.
  230. */
  231. public function getBasePath()
  232. {
  233. return $this->_basePath;
  234. }
  235. /**
  236. * Sets the root directory of the application.
  237. * This method can only be invoked at the begin of the constructor.
  238. * @param string $path the root directory of the application.
  239. * @throws CException if the directory does not exist.
  240. */
  241. public function setBasePath($path)
  242. {
  243. if(($this->_basePath=realpath($path))===false || !is_dir($this->_basePath))
  244. throw new CException(Yii::t('yii','Application base path "{path}" is not a valid directory.',
  245. array('{path}'=>$path)));
  246. }
  247. /**
  248. * Returns the directory that stores runtime files.
  249. * @return string the directory that stores runtime files. Defaults to 'protected/runtime'.
  250. */
  251. public function getRuntimePath()
  252. {
  253. if($this->_runtimePath!==null)
  254. return $this->_runtimePath;
  255. else
  256. {
  257. $this->setRuntimePath($this->getBasePath().DIRECTORY_SEPARATOR.'runtime');
  258. return $this->_runtimePath;
  259. }
  260. }
  261. /**
  262. * Sets the directory that stores runtime files.
  263. * @param string $path the directory that stores runtime files.
  264. * @throws CException if the directory does not exist or is not writable
  265. */
  266. public function setRuntimePath($path)
  267. {
  268. if(($runtimePath=realpath($path))===false || !is_dir($runtimePath) || !is_writable($runtimePath))
  269. throw new CException(Yii::t('yii','Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.',
  270. array('{path}'=>$path)));
  271. $this->_runtimePath=$runtimePath;
  272. }
  273. /**
  274. * Returns the root directory that holds all third-party extensions.
  275. * @return string the directory that contains all extensions. Defaults to the 'extensions' directory under 'protected'.
  276. */
  277. public function getExtensionPath()
  278. {
  279. return Yii::getPathOfAlias('ext');
  280. }
  281. /**
  282. * Sets the root directory that holds all third-party extensions.
  283. * @param string $path the directory that contains all third-party extensions.
  284. * @throws CException if the directory does not exist
  285. */
  286. public function setExtensionPath($path)
  287. {
  288. if(($extensionPath=realpath($path))===false || !is_dir($extensionPath))
  289. throw new CException(Yii::t('yii','Extension path "{path}" does not exist.',
  290. array('{path}'=>$path)));
  291. Yii::setPathOfAlias('ext',$extensionPath);
  292. }
  293. /**
  294. * Returns the language that the user is using and the application should be targeted to.
  295. * @return string the language that the user is using and the application should be targeted to.
  296. * Defaults to the {@link sourceLanguage source language}.
  297. */
  298. public function getLanguage()
  299. {
  300. return $this->_language===null ? $this->sourceLanguage : $this->_language;
  301. }
  302. /**
  303. * Specifies which language the application is targeted to.
  304. *
  305. * This is the language that the application displays to end users.
  306. * If set null, it uses the {@link sourceLanguage source language}.
  307. *
  308. * Unless your application needs to support multiple languages, you should always
  309. * set this language to null to maximize the application's performance.
  310. * @param string $language the user language (e.g. 'en_US', 'zh_CN').
  311. * If it is null, the {@link sourceLanguage} will be used.
  312. */
  313. public function setLanguage($language)
  314. {
  315. $this->_language=$language;
  316. }
  317. /**
  318. * Returns the time zone used by this application.
  319. * This is a simple wrapper of PHP function date_default_timezone_get().
  320. * @return string the time zone used by this application.
  321. * @see http://php.net/manual/en/function.date-default-timezone-get.php
  322. */
  323. public function getTimeZone()
  324. {
  325. return date_default_timezone_get();
  326. }
  327. /**
  328. * Sets the time zone used by this application.
  329. * This is a simple wrapper of PHP function date_default_timezone_set().
  330. * @param string $value the time zone used by this application.
  331. * @see http://php.net/manual/en/function.date-default-timezone-set.php
  332. */
  333. public function setTimeZone($value)
  334. {
  335. date_default_timezone_set($value);
  336. }
  337. /**
  338. * Returns the localized version of a specified file.
  339. *
  340. * The searching is based on the specified language code. In particular,
  341. * a file with the same name will be looked for under the subdirectory
  342. * named as the locale ID. For example, given the file "path/to/view.php"
  343. * and locale ID "zh_cn", the localized file will be looked for as
  344. * "path/to/zh_cn/view.php". If the file is not found, the original file
  345. * will be returned.
  346. *
  347. * For consistency, it is recommended that the locale ID is given
  348. * in lower case and in the format of LanguageID_RegionID (e.g. "en_us").
  349. *
  350. * @param string $srcFile the original file
  351. * @param string $srcLanguage the language that the original file is in. If null, the application {@link sourceLanguage source language} is used.
  352. * @param string $language the desired language that the file should be localized to. If null, the {@link getLanguage application language} will be used.
  353. * @return string the matching localized file. The original file is returned if no localized version is found
  354. * or if source language is the same as the desired language.
  355. */
  356. public function findLocalizedFile($srcFile,$srcLanguage=null,$language=null)
  357. {
  358. if($srcLanguage===null)
  359. $srcLanguage=$this->sourceLanguage;
  360. if($language===null)
  361. $language=$this->getLanguage();
  362. if($language===$srcLanguage)
  363. return $srcFile;
  364. $desiredFile=dirname($srcFile).DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.basename($srcFile);
  365. return is_file($desiredFile) ? $desiredFile : $srcFile;
  366. }
  367. /**
  368. * Returns the locale instance.
  369. * @param string $localeID the locale ID (e.g. en_US). If null, the {@link getLanguage application language ID} will be used.
  370. * @return CLocale the locale instance
  371. */
  372. public function getLocale($localeID=null)
  373. {
  374. return CLocale::getInstance($localeID===null?$this->getLanguage():$localeID);
  375. }
  376. /**
  377. * Returns the directory that contains the locale data.
  378. * @return string the directory that contains the locale data. It defaults to 'framework/i18n/data'.
  379. * @since 1.1.0
  380. */
  381. public function getLocaleDataPath()
  382. {
  383. return CLocale::$dataPath===null ? Yii::getPathOfAlias('system.i18n.data') : CLocale::$dataPath;
  384. }
  385. /**
  386. * Sets the directory that contains the locale data.
  387. * @param string $value the directory that contains the locale data.
  388. * @since 1.1.0
  389. */
  390. public function setLocaleDataPath($value)
  391. {
  392. CLocale::$dataPath=$value;
  393. }
  394. /**
  395. * @return CNumberFormatter the locale-dependent number formatter.
  396. * The current {@link getLocale application locale} will be used.
  397. */
  398. public function getNumberFormatter()
  399. {
  400. return $this->getLocale()->getNumberFormatter();
  401. }
  402. /**
  403. * Returns the locale-dependent date formatter.
  404. * @return CDateFormatter the locale-dependent date formatter.
  405. * The current {@link getLocale application locale} will be used.
  406. */
  407. public function getDateFormatter()
  408. {
  409. return $this->getLocale()->getDateFormatter();
  410. }
  411. /**
  412. * Returns the database connection component.
  413. * @return CDbConnection the database connection
  414. */
  415. public function getDb()
  416. {
  417. return $this->getComponent('db');
  418. }
  419. /**
  420. * Returns the error handler component.
  421. * @return CErrorHandler the error handler application component.
  422. */
  423. public function getErrorHandler()
  424. {
  425. return $this->getComponent('errorHandler');
  426. }
  427. /**
  428. * Returns the security manager component.
  429. * @return CSecurityManager the security manager application component.
  430. */
  431. public function getSecurityManager()
  432. {
  433. return $this->getComponent('securityManager');
  434. }
  435. /**
  436. * Returns the state persister component.
  437. * @return CStatePersister the state persister application component.
  438. */
  439. public function getStatePersister()
  440. {
  441. return $this->getComponent('statePersister');
  442. }
  443. /**
  444. * Returns the cache component.
  445. * @return CCache the cache application component. Null if the component is not enabled.
  446. */
  447. public function getCache()
  448. {
  449. return $this->getComponent('cache');
  450. }
  451. /**
  452. * Returns the core message translations component.
  453. * @return CPhpMessageSource the core message translations
  454. */
  455. public function getCoreMessages()
  456. {
  457. return $this->getComponent('coreMessages');
  458. }
  459. /**
  460. * Returns the application message translations component.
  461. * @return CMessageSource the application message translations
  462. */
  463. public function getMessages()
  464. {
  465. return $this->getComponent('messages');
  466. }
  467. /**
  468. * Returns the request component.
  469. * @return CHttpRequest the request component
  470. */
  471. public function getRequest()
  472. {
  473. return $this->getComponent('request');
  474. }
  475. /**
  476. * Returns the URL manager component.
  477. * @return CUrlManager the URL manager component
  478. */
  479. public function getUrlManager()
  480. {
  481. return $this->getComponent('urlManager');
  482. }
  483. /**
  484. * @return CController the currently active controller. Null is returned in this base class.
  485. * @since 1.1.8
  486. */
  487. public function getController()
  488. {
  489. return null;
  490. }
  491. /**
  492. * Creates a relative URL based on the given controller and action information.
  493. * @param string $route the URL route. This should be in the format of 'ControllerID/ActionID'.
  494. * @param array $params additional GET parameters (name=>value). Both the name and value will be URL-encoded.
  495. * @param string $ampersand the token separating name-value pairs in the URL.
  496. * @return string the constructed URL
  497. */
  498. public function createUrl($route,$params=array(),$ampersand='&')
  499. {
  500. return $this->getUrlManager()->createUrl($route,$params,$ampersand);
  501. }
  502. /**
  503. * Creates an absolute URL based on the given controller and action information.
  504. * @param string $route the URL route. This should be in the format of 'ControllerID/ActionID'.
  505. * @param array $params additional GET parameters (name=>value). Both the name and value will be URL-encoded.
  506. * @param string $schema schema to use (e.g. http, https). If empty, the schema used for the current request will be used.
  507. * @param string $ampersand the token separating name-value pairs in the URL.
  508. * @return string the constructed URL
  509. */
  510. public function createAbsoluteUrl($route,$params=array(),$schema='',$ampersand='&')
  511. {
  512. $url=$this->createUrl($route,$params,$ampersand);
  513. if(strpos($url,'http')===0)
  514. return $url;
  515. else
  516. return $this->getRequest()->getHostInfo($schema).$url;
  517. }
  518. /**
  519. * Returns the relative URL for the application.
  520. * This is a shortcut method to {@link CHttpRequest::getBaseUrl()}.
  521. * @param boolean $absolute whether to return an absolute URL. Defaults to false, meaning returning a relative one.
  522. * @return string the relative URL for the application
  523. * @see CHttpRequest::getBaseUrl()
  524. */
  525. public function getBaseUrl($absolute=false)
  526. {
  527. return $this->getRequest()->getBaseUrl($absolute);
  528. }
  529. /**
  530. * @return string the homepage URL
  531. */
  532. public function getHomeUrl()
  533. {
  534. if($this->_homeUrl===null)
  535. {
  536. if($this->getUrlManager()->showScriptName)
  537. return $this->getRequest()->getScriptUrl();
  538. else
  539. return $this->getRequest()->getBaseUrl().'/';
  540. }
  541. else
  542. return $this->_homeUrl;
  543. }
  544. /**
  545. * @param string $value the homepage URL
  546. */
  547. public function setHomeUrl($value)
  548. {
  549. $this->_homeUrl=$value;
  550. }
  551. /**
  552. * Returns a global value.
  553. *
  554. * A global value is one that is persistent across users sessions and requests.
  555. * @param string $key the name of the value to be returned
  556. * @param mixed $defaultValue the default value. If the named global value is not found, this will be returned instead.
  557. * @return mixed the named global value
  558. * @see setGlobalState
  559. */
  560. public function getGlobalState($key,$defaultValue=null)
  561. {
  562. if($this->_globalState===null)
  563. $this->loadGlobalState();
  564. if(isset($this->_globalState[$key]))
  565. return $this->_globalState[$key];
  566. else
  567. return $defaultValue;
  568. }
  569. /**
  570. * Sets a global value.
  571. *
  572. * A global value is one that is persistent across users sessions and requests.
  573. * Make sure that the value is serializable and unserializable.
  574. * @param string $key the name of the value to be saved
  575. * @param mixed $value the global value to be saved. It must be serializable.
  576. * @param mixed $defaultValue the default value. If the named global value is the same as this value, it will be cleared from the current storage.
  577. * @see getGlobalState
  578. */
  579. public function setGlobalState($key,$value,$defaultValue=null)
  580. {
  581. if($this->_globalState===null)
  582. $this->loadGlobalState();
  583. $changed=$this->_stateChanged;
  584. if($value===$defaultValue)
  585. {
  586. if(isset($this->_globalState[$key]))
  587. {
  588. unset($this->_globalState[$key]);
  589. $this->_stateChanged=true;
  590. }
  591. }
  592. elseif(!isset($this->_globalState[$key]) || $this->_globalState[$key]!==$value)
  593. {
  594. $this->_globalState[$key]=$value;
  595. $this->_stateChanged=true;
  596. }
  597. if($this->_stateChanged!==$changed)
  598. $this->attachEventHandler('onEndRequest',array($this,'saveGlobalState'));
  599. }
  600. /**
  601. * Clears a global value.
  602. *
  603. * The value cleared will no longer be available in this request and the following requests.
  604. * @param string $key the name of the value to be cleared
  605. */
  606. public function clearGlobalState($key)
  607. {
  608. $this->setGlobalState($key,true,true);
  609. }
  610. /**
  611. * Loads the global state data from persistent storage.
  612. * @see getStatePersister
  613. * @throws CException if the state persister is not available
  614. */
  615. public function loadGlobalState()
  616. {
  617. $persister=$this->getStatePersister();
  618. if(($this->_globalState=$persister->load())===null)
  619. $this->_globalState=array();
  620. $this->_stateChanged=false;
  621. $this->detachEventHandler('onEndRequest',array($this,'saveGlobalState'));
  622. }
  623. /**
  624. * Saves the global state data into persistent storage.
  625. * @see getStatePersister
  626. * @throws CException if the state persister is not available
  627. */
  628. public function saveGlobalState()
  629. {
  630. if($this->_stateChanged)
  631. {
  632. $this->_stateChanged=false;
  633. $this->detachEventHandler('onEndRequest',array($this,'saveGlobalState'));
  634. $this->getStatePersister()->save($this->_globalState);
  635. }
  636. }
  637. /**
  638. * Handles uncaught PHP exceptions.
  639. *
  640. * This method is implemented as a PHP exception handler. It requires
  641. * that constant YII_ENABLE_EXCEPTION_HANDLER be defined true.
  642. *
  643. * This method will first raise an {@link onException} event.
  644. * If the exception is not handled by any event handler, it will call
  645. * {@link getErrorHandler errorHandler} to process the exception.
  646. *
  647. * The application will be terminated by this method.
  648. *
  649. * @param Exception $exception exception that is not caught
  650. */
  651. public function handleException($exception)
  652. {
  653. // disable error capturing to avoid recursive errors
  654. restore_error_handler();
  655. restore_exception_handler();
  656. $category='exception.'.get_class($exception);
  657. if($exception instanceof CHttpException)
  658. $category.='.'.$exception->statusCode;
  659. // php <5.2 doesn't support string conversion auto-magically
  660. $message=$exception->__toString();
  661. if(isset($_SERVER['REQUEST_URI']))
  662. $message.="\nREQUEST_URI=".$_SERVER['REQUEST_URI'];
  663. if(isset($_SERVER['HTTP_REFERER']))
  664. $message.="\nHTTP_REFERER=".$_SERVER['HTTP_REFERER'];
  665. $message.="\n---";
  666. Yii::log($message,CLogger::LEVEL_ERROR,$category);
  667. try
  668. {
  669. $event=new CExceptionEvent($this,$exception);
  670. $this->onException($event);
  671. if(!$event->handled)
  672. {
  673. // try an error handler
  674. if(($handler=$this->getErrorHandler())!==null)
  675. $handler->handle($event);
  676. else
  677. $this->displayException($exception);
  678. }
  679. }
  680. catch(Exception $e)
  681. {
  682. $this->displayException($e);
  683. }
  684. try
  685. {
  686. $this->end(1);
  687. }
  688. catch(Exception $e)
  689. {
  690. // use the most primitive way to log error
  691. $msg = get_class($e).': '.$e->getMessage().' ('.$e->getFile().':'.$e->getLine().")\n";
  692. $msg .= $e->getTraceAsString()."\n";
  693. $msg .= "Previous exception:\n";
  694. $msg .= get_class($exception).': '.$exception->getMessage().' ('.$exception->getFile().':'.$exception->getLine().")\n";
  695. $msg .= $exception->getTraceAsString()."\n";
  696. $msg .= '$_SERVER='.var_export($_SERVER,true);
  697. error_log($msg);
  698. exit(1);
  699. }
  700. }
  701. /**
  702. * Handles PHP execution errors such as warnings, notices.
  703. *
  704. * This method is implemented as a PHP error handler. It requires
  705. * that constant YII_ENABLE_ERROR_HANDLER be defined true.
  706. *
  707. * This method will first raise an {@link onError} event.
  708. * If the error is not handled by any event handler, it will call
  709. * {@link getErrorHandler errorHandler} to process the error.
  710. *
  711. * The application will be terminated by this method.
  712. *
  713. * @param integer $code the level of the error raised
  714. * @param string $message the error message
  715. * @param string $file the filename that the error was raised in
  716. * @param integer $line the line number the error was raised at
  717. */
  718. public function handleError($code,$message,$file,$line)
  719. {
  720. if($code & error_reporting())
  721. {
  722. // disable error capturing to avoid recursive errors
  723. restore_error_handler();
  724. restore_exception_handler();
  725. $log="$message ($file:$line)\nStack trace:\n";
  726. $trace=debug_backtrace();
  727. // skip the first 3 stacks as they do not tell the error position
  728. if(count($trace)>3)
  729. $trace=array_slice($trace,3);
  730. foreach($trace as $i=>$t)
  731. {
  732. if(!isset($t['file']))
  733. $t['file']='unknown';
  734. if(!isset($t['line']))
  735. $t['line']=0;
  736. if(!isset($t['function']))
  737. $t['function']='unknown';
  738. $log.="#$i {$t['file']}({$t['line']}): ";
  739. if(isset($t['object']) && is_object($t['object']))
  740. $log.=get_class($t['object']).'->';
  741. $log.="{$t['function']}()\n";
  742. }
  743. if(isset($_SERVER['REQUEST_URI']))
  744. $log.='REQUEST_URI='.$_SERVER['REQUEST_URI'];
  745. Yii::log($log,CLogger::LEVEL_ERROR,'php');
  746. try
  747. {
  748. Yii::import('CErrorEvent',true);
  749. $event=new CErrorEvent($this,$code,$message,$file,$line);
  750. $this->onError($event);
  751. if(!$event->handled)
  752. {
  753. // try an error handler
  754. if(($handler=$this->getErrorHandler())!==null)
  755. $handler->handle($event);
  756. else
  757. $this->displayError($code,$message,$file,$line);
  758. }
  759. }
  760. catch(Exception $e)
  761. {
  762. $this->displayException($e);
  763. }
  764. try
  765. {
  766. $this->end(1);
  767. }
  768. catch(Exception $e)
  769. {
  770. // use the most primitive way to log error
  771. $msg = get_class($e).': '.$e->getMessage().' ('.$e->getFile().':'.$e->getLine().")\n";
  772. $msg .= $e->getTraceAsString()."\n";
  773. $msg .= "Previous error:\n";
  774. $msg .= $log."\n";
  775. $msg .= '$_SERVER='.var_export($_SERVER,true);
  776. error_log($msg);
  777. exit(1);
  778. }
  779. }
  780. }
  781. /**
  782. * Raised when an uncaught PHP exception occurs.
  783. *
  784. * An event handler can set the {@link CExceptionEvent::handled handled}
  785. * property of the event parameter to be true to indicate no further error
  786. * handling is needed. Otherwise, the {@link getErrorHandler errorHandler}
  787. * application component will continue processing the error.
  788. *
  789. * @param CExceptionEvent $event event parameter
  790. */
  791. public function onException($event)
  792. {
  793. $this->raiseEvent('onException',$event);
  794. }
  795. /**
  796. * Raised when a PHP execution error occurs.
  797. *
  798. * An event handler can set the {@link CErrorEvent::handled handled}
  799. * property of the event parameter to be true to indicate no further error
  800. * handling is needed. Otherwise, the {@link getErrorHandler errorHandler}
  801. * application component will continue processing the error.
  802. *
  803. * @param CErrorEvent $event event parameter
  804. */
  805. public function onError($event)
  806. {
  807. $this->raiseEvent('onError',$event);
  808. }
  809. /**
  810. * Displays the captured PHP error.
  811. * This method displays the error in HTML when there is
  812. * no active error handler.
  813. * @param integer $code error code
  814. * @param string $message error message
  815. * @param string $file error file
  816. * @param string $line error line
  817. */
  818. public function displayError($code,$message,$file,$line)
  819. {
  820. if(YII_DEBUG)
  821. {
  822. echo "<h1>PHP Error [$code]</h1>\n";
  823. echo "<p>$message ($file:$line)</p>\n";
  824. echo '<pre>';
  825. $trace=debug_backtrace();
  826. // skip the first 3 stacks as they do not tell the error position
  827. if(count($trace)>3)
  828. $trace=array_slice($trace,3);
  829. foreach($trace as $i=>$t)
  830. {
  831. if(!isset($t['file']))
  832. $t['file']='unknown';
  833. if(!isset($t['line']))
  834. $t['line']=0;
  835. if(!isset($t['function']))
  836. $t['function']='unknown';
  837. echo "#$i {$t['file']}({$t['line']}): ";
  838. if(isset($t['object']) && is_object($t['object']))
  839. echo get_class($t['object']).'->';
  840. echo "{$t['function']}()\n";
  841. }
  842. echo '</pre>';
  843. }
  844. else
  845. {
  846. echo "<h1>PHP Error [$code]</h1>\n";
  847. echo "<p>$message</p>\n";
  848. }
  849. }
  850. /**
  851. * Displays the uncaught PHP exception.
  852. * This method displays the exception in HTML when there is
  853. * no active error handler.
  854. * @param Exception $exception the uncaught exception
  855. */
  856. public function displayException($exception)
  857. {
  858. if(YII_DEBUG)
  859. {
  860. echo '<h1>'.get_class($exception)."</h1>\n";
  861. echo '<p>'.$exception->getMessage().' ('.$exception->getFile().':'.$exception->getLine().')</p>';
  862. echo '<pre>'.$exception->getTraceAsString().'</pre>';
  863. }
  864. else
  865. {
  866. echo '<h1>'.get_class($exception)."</h1>\n";
  867. echo '<p>'.$exception->getMessage().'</p>';
  868. }
  869. }
  870. /**
  871. * Initializes the class autoloader and error handlers.
  872. */
  873. protected function initSystemHandlers()
  874. {
  875. if(YII_ENABLE_EXCEPTION_HANDLER)
  876. set_exception_handler(array($this,'handleException'));
  877. if(YII_ENABLE_ERROR_HANDLER)
  878. set_error_handler(array($this,'handleError'),error_reporting());
  879. }
  880. /**
  881. * Registers the core application components.
  882. * @see setComponents
  883. */
  884. protected function registerCoreComponents()
  885. {
  886. $components=array(
  887. 'coreMessages'=>array(
  888. 'class'=>'CPhpMessageSource',
  889. 'language'=>'en_us',
  890. 'basePath'=>YII_PATH.DIRECTORY_SEPARATOR.'messages',
  891. ),
  892. 'db'=>array(
  893. 'class'=>'CDbConnection',
  894. ),
  895. 'messages'=>array(
  896. 'class'=>'CPhpMessageSource',
  897. ),
  898. 'errorHandler'=>array(
  899. 'class'=>'CErrorHandler',
  900. ),
  901. 'securityManager'=>array(
  902. 'class'=>'CSecurityManager',
  903. ),
  904. 'statePersister'=>array(
  905. 'class'=>'CStatePersister',
  906. ),
  907. 'urlManager'=>array(
  908. 'class'=>'CUrlManager',
  909. ),
  910. 'request'=>array(
  911. 'class'=>'CHttpRequest',
  912. ),
  913. 'format'=>array(
  914. 'class'=>'CFormatter',
  915. ),
  916. );
  917. $this->setComponents($components);
  918. }
  919. }