Thursday, June 20, 2024

To Tell the Truth – The Daily WTF

Programming LanguageTo Tell the Truth - The Daily WTF


So many languages eschew “truth” for “truthiness”. Today, we’re looking at PHP’s approach.

PHP automatically coerces types to a boolean with some fairly simple rules:

  • the boolean false is false
  • the integer 0 is false, as is the float 0.0 and -0.0.
  • empty strings and the string "0" are false
  • arrays with no elements are false
  • NULL is false
  • objects may also override the cast behavior to define their own
  • everything else is true

Honestly, for PHP, this is fairly sane and reasonable. The string "0" makes my skin itch a bit, and the fact that the documentation page needs a big disclaimer warning that -1 is true hints at some people blundering as they convert from other languages.

But we’re not doing a language critique. We’re looking at a very specific block of code, which Jakub inherited. You see, someone didn’t like this, so they implemented their own version:

protected static function emulate_filter_bool( $value ) {
	
	static $true  = array(
		'1',
		'true', 'True', 'TRUE',
		'y', 'Y',
		'yes', 'Yes', 'YES',
		'on', 'On', 'ON',
	);
	static $false = array(
		'0',
		'false', 'False', 'FALSE',
		'n', 'N',
		'no', 'No', 'NO',
		'off', 'Off', 'OFF',
	);
	

	if ( is_bool( $value ) ) {
		return $value;
	} elseif ( is_int( $value ) && ( 0 === $value || 1 === $value ) ) {
		return (bool) $value;
	} elseif ( ( is_float( $value ) && ! is_nan( $value ) ) && ( (float) 0 === $value || (float) 1 === $value ) ) {
		return (bool) $value;
	} elseif ( is_string( $value ) ) {
		$value = trim( $value );
		if ( in_array( $value, $true, true ) ) {
			return true;
		} elseif ( in_array( $value, $false, true ) ) {
			return false;
		} else {
			return false;
		}
	}

	return false;
}

I love when a code block starts with an annotation to force the linter to ignore them. Always great.

We start by declaring arrays of a variety of possible true and false values. Most of the variations are in capitalization, but not exhaustive variations in capitalization, which I’m sure will cause problems at some point. But let’s see how this code works.

First, we check if the value is a boolean, and if so, return it. Fine, very reasonable.

Second, we check if it’s an integer, and if it is either 0 or 1, we cast it to a boolean and return it. In pure PHP, anything non-zero is true, but here only 1 is true.

We then do a similar check for floats- if it’s a float, it is a number, and it’s either 0 or 1, we cast it to float and return it. Note, however, that they’re doing a simple equality comparison- which means that floating point inputs like 1.000000001 will fail this test, but will often still get printed as 1 when formatted for output, causing developers to get very confused (ask Jakub how he knows).

Finally, we check for strings, and do an in_array comparison to check if the value is either true or false. If it’s neither true nor false, we simply return false (instead of FILE_NOT_FOUND). This raises the obvious question: why even bother with the check against the false values array, when we can just assume the input is false if it isn’t in the true values array?

Jakub has this to add:

This method finds application in numerous places, particularly where front-end interfaces display checkboxes. I truly dread discovering the full extent of the code responsible for that…

[Advertisement]
Continuously monitor your servers for configuration changes, and report when there’s configuration drift. Get started with Otter today!

Check out our other content

Check out other tags:

Most Popular Articles