@@ -29,6 +29,7 @@ ZEND_DECLARE_MODULE_GLOBALS(filter)
2929
3030#include "filter_private.h"
3131#include "filter_arginfo.h"
32+ #include "zend_exceptions.h"
3233
3334typedef struct filter_list_entry {
3435 const char * name ;
@@ -76,6 +77,9 @@ static const filter_list_entry filter_list[] = {
7677static unsigned int php_sapi_filter (int arg , const char * var , char * * val , size_t val_len , size_t * new_val_len );
7778static unsigned int php_sapi_filter_init (void );
7879
80+ zend_class_entry * php_filter_exception_ce ;
81+ zend_class_entry * php_filter_failed_exception_ce ;
82+
7983/* {{{ filter_module_entry */
8084zend_module_entry filter_module_entry = {
8185 STANDARD_MODULE_HEADER ,
@@ -159,6 +163,9 @@ PHP_MINIT_FUNCTION(filter)
159163
160164 sapi_register_input_filter (php_sapi_filter , php_sapi_filter_init );
161165
166+ php_filter_exception_ce = register_class_Filter_FilterException (zend_ce_exception );
167+ php_filter_failed_exception_ce = register_class_Filter_FilterFailedException (php_filter_exception_ce );
168+
162169 return SUCCESS ;
163170}
164171/* }}} */
@@ -250,6 +257,15 @@ static void php_zval_filter(zval *value, zend_long filter, zend_long flags, zval
250257 ce = Z_OBJCE_P (value );
251258 if (!ce -> __tostring ) {
252259 zval_ptr_dtor (value );
260+ if (flags & FILTER_THROW_ON_FAILURE ) {
261+ zend_throw_exception_ex (
262+ php_filter_failed_exception_ce ,
263+ 0 ,
264+ "filter validation failed: object of type %s has no __toString() method" ,
265+ ZSTR_VAL (ce -> name )
266+ );
267+ return ;
268+ }
253269 /* #67167: doesn't return null on failure for objects */
254270 if (flags & FILTER_NULL_ON_FAILURE ) {
255271 ZVAL_NULL (value );
@@ -263,8 +279,30 @@ static void php_zval_filter(zval *value, zend_long filter, zend_long flags, zval
263279 /* Here be strings */
264280 convert_to_string (value );
265281
282+ zend_string * copy_for_throwing = NULL ;
283+ if (flags & FILTER_THROW_ON_FAILURE ) {
284+ copy_for_throwing = zend_string_copy (Z_STR_P (value ));
285+ }
286+
266287 filter_func .function (value , flags , options , charset );
267288
289+ if (flags & FILTER_THROW_ON_FAILURE ) {
290+ ZEND_ASSERT (copy_for_throwing != NULL );
291+ if (Z_ISERROR_P (value )) {
292+ zend_throw_exception_ex (
293+ php_filter_failed_exception_ce ,
294+ 0 ,
295+ "filter validation failed: filter %s not satisfied by %s" ,
296+ filter_func .name ,
297+ ZSTR_VAL (copy_for_throwing )
298+ );
299+ zend_string_delref (copy_for_throwing );
300+ return ;
301+ }
302+ zend_string_delref (copy_for_throwing );
303+ copy_for_throwing = NULL ;
304+ }
305+
268306handle_default :
269307 if (options && Z_TYPE_P (options ) == IS_ARRAY &&
270308 ((flags & FILTER_NULL_ON_FAILURE && Z_TYPE_P (value ) == IS_NULL ) ||
@@ -449,7 +487,8 @@ PHP_FUNCTION(filter_has_var)
449487
450488static void php_filter_call (
451489 zval * filtered , zend_long filter , HashTable * filter_args_ht , zend_long filter_args_long ,
452- zend_long filter_flags
490+ zend_long filter_flags ,
491+ uint32_t options_arg_num
453492) /* {{{ */ {
454493 zval * options = NULL ;
455494 char * charset = NULL ;
@@ -491,10 +530,25 @@ static void php_filter_call(
491530 }
492531 }
493532
533+ /* Cannot use both FILTER_NULL_ON_FAILURE and FILTER_THROW_ON_FAILURE */
534+ if ((filter_flags & FILTER_NULL_ON_FAILURE ) && (filter_flags & FILTER_THROW_ON_FAILURE )) {
535+ zend_argument_value_error (
536+ options_arg_num ,
537+ "cannot use both FILTER_NULL_ON_FAILURE and FILTER_THROW_ON_FAILURE"
538+ );
539+ return ;
540+ }
541+
494542 if (Z_TYPE_P (filtered ) == IS_ARRAY ) {
495543 if (filter_flags & FILTER_REQUIRE_SCALAR ) {
496544 zval_ptr_dtor (filtered );
497- if (filter_flags & FILTER_NULL_ON_FAILURE ) {
545+ if (filter_flags & FILTER_THROW_ON_FAILURE ) {
546+ zend_throw_exception (
547+ php_filter_failed_exception_ce ,
548+ "filter validation failed: not a scalar value (got an array)" ,
549+ 0
550+ );
551+ } else if (filter_flags & FILTER_NULL_ON_FAILURE ) {
498552 ZVAL_NULL (filtered );
499553 } else {
500554 ZVAL_FALSE (filtered );
@@ -505,8 +559,16 @@ static void php_filter_call(
505559 return ;
506560 }
507561 if (filter_flags & FILTER_REQUIRE_ARRAY ) {
562+ const char * got_type = zend_zval_value_name (filtered );
508563 zval_ptr_dtor (filtered );
509- if (filter_flags & FILTER_NULL_ON_FAILURE ) {
564+ if (filter_flags & FILTER_THROW_ON_FAILURE ) {
565+ zend_throw_exception_ex (
566+ php_filter_failed_exception_ce ,
567+ 0 ,
568+ "filter validation failed: not an array (got %s)" ,
569+ got_type
570+ );
571+ } else if (filter_flags & FILTER_NULL_ON_FAILURE ) {
510572 ZVAL_NULL (filtered );
511573 } else {
512574 ZVAL_FALSE (filtered );
@@ -515,6 +577,10 @@ static void php_filter_call(
515577 }
516578
517579 php_zval_filter (filtered , filter , filter_flags , options , charset );
580+ // Don't wrap in an array if we are throwing an exception
581+ if (EG (exception )) {
582+ return ;
583+ }
518584 if (filter_flags & FILTER_FORCE_ARRAY ) {
519585 zval tmp ;
520586 ZVAL_COPY_VALUE (& tmp , filtered );
@@ -529,7 +595,7 @@ static void php_filter_array_handler(zval *input, HashTable *op_ht, zend_long op
529595) /* {{{ */ {
530596 if (!op_ht ) {
531597 ZVAL_DUP (return_value , input );
532- php_filter_call (return_value , -1 , NULL , op_long , FILTER_REQUIRE_ARRAY );
598+ php_filter_call (return_value , -1 , NULL , op_long , FILTER_REQUIRE_ARRAY , 2 );
533599 } else {
534600 array_init (return_value );
535601 zend_string * arg_key ;
@@ -556,8 +622,13 @@ static void php_filter_array_handler(zval *input, HashTable *op_ht, zend_long op
556622 php_filter_call (& nval , -1 ,
557623 Z_TYPE_P (arg_elm ) == IS_ARRAY ? Z_ARRVAL_P (arg_elm ) : NULL ,
558624 Z_TYPE_P (arg_elm ) == IS_ARRAY ? 0 : zval_get_long (arg_elm ),
559- FILTER_REQUIRE_SCALAR
625+ FILTER_REQUIRE_SCALAR ,
626+ 2
560627 );
628+ if (EG (exception )) {
629+ zval_ptr_dtor (& nval );
630+ RETURN_THROWS ();
631+ }
561632 zend_hash_update (Z_ARRVAL_P (return_value ), arg_key , & nval );
562633 }
563634 } ZEND_HASH_FOREACH_END ();
@@ -597,11 +668,34 @@ PHP_FUNCTION(filter_input)
597668 if (!filter_args_ht ) {
598669 filter_flags = filter_args_long ;
599670 } else {
600- zval * option , * opt , * def ;
671+ zval * option ;
601672 if ((option = zend_hash_str_find (filter_args_ht , "flags" , sizeof ("flags" ) - 1 )) != NULL ) {
602673 filter_flags = zval_get_long (option );
603674 }
675+ }
676+
677+ /* Cannot use both FILTER_NULL_ON_FAILURE and FILTER_THROW_ON_FAILURE */
678+ if ((filter_flags & FILTER_NULL_ON_FAILURE ) && (filter_flags & FILTER_THROW_ON_FAILURE )) {
679+ zend_argument_value_error (
680+ 4 ,
681+ "cannot use both FILTER_NULL_ON_FAILURE and FILTER_THROW_ON_FAILURE"
682+ );
683+ RETURN_THROWS ();
684+ }
685+
686+ if (filter_flags & FILTER_THROW_ON_FAILURE ) {
687+ zend_throw_exception (
688+ php_filter_failed_exception_ce ,
689+ "input value not found" ,
690+ 0
691+ );
692+ RETURN_THROWS ();
693+ }
604694
695+ /* FILTER_THROW_ON_FAILURE overrides defaults, needs to be checked
696+ * before the default is used. */
697+ if (filter_args_ht ) {
698+ zval * opt , * def ;
605699 if ((opt = zend_hash_str_find_deref (filter_args_ht , "options" , sizeof ("options" ) - 1 )) != NULL &&
606700 Z_TYPE_P (opt ) == IS_ARRAY &&
607701 (def = zend_hash_str_find_deref (Z_ARRVAL_P (opt ), "default" , sizeof ("default" ) - 1 )) != NULL
@@ -625,7 +719,7 @@ PHP_FUNCTION(filter_input)
625719
626720 ZVAL_DUP (return_value , tmp );
627721
628- php_filter_call (return_value , filter , filter_args_ht , filter_args_long , FILTER_REQUIRE_SCALAR );
722+ php_filter_call (return_value , filter , filter_args_ht , filter_args_long , FILTER_REQUIRE_SCALAR , 4 );
629723}
630724/* }}} */
631725
@@ -651,7 +745,7 @@ PHP_FUNCTION(filter_var)
651745
652746 ZVAL_DUP (return_value , data );
653747
654- php_filter_call (return_value , filter , filter_args_ht , filter_args_long , FILTER_REQUIRE_SCALAR );
748+ php_filter_call (return_value , filter , filter_args_ht , filter_args_long , FILTER_REQUIRE_SCALAR , 3 );
655749}
656750/* }}} */
657751
0 commit comments