/bitrix/modules/main/lib/diag/exceptionhandler.php

https://gitlab.com/neuser/bitrix-core · PHP · 371 lines · 180 code · 39 blank · 152 comment · 16 complexity · 591ca836522922d5d5d12977ecd2ab80 MD5 · raw file

  1. <?php
  2. namespace Bitrix\Main\Diag;
  3. use Bitrix\Main;
  4. class ExceptionHandler
  5. {
  6. private $debug = false;
  7. private $handledErrorsTypes;
  8. private $exceptionErrorsTypes;
  9. private $catchOverflowMemory = false;
  10. private $memoryReserveLimit = 65536;
  11. private $memoryReserve;
  12. private $ignoreSilence = false;
  13. private $assertionThrowsException = true;
  14. private $assertionErrorType = E_USER_ERROR;
  15. /**
  16. * @var ExceptionHandlerLog
  17. */
  18. private $handlerLog = null;
  19. private $handlerLogCreator = null;
  20. /**
  21. * @var IExceptionHandlerOutput
  22. */
  23. private $handlerOutput = null;
  24. private $handlerOutputCreator = null;
  25. private $isInitialized = false;
  26. /**
  27. * ExceptionHandler constructor.
  28. */
  29. public function __construct()
  30. {
  31. $this->handledErrorsTypes = E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR;
  32. $this->exceptionErrorsTypes = E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR;
  33. }
  34. /**
  35. * Sets debug mode.
  36. * Should be used for development install.
  37. *
  38. * @param boolean $debug If true errors will be displayed in html output. If false most errors will be suppressed.
  39. *
  40. * @return void
  41. */
  42. public function setDebugMode($debug)
  43. {
  44. $this->debug = $debug;
  45. }
  46. /**
  47. * Whenever to try catch and report memory overflows errors or not.
  48. *
  49. * @param boolean $catchOverflowMemory If true memory overflow errors will be handled.
  50. *
  51. * @return void
  52. */
  53. public function setOverflowMemoryCatching($catchOverflowMemory)
  54. {
  55. $this->catchOverflowMemory = $catchOverflowMemory;
  56. }
  57. /**
  58. * Sets error types to be handled.
  59. *
  60. * @param integer $handledErrorsTypes Bitmask of error types.
  61. *
  62. * @return void
  63. * @see http://php.net/manual/en/errorfunc.constants.php
  64. */
  65. public function setHandledErrorsTypes($handledErrorsTypes)
  66. {
  67. $this->handledErrorsTypes = $handledErrorsTypes;
  68. }
  69. /**
  70. * Sets assertion types to be handled.
  71. *
  72. * @param integer $assertionErrorType Bitmask of assertion types.
  73. *
  74. * @return void
  75. * @see http://php.net/manual/en/errorfunc.constants.php
  76. */
  77. public function setAssertionErrorType($assertionErrorType)
  78. {
  79. $this->assertionErrorType = $assertionErrorType;
  80. }
  81. /**
  82. * Whenever to throw an exception on assertion or not.
  83. *
  84. * @param boolean $assertionThrowsException If true an assertion will throw exception.
  85. *
  86. * @return void
  87. */
  88. public function setAssertionThrowsException($assertionThrowsException)
  89. {
  90. $this->assertionThrowsException = $assertionThrowsException;
  91. }
  92. /**
  93. * Sets which errors will raise an exception.
  94. *
  95. * @param integer $errorTypesException Bitmask of error types.
  96. *
  97. * @return void
  98. * @see http://php.net/manual/en/errorfunc.constants.php
  99. */
  100. public function setExceptionErrorsTypes($errorTypesException)
  101. {
  102. $this->exceptionErrorsTypes = $errorTypesException;
  103. }
  104. /**
  105. * Whenever to ignore error_reporting() == 0 or not.
  106. *
  107. * @param boolean $ignoreSilence If true then error_reporting()==0 will be ignored.
  108. *
  109. * @return void
  110. */
  111. public function setIgnoreSilence($ignoreSilence)
  112. {
  113. $this->ignoreSilence = $ignoreSilence;
  114. }
  115. /**
  116. * Sets logger object to use for log writing.
  117. *
  118. * @param \Bitrix\Main\Diag\ExceptionHandlerLog $handlerLog Logger object.
  119. *
  120. * @return void
  121. */
  122. public function setHandlerLog(\Bitrix\Main\Diag\ExceptionHandlerLog $handlerLog = null)
  123. {
  124. $this->handlerLog = $handlerLog;
  125. }
  126. /**
  127. * Sets an object used for error message display to user.
  128. *
  129. * @param \Bitrix\Main\Diag\IExceptionHandlerOutput $handlerOutput Object will display errors to user.
  130. *
  131. * @return void
  132. */
  133. public function setHandlerOutput(\Bitrix\Main\Diag\IExceptionHandlerOutput $handlerOutput)
  134. {
  135. $this->handlerOutput = $handlerOutput;
  136. }
  137. /**
  138. * Adjusts PHP for error handling.
  139. *
  140. * @return void
  141. */
  142. protected function initializeEnvironment()
  143. {
  144. if ($this->debug)
  145. {
  146. error_reporting($this->handledErrorsTypes);
  147. @ini_set('display_errors', 'On');
  148. @ini_set('display_startup_errors', 'On');
  149. @ini_set('report_memleaks', 'On');
  150. }
  151. else
  152. {
  153. error_reporting(E_ERROR | E_PARSE);
  154. }
  155. }
  156. /**
  157. * Returns an object used for error message display to user.
  158. *
  159. * @return IExceptionHandlerOutput|null
  160. */
  161. protected function getHandlerOutput()
  162. {
  163. if ($this->handlerOutput === null)
  164. {
  165. $h = $this->handlerOutputCreator;
  166. if (is_callable($h))
  167. $this->handlerOutput = call_user_func_array($h, array());
  168. }
  169. return $this->handlerOutput;
  170. }
  171. /**
  172. * Returns an object for error message writing to log.
  173. *
  174. * @return ExceptionHandlerLog|null
  175. */
  176. protected function getHandlerLog()
  177. {
  178. if ($this->handlerLog === null)
  179. {
  180. $h = $this->handlerLogCreator;
  181. if (is_callable($h))
  182. $this->handlerLog = call_user_func_array($h, array());
  183. }
  184. return $this->handlerLog;
  185. }
  186. /**
  187. * Initializes error handling.
  188. * Must be called after the object creation.
  189. *
  190. * @param callable $exceptionHandlerOutputCreator Function to return an object for error message formatting.
  191. * @param callable|null $exceptionHandlerLogCreator Function to return an object for log writing.
  192. *
  193. * @return void
  194. */
  195. public function initialize($exceptionHandlerOutputCreator, $exceptionHandlerLogCreator = null)
  196. {
  197. if ($this->isInitialized)
  198. return;
  199. $this->initializeEnvironment();
  200. $this->handlerOutputCreator = $exceptionHandlerOutputCreator;
  201. $this->handlerLogCreator = $exceptionHandlerLogCreator;
  202. if ($this->catchOverflowMemory)
  203. {
  204. $this->memoryReserve = str_repeat('b', $this->memoryReserveLimit);
  205. }
  206. set_error_handler(array($this, "handleError"), $this->handledErrorsTypes);
  207. set_exception_handler(array($this, "handleException"));
  208. register_shutdown_function(array($this, "handleFatalError"));
  209. if ($this->debug)
  210. {
  211. assert_options(ASSERT_ACTIVE, 1);
  212. assert_options(ASSERT_WARNING, 0);
  213. assert_options(ASSERT_BAIL, 0);
  214. assert_options(ASSERT_CALLBACK, array($this, "handleAssertion"));
  215. }
  216. else
  217. {
  218. assert_options(ASSERT_ACTIVE, 0);
  219. }
  220. $this->isInitialized = true;
  221. }
  222. /**
  223. * Writes exception information into log, displays it to user and terminates with die().
  224. *
  225. * @param \Exception|\Error $exception Exception object.
  226. *
  227. * @param int $logType
  228. * @return void
  229. * @see \Bitrix\Main\Diag\ExceptionHandler::writeToLog
  230. * @see \Bitrix\Main\Diag\ExceptionHandler::initialize
  231. */
  232. public function handleException($exception, $logType = ExceptionHandlerLog::UNCAUGHT_EXCEPTION)
  233. {
  234. $this->writeToLog($exception, $logType);
  235. $out = $this->getHandlerOutput();
  236. $out->renderExceptionMessage($exception, $this->debug);
  237. die();
  238. }
  239. /**
  240. * Creates and exception object from its arguments.
  241. * Throws it if $code matches exception mask or writes it into log.
  242. *
  243. * @param integer $code Error code.
  244. * @param string $message Error message.
  245. * @param string $file File where error has occurred.
  246. * @param integer $line File line number where error has occurred.
  247. *
  248. * @return true
  249. * @throws \ErrorException
  250. * @see \Bitrix\Main\Diag\ExceptionHandler::setExceptionErrorsTypes
  251. */
  252. public function handleError($code, $message, $file, $line)
  253. {
  254. $exception = new \ErrorException($message, 0, $code, $file, $line);
  255. if ((error_reporting() === 0) && !$this->ignoreSilence)
  256. {
  257. return true;
  258. }
  259. if ($code & $this->exceptionErrorsTypes)
  260. {
  261. throw $exception;
  262. }
  263. else
  264. {
  265. $this->writeToLog($exception, ExceptionHandlerLog::LOW_PRIORITY_ERROR);
  266. return true;
  267. }
  268. }
  269. /**
  270. * Creates and exception object from its arguments.
  271. * Throws it if assertion set to raise exception (which is by default) or writes it to log.
  272. *
  273. * @param string $file File where error has occurred.
  274. * @param integer $line File line number where error has occurred.
  275. * @param string $message Error message.
  276. *
  277. * @return void
  278. * @throws \ErrorException
  279. * @see \Bitrix\Main\Diag\ExceptionHandler::setAssertionThrowsException
  280. */
  281. public function handleAssertion($file, $line, $message)
  282. {
  283. $exception = new \ErrorException($message, 0, $this->assertionErrorType, $file, $line);
  284. if ($this->assertionThrowsException)
  285. {
  286. throw $exception;
  287. }
  288. else
  289. {
  290. $this->writeToLog($exception, ExceptionHandlerLog::ASSERTION);
  291. return;
  292. }
  293. }
  294. /**
  295. * Gets error information from error_get_last() function.
  296. * Checks if type for certain error types and writes it to log.
  297. *
  298. * @return void
  299. * @see error_get_last
  300. * @see \Bitrix\Main\Diag\ExceptionHandler::setHandledErrorsTypes
  301. */
  302. public function handleFatalError()
  303. {
  304. unset($this->memoryReserve);
  305. if ($error = error_get_last())
  306. {
  307. if (($error['type'] & (E_ERROR | E_USER_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_RECOVERABLE_ERROR)))
  308. {
  309. if(($error['type'] & $this->handledErrorsTypes))
  310. {
  311. $exception = new \ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']);
  312. $this->handleException($exception, ExceptionHandlerLog::FATAL);
  313. }
  314. }
  315. }
  316. }
  317. /**
  318. * Writes an exception information to log.
  319. *
  320. * @param \Exception $exception Exception object.
  321. * @param integer|null $logType See ExceptionHandlerLog class constants.
  322. *
  323. * @return void
  324. * @see \Bitrix\Main\Diag\ExceptionHandler::initialize
  325. */
  326. public function writeToLog($exception, $logType = null)
  327. {
  328. $log = $this->getHandlerLog();
  329. if ($log !== null)
  330. $log->write($exception, $logType);
  331. }
  332. }