What is the difference between these two array Iterations? - php

I'm studying PHP for an advanced exam. The practice test said that the first iteration is better than the second. I not figure out why. They both iterate the contents of an array just fine.
// First one:
foreach($array as $key => &$val) { /* ... */ }
// Second one:
foreach($array as $key => $val) { /* ... */ }

The practice test said that the first iteration is better than the second.
That isn't the best advice. They're different tools for different jobs.
The & means to treat the variable by reference as opposed to a copy.
When you have a variable reference, it is similar to a pointer in C. Accessing the variable lets you access the memory location of the original variable, allowing you to modify its value through a different identifier.
// Some variable.
$a = 42;
// A reference to $a.
// & operator returns a reference.
$ref = &$a;
// Assignment of $ref.
$ref = "fourty-two";
// $a itself has changed, through
// the reference $ref.
var_dump($a); // "fourty-two"
Reference example on CodePad.
The normal behaviour of foreach is to make a copy available to the associated block. This means you are free to reassign it, and it won't affect the array member (this won't be the case for variables which are always references, such as class instances).
Copy example on CodePad.
Class instance example on CodePad.
Using a reference in a foreach has some side effects, such as a dangling $val reference to the last value iterated over, which can be modified later by accident and affect the array.
Dangling reference example on CodePad.

The first example includes the & reference-operator for $val --> any changes to $val will be saved to $array.
This is by no means "better" than example #2, though.

Related

Strange PHP loop scope what am i missing? [duplicate]

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)

PHP: Why should only variables be passed by reference?

If you are a PHP developer you most probably have seen the following notice:
Notice: Only variables should be passed by reference in /somefile.php
on line xxx
(Problem extensivley treated in Only variables should be passed by reference)
Example throwing notice:
$string = "hi-dude";
echo end(explode('-', $string));
Working example:
$string = "hi-dude";
$strings = explode('-', $string);
echo end($strings);
Explanation:
Only real variables may be passed by reference, not functions which are returning the correct variable.
However I can not think of a good reason why this notice is happening. It feels unecessary and requires me to write a lot of extra lines of code sometimes. What is the reason for PHP having this strange restriction? Why does this problem even exist?
end() or array_pop() will return the E_NOTICE with message
Only variables should be passed by reference
The reason is that end() requires a reference, because it makes the current element pointer point to the last element.
You can do it with one line,
$string = "this-is-a-sample-text";
echo substr(strrchr($string, '-'), 1);
DEMO: https://3v4l.org/jO29n
Finally I found a great explanation which helped me to understand this: What's the difference between passing by reference vs. passing by value?
As Daniel Pryden states:
In simplest terms:
call by value means that you pass values as function arguments
call by reference means that you pass variables as function arguments
In metaphoric terms:
Call by value is where I write down something on a piece of paper and hand it to you. Maybe it's a URL, maybe it's a complete copy of
War and Peace. No matter what it is, it's on a piece of paper which
I've given to you, and so now it is effectively your piece of paper.
You are now free to scribble on that piece of paper, or use that piece
of paper to find something somewhere else and fiddle with it,
whatever.
Call by reference is when I give you my notebook which has something written down in it. You may scribble in my notebook (maybe I
want you to, maybe I don't), and afterwards I keep my notebook, with
whatever scribbles you've put there. Also, if what either you or I
wrote there is information about how to find something somewhere else,
either you or I can go there and fiddle with that information.
In this case the notice "Only variables should be passed by reference" is still unjustified as we are only interested in retrieving the last value of the array. However the function end() is defined like
mixed end ( array &$array )
The & sign which states passing by reference is there for a certain reason: end() is not just returning the last element of an array, it also changes its internal pointer to the end. Therefore the array is modified.
If we only would return the last element of an array without touching the array there would be no need to pass the array by reference and we would not get this notice. But end() is somehow the wrong function for that.
What if there is no justification for me getting this notice?
Note that also the function to be called might be defined wrong. In my case I hade a function defined like this:
/**
* Flatten an array by one level if only needing a certain key value from a sub array.
*
* Example: [["foo"=>"bar","foo"=>"cheese"]]
* Result: ["bar","cheese"]
*
* #param $array: The input array.
* #param $key: The key to flatupshift. Default is 0.
* #return $array: The result
*/
private function array_flatupshift(&$array, $key = 0) {
$a = [];
foreach ($array as $item) {
if (is_object($item)) {
array_push($a, $item->$key);
} else if (is_array($item)) {
array_push($a, $item[$key]);
}
}
return $a;
}
This is simply a wrong function definition. So if you also get notices like this: Check if the function you call is defined correctly. Passing by reference does not make sense here as the array being passed is not touched in any way. Therefore the function definition should be without the "reference &/":
private function array_flatupshift($array, $key = 0) {
There are some cases where you MIGHT use the error control operator if you know what you are doing. Therefore:
$string = "hi-dude";
echo #end(explode('-', $string));
... would be o.k. I guess is the result of explode is not needed anymore. However notice the drawbacks of suppressing all possible errors. Please correct me if I go wrong here.

PHP: strange reference variable

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

Practical uses of prepending an ampersand to PHP variables

I know that prepending a '&' to your PHP variable sets up a reference to the original variable instead of copying its value like so:
$original = 'apples';
$secondary = &$original;
$original = 'oranges';
echo $secondary; // 'oranges'
If it works this way, why not just use the original variable then?
Passing by reference is useful and necessary when passing a variable as a parameter to a function, expecting that variable to be modified without a copy being created in memory. Many of PHP's native array_*() functions operate on array references, for example.
This function, for example, receives an array reference and appends an element onto the original array. If this was done without the & reference, a new array copy would be created in scope of the function. It would then have to be returned and reassigned to be used.
function add_to_an_array(&$array)
{
// Append a value to the array
$array[] = 'another value';
}
$array = array('one', 'two', 'three');
add_to_an_array($array);
print_r($array);
Array
(
[0] => one
[1] => two
[2] => three
[3] => another value
)
$original = 'apples';
function foo($word) {
$word = 'oranges';
}
foo($original);
echo $original; // apples, because only local $word was changed, not $original.
foo(&$original);
echo $original; // oranges, because $original and $word are the same
Pass by reference is really a cop out and goes against good encapsulation. If you need to manipulate a variable in that way, it probably should belong to a class as a member variable and then does not need to be passed to the function. Good OO design would usually make member variables immutable with a "final" keyword, but PHP doesn't have this. It's not intuitive that passing a variable to a function might change it's value which is why it should be avoided in most cases.
Also going to a more full OO design prevents you have having method signatures that are long and complex with many optional parameters that are difficult to re-factor.
A more interesting use of the is when it's used in the formal argument to a function
foo($a);
...
function foo (&$a) {
....
}
this allows you to modify a in the function.
There are many uses for references.
You can pass a reference to a variable to a function so you can change the value inside the function
You can use references to create linked lists
etc...
Just keep in mind that they're there, and you'll surely find an application for them when the time comes and you face a problem that can be solved with references.
Check out the following article for other ideas and uses of references:
http://www.elated.com/articles/php-references/

What are the advantages of a pass by reference iterator in a foreach loop in PHP?

I just read this answer by Bill Karwin.
Note that the $node is pass by reference.
I always thought that variable created there is only ever temporary and exists only until the end of the loop (or maybe it remains set to the last iteration - I have not tested it).
So, what are the advantages of making it pass by reference?
I'm sure there is one, but I can't figure it out yet.
It has nothing to do with being faster or more efficient.
PHP implements copy-on-write so a variable may refer to the same area of memory until you change its value. Then when you change the variable, PHP decides whether or not to create a copy depending on whether you're accessing it by value or by reference.
Access by value -- changes to a variable creates a copy:
$a = array("abc");
foreach ($a as $element) {
$element = "def";
}
print_r($a);
Array
(
[0] => abc
)
Access by reference -- changes to a variable affect the original copy:
foreach ($a as &$element) {
$element = "def";
}
print_r($a);
Array
(
[0] => def
)
Note that all objects in PHP 5 are accessed by reference without using the & operator.
This is a good reason to use objects!
Be very careful using references in PHP, because they can have confusing effects.
Be sure to read this blog: Do not use PHP references by PHP core developer Johannes Schlüter
The advantage is that it lets you manipulate the array directly.
foreach ($array as $key => $node) {
$array[$key]['foo'] = 'bar';
}
can be made shorter and more efficient like this:
foreach ($array as &$node) {
$node['foo'] = 'bar';
}
In this case $node is not a copy of the contents of the array entry, but a reference to the actual array entry; whatever you do to it, you do to the array.

Categories