How to implement is_empty() in PHP? - php

The empty() native in PHP will fail if the string is "0" or something like that.
So how to implement the exact is_empty() function in PHP?

try this:
function is_empty(&$val) {
return empty($val) && $val !== "0";
}
&$val is needed, so you don’t get a warning on undefined variables.
if you only want to check if a variable is set (regardless of its value) you should use PHP’s isset

You may want to use isset if you are trying to check if that variable is defined.

Check out the type comparison table in the PHP manual for the exact behaviour of empty(), isset(), is_null() etc. You'll probably find what you're looking for there.

My solution for this one:
/**
* Checks if a scalar value is FALSE, without content or only full of
* whitespaces.
* For non-scalar values will evaluate if value is empty().
*
* #param mixed $v to test
* #return bool if $v is blank
*/
function is_blank (&$v)
{
return !isset($v) || (is_scalar($v) ? (trim($v) === '') : empty($v));
}
See http://3960.org/sandbox/empty.php for its behavior. Essentially all values will be returned the same as with empty(), but for strings and numbers evaluating to '0'.

Quite simple, check for the error case described above. If you run into it, return the result you want. If not, call empty() and return that result.

I always add to my codebase
function is_blank($value) {
return empty($value) && !is_numeric($value);
}
and use it instead of empty(). It solves the issue of keeping zeros (int, float or string) as non-empty.
See http://www.php.net/manual/en/function.empty.php#103756 which was added May 2011.

Related

initialize a PHP variable if not set [duplicate]

I have several older applications that throw a lot of "xyz is undefined" and "undefined offset" messages when running on the E_NOTICE error level, because the existence of variables is not explicitly checked using isset() and consorts.
I am considering working through them to make them E_NOTICE compatible, as notices about missing variables or offsets can be lifesavers, there may be some minor performance improvements to be gained, and it's overall the cleaner way.
However, I don't like what inflicting hundreds of isset() empty() and array_key_exists() s does to my code. It gets bloated, becomes less readable, without gaining anything in terms of value or meaning.
How can I structure my code without an excess of variable checks, while also being E_NOTICE compatible?
For those interested, I have expanded this topic into a small article, which provides the below information in a somewhat better structured form: The Definitive Guide To PHP's isset And empty
IMHO you should think about not just making the app "E_NOTICE compatible", but restructuring the whole thing. Having hundreds of points in your code that regularly try to use non-existent variables sounds like a rather badly structured program. Trying to access non-existent variables should never ever happen, other languages balk at this at compile time. The fact that PHP allows you to do it doesn't mean you should.
These warnings are there to help you, not to annoy you. If you get a warning "You're trying to work with something that doesn't exist!", your reaction should be "Oops, my bad, let me fix that ASAP." How else are you going to tell the difference between "variables that work just fine undefined" and honestly wrong code that may lead to serious errors? This is also the reason why you always, always, develop with error reporting turned to 11 and keep plugging away at your code until not a single NOTICE is issued. Turning error reporting off is for production environments only, to avoid information leakage and provide a better user experience even in the face of buggy code.
To elaborate:
You will always need isset or empty somewhere in your code, the only way to reduce their occurrence is to initialize your variables properly. Depending on the situation there are different ways to do that:
Function arguments:
function foo ($bar, $baz = null) { ... }
There's no need to check whether $bar or $baz are set inside the function because you just set them, all you need to worry about is if their value evaluates to true or false (or whatever else).
Regular variables anywhere:
$foo = null;
$bar = $baz = 'default value';
Initialize your variables at the top of a block of code in which you're going to use them. This solves the !isset problem, ensures that your variables always have a known default value, gives the reader an idea of what the following code will work on and thereby also serves as a sort of self-documentation.
Arrays:
$defaults = array('foo' => false, 'bar' => true, 'baz' => 'default value');
$values = array_merge($defaults, $incoming_array);
The same thing as above, you're initializing the array with default values and overwrite them with actual values.
In the remaining cases, let's say a template where you're outputting values that may or may not be set by a controller, you'll just have to check:
<table>
<?php if (!empty($foo) && is_array($foo)) : ?>
<?php foreach ($foo as $bar) : ?>
<tr>...</tr>
<?php endforeach; ?>
<?php else : ?>
<tr><td>No Foo!</td></tr>
<?php endif; ?>
</table>
If you find yourself regularly using array_key_exists, you should evaluate what you're using it for. The only time it makes a difference is here:
$array = array('key' => null);
isset($array['key']); // false
array_key_exists('key', $array); // true
As stated above though, if you're properly initializing your variables, you don't need to check if the key exists or not, because you know it does. If you're getting the array from an external source, the value will most likely not be null but '', 0, '0', false or something like it, i.e. a value you can evaluate with isset or empty, depending on your intent. If you regularly set an array key to null and want it to mean anything but false, i.e. if in the above example the differing results of isset and array_key_exists make a difference to your program logic, you should ask yourself why. The mere existence of a variable shouldn't be important, only its value should be of consequence. If the key is a true/false flag, then use true or false, not null. The only exception to this would be 3rd party libraries that want null to mean something, but since null is so hard to detect in PHP I have yet to find any library that does this.
Just write a function for that. Something like:
function get_string($array, $index, $default = null) {
if (isset($array[$index]) && strlen($value = trim($array[$index])) > 0) {
return get_magic_quotes_gpc() ? stripslashes($value) : $value;
} else {
return $default;
}
}
which you can use as
$username = get_string($_POST, 'username');
Do the same for trivial stuff like get_number(), get_boolean(), get_array() and so on.
I believe one of the best ways of coping with this problem is by accessing values of GET and POST (COOKIE, SESSION, etc.) arrays through a class.
Create a class for each of those arrays and declare __get and __set methods (overloading). __get accepts one argument which will be the name of a value. This method should check this value in the corresponding global array, either using isset() or empty() and return the value if it exists or null (or some other default value) otherwise.
After that you can confidently access array values in this manner: $POST->username and do any validation if needed without using any isset()s or empty()s. If username does not exist in the corresponding global array then null will be returned, so no warnings or notices will be generated.
I don't mind using the array_key_exists() function. In fact, I prefer using this specific function rather than relying on hack functions which may change their behavior in the future like empty and isset (strikedthrough to avoid susceptibilities).
I do however, use a simple function that comes handy in this, and some other situations in dealing with array indexes:
function Value($array, $key, $default = false)
{
if (is_array($array) === true)
{
settype($key, 'array');
foreach ($key as $value)
{
if (array_key_exists($value, $array) === false)
{
return $default;
}
$array = $array[$value];
}
return $array;
}
return $default;
}
Let's say you've the following arrays:
$arr1 = array
(
'xyz' => 'value'
);
$arr2 = array
(
'x' => array
(
'y' => array
(
'z' => 'value',
),
),
);
How do you get the "value" out of the arrays? Simple:
Value($arr1, 'xyz', 'returns this if the index does not exist');
Value($arr2, array('x', 'y', 'z'), 'returns this if the index does not exist');
We already have uni and multi-dimensional arrays covered, what else can we possibly do?
Take the following piece of code for instance:
$url = 'https://stackoverflow.com/questions/1960509';
$domain = parse_url($url);
if (is_array($domain) === true)
{
if (array_key_exists('host', $domain) === true)
{
$domain = $domain['host'];
}
else
{
$domain = 'N/A';
}
}
else
{
$domain = 'N/A';
}
Pretty boring isn't it? Here is another approach using the Value() function:
$url = 'https://stackoverflow.com/questions/1960509';
$domain = Value(parse_url($url), 'host', 'N/A');
As an additional example, take the RealIP() function for a test:
$ip = Value($_SERVER, 'HTTP_CLIENT_IP', Value($_SERVER, 'HTTP_X_FORWARDED_FOR', Value($_SERVER, 'REMOTE_ADDR')));
Neat, huh? ;)
Welcome to null coalescing operator (PHP >= 7.0.1):
$field = $_GET['field'] ?? null;
PHP says:
The null coalescing operator (??) has been added as syntactic sugar for the common case of needing to use a ternary in conjunction with isset(). It returns its first operand if it exists and is not NULL; otherwise it returns its second operand.
I'm here with you. But PHP designers has made a lot more worse mistakes than that. Short of defining a custom function for any value reading, there isn't any way around it.
I use these functions
function load(&$var) { return isset($var) ? $var : null; }
function POST($var) { return isset($_POST[$var]) ? $_POST[$var] : null; }
Examples
$y = load($x); // null, no notice
// this attitude is both readable and comfortable
if($login=POST("login") and $pass=POST("pass")) { // really =, not ==
// executes only if both login and pass were in POST
// stored in $login and $pass variables
$authorized = $login=="root" && md5($pass)=="f65b2a087755c68586568531ad8288b4";
}
Make a function which returns false if not set, and, if specified, false if empty. If valid it returns the variable. You can add more options as seen in the code below:
<?php
function isset_globals($method, $name, $option = "") {
if (isset($method[$name])) { // Check if such a variable
if ($option === "empty" && empty($method[$name])) { return false; } // Check if empty
if ($option === "stringLength" && strlen($method[$name])) { return strlen($method[$name]); } // Check length of string -- used when checking length of textareas
return ($method[$name]);
} else { return false; }
}
if (!isset_globals("$_post", "input_name", "empty")) {
echo "invalid";
} else {
/* You are safe to access the variable without worrying about errors! */
echo "you uploaded: " . $_POST["input_name"];
}
?>
Software does not magically run by the grace of god. If you are expecting something that is missing, you need to properly handle it.
If you ignore it, you are probably creating security holes in your applications. In static languages accessing a non-defined variable it is just not possible. It won't simply compile or crash your application if it's null.
Furthermore, it makes your application unmaintainable, and you are going to go mad when unexpected things happen. Language strictness is a must and PHP, by design, is wrong in so many aspects. It will make you a bad programmer if you are not aware.
I'm not sure what your definition of readability is, but proper use of empty(), isset() and try/throw/catch blocks, is pretty important to the whole process.
If your E_NOTICE is coming from $_GET or $_POST, then they should be checked against empty() right along with all the other security checks that that data should have to pass.
If it's coming from external feeds or libraries, it should be wrapped in try/catch.
If it's coming from the database, $db_num_rows() or its equivalent should be checked.
If it's coming from internal variables, they should be properly initialized. Often, these types of notices come from assigning a new variable to the return of a function that returns FALSE on a failure. Those should be wrapped in a test that, in the event of a failure, can either assign the variable an acceptable default value that the code can handle, or throwing an exception that the code can handle.
These things make the code longer, add extra blocks, and add extra tests, but I disagree with you in that I think they most definitely add extra value.
What about using the # operator?
For example:
if(#$foo) { /* Do something */ }
You may say this is bad because you have no control of what happens "inside" $foo (if it was a function call that contains a PHP error for example), but if you only use this technique for variables, this is equivalent to:
if(isset($foo) && $foo) { /* ... */ }

Can't use function return value in write

I got new one problem with empty or trim function. I guess trim cant return a boolean but I dont know why ? :c
$author = htmlentities($_POST['author']);
if(empty(trim($author))) $author="Unknown";
Gives me this error:
Fatal error: Can't use function return value in write context
From http://php.net/manual/es/function.empty.php
"Prior to PHP 5.5, empty() only supports variables; anything else will result in a parse error. In other words, the following will not work: empty(trim($name)). Instead, use trim($name) == false."
Or save trim($name) into another variable and use empty there
Trim the string variable separately. trim() can not return a boolean value as it returns a string value. If you mean that empty() combined with trim() can't return a boolean value, I think that is weird and should work.
Try this:
$author = htmlentities($_POST['author']);
$author = trim($author);
if(empty($author)){
$author = "Unknown";
}
http://php.net/manual/en/function.empty.php
Note:
Prior to PHP 5.5, empty() only supports variables; anything else will
result in a parse error. In other words, the following will not work:
empty(trim($name)). Instead, use trim($name) == false.

PHP: is empty($var) equivalent to !$var

For validating a variable value we can do
if(empty($var)){
}
OR
This will return true on empty string, 0 as number, false, null
if(!$var){
}
What is the difference between this two approaches, are them equivalent?
EDIT: Some examples where they behave different will be more pratical.
EDIT2: The only difference founded from answers is that the second will throw a notice if $var is undefined. What about the boolean value they return?
EDIT3: for $var I mean any variable with any value, or even an undefined variable.
Conclusion from users answers:
if(!$var) and empty($var) are equivalent as described here http://php.net/manual/en/types.comparisons.php, they will return the same bool value on the same variable.
if(!$var) will return a php notice if $var is not defined, normally this is not the case (if we write good code) most IDEs will underline it.
When checking simple variables if(!$var) should be ok
When checking arrays index ($var['key']) or object properties ($var->key)
empty($var['key']) is better using empty.
the problem is that since !$vars is shorter than empty($vars) many of us will prefer the first way
You prefer the first one because it is a "shorter way"?
Shorter code does not mean better code, or faster scripts ;)
The speed of PHP functions and its various other behaviours is not determined by the length of the function name. It is determined by what PHP is actually doing to evaluate, action, and return results.
Besides that, don't choose methods based on length of code, choose methods based on scenario and best approach "for a given scenario".
Which is best depends on what you need, and there are other variable checks other than the two you mentioned (isset() for one).
but the problem is are they equivalent always
Not necessarily - see
http://php.net/manual/en/types.comparisons.php
Or create a quick test script to see what PHP returns for your two scenarios.
You could also be initialising your variables in your framework (or, likely, stand alone script), which means the scenario changes, as could your question and approach you use.
It's all contextual as to which is the best.
As for the required answer.
Anyway, to answer your question, here are some tests:
(!$vars)
Example code:
if ( !$vars )
{
echo "TRUE";
}
else
{
echo "FALSE";
}
will return:
Notice: Undefined variable: vars in /whatever/ on line X
TRUE
However, if you initialise the var in your scripts somewhere first:
$vars = "";
$vars = NULL;
$vars = 0;
Any of the above will return:
[no PHP notice]
TRUE
$vars = "anything";
will return:
FALSE
This is because with the exclamation mark you are testing if the var is FALSE, so when not initialised with a string the test script returns TRUE because it is NOT FALSE.
When we initialise it with a string then the var NOT being FALSE is FALSE.
empty($vars)
Example code:
if ( empty($vars) )
{
echo "TRUE";
}
else
{
echo "FALSE";
}
Not initialised/set at all, and all of the following:
$vars = "";
$vars = NULL;
$vars = 0;
will return:
TRUE
There is no PHP notice for using empty, so here we show a difference between the two options (and remember when I said the shortest code is not necessarily the "best"? Depends on the scenario etc.).
And as with the previous test:
$vars = "anything";
returns:
FALSE
This is the same with ( !$var ), you are testing IF EMPTY, and without the var being initialised at all, or with any "empty()" value: eg (""), or NULL, or zero (0), then testing if the var is empty is TRUE, so we get TRUE output.
You get FALSE when setting the var to a string because IS EMPTY = FALSE as we set it to something.
The difference is empty() does not throw a PHP notice when var is not defined, whereas (!$var) will.
Also, you may prefer it for being "shorter code", but if (!$var) isn't really much shorter/better looking than the widely used if (empty($var)).
But again, this depends on the scenario - PHP provides different options to suit different requirements.
No they are not equal
if(empty($var)){
echo "empty used\n";
}
if(!$var){ # line no. 6
echo "! used";
}
will output
empty used
Notice: Undefined variable: var in /var/www/html/test.php on line 6
! used
Following values are considered to be empty when using empty() function
"" (an empty string)
0 (0 as an integer)
0.0 (0 as a float)
"0" (0 as a string)
NULL
FALSE
array() (an empty array)
$var; (a variable declared, but without a value)
As you can read in empty docs
empty() is essentially the concise equivalent to !isset($var) || $var
== false.
They are not.
The first one verify if $var has any value.
The second one verify as boolean - if true or not.
The second one will give you a notice, the first one will be true if the value is empty for $var.
If you wish to verify if $var exists, use isset.
if( !isset($var) )
echo '$var does not exists!<br>';
if(empty($var))
echo 'The value is empty or does not exists!<br>';
if( !$var )
echo 'Value is false!<br>';
$var = '';
if(empty($var))
echo 'The value is empty or does not exists!<br>';
Use this to view the notice
error_reporting(-1);
ini_set('display_errors', 1);
The main difference is that empty() will not complain if the variable does not exist, whereas using ! will generate a warning.
In older versions of PHP, empty() only worked on direct variables, meaning this would fail:
empty($a && $b);
This has been changed in 5.5
The official manual contains all you have to know on this subject:
http://php.net/manual/en/types.comparisons.php
if (!$var) is the last column, boolean : if($x) negated.
As you can see they are the same, but empty won't complain if the variable wasn't set.
From the manual:
empty() does not generate a warning if the variable does not exist
Take some time, and study that chart.
As far as I know, it's pretty simple.
empty() is basically equivalent to !isset($var) || !$var and does not throw any warnings/notices, whereas using just !$var will throw a notice if the variable isn't defined.
For the sake of completeness, the following are considered empty when using empty():
empty strings
empty arrays
0, 0.0, '0' (int, float, string)
null
false
defined variables without a value
From a boolean value return empty is equivaliend to !:
empty($var) === !$var; // supposed that $vars has been defined here
From a notice/waning notification they are not equivalent:
empty($array['somekey']); //will be silent if somekey does not exists
!$array['somekey']; // will through a warning if somekey does not exists

Is it correct way to return value in PHP

I have very basic question regarding return value of a function, and checking the variable value.
function test($var1, $var2){
if ($var1 == $var2){
$var3 = "abc";
return $var3;
}
return false
}
$value = test($var1, $var2);
if ($value){
echo "Value is".$value; //should output abc.
} else {
echo "Not equal";
}
Is it ok to either return a value or return false? For example I am not returning TRUE, it is ok?
When i call the function, i store the return value in a variable $value. How can i check the function did return the $var3? Which of the if condition should be used?
if (!empty($value)) or if (isset($value)) or if ($value) or if (value != false)
Yes, it is common practice in PHP to return FALSE as an indicator of an error condition. (What constitutes an error is your own decision and depends on what the function is supposed to do.)
However, since PHP automatically casts values to Boolean that are of another type (like the empty string or 0, which evaluate to FALSE as well), you should do an explicit check for FALSE like this:
if ($value !== FALSE) ...
As Felix Kling notes in the comments, this is called "strict comparison" (or "identity comparison"). It checks if a value is identical to FALSE, where as != FALSE, == FALSE and if ($value) only check if a value could be interpreted as FALSE.
I'm not a PHP developer, but I don't think your first approach works.
There are other things than the boolean value false interpreted as false:
When converting to boolean, the following values are considered FALSE:
* the boolean FALSE itself
* the integer 0 (zero)
* the float 0.0 (zero)
* the empty string, and the string "0"
* an array with zero elements
* an object with zero member variables (PHP 4 only)
* the special type NULL (including unset variables)
* SimpleXML objects created from empty tags
http://php.net/manual/en/language.types.boolean.php
It's perfectly OK to return different data types.
If you want to check against false, use: if ($value !== false). If you get lost which condition to use, this will clarify it: http://www.php.net/manual/en/types.comparisons.php
Your function returns false, so I would go with that check: if ($value != false)
$var3 = "abc";
return $var3;
That's pointless. You're returning a value, not a variable. return "abc"; is perfectly fine.
Is it ok to either return a value or return false?
Yes, that's perfectly fine for a simple case such as this.
How can i check the function did return the $var3?
As said above, the function returns the value "abc", not $var3. You're saving it in a new variable $value. This variable is definitely set (you just created it right there), so there's no need for isset or empty. Just test whether its value is true or false (or whatever else you want to test for). So the way you're doing it in fine.
Yes, you can return pretty much anything from the function, or you can just "return" without returning anything. In your example, you'll get a string or "false" in return.
To check for false you either do if (!$variable) or if ($variable===false). Zero will return true if you do "if ($variable==false)" due to auto casting of zero to false (and any other positive number to true). Three "===" makes sure it really is false and nothing else. The isset($var) checks for existance, not value - and is not applicable to your example since your function will return a value or "false" and thus always exists.
The only right answer here is: it depends.
I always ask myself this question when creating a function like this. To answer it, I analyze what the function does, instead of what it returns.
For instance, if I have a getter, I expect to get a value, or nothing. In this case I often return null when nothing is found/something went wrong.
A test function like yours should return a boolean at all times, in my opinion. Returning a variable when you're checking for something to be true or false is semantically incorrect, I think.
Aside from the semantics: returning 0, false or null does not really matter when you're checking it with if (test($var1, $var2)), since it will all work the same. However, if you want some finer details, you want to do an identity check (===) rather than a equality check. In PHP this is sometimes the case, for instance strpos can return 0 or false, 0 being a match is found, and false is not. Therefore the following would fail:
// returns 0, which is casted to false, so the 'else' part is executed
if (strpos('a', 'abc')) {
// 'abc' contains 'a'
} else {
// 'abc' does not contain 'a'
}
So, long story short: it depends...

How to avoid isset() and empty()

I have several older applications that throw a lot of "xyz is undefined" and "undefined offset" messages when running on the E_NOTICE error level, because the existence of variables is not explicitly checked using isset() and consorts.
I am considering working through them to make them E_NOTICE compatible, as notices about missing variables or offsets can be lifesavers, there may be some minor performance improvements to be gained, and it's overall the cleaner way.
However, I don't like what inflicting hundreds of isset() empty() and array_key_exists() s does to my code. It gets bloated, becomes less readable, without gaining anything in terms of value or meaning.
How can I structure my code without an excess of variable checks, while also being E_NOTICE compatible?
For those interested, I have expanded this topic into a small article, which provides the below information in a somewhat better structured form: The Definitive Guide To PHP's isset And empty
IMHO you should think about not just making the app "E_NOTICE compatible", but restructuring the whole thing. Having hundreds of points in your code that regularly try to use non-existent variables sounds like a rather badly structured program. Trying to access non-existent variables should never ever happen, other languages balk at this at compile time. The fact that PHP allows you to do it doesn't mean you should.
These warnings are there to help you, not to annoy you. If you get a warning "You're trying to work with something that doesn't exist!", your reaction should be "Oops, my bad, let me fix that ASAP." How else are you going to tell the difference between "variables that work just fine undefined" and honestly wrong code that may lead to serious errors? This is also the reason why you always, always, develop with error reporting turned to 11 and keep plugging away at your code until not a single NOTICE is issued. Turning error reporting off is for production environments only, to avoid information leakage and provide a better user experience even in the face of buggy code.
To elaborate:
You will always need isset or empty somewhere in your code, the only way to reduce their occurrence is to initialize your variables properly. Depending on the situation there are different ways to do that:
Function arguments:
function foo ($bar, $baz = null) { ... }
There's no need to check whether $bar or $baz are set inside the function because you just set them, all you need to worry about is if their value evaluates to true or false (or whatever else).
Regular variables anywhere:
$foo = null;
$bar = $baz = 'default value';
Initialize your variables at the top of a block of code in which you're going to use them. This solves the !isset problem, ensures that your variables always have a known default value, gives the reader an idea of what the following code will work on and thereby also serves as a sort of self-documentation.
Arrays:
$defaults = array('foo' => false, 'bar' => true, 'baz' => 'default value');
$values = array_merge($defaults, $incoming_array);
The same thing as above, you're initializing the array with default values and overwrite them with actual values.
In the remaining cases, let's say a template where you're outputting values that may or may not be set by a controller, you'll just have to check:
<table>
<?php if (!empty($foo) && is_array($foo)) : ?>
<?php foreach ($foo as $bar) : ?>
<tr>...</tr>
<?php endforeach; ?>
<?php else : ?>
<tr><td>No Foo!</td></tr>
<?php endif; ?>
</table>
If you find yourself regularly using array_key_exists, you should evaluate what you're using it for. The only time it makes a difference is here:
$array = array('key' => null);
isset($array['key']); // false
array_key_exists('key', $array); // true
As stated above though, if you're properly initializing your variables, you don't need to check if the key exists or not, because you know it does. If you're getting the array from an external source, the value will most likely not be null but '', 0, '0', false or something like it, i.e. a value you can evaluate with isset or empty, depending on your intent. If you regularly set an array key to null and want it to mean anything but false, i.e. if in the above example the differing results of isset and array_key_exists make a difference to your program logic, you should ask yourself why. The mere existence of a variable shouldn't be important, only its value should be of consequence. If the key is a true/false flag, then use true or false, not null. The only exception to this would be 3rd party libraries that want null to mean something, but since null is so hard to detect in PHP I have yet to find any library that does this.
Just write a function for that. Something like:
function get_string($array, $index, $default = null) {
if (isset($array[$index]) && strlen($value = trim($array[$index])) > 0) {
return get_magic_quotes_gpc() ? stripslashes($value) : $value;
} else {
return $default;
}
}
which you can use as
$username = get_string($_POST, 'username');
Do the same for trivial stuff like get_number(), get_boolean(), get_array() and so on.
I believe one of the best ways of coping with this problem is by accessing values of GET and POST (COOKIE, SESSION, etc.) arrays through a class.
Create a class for each of those arrays and declare __get and __set methods (overloading). __get accepts one argument which will be the name of a value. This method should check this value in the corresponding global array, either using isset() or empty() and return the value if it exists or null (or some other default value) otherwise.
After that you can confidently access array values in this manner: $POST->username and do any validation if needed without using any isset()s or empty()s. If username does not exist in the corresponding global array then null will be returned, so no warnings or notices will be generated.
I don't mind using the array_key_exists() function. In fact, I prefer using this specific function rather than relying on hack functions which may change their behavior in the future like empty and isset (strikedthrough to avoid susceptibilities).
I do however, use a simple function that comes handy in this, and some other situations in dealing with array indexes:
function Value($array, $key, $default = false)
{
if (is_array($array) === true)
{
settype($key, 'array');
foreach ($key as $value)
{
if (array_key_exists($value, $array) === false)
{
return $default;
}
$array = $array[$value];
}
return $array;
}
return $default;
}
Let's say you've the following arrays:
$arr1 = array
(
'xyz' => 'value'
);
$arr2 = array
(
'x' => array
(
'y' => array
(
'z' => 'value',
),
),
);
How do you get the "value" out of the arrays? Simple:
Value($arr1, 'xyz', 'returns this if the index does not exist');
Value($arr2, array('x', 'y', 'z'), 'returns this if the index does not exist');
We already have uni and multi-dimensional arrays covered, what else can we possibly do?
Take the following piece of code for instance:
$url = 'https://stackoverflow.com/questions/1960509';
$domain = parse_url($url);
if (is_array($domain) === true)
{
if (array_key_exists('host', $domain) === true)
{
$domain = $domain['host'];
}
else
{
$domain = 'N/A';
}
}
else
{
$domain = 'N/A';
}
Pretty boring isn't it? Here is another approach using the Value() function:
$url = 'https://stackoverflow.com/questions/1960509';
$domain = Value(parse_url($url), 'host', 'N/A');
As an additional example, take the RealIP() function for a test:
$ip = Value($_SERVER, 'HTTP_CLIENT_IP', Value($_SERVER, 'HTTP_X_FORWARDED_FOR', Value($_SERVER, 'REMOTE_ADDR')));
Neat, huh? ;)
Welcome to null coalescing operator (PHP >= 7.0.1):
$field = $_GET['field'] ?? null;
PHP says:
The null coalescing operator (??) has been added as syntactic sugar for the common case of needing to use a ternary in conjunction with isset(). It returns its first operand if it exists and is not NULL; otherwise it returns its second operand.
I'm here with you. But PHP designers has made a lot more worse mistakes than that. Short of defining a custom function for any value reading, there isn't any way around it.
I use these functions
function load(&$var) { return isset($var) ? $var : null; }
function POST($var) { return isset($_POST[$var]) ? $_POST[$var] : null; }
Examples
$y = load($x); // null, no notice
// this attitude is both readable and comfortable
if($login=POST("login") and $pass=POST("pass")) { // really =, not ==
// executes only if both login and pass were in POST
// stored in $login and $pass variables
$authorized = $login=="root" && md5($pass)=="f65b2a087755c68586568531ad8288b4";
}
Make a function which returns false if not set, and, if specified, false if empty. If valid it returns the variable. You can add more options as seen in the code below:
<?php
function isset_globals($method, $name, $option = "") {
if (isset($method[$name])) { // Check if such a variable
if ($option === "empty" && empty($method[$name])) { return false; } // Check if empty
if ($option === "stringLength" && strlen($method[$name])) { return strlen($method[$name]); } // Check length of string -- used when checking length of textareas
return ($method[$name]);
} else { return false; }
}
if (!isset_globals("$_post", "input_name", "empty")) {
echo "invalid";
} else {
/* You are safe to access the variable without worrying about errors! */
echo "you uploaded: " . $_POST["input_name"];
}
?>
Software does not magically run by the grace of god. If you are expecting something that is missing, you need to properly handle it.
If you ignore it, you are probably creating security holes in your applications. In static languages accessing a non-defined variable it is just not possible. It won't simply compile or crash your application if it's null.
Furthermore, it makes your application unmaintainable, and you are going to go mad when unexpected things happen. Language strictness is a must and PHP, by design, is wrong in so many aspects. It will make you a bad programmer if you are not aware.
I'm not sure what your definition of readability is, but proper use of empty(), isset() and try/throw/catch blocks, is pretty important to the whole process.
If your E_NOTICE is coming from $_GET or $_POST, then they should be checked against empty() right along with all the other security checks that that data should have to pass.
If it's coming from external feeds or libraries, it should be wrapped in try/catch.
If it's coming from the database, $db_num_rows() or its equivalent should be checked.
If it's coming from internal variables, they should be properly initialized. Often, these types of notices come from assigning a new variable to the return of a function that returns FALSE on a failure. Those should be wrapped in a test that, in the event of a failure, can either assign the variable an acceptable default value that the code can handle, or throwing an exception that the code can handle.
These things make the code longer, add extra blocks, and add extra tests, but I disagree with you in that I think they most definitely add extra value.
What about using the # operator?
For example:
if(#$foo) { /* Do something */ }
You may say this is bad because you have no control of what happens "inside" $foo (if it was a function call that contains a PHP error for example), but if you only use this technique for variables, this is equivalent to:
if(isset($foo) && $foo) { /* ... */ }

Categories