It was my understanding for a while that foreach clones the individual objects in its iterations, and I had used & to iterate by reference. However, in my latest machine which runs PHP 5.5.10, I'm able to omit & and still update the original object. I don't see anything in the release notes. Am I misunderstanding something?
foreach ($items as $item) { // No "&"
$item->setData('123'); // Updates the respective object in $items
// Checked object ID hash, and they're the same with or without "&"
}
PHP Change Log: http://php.net/ChangeLog-5.php
PHP paradigm is that objects (and resources) are always references, while other types (base types or arrays) are copied, so the & operator has no effect on objects (and is meaningless on resources since only "special functions" i.e. external library modules can take them as parameters), but allows to pass variables of other types by reference.
In PHP objects are always passed by reference. How it work? Objects has identifier which is passed, we can call this handle. So if you:
$a = new A();
$b = $a;
$b->foo = 2;
echo $a->foo;
return value will be 2. Why? Because the handle of the object is copied to $b and they both - $a and $b - point to the same object. We can term this as a reference, but it isn't reference meaning in strict way, but behaviour of this implementation is similar to reference. So you do not need any use of references because PHP make it for you by default.
Read the OOP references documentation.
Foreach assigns each element of the array to the given variable, as in simple assignment, with =, which copies the value of the element to the variable. This is always true regardless of what type the value happens to have. (Only if you do as &$item will it be a reference.)
$item is not "an object". $item is an object reference, basically a pointer to an object. In PHP 5, you cannot have a variable whose value "is an object" -- when you do new something(), you get a reference to an object; when you access a field or method with ->, the left side must be a reference to an object. Basically, you always deal with references to objects, never objects themselves.
Two object references can point to the same object, and if you modify an object by calling a method on it using one object reference, the result is visible through another object reference that points to the same object.
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)
While doing some code refactoring I momentarily ended up in a situation where I was basically doing the (somewhat abstracted-out) equivalent of
$data = (object)json_decode('"test"');
Of course I understand json_decode() generates objects on its own unless assoc is false. (Incidentally I got into this situation because I was in the middle of moving some format processing code around, and I hadn't yet realized one of my (object) casts was now redundant.)
But... when this happened, PHP decided that $data contained:
stdClass Object
(
[scalar] => test
)
Wat.
scalar?!
Last I learned, "test" is a string, so it seems more than one pile of things has fallen over internally here. Or is this unintuitive yet intended design?!
I have of course removed the (object) and things work exactly how I intended now. So there's no bug here per se. I just wanted to understand what just happened.
Here you go, in case you want to join in the headscratching:
php -r 'print_r((object)json_decode("\"test\""));'
I'm using 7.0.25.
This is exactly what the manual specifies will happen when casting a scalar type (i.e. int, string, float, boolean) to an object.
If an object is converted to an object, it is not modified. If a value of any other type is converted to an object, a new instance of the stdClass built-in class is created. If the value was NULL, the new instance will be empty. An array converts to an object with properties named by keys and corresponding values. Note that in this case before PHP 7.2.0 numeric keys have been inaccessible unless iterated.
For any other value, a member variable named scalar will contain the value.
$obj = (object) 'ciao';
echo $obj->scalar; // outputs 'ciao'
While reading the manual page on the built-in function serialize() I came across the following quoted text :
You can even serialize() arrays that contain references to itself.
Circular references inside the array/object you are serializing will
also be stored. Any other reference will be lost.
I didn't understand what does mean by the array that contain references to itself?
I also didn't understand what are circular references inside the array/object?
The only thing I understood from the above text is I can generate a storable representation of such types of values using the built-in function serialize()
Also, there are no more code examples or explanation has been given with this regard.
I googled to understand the meaning of these types of entities specifically in PHP but I got nothing from Google.
Can someone please explain me these concepts with the help of easy to understand explanation accompanied with the suitable code examples. Examples would make things understand better.
Examples are at the end of the answer.
In (a) we try insert an array into itself. But a mechanism called copy on write kicks in and we insert $a into a copy of $a and end up with an array that contains an empty array. (b) is a little tricky. First we create an array that contains a reference to itself, but that means $b will itself be a reference. Then we pass $b to serialize, but serialize accepts a value only and thus will create a copy of $b. The output shows an array that contains an array that contains a reference to itself. The R:2; in the output indicates a reference to the second array. For some internal reason the count starts with 1 and not 0. (If you're really interested, I could lookup the reason, but for this question you can just accept it).
Object act a little different. This is because objects in PHP are accessed by a an ID, and the copy on write only duplicates the ID structure and not the object itself. In the output of (c) you can see that this is by a small r. The number is just as above. But as seen in (d) you can also assign objects by reference and the small r will become a capital R. The output differs from (b), because while $d will be copied just like $b it again only affects the ID structure, and we see an object that contains a reference to itself. (e) utilizes the clone keyword to create an actual copy and serializes as such.
Further Reading about PHP refereces.
<?php
$a = [];
$a[] = $a;
echo "\na: ".serialize($a);
$b = [];
$b[] =& $b;
echo "\nb: ".serialize($b);
$c = new stdClass();
$c->c = $c;
echo "\nc: ".serialize($c);
$d = new stdClass();
$d->d =& $d;
echo "\nd: ".serialize($d);
$e = new stdClass();
$e->e = clone $e;
echo "\ne: ".serialize($e);
Output:
a: a:1:{i:0;a:0:{}}
b: a:1:{i:0;a:1:{i:0;R:2;}}
c: O:8:"stdClass":1:{s:1:"c";r:1;}
d: O:8:"stdClass":1:{s:1:"d";R:1;}
e: O:8:"stdClass":1:{s:1:"e";O:8:"stdClass":0:{}}
Link to play around with.
Feel free to ask for clarification in comments the question is quite broad and I tried to keep it brief.
I have a function that requires information to be passed to it. The information is contained within an object. Therefore I must pass that object as one of the function arguments. The object is very large however, and I would like to reduce the overhead involved in making copies every time it is passed. Here is an example of
My function Call:
1 myFunction($myObject1);
and the function:
2 function myFunction($myObject2){
3 //do stuff
4 }
I understand there is more to it in php than just pass-by-reference vs pass-by-value. Correct me if I am wrong, but I believe on line 1 there is only a reference to the object made, but on line 2 the object is copied. To avoid this copy I have replaced ($myObject2) with (&$myObject2). I still refer to the object within the function definition as $myObject2 and everything seems to work. I believe I am now using a reference only and therefore making no copies of the object (which was my goal). Is my thinking correct? If not not why?
In PHP5, "objects" are not values. The value of the variables $myObject1, $myObject2 are object references (i.e. pointers to objects). You cannot get "the object itself"; objects can only be manipulated through these pointers.
Assignment and passing by value only copy values. Since objects are not values, they cannot ever be cloned through assignment, passing, etc. The only way to duplicate an object is to use the clone operator.
Putting & on a variable makes it pass or assign by reference, instead of by value without the &. Passing by reference allows you to modify the variable passed. Since the value of a variable cannot be an object, this has nothing to do with objects.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
In PHP can someone explain cloning vs pointer reference?
According to http://php.net/manual/en/language.oop5.references.php
One of the key-points of PHP 5 OOP that is often mentioned is that "objects are passed by references by default". This is not completely true. This section rectifies that general thought using some examples.
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.
If objects are passed by alias or handler then in what situation would you actually want to pass by reference?
myFunc($obj); // Pass by alias/handler
myFunc(& $obj); // Pass by reference (when would you do this?)
myFunc(clone $obj); // Create a new object
What are the different use-cases for each of these?
What are the different use-cases for each of these?
You already named the three different use-cases:
Passing an object as parameter
Passing an alias as parameter
Passing an object clone as parameter
Naturally you do each of these three things when you need to do the specific case. You normally only need 1. in 99.9%. You do the other 0.1% when you need to.
An examlary use-case (as asked for in comments) for a variable alias as parameter as a little example (Demo):
class VarFreezer
{
private $alias;
private $value;
public function __construct(&$object) {
$this->alias = &$object;
$this->value = $object;
}
public function restore()
{
$this->alias = $this->value;
}
}
$var = new stdClass();
$var->value = 'I am an object now.';
$freezer = new VarFreezer($var);
$var = 'I am a string now.';
$freezer->restore();
var_dump($var);
When you give an object to a method the reference to the existing objects is passed so that you have the variable in the calling scope and the method parameter variable in the method's scope both referencing to the same object.
You can explicitly use & to give an reference to a primitive like an array, so that an invoked method can change the data of the original array. For objects this is the default nowadays as you mentioned.
A clone seems obvious.
With &$obj you create an alias to $obj not a reference to the object $obj refers to. Quoting php.net:
$c = new A;
$d = &$c;
$c->foo = 2;
$c = NULL;
echo $d->foo."\n"; // Notice: Trying to get property of non-object...
myfunc($obj) //pass in identifier
For when your function needs to get access to the object and manipulate/ read from it somehow.
myfunc(& $obj) //pass in reference to the identifier.
The key here is that your passing a reference to the identifier. Which means that if the outer variable ($obj) is set to null, then the inner object also becomes null because you've removed the identifier from $obj and so the reference to it has also been removed. Unlike when you pass in the identifier because what your actually doing it passing in a copy of the identifier and so if $obj was set to null then the value inside the function would still point to the object.
example:
var x = new Object();
var y = x //y and x both contain **different** identifiers to the same object in memory.
function a($_a){} // a function for examples
a($y) // passes in a copy of $y so $_a (inside the function) is a third identifier pointing to th in memory object
a(& $y) // now $_a contains the **same** identifier as $y so if either is emptied then the other is also emptied.
a(clone $y) //creates a new object identical to the first and passes in an identifier of the new object.
hope thats clear enough.
you would pass an identifier by reference if you want to be able to modify or delete the identifier from inside the function and have it affect the external identifier variable whilst being able to return an independently calculated value. e.g serialize an object, save it to a file return the file string but also clear the identifier so the object can be cleared from memory.