PHP: Why should only variables be passed by reference? - php

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.

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 problem with array_push(mainArr, subAssociativeArr) inside a function

the array_push(mainArr, subAssociativeArr) does not work when it is inside a function. I need some help with this code:
$store=array();
$samsung=array('id'=>'10','name'=>'samsung');
$sony=array('id'=>'11','name'=>'sony');
function addOne($store, $element){
array_push($store, $element);
}
addOne($store, $samsung);
var_dump($store); //output: empty array
however it works fine if without function; like the following:
$store=array();
$samsung=array('id'=>'10','name'=>'samsung');
$sony=array('id'=>'11','name'=>'sony');
array_push($store, $samsung);
var_dump($store); //output: array is added
so, what is the problem???
You forgot to return
function addOne($store, $element){
$store[]=$element;
return $store;
}
$store = addOne($store, $samsung);
You could also pass by reference if you want to (which is more in line with the code you have):
function addOne(&$store, $element){
$store[]=$element;
}
addOne($store, $samsung);
Note the &. Instead of copying the inputs, this is more like a pointer to the original variable, so you can update it directly. Ether way is fine here, it's a matter of developers choice really. For example it can be very easy to mix the two:
//Don't do this
function addOne(&$store, $element){ //returns null
$store[]=$element;
}
$store = addOne($store, $samsung); //sets $store to null
Which you probably don't want to do, so I can see an argument for both ways. Unless you have super big array, it probably doesn't matter much. It's very easy to forget that a random function is pass by reference.
So use whatever method makes more sense to you.
P.S. - I refuse to use array_push, it's ugly and I don't like it :). Doing $store[]=$element; is the same as array_push($store,$element), except it avoids an unnecessary function call.
Cheers.
When it's in a function, you have a different scope. While the parameters to your addOne function have the same name, they are actually copies of the variables passed, not references to them.
So when you array_push() in a function, you're only affecting the variables in that function's scope, not the outer scope.
You can either return $store, or pass the variables by reference.
If you want it to work within a function you need a reference to the variable. A reference in PHP is defined as & and they are similar to "pointers" in C or C++.
Try this:
function addOne(&$store, $element){
array_push($store, $element);
}
addOne($store, $samsung);
A PHP reference is an alias, which allows two different variables to write to the same value. 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.
http://www.php.net/manual/en/language.oop5.references.php

Can you get value from an array without getting the array first?

Bear with me, I'm learning.
I often see snippets like the one below:
<?p
$imageArray = get_field('image_field');
$imageAlt = $imageArray['alt'];
$imageURL = $imageArray['url'];
?>
It is pedagogical and clear and organized. But is it necessary to get the entire array before querying the array for values? Can I not define the variable in just a single line? Something like the below (which doesn't work, neither the other variants I have tried):
$imageAlt = get_field('image_field', ['alt']);
$imageURL = get_field('image_field', ['url']);
Yes, you can.
As of PHP 5.4 it is possible to array dereference the result of a function or method call directly. Before it was only possible using a temporary variable. - Source
$imageAlt = get_field('image_field')['alt'];
https://eval.in/548036
The question you are asking can be answered by asking 2 questions:
Is it doable ?
Is it a good idea to do it that way ?
Is it doable ?
Yes! You do not have to store the array in a variable and re-use it later.
For instance, you could do:
$imageAlt = get_field('image_field')['alt'];
Note: This will work in PHP 5.4+ and is called: Array dereferencing.
But that is not the only consideration...
Is it a good idea to do it that way ?
No. It's not a good idea in many cases. The get_field() function, depending on your context, is probably doing a lot of work and, each time you call it, the same work is don multiple times.
Let's say you use the count() function. It will count the number of items in an array. To do that, it must iterate through all items to get the value.
If you use the count() function each time you need to validate number of items in an array, you are doing the task of counting each and every time. If you have 10 items in your array, you probably won't notice. But if you have thousands of items in your array, this may cause a delay problem to compute your code (a.k.a. it will be slow).
That is why you would want to do something like: $count = count($myArray); and use a variable instead of calling the function.
The same applies to your question.
While PHP 5.4+ allows you to directly dereference a function return value like this:
get_field('image_field')['alt']
...in this particular case I would not suggest you do so, since you're using two values from the resulting array. A function call has a certain overhead just in itself, and additionally you don't know what the function does behind the scenes before it returns a result. If you call the function twice, you may incur a ton of unnecessary work, where a single function call would have done just as well.
This is not to mention keeping your code DRY; if you need to change the particulars of the function call, you now need to change it twice...
PHP allows you to play around quite a bit:
function foo(){
return array('foo' => 1, 'bar' => 2);
}
Option 1
echo foo()['foo']; // 1
# Better do this if you plan to reuse the array value.
echo ($tmp = foo())['foo']; // 1
echo $tmp['bar']; // 2
It is not recommended to call a function that returns an array, to specifically fetch 1 key and on the next line doing the same thing.
So it is better to store the result of the function in a variable so you can use it afterwards.
Option 2
list($foo, $bar) = array_values(foo());
#foo is the first element of the array, and sets in $foo.
#bar is the second element, and will be set in $bar.
#This behavior is in PHP 7, previously it was ordered from right to left.
echo $foo, $bar; // 12
Option 3
extract(foo()); // Creates variable from the array keys.
echo $foo, $bar;
extract(get_field('image_field'));
echo $alt, $url;
Find more information on the list constructor and extract function.

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/

Categories