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
59 $this->errorScope
= CRM_Core_TemporaryErrorScope
::useException();
63 * Disable the error policy.
65 public function deactivate() {
66 $this->errorScope
= NULL;
67 restore_error_handler();
73 ini_set($key, $this->backup
[$key]);
75 $this->active
= FALSE;
79 * Execute the callable. Activate and deactivate the error policy
82 * @param callable|array|string $callable
83 * A callback function.
87 public function call($callable) {
90 $result = $callable();
93 $this->reportException($e);
100 * Receive (semi) recoverable error notices.
102 * @see set_error_handler
104 * @param string $errno
105 * @param string $errstr
106 * @param string $errfile
107 * @param int $errline
112 public function onError($errno, $errstr, $errfile, $errline) {
113 if (!(error_reporting() & $errno)) {
116 throw new Exception(sprintf('PHP Error %s at %s:%s: %s', $errno, $errfile, $errline, $errstr));
120 * Receive non-recoverable error notices
122 * @see register_shutdown_function
123 * @see error_get_last
125 public function onShutdown() {
126 if (!$this->active
) {
129 $error = error_get_last();
130 if (is_array($error) && ($error['type'] & $this->level
)) {
131 $this->reportError($error);
136 * Print a fatal error.
138 * @param array $error
139 * The PHP error (with "type", "message", etc).
141 public function reportError($error) {
145 'exception' => htmlentities(sprintf('Error %s: %s in %s, line %s', $error['type'], $error['message'], $error['file'], $error['line'])),
147 global $activeQueueRunner;
148 if (is_object($activeQueueRunner)) {
149 $response['last_task_title'] = $activeQueueRunner->lastTaskTitle
;
151 CRM_Core_Error
::debug_var('CRM_Queue_ErrorPolicy_reportError', $response);
152 echo json_encode($response);
153 // civiExit() is unnecessary -- we're only called as part of abend
157 * Print an unhandled exception.
159 * @param Exception $e
160 * The unhandled exception.
162 public function reportException(Exception
$e) {
163 CRM_Core_Error
::debug_var('CRM_Queue_ErrorPolicy_reportException', CRM_Core_Error
::formatTextException($e));
170 $config = CRM_Core_Config
::singleton();
171 if ($config->backtrace || CRM_Core_Config
::isUpgradeMode()) {
172 $response['exception'] = CRM_Core_Error
::formatHtmlException($e);
175 $response['exception'] = htmlentities($e->getMessage());
178 global $activeQueueRunner;
179 if (is_object($activeQueueRunner)) {
180 $response['last_task_title'] = $activeQueueRunner->lastTaskTitle
;
182 CRM_Utils_JSON
::output($response);