Skip to content

Introduce abstraction for error display callbacks. #5484

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Zend/zend.c
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,8 @@ int zend_startup(zend_utility_functions *utility_functions) /* {{{ */
fpsetmask(0);
#endif

zend_startup_error_display_functions();

zend_startup_strtod();
zend_startup_extensions_mechanism();

Expand Down Expand Up @@ -1077,6 +1079,7 @@ void zend_shutdown(void) /* {{{ */
free(GLOBAL_AUTO_GLOBALS_TABLE);

zend_shutdown_extensions();
zend_shutdown_error_display_functions();
free(zend_version_info);

free(GLOBAL_FUNCTION_TABLE);
Expand Down
105 changes: 105 additions & 0 deletions Zend/zend_errors.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
| Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 2.00 of the Zend license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.zend.com/license/2_00.txt. |
| If you did not receive a copy of the Zend license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@zend.com so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Benjamin Eberlei <beberlei@php.net> |
+----------------------------------------------------------------------+
*/

#include "zend.h"

/**
* A display callback is responsible for rendering an error. Extensions can
* append to the list of core error handlers. Callbacks are triggered from the
* tail of the list going to head.
*/
zend_llist zend_error_display_callbacks;

char *zend_error_get_type_string(int type)
{
char *error_type_str;

switch (type) {
case E_ERROR:
case E_CORE_ERROR:
case E_COMPILE_ERROR:
case E_USER_ERROR:
error_type_str = "Fatal error";
break;
case E_RECOVERABLE_ERROR:
error_type_str = "Recoverable fatal error";
break;
case E_WARNING:
case E_CORE_WARNING:
case E_COMPILE_WARNING:
case E_USER_WARNING:
error_type_str = "Warning";
break;
case E_PARSE:
error_type_str = "Parse error";
break;
case E_NOTICE:
case E_USER_NOTICE:
error_type_str = "Notice";
break;
case E_STRICT:
error_type_str = "Strict Standards";
break;
case E_DEPRECATED:
case E_USER_DEPRECATED:
error_type_str = "Deprecated";
break;
default:
error_type_str = "Unknown error";
break;
}

return error_type_str;
}

void zend_startup_error_display_functions(void)
{
zend_llist_init(&zend_error_display_callbacks, sizeof(zend_error_display_callback), NULL, 1);
}

void zend_shutdown_error_display_functions(void)
{
zend_llist_destroy(&zend_error_display_callbacks);
}

void zend_register_error_display_callback(zend_error_display_cb cb)
{
zend_error_display_callback callback;

callback.display_callback = cb;

zend_llist_add_element(&zend_error_display_callbacks, &callback);
}

int zend_error_display_handle(int type, const char *error_filename, const uint32_t error_lineno, char *buffer, int buffer_len)
{
zend_llist_element *element;
zend_error_display_callback *callback;
int handled = 0;

for (element = zend_error_display_callbacks.tail; element; element = element->prev) {
callback = (zend_error_display_callback*) element->data;
handled = callback->display_callback(type, error_filename, error_lineno, buffer, buffer_len);

if (handled == 1) {
return 1;
}
}

return 0;
}
14 changes: 14 additions & 0 deletions Zend/zend_errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,18 @@

#define E_HAS_ONLY_FATAL_ERRORS(mask) !((mask) & ~E_FATAL_ERRORS)

typedef int (*zend_error_display_cb)(int type, const char *error_filename, const uint32_t error_lineno, char *buffer, int buffer_len);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either this should return zend_bool, or it should return SUCCESS/FAILURE.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buffer_len should probably be size_t?


BEGIN_EXTERN_C()
typedef struct {
zend_error_display_cb display_callback;
} zend_error_display_callback;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have extra fields planned here? Otherwise the nesting is unnecessary.


char *zend_error_get_type_string(int type);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ZEND_API these

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the return type of this one can be const char *

void zend_register_error_display_callback(zend_error_display_cb callback);
void zend_startup_error_display_functions(void);
void zend_shutdown_error_display_functions(void);
int zend_error_display_handle(int type, const char *error_filename, const uint32_t error_lineno, char *buffer, int buffer_len);
END_EXTERN_C()

#endif /* ZEND_ERRORS_H */
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -1456,7 +1456,7 @@ PHP_ADD_SOURCES(Zend, \
zend_execute_API.c zend_highlight.c zend_llist.c \
zend_vm_opcodes.c zend_opcode.c zend_operators.c zend_ptr_stack.c zend_stack.c \
zend_variables.c zend.c zend_API.c zend_extensions.c zend_hash.c \
zend_list.c zend_builtin_functions.c \
zend_list.c zend_builtin_functions.c zend_errors.c \
zend_ini.c zend_sort.c zend_multibyte.c zend_ts_hash.c zend_stream.c \
zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c zend_gc.c \
zend_closures.c zend_weakrefs.c zend_float.c zend_string.c zend_signal.c zend_generators.c \
Expand Down
160 changes: 46 additions & 114 deletions ext/soap/soap.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
#endif
#include "soap_arginfo.h"
#include "zend_exceptions.h"

#include "zend_errors.h"

static int le_sdl = 0;
int le_url = 0;
Expand All @@ -52,6 +52,7 @@ static void set_soap_fault(zval *obj, char *fault_code_ns, char *fault_code, cha
static void add_soap_fault_ex(zval *fault, zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail);
static ZEND_NORETURN void soap_server_fault(char* code, char* string, char *actor, zval* details, char *name);
static void soap_server_fault_ex(sdlFunctionPtr function, zval* fault, soapHeader* hdr);
static int soap_error_display_cb(int error_num, const char *error_filename, const uint32_t error_lineno, char *buffer, int buffer_len);

static sdlParamPtr get_param(sdlFunctionPtr function, char *param_name, int index, int);
static sdlFunctionPtr get_function(sdlPtr sdl, const char *function_name);
Expand All @@ -67,8 +68,6 @@ static void delete_service(void *service);
static void delete_url(void *handle);
static void delete_hashtable(void *hashtable);

static void soap_error_handler(int error_num, const char *error_filename, const uint32_t error_lineno, const char *format, va_list args);

#define SOAP_SERVER_BEGIN_CODE() \
zend_bool _old_handler = SOAP_GLOBAL(use_soap_error_handler);\
char* _old_error_code = SOAP_GLOBAL(error_code);\
Expand Down Expand Up @@ -163,16 +162,6 @@ zend_class_entry* soap_var_class_entry;

ZEND_DECLARE_MODULE_GLOBALS(soap)

static void (*old_error_handler)(int, const char *, const uint32_t, const char*, va_list);

#define call_old_error_handler(error_num, error_filename, error_lineno, format, args) \
{ \
va_list copy; \
va_copy(copy, args); \
old_error_handler(error_num, error_filename, error_lineno, format, copy); \
va_end(copy); \
}

#define PHP_SOAP_SERVER_CLASSNAME "SoapServer"
#define PHP_SOAP_CLIENT_CLASSNAME "SoapClient"
#define PHP_SOAP_VAR_CLASSNAME "SoapVar"
Expand Down Expand Up @@ -328,7 +317,6 @@ static void php_soap_init_globals(zend_soap_globals *soap_globals)

PHP_MSHUTDOWN_FUNCTION(soap)
{
zend_error_cb = old_error_handler;
zend_hash_destroy(&SOAP_GLOBAL(defEnc));
zend_hash_destroy(&SOAP_GLOBAL(defEncIndex));
zend_hash_destroy(&SOAP_GLOBAL(defEncNs));
Expand Down Expand Up @@ -514,8 +502,7 @@ PHP_MINIT_FUNCTION(soap)
REGISTER_LONG_CONSTANT("SOAP_SSL_METHOD_SSLv3", SOAP_SSL_METHOD_SSLv3, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SOAP_SSL_METHOD_SSLv23", SOAP_SSL_METHOD_SSLv23, CONST_CS | CONST_PERSISTENT);

old_error_handler = zend_error_cb;
zend_error_cb = soap_error_handler;
zend_register_error_display_callback(soap_error_display_cb);

return SUCCESS;
}
Expand Down Expand Up @@ -1846,17 +1833,11 @@ static ZEND_NORETURN void soap_server_fault(char* code, char* string, char *acto
}
/* }}} */

static zend_never_inline ZEND_COLD void soap_real_error_handler(int error_num, const char *error_filename, const uint32_t error_lineno, const char *format, va_list args) /* {{{ */
static zend_never_inline ZEND_COLD int soap_error_display_cb(int error_num, const char *error_filename, const uint32_t error_lineno, char *buffer, int buffer_len)
{
zend_bool _old_in_compilation;
zend_execute_data *_old_current_execute_data;
int _old_http_response_code;
char *_old_http_status_line;

_old_in_compilation = CG(in_compilation);
_old_current_execute_data = EG(current_execute_data);
_old_http_response_code = SG(sapi_headers).http_response_code;
_old_http_status_line = SG(sapi_headers).http_status_line;
if (EXPECTED(!SOAP_GLOBAL(use_soap_error_handler))) {
return 0;
}

if (Z_OBJ(SOAP_GLOBAL(error_object)) &&
instanceof_function(Z_OBJCE(SOAP_GLOBAL(error_object)), soap_class_entry)) {
Expand All @@ -1876,118 +1857,69 @@ static zend_never_inline ZEND_COLD void soap_real_error_handler(int error_num, c
use_exceptions) {
zval fault;
char* code = SOAP_GLOBAL(error_code);
char buffer[1024];
size_t buffer_len;
va_list argcopy;

va_copy(argcopy, args);
buffer_len = vslprintf(buffer, sizeof(buffer)-1, format, argcopy);
va_end(argcopy);

buffer[sizeof(buffer)-1]=0;
if (buffer_len > sizeof(buffer) - 1 || buffer_len == (size_t)-1) {
buffer_len = sizeof(buffer) - 1;
}

if (code == NULL) {
code = "Client";
}
add_soap_fault_ex(&fault, &SOAP_GLOBAL(error_object), code, buffer, NULL, NULL);
Z_ADDREF(fault);
zend_throw_exception_object(&fault);
zend_bailout();

return 1;
} else if (!use_exceptions ||
!SOAP_GLOBAL(error_code) ||
strcmp(SOAP_GLOBAL(error_code),"WSDL") != 0) {
/* Ignore libxml warnings during WSDL parsing */
call_old_error_handler(error_num, error_filename, error_lineno, format, args);
return 0;
}
} else {
int old = PG(display_errors);
int fault = 0;
zval fault_obj;
va_list argcopy;
}
zval fault_obj;

if (error_num == E_USER_ERROR ||
error_num == E_COMPILE_ERROR ||
error_num == E_CORE_ERROR ||
error_num == E_ERROR ||
error_num == E_PARSE) {
if (error_num == E_USER_ERROR ||
error_num == E_COMPILE_ERROR ||
error_num == E_CORE_ERROR ||
error_num == E_ERROR ||
error_num == E_PARSE) {

char* code = SOAP_GLOBAL(error_code);
char buffer[1024];
zval outbuf;
zval *tmp;
soapServicePtr service;
char* code = SOAP_GLOBAL(error_code);
zval outbuf;
zval *tmp;
soapServicePtr service;

ZVAL_UNDEF(&outbuf);
if (code == NULL) {
code = "Server";
}
if (Z_OBJ(SOAP_GLOBAL(error_object)) &&
instanceof_function(Z_OBJCE(SOAP_GLOBAL(error_object)), soap_server_class_entry) &&
(tmp = zend_hash_str_find(Z_OBJPROP(SOAP_GLOBAL(error_object)), "service", sizeof("service")-1)) != NULL &&
(service = (soapServicePtr)zend_fetch_resource_ex(tmp, "service", le_service)) &&
!service->send_errors) {
strcpy(buffer, "Internal Error");
} else {
zval outbuflen;

ZVAL_UNDEF(&outbuf);
if (code == NULL) {
code = "Server";
/* Get output buffer and send as fault details */
if (php_output_get_length(&outbuflen) != FAILURE && Z_LVAL(outbuflen) != 0) {
php_output_get_contents(&outbuf);
}
if (Z_OBJ(SOAP_GLOBAL(error_object)) &&
instanceof_function(Z_OBJCE(SOAP_GLOBAL(error_object)), soap_server_class_entry) &&
(tmp = zend_hash_str_find(Z_OBJPROP(SOAP_GLOBAL(error_object)), "service", sizeof("service")-1)) != NULL &&
(service = (soapServicePtr)zend_fetch_resource_ex(tmp, "service", le_service)) &&
!service->send_errors) {
strcpy(buffer, "Internal Error");
} else {
size_t buffer_len;
zval outbuflen;

va_copy(argcopy, args);
buffer_len = vslprintf(buffer, sizeof(buffer)-1, format, argcopy);
va_end(argcopy);

buffer[sizeof(buffer)-1]=0;
if (buffer_len > sizeof(buffer) - 1 || buffer_len == (size_t)-1) {
buffer_len = sizeof(buffer) - 1;
}
php_output_discard();

/* Get output buffer and send as fault detials */
if (php_output_get_length(&outbuflen) != FAILURE && Z_LVAL(outbuflen) != 0) {
php_output_get_contents(&outbuf);
}
php_output_discard();
}
ZVAL_NULL(&fault_obj);
set_soap_fault(&fault_obj, NULL, code, buffer, NULL, &outbuf, NULL);

}
ZVAL_NULL(&fault_obj);
set_soap_fault(&fault_obj, NULL, code, buffer, NULL, &outbuf, NULL);
fault = 1;
}

PG(display_errors) = 0;
SG(sapi_headers).http_status_line = NULL;
zend_try {
call_old_error_handler(error_num, error_filename, error_lineno, format, args);
} zend_catch {
CG(in_compilation) = _old_in_compilation;
EG(current_execute_data) = _old_current_execute_data;
if (SG(sapi_headers).http_status_line) {
efree(SG(sapi_headers).http_status_line);
}
SG(sapi_headers).http_status_line = _old_http_status_line;
SG(sapi_headers).http_response_code = _old_http_response_code;
} zend_end_try();
PG(display_errors) = old;
soap_server_fault_ex(NULL, &fault_obj, NULL);

if (fault) {
soap_server_fault_ex(NULL, &fault_obj, NULL);
zend_bailout();
}
return 1;
}
}
/* }}} */

static void soap_error_handler(int error_num, const char *error_filename, const uint32_t error_lineno, const char *format, va_list args) /* {{{ */
{
if (EXPECTED(!SOAP_GLOBAL(use_soap_error_handler))) {
call_old_error_handler(error_num, error_filename, error_lineno, format, args);
} else {
soap_real_error_handler(error_num, error_filename, error_lineno, format, args);
}
// ignore rendering all other errors by stopping the display chain
return 1;
}
/* }}} */


/* {{{ proto use_soap_error_handler([bool $handler = TRUE]) */
PHP_FUNCTION(use_soap_error_handler)
{
Expand Down
Loading