This question already has answers here:
Notice: Undefined index when trying to increment an associative array in PHP
(6 answers)
Closed 4 months ago.
I want to increment a value of an array, which is potentially not existing yet.
$array = [];
$array['nonExistentYet']++; // Notice
Problem
This leads to a NOTICE.
Attempt
I found a way to do this, but its kinda clunky:
$array = [];
$array['nonExistentYet'] = ($array['nonExistentYet'] ?? 0) + 1;
Question
Is there a more human readable/elegant way to do this?
well i guess a more readable way would be to use if..else as,
$arr = [];
if(array_key_exists('nonExistentYet', $arr)) {
$arr['nonExistentYet'] += 1;
}
else {
$arr['nonExistentYet'] = 1;
}
If this is used often, you can define a little helper method, which also uses an interesting side effect...
function inc(&$element) {
$element++;
}
$array = [];
inc($array['nonExistentYet']);
print_r($array);
gives...
Array
(
[nonExistentYet] => 1
)
with no warning.
As you can see the function defines the parameter as &$element, if this value doesn't exist, then it will be created, so the function call itself will create the element and then it will just increment it.
My standard implementation for this is:
if (isset($array['nonExistentYet']))
$array['nonExistentYet']++;
else
$array['nonExistentYet'] = 1;
But this is one of the rarely scenarios where I use the # operator to suppress warnings, but only if I have full control over the array:
#$array['nonExistentYet']++;
Generally, it is not good to suppress warnings or error messages!
What you ask is a little vague,
Either the variable exists and you increment it, or it does not exist in this case you create it.
In another case suppose that you want to do it in a for loop, in this case you do not have to worry about the existence of the variable.
One way is ternary operator, which checks if array value exists:
$array['iDoNotExistYet'] = empty($array['iDoNotExistYet']) ? 1 : ++$array['iDoNotExistYet'];
Other one would be just rewriting it to if and else condition.
Related
I'm used to JavaScript where I often write things like this: const ref = obj.arr || (obj.arr = []);.
With one line of code, this gives me a reference on obj.arr after having initialized it to empty array if it didn't exist.
I'm trying to do the same in PHP but I'm struggling with ampersands, since the assignation is done by copy of value by default. Here is my failing attempt which I thought it would work:
$ref= &$obj['arr'] ?? &($obj['arr'] = []);
If I understand your question correctly, a ternary operator should suffice:
$obj['arr'] = isset($obj['arr']) ? $obj['arr'] : [];
$ref = &$obj['arr']);
This question already has answers here:
Notice: Undefined index when trying to increment an associative array in PHP
(6 answers)
Closed 4 months ago.
I have a foreach loop, inside of which I am building a big multi-dimensional array and doing lots of incriminating, like this:
$totalCosts['sawn']['materials'] += $sawnMaterialsCost;
On the first iteration of the loop, the key 'materials' is not set, so it throws an undefined index notice (not terrible, but annoying).
I can fix that by defining it before the loop like this:
$totalCosts['sawn']['materials'] = '0.00';
BUT, I have many keys I am filling by incrementing, and I don't like having to set each variable/key to '0' for every one before looping. Is there a better way to do this so the first iteration of the loop checks for a value and sets it to 1 if not found?
$totalCosts['sawn']['materials'] = ($totalCosts['sawn']['materials'] ?? 0) + $sawnMaterialsCost;
??, an operator introduced in PHP 7, uses the left-hand operand if defined and not null, otherwise the right-hand operand.
The performance cost of ?? is negligible, unless you are doing millions of comparisons. On an Amazon c3.medium, I measured each at ~250ns more than a static assignment. On a loop of 10,000,000 that's a half-second penalty.
perf.code.
Yes - first check if it exists, and if not, create it.
foreach($something as $key) { //e.g. $key = 'materials'
if (!isset($totalCosts['sawn'][$key])) $totalCosts['sawn'][$key] = 0;
$totalCosts['sawn'][$key] += $sawnMaterialsCost;
}
Well, I don't think there is a solution way more compact than this:
foreach(...) {
if(!isset($totalCosts['sawn'][$yourkey]))
$totalCosts['sawn'][$yourkey] = 0.00;
$totalCosts['sawn'][$yourkey]+=$sawnMaterialsCost;
}
Using the ternary operator is one way you can accomplish this.
$totalCosts['sawn']['materials'] = !empty($totalCosts['sawn']['materials']) ? $totalCosts['sawn']['materials'] + $sawnMaterialsCost : $sawnMaterialsCost;
This way you won't try adding to a non-existent value.
$arr[0]=123;
$a="arr[0]";
echo $$a;
gives me error
Notice: Undefined variable: arr[0]
on the last line.
What should I do to make it work?
EDIT:
Above is the simplification of what I want to do. If someone wants to know why I want to do this, then here's the explanation:
This is something like what I want to do:
if(condition){
$a=$arr1[0][0];
$b=$arr1[0][1];
$c=$arr1[0][2];
}
else{
$a=$arr2[0];
$b=$arr2[1];
$c=$arr2[2];
}
I can compact it like this:
if(condition)
$arr=$arr1[0];
else
$arr=$arr2;
$a=$arr[0];
$a=$arr[1];
$a=$arr[2];
But I wanted to try doing this using variable variable:
if(condition)
$arr="$arr1[0]";
else
$arr="$arr2";
$a={$$arr}[0];
$b={$$arr}[1];
$c={$$arr}[2];
Sure, we don't need variable variables as we can still code without them. I want to know, for learning PHP, why the code won't work.
Now that you said what you’re actually trying to accomplish: Your code doesn’t work because if you look at $arr1[0][0], only arr is the variable name; the [0] are special accessors for certain types like strings or arrays.
With variable variables you can only specify the name but not any accessor or other operation:
A variable variable takes the value of a variable and treats that as the name of a variable.
Your solution with the additional variable holding the array to access later on would be the best solution to your problem.
What you are trying to do just won't work - the code $arr[0] is referencing a variable called $arr, and then applying the array-access operator ([$key]) to get the element with key 0. There is no variable called $arr[0], so you cannot reference it with variable-variables any more than you could the expression $foo + 1 .
The real question is why you want to do this; variable variables are generally a sign of very messy code, and probably some poor choices of data structure. For instance, if you need to select one of a set of variables based on some input, you probably want a hash, and to look up an item using $hash[$item] or similar. If you need something more complex, a switch statement can often cover the cases you actually need.
If for some reason you really need to allow an arbitrary expression like $arr[0] as input and evaluate it at runtime, you could use eval(), but be very very careful of where the input is coming from, as this can be a very easy way of introducing security holes into your code.
FROM PHP DOC
In order to use variable variables with arrays, you have to resolve an ambiguity problem. That is, if you write $$a[1] then the parser needs to know if you meant to use $a[1] as a variable, or if you wanted $$a as the variable and then the [1] index from that variable. The syntax for resolving this ambiguity is: ${$a[1]} for the first case and ${$a}[1] for the second.
Use
echo ${$a}[0]; // 123
Edit : Based on your edit you can simply have
list($a, $b, $c) = (condition) ? $arr1[0] : $arr2;
Or
$array = (condition) ? $arr1[0] : $arr2;
$a = $array[0];
$b = $array[1];
$c = $array[2];
As pointed out you don't need variable variables. To get a PHP variable variable name containing index (a key) use array_keys() or array_search() or other array parsers. From php's site:
$array = array(0 => 'blue', 1 => 'red', 2 => 'green', 3 => 'red');
$key = array_search('green', $array); // $key = 2;
$key = array_search('red', $array); // $key = 1;
You could also use the following (using $var= instead of echo):
$arr[0]=123;
$arr[1]=456;
foreach ($arr as $key => $value) {
echo "arr[{$key}] = {$value} \r\n";
}
Which outputs:
arr[0] = 123
arr[1] = 456
But I don't see why you'd do that, since the whole point of the array is not doing that kind of stuff.
If accessing an undefined index of a null reference, PHP does not throw any errors.
<?php
$array = &$foo['bar'];
if ($array['stuff']) echo 'Cool'; // No PHP notice
$array['thing'] = 1; // Array created; $foo['bar']['thing'] == 1
$array['stuff']; // PHP notice
If $array wasn't a reference PHP would have complained on the first line.
Why doesn't it for references? Do I need bother with isset for null references, or is PHP complaining internally and not letting me know?
In your code $array is null. The following code will not give you a notice either:
$b = null;
if ($b['stuff']) echo 'cool';
This is strange, this comment in the documentation points to that fact.
You must raise your error reporting level. Your example $array['stuff'] will throw warnings about index not found. I often combine a test for key in with the evaluation so as to prevent those warnings:
if( array_key_exists("blah",$arr) && strlen($arr['blah']) > 0 ) {
; // do stuff here
}
I often combine variables in with array names because anytime I have to cut-n-paste copy code to the next section to do the same-ish thing, I'd rather make an array of variable names and then iterate through the variable names. The most absurd condition is when I have billing and shipping data to manipulate, where I'll have an array variable name $BorS or just $BS and then at the top, set $BorS="shipping"; and end up with really interesting statements like:
${$BorS."data"}[${$BorS."_addr1"}]=$input_array[$BorS."_address_line_1"];
Why not just do:
$array = array();
I have an array with numerous dimensions, and I want to test for the existence of a cell.
The below cascaded approach, will be for sure a safe way to do it:
if (array_key_exists($arr, 'dim1Key'))
if (array_key_exists($arr['dim1Key'], 'dim2Key'))
if (array_key_exists($arr['dim1Key']['dim2Key'], 'dim3Key'))
echo "cell exists";
But is there a simpler way?
I'll go into more details about this:
Can I perform this check in one single statement?
Do I have to use array_key_exist or can I use something like isset? When do I use each and why?
isset() is the cannonical method of testing, even for multidimensional arrays. Unless you need to know exactly which dimension is missing, then something like
isset($arr[1][2][3])
is perfectly acceptable, even if the [1] and [2] elements aren't there (3 can't exist unless 1 and 2 are there).
However, if you have
$arr['a'] = null;
then
isset($arr['a']); // false
array_key_exists('a', $arr); // true
comment followup:
Maybe this analogy will help. Think of a PHP variable (an actual variable, an array element, etc...) as a cardboard box:
isset() looks inside the box and figures out if the box's contents can be typecast to something that's "not null". It doesn't care if the box exists or not - it only cares about the box's contents. If the box doesn't exist, then it obviously can't contain anything.
array_key_exists() checks if the box itself exists or not. The contents of the box are irrelevant, it's checking for traces of cardboard.
I was having the same problem, except i needed it for some Drupal stuff. I also needed to check if objects contained items as well as arrays. Here's the code I made, its a recursive search that looks to see if objects contain the value as well as arrays. Thought someone might find it useful.
function recursiveIsset($variable, $checkArray, $i=0) {
$new_var = null;
if(is_array($variable) && array_key_exists($checkArray[$i], $variable))
$new_var = $variable[$checkArray[$i]];
else if(is_object($variable) && array_key_exists($checkArray[$i], $variable))
$new_var = $variable->$checkArray[$i];
if(!isset($new_var))
return false;
else if(count($checkArray) > $i + 1)
return recursiveIsset($new_var, $checkArray, $i+1);
else
return $new_var;
}
Use: For instance
recursiveIsset($variables, array('content', 'body', '#object', 'body', 'und'))
In my case in drupal this ment for me that the following variable existed
$variables['content']['body']['#object']->body['und']
due note that just because '#object' is called object does not mean that it is. My recursive search also would return true if this location existed
$variables->content->body['#object']->body['und']
For a fast one liner you can use has method from this array library:
Arr::has('dim1Key.dim2Key.dim3Key')
Big benefit is that you can use dot notation to specify array keys which makes things simpler and more elegant.
Also, this method will work as expected for null value because it internally uses array_key_exists.
If you want to check $arr['dim1Key']['dim2Key']['dim3Key'], to be safe you need to check if all arrays exist before dim3Key. Then you can use array_key_exists.
So yes, there is a simpler way using one single if statement like the following:
if (isset($arr['dim1Key']['dim2Key']) &&
array_key_exists('dim3Key', $arr['dim1Key']['dim2Key'])) ...
I prefer creating a helper function like the following:
function my_isset_multi( $arr,$keys ){
foreach( $keys as $key ){
if( !isset( $arr[$key] ) ){
return false;
}
$arr = $arr[$key];
}
return $arr;
}
Then in my code, I first check the array using the function above, and if it doesn't return false, it will return the array itself.
Imagine you have this kind of array:
$arr = array( 'sample-1' => 'value-1','sample-2' => 'value-2','sample-3' => 'value-3' );
You can write something like this:
$arr = my_isset_multi( $arr,array( 'sample-1','sample-2','sample-3' ) );
if( $arr ){
//You can use the variable $arr without problems
}
The function my_isset_multi will check for every level of the array, and if a key is not set, it will return false.