php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #49510 boolean validation fails with FILTER_NULL_ON_FAILURE
Submitted: 2009-09-09 11:25 UTC Modified: 2015-08-21 21:42 UTC
Votes:26
Avg. Score:4.3 ± 0.8
Reproduced:22 of 23 (95.7%)
Same Version:8 (36.4%)
Same OS:13 (59.1%)
From: m dot kurzyna at crystalpoint dot pl Assigned: pajoye (profile)
Status: Closed Package: Filter related
PHP Version: 5.3.0 OS: Linux
Private report: No CVE-ID: None
 [2009-09-09 11:25 UTC] m dot kurzyna at crystalpoint dot pl
Description:
------------
filter_var() when validating (boolean)false with FILTER_NULL_ON_FAILURE returns null instead of false.

Reproduce code:
---------------
var_dump(
	filter_var(false, FILTER_VALIDATE_BOOLEAN, 
		array("flags" => FILTER_NULL_ON_FAILURE)
	)
);

Expected result:
----------------
false


Actual result:
--------------
null


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2009-09-09 19:55 UTC] sjoerd@php.net
This is exactly according to the documentation:
If FILTER_NULL_ON_FAILURE is set, FALSE is returned only for "0", "false", "off", "no", and "", and NULL is returned for all non-boolean values.

Note that "false" is in the list, but false is not. That is, the string "false" will give a result, the boolean false will return null. "Non-boolean" makes this a little bit confusing.

Note that the goal here is to convert a string to a boolean. Because false is not a string, filter_var does not consider it valid input.
 [2009-09-09 22:30 UTC] m dot kurzyna at crystalpoint dot pl
If this is, as you say, intended behaviour then sorry, but it's broken by design. Although because of BC it'll never get fixed unfortunately so i'll just vent my frustration.

In current state you have no way to use filters to *validate* boolean input (note that this is filter_validate_boolean not filter_sanitize_boolean - there is no such).

As a sanitizer it's also broken though because real boolean values can't be used (or distinguished from error values when FILTER_NULL_ON_FAILURE is given) so you'd have to first check whether value is boolean and if not try to sanitize it to boolean using validating filter. It even sound broken.

Also it's internally inconsistent. It works as expected for plain filtering:

var_dump(
        filter_var(true,FILTER_VALIDATE_BOOLEAN),       // true
        filter_var("true",FILTER_VALIDATE_BOOLEAN),     // true
        filter_var(1,FILTER_VALIDATE_BOOLEAN),          // true
        filter_var("1",FILTER_VALIDATE_BOOLEAN),        // true
        filter_var("on",FILTER_VALIDATE_BOOLEAN),       // true
        filter_var("yes",FILTER_VALIDATE_BOOLEAN)       // true
);

var_dump(
        filter_var(false,FILTER_VALIDATE_BOOLEAN),      // false
        filter_var("false",FILTER_VALIDATE_BOOLEAN),    // false
        filter_var(0,FILTER_VALIDATE_BOOLEAN),          // false
        filter_var("0",FILTER_VALIDATE_BOOLEAN),        // false
        filter_var("off",FILTER_VALIDATE_BOOLEAN),      // false
        filter_var("no",FILTER_VALIDATE_BOOLEAN)        // false
);

var_dump(
        filter_var(7,FILTER_VALIDATE_BOOLEAN),          // false
        filter_var("garbage",FILTER_VALIDATE_BOOLEAN)   // false
);

But fails to keep consistency when i try to distinguish whether value is "boolean" or not witch should be the sole role of *VALIDATE* filter:

var_dump(
        filter_var(true,FILTER_VALIDATE_BOOLEAN,array('flags'=>FILTER_NULL_ON_FAILURE)),        // got true, expected true
        filter_var("true",FILTER_VALIDATE_BOOLEAN,array('flags'=>FILTER_NULL_ON_FAILURE)),      // got true, expected true
        filter_var(1,FILTER_VALIDATE_BOOLEAN,array('flags'=>FILTER_NULL_ON_FAILURE)),           // got true, expected true
        filter_var("1",FILTER_VALIDATE_BOOLEAN,array('flags'=>FILTER_NULL_ON_FAILURE)),         // got true, expected true
        filter_var("on",FILTER_VALIDATE_BOOLEAN,array('flags'=>FILTER_NULL_ON_FAILURE)),        // got true, expected true
        filter_var("yes",FILTER_VALIDATE_BOOLEAN,array('flags'=>FILTER_NULL_ON_FAILURE))        // got true, expected true
);

var_dump(
        filter_var(false,FILTER_VALIDATE_BOOLEAN,array('flags'=>FILTER_NULL_ON_FAILURE)),       // got NULL, expected false
        filter_var("false",FILTER_VALIDATE_BOOLEAN,array('flags'=>FILTER_NULL_ON_FAILURE)),     // got false, expected false
        filter_var(0,FILTER_VALIDATE_BOOLEAN,array('flags'=>FILTER_NULL_ON_FAILURE)),           // got false, expected false
        filter_var("0",FILTER_VALIDATE_BOOLEAN,array('flags'=>FILTER_NULL_ON_FAILURE)),         // got false, expected false
        filter_var("off",FILTER_VALIDATE_BOOLEAN,array('flags'=>FILTER_NULL_ON_FAILURE)),       // got false, expected false
        filter_var("no",FILTER_VALIDATE_BOOLEAN,array('flags'=>FILTER_NULL_ON_FAILURE))         // got false, expected false
);

var_dump(
        filter_var(7,FILTER_VALIDATE_BOOLEAN,array('flags'=>FILTER_NULL_ON_FAILURE)),           // got null, expected null
        filter_var("garbage",FILTER_VALIDATE_BOOLEAN,array('flags'=>FILTER_NULL_ON_FAILURE))    // got null, expected null
);

See that validating true works as expected so dunno why false should work differently. Also the "little bit confusing" part of false being non boolean is just plain wrong.



In summary - please rethink this behaviour or at least fix the documentation by a) giving above example of all results (to show what is really to be expected) and b) state this is a validate filter by mistake but will stay like this because of BC concerns.
 [2009-09-09 23:00 UTC] pajoye@php.net
This case sounds wrong:

filter_var(false,FILTER_VALIDATE_BOOLEAN,array('flags'=>FILTER_NULL_ON_FAILURE)),
     // got NULL, expected false

I have to check back the code, but this case does not make sense to me.
 [2009-09-10 08:53 UTC] m dot kurzyna at crystalpoint dot pl
Actually it is broken even more then i initially reported because it also returns NULL for empty string:

filter_var('',FILTER_VALIDATE_BOOLEAN,array('flags'=>FILTER_NULL_ON_FAILURE))       // got NULL, expected false


The problem is in ext/filter/logical_filters.c(233) - the check is done by using string representation of zval being checked. For false value it's an empty string and the switch from line 244 doesn't cover this case (hence same result for false and empty string).

Something along the lines of following patch should fix the problem: 

--- logical_filters.c   2009-06-10 21:01:17.000000000 +0200
+++ logical_filters.fixed.c     2009-09-10 10:48:59.953675880 +0200
@@ -242,6 +242,10 @@
         * returns false for "0", "false", "off", "no", and ""
         * null otherwise. */
        switch (len) {
+               case 0:
+                               ret = 0;
+                       break;
+
                case 1:
                        if (*str == '1') {
                                ret = 1;





On the side note: i was always wondering why (string)false == '' and not '0'?
 [2009-09-10 08:57 UTC] sjoerd@php.net
Why do you think it is wrong that it returns null for an empty string?
 [2009-09-10 09:05 UTC] m dot kurzyna at crystalpoint dot pl
Personally i think it's just fine (empty string ain't false - if anything it's null) but in PHP world it is (both on PHP and C levels):

(string)false = ''
(boolean)'' == false

Z_STRLEN_P(value) = 0


Oh, and there is this little documentation thingy you like to cite from time to time:

If FILTER_NULL_ON_FAILURE is set, FALSE is returned only for "0", "false", "off", "no", and "", and NULL is returned for all non-boolean values.

where empty string is explicitly stated as being false.
 [2009-09-10 11:09 UTC] sjoerd@php.net
I agree that filter_var() should return null for the empty string. I think that this usage of filter_var() is meant to convert string representations of booleans to boolean values. That is, "true", "on", "1", "false", "off" and "0" should be converted, other strings should return null.
 [2009-09-10 11:24 UTC] m dot kurzyna at crystalpoint dot pl
As much as i'd like to have empty string be invalid false cast i have to disagree with you for consistency reasons.

If (boolean)'' == false then filter_var('','boolean') should also return false. Both in general and in case of FILTER_NULL_ON_FAILURE (just like the documentation states).

Also, because i can't stress it enough, this is a VALIDATOR not a SANITIZER so using it as a strict caster is secondary to it's validation purpose and as such it currently fails both on implied and explicit behavior.

The ideal solution would be to have FILTER_VALIDATE_BOOLEAN roughly equal to current behavior with FILTER_NULL_ON_FAILURE and a *seperate* FILTER_SANITIZE_BOOLEAN similar to current behavior w/o the null failure flag. This however probably is impossible due to BC.
 [2010-09-01 13:55 UTC] schkovich at gmail dot com
filter_var(false,FILTER_VALIDATE_BOOLEAN,FILTER_NULL_ON_FAILURE)
     // got NULL, expected false

That does not make sense at all! Further on, I have to agree with m.kurzyna that since false === (bool)"" filter_var("",FILTER_VALIDATE_BOOLEAN,FILTER_NULL_ON_FAILURE) should 
return FALSE and not NULL.

Basically, as implemented, getting FALSE from filter_var(false,FILTER_VALIDATE_BOOLEAN) means that validation failed. It appears to be a design problem since filter_var() as 
specified will return FALSE if the filter fails making it impossible to distinguish if filter failed  or valid FALSE value is returned. Therefore, instead returning FALSE if 
filter fails perhaps warning could be issued or even better exception thrown.

On addition when voting I've wrongly selected that I am not using the same version and the same operating system. Correct ones are:
PHP Version => 5.3.2-1ubuntu4.2
System => Linux schkovich 2.6.32-24-generic #42-Ubuntu SMP Fri Aug 20 14:21:58 UTC 2010 x86_64
 [2010-12-20 13:19 UTC] jani@php.net
-Package: Feature/Change Request +Package: Filter related
 [2012-06-24 00:34 UTC] 2072 at teaser dot fr
Knowing this issue I wanted to make a boolean validation filter of my own using 
FILTER_CALLBACK but it suffers from the same problem, these filters are not
"boolean safe".

It appears that what is to be validated is first converted to a string.

So when given (bool)true my callback actually receives (string)'1' and 
(string)'' when given (bool)false.

There is definitely something wrong.

(I'm using PHP 5.3.8)
 [2012-07-15 04:57 UTC] stas@php.net
Filters operate on strings. So any value that is passed to the filter_var() will 
be coerced into string. This means (boolean)false and '' is exactly the same for 
the filter. And that means the callbacks will be receiving strings too. 

Now, the docs specifically say '' is a valid value for "boolean" filter and is 
converted to false, so '' should not return NULL with FILTER_NULL_ON_FAILURE I 
guess since it's documented not to be failure value.
 [2012-09-12 20:22 UTC] dernelson at corelogic dot com
The question the developer is asking filter_var() is: "is boolean FALSE a valid boolean", and the answer filter_var() is giving back is "nope."  Regardless of the technical details underlying the implementation, there is an obvious problem here.

Short of changing PHP so that (string)FALSE === '0' (hah), I would suggest an explicit test case for boolean FALSE values, so that the function can return boolean FALSE in those cases, instead of NULL.
 [2012-10-26 03:32 UTC] pgarvin76 at gmail dot com
There was a fix committed in branch 5.4.8 (a26390ef0c22be3637795d9b5ab1c445e1d3f847). But the problem still persists for me. The test file (bug49510.phpt) fails on my fresh build.
 [2012-10-26 04:16 UTC] pgarvin76 at gmail dot com
Never mind my last comment. I was using the wrong executable when running the tests. The fix works.

However, this patch was not merged into the 5.3 branch. Can we get it merged for 5.3.19?
 [2015-08-21 21:42 UTC] cmb@php.net
-Status: Assigned +Status: Closed
 [2015-08-21 21:42 UTC] cmb@php.net
Implemented as of PHP 5.4.8, see <https://3v4l.org/s4nvV>.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 08:01:29 2024 UTC