I have created a php class that implements the Iterator interface. I am successfully able to iterate through the class like this:
foreach($data as $key=>$value)
echo $key;
This, however, consistently gives me a strange result:
foreach($data as $item)
echo key($item)
The first style calls the key() method in my class for every element. The latter never calls it.
Am I missing something? That should work, shouldn't it?
Update: I think I am missing something. The key function doesn't work as I would expect it to for a simple array, either:
$test = [['name'=>'foo'],['name'=>'bar'],['name'=>'fizz']];
foreach($test as $key=>$item)
echo $key;
foreach ($test as $item)
echo key($item);
gives me 012namenamename... not quite what I was expecting. I think I need to just use the $key=>value notation, and never count on key() in a loop.
Your first version is correct, which is why it works. For a short explanation of why your second method doesn't work, see this comment in the manual.
This is because (from the manual for foreach() )
On each iteration, the value of the current element is assigned to $value and the internal array pointer is advanced by one
So your second method is fetching the next key, not the current one.
Related
I'm seeing some confusing behavior related to reference/assignment in PHP...
private function doSomething($things)
{
foreach ($things as $thing) {
echo $thing->property; // 'foobar'
$copyThing = $thing;
unset($copyThing->property);
echo $thing->property; // undefined
I expect this behavior when passing variables by reference (&$thing) but I'm not trying to do that here and it seems to be happening anyway. What am I missing?
Just explaining my comment:
objects in foreach loops are always passed by reference
When you use a foreach loop for an array of objects the variable that you are using inside the loop is a pointer to that object so it works as a reference, any change on the object inside the loop is a change on the object outside.
This is because:
objects are always passed by reference (#user3137702 quote)
Detailed and official explanation here.
When you copy and unset your variable:
$copyThing = $thing;
unset($copyThing->property);
you are creating another pointer and unseting it, so the original value is a gone. As a matter of fact, since the foreach loop also uses a pointer the $things array is also affected.
check this ideone (notice the vardump [where the 'a' property is gone], as the output is the same as you got)
I do not know in which version it changed, if ever, as it seems like default object/pointer behavior
As a workaround (some ideas):
Copy your initial array
Use clone: $x = clone($obj); (As long as the default copy constructor works for your objects)
So I have this slightly annoying issue: say I have a foreach loop like this:
foreach ($arr as $key=>$value) {
do_something($key);
}
In my eclipse environment, I have turned on the feature that displays warnings for unused variables, which is really helpful. However, it complains for all such occurences, where the $value is not used in the loop.
I was wondering if there is some syntax where I don't use this, like is available for list() :
list(,,$my_var) = some_func();
//these returns an array with 3 elements, but I only need the last one
Note: The obvious would be to use array_keys(), but I don't want a function call; I'm merely asking if there's a shorthand I don't know of, or something like it. This is why the question PHP foreach that returns keys only does not cover what I'm asking.
TBH, I couldn't find any resource to back this answer, it works fine as far as my tests went, BUT I CAN'T SAY FOR SURE WHETHER THIS IS OR ISN'T RECOMMENDED TO USE. (Probably not)
Here is what I came up with:
$arr = array('kN1' => '50', 'kN2' => 400);
//$arr = array('50', 400);
foreach ($arr as $var => $var) { // use same variable for both key and value
print_r($var);
echo '<br>';
}
// kN1
// kN2
Run Viper
To get rid of the warning without introducing too much overhead just unset the unused variable once the loop is done.
foreach ($arr as $key => &$val) {
print_r($key);
}
unset($val);
BTW: I believe one should use a reference to the unused variable (&$val instead of $val). Otherwise you might end up producing a full copy of the variable with each iteration and that might be a costly operation.
As I understand, when you pass a variable to a function, and if you don't use reference sign (&) , it means any changes inside your function will not affect to your variable outside the function. In other words, it means the compiler will make a copy of the outside variable to use inside function, doesn't it?
But when I run these testing code, it does not happen like that.
Can anyone explain me what I miss here? Thank you
My test code: the expected result should be 3, but it becomes 1?
function test($arr2) {
foreach($arr2 as &$item) {
$item = 1;
}
}
$arr = array(2);
foreach($arr as &$item2) {
$item2 = 3;
}
test($arr);
print_r($arr);
This issue has been solved a few times before you've asked this (#1). The issue is due to the fact that:
Reference of a $value and the last array element remain even after the
foreach loop. It is recommended to destroy it by unset().
Reference: PHP foreach()
You need to unset the last $item2 after your foreach:
foreach ($arr as &$item2) {
$item2 = 3;
}
unset($item2);
This is quite interesting, it seems like the behavior of array are the same as objects in php in which new array still holds the copy of the members identifier (which points to the same value as the array it was copied from).
As of PHP 5, an object variable doesn't contain the object itself as value anymore. It only contains an object identifier which allows object accessors to find the actual object. When an object is sent by argument, returned or assigned to another variable, the different variables are not aliases: they hold a copy of the identifier, which points to the same object.
PHP Manual - Objects and references
Even though you are not passing $arr as Reference variable, you are still accessing $arr elements as References in function test(). So anything that changes in function will effect outside function too.
If you are looking to change $arr ( which has been passed as $arr2 in test function ) only in test function, then remove &from $item
If I need to change a function parameter, I can use foo(&$x) and the function can modify it.
I need something like this in a foreach loop,
$x=array(1,2,3);
foreach($x as &$i) $i=1;
var_dump($x);
results with some strange "&int(1)"...
array(3) {
[0]=>
int(1)
[1]=>
int(1)
[2]=>
&int(1)
}
But, of course, this is not the syntax (for what I whant)... It is possible to do this with PHP?
It is not "so elegant" to use for (now it works!),
$x=array(1,2,3);
for($i=0; $i<count($x); $i++) $x[$i]=1;
var_dump($x);
First off, this isn't technically a pointer. It is a reference. Before using them in your code, I'd suggest you get familiar with how they work in PHP by reading the manual section that explains them.
In this case, your code is actually correct, as documented in the manual page for foreach. foreach($x as &$i) is precisely the way to do this. Your confusion apparently comes from the output of var_dump:
&int(1)
This is precisely what you should expect. The & signifies that another variable is a reference pointing to this value. This is because $i continues to be a reference to the last element in the array even after the loop is over. This is potentially dangerous, because code elsewhere in your file may modify $i and therefore your array without you wanting it to.
It is probably good practice to unset $i after the loop is over to avoid this.
When a loop ends, the iterator still holds the last value used. In this case, the last item in the array. So it's still referenced. It doesn't really matter unless you try to reuse $i later, in which case you may get some weird results.
It would be safer to do something like this:
foreach($x as $k=>$v) {
$x[$k] = 1;
}
My homework affter good comments and answer at the comments.
Read the guide
As #lonesomeday says, "that is precisely the right syntax". The PHP guide shows that
$arr = array(1, 2, 3, 4);
foreach ($arr as &$value)
$value = $value * 2;
unset($value); // break the reference with the last element
and, as #Wrikken say, needs the unset.
Use "intermediate elegance" when you are not sure
As #MarcB20 says, take care..."foreach($x as &$i) $i=1; is VERY dangerous. $i will REMAIN a reference for the duration of your code (or until you unset it), and you can unwittingly modify the contents of the array by reusing the variable in some other context down the road"...
So, you can use an "intermediate elegance" syntax,
foreach ($arr as $key=>$value) {
// ... use $value ...
$arr[$key] = $anothervalue;
}
prefer to use simple for (with array_keys if an array associative) if the $value not used.
I am reading http://php.net/manual/en/class.iterator.php, but had a hard time to understand the Example #1 Basic usage.
Questions:
var_dump(__METHOD__);
I know you can use variable here, eg: var_dump($count), but METHOD is not variable, or it is global variable/constant?
foreach($it as $key => $value) {
var_dump($key, $value);
echo "\n";
}
if I change it to:
foreach($it as $key => $value) {
}
if I run the script, it can still show the outcome, why?
var_dump($key, $value);
the outcome is
int 0 string 'firstelement' (length=12)
int 1 string 'secondelement' (length=13)
...
why it is this result? foreach($it as $key => $value), $it is object, it is not $array, so how could this happen?
The Iterator interface allows the class to behave like it was an array in the foreach statement.
Because it's not an array, the class must know, how to behave in this situation. This is done by calling (by the foreach, let's say for simplicity) some methods that are implemented from the Iterator interface. As it's the interface requirements, all of the methods should be implemented, even if you're not going to use some of them, like retrieving the key.
In the methods you can type whatever you like, even something that does not makes sense in the foreach loop (say you do not increase the counter $position).
In the manual the var_dump()s are used to show you which methods are called. The __METHOD__ pseudo-constant is a string that returns the current method's name. You should remove these lines, as they are given for the example purposes only.
Each of the methods from the Iterator interface are public, so you can call them from any place in the code, but there is no need to call them in your program. In the foreach loop they are called automatically so that's why your empty loop works.