Suppress notice when passed array is a key and not array - php

I have a simple function which should check if the passed in parameter is an array and has values. The function works perfectly except in the event when I pass a multi-dimensional array as the property. I suspect the isset() passes but because the key may possibly not exist it gives an undefined:
<?php
$array1 = array("John","Doe");
$array2 = array();
function valid_array($array) {
if (is_array($array) && count($array) > 0) {
return true;
}
return false;
}
// Below works great:
valid_array($array1);
// If I pass the following I get the notice
// Notice: Undefined index: sample_key in ....:
valid_array($array2['sample_key']);
?>
Any ideas?

That means, there is no array item as sample_key as its key. Use isset() to verify that, don't suppress it.
if(isset($array2['sample_key']))
valid_array($array2['sample_key']);
Such cases are called Exceptions catch them and throw an error instead.
Here is a way to raise an error upon use.
function valid_array(array $array) {
// ^ Asks for a generic array to be passed
if (is_array($array) && count($array) > 0) {
return true;
}
return false;
}

The easiest way to suppress an error is by using the '#' symbol before the function is called:
Example: #my_custom_function();
This will suppress any errors your function might churn out (but it will not fix them)

Related

Shorthand for checking if associative array not null and if key exists

Is there any way of checking if a variable is not null and then to check if variable has nested associative array keys using a shorthand? Something like optional chaining for associative arrays?
Example of what I would like to make concise:
public $arr;
//$arr gets set as an associative array somewhere else in the code.
function someFunc() {
if ($this->arr && $this->arr['key1'] && $this->arr['key1']['key2'] == 'Some Value') { // shorten this line?
// Do Something Cool!
}
}
I am looking for something similar to Optional Chaining in Javascript e.g. :
if (obj.key1?.key2 == 'Some Value') {
// Do something kool
}
There is a high probability that this is a duplicate and I apologize in advance if that is the case. I tried searching for this for associative arrays and could not find anything specific.
You could keep the if statement intact, but replace $this->arr && $this->arr['key1'] with an Null Coalescing Operator (??), so if those aren't defined, it will use the fallback, that isn't equal to the test string:
if (($this->arr['key1']['key2'] ?? false) == 'Some Value') {
// Do Something Cool!
}
So if $this->arr['key1']['key2'] is defined, you'll compare that to Some Value, otherwise, if it's not defined, you'll compare (eg) false to Some Value witch will remain false.
Use the php function isset: https://www.php.net/manual/en/function.isset.php
It will check for multiple keys: if( isset($this->arr['key1']['key2'] ) {...} and also includes a null check.
If you for some reason need to shorten it even more, simply decompose it to another function:
public $arr;
//$arr gets set as an associative array somewhere else in the code.
function someFunc()
{
if ($this->checkForSomeValue($this->arr, 'Some Value', 'key1', 'key2')) { // shorten this line?
// Do Something Cool!
}
}
function checkForSomeValue(array $arr, string $valueToCheck, ...$keys)
{
$valueCompare = $arr;
foreach($keys as $key)
{
if(!isset($valueCompare[$key]))
{
$valueCompare = null;
break;
}
else
{
$valueCompare = $valueCompare[$key];
}
}
return $valueCompare && $valueCompare === $valueToCheck;
}
I've updated the answer to allow supplying keys

php: write isset function which returns value or null

I have the following code in numerous places (thousands of places) around my project:
$foo = isset($mixed) ? $mixed : null;
Where $mixed can be anything: array, array element, object, object property, scalar, etc. For example:
$foo = isset($array['element']) ? $array['element'] : null;
$foo = isset($nestedArray['element']['key']) ? $nestedArray['element']['key'] : null;
$foo = isset($object->prop) ? $object->prop : null;
$foo = isset($object->chain->of->props) ? $object->chain->of->props : null;
Is there a way to write this repeated logic as a (simple) function? For example, I tried:
function myIsset($mixed)
{
return isset($mixed) ? $mixed : null;
}
The above function looks like it would work, but it does not in practice. For example, if $object->prop does not exist, and I call myIsset($object->prop)), then I get fatal error: Undefined property: Object::$prop before the function has even been called.
Any ideas on how I would write such a function? Is it even possible?
I realize some solutions were posted here and here, but those solutions are for arrays only.
PHP 7 has a new "Null coalescing operator" which does exactly this. It is a double ?? such as:
$foo = $mixed ?? null;
See http://php.net/manual/en/migration70.new-features.php
I stumbled across the answer to my own question while reading about php references. My solution is as follows:
function issetValueNull(&$mixed)
{
return (isset($mixed)) ? $mixed : null;
}
Calls to this function now look like:
$foo = issetValueNull($array['element']);
$foo = issetValueNull($nestedArray['element']['key']);
$foo = issetValueNull($object->prop);
$foo = issetValueNull($object->chain->of->props);
Hopefully this helps anyone out there looking for a similar solution.
isset is a language construct, not a regular function. Therefore, it can take what would otherwise cause an error, and just return false.
When you call myIsset($object->prop)), the evaluation occurs and you get the error.
See http://php.net/manual/en/function.isset.php
This is the same problem as using typeof nonExistentVariable in JavaScript. typeof is a language construct and will not cause an error.
However, if you try to create a function, you get an error for trying to use an undefined variable.
function isDef(val) {
return typeof val !== 'undefined';
}
console.log( typeof nonExistent !== 'undefined'); // This is OK, returns false
isDef(nonExistent); // Error nonExistent is not defined
You could actually just write it like:
$foo = $mixed?:null;
If you just want to check if it exist do this
function myIsset($mixed)
{
return isset($mixed); // this is a boolean so it will return true or false
}
function f(&$v)
{
$r = null;
if (isset($v)) {
$r = $v;
}
return $r;
}

isset() and empty() do not seem to fix the "undefined index" notice

The company keeps their PHP errors suppressed in code, but I turned it on to see why something of mine was not working.
Now, however, I am getting hundreds of Undefined index messages that I would like to turn off so that I can find the messages from MY code.
Here is one particular block that gives many errors:
final public function getAttribute($name) {
$value = '';
if(is_array($this->attributes[$name]) === false) { // Notice: Undefined index: name
$value = trim($this->attributes[$name]);
} else {
$value = trim(implode(' ', $this->attributes[$name]));
}
return $value;
}
To eliminate these notices, I followed the post Why check both isset() and !empty() to write it like this:
final public function getAttribute($name='') {
if (!isset($name) || empty($name)) {
return '';
}
$value = '';
if(is_array($this->attributes[$name]) === false) { // Notice: Undefined index: name
$value = trim($this->attributes[$name]);
} else {
$value = trim(implode(' ', $this->attributes[$name]));
}
return $value;
}
I still get the notices, and in the same place.
How do I fix the code so that it does not create this condition?
You are not using the right variable to check against. You need to check for whether the index of the array exists.
final public function getAttribute($name='') {
if (!isset($this->attributes[$name]) || empty($this->attributes[$name])) {
return '';
}
// ...
}
try isset($this->attributes[$name]) in your second block
This will not throw undefined index:
[...]
if (isset($this->attributes[$name])) {
// The key $name is set in $this->attributes
}
else {
// The key is not set
}
if (!isset($this->attributes[$name])) {
return NULL;
}
The above code should do it.
Use empty first, because isset will throw undefined error if the index doesn't exist, and empty will never do, like:
if (empty($name) && !isset($name)) {
return '';
}
Using AND here will prevent returning if the value is already set but equals to '', o or false.
If you want to return if the value is true, some string or value, just use empty. Not need to use isset.

How to write a function that checks if a variable is defined and is a number?

In the past when I needed to check if a variable was set and also a number, I would do:
if( isset($_GET['var']) && is_numeric($_GET['var']) )
But I think that's kind of ugly, especially when I need to check a bunch of variables in the same if statement, so I made a function:
function setAndNum($var)
{
if(isset($var) && is_numeric($var))
return 1;
else
return 0;
}
The problem is that when I pass an undefined variable to the function, like this (supposing the variable in the GET array is undefined):
if( setAndNum($_GET['var']) )
I get the php error:
Notice: Undefined index: ...
So the whole purpose of the function is basically defeated (or half the purpose, at least ;) ).
One thing that confuses me is how the isset() function works, and why I can pass an undefined variable to it but not to my own function?
Is it possible to make my setAndNum() function work?
Your problem is with $_GET being an array. When you pass $_GET['var'] to your function, this array value is already looked up and used as an argument to the function. Therefore you cannot effectively check the presence of 'var' in $_GET from within this function. You could rewrite it a bit to make it work for array values, something like this:
function setAndNum($key, $array)
{
if(array_key_exists($key, $array) && is_numeric($array[$key]))
return 1;
else
return 0;
}
Then call it like this:
if( setAndNum('var', $_GET) )
It's good practice to verify a key exists before using it:
if (array_key_exists($_GET, 'var')) {
// do stuff with $_GET['var']
}
function setAndNum(&$var)
{
if(isset($var) && is_numeric($var))
return 1;
else
return 0;
}
Please, try using this version:
function setAndNum(&$var)
{
if(isset($var) && is_numeric($var))
return 1;
else
return 0;
}
You can use the # operator to prevent error reporting:
setAndNum(#$_GET['var']);
This way, the error message of the non-existant index will not be printed, and the return value will be 0.
You could also write two functions, one that checks for an array and one that checks for normal variable
function setAndNum($var)
{
if(isset($var) && is_numeric($var))
return 1;
else
return 0;
}
function setAndNumArray($array, $key)
{
if(isset($array) && isset($array[$key]) && is_numeric($array[$key]))
return 1;
else
return 0;
}
if you are using variables from GET or POST method you may do like this as these are super globals.
function setAndNum()
{
if(isset($_GET['var']) && is_numeric($_GET['var']))
return 1;
else
return 0;
}
now coming to your another query. isset checks whether a variable is s et or not like
if(isset($_POST['submit']))
{
// any code under button click
}

safely get array element value for defined and undefined indexes

I'm getting tired of writing ternary expressions to sanitize the data, things like:
$x = isset($array['idx']) ? $array['idx'] : null;
// and
$x = !empty($array['idx']) ? $array['idx'] : null;
Is there a native way or ZF accessor/filter to get array element value for some given array without:
disabling error_reporting
ternary isset/empty check
error control operator #
creating my own global function or application accessor/filter
Something like:
$x = get_if_set($array['idx']);
// or
$x = Zend_XXX($array, 'idx')
PHP7 introduced the null coalesce operator ??. Assuming you're lucky enough to be running it, you can just do
$x = $array['idx'] ?? null;
Since PHP 7.0
Things are much more easy - thanks to Andrea Faulds and Nikita Popov for - the Null Coalescing Operator ??Docs, Migration, RFC:
$x = $array['idx'] ?? NULL;
or with a $default:
$x = $array['idx'] ?? $default ?? NULL;
Like isset or empty it gives no warning and the expression falls through to the right (if unset). if set and not NULL the value is taken. This is also how $default in the previous example works, always, even if undefined.
Since PHP 7.4
Thanks to Midori Kocak - is the Null Coalescing Assignment Operator ??=Docs, RFC (which was one of the things I missed after ??) allows to assign default values directly:
$array['idx'] ??= null;
$x = $array['idx'];
I don't use it by far as often as ??, but it is good to know about it, especially if while break up data-handling logic and want to have defaults early.
Original old answer
As far as you only need NULL as "default" value, you can make use of the error suppression operator:
$x = #$array['idx'];
Criticism: Using the error suppression operator has some downsides. First it uses the error suppression operator, so you can not easily recover issues if that part of the code has some. Additionally the standard error situation if undefined does pollute looking for screams. Your code is not as expressing itself as precise as it could be. Another potential issue is to make use of an invalid index value, e.g. injecting objects for indexes etc.. This would get unnoticed.
It will prevent warnings. However if you like to allow other default values as well, you can encapsulate the access to an array offset via the ArrayAccess interface:
class PigArray implements ArrayAccess
{
private $array;
private $default;
public function __construct(array $array, $default = NULL)
{
$this->array = $array;
$this->default = $default;
}
public function offsetExists($offset)
{
return isset($this->array[$offset]);
}
public function offsetGet($offset)
{
return isset($this->array[$offset])
? $this->array[$offset]
: $this->default
;
}
public function offsetSet($offset, $value)
{
$this->array[$offset] = $value;
}
public function offsetUnset($offset)
{
unset($this->array[$offset]);
}
}
Usage:
$array = array_fill_keys(range('A', 'C'), 'value');
$array = new PigArray($array, 'default');
$a = $array['A']; # string(13) "value"
$idx = $array['IDX']; # NULL "default"
var_dump($a, $idx);
Demo: https://eval.in/80896
I would say, use a helper function to interface with your array.
function getValue($key, $arr, $default=null) {
$pieces = explode('.', $key);
$array = $arr;
foreach($pieces as $array_key) {
if(!is_null($array) && is_array($array) && array_key_exists($array_key, $array)) {
$array = $array[$array_key];
}
else {
$array = null;
break;
}
}
return is_null($array) ? $default : $array;
}
$testarr = [
['foobar' => 'baz'],
['active' => false]
];
$output = getValue('0.foobar',$testarr,'NOT FOUND');
var_dump($output);
$output = getValue('0',$testarr,'NOT FOUND');
var_dump($output);
$output = getValue('1.active',$testarr,'NOT FOUND');
var_dump($output);
$output = getValue('i.do.not.exist',$testarr,'NOT FOUND');
var_dump($output);
This way you can provide a default value instead of null to your liking, you don't need to reinstate the array as another object and you can request any value as deeply nested as you wish without having to check the "parent" arrays.
https://ideone.com/11jtzj
Declare your variables and give them some initial value.
$x = NULL;
$y = 'something other than NULL';
now if you have an array $myArray that has x and y keys the extract function will overwrite the initial values (you can also configure it not to)
$myArray['x'] = 'newX';
extract($myArray);
//$x is now newX
if no keys are present the initial values for variables remain. It will also put other array keys in respective variables.

Categories