How can a variable be overridden if not passed by reference? - php

This is more a discussion topic than a 'help me' question.
I have just come across a strange problem. A variable (object) is being overridden when not passed anywhere.
Example;
var_dump($node->title);
$entity = $server->pull($item);
var_dump($node->title);
The two var_dumps display two different values.
$item is an unrelated string.
At this point, I dont think the contents of the 'pull()' method are relevant - I'm not curious about WHERE this is being overridden, I'm curious about HOW its being overridden.
How can PHP alter the variable unless it has been passed to the method?
There are no references in my function, node is passed direct;
function my_function($node) {
Even if I make a clone of my object
Even if I rename the var
$my_node = $node;
The cloned object is still overridden.
The renamed object is still overridden (ie, its not related to the name.)
I would love to know how this is possible. How can pull() (or associated methods) over ride a variable that they have not been given?
I need to reiterate; I don't care where the value is being changed, I care about how it is being changed when it isn't passed to the method.

Doing this:
$my_node = $node;
Doesn't actually clone the object. If you change $my_node, it will still be reflected in $node because both are just pointers to the object, see http://www.php.net/manual/en/language.oop5.references.php
So, possibly something is acting on $node or a copy of $node and therefore changing the object.
See this example from the link above:
class A {
public $foo = 1;
}
$a = new A;
$b = $a; // $a and $b are copies of the same identifier
// ($a) = ($b) = <id>
$b->foo = 2;
echo $a->foo."\n";
Even though we did $b = $a, if we change the property on $b, it changes the property on $a.

So, an important part of OO in PHP5 is that objects are always passed by reference, which I'd totally forgotten.
This makes it more than likely that the method being called has access to my object via a reference and makes this question seem silly. :)
http://php.net/manual/en/language.oop5.references.php

Related

PHP 5 | Objects Passed by Reference / Value vs Copy on Write | When added as class property

class MyClass {
// data going in
private $in;
// data going out
public $out;
// constructor
public function __construct($obj0, $obj1) {
$this->in = array('obj0' => $obj0, 'obj1' => $obj1);
$this->out = array();
}
// method
public function process() {
foreach ($this->in as $key => $value) {
$this->out[$key] = $value;
}
}
}
PRIOR KNOWLEDGE
Objects are passed by reference by default, but there are edge cases
PHP uses copy on write
QUESTION
Am I doubling the memory usage now since I have the objects in MyClass->in and MyClass->out?
If i use a method of obj0 or obj1 am I forcing a copy on write, even if it does not change any of the values of the properties of these objects?
Is the object still passed by reference when added as a class property?
As the manual page you link to says, the statement "objects are passed by reference" is not a good description of what is happening. A better way to think of it is that the "value" of an object is a handle, pointer, or address to something that exists in a different space.
Whether you assign it to an object property, an array element, or a normal variable, this "value" remains the same, and changes to the object are visible wherever you look at them. Copying the value uses a few bytes (the size of the pointer) but doesn't duplicate the memory of the object itself.
As a final clarification, the "write" in "copy-on-write" refers to modification of an existing value, after copying it from one place to another. So writing $foo = $bar, where $bar is an array, will not duplicate the memory used by the array, but subsequently writing $foo[0]=1; or $bar[0]=1 will, because the two copies need to be distinguished. This doesn't actually come into play in your example; if it did, it would be just the "object pointer" that was copied, so very little extra memory would be needed.

Is it possible to replace an object but keep all the same references to it

Is possible in PHP to replace an object A of class1 with an object B of class2 so that all references to A are now redirected to B.
The goal is to avoid tracking all the references to A, which may exist in a complex system of relations, and replacing the object one by one.
A bit more context: I need object A to be a temporary stand-in during parsing of an svg file, representing a <use> tag, to be replaced by the actual instance referred to by the <use> tag, once the file is parsed. The problem is that <use> may refer to a tag that has not been parsed yet, and <use> tag may refer to object of different geometric type.
Here is a (generic) example of the behavior I want:
$A = new Class1();
$Array_1["name1"] = $A;
...
$Array_n["namen"] = $A;
$object_1->field_1 = $A;
...
$object_n->field_n = $A;
$B = new Class2(); // In my case, $B is assigned to a field of $A, and both extend the same abstract class.
TELL_PHP_ENGINE_TO_REPLACE($A, $B); //Needed operation
Note that this operation must happen at the level of the php engine, by changing the the handle and handler fields, handler table, (etc.) related to the zval structure.
This may be impossible currently, but I want to check before I invest time writing a function to do all replacements manually. Also, a manual function will have to be updated every time I modify the architecture.

PHP: How To Create A Dynamic Variable Within a Class

Alright so I think this may be extremely basic, but it has me stumped nonetheless. Before I get to my question, let me demonstrate the concept my question is based on with this working example:
<?php
$a = 'Stack';
$b = $a.' Overflow';
echo $b; // Result: "Stack Overflow"
?>
In the above example, $b is defined as the combination of $a and ' Overflow'.
Now, let's assume I want to do the same thing as above except I don't want to use global variables. I want to use classes. This is how I have attempted to achieve that:
<?php
class ClassName {
public $a = 'Stack';
public $b = $this->a.' Overflow'; // This gives me: "Parse error: syntax error, unexpected '$this'"
}
$instantiate = new ClassName;
echo $instantiate->$b; // Desired result: "Stack Overflow"
?>
As stated, this attempt results in an error. To me, this attempt seems logical, but I guess PHP doesn't think so.
Question: Is this possible, and if so, how do I go about achieving the desired result? Also, if you could explain why my attempt has failed (logically), that would be a bonus.
I've searched and researched for hours on end trying to find an answer or figure this out on my own, but for the life of me, I cannot find anyone or anything that even touches on this (including other Stack Overflow threads). I can't even find anywhere saying it's impossible or anything of the sort either.
I'm a PHP novice, so I may need more explanation than others, but any kind of help or general guidance would be much appreciated. Thank you.
You cannot use $this when defining the class because it refers to a concrete object context which becomes available after instantiation. You can use a constructor for that kinds of stuff.
class ClassName
{
public $a = 'Stack';
public $b = "";
function __construct()
{
$this->b = $this->a.' Overflow';
}
}
$instantiate = new ClassName;
echo $instantiate->b;
Quoting from the PHP Docs about defining class properties
They are defined by using one of the keywords public, protected, or private, followed by a normal variable declaration. This declaration may include an initialization, but this initialization must be a constant value -- that is, it must be able to be evaluated at compile time and must not depend on run-time information in order to be evaluated.
(my emphasis)
Concatenation cannot be evaluated at compile time, only at run time.
If you need to do this, then define the initial value for the property in the constructor
This would throw a syntax error because you are using $this in a scope that it is not allowed to:
The pseudo-variable $this is available inside any class method when that method is called from within an object context. $this is a reference to the calling object (usually the object to which the method belongs, but possibly another object, if the method is called statically from the context of a secondary object).
This means, even if you wanted you won't be able to do what you want to do because of such restriction. This is a common restriction that is in many programming languages, properties have to be initialized to static values. To solve your issue you can do any of the following:
In your constructor, create those variables.
Create the second variable on its own, and create a method that concatenates the two.
Create a magic method to do it for you.
The same restriction holds true for static class properties.

What is the point of having $this and self:: in PHP?

Why does PHP require you to explicitly write $this? I would understand if you had to use $this here:
function foo($bar) {
$this->bar = $bar;
}
But you must write it explicitly in verbose code that looks like this:
$this->var3 = globalFun($this->var, $this->var2[$this->anotherVar], $this->method());
as opposed to:
$var3 = globaFun($var, $var2[$anotherVar], method());
So what is the point of $this?
Additional Bonus Question:
Why do we have to differentiate static references and instances? Why do we need:
static function getValue() {
return self::value;
}
Can't PHP find out at runtime if the variable/method in question is static? Now if I want to change a method from static to non-static, I have to replace all those self:: with $this-> (and vice-versa).
Wouldn't it be better if we had a $this that behaves like it does in Java?
Since this was re-opened, I'll post here my answer, as promised.
TL;DR version If it were not required to qualify a member access, there would be not only performance penalties, but the same line of code could simultaneously signify a field access and a local variable access, depending on the code path.
Full version
In PHP, there's always one symbol table active at the table. This is either the global symbol table or a function/method local symbol table (which by the way, are lazily built). Superglobals and optimizations like compiled variables aside, when a variable $var is requested, it is looked up in the current symbol table. Since the object properties live not on the symbol tables, but instead on either in the objects (the instance properties) or the structure associated the class (the static properties), a lookup for $var can never return a property.
To bring a given variable to the function scope, you must explicitly signal your intention by creating a reference. Examples:
$myglobal = 7;
class A {
private $prop;
public function meth() {
global $myglobal; //bring a global to the current scope
$prop =& $this->prop; //brings a property to the current scope
$local = 4;
$lambda = function () use (&$local) { };
}
}
Obviously, this is just a more sophisticated way to phrase what currently happens. The question is why this behavior?
After all, in Java we only have to type this.prop when there's a local variable called prop hiding the property. Why is this not a good option for PHP?
I can think of several reasons.
The object properties are determined at runtime
PHP has something called "dynamic properties". You can assign new properties to objects at runtime. In fact given two objects of the same class, one can have a given property $a and while the other doesn't. Example:
$obj1 = new stdClass();
$obj2 = new stdClass();
$obj1->a = 7;
In PHP, the defined local variables are determined at runtime
Variables do not have to be declared; consequently, depending on the code path, at some point a variable may or may not be defined. To add insult to the injury, we also have the monster called "variable variables". Example:
class A {
private $g = 3;
public function func($varname) {
if (rand(1,2) == 1) {
$g = 4; //no block scope; the scope is the function's
}
$$varname = 5; //god knows what's happening here
//if local variables hid properties, we'd have trouble
}
}
In Java, a given identifier may also represent, inside the same function, a local variable and a property, but:
Not within the same block (in PHP, all blocks in a function share exactly the same scope).
You get a warning if you're hiding a property.
Crucially, in any given occurrence of an identifier, it's either a property or a local variable, it can't sometimes be one and other times the other.
Consequences
Owing to these facts, it would be impossible to determine at compile time if $var referred to a local variable or to a property. Consequently:
At runtime, every time a variable occurred, it would have to looked up first in the local symbol table, then in the instance properties table, and finally in the static properties list, or any other order (since there can't be an instance and a static property with the same name and static properties need to be declared, there would be some optimization potential here, but the point stands). This means a symbol would have, in the worst case, would have to be looked up in three different places. This is bad from a performance perspective.
A given symbol occurrence could mean different things on different occasions. This is a recipe for disaster.
Okay, so let's remove the need for writing $this everywhere. Take a look at this situation:
class Foo {
public function setBar($value) {
$bar = $value;
}
}
$foo = new Foo();
$foo->setBar('some value');
Is $bar a local variable or a member of $foo?
There has to be some differentiation. They could have allowed declaration of local variables with the var keyword, but that would not have been backwards-compatible and would have been very confusing to people upgrading from older versions of PHP.
Same thing applies to self::. How does the interpreter know whether the function you wanted to call is global or specific to the class?
PHP was not OOP.
Now it is, but with side effects.
Actually, I know people who use this. in Java even where unnecessary because they feel it creates clearer code ;) I don't have a really definite answer, but I guess that, internally, getting $var would always have to be translated to $this->var. So it's not like someone intentionally made things more complicated by forcing us to do $this->var, but just decided to not implement the $var shortcut. If that helps in any way, I don't know ;)

PHP 5 - serializing objects and storing their relationship

I am writing a fairly complex PHP applications where a single user action can trigger changes in many other sub-systems, and I'm contemplating using an observer pattern. However, I am wondering if I have to re-create all the objects involved.
Is it possible to while serializing objects to store their relationships? For example
$equipmentHandler = new EquipmentHandler();
$character = new Character();
$character->subscribeOnEquipmentChanged($equipmentHandler);
$_SESSION['character'] = serialize($character);
$_SESSION['subscriber'] = serialize($equipmentHandler);
Will the relationship be preserved after unserializing? Or do I have do lump them all into one object?
$cache['character'] = $character;
$cache['subscriber'] = $equipmentHandler;
$_SESSION['cache'] = serialize($cache);
Any advice would be appreciated.
(PS. The character data requires many DB requests to create and I am thinking of storing it by doing a write to cache and DB, but only read from cache policy, so it will be serialized anyway)
A relation will be kept, but it will be different than you expect. When you serialize two instances of Character that both refer to the same EquipmentHandler, you're going to get two separate instances of this EquipmentHandler, instead of the single one you expected. As this example illustrates:
<?php
echo "BEFORE SERIALIZE:\n";
class A { }
class B { }
$a = new A;
$b = new B;
$a -> b = $b;
$a2 = new A;
$a2 -> b = $b;
var_dump($a->b);
var_dump($a2->b);
echo "AFTER SERIALIZE:\n";
$a3 = unserialize(serialize($a));
$a4 = unserialize(serialize($a2));
var_dump($a3->b);
var_dump($a4->b);
The output of this is:
BEFORE SERIALIZE:
object(B)#2 (0) {
}
object(B)#2 (0) {
}
AFTER SERIALIZE:
object(B)#5 (0) {
}
object(B)#7 (0) {
}
Look for the number after the pound. This refers to the object ID within PHP. Before serializing both $a->b and $a2->b refer to an object with object ID #2: the same instance. But after the serialization they refer to object IDs #5 and #7: different instances.
This may, or may not, be a problem for you.
To restore the connection to one single B object, you're going to have to get a little tricky. You could use the __sleep() handler in A to flatten the actual reference to an INSTANCE of B to just a mentioning of B: "I had a reference to B". Then implement the __wakeup() handler using that mentioning of a B instance in A to acquire a single instance of a new B object.
BTW. The PHP session extension already does serializing automatically, no need for you to pre-serialize it yourself :)
According to the manual of the serialize function:
The value to be serialized. serialize() handles all types, except the resource-type. 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.
When serializing objects, PHP will attempt to call the member function __sleep prior to serialization. This is to allow the object to do any last minute clean-up, etc. prior to being serialized. Likewise, when the object is restored using unserialize() the __wakeup member function is called.
So I guess it is not possible unless you do something smart in the _sleep and _wakeup
Your actually have the solution in your question! More complex cases might need to make use of __sleep and __wakeup ... but given the information you provided, all you have to do is -- as you suggest -- "lump them all into one object".
Explanation
In an answer to a similar question, I said:
Serialization will maintain "relative" references. (Technically, there is no such thing as a relative reference in PHP, but its a good way to conceptualize it.)
If you collect your referenced and referencing variables in an array, serializing the array will save the reference relationship. It won't maintain the original reference, but will automatically recreate it in the context of the new array returned by unserialize. ... It works the same way for internal references in objects.
Example
// example objects
class A {}
class B {}
$a = new A();
$b = new B();
$b->a = $a;
// collect referenced and referencing objects in array
$cache = array( 'a' => $a, 'b' => $b );
// flatten and recreate cache (represents data stored & retrieved from db)
$cached = unserialize( serialize( $cache ) );
// overwrite local variables from cache
extract( $cached, EXTR_OVERWRITE );
Then, if you do var_dump( $a ); var_dump( $b->a );, notice in the output below how the object IDs for $a and for $b->a are both '3', indicating they both refer to the same instance of A.
object(A)#3 (0) {
}
object(A)#3 (0) {
}

Categories