PageRenderTime 58ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/yii/framework/base/CApplication.php

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