I've read up about PHP variable references but I'm not 100% and was hoping someone could help.
If I have a class like the following:
class Item
{
public $value;
}
I then have an array of those items in a variable - lets call that $items. All I did was new Item()...and $items[] = $newItem;.
Now, I want to populate another array but it filters the original array based on its value. So like the following:
foreach($items as $key => $value)
{
$filteredItems[] = &value;
}
Now, I have ANOTHER variable that iterates over that filtered list and does something like so:
$theItem = $filteredItems[10];
$theItem->value = 100;
Now this is where I'm confused. Do I need to set $theItem to &filteredItems[10]; (reference) or will it just know that the value in the array is a reference type and $theItem also becomes a reference to that same item? I'm after that last set of $theItem->value = 100; changes the very original object stored in the $items list.
In PHP 5 objects are always passed around by their "handle" for lack of better word. This means if you do this:
$a = new Item();
$a->value = 1;
$b = $a;
$b->value++;
echo $a->value;
The value of 2 is echoed. Why? Because the handle of the object is copied from $a to $b and they both point to the same object. This isn't a reference in terms of using &, but behaves similarly enough to the point that people generally call it the same thing... even though it's not.
So you do not need any use of references in your code. Usually in PHP, you never need to use references when using objects.
With respect to objects, you really only notice references if you do this (assign a new value to the variable itself):
function foo(Item &$a)
{
$a = null;
}
$b = new Item();
foo($b);
var_dump($b);
This results in NULL, which wouldn't happen without a reference. But again, this is not typical usage, so you can really forget about using references with objects.
(And of course the use of a function isn't necessary here to illustrate the point, but that's the most typical place you'll see them in the "real world.")
It's like this:
foreach($items as $key => &$value) {
$filteredItems[] = $value;
}
The point where you give the original instance into a different scope is where you put the &.
Same is for functions:
function myFunction(&$variable) { }
Example:
<?php
class test {
public $testVar;
public function __construct() {
$this->testVar = "1";
}
}
function changeByReference(&$obj) {
$obj->testVar = "2";
}
$instance = new test();
// Prints 1
echo $instance->testVar, PHP_EOL;
changeByReference($instance);
// Prints 2
echo $instance->testVar, PHP_EOL;
Read more about it here: http://php.net/manual/en/language.oop5.references.php
If you want to copy an instance, use clone - php.net/clone
The easiest way to get it is when you know the difference between these: class, object and instance. (I'd explain it more at this point but it would only confuse you more because my english is not accurate enough for now to explain the details enough.)
Related
$a = some object;
$b = another object;
function do_some_stuff() {
$c = $a;
$b->a_property = $c;
}
do_some_stuff();
echo $b->a_property; # Undefined, becuase $c was deleted when the function exited.
When I type $b->a_property = $c, I want the PHP interpereter to do $b->a_property = $a. How do I do that, if I'm unable to assign it to $a directly?
My actual code is:
function setup() {
$playerHeroes = [];
$enemyHeroes = [];
foreach ($entities as $object) {
if ($object->team == "player") {
$playerHeroes[] = $object;
}
else if ($object->team == "enemy") {
$enemyHeroes[] = $object;
}
}
foreach ($playerHeroes as $object) {
$object->target = $enemyHeroes[array_rand($enemyHeroes)];
}
foreach ($enemyHeroes as $object) {
$object->target = $playerHeroes[array_rand($playerHeroes)];
}
}
I sort through a list of entities in the game as either being on the player's team or being on the enemy's team. Each hero must target a hero on the opposing team. When the setup function exits, $object, $playerHeroes and $enemyHeroes get destroyed. The ->target properties are accessed later, and it's null. How do I make it so that when I assign a variable to a reference of an object, it assigns it to the object itself?
EDIT: I want the variable to change when the object changes, so cloning/copying by value is not an option.
In addition to echoing the comments from #IvoP and #mega6382, it's worth noting that the problem you're having is not a "solvable" one per se. That's because what you're running into is fundamental to how objects work in PHP. You can learn all about it at http://ca2.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.
(hat tip to #tomzx)
You'll likely do better to rearchitect your code to deal with this or use a language whose default behavior is call-by-value like C or Java.
Since objects are passed by reference by default now, is there maybe some special case when &$obj would make sense?
Objects use a different reference mechanism. &$object is more a reference of a reference. You can't really compare them both.
See Objects and references:
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.
&$object is something else than $object. I'll give you an example:
foreach ($objects as $object) {
if ($cond) {
$object = new Object(); // This won't affect $objects
}
}
foreach ($objects as &$object) {
if ($cond) {
$object = new Object(); // This will affect $objects
}
}
I won't answer the question if it makes sense, or if there is a need. These are opinion based questions. You can definitely live without the & reference on objects, as you could without objects at all. The existence of two mechanisms is a consequence of PHP's backward compatibility.
There are situations where you add & in front of function name, to return any value as a reference.
To call those function we need to add & in front of object.
If we add & in front of object, then it will return value as reference otherwise it will only return a copy of that variable.
class Fruit() {
protected $intOrderNum = 10;
public function &getOrderNum() {
return $this->intOrderNum;
}
}
class Fruitbox() {
public function TestFruit() {
$objFruit = new Fruit();
echo "Check fruit order num : " . $objFruit->getOrderNum(); // 10
$intOrderNumber = $objFruit->getOrderNum();
$intOrderNumber++;
echo "Check fruit order num : " . $objFruit->getOrderNum(); // 10
$intOrderNumber = &$objFruit->getOrderNum();
$intOrderNumber++;
echo "Check fruit order num : " . $objFruit->getOrderNum(); // 11
}
}
First of all, I apologize that this question is so vague. I can't remember what this is called, or how they work, so it's very difficult to start searching or formulate a good title.
I have two questions wrapped into one:
First:
How are objects converted to other types internally? What is this called?
Example:
$Obj{
$value = 1;
$other = 2;
$more = 3;
}
$myObj = (string)$Obj;
print $myObj; // prints "1, 2, 3" or something like that
Second:
Can this method be used in math? Is there some override function that recognizes when an Object is being used in math?
Example:
$Obj{
$value = 1;
$other = 2;
$more = 3;
}
$result = 4 / $Obj;
print $result; // prints ".66666667" or something similar (sum of all properties)
Update:
I think it might have something to do with serialize(), but I know I've heard of a case where this is done "automatically" without having to call serialize() and it's done in a way that doesn't actually serialize the whole object, it just converts it to a useable value, like my above examples.
Final:
Thanks for #trey for being right about it being casting and to #webbiedave for pointing me to the magic method __toString.
It is casting as you can define the magic method __toString to allow the object to be cast to a string as desired, which will then allow PHP to cast it to an int or float in math.
Take the following example:
class A
{
public $value = 1;
public $other = 2;
public $more = 3;
public function __toString()
{
return (string)($this->value + $this->other + $this->more);
}
}
$obj = new A();
echo 4 / (string)$obj; // outputs 0.66666666666667
It's called type casting when you change an object to a different data type, as for the second part, I'm not entirely sure I understand you, are you trying to type cast during a math function?
it sounds like this may be more along the lines of what you're looking for:
class User
{
public $first_name='John';
public $last_name='Smith';
public function __toString()
{
return "User [first='$this->first_name', last='$this->last_name']";
}
}
$user=new User;
print '<span>'.$user.'</span>';
but I'm unable to find documentation about how to make this work when the object is converted to an interger... I'll update if I do
Within a class I have three arrays (as instance variables) that must all point to the same objects. The first is a numerically indexed array that I get back from some external function. The second array must index the same objects by their name. The third by some other property. This is for fast access via those properties. So the point is: it shouldn't matter which array I use to access an object and modify it.
Yet it can't make it happen. I know about PHP references. I know about java references. I know about C pointers, but I can't make it happen - wherever I try to place the ampersand (=&).
class xxx {
private $objs;
private $objsByName;
public function __construct() {
$this->objs = getObjs();
$this->objsByName = array();
foreach($this->objs as $obj) {
$this->objsByName[$obj->getName()] = $obj;
}
}
}
There is no place here where I have not tried replacing = with =&
I am missing something?
Replace your foreach loop with this one.
for($i = 0 , $n = count($this->objs); $i < $n ; $i ++){
$this->objsByName[$this->objs[$i]->getName()] = &$this->objs[$i];
}
This will solve your problem :)
Thanks!
Hussain.
In PHP, objects are always passed by reference (you cannot copy an object unless you use clone keyword). This code illustrates this:
<?php
class AnObject{
private $name;
public $count=0;
public function __construct($name){
$this->name = $name;
}
public function getName(){
return $this->name;
}
}
function getObjs(){
return array(
new AnObject('One'),
new AnObject('Two'),
new AnObject('Three'),
);
}
class xxx {
private $objs;
private $objsByName;
public function __construct() {
$this->objs = getObjs();
$this->objsByName = array();
foreach($this->objs as $obj) {
$this->objsByName[$obj->getName()] = $obj;
}
}
public function alterAnObjectByName($name){
$this->objsByName[$name]->count++;
}
}
$x = new xxx;
var_dump($x);
$x->alterAnObjectByName('Two');
var_dump($x);
The issue is probably somewhere else.
Mark this part from the php manual:
http://php.net/manual/en/control-structures.foreach.php
Unless the array is referenced, foreach operates on a copy of the specified array and not the array itself. foreach has some side effects on the array pointer. Don't rely on the array pointer during or after the foreach without resetting it.
So your $obj is a copy.
I have not tested Hussain's code, but that looks like a good option :)
PHP always use copy on write functions, chances are that your objects will be same until one is changed.
To avoid errors you should say that one of your array contains the data and that thee others are just indexes.
Use the index to find the id, and use tha data array, indexed by id, to read and alter data.
In PHP5 you can add & to the as variable to make the foreach loop not clone the object:
foreach($this->objs as &$obj) {
$this->objsByName[$obj->getName()] = &$obj;
}
Reference: http://php.net/manual/en/control-structures.foreach.php
p/s: I'm not sure if the & inside the foreach body is needed or not but just add it to be safe. Or you can try removing it to see if it's needed or not.
Is there any way to emulate a structure class in PHP? ie a class which passes by value and not by reference, so it can still be type hinted...
And if so, what different techniques could be used? What's the best technique?
If this is possible you could obviously create a fully type safe layer for PHP, are there such layers? Has anyone had any experience with this?
Objects are always passed by reference. The only way to make them pass as a copy is to explicitly use the clone keyword (yes, everywhere).
My recommendation would be to use an array, which are value types and thus always copied. Since you can use it as an associative array (eg string -> value), it might as well be an object. The downside is, of course, you can't use methods (but that's like a struct so you may be happy with this). There is no way to enforce type safety, however.
But with all your requirements it sounds like PHP isn't your kind of language, to be honest.
I think the easiest way is to do it like java does - have your value classes be immutable, and let all "modification" methods return a new object instead.
I don't think you can achieve that goal, only with PHP code.
You have no control on how PHP function handle parameters, and I don't see how you could make sure everything is handled the way you want, without having to change the (lower-level) code in the PHP binary and modules.
It would be pretty cool, though :)
I was playing around with anonymous's suggestion to make any mutations of the object return a new object, and this works, but it's awkward.
<?php
class FruityEnum {
private $valid = array("apple", "banana", "cantaloupe");
private $value;
function __construct($val) {
if (in_array($val, $this->valid)) {
$this->value = $val;
} else {
throw new Exception("Invalid value");
}
}
function __set($var, $val) {
throw new Exception("Use set()!!");
}
function set(FruityEnum &$obj, $val) {
$obj = new FruityEnum($val);
}
function __get($var) { //everything returns the value...
return $this->value;
}
function __toString() {
return $this->value;
}
}
And now to test it:
function mutate(FruityEnum $obj) { // type hinting!
$obj->set($obj, 'banana');
return $obj;
}
$x = new FruityEnum('apple');
echo $x; // "apple"
$y = mutate($x);
echo $x // still "apple"
. $y // "banana"
It works, but you have to use a strange way to change the object:
$obj->set($obj, 'foo');
The only other way I could think to do it would be to use the __set() method, but that was even worse. You had to do this, which is bloody confusing.
$obj = $obj->blah = 'foo';
In the end, it's probably easier to make the variables private and provide no mutators, so the only way to change a variable's "enum" value would be to create a new one:
echo $obj; // "banana"
$obj = new FruityEnum("cantaloupe");