Actually, I have a function where a certain variable is passed as argument by reference. I want to create an actual copy of this variable inside my function instead of having a reference. How can I accomplish this in php?
References in PHP do not work as pointers; actually variables in PHP are zval structures, and they contain information for the ref count, is the variable a reference and so on. This works transparently for you, and all that matters when you are using a reference is that you are modifying the original object, and possibly use less memory.
So, if you want to work with a fresh copy of the variable, to be safe from modifications, you can do:
$new_copy = $copy;
or if $copy is an object:
$new_copy = clone $copy;
Related
Lets assume the following:
private $array = array(/*really big multi-dimensional array*/);
public function &func1($specific_large_sub_array_key)
{
return $this->array[$specific_large_sub_array_key]
}
public function func2()
{
$specificArray = &$this->func1(1);
$this->func3($specificArray);
}
public function func3($specificArray)
{
/* do stuff here*/
}
My question is this:
If func3 does not specify that $specificArray is not passed by reference to it, does PHP make a copy of $specificArray when it calls func3 inside of func2? Or does PHP keep the reference and propagate it automatically?
i.e. Will this...
public function func3($specificArray)
{
unset($specificArray[234]);
}
...affect $array?
Thank you
Note, this example is extremely simplified.
PHP is pretty intelligent as to how it deals with variables and copies.
Take the following example:
// Allocate one variable with content 'Hello'
$var = 'Hello';
At this point, the Zend Engine has a representation of your string variable with the content, Hello.
Now if you do this:
$varCopy = $var;
You have 2 independent variables ($var and $varCopy), but since their contents are the same, the content only exists in one place in memory (basically a true copy hasn't been made yet). At this point, the two variables reference the same value (Hello) in a symbol table. It will only copy the contents once one of the two variables is modified. This same logic works for 2 copies to any number of copies.
Put simply, PHP is smart enough not to copy the value of the variable or array when it isn't necessary to make a copy.
You can learn more about this on the Reference Counting Basics page on the PHP manual. They even give an example specific to arrays towards the end.
A useful function is memory_get_usage which can show you how much memory PHP is using. You can use this to track the fact that the memory usage will change very little as you pass multiple copies of your array around. This can help prove the point outlined in the reference counting basics section of the manual.
You don't need to know all the details about how it works, but do be aware that PHP is smart in how it creates and manages references.
EDIT:
To answer your actual question directly, no, in func3 PHP will not make a copy of the array even if you don't pass it by reference. It will use references as illustrated in the reference counting basics section, so you can pass it by value without any concern.
If you call unset however, the value you unset will only be removed from the local copy of the array, so it ultimately isn't removed from the source array unless you pass it by reference to the function. But passing it by value does not create a whole new copy of the entire gigantic array. Even removing one value from the copy doesn't create a whole new copy minus the entry you removed (you just have a second array with all identical references to the first, but it is missing the one reference to the removed entry).
Can't do multiline comments, so as an answer:
return &$this->array[$specific_large_sub_array_key]
^
But to also give you an answer to your question:
i.e. Will this... [...] ...affect $array?
Plain and simple: No. Reason: It's a different variable, not an alias (reference).
I noticed that in PHP extract(some_function()); will work just like:
$stuff = some_function();
extract($stuff);
But in the PHP's documentation the extract function argument has the & thingy in front, and from what I know that means you have to pass a variable to it.
If the documentation was right, this would produce a strict standards message:
PHP Strict standards: Only variables should be passed by reference
So I think you just found a bug in the documentation. Congratulations.
EDIT
It still doesn't complain if you use it with EXTR_REFS as a second argument:
~❯ php -a
Interactive shell
php > function a(){return array('pwet'=> 42);}
php > extract(a(), EXTR_REFS);
php > echo $pwet;
42
Which is strange because referencing variables defined inside a function doesn't make much sense to me. I think the & might have been introduced because of this option, but appears only in the doc and is not enforced in the code.
EDIT
It seems I'm right, I found this comment in ext/standard/array.c (branches 5.3 and 5.4):
/* var_array is passed by ref for the needs of EXTR_REFS (needs to
* work on the original array to create refs to its members)
* simulate pass_by_value if EXTR_REFS is not used */
The ampersand passes a variable by reference so that when it is used in a function, you are manipulating the original object -- not a new variable with the same value. The documentation is telling you that if you pass a variable to the extract function, then the original object can be updated in some fashion by that function.
So, the answer is yes, you need to pass a variable to that function.
The reason $var_array parameter of the extract function is passed by reference (most likely) is from a holdover from older versions of PHP. Newer versions automatically pass arrays by reference.
The extract function creates a variable list from the contents of a (potentially large) array and it is not recommended that data of that type be passed by value.
Long story short, assign your array to a variable and pass it in that way.
I will always be in confusion whether to create pass/call by reference functions. It would be great if someone could explain when exactly I should use it and some realistic examples.
A common reason for calling by reference (or pointers) in other languages is to save on space - but PHP is smart enough to implement copy-on-write for arguments which are declared as passed-by-value (copies). There are also some hidden semantic oddities - although PHP5 introduced the practice of always passing objects by reference, array values are always stored as references, call_user_func() always calls by value - never by reference (because it itself is a function - not a construct).
But this is additional to the original question asked.
In general its good practice to always declare your code as passing by value (copy) unless you explicitly want the value to be different after the invoked functionality returns. The reason being that you should know how the invoked functionality changes the state of the code you are currently writing. These concepts are generally referred to as isolation and separation of concerns.
Since PHP 5 there is no real reason to pass values by reference.
One exception is if you want to modify arrays in-place. Take for example the sort function. You can see that the array is passed by reference, which means that the array is sorted in place (no new array is returned).
Or consider a recursive function where each call needs to have access to the same datum (which is often an array too).
In php4 it was used for large variables. If you passed an array in a function the array was copied for use in the function, using a lot of memory and cpu. The solution was this:
function foo(&$arr)
{
echo $arr['value'];
}
$arr = new array();
foo($arr);
This way you only passed the reference, a link to the array and save memory and cpu. Since php5 every object and array (not sure of scalars like int) are passed by reference internally so there isn't any need to do it yourself.
This is best when your function will always return a modified version of the variable that is passed to it to the same variable
$var = modify($var);
function modify($var)
{
return $var.'ret';
}
If you will always return to the passed variable, using reference is great.
Also, when dealing with large variables and especially arrays, it is good to pass by reference wherever feasible. This helps save on memory.
Usually, I pass by reference when dealing with arrays since I usually return to the modified array to the original array.
References:
If I pass a variable to a function (e.g. $var), is that supposed to be a copy of a reference to the actual variable (such that setting it null doesn't affect other copies)?
Or is it receiving a reference to what is a new copy of the actual variable (such that setting it to null destroys its copy only)?
If the latter, does this copy objects and arrays in memory? That seems like a good way to waste memory and CPU time, if so.
I think can understand passing by reference (e.g. &$var) correctly by knowing how this works, first.
Scope:
What's the deal with local scope? Am I right in observing that I can declare an array in one function and then use that array in other functions called within that function WITHOUT passing it to them as a parameter?
Similarly, does declaring in array in a function called within a function allow it to be available in the caller?
If not, does scoping work by a call stack or whatever like every bloody thing I've come to understand about programming tells me it should?
PHP is so much fun. :(
If I pass a variable to a function (e.g. $var), is that supposed to be a copy of a reference to the actual variable (such that setting it null doesn't affect other copies)?
Depends on the function. And also how you call it. Look at this example:
http://www.ideone.com/LueFc
Or is it receiving a reference to what is a new copy of the actual variable (such that setting it to null destroys its copy only)?
Again depends on the function
If the latter, does this copy objects and arrays in memory? That seems like a good way to waste memory and CPU time, if so.
Its going to save memory to use a reference, certainly. In php>4 it always uses reference for objects unless you specify otherwise.
What's the deal with local scope? Am I right in observing that I can declare an array in one function and then use that array in other functions called within that function WITHOUT passing it to them as a parameter?
No you can't.
Similarly, does declaring in array in a function called within a function allow it to be available in the caller?
No, it doesn't.
If not, does scoping work by a call stack or whatever like every bloody thing I've come to understand about programming tells me it should?
If you want to use a variable from outside the function, before using it, you'd write global $outsidevar
Concerning your first set of questions:
foo($a);
function foo($b) { echo $b; }
In this case, $a will not be copied to a new variable $b, only because it is passed by value.
This is because PHP uses the copy-on-write concept. PHP will not copy the contents of a variable, unless they are changed. Instead PHP will increment the refcount property of the existing "zval" of $a.
Well, the whole thing is not that trivial, but to answer your question: No, it does not copy the variable, unless you write to it in the function and no, you won't save CPU and Memory by using a reference. In most cases the reference won't change performance at all, but in the worst case it will actually degrade it (because if a not is_ref variant of the variable already exists and a reference is created the value of the variable must be copied to get a zval with is_ref and one without). Optimizing code by using references is no good.
if argument to a function is defined as so "function my_function($variable) {}" then you are getting a copy of the variable and any alterations made to the variable inside your function will not be available to the function caller. you can pass a variable by reference by prepending an ampersand to the argument when defining your function and thus any alterations made to the variable will persist to the function caller, ie "function my_function(&$variable) {}"
function myfunction($var) {
$var = 'World';
}
$var = 'Hello';
myfunction($var);
echo $var; // 'Hello';
Passing a variable by reference
function myfunction(&$var) {
$var = 'World';
}
$var = 'Hello';
myfunction($var);
echo $var; // 'World'
Are Php function parameters passed as references to object or as copy of object?
It's very clear in C++ but in Php5 I don't know.
Example:
<?php
$dom = new MyDocumentHTMLDom();
myFun($dom);
Is parameter $dom passed as a reference or as a copy?
In PHP5, objects are passed by reference. Well, not exactly in technical way, because it's a copy - but object variables in PHP5 are storing object IDENTIFIER, not the object itself, so essentially it's the same as passing by reference.
More here:
http://www.php.net/manual/en/language.oop5.references.php
Objects are always pass by reference, so any modifications made to the object in your function are reflected in the original
Scalars are pass by reference. However, if you modify the scalar variable in your code, then PHP will take a local copy and modify that... unless you explicitly used the & to indicate pass by reference in the function definition, in which case modification is to the original
In PHP5, the default for objects is to pass by reference.
Here is one blog post that highlights this: http://mjtsai.com/blog/2004/07/15/php-5-object-references/
Copy, unless you specify, in your case, &$dom in your function declaration.
UPDATE
In the OP, the example was an object. My answer was general and brief. Tomasz Struczyński provided an excellent, more detailed answer.
in php5 objects pass by reference, in php4 and older - by value(copy) to pass by reference in php4 you must set & before object's name
It has nothing to do with function parameters. PHP 5 only has pointers to objects; it does not have "objects" as values. So your code is equivalent to this in C++:
MyDocumentHTMLDom *dom = new MyDocumentHTMLDom;
myFun(dom);
Now, many people mentioned pass by value or pass by reference. You didn't ask about this in the question, but since people mention it, I will talk about it. Like in C++ (since you mentioned you know C++), pass by value or pass by reference is determined by how the function is declared.
A parameter is pass by reference if and only if it has a & in the function declaration:
function myFun(&$dom) { ... }
just like in C++:
void myFun(MyDocumentHTMLDom *&dom) { ... }
If it does not, then it is pass by value:
function myFun($dom) { ... }
just like in C++:
void myFun(MyDocumentHTMLDom *dom) { ... }