PageRenderTime 56ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/base/CApplication.php

https://bitbucket.org/stden/yiiru
PHP | 991 lines | 486 code | 71 blank | 434 comment | 58 complexity | 4c9a402722bfb9b6db543a8273649fbc MD5 | raw file
  1. <?php
  2. /**
  3. * Файл класса CApplication.
  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 - это базовый класс для всех классов приложения.
  12. *
  13. * Приложение служит в качестве глобального контекста, в котором выполняется запрос пользователя.
  14. * Оно управляет набором компонентов приложения, предоставляющих
  15. * специальные функции для всего приложения.
  16. *
  17. * Список компонентов ядра приложения, предоставляемых классом CApplication:
  18. * <ul>
  19. * <li>{@link getErrorHandler errorHandler}: обрабатывает ошибки PHP и
  20. * неперехваченные исключения. Данный компонент приложения загружается динамически при необходимости;</li>
  21. * <li>{@link getSecurityManager securityManager}: предоставляет функции безопасности
  22. * такие, как хэширование, шифрование. Данный компонент приложения загружается динамически при необходимости;</li>
  23. * <li>{@link getStatePersister statePersister}: предоставляет функцию постоянного глобального состояния.
  24. * Данный компонент приложения загружается динамически при необходимости;</li>
  25. * <li>{@link getCache cache}: предоставляет функции кэширования. Данный компонент по умолчанию отключен;</li>
  26. * <li>{@link getMessages messages}: предоставляет источник сообщений для перевода сообщений
  27. * приложения. Данный компонент приложения загружается динамически при необходимости;</li>
  28. * <li>{@link getCoreMessages coreMessages}: предоставляет источник сообщений для перевода сообщений
  29. * фреймворка Yii. Данный компонент приложения загружается динамически при необходимости.</li>
  30. * </ul>
  31. *
  32. * CApplication работает по следующему жизненному циклу при обработке пользовательского запроса:
  33. * <ol>
  34. * <li>загружает конфигурацию приложения;</li>
  35. * <li>устанавливает класс автозагрузчика и обработчика ошибок;</li>
  36. * <li>загружает статические компоненты приложения;</li>
  37. * <li>{@link onBeginRequest}: выполняет действия перед выполнением пользовательского запроса;</li>
  38. * <li>{@link processRequest}: выполняет пользовательский запрос;</li>
  39. * <li>{@link onEndRequest}: выполняет действия после выполнения пользовательского запроса;</li>
  40. * </ol>
  41. *
  42. * Начиная с пункта 3, при возникновении ошибки PHP или неперехваченного
  43. * исключения, приложение переключается на его обработчик ошибок и после
  44. * переходит к шагу 6.
  45. *
  46. * @property string $id уникальный идентификатор приложения
  47. * @property string $basePath корневая директория приложения. По умолчанию -
  48. * 'protected'
  49. * @property string $runtimePath директория, хранящая рабочие файлы. По
  50. * умолчанию - 'protected/runtime'
  51. * @property string $extensionPath директория, содержащая все расширения. По
  52. * умолчанию - директория 'extensions' в директории 'protected'
  53. * @property string $language язык, используемый пользователем и приложением.
  54. * По умолчанию задан свойством {@link sourceLanguage}
  55. * @property string $timeZone временная зона, используемая приложением
  56. * @property CLocale $locale экземпляр локали
  57. * @property string $localeDataPath директория, содержащая данные локали. По
  58. * умолчанию - 'framework/i18n/data'
  59. * @property CNumberFormatter $numberFormatter локалезависимый менеджер
  60. * форматирования чисел. Используется текущая {@link getLocale локаль приложения}
  61. * @property CDateFormatter $dateFormatter локалезависимый менеджер
  62. * форматирования дат. Используется текущая {@link getLocale локаль приложения}
  63. * @property CDbConnection $db компонент соединения с базой
  64. * @property CErrorHandler $errorHandler комопонент приложения, отвечающий за
  65. * обработку ошибок
  66. * @property CSecurityManager $securityManager компонент приложения, отвечающий
  67. * за безопасность
  68. * @property CStatePersister $statePersister компонент приложения,
  69. * представляющий постоянное состояние (state persister)
  70. * @property CCache $cache компонент приложения кэша. Null, если компонент не
  71. * включен
  72. * @property CPhpMessageSource $coreMessages компонент приложения, отвечающий
  73. * за перевод сообщений ядра
  74. * @property CMessageSource $messages компонент приложения, отвечающий за
  75. * перевод сообщений приложения
  76. * @property CHttpRequest $request компонент запроса
  77. * @property CUrlManager $urlManager менеджер URL маршрутов
  78. * @property CController $controller текущий активный контроллер. В данном
  79. * базовом классе возвращается значение null
  80. * @property string $baseUrl относительный URL-адрес приложения
  81. * @property string $homeUrl URL-адрес домашней страницы
  82. *
  83. * @author Qiang Xue <qiang.xue@gmail.com>
  84. * @version $Id: CApplication.php 3515 2011-12-28 12:29:24Z mdomba $
  85. * @package system.base
  86. * @since 1.0
  87. */
  88. abstract class CApplication extends CModule
  89. {
  90. /**
  91. * @var string имя приложения. По умолчанию - 'My Application'.
  92. */
  93. public $name='My Application';
  94. /**
  95. * @var string кодировка, используемая приложением. По умолчанию - 'UTF-8'.
  96. */
  97. public $charset='UTF-8';
  98. /**
  99. * @var string язык приложения. В основном это язык сообщений и представлений.
  100. * По умолчанию - 'en_us' (US English).
  101. */
  102. public $sourceLanguage='en_us';
  103. private $_id;
  104. private $_basePath;
  105. private $_runtimePath;
  106. private $_extensionPath;
  107. private $_globalState;
  108. private $_stateChanged;
  109. private $_ended=false;
  110. private $_language;
  111. private $_homeUrl;
  112. /**
  113. * Выполняет запрос.
  114. * Это то место, где выполняется основная работа по запросу.
  115. * Классы-наследники должны переопределить данный метод.
  116. */
  117. abstract public function processRequest();
  118. /**
  119. * Конструктор.
  120. * @param mixed $config конфигурация приложения.
  121. * Если передана строка, она считается путем к файлу, содержащему конфигурацию;
  122. * если передан массив, он считается реальной информацией конфигурации.
  123. * Убедитесь, что свойство {@link getBasePath basePath} определено в конфигурации и
  124. * указывает на директорию, содержащую всю логику приложения, шаблоны и данные.
  125. * Если это свойство не указано, по умолчанию будет использована директория 'protected'.
  126. */
  127. public function __construct($config=null)
  128. {
  129. Yii::setApplication($this);
  130. // set basePath at early as possible to avoid trouble
  131. if(is_string($config))
  132. $config=require($config);
  133. if(isset($config['basePath']))
  134. {
  135. $this->setBasePath($config['basePath']);
  136. unset($config['basePath']);
  137. }
  138. else
  139. $this->setBasePath('protected');
  140. Yii::setPathOfAlias('application',$this->getBasePath());
  141. Yii::setPathOfAlias('webroot',dirname($_SERVER['SCRIPT_FILENAME']));
  142. Yii::setPathOfAlias('ext',$this->getBasePath().DIRECTORY_SEPARATOR.'extensions');
  143. $this->preinit();
  144. $this->initSystemHandlers();
  145. $this->registerCoreComponents();
  146. $this->configure($config);
  147. $this->attachBehaviors($this->behaviors);
  148. $this->preloadComponents();
  149. $this->init();
  150. }
  151. /**
  152. * Запускает приложение.
  153. * Метод загружает статические компоненты приложения. Классы-наследники обычно переопределяют
  154. * данны метод для выполнения более специфичных задач приложения.
  155. * Не забудьте вызвать метод родителя для загрузки статических компонентов приложения.
  156. */
  157. public function run()
  158. {
  159. if($this->hasEventHandler('onBeginRequest'))
  160. $this->onBeginRequest(new CEvent($this));
  161. $this->processRequest();
  162. if($this->hasEventHandler('onEndRequest'))
  163. $this->onEndRequest(new CEvent($this));
  164. }
  165. /**
  166. * Завершает приложение.
  167. * Метод заменяет PHP функцию exit() вызовом метода {@link onEndRequest} перед выходом.
  168. * @param integer $status статус выхода (значение 0 означает нормальный выход, другое значение означает выход с ошибкой)
  169. * @param boolean $exit завершить ли текущий запрос. Данный параметр доступен с версии 1.1.5.
  170. * По умолчанию - true, т.е., PHP функция exit() будет вызываться в конце данного метода
  171. */
  172. public function end($status=0, $exit=true)
  173. {
  174. if($this->hasEventHandler('onEndRequest'))
  175. $this->onEndRequest(new CEvent($this));
  176. if($exit)
  177. exit($status);
  178. }
  179. /**
  180. * Выполняется непосредственно ПЕРЕД обработкой запроса приложением.
  181. * @param CEvent $event параметр события
  182. */
  183. public function onBeginRequest($event)
  184. {
  185. $this->raiseEvent('onBeginRequest',$event);
  186. }
  187. /**
  188. * Выполняется сразу ПОСЛЕ обработки запроса приложением.
  189. * @param CEvent $event параметр события
  190. */
  191. public function onEndRequest($event)
  192. {
  193. if(!$this->_ended)
  194. {
  195. $this->_ended=true;
  196. $this->raiseEvent('onEndRequest',$event);
  197. }
  198. }
  199. /**
  200. * Возвращает уникальный идентификатор приложения
  201. * @return string уникальный идентификатор приложения
  202. */
  203. public function getId()
  204. {
  205. if($this->_id!==null)
  206. return $this->_id;
  207. else
  208. return $this->_id=sprintf('%x',crc32($this->getBasePath().$this->name));
  209. }
  210. /**
  211. * Устанавливает уникальный идентификатор приложения
  212. * @param string $id уникальный идентификатор приложения
  213. */
  214. public function setId($id)
  215. {
  216. $this->_id=$id;
  217. }
  218. /**
  219. * Возвращает корневую директорию приложения
  220. * @return string корневая директория приложения. По умолчанию - 'protected'
  221. */
  222. public function getBasePath()
  223. {
  224. return $this->_basePath;
  225. }
  226. /**
  227. * Устанавливает корневую директорию приложения.
  228. * Метод может быть вызван только в начале конструктора
  229. * @param string $path корневая директория приложения
  230. * @throws CException вызывается, если директория не существует
  231. */
  232. public function setBasePath($path)
  233. {
  234. if(($this->_basePath=realpath($path))===false || !is_dir($this->_basePath))
  235. throw new CException(Yii::t('yii','Application base path "{path}" is not a valid directory.',
  236. array('{path}'=>$path)));
  237. }
  238. /**
  239. * Возвращает директорию, хранящую рабочие файлы
  240. * @return string директория, хранящая рабочие файлы. По умолчанию - 'protected/runtime'
  241. */
  242. public function getRuntimePath()
  243. {
  244. if($this->_runtimePath!==null)
  245. return $this->_runtimePath;
  246. else
  247. {
  248. $this->setRuntimePath($this->getBasePath().DIRECTORY_SEPARATOR.'runtime');
  249. return $this->_runtimePath;
  250. }
  251. }
  252. /**
  253. * Устанавливает директорию, хранящую рабочие файлы
  254. * @param string $path директория, хранящая рабочие файлы
  255. * @throws CException вызывается, если директория не существует или недоступна для записи
  256. */
  257. public function setRuntimePath($path)
  258. {
  259. if(($runtimePath=realpath($path))===false || !is_dir($runtimePath) || !is_writable($runtimePath))
  260. 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.',
  261. array('{path}'=>$path)));
  262. $this->_runtimePath=$runtimePath;
  263. }
  264. /**
  265. * Возвращает корневую директорию, хранящую все сторонние расширения
  266. * @return string директория, содержащая все расширения. По умолчанию - директория 'extensions' в директории 'protected'
  267. */
  268. public function getExtensionPath()
  269. {
  270. return Yii::getPathOfAlias('ext');
  271. }
  272. /**
  273. * Устанавливает корневую директорию, хранящую все сторонние расширения
  274. * @param string $path директория, содержащая все сторонние расширения
  275. */
  276. public function setExtensionPath($path)
  277. {
  278. if(($extensionPath=realpath($path))===false || !is_dir($extensionPath))
  279. throw new CException(Yii::t('yii','Extension path "{path}" does not exist.',
  280. array('{path}'=>$path)));
  281. Yii::setPathOfAlias('ext',$extensionPath);
  282. }
  283. /**
  284. * Возвращает язык, используемый пользователем и приложением
  285. * @return string язык, используемый пользователем и приложением.
  286. * По умолчанию задан свойством {@link sourceLanguage}
  287. */
  288. public function getLanguage()
  289. {
  290. return $this->_language===null ? $this->sourceLanguage : $this->_language;
  291. }
  292. /**
  293. * Определяет язык, используемый приложением.
  294. *
  295. * Это язык, отображаемый приложением конечным пользователям.
  296. * Если null, будет использован язык, заданный свойством {@link sourceLanguage}.
  297. *
  298. * Если ваше приложение должно поддерживать несколько языков, вы должны всегда
  299. * устанавливать данный язык в null для улучшения производительности приложения
  300. * @param string $language язык пользователя (например, 'en_US', 'zh_CN').
  301. * Если null, будет использован язык, заданный свойством {@link sourceLanguage}
  302. */
  303. public function setLanguage($language)
  304. {
  305. $this->_language=$language;
  306. }
  307. /**
  308. * Возвращает временную зону, используемую приложением.
  309. * Это простая обертка PHP-функции date_default_timezone_get()
  310. * @return string временная зона, используемая приложением
  311. * @see http://php.net/manual/en/function.date-default-timezone-get.php
  312. */
  313. public function getTimeZone()
  314. {
  315. return date_default_timezone_get();
  316. }
  317. /**
  318. * Устанавливает временную зону, используемую приложением.
  319. * Это простая обертка PHP-функции date_default_timezone_set()
  320. * @param string $value временная зона, используемая приложением
  321. * @see http://php.net/manual/en/function.date-default-timezone-set.php
  322. */
  323. public function setTimeZone($value)
  324. {
  325. date_default_timezone_set($value);
  326. }
  327. /**
  328. * Возвращает локализованную версию определенного файла.
  329. *
  330. * Поиск идет по коду определенного языка. В частности,
  331. * файл с таким же именем будет искаться в поддиректории с именем,
  332. * равным иеднтификатору локали. Например, если переданы файл "path/to/view.php"
  333. * и локаль "zh_cn", то путёт поиска локализованнного файла будет
  334. * "path/to/zh_cn/view.php". Если файл не найден, будет возвращен оригинальный файл.
  335. *
  336. * Для согласованности рекомендуется передавать идентификатор локали
  337. * в нижнем регистре и в формате идентификаторЯзыка_идентификаторРегиона (например, "en_us")
  338. *
  339. * @param string $srcFile оригинальный файл
  340. * @param string $srcLanguage язык оригинального файла. Если null, используется язык, заданный свойством {@link sourceLanguage}
  341. * @param string $language желаемый язык, локализованная версия файла которого требуется. Если null, используется {@link getLanguage язык приложения}
  342. * @return string соответствующий локализованный файл. Если локализованныя версия не найдена или исходный язык равен желаемомоу, возвращается оригинальный файл
  343. */
  344. public function findLocalizedFile($srcFile,$srcLanguage=null,$language=null)
  345. {
  346. if($srcLanguage===null)
  347. $srcLanguage=$this->sourceLanguage;
  348. if($language===null)
  349. $language=$this->getLanguage();
  350. if($language===$srcLanguage)
  351. return $srcFile;
  352. $desiredFile=dirname($srcFile).DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.basename($srcFile);
  353. return is_file($desiredFile) ? $desiredFile : $srcFile;
  354. }
  355. /**
  356. * Возвращает экземпляр локали
  357. * @param string $localeID идентификатор локали (например, en_US). Если null, используется идентификатор {@link getLanguage языка приложения}
  358. * @return CLocale экземпляр локали
  359. */
  360. public function getLocale($localeID=null)
  361. {
  362. return CLocale::getInstance($localeID===null?$this->getLanguage():$localeID);
  363. }
  364. /**
  365. * Возвращает директорию, содержащую данные локали
  366. * @return string директория, содержащая данные локали. По умолчанию - 'framework/i18n/data'
  367. * @since 1.1.0
  368. */
  369. public function getLocaleDataPath()
  370. {
  371. return CLocale::$dataPath===null ? Yii::getPathOfAlias('system.i18n.data') : CLocale::$dataPath;
  372. }
  373. /**
  374. * Устанавливает директорию, содержащую данные локали
  375. * @param string $value директория, содержащая данные локали
  376. * @since 1.1.0
  377. */
  378. public function setLocaleDataPath($value)
  379. {
  380. CLocale::$dataPath=$value;
  381. }
  382. /**
  383. * Возвращает локалезависимый менеджер форматирования чисел
  384. * @return CNumberFormatter локалезависимый менеджер форматирования чисел.
  385. * Используется текущая {@link getLocale локаль приложения}
  386. */
  387. public function getNumberFormatter()
  388. {
  389. return $this->getLocale()->getNumberFormatter();
  390. }
  391. /**
  392. * Возвращает локалезависимый менеджер форматирования дат
  393. * @return CDateFormatter локалезависимый менеджер форматирования дат.
  394. * Используется текущая {@link getLocale локаль приложения}
  395. */
  396. public function getDateFormatter()
  397. {
  398. return $this->getLocale()->getDateFormatter();
  399. }
  400. /**
  401. * Возвращает компонент соединения с базой
  402. * @return CDbConnection компонент соединения с базой
  403. */
  404. public function getDb()
  405. {
  406. return $this->getComponent('db');
  407. }
  408. /**
  409. * Возвращает комопонент приложения, отвечающий за обработку ошибок
  410. * @return CErrorHandler комопонент приложения, отвечающий за обработку ошибок
  411. */
  412. public function getErrorHandler()
  413. {
  414. return $this->getComponent('errorHandler');
  415. }
  416. /**
  417. * Возвращает компонент приложения, отвечающий за безопасность
  418. * @return CSecurityManager компонент приложения, отвечающий за безопасность
  419. */
  420. public function getSecurityManager()
  421. {
  422. return $this->getComponent('securityManager');
  423. }
  424. /**
  425. * Возвращает компонент приложения, представляющий постоянное состояние
  426. * (state persister)
  427. * @return CStatePersister компонент приложения, представляющий постоянное
  428. * состояние (state persister)
  429. */
  430. public function getStatePersister()
  431. {
  432. return $this->getComponent('statePersister');
  433. }
  434. /**
  435. * Возвращает компонент приложения кэша
  436. * @return CCache компонент приложения кэша. Null, если компонент не включен
  437. */
  438. public function getCache()
  439. {
  440. return $this->getComponent('cache');
  441. }
  442. /**
  443. * Возвращает компонент приложения, отвечающий за перевод сообщений ядра
  444. * @return CPhpMessageSource компонент приложения, отвечающий за перевод сообщений ядра
  445. */
  446. public function getCoreMessages()
  447. {
  448. return $this->getComponent('coreMessages');
  449. }
  450. /**
  451. * Возвращает компонент приложения, отвечающий за перевод сообщений приложения
  452. * @return CMessageSource компонент приложения, отвечающий за перевод сообщений приложения
  453. */
  454. public function getMessages()
  455. {
  456. return $this->getComponent('messages');
  457. }
  458. /**
  459. * Возвращает компонент запроса
  460. * @return CHttpRequest компонент запроса
  461. */
  462. public function getRequest()
  463. {
  464. return $this->getComponent('request');
  465. }
  466. /**
  467. * Возвращает менеджер URL маршрутов
  468. * @return CUrlManager менеджер URL маршрутов
  469. */
  470. public function getUrlManager()
  471. {
  472. return $this->getComponent('urlManager');
  473. }
  474. /**
  475. * @return CController текущий активный контроллер. В данном базовом классе
  476. * возвращается значение null
  477. * @since 1.1.8
  478. */
  479. public function getController()
  480. {
  481. return null;
  482. }
  483. /**
  484. * Создает относительный URL-адрес приложения на основе информации о
  485. * переданных контроллере и действии
  486. * @param string $route URL-маршрут. Должен быть в формате
  487. * 'ControllerID/ActionID'
  488. * @param array $params дополнительные GET-параметры (имя => значение). И
  489. * имя и значение пройдут URL-кодирование
  490. * @param string $ampersand символ, разделяющий пары имя-значение в
  491. * URL-адресе
  492. * @return string созданный URL-адрес
  493. */
  494. public function createUrl($route,$params=array(),$ampersand='&')
  495. {
  496. return $this->getUrlManager()->createUrl($route,$params,$ampersand);
  497. }
  498. /**
  499. * Создает абсолютный URL-адрес приложения на основе информации о
  500. * переданных контроллере и действии
  501. * @param string $route URL-маршрут. Должен быть в формате
  502. * 'ControllerID/ActionID'
  503. * @param array $params дополнительные GET-параметры (имя => значение). И
  504. * имя и значение пройдут URL-кодирование
  505. * @param string $schema используемый протокол (например, http, https).
  506. * Если пусто, то используется протокол текущего запроса
  507. * @param string $ampersand символ, разделяющий пары имя-значение в
  508. * URL-адресе
  509. * @return string созданный URL-адрес
  510. */
  511. public function createAbsoluteUrl($route,$params=array(),$schema='',$ampersand='&')
  512. {
  513. $url=$this->createUrl($route,$params,$ampersand);
  514. if(strpos($url,'http')===0)
  515. return $url;
  516. else
  517. return $this->getRequest()->getHostInfo($schema).$url;
  518. }
  519. /**
  520. * Возвращает относительный URL-адрес приложения. Является оберткой для
  521. * метода {@link CHttpRequest::getBaseUrl()}
  522. * @param boolean $absolute возвращать ли абсолютный URL-адрес. По
  523. * умолчанию - false, т.е., возвращается относительный URL-адрес
  524. * @return string относительный URL-адрес приложения
  525. * @see CHttpRequest::getBaseUrl()
  526. */
  527. public function getBaseUrl($absolute=false)
  528. {
  529. return $this->getRequest()->getBaseUrl($absolute);
  530. }
  531. /**
  532. * @return string URL-адрес домашней страницы
  533. */
  534. public function getHomeUrl()
  535. {
  536. if($this->_homeUrl===null)
  537. {
  538. if($this->getUrlManager()->showScriptName)
  539. return $this->getRequest()->getScriptUrl();
  540. else
  541. return $this->getRequest()->getBaseUrl().'/';
  542. }
  543. else
  544. return $this->_homeUrl;
  545. }
  546. /**
  547. * @param string $value URL-адрес домашней страницы
  548. */
  549. public function setHomeUrl($value)
  550. {
  551. $this->_homeUrl=$value;
  552. }
  553. /**
  554. * Возвращает глобальное значение.
  555. *
  556. * Глобальное значение - это постоянное для пользовательских сессий и запросов значение.
  557. * @param string $key имя возвращаемого значения
  558. * @param mixed $defaultValue значение по умолчанию. Возвращается, если именованное глобальное значение не было найдено.
  559. * @return mixed именованное глобальное значение
  560. * @see setGlobalState
  561. */
  562. public function getGlobalState($key,$defaultValue=null)
  563. {
  564. if($this->_globalState===null)
  565. $this->loadGlobalState();
  566. if(isset($this->_globalState[$key]))
  567. return $this->_globalState[$key];
  568. else
  569. return $defaultValue;
  570. }
  571. /**
  572. * Устанавливает глобальное значение.
  573. *
  574. * Глобальное значение - это постоянное для пользовательских сессий и запросов значение.
  575. * Убедитесь, что значение сериализуемо и десереализуемо.
  576. * @param string $key имя сохраняемого значения
  577. * @param mixed $value сохраняемое значение. Должно быть сериализуемо
  578. * @param mixed $defaultValue значение по умолчанию. Если именованое глобальное значение такое же как и данное, оно будет удалено из текущего хранилища
  579. * @see getGlobalState
  580. */
  581. public function setGlobalState($key,$value,$defaultValue=null)
  582. {
  583. if($this->_globalState===null)
  584. $this->loadGlobalState();
  585. $changed=$this->_stateChanged;
  586. if($value===$defaultValue)
  587. {
  588. if(isset($this->_globalState[$key]))
  589. {
  590. unset($this->_globalState[$key]);
  591. $this->_stateChanged=true;
  592. }
  593. }
  594. else if(!isset($this->_globalState[$key]) || $this->_globalState[$key]!==$value)
  595. {
  596. $this->_globalState[$key]=$value;
  597. $this->_stateChanged=true;
  598. }
  599. if($this->_stateChanged!==$changed)
  600. $this->attachEventHandler('onEndRequest',array($this,'saveGlobalState'));
  601. }
  602. /**
  603. * Очищает глобальное значение.
  604. *
  605. * Очищенное значение больше не будет доступно ни в данном запросе ни в последующих.
  606. * @param string $key имя очищаемого значения
  607. */
  608. public function clearGlobalState($key)
  609. {
  610. $this->setGlobalState($key,true,true);
  611. }
  612. /**
  613. * Загружает данные глобального значения из постоянного хранилища.
  614. * @see getStatePersister
  615. * @throws CException вызывается, если менеджер постоянного состояния недоступен
  616. */
  617. public function loadGlobalState()
  618. {
  619. $persister=$this->getStatePersister();
  620. if(($this->_globalState=$persister->load())===null)
  621. $this->_globalState=array();
  622. $this->_stateChanged=false;
  623. $this->detachEventHandler('onEndRequest',array($this,'saveGlobalState'));
  624. }
  625. /**
  626. * Сохраняет данные глобального состояния в постоянное хранилище.
  627. * @see getStatePersister
  628. * @throws CException вызывается, если менеджер постоянного состояния недоступен
  629. */
  630. public function saveGlobalState()
  631. {
  632. if($this->_stateChanged)
  633. {
  634. $this->_stateChanged=false;
  635. $this->detachEventHandler('onEndRequest',array($this,'saveGlobalState'));
  636. $this->getStatePersister()->save($this->_globalState);
  637. }
  638. }
  639. /**
  640. * Обрабатывает неперехваченные исключения PHP.
  641. *
  642. * Метод реализован как обработчик исключений PHP. Он требует, чтобы
  643. * константа YII_ENABLE_EXCEPTION_HANDLER была установлена в значение true.
  644. *
  645. * Сначала метод вызывает событие {@link onException}.
  646. * Если исключение не обработано каким-либо другим обработчиком, для его
  647. * обработки будет вызван {@link getErrorHandler errorHandler}.
  648. *
  649. * При вызове данного метода приложение завершается.
  650. *
  651. * @param Exception $exception неперехваченное исключение
  652. */
  653. public function handleException($exception)
  654. {
  655. // disable error capturing to avoid recursive errors
  656. restore_error_handler();
  657. restore_exception_handler();
  658. $category='exception.'.get_class($exception);
  659. if($exception instanceof CHttpException)
  660. $category.='.'.$exception->statusCode;
  661. // php <5.2 doesn't support string conversion auto-magically
  662. $message=$exception->__toString();
  663. if(isset($_SERVER['REQUEST_URI']))
  664. $message.="\nREQUEST_URI=".$_SERVER['REQUEST_URI'];
  665. if(isset($_SERVER['HTTP_REFERER']))
  666. $message.="\nHTTP_REFERER=".$_SERVER['HTTP_REFERER'];
  667. $message.="\n---";
  668. Yii::log($message,CLogger::LEVEL_ERROR,$category);
  669. try
  670. {
  671. $event=new CExceptionEvent($this,$exception);
  672. $this->onException($event);
  673. if(!$event->handled)
  674. {
  675. // try an error handler
  676. if(($handler=$this->getErrorHandler())!==null)
  677. $handler->handle($event);
  678. else
  679. $this->displayException($exception);
  680. }
  681. }
  682. catch(Exception $e)
  683. {
  684. $this->displayException($e);
  685. }
  686. try
  687. {
  688. $this->end(1);
  689. }
  690. catch(Exception $e)
  691. {
  692. // use the most primitive way to log error
  693. $msg = get_class($e).': '.$e->getMessage().' ('.$e->getFile().':'.$e->getLine().")\n";
  694. $msg .= $e->getTraceAsString()."\n";
  695. $msg .= "Previous exception:\n";
  696. $msg .= get_class($exception).': '.$exception->getMessage().' ('.$exception->getFile().':'.$exception->getLine().")\n";
  697. $msg .= $exception->getTraceAsString()."\n";
  698. $msg .= '$_SERVER='.var_export($_SERVER,true);
  699. error_log($msg);
  700. exit(1);
  701. }
  702. }
  703. /**
  704. * Обрабатывает ошибки выполнения PHP такие, как предупреждения (warnings), замечания (notices).
  705. *
  706. * Метод реализован как обработчик ошибок PHP. Он требует, чтобы
  707. * константа YII_ENABLE_ERROR_HANDLER была установлена в значение true.
  708. *
  709. * Сначала метод вызывает событие {@link onError}.
  710. * Если ошибка не обработана каким-либо другим обработчиком, для ее
  711. * обработки будет вызван {@link getErrorHandler errorHandler}.
  712. *
  713. * При вызове данного метода приложение завершается.
  714. *
  715. * @param integer $code уровень ошибки
  716. * @param string $message сообщение ошибки
  717. * @param string $file файл, в котором произошла ошибка
  718. * @param string $line строка кода, в которой произошла ошибка
  719. */
  720. public function handleError($code,$message,$file,$line)
  721. {
  722. if($code & error_reporting())
  723. {
  724. // disable error capturing to avoid recursive errors
  725. restore_error_handler();
  726. restore_exception_handler();
  727. $log="$message ($file:$line)\nStack trace:\n";
  728. $trace=debug_backtrace();
  729. // skip the first 3 stacks as they do not tell the error position
  730. if(count($trace)>3)
  731. $trace=array_slice($trace,3);
  732. foreach($trace as $i=>$t)
  733. {
  734. if(!isset($t['file']))
  735. $t['file']='unknown';
  736. if(!isset($t['line']))
  737. $t['line']=0;
  738. if(!isset($t['function']))
  739. $t['function']='unknown';
  740. $log.="#$i {$t['file']}({$t['line']}): ";
  741. if(isset($t['object']) && is_object($t['object']))
  742. $log.=get_class($t['object']).'->';
  743. $log.="{$t['function']}()\n";
  744. }
  745. if(isset($_SERVER['REQUEST_URI']))
  746. $log.='REQUEST_URI='.$_SERVER['REQUEST_URI'];
  747. Yii::log($log,CLogger::LEVEL_ERROR,'php');
  748. try
  749. {
  750. Yii::import('CErrorEvent',true);
  751. $event=new CErrorEvent($this,$code,$message,$file,$line);
  752. $this->onError($event);
  753. if(!$event->handled)
  754. {
  755. // try an error handler
  756. if(($handler=$this->getErrorHandler())!==null)
  757. $handler->handle($event);
  758. else
  759. $this->displayError($code,$message,$file,$line);
  760. }
  761. }
  762. catch(Exception $e)
  763. {
  764. $this->displayException($e);
  765. }
  766. try
  767. {
  768. $this->end(1);
  769. }
  770. catch(Exception $e)
  771. {
  772. // use the most primitive way to log error
  773. $msg = get_class($e).': '.$e->getMessage().' ('.$e->getFile().':'.$e->getLine().")\n";
  774. $msg .= $e->getTraceAsString()."\n";
  775. $msg .= "Previous error:\n";
  776. $msg .= $log."\n";
  777. $msg .= '$_SERVER='.var_export($_SERVER,true);
  778. error_log($msg);
  779. exit(1);
  780. }
  781. }
  782. }
  783. /**
  784. * Выполняется при возникновении неперехваченного исключения PHP.
  785. *
  786. * Обработчик события может установить свойство {@link CErrorEvent::handled handled}
  787. * параметра события в значение true для индикации того, что дальнейшая обработка ошибок не
  788. * требуется. В ином случае, компонент приложения {@link getErrorHandler errorHandler}
  789. * будет продолжать обрабатывать ошибки.
  790. *
  791. * @param CExceptionEvent $event параметр события
  792. */
  793. public function onException($event)
  794. {
  795. $this->raiseEvent('onException',$event);
  796. }
  797. /**
  798. * Выполняется при возникновении ошибки исполнения скрипта PHP.
  799. *
  800. * Обработчик события может установить свойство {@link CErrorEvent::handled handled}
  801. * параметра события в значение true для индикации того, что дальнейшая обработка ошибок не
  802. * требуется. В ином случае, компонент приложения {@link getErrorHandler errorHandler}
  803. * будет продолжать обрабатывать ошибки.
  804. *
  805. * @param CErrorEvent $event параметр события
  806. */
  807. public function onError($event)
  808. {
  809. $this->raiseEvent('onError',$event);
  810. }
  811. /**
  812. * Отображает перехваченную ошибку PHP.
  813. * Метод отображает ошибку в коде HTML, если
  814. * для нее нет обработчика.
  815. * @param integer $code код ошибки
  816. * @param string $message сообщение об ошибке
  817. * @param string $file файл, в котором произошла ошибка
  818. * @param string $line строка кода, в которой произошла ошибка
  819. */
  820. public function displayError($code,$message,$file,$line)
  821. {
  822. if(YII_DEBUG)
  823. {
  824. echo "<h1>PHP Error [$code]</h1>\n";
  825. echo "<p>$message ($file:$line)</p>\n";
  826. echo '<pre>';
  827. $trace=debug_backtrace();
  828. // skip the first 3 stacks as they do not tell the error position
  829. if(count($trace)>3)
  830. $trace=array_slice($trace,3);
  831. foreach($trace as $i=>$t)
  832. {
  833. if(!isset($t['file']))
  834. $t['file']='unknown';
  835. if(!isset($t['line']))
  836. $t['line']=0;
  837. if(!isset($t['function']))
  838. $t['function']='unknown';
  839. echo "#$i {$t['file']}({$t['line']}): ";
  840. if(isset($t['object']) && is_object($t['object']))
  841. echo get_class($t['object']).'->';
  842. echo "{$t['function']}()\n";
  843. }
  844. echo '</pre>';
  845. }
  846. else
  847. {
  848. echo "<h1>PHP Error [$code]</h1>\n";
  849. echo "<p>$message</p>\n";
  850. }
  851. }
  852. /**
  853. * Отображает неперехваченные исключения PHP.
  854. * Метод отображает исключения в HTML, когда нет активного обработчика ошибок.
  855. * @param Exception $exception неперехваченное исключение
  856. */
  857. public function displayException($exception)
  858. {
  859. if(YII_DEBUG)
  860. {
  861. echo '<h1>'.get_class($exception)."</h1>\n";
  862. echo '<p>'.$exception->getMessage().' ('.$exception->getFile().':'.$exception->getLine().')</p>';
  863. echo '<pre>'.$exception->getTraceAsString().'</pre>';
  864. }
  865. else
  866. {
  867. echo '<h1>'.get_class($exception)."</h1>\n";
  868. echo '<p>'.$exception->getMessage().'</p>';
  869. }
  870. }
  871. /**
  872. * Инициализирует обработчики исключений и ошибок.
  873. */
  874. protected function initSystemHandlers()
  875. {
  876. if(YII_ENABLE_EXCEPTION_HANDLER)
  877. set_exception_handler(array($this,'handleException'));
  878. if(YII_ENABLE_ERROR_HANDLER)
  879. set_error_handler(array($this,'handleError'),error_reporting());
  880. }
  881. /**
  882. * Регистрирует компоненты ядра приложения.
  883. * @see setComponents
  884. */
  885. protected function registerCoreComponents()
  886. {
  887. $components=array(
  888. 'coreMessages'=>array(
  889. 'class'=>'CPhpMessageSource',
  890. 'language'=>'en_us',
  891. 'basePath'=>YII_PATH.DIRECTORY_SEPARATOR.'messages',
  892. ),
  893. 'db'=>array(
  894. 'class'=>'CDbConnection',
  895. ),
  896. 'messages'=>array(
  897. 'class'=>'CPhpMessageSource',
  898. ),
  899. 'errorHandler'=>array(
  900. 'class'=>'CErrorHandler',
  901. ),
  902. 'securityManager'=>array(
  903. 'class'=>'CSecurityManager',
  904. ),
  905. 'statePersister'=>array(
  906. 'class'=>'CStatePersister',
  907. ),
  908. 'urlManager'=>array(
  909. 'class'=>'CUrlManager',
  910. ),
  911. 'request'=>array(
  912. 'class'=>'CHttpRequest',
  913. ),
  914. 'format'=>array(
  915. 'class'=>'CFormatter',
  916. ),
  917. );
  918. $this->setComponents($components);
  919. }
  920. }