3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
13 * To ensure that PHP errors or unhandled exceptions are reported in JSON
14 * format, wrap this around your code. For example:
17 * $errorContainer = new CRM_Queue_ErrorPolicy();
18 * $errorContainer->call(function() {
19 * ...include some files, do some work, etc...
23 * Note: Most of the code in this class is pretty generic vis-a-vis error
24 * handling -- except for 'reportError', whose message format is only
25 * appropriate for use with the CRM_Queue_Page_AJAX. Some kind of cleanup
26 * will be necessary to get reuse from the other parts of this class.
28 class CRM_Queue_ErrorPolicy
{
32 * @param null|int $level
33 * PHP error level to capture (e.g. E_PARSE|E_USER_ERROR).
35 public function __construct($level = NULL) {
36 register_shutdown_function([$this, 'onShutdown']);
37 if ($level === NULL) {
38 $level = E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR
;
40 $this->level
= $level;
44 * Enable the error policy.
46 public function activate() {
54 $this->backup
[$key] = ini_get($key);
57 set_error_handler([$this, 'onError'], $this->level
);
58 // FIXME make this temporary/reversible
62 * Disable the error policy.
64 public function deactivate() {
65 $this->errorScope
= NULL;
66 restore_error_handler();
72 ini_set($key, $this->backup
[$key]);
74 $this->active
= FALSE;
78 * Execute the callable. Activate and deactivate the error policy
81 * @param callable|array|string $callable
82 * A callback function.
86 public function call($callable) {
89 $result = $callable();
92 $this->reportException($e);
99 * Receive (semi) recoverable error notices.
101 * @see set_error_handler
103 * @param string $errno
104 * @param string $errstr
105 * @param string $errfile
106 * @param int $errline
111 public function onError($errno, $errstr, $errfile, $errline) {
112 if (!(error_reporting() & $errno)) {
115 throw new Exception(sprintf('PHP Error %s at %s:%s: %s', $errno, $errfile, $errline, $errstr));
119 * Receive non-recoverable error notices
121 * @see register_shutdown_function
122 * @see error_get_last
124 public function onShutdown() {
125 if (!$this->active
) {
128 $error = error_get_last();
129 if (is_array($error) && ($error['type'] & $this->level
)) {
130 $this->reportError($error);
135 * Print a fatal error.
137 * @param array $error
138 * The PHP error (with "type", "message", etc).
140 public function reportError($error) {
144 'exception' => htmlentities(sprintf('Error %s: %s in %s, line %s', $error['type'], $error['message'], $error['file'], $error['line'])),
146 global $activeQueueRunner;
147 if (is_object($activeQueueRunner)) {
148 $response['last_task_title'] = $activeQueueRunner->lastTaskTitle
;
150 CRM_Core_Error
::debug_var('CRM_Queue_ErrorPolicy_reportError', $response);
151 echo json_encode($response);
152 // civiExit() is unnecessary -- we're only called as part of abend
156 * Print an unhandled exception.
158 * @param Exception $e
159 * The unhandled exception.
161 public function reportException(Exception
$e) {
162 CRM_Core_Error
::debug_var('CRM_Queue_ErrorPolicy_reportException', CRM_Core_Error
::formatTextException($e));
169 $config = CRM_Core_Config
::singleton();
170 if ($config->backtrace || CRM_Core_Config
::isUpgradeMode()) {
171 $response['exception'] = CRM_Core_Error
::formatHtmlException($e);
174 $response['exception'] = htmlentities($e->getMessage());
177 global $activeQueueRunner;
178 if (is_object($activeQueueRunner)) {
179 $response['last_task_title'] = $activeQueueRunner->lastTaskTitle
;
181 CRM_Utils_JSON
::output($response);