diff --git a/NEWS b/NEWS index c066652e8445e..cf49e3c0b4f82 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,10 @@ PHP NEWS . Fix references in request_parse_body() options array. (nielsdos) . Add RoundingMode enum. (timwolla, saki) +- Tidy: + . Failures in the constructor now throw exceptions rather than emitting + warnings and having a broken object. (nielsdos) + - XSL: . Fix trampoline leak in xpath callables. (nielsdos) diff --git a/UPGRADING b/UPGRADING index 6e748906059da..18bb69527778b 100644 --- a/UPGRADING +++ b/UPGRADING @@ -167,6 +167,10 @@ PHP 8.4 UPGRADE NOTES . strcspn() with empty $characters now returns the length of the string instead of incorrectly stopping at the first NUL character. See GH-12592. +- Tidy: + . Failures in the constructor now throw exceptions rather than emitting + warnings and having a broken object. + - XML: . The xml_set_*_handler() functions now declare and check for an effective signature of callable|string|null for the $handler parameters. diff --git a/ext/tidy/tests/bug54682.phpt b/ext/tidy/tests/bug54682.phpt index 983a33017e8e5..333b89e804c4f 100644 --- a/ext/tidy/tests/bug54682.phpt +++ b/ext/tidy/tests/bug54682.phpt @@ -5,9 +5,15 @@ tidy --FILE-- diagnose(); +var_dump($nx); ?> --EXPECTF-- -Warning: tidy::__construct(): Cannot load "*" into memory%win %s on line %d +object(tidy)#%d (2) { + ["errorBuffer"]=> + NULL + ["value"]=> + NULL +} diff --git a/ext/tidy/tests/open_basedir_failure_config.phpt b/ext/tidy/tests/open_basedir_failure_config.phpt index b14393b7b801c..3b12c9e4ea8f6 100644 --- a/ext/tidy/tests/open_basedir_failure_config.phpt +++ b/ext/tidy/tests/open_basedir_failure_config.phpt @@ -17,7 +17,11 @@ echo "=== tidy_parse_file ===\n"; tidy_parse_file(__DIR__.'/open_basedir/test.html', 'my_config_file.ini'); echo "=== __construct ===\n"; -$tidy = new tidy(__DIR__.'/open_basedir/test.html', 'my_config_file.ini'); +try { + $tidy = new tidy(__DIR__.'/open_basedir/test.html', 'my_config_file.ini'); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} echo "=== parseFile ===\n"; $tidy = new tidy; @@ -38,8 +42,7 @@ Warning: tidy_parse_string(): open_basedir restriction in effect. File(my_config Warning: tidy_parse_file(): open_basedir restriction in effect. File(my_config_file.ini) is not within the allowed path(s): (%sopen_basedir) in %s on line %d === __construct === - -Warning: tidy::__construct(): open_basedir restriction in effect. File(my_config_file.ini) is not within the allowed path(s): (%sopen_basedir) in %s on line %d +tidy::__construct(): open_basedir restriction in effect. File(my_config_file.ini) is not within the allowed path(s): (%sopen_basedir) === parseFile === Warning: tidy::parseFile(): open_basedir restriction in effect. File(my_config_file.ini) is not within the allowed path(s): (%sopen_basedir) in %s on line %d diff --git a/ext/tidy/tests/parsing_inexistent_file.phpt b/ext/tidy/tests/parsing_inexistent_file.phpt index 26dfea798574e..e5f73c5388ce0 100644 --- a/ext/tidy/tests/parsing_inexistent_file.phpt +++ b/ext/tidy/tests/parsing_inexistent_file.phpt @@ -10,7 +10,11 @@ var_dump($tidy->parseFile("does_not_exist.html")); var_dump(tidy_parse_file("does_not_exist.html")); -$tidy = new tidy("does_not_exist.html"); +try { + $tidy = new tidy("does_not_exist.html"); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} ?> --EXPECTF-- Warning: tidy::parseFile(): Cannot load "does_not_exist.html" into memory in %s on line %d @@ -18,5 +22,4 @@ bool(false) Warning: tidy_parse_file(): Cannot load "does_not_exist.html" into memory in %s on line %d bool(false) - -Warning: tidy::__construct(): Cannot load "does_not_exist.html" into memory in %s on line %d +Cannot load "does_not_exist.html" into memory diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index 3a743df396674..8682c9c298e04 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -40,6 +40,8 @@ #include "tidy_arginfo.h" +#include "Zend/zend_exceptions.h" + /* compatibility with older versions of libtidy */ #ifndef TIDY_CALL #define TIDY_CALL @@ -54,7 +56,7 @@ #define TIDY_FETCH_OBJECT \ PHPTidyObj *obj; \ zval *object; \ - if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, tidy_ce_doc) == FAILURE) { \ + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, tidy_ce_doc) != SUCCESS) { \ RETURN_THROWS(); \ } \ obj = Z_TIDY_P(object); \ @@ -69,7 +71,7 @@ #define TIDY_FETCH_ONLY_OBJECT \ PHPTidyObj *obj; \ TIDY_SET_CONTEXT; \ - if (zend_parse_parameters_none() == FAILURE) { \ + if (zend_parse_parameters_none() != SUCCESS) { \ RETURN_THROWS(); \ } \ obj = Z_TIDY_P(object); \ @@ -118,7 +120,7 @@ static inline PHPTidyObj *php_tidy_fetch_object(zend_object *obj) { /* }}} */ /* {{{ ext/tidy prototypes */ -static zend_string *php_tidy_file_to_mem(char *, bool); +static zend_string *php_tidy_file_to_mem(const char *, bool); static void tidy_object_free_storage(zend_object *); static zend_object *tidy_object_new_node(zend_class_entry *); static zend_object *tidy_object_new_doc(zend_class_entry *); @@ -129,8 +131,8 @@ static void tidy_doc_update_properties(PHPTidyObj *); static void tidy_add_node_default_properties(PHPTidyObj *); static void *php_tidy_get_opt_val(PHPTidyDoc *, TidyOption, TidyOptionType *); static void php_tidy_create_node(INTERNAL_FUNCTION_PARAMETERS, tidy_base_nodetypes); -static int _php_tidy_set_tidy_opt(TidyDoc, char *, zval *); -static int _php_tidy_apply_config_array(TidyDoc doc, HashTable *ht_options); +static int _php_tidy_set_tidy_opt(TidyDoc, const char *, zval *); +static int _php_tidy_apply_config_array(TidyDoc doc, const HashTable *ht_options); static PHP_INI_MH(php_tidy_set_clean_output); static void php_tidy_clean_output_start(const char *name, size_t name_len); static php_output_handler *php_tidy_output_handler_init(const char *handler_name, size_t handler_name_len, size_t chunk_size, int flags); @@ -195,7 +197,7 @@ static void TIDY_CALL php_tidy_free(void *buf) static void TIDY_CALL php_tidy_panic(ctmbstr msg) { - php_error_docref(NULL, E_ERROR, "Could not allocate memory for tidy! (Reason: %s)", (char *)msg); + php_error_docref(NULL, E_ERROR, "Could not allocate memory for tidy! (Reason: %s)", (const char *)msg); } static void php_tidy_load_config(TidyDoc doc, const char *path) @@ -208,7 +210,7 @@ static void php_tidy_load_config(TidyDoc doc, const char *path) } } -static zend_result php_tidy_apply_config(TidyDoc doc, zend_string *str_string, HashTable *ht_options) +static zend_result php_tidy_apply_config(TidyDoc doc, const zend_string *str_string, const HashTable *ht_options) { if (ht_options) { return _php_tidy_apply_config_array(doc, ht_options); @@ -221,7 +223,7 @@ static zend_result php_tidy_apply_config(TidyDoc doc, zend_string *str_string, H return SUCCESS; } -static int _php_tidy_set_tidy_opt(TidyDoc doc, char *optname, zval *value) +static int _php_tidy_set_tidy_opt(TidyDoc doc, const char *optname, zval *value) { TidyOption opt = tidyGetOptionByName(doc, optname); zend_string *str, *tmp_str; @@ -365,7 +367,7 @@ static void php_tidy_quick_repair(INTERNAL_FUNCTION_PARAMETERS, bool is_file) tidySaveBuffer (doc, &output); FIX_BUFFER(&output); - RETVAL_STRINGL((char *) output.bp, output.size ? output.size-1 : 0); + RETVAL_STRINGL((const char *) output.bp, output.size ? output.size-1 : 0); tidyBufFree(&output); } else { RETVAL_FALSE; @@ -382,7 +384,7 @@ static void php_tidy_quick_repair(INTERNAL_FUNCTION_PARAMETERS, bool is_file) tidyRelease(doc); } -static zend_string *php_tidy_file_to_mem(char *filename, bool use_include_path) +static zend_string *php_tidy_file_to_mem(const char *filename, bool use_include_path) { php_stream *stream; zend_string *data = NULL; @@ -497,7 +499,7 @@ static zend_result tidy_doc_cast_handler(zend_object *in, zval *out, int type) tidyBufInit(&output); tidySaveBuffer (obj->ptdoc->doc, &output); if (output.size) { - ZVAL_STRINGL(out, (char *) output.bp, output.size-1); + ZVAL_STRINGL(out, (const char *) output.bp, output.size-1); } else { ZVAL_EMPTY_STRING(out); } @@ -535,7 +537,7 @@ static zend_result tidy_node_cast_handler(zend_object *in, zval *out, int type) tidyBufInit(&buf); if (obj->ptdoc) { tidyNodeGetText(obj->ptdoc->doc, obj->node, &buf); - ZVAL_STRINGL(out, (char *) buf.bp, buf.size-1); + ZVAL_STRINGL(out, (const char *) buf.bp, buf.size-1); } else { ZVAL_EMPTY_STRING(out); } @@ -587,7 +589,7 @@ static void tidy_add_node_default_properties(PHPTidyObj *obj) TidyAttr tempattr; TidyNode tempnode; zval attribute, children, temp; - char *name; + const char *name; tidyBufInit(&buf); tidyNodeGetText(obj->ptdoc->doc, obj->node, &buf); @@ -597,13 +599,13 @@ static void tidy_add_node_default_properties(PHPTidyObj *obj) &obj->std, "value", sizeof("value") - 1, - buf.size ? (char *) buf.bp : "", + buf.size ? (const char *) buf.bp : "", buf.size ? buf.size - 1 : 0 ); tidyBufFree(&buf); - name = (char *) tidyNodeGetName(obj->node); + name = (const char *) tidyNodeGetName(obj->node); zend_update_property_string( tidy_ce_node, @@ -671,12 +673,12 @@ static void tidy_add_node_default_properties(PHPTidyObj *obj) tempattr = tidyAttrFirst(obj->node); if (tempattr) { - char *name, *val; + const char *name, *val; array_init(&attribute); do { - name = (char *)tidyAttrName(tempattr); - val = (char *)tidyAttrValue(tempattr); + name = (const char *)tidyAttrName(tempattr); + val = (const char *)tidyAttrValue(tempattr); if (name) { if (val) { add_assoc_string(&attribute, name, val); @@ -783,7 +785,7 @@ static void php_tidy_create_node(INTERNAL_FUNCTION_PARAMETERS, tidy_base_nodetyp tidy_create_node_object(return_value, obj->ptdoc, node); } -static int _php_tidy_apply_config_array(TidyDoc doc, HashTable *ht_options) +static int _php_tidy_apply_config_array(TidyDoc doc, const HashTable *ht_options) { zval *opt_val; zend_string *opt_name; @@ -799,7 +801,7 @@ static int _php_tidy_apply_config_array(TidyDoc doc, HashTable *ht_options) return SUCCESS; } -static int php_tidy_parse_string(PHPTidyObj *obj, char *string, uint32_t len, char *enc) +static int php_tidy_parse_string(PHPTidyObj *obj, const char *string, uint32_t len, const char *enc) { TidyBuffer buf; @@ -884,12 +886,12 @@ static PHP_MINFO_FUNCTION(tidy) php_info_print_table_start(); php_info_print_table_row(2, "Tidy support", "enabled"); #ifdef HAVE_TIDYBUFFIO_H - php_info_print_table_row(2, "libTidy Version", (char *)tidyLibraryVersion()); + php_info_print_table_row(2, "libTidy Version", (const char *)tidyLibraryVersion()); #elif defined(HAVE_TIDYP_H) - php_info_print_table_row(2, "libtidyp Version", (char *)tidyVersion()); + php_info_print_table_row(2, "libtidyp Version", (const char *)tidyVersion()); #endif #ifdef HAVE_TIDYRELEASEDATE - php_info_print_table_row(2, "libTidy Release", (char *)tidyReleaseDate()); + php_info_print_table_row(2, "libTidy Release", (const char *)tidyReleaseDate()); #endif php_info_print_table_end(); @@ -1019,7 +1021,7 @@ PHP_FUNCTION(tidy_parse_string) obj = Z_TIDY_P(return_value); if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht) != SUCCESS - || php_tidy_parse_string(obj, ZSTR_VAL(input), (uint32_t)ZSTR_LEN(input), enc) == FAILURE) { + || php_tidy_parse_string(obj, ZSTR_VAL(input), (uint32_t)ZSTR_LEN(input), enc) != SUCCESS) { zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -1032,7 +1034,7 @@ PHP_FUNCTION(tidy_get_error_buffer) TIDY_FETCH_OBJECT; if (obj->ptdoc->errbuf && obj->ptdoc->errbuf->bp) { - RETURN_STRINGL((char*)obj->ptdoc->errbuf->bp, obj->ptdoc->errbuf->size-1); + RETURN_STRINGL((const char*)obj->ptdoc->errbuf->bp, obj->ptdoc->errbuf->size-1); } else { RETURN_FALSE; } @@ -1048,7 +1050,7 @@ PHP_FUNCTION(tidy_get_output) tidyBufInit(&output); tidySaveBuffer(obj->ptdoc->doc, &output); FIX_BUFFER(&output); - RETVAL_STRINGL((char *) output.bp, output.size ? output.size-1 : 0); + RETVAL_STRINGL((const char *) output.bp, output.size ? output.size-1 : 0); tidyBufFree(&output); } /* }}} */ @@ -1087,7 +1089,7 @@ PHP_FUNCTION(tidy_parse_file) obj = Z_TIDY_P(return_value); if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht) != SUCCESS - || php_tidy_parse_string(obj, ZSTR_VAL(contents), (uint32_t)ZSTR_LEN(contents), enc) == FAILURE) { + || php_tidy_parse_string(obj, ZSTR_VAL(contents), (uint32_t)ZSTR_LEN(contents), enc) != SUCCESS) { zval_ptr_dtor(return_value); RETVAL_FALSE; } @@ -1141,14 +1143,14 @@ PHP_FUNCTION(tidy_diagnose) /* {{{ Get release date (version) for Tidy library */ PHP_FUNCTION(tidy_get_release) { - if (zend_parse_parameters_none() == FAILURE) { + if (zend_parse_parameters_none() != SUCCESS) { RETURN_THROWS(); } #ifdef HAVE_TIDYRELEASEDATE - RETURN_STRING((char *)tidyReleaseDate()); + RETURN_STRING((const char *)tidyReleaseDate()); #else - RETURN_STRING((char *)"unknown"); + RETURN_STRING((const char *)"unknown"); #endif } /* }}} */ @@ -1159,12 +1161,13 @@ PHP_FUNCTION(tidy_get_release) PHP_FUNCTION(tidy_get_opt_doc) { PHPTidyObj *obj; - char *optval, *optname; + const char *optval; + char *optname; size_t optname_len; TidyOption opt; zval *object; - if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, tidy_ce_doc, &optname, &optname_len) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, tidy_ce_doc, &optname, &optname_len) != SUCCESS) { RETURN_THROWS(); } @@ -1177,7 +1180,7 @@ PHP_FUNCTION(tidy_get_opt_doc) RETURN_THROWS(); } - if ( (optval = (char *) tidyOptGetDoc(obj->ptdoc->doc, opt)) ) { + if ( (optval = (const char *) tidyOptGetDoc(obj->ptdoc->doc, opt)) ) { RETURN_STRING(optval); } @@ -1191,7 +1194,7 @@ PHP_FUNCTION(tidy_get_opt_doc) PHP_FUNCTION(tidy_get_config) { TidyIterator itOpt; - char *opt_name; + const char *opt_name; void *opt_value; TidyOptionType optt; @@ -1204,7 +1207,7 @@ PHP_FUNCTION(tidy_get_config) while (itOpt) { TidyOption opt = tidyGetNextOption(obj->ptdoc->doc, &itOpt); - opt_name = (char *)tidyOptGetName(opt); + opt_name = (const char *)tidyOptGetName(opt); opt_value = php_tidy_get_opt_val(obj->ptdoc, opt, &optt); switch (optt) { case TidyString: @@ -1220,8 +1223,6 @@ PHP_FUNCTION(tidy_get_config) break; } } - - return; } /* }}} */ @@ -1308,7 +1309,7 @@ PHP_FUNCTION(tidy_getopt) TidyOptionType optt; zval *object; - if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, tidy_ce_doc, &optname, &optname_len) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, tidy_ce_doc, &optname, &optname_len) != SUCCESS) { RETURN_THROWS(); } @@ -1370,8 +1371,8 @@ PHP_METHOD(tidy, __construct) if (inputfile) { if (!(contents = php_tidy_file_to_mem(ZSTR_VAL(inputfile), use_include_path))) { - php_error_docref(NULL, E_WARNING, "Cannot load \"%s\" into memory%s", ZSTR_VAL(inputfile), (use_include_path) ? " (using include path)" : ""); - return; + zend_throw_error(zend_ce_exception, "Cannot load \"%s\" into memory%s", ZSTR_VAL(inputfile), (use_include_path) ? " (using include path)" : ""); + RETURN_THROWS(); } if (ZEND_SIZE_T_UINT_OVFL(ZSTR_LEN(contents))) { @@ -1380,11 +1381,14 @@ PHP_METHOD(tidy, __construct) RETURN_THROWS(); } + zend_error_handling error_handling; + zend_replace_error_handling(EH_THROW, NULL, &error_handling); if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht) != SUCCESS) { - /* TODO: this is the constructor, we should throw probably... */ + zend_restore_error_handling(&error_handling); zend_string_release_ex(contents, 0); - RETURN_FALSE; + RETURN_THROWS(); } + zend_restore_error_handling(&error_handling); php_tidy_parse_string(obj, ZSTR_VAL(contents), (uint32_t)ZSTR_LEN(contents), enc); @@ -1423,12 +1427,8 @@ PHP_METHOD(tidy, parseFile) RETURN_THROWS(); } - if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht) != SUCCESS - || php_tidy_parse_string(obj, ZSTR_VAL(contents), (uint32_t)ZSTR_LEN(contents), enc) == FAILURE) { - RETVAL_FALSE; - } else { - RETVAL_TRUE; - } + RETVAL_BOOL(php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht) == SUCCESS + && php_tidy_parse_string(obj, ZSTR_VAL(contents), (uint32_t)ZSTR_LEN(contents), enc) == SUCCESS); zend_string_release_ex(contents, 0); } @@ -1456,12 +1456,8 @@ PHP_METHOD(tidy, parseString) TIDY_SET_CONTEXT; obj = Z_TIDY_P(object); - if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht) == SUCCESS - && php_tidy_parse_string(obj, ZSTR_VAL(input), (uint32_t)ZSTR_LEN(input), enc) == SUCCESS) { - RETURN_TRUE; - } - - RETURN_FALSE; + RETURN_BOOL(php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht) == SUCCESS + && php_tidy_parse_string(obj, ZSTR_VAL(input), (uint32_t)ZSTR_LEN(input), enc) == SUCCESS); }