@@ -30,11 +30,12 @@ ZEND_DECLARE_MODULE_GLOBALS(filter)
3030#include "zend_attributes.h"
3131#include "filter_private.h"
3232#include "filter_arginfo.h"
33+ #include "zend_exceptions.h"
3334
3435typedef struct filter_list_entry {
3536 const char * name ;
3637 int id ;
37- void (* function )(PHP_INPUT_FILTER_PARAM_DECL );
38+ zend_result (* function )(PHP_INPUT_FILTER_PARAM_DECL );
3839} filter_list_entry ;
3940
4041/* {{{ filter_list */
@@ -77,6 +78,9 @@ static const filter_list_entry filter_list[] = {
7778static unsigned int php_sapi_filter (int arg , const char * var , char * * val , size_t val_len , size_t * new_val_len );
7879static unsigned int php_sapi_filter_init (void );
7980
81+ zend_class_entry * php_filter_exception_ce ;
82+ zend_class_entry * php_filter_failed_exception_ce ;
83+
8084/* {{{ filter_module_entry */
8185zend_module_entry filter_module_entry = {
8286 STANDARD_MODULE_HEADER ,
@@ -160,6 +164,9 @@ PHP_MINIT_FUNCTION(filter)
160164
161165 sapi_register_input_filter (php_sapi_filter , php_sapi_filter_init );
162166
167+ php_filter_exception_ce = register_class_Filter_FilterException (zend_ce_exception );
168+ php_filter_failed_exception_ce = register_class_Filter_FilterFailedException (php_filter_exception_ce );
169+
163170 return SUCCESS ;
164171}
165172/* }}} */
@@ -251,6 +258,16 @@ static void php_zval_filter(zval *value, zend_long filter, zend_long flags, zval
251258 ce = Z_OBJCE_P (value );
252259 if (!ce -> __tostring ) {
253260 zval_ptr_dtor (value );
261+ if (flags & FILTER_THROW_ON_FAILURE ) {
262+ zend_throw_exception_ex (
263+ php_filter_failed_exception_ce ,
264+ 0 ,
265+ "filter validation failed: object of type %s has no __toString() method" ,
266+ ZSTR_VAL (ce -> name )
267+ );
268+ ZVAL_NULL (value );
269+ return ;
270+ }
254271 /* #67167: doesn't return null on failure for objects */
255272 if (flags & FILTER_NULL_ON_FAILURE ) {
256273 ZVAL_NULL (value );
@@ -264,7 +281,29 @@ static void php_zval_filter(zval *value, zend_long filter, zend_long flags, zval
264281 /* Here be strings */
265282 convert_to_string (value );
266283
267- filter_func .function (value , flags , options , charset );
284+ zend_string * copy_for_throwing = NULL ;
285+ if (flags & FILTER_THROW_ON_FAILURE ) {
286+ copy_for_throwing = zend_string_copy (Z_STR_P (value ));
287+ }
288+
289+ zend_result result = filter_func .function (value , flags , options , charset );
290+
291+ if (flags & FILTER_THROW_ON_FAILURE ) {
292+ ZEND_ASSERT (copy_for_throwing != NULL );
293+ if (result == FAILURE ) {
294+ zend_throw_exception_ex (
295+ php_filter_failed_exception_ce ,
296+ 0 ,
297+ "filter validation failed: filter %s not satisfied by '%s'" ,
298+ filter_func .name ,
299+ ZSTR_VAL (copy_for_throwing )
300+ );
301+ zend_string_delref (copy_for_throwing );
302+ return ;
303+ }
304+ zend_string_delref (copy_for_throwing );
305+ copy_for_throwing = NULL ;
306+ }
268307
269308handle_default :
270309 if (options && Z_TYPE_P (options ) == IS_ARRAY &&
@@ -450,7 +489,8 @@ PHP_FUNCTION(filter_has_var)
450489
451490static void php_filter_call (
452491 zval * filtered , zend_long filter , HashTable * filter_args_ht , zend_long filter_args_long ,
453- zend_long filter_flags
492+ zend_long filter_flags ,
493+ uint32_t options_arg_num
454494) /* {{{ */ {
455495 zval * options = NULL ;
456496 char * charset = NULL ;
@@ -492,10 +532,28 @@ static void php_filter_call(
492532 }
493533 }
494534
535+ /* Cannot use both FILTER_NULL_ON_FAILURE and FILTER_THROW_ON_FAILURE */
536+ if ((filter_flags & FILTER_NULL_ON_FAILURE ) && (filter_flags & FILTER_THROW_ON_FAILURE )) {
537+ zval_ptr_dtor (filtered );
538+ ZVAL_NULL (filtered );
539+ zend_argument_value_error (
540+ options_arg_num ,
541+ "cannot use both FILTER_NULL_ON_FAILURE and FILTER_THROW_ON_FAILURE"
542+ );
543+ return ;
544+ }
545+
495546 if (Z_TYPE_P (filtered ) == IS_ARRAY ) {
496547 if (filter_flags & FILTER_REQUIRE_SCALAR ) {
497548 zval_ptr_dtor (filtered );
498- if (filter_flags & FILTER_NULL_ON_FAILURE ) {
549+ if (filter_flags & FILTER_THROW_ON_FAILURE ) {
550+ ZVAL_NULL (filtered );
551+ zend_throw_exception (
552+ php_filter_failed_exception_ce ,
553+ "filter validation failed: not a scalar value (got an array)" ,
554+ 0
555+ );
556+ } else if (filter_flags & FILTER_NULL_ON_FAILURE ) {
499557 ZVAL_NULL (filtered );
500558 } else {
501559 ZVAL_FALSE (filtered );
@@ -506,6 +564,17 @@ static void php_filter_call(
506564 return ;
507565 }
508566 if (filter_flags & FILTER_REQUIRE_ARRAY ) {
567+ if (filter_flags & FILTER_THROW_ON_FAILURE ) {
568+ zend_throw_exception_ex (
569+ php_filter_failed_exception_ce ,
570+ 0 ,
571+ "filter validation failed: not an array (got %s)" ,
572+ zend_zval_value_name (filtered )
573+ );
574+ zval_ptr_dtor (filtered );
575+ ZVAL_NULL (filtered );
576+ return ;
577+ }
509578 zval_ptr_dtor (filtered );
510579 if (filter_flags & FILTER_NULL_ON_FAILURE ) {
511580 ZVAL_NULL (filtered );
@@ -516,6 +585,10 @@ static void php_filter_call(
516585 }
517586
518587 php_zval_filter (filtered , filter , filter_flags , options , charset );
588+ /* Don't wrap in an array if we are throwing an exception */
589+ if (EG (exception )) {
590+ return ;
591+ }
519592 if (filter_flags & FILTER_FORCE_ARRAY ) {
520593 zval tmp ;
521594 ZVAL_COPY_VALUE (& tmp , filtered );
@@ -530,7 +603,7 @@ static void php_filter_array_handler(zval *input, HashTable *op_ht, zend_long op
530603) /* {{{ */ {
531604 if (!op_ht ) {
532605 ZVAL_DUP (return_value , input );
533- php_filter_call (return_value , -1 , NULL , op_long , FILTER_REQUIRE_ARRAY );
606+ php_filter_call (return_value , -1 , NULL , op_long , FILTER_REQUIRE_ARRAY , 2 );
534607 } else {
535608 array_init (return_value );
536609 zend_string * arg_key ;
@@ -557,8 +630,12 @@ static void php_filter_array_handler(zval *input, HashTable *op_ht, zend_long op
557630 php_filter_call (& nval , -1 ,
558631 Z_TYPE_P (arg_elm ) == IS_ARRAY ? Z_ARRVAL_P (arg_elm ) : NULL ,
559632 Z_TYPE_P (arg_elm ) == IS_ARRAY ? 0 : zval_get_long (arg_elm ),
560- FILTER_REQUIRE_SCALAR
633+ FILTER_REQUIRE_SCALAR ,
634+ 2
561635 );
636+ if (EG (exception )) {
637+ RETURN_THROWS ();
638+ }
562639 zend_hash_update (Z_ARRVAL_P (return_value ), arg_key , & nval );
563640 }
564641 } ZEND_HASH_FOREACH_END ();
@@ -598,11 +675,35 @@ PHP_FUNCTION(filter_input)
598675 if (!filter_args_ht ) {
599676 filter_flags = filter_args_long ;
600677 } else {
601- zval * option , * opt , * def ;
678+ zval * option ;
602679 if ((option = zend_hash_str_find (filter_args_ht , "flags" , sizeof ("flags" ) - 1 )) != NULL ) {
603680 filter_flags = zval_get_long (option );
604681 }
682+ }
683+
684+ /* Cannot use both FILTER_NULL_ON_FAILURE and FILTER_THROW_ON_FAILURE */
685+ if ((filter_flags & FILTER_NULL_ON_FAILURE ) && (filter_flags & FILTER_THROW_ON_FAILURE )) {
686+ zend_argument_value_error (
687+ 4 ,
688+ "cannot use both FILTER_NULL_ON_FAILURE and FILTER_THROW_ON_FAILURE"
689+ );
690+ RETURN_THROWS ();
691+ }
692+
693+ if (filter_flags & FILTER_THROW_ON_FAILURE ) {
694+ zend_throw_exception_ex (
695+ php_filter_failed_exception_ce ,
696+ 0 ,
697+ "input value '%s' not found" ,
698+ ZSTR_VAL (var )
699+ );
700+ RETURN_THROWS ();
701+ }
605702
703+ /* FILTER_THROW_ON_FAILURE overrides defaults, needs to be checked
704+ * before the default is used. */
705+ if (filter_args_ht ) {
706+ zval * opt , * def ;
606707 if ((opt = zend_hash_str_find_deref (filter_args_ht , "options" , sizeof ("options" ) - 1 )) != NULL &&
607708 Z_TYPE_P (opt ) == IS_ARRAY &&
608709 (def = zend_hash_str_find_deref (Z_ARRVAL_P (opt ), "default" , sizeof ("default" ) - 1 )) != NULL
@@ -626,7 +727,7 @@ PHP_FUNCTION(filter_input)
626727
627728 ZVAL_DUP (return_value , tmp );
628729
629- php_filter_call (return_value , filter , filter_args_ht , filter_args_long , FILTER_REQUIRE_SCALAR );
730+ php_filter_call (return_value , filter , filter_args_ht , filter_args_long , FILTER_REQUIRE_SCALAR , 4 );
630731}
631732/* }}} */
632733
@@ -652,7 +753,7 @@ PHP_FUNCTION(filter_var)
652753
653754 ZVAL_DUP (return_value , data );
654755
655- php_filter_call (return_value , filter , filter_args_ht , filter_args_long , FILTER_REQUIRE_SCALAR );
756+ php_filter_call (return_value , filter , filter_args_ht , filter_args_long , FILTER_REQUIRE_SCALAR , 3 );
656757}
657758/* }}} */
658759
0 commit comments