rfc:empty_function

PHP RFC: standard built-in is_empty() function

Introduction

The official documentation of the function empty() is at https://www.php.net/manual/en/function.empty.php. All the quotations below have been reported from the online version up to the date of this RFC, namely October 31th 2023.

Here the purpose of the function empty() is to `Determine whether a variable is considered to be empty'. Before exploring the behavior of this built-in function, we would argue upon the following sentence: `A variable is considered empty if it does not exist or if its value equals false'. While we agree on the first part, we could not do about the second half, as it throws an exception about the meaning of `emptiness'.

Defining `emptiness' might be very a subtle argument and actually not all the most blasoned online dictionaries succeed in giving a quite insightful definition. First,

Merriam-Webster: `containing nothing', `not occupied or inhabited'

I do not like the usage of the above term `nothing', as this is a generic term which does not mean anything with regard to the relation between containers and contents, which is the core of `emptiness' definition.

In this sense, a little better version follows:

Oxford dictionary: `The condition of being without contents'

But it can be improved further in terms of specialized contents for containers, being actually the habitat of variables in programming languages. Finally, a more refined definition is the next, because it sets up a quality relation in terms of `appropriate' contents.

Dictionary.com: `the fact or state of containing nothing or of being without the usual or appropriate contents'.

Before our conclusions, I will first resume basic notions about variable declaration; each example is followed by some equivalent statement in C-family languages, as they are pretty clearer about the strict connection between the kind of containers and contents.

//a container for storing an integer number was instantiated,
//named `integer' and set to the constant value 1
$integer = 1;
 
//In C-family languages, it will ruled through this instantiation
//int <var_name> = 1;

Emptiness here occurs only when

$integer;

is coded.

//a container for storing an integer number was instantiated,
//named `float' and set to the constant value 1.1
$float = 1.1;
 
//In C-family languages, it will ruled through this instantiation
//double <var_name> = 1.1;

Analogously, emptiness here occurs only when

$float;

is coded. A slightly variegated situation occurs for emptiness of strings:

//a container for storing a sequence of characters was instantiated
//named `string' and then set to something
$string = "<any or no character>";
 
//In C-family languages, it corresponds to the pointer-like syntax
//char* <var_name> = "<any or no characters>";

Emptiness here occurs in two cases: when

$string;

or

$string="";

are coded.

We conclude that the definition of `emptiness' changes according to the kind of container. Therefore the second option in the official definition of emptiness in the empty function documentation, namely `a variable is considered empty if its value equals false', cannot logically holds, with regard to the essence of the variable container. The constant boolean value `false' alludes to the instantiation of a one-byte container, which is filled through the `false' constant value, hence the one-byte container cannot be judged as `empty'. Moreover, the same objection holds for the zero (0) input value, according to the official return value of empty() function in php.net, being documented as follows: `Returns true if var does not exist or has a value that is empty or equal to zero, aka falsey, see conversion to boolean. Otherwise returns false'.

In my viewpoint, the optimal behavior of any emptiness-test function shall rely upon the match between the container and the kind of contents. I resumed here below the flaws that should be eventually settled:

1) the semantics: the function name is ambiguous about the action, that is, it is not clear whether the goal of empty() function should consist of testing the `emptiness' of input variable or return an empty version of the input variable.

2) the behavior. The only three issues arise from inputing no variable or managing the false and 0 constant values.

The ordinary behavior with all the built-in datatypes has been listed below:

echo "built-in function `empty' behavior:\n";
echo "Stage 1: empty, undeclared or null containers (`empty' function)\n";
echo "null value >> empty( null ): ".( empty( null ) ? "Empty" : "Not empty" )."\n";
echo "undeclared var >> empty( \$i ): ".( empty( $i ) ? "Empty" : "Not empty" )."\n";
//echo "empty input >> empty() : ".( empty() ? "Empty" : "Not empty" )."\n"; //empty input var will let it crash
echo "empty string >> empty( \"\" ) : ".( empty( "" ) ? "Empty" : "Not empty" )."\n";
echo "empty array >> empty( [] ) : ".( empty( [] ) ? "Empty" : "Not empty" )."\n";
 
$res = fopen( 'file.txt', "w+" );
echo "resource \$res = fopen( 'file.txt', \"w+\" );\n";
echo "resource >> empty( \$res ) : ".( empty( $res ) ? "Empty" : "Not empty" )."\n";
if ( $res !== false ) fclose( $res );
 
echo "Stage 2: non-empty containers (`empty' function)\n";
echo "boolean true >> empty( true ) : ".( empty( true ) ? "Empty" : "Not empty" )."\n";
echo "boolean false >> empty( false ) : ".( empty( false ) ? "Empty" : "Not empty" )."\n"; // expected return value 0, but 1 is returned
echo "constant 0 value >> empty( 0 ) : ".( empty( 0 ) ? "Empty" : "Not empty" )."\n"; // expected return value 0, but 1 is returned
 
$a;
echo "instantiated variable \$a but not filled >> empty( \$a ) : ".( empty( $a ) ? "Empty" : "Not empty" )."\n";
 
$i = 1; // I have declared a variable for the test below
echo "instanatiated var and filled >> empty( \$i ) : ".( empty( $i ) ? "Empty" : "Not empty" )."\n";
 
echo "non-empty string >> empty( \"something\" ) : ".( empty( "something" ) ? "Empty" : "Not empty" )."\n";
echo "non empty array >> empty( [10] ) : ".( empty( [10] ) ? "Empty" : "Not empty" )."\n";

We remark, to the benefit of readers, that the empty string is namely “”, that is, no characters between (double)quotes. The empty space, namely “ ”, cannot match the definition of emptiness. Contents of string containers are properly characters only (i.e., any kind of symbol); hence, according to the dictionaries definitions listed at the top of this RFC, the emptiness of some string occurs when the given variable container for string storage includes no character in general, either if the statements

$string;

or

$string = "";

are coded.

Proposal

I understand that this proposal could represent a huge break, anyway I believe that my points above shall not be disregarded in order to strengthen parts of PHP that look flawed or fuzzy so far. Honestly, I still read tons of people out there mocking up PHP for its non-typified nature. Also consider that 1) semantics, as you know, is essential for languages. 2) the function behavior match the purposes declared in its name as well as it must cover all cases as possible, without exceptions.

First, the empty() name shall be adequated to the is_*() family of php built-in functions (is_null(), is_bool(), is_string(), is_array(), ...). Hence I propose is_empty(). The return value will be true for emptiness and false otherwise. If the input variable is not empty would throw an error.

Second, I implemented the following version which fixes all the above flaws:

function is_empty( $input = null )
{
     $ser = @serialize( $input );
     if ( preg_match( "/^N;$/i", $ser ) === 1 ) return 1;
     if ( preg_match( "/^b\:[01]\;?$/i", $ser ) === 1 ) return 0;
     return preg_match( "/0\:(\{\}|\[\]|\"\")\;?$/i", $ser ) === 1 ? 1 : 0;
}

Let me first say that I resorted to the serialize() built-in function just for showing here how the function is_empty() should work. The serialization just represents an escamotage in PHP for obtaining a formal basis for eventually checking the input variable emptiness in the examples below. However, I presume that the internal C-implementation would approach emptiness in most proper ways involving memory tests. Please, note that I suppressed warnings in order to let this function run throughout the next tests, that also cover undefined variables:

echo ">new `is_empty()' function behavior\n";
echo "Stage 1: empty, undeclared or null containers (`is_empty' function)\n";
echo "null value >> is_empty( null ): ".( is_empty( null ) ? "Empty" : "Not empty" )."\n";
echo "undeclared var >> is_empty( \$un ) : ".( is_empty( $un ) ? "Empty" : "Not empty" )."\n";
echo "empty input >> is_empty() : ".( is_empty() ? "Empty" : "Not empty" )."\n";
echo "empty string >> is_empty( \"\" ) : ".( is_empty( "" ) ? "Empty" : "Not empty" )."\n";
echo "empty array >> is_empty( [] ) : ".( is_empty( [] ) ? "Empty" : "Not empty" )."\n";
 
echo "Stage 2: non-empty containers (`is_empty' function)\n";
echo "boolean true >> is_empty( true ) : ".( is_empty( true ) ? "Empty" : "Not empty" )."\n";
echo "boolean false >> is_empty( false ) : ".( is_empty( false ) ? "Empty" : "Not empty" )."\n";
echo "zero (0) constant value >> is_empty( 0 ) : ".( is_empty( 0 ) ? "Empty" : "Not empty" )."\n";
 
$a;
echo "instantiated variable \$a but not filled >> is_empty( \$a ) : ".( is_empty( $a ) ? "Empty" : "Not empty" )."<br/>";
 
$i = 1;
echo "instantiated var and filled >> is_empty( \$i ) : ".( is_empty( $i ) ? "Empty" : "Not empty" )."\n";
 
echo "non-empty string >> is_empty( \"something\" ) : ".( is_empty( "something" ) ? "Empty" : "Not empty" )."\n";
echo "non empty array >> is_empty( [ 10 ] ): ".( is_empty( [10] ) ? "Empty" : "Not empty" )."\n";
 
$res = fopen( 'file.txt', "w+" );
echo "resource \$res = fopen( 'file.txt', \"w+\" );\n";
echo "resource >> is_empty( \$res ) : ".( is_empty( $res ) ? "Empty" : "Not empty" )."\n";
if ( $res !== false ) fclose( $res );

The above code works correctly covers all cases of basic datatypes and, last but not least, can be also finely read.

Proposed PHP Version(s)

According to the official policies documented at https://wiki.php.net/rfc/releaseprocess, backwards compatibility can only be broken in a major version, hence the new function is_empty() would appear not before than version 9.0

The option of keeping empty() function together with is_empty() is fine too, so deprecation is not required: backward compatibility would be saved, though I would recommend the usage of is_empty() however.

RFC Impact

To SAPIs

None

To Existing Extensions

None

To Opcache

None

Please explain how you have verified your RFC's compatibility with opcache.

New Constants

None

php.ini Defaults

None

Open Issues

None

Unaffected PHP Functionality

Just the current nomenclature of standard built-in empty() function.

Future Scope

None

Proposed Voting Choices

We will probably vote for or against adding these functions. This requires 2/3 majority.

Patches and Tests

None

Implementation

Refer to the above code.

References

No refs or links

Rejected Features

None.

rfc/empty_function.txt · Last modified: 2023/10/31 16:42 by alessandro.a.rosa_gmail.com