How to avoid this sort of code bloat with PHP 8+ Warning for missing array keys - php

With PHP 8 The concept of an unfound array key was promoted from a NOTICE error level to a WARNING error level.
This causes us huge code and error_log bloats where we turn off notices and retain warnings on error logs which end up being filled with repetative warnings about array key existance.
For example; Some code to check if a SESSION value exists and if so then use it in some local code. SESSION value is set in some other part of the website and is dynamically set.
<?php
// This code is server side action example
include whatever_header.php //sets session start etc.
if($_SESSION['action'] === "submit"){
...
}
$localPageDataArray = $_SESSION['core']['data']?:['default' => 'no'];
Previous to PHP 8, if $_SESSION action was not set it didn't matter, because the if condition only executed if it was set to a specific value. To be clear; if $_SESSION['action'] was never set then the code would work in exactly the same way as if $_SESSION['action'] === "sleep" , ie various conditionals would still fire or not fire accordingly.
NOW, the error logs blow up with various warnings such as:
PHP Warning: Undefined array key "action" in /home/public_html/session_test_script.php on line 5
PHP Warning: Undefined array key "data" in /home/public_html/session_test_script.php on line 9
There are a couple of issues with this warning feedback:
It blows up and hugely inflates the error logs
It causes huge code bloat to workaround this issue:
Example of code bloat being to resolve the above warning would be:
<?php
// This code is server side action example
include whatever_header.php //sets session start etc.
if(array_key_exists('action',$_SESSION) && $_SESSION['action'] === "submit"){
...
}
Or even worse:
<?php
// This code is server side action example
include whatever_header.php //sets session start etc.
if(array_key_exists('action',$_SESSION) && $_SESSION['action'] === "submit"){
...
}
$localPageDataArray = ['default' => 'no']; //default value if below IF is false
if(array_key_exists($_SESSION,'core') && is_array($_SESSION['core'])
&& array_key_exists($_SESSION['core'],'data') {
$localPageDataArray = $_SESSION['core']['data']?:['default' => 'no'];
}
This is a huge bloat on the code for absolutely zero benefit aside from removing tedious warning messages from the error logs. Because the issue has been promoted to WARNING level then we can't realistically turn of WARNING logs as that's a bad idea for genuine warnings.
While we can slightly streamline the above to being an isset check thus:
if(isset($_SESSION['core']['data']) && is_array($_SESSION['core']['data'])) {
$localPageDataArray = $_SESSION['core']['data']?:['default' => 'no'];
}
This still greatly inflates the code size for zero practical improvment.
Question
Are there any workarounds to how to easily and efficiently avoid these Warning error log messages without having to deal with multiple lines of hard coding checking processes?
Note that these issues are due to $_SESSION (and sometimes other superglobal data) being dynamically generated so we can't always ensure that a value exists and that's built into the default checking mechanisms as illustrated here.
If we can do the above with one line, this would be a great compromise:
$localPageDataArray = isset($_SESSION['core']['data'])?$_SESSION['core']['data']:['default' => 'no'];
BUT This only sets the default value (['default' => 'no']) if the $_SESSION['core']['data'] is not set, rather than if it is set and is empty
Not a Dupe
This question is not a dupe because I'm not caring about speed (although it's nice isset is so fast) but asking about how to avoid the error_log Warning messages found in PHP 8+ which are a different issue to speed.

With the help of Cid a minimal coded solution would be to use ?? Null coalescing operator and/or isset to check values are set without getting strung up on intermediate array keys in multidimensional arrays.
e.g:
if(isset($_SESSION['action']) && $_SESSION['action'] === "submit"){
...
}
$localPageDataArray = $_SESSION['core']['data']??['default' => 'no'];
Neither of these tests will now produce Warnings about undefined array keys

Related

No E_NOTICE for undefined variables in array?

So.. I'm still confused by this, when creating an array with $array = array(); and then manually setting variables like:
<?php
$array[] = 1;
$array['type'] = 2;
$array['number'] = 3;
I know, this is OK for PHP to do, but then when I echo something like $array['none'] it won't show a E_NOTICE for undefined variables.
Can someone explain me, why?
It will. If you have turned on error reporting, it should display a warning similar to the one below:
Notice: Undefined index: none in /path/to/script.php line X.
To check, try the following:
<?php
ini_set('display_errors',1);
error_reporting(E_ALL);
$array = array();
echo $array['none'];
And, if you want to actually make sure they exist before trying to use them in your code, use isset():
if(isset($array['none'])) {
// do stuff ...
}
See it live!
All this is explained on the config doc page, too:
Enabling E_NOTICE during development has some benefits.
For debugging purposes: NOTICE messages will warn you about possible bugs in your code. For example, use of unassigned values is warned. It is extremely useful to find typos and to save time for debugging.
NOTICE messages will warn you about bad style. For example, $arr[item] is better to be written as $arr['item'] since PHP tries to treat "item" as constant. If it is not a constant, PHP assumes it is a string index for the array.
It's quite simple: When you access a key that doesn't exist, and assign it a new value, PHP will create that key, and add it to the list (or array). But when you try to access a non-existing key, and attempt to echo it's value, PHP won't crash, but it'll let you know that your code contains a possible bug:
$arr = array('foo' => 'bar');
echo $arr['fo'];
This issues a notice because my code may contain a typo. I may expect the key fo to exist, while clearly it doesn't, so I need to work on my code some more.
Another reason why this notice is issued is because lookups for non existing properties/keys are "slow". In order for PHP to know for a fact that the key doesn't exist, the entire array has to be scanned. That, too, is not ideal, though inevitable at times. If you have code that issues tons of E_NOTICE's, chances are that some simple if's, like:
if (!isset($arr['fo']))
{
$arr['fo'] = '';
}
echo $arr['fo'];
Will, though adding more code, effectively speed up your code. Not in the least because issueing notices isn't free (it's not that expensive, but not free either).
Other benefits:
Notices also let you know when you forgot to quote array keys, for example
echo $arr[foo];
echo $arr['foo'];
Initially, both will echo bar, but let's add 1 line of code to this:
define('foo', 'bar');
echo $arr[foo];
echo $arr['foo'];
This won't, because foo is now a constant, so $arr[foo] amounts to $arr['bar'];, which is an undefined index. Turning off notices, will just echo the string representation of NULL, which is an empty string.
Basically, notices help you. Use them, listen to them, and fix them. If your site is broken, fix it. If you get into the habbit of ignoring these notices, you'll probably set your ini files to a more "forgiving" setting, and grow lazy.
As time progresses, your code will become ever more messy/smelly, until such time you actually have a difficult to trace bug. You'll decide to turn your error reporting to E_STRICT | E_ALL, and won't be able to see the actual notice/warning that points out where your bug actually is, because your screen will be cluttered with E_NOTICE undefined index/variable...

PHP: with an associative array of counters, is it more idiomatic to explicitly initialize each value to 0 before the first increment?

I'm writing some code that builds up associative array of counters. When it encounters a new item for the first time it creates a new key and initializes it to zero. I.e.:
if (!array_key_exists($item, $counters)) {
$counters[$item] = 0;
}
$counters[$item]++;
However, PHP actually does that first part implicitly. If I just do...
$counters[$item]++;
... then $counters[$item] will evaluate to NULL and be converted to 0 before it's incremented. Obviously the second way is simpler and more concise, but it feels a little sleazy because it's not obvious that $counters[$item] might not exist yet. Is one way or the other preferred in PHP?
For comparison, in Python the idiomatic approach would be to use collections.Counter when you want keys that initialize themselves to 0, and a regular dictionary when you want to initialize them yourself. In PHP you only have the first option.
Incrementing an uninitialized key will generate a PHP Notice, and is a bad idea. You should always initialize first.
However, the use of array_key_exists is not very idiomatic. I know coming from Python it may seem natural, but if you know that $counter has no meaningful NULL values it's more idiomatic to use isset() to test for array membership. (It's also much faster for no reason I can discern!)
This is how I would write a counter in PHP:
$counters = array();
foreach ($thingtobecounted as $item) {
if (isset($counters[$item])) {
$counters[$item]++;
} else {
$counters[$item] = 1;
}
}
Unfortunately unlike Python PHP does not provide any way to do this without performing two key lookups.
the first is preferred. the second option will generate a Notice in your logs that $counters[$item] is undefined. it still works but if you change display_errors = On; and error_reporting = E_ALL. in your php.ini file you will see these notices in your browser.
The first way is generally how you do it, if for nothing other than simpler maintenance. Remember, you may not be the one maintaining the code. You don't want error logs riddled with correctly operating code. Even worse, you may need to transfer methods to other languages (or earlier versions of PHP) where implicit initialization might not occur.
If you don't really need a check on each array index - or know that most of the indexes will be undefinded - why not suppress errors like: ?
(this way you save some performance on initializing [useless] indexes)
if (#!array_key_exists($item, $counters)) {

PHP "Notice: Undefined index" is it harmless or a real error?

While my site was working without any problem I suddenly started to have a really high CPU usage on my server so I started to check the code more carefully and enabled E_ALL error reporting.
Then I found out I had a great many of this "notices":
Notice: Undefined index: userID in /var/www/vhosts/mydomain.com/httpdocs/header.php on line 8
Most or them refer to unset cookies, for example this:
$uid = $_COOKIE['userID'];
If the user is unlogged I get a notice right there, and every time I use $uid.
What I want to know if this: Are this notices harmless or can they really cause any problems in my site? (Speed issues, errors etc.)
It is a notice only, try this code:
$uid = isset($_COOKIE['userID']) ? $_COOKIE['userID'] : 0;
It is not hamless (depending on the point of view), and you can disable this with error reporting functions, otherwise, the correct way is verify if index exists isset($_COOKIE['userID']) and if not, define a default value (null for instance)
$var = isset($foo) ? $foo : 'default';
You need to verify if variable exists, if you don't known it exists or not.
$var = 'foo'
if($var == 'foo') { // I known $var is defined, because I have defined it.
[..]
}
/**
* Above, I don't known if user go to mywebsite.com/index.php or
* mywebsite.com/index.php?foo=bar, so, I need to verify if index is defined
*/
if(isset($_GET['foo']) && $_GET['foo'] == 'bar') {
[...]
}
Those notices will cause a little bit of a speed problem, because raising a notice costs some extra effort.
The main problem though is that this is a serious error. You are trying to work with something that doesn't exist. This may or may not lead to Bad Things Happening, but it means your program is not correct. Since you should always develop with error reporting on full power to see and solve actual problems, notices about undefined indexes or undefined variables are serious and need to be solved. Anything that PHP complains about is serious and needs to be solved. See The Definitive Guide To PHP's isset And empty.
Notices are in general harmless, yet they may indicate a poor application design. In general it is always a good idea to utilize available PHP tools (i.e isset($someVar)) to make sure that your business logic is taking proper care of variable initialization. When you see no such notices with E_ALL error reporting setting, it's always better.
The Notice warning is in the first point of view harmless, but you should keep in mind, that the programming is not right and it might be causes errors on following lines.
In your example it es better to use
$uid = isset($_COOKIE['userID'])?$_COOKIE['userID']:0;
So you can be sure, that $uid always has a value and when the value greater then 0 you have a falid userId ...

Undefined index: Error in php script

In a php page I have following code:
if($_REQUEST['c']!="") // I get error on this line itself. Why?
{
$pidis=(int)($_REQUEST['c']);
}
I keep getting Undefined index error.
On Googling I manage to understand that if a page is access without parameters (in URL) which we are trying to access we can get this error/warning. I believe that if a parameter is not defined in the URL it should just return empty instead of giving error/warning message.
I know that it is possible to suppress errors and warning by adding
error_reporting(E_ALL ^ E_NOTICE);
But I do not want to do this.
This same page work just fine on our company's web server but does not work on our clients web server.
Why is this happening?
How to solve this problem?
You are getting that error because you are attempting to compare $_REQUEST['c'] to something when $_REQUEST['c'] does not exist.
The solution is to use isset() before comparing it. This will remove the warning, since the comparison won't happen if $_REQUEST['c'] doesn't exist.
if(isset($_REQUEST['c']) && $_REQUEST['c']!="")
{
$pidis=(int)($_REQUEST['c']);
}
It is an E_NOTICE level error, and your level of error reporting will affect whether the error shows up or not. Your client's server has E_NOTICE level error reporting turned on, which is why it shows up there.
It is a good idea to always develop using E_ALL so that you can catch this kind of error before moving your code to other servers.
Another solution is to use the following:
$pidis = isset($_REQUEST['c']) ? $_REQUEST['c'] : '';
You can also, if you prefer to return a value other than empty, by placing a default value within the final set of single quotes, e.g.
$pidis = isset($_REQUEST['c']) ? $_REQUEST['c'] : 'Default Value';
or return a different variable type, for instance an integer:
$pidis = isset($_REQUEST['c']) ? $_REQUEST['c'] : 34;
Instead of isset() you can also use: array_key_exists().
The difference between both methods is that isset() checks also whether the value of the variable is null. If it is null then isset returns false whereas array_key_exists() returns always true if the key exists (no mater which value). E.g.:
$array = array('c' => null);
var_dump(isset($array['c']))); // isset() returns FALSE here
var_dump(array_key_exists($array['c']); //array_key_exists() returns TRUE
Depending on the context, it is important to distinguish this. In your case I don't think it matters doesn't matter, as (I guess) a request parameter never will be null (except one overwrites it manually).
Use isset($_REQUEST['c']) to test if it exists first.
PHP is giving a notice (which is not an error : it's just a notice) when you are trying to use a variable that doesn't exists, or an array element that doesn't exist.
This is just to help you, and you should not mask those notices : they are here to help you -- for instance, to help you detect typos in variable names.
Before using that array index, if it's not always present, you should test if it's here, using isset :
if (isset($_REQUEST['c']) && $_REQUEST['c']!="") {
// ...
}
Clean way could be :
$pidis = $_REQUEST['c'] ?? null
this is same as checking isset request but shorter.

Notice: Undefined index: XXX - Does it really matter?

Since i changed my error reporting level to error_reporting(E_ALL | E_STRICT); I am facing this error. I can obviate from this error using isset() but the code looks so ugly!
So my question is: What if I go back to my normal settings of error reporting? does it really matter to know that something is not already defined? because it woks properly without the Notice error.
Because i have +10 inputs and i get them like that:
$username = $_POST['username'];
I also tried to pre-define the variables using this in the top on the file.
$username = null; and $username = 0; but they don't work.
Thanks.
It does matter. Errors slow down PHP and you really should design you application not to throw errors. Many other languages will completely die in situations where PHP happily continues script execution.
When developing, your script should not throw any errors (even an E_NOTICE).
I would suggest creating a simple function to grab the $_POST values and do the checking for you.
e.g.
<?php
function getPost($key)
{
return isset($_POST[$key]) ? $_POST[$key] : null;
}
Edit:
Apparently it wasn't clear to the OP how to use this:
$username = getPost('username');
It means there is no key 'username' in the POST array.
Generally, it is a good idea to check and correct these things, as they may ripple to other parts in your application that do depend on the missing value.
It does matter -- when I get strange behaviour in a php application the error log is the first place I look and nine times out of ten an "UNDEFINED INDEX" message leads me straight to the root cause.
Notices do have a purpose: they're a tool to detect potential errors in your code. If you write code that triggers notices for trivial operations and you are not willing to change it, you'll have to disable notice reporting and thus reject a helpful tool on purpose and make your work harder than needed.
Historically, PHP was designed with extreme simplicity in mind (in old versions you'd just have an $username available with zero lines of code) but this approach proved highly inadequate as the web evolved: it only lead to code that was insecure and hard to maintain.
All errors should be addressed, no matter the level, for portability.
If you build your application not addressing strict errors, and your application is deployed on a server that does have strict error reporting, your application is going to fall over pretty quickly.
Your best bet is to check the existence of $_POST['username'] and then act independently on that return value. Using isset() your return value with either be true or false.
I'm guessing $_POST['username'] is for use in an authentication system of some description? Therefore, if your isset() function returns false you could then display an error detailing to the user that username is required.

Categories