$a = true;
1,new test($a);
2,new test(true);
Is there a difference between them(1,2), if have,what's of it? thank you,.
Well the other one uses a variable and the other one doesn't. This causes a fatal error in this case:
class test {
public function __construct( &$a )
{
}
}
$a = true;
new test($a);
new test(true); //Fatal error because this cannot be passed by reference
Strictly speaking, it depends on how test is defined.
If test is defined such that input parameters are passed by reference, then 2 would raise a fatal error, since true is a literal value.
Also, test could have side effects that mean that the order in which you execute lines 1 and 2 matter.
It depends on the constructor of the test class. In a regular pass-by-value constructor they are exactly the same:
class test {
public $b;
function __construct($a) { $this->b = $a; }
}
Here, $obj->b will be true for both your statements, as expected.
If, on the other hand, you are passing by reference you may get different results if you change the global $a later on. Example:
class test {
public $b;
function __construct( &$a ) { $this->b = &$a; }
}
$a = true;
$obj = new test($a);
$a = false;
$obj->b will be false in this case because it is a reference to $a! With references, you can also do it the other way around, changing $a from within the constructor:
class test {
function __construct( &$a ) { $a = false; }
}
$a = true;
$obj = new test($a);
$a will now be false even in the global scope!
In addition, new test(true) is not possible to do when passing by reference because you cannot have a reference to a literal value, only to other variables.
Related
First, a quote from the ole' manual on ArrayAccess::offsetSet():
This function is not called in assignments by reference and otherwise indirect changes to array dimensions overloaded with ArrayAccess (indirect in the sense they are made not by changing the dimension directly, but by changing a sub-dimension or sub-property or assigning the array dimension by reference to another variable). Instead, ArrayAccess::offsetGet() is called. The operation will only be successful if that method returns by reference, which is only possible since PHP 5.3.4.
I'm a bit confused by this. It appears that this suggests that (as of 5.3.4) one can define offsetGet() to return by reference in an implementing class, thus handling assignments by reference.
So, now a test snippet:
(Disregard the absence of validation and isset() checking)
class Test implements ArrayAccess
{
protected $data = array();
public function &offsetGet($key)
{
return $this->data[$key];
}
public function offsetSet($key, $value)
{
$this->data[$key] = $value;
}
public function offsetExists($key) { /* ... */ }
public function offsetUnset($key) { /* ... */ }
}
$test = new Test();
$test['foo'] = 'bar';
$test['foo'] = &$bar; // Fatal error: Cannot assign by reference to
// overloaded object in
var_dump($test, $bar);
Ok, so that doesn't work. Then what does this manual note refer to?
Reason
I'd like to permit assignment by reference via the array operator to an object implementing ArrayAccess, as the example snippet shows. I've investigated this before, and I didn't think it was possible, but having come back to this due to uncertainty, I (re-)discovered this mention in the manual. Now I'm just confused.
Update: As I hit Post Your Question, I realized that this is likely just referring to assignment by reference to another variable, such as $bar = &$test['foo'];. If that's the case, then apologies; though knowing how, if it is at all possible, to assign by reference to the overloaded object would be great.
Further elaboration: What it all comes down to, is I would like to have the following method aliases:
isset($obj[$key]); // $obj->has_data($key);
$value = $obj[$key]; // $obj->get_data($key);
$obj[$key] = $value; // $obj->set_data($key, $value);
$obj[$key] = &$variable; // $obj->bind_data($key, $variable);
// also, flipping the operands is a syntactic alternative
$variable = &$obj[$key]; // $obj->bind_data($key, $variable);
unset($obj[$key]); // $obj->remove_data($key);
As far as has, get, set, and remove go, they're no problem with the supported methods of ArrayAccess. The binding functionality is where I'm at a loss, and am beginning to accept that the limitations of ArrayAccess and PHP are simply prohibitive of this.
What the manual is referring to are so called "indirect modifications". Consider the following script:
$array = new ArrayObject;
$array['foo'] = array();
$array['foo']['bar'] = 'foobar';
In the above script $array['foo'] = array(); will trigger a offsetSet('foo', array()). $array['foo']['bar'] = 'foobar'; on the other hand will trigger a offsetGet('foo'). Why so? The last line will be evaluated roughly like this under the hood:
$tmp =& $array['foo'];
$tmp['bar'] = 'foobar';
So $array['foo'] is first fetched by ref and then modified. If your offsetGet returns by ref this will succeed. If not you'll get some indirect modification error.
What you want on the other hand is the exact opposite: Not fetch a value by reference, but assign it. This would theoretically require a signature of offsetSet($key, &$value), but practically this is just not possible.
By the way, references are hard to grasp. You'll get lots of non-obvious behavior and this is especially true for array item references (those have some special rules). I'd recommend you to just avoid them altogether.
This does not work with ArrayAccess, you could add yourself a public function that allows you to set a reference to an offset (sure, this looks different to using array syntax, so it's not really a sufficient answer):
class Test implements ArrayAccess{
protected $_data = array();
public function &offsetGet($key){
return $this->_data[$key];
}
...
public function offsetSetReference($key, &$value)
{
$this->_data[$key] = &$value;
}
}
$test = new Test();
$test['foo'] = $var = 'bar';
$test->offsetSetReference('bar', $var);
$var = 'foo';
echo $test['bar']; # foo
$alias = &$test['bar'];
$alias = 'hello :)';
echo $var; # hello :)
Probably such a function was forgotten when ArrayAccess was first implemented.
Edit: Pass it as "a reference assignment":
class ArrayAccessReferenceAssignment
{
private $reference;
public function __construct(&$reference)
{
$this->reference = &$reference;
}
public function &getReference()
{
$reference = &$this->reference;
return $reference;
}
}
class Test implements ArrayAccess{
...
public function offsetSet($key, $value){
if ($value instanceof ArrayAccessReferenceAssignment)
{
$this->offsetSetReference($key, $value->getReference());
}
else
{
$this->_data[$key] = $value;
}
}
Which then works flawlessly because you implemented it. That's probably more nicely interfacing than the more explicit offsetSetReference variant above:
$test = new Test();
$test['foo'] = $var = 'bar';
$test['bar'] = new ArrayAccessReferenceAssignment($var);
$var = 'foo';
echo $test['bar']; # foo
$alias = &$test['bar'];
$alias = 'hello :)';
echo $var; # hello :)
For some reason this function won't return the value ciao:
$a = "ciao";
function a() {
return $a;
}
I have no idea why.
Functions can only return variables they have in their local space, called scope:
$a = "ciao";
function a() {
$a = 'hello`;
return $a;
}
Will return hello, because within a(), $a is a variable of it's own. If you need a variable within the function, pass it as parameter:
$a = "ciao";
function a($a) {
return $a;
}
echo a($a); # "ciao"
BTW, if you enable NOTICES to be reported (error_reporting(-1);), PHP would have given you notice that return $a in your original code was using a undefined variable.
In PHP, functions don't have access to global variables. Use global $a in body of the function or pass the value of $a as parameter.
$a is not in scope within the function.
PHP does not work with a closure like block scope that JS works with for instance, if you wish to access an external variable in a function, you must pass it in which is sensible, or use global to make it available, which is frowned on.
$a = "ciao";
function a() {
global $a;
return $a;
}
or with a closure style in PHP5.3+
function a() use ($a) {
return $a;
}
echo 'test';
class createdclass {
public $name;
}
class testc {
function &testm(){
$myvar =& new createdclass();
return $myvar;
}
}
$testo = new testc();
$a =& $testo->testm();
$a->name = 'Douglas';
$b =& $testo->testm();
$b->name = 'Scott';
echo $a->name;
echo $b->name;
myvar is a reference to an object
a and b are references to the same object
I changed a, then I changed b, but a wasn't changed by b
Why?
In your code each call to testm() creates a new instance of createdclass. So $a and $b aren't the same object.
Ok, first off, you shouldn't use $myvar =& new .... It's a deprecated syntax and is completely unnecessary (Since there's nothing to reference to)...
Secondly, you don't need the =& operator in the lines $a =& $testo->testm(). The fact that the method returns a reference is good enough. Not to mention that objects are passed by reference by default anyway, so you really don't need those lines anyway. I put them in the method signature function &foo() mainly for readability (to show that we're expecting the return to be a reference)...
Third, the problem is what you're referencing. References bind to a variable. When you leave the scope, since $myvar is a local variable (and as such is garbage collected -- it is deleted -- when the method exits), the bound reference disappears. So if you want that to work, you need to persist that variable.
Here's one example that works:
class testc {
protected $createdclass = null;
public function __construct() {
$this->createdclass = new CreatedClass();
}
public function &testm() {
return $this->createdclass;
}
}
$tester = new testc;
$a = $tester->testm();
$a->name = 'foo';
$b = $tester->testm();
echo $b->name; //displays "foo"...
in your example, a and b do not reference the same object because you create a new one in the testm() function.
here is a short example that might clarify things a little:
<?php
class createdclass {
public $name;
}
$a = new createdclass();
$a->name = 'Douglas';
// make $b reference the same as $a, i.e. let $p point to the same content as $a
$b = &$a;
$b = new createdclass();
$b->name = 'Scott';
echo $a->name;
echo $b->name;
?>
this will output ScottScott
if you want to learn more about references in php i'd recommend to read References Explained in the php manual
Can one please explain with example what does $obj->$a()->$b mean? I've used PHP OOP quite a long time and have seen in some places this structure and not just this $obj->$a(); In what cases should I use it?
$a = 'someMethod';
$b = 'someProperty';
$obj->$a()->$b;
is equal to:
$obj->someMethod()->someProperty;
Read more about variable variables
$a is the name to a method. Hence, if $a = "myFunc", this is equivalent to:
$obj->myFunc()->$b;
$b appears to be a reference to a property. The method appears to return an object, so, if $b = "myProp", we can modify this to be:
$obj->myFunc()->myprop;
This is really poor form for understandability.
It means that $a() returns an object, and that $b is a member of the object $a() returns.
This is called method chaining when each method returns the original object, so various methods of the same object can be called without having to repeatedly specify $obj-> before each invocation.
The actual term is Fluent Interface, as stated is returns the original object, heres a full example class
Class Fluent {
public $var1;
public $var2;
function setVar1($value) {
$this->var1 = $value;
return $this;
}
function setVar2($value) {
$this->var2 = $value;
return $this;
}
function getVars() {
echo $this->var1 . $this->var2;
}
}
$fluent = new Fluent();
$fluent->setVar1("Foo")->setVar2("Bar")->getVars();
Which will obviously return "FooBar".
HTH
In the minutes php6 developer meeting, i came across a point where it said that call-time-pass-by-reference is no longer there in PHP6. For example, following both are incorrect for PHP6:
<?php
$foo =& new StdClass();
?>
<?php
function &foo()
{
return new StdClass();
}
$f = foo();
?>
If we can't use something like this in PHP6:
$foo =& new StdClass();
What is the alternative to that, is there any way to mimic that?
EDIT:
Ans what about variables in PHP6, can we do that for variables eg:
$this->data =& $_SESSION;
You don't pass parameters by reference, but you still can/must declare your function/method as receiving parameters by reference.
i.e., you don't do this (passing parameters by reference) :
my_function(& $my_var);
function my_function($a) {
// ...
}
But you can do this (declaring the function as receiving parameters by reference) :
my_function($my_var);
function my_function(& $a) {
// ...
}
And... The code examples you gave are not related to call-time-pass-by-reference, but are related to return-by-reference.
For that second thing, what if you just remove the & ? The instance of the object that's been created inside the function will be returned, and you'll still be able to work with it, won't you ?
Objects are always passed by reference since PHP5, so this:
$foo =& new StdClass();
is the same as:
$foo = new StdClass();
.
Ans what about variables in PHP6, can we do that for variables eg:
$this->data =& $_SESSION;
I see no reason why not -- PHP will not be removing references, as they are far too useful. There is no other way to create a reference to a variable.
Call-time pass-by-reference has been deprecated for a while. It just means that instead of doing this:
function foo($a) { return ++$a; }
foo(&$my_a);
You would do this:
function foo(&$a) { return ++$a; }
foo($my_a);
This leads to a much cleaner and easier-to-understand programming style, and it also ensures that variables are always referenced when you expect, and not when you don't.
Dealing with objects is a special case -- they are always passed by reference, and the only way to simulate pass-by-value is to use clone:
$a = new StdClass();
$a->foo = 'bar';
$b = $a;
$a->foo = 'qux';
// $b->foo is 'qux' too
$a = new StdClass();
$a->foo = 'bar';
$b = clone $a;
$a->foo = 'qux';
// $a->foo is 'qux' but $b->foo is 'bar'
Hope that helps!