Return reference to parameter - php

is any way to return reference at class parameter or global variable? I try this
class test{
public static $var;
public static function get(&$ref){
$ref = self::$var;
}
}
test::get($ref);
$ref = 'test';
var_dump(test::$var);
it's a basic example, i know, then this example can be use another way, but i need to keep principle
this is my function, where is problem with reference to variable
class mySession{
public static function issetKeys(){
$keys = func_get_args();
$session = &$_SESSION;
$c = 0;
if (is_array($keys)){
foreach ($keys as $val){
if (isset($session[$val])){
$session = &$session[$val];
$c++;
}
else break;
}
}
return $c == count($keys);
}
public static function &get(){
$keys = func_get_args();
$session = &$_SESSION;
if (is_array($keys)){
foreach ($keys as $val){
if (!isset($session[$val])) $session[$val] = Array();
$session = &$session[$val];
}
}
return $session;
}
}
function getValue(){
if (!mySession::issetKeys('p1', 'p2')){
$session = mySession::get('p1', 'p2');
$session = 'string';
}
return mySession::get('p1', 'p2');
}
print_r($_SESSION);
but no variable save in to $_SESSION

No, and why would you ever do such a thing?
If you want to access a public static variable, you'll just write
test::$var = "Hello there handsome";
In the case above you're not passing the reference of this::$var onto $ref, you're letting a reference to $ref contain the value of this::$var. PHP is not C, and references should generally be avoided when not necessary in PHP.

In the general case, a parameter only exists for the duration of the function to which it was passed; when the function returns, the call stack is unwound and the parameter disappears, which means that any reference to a variable that lived in the call stack will no longer be valid (since it points to memory which has been freed and now quite possibly contains something else entirely.)
That said, it is possible to return a reference to a parameter, although this will not work if you try to return a reference to a variable that actually lives in the stack (that is, one that was not passed by reference):
$myGlobal = 10;
// Note the & before the function name
function &returnReference(&$x) {
return $x;
}
// Note the & before the function call
$someValue = &derp($myGlobal);
echo $myGlobal;
$someValue = 0;
echo $myGlobal;

Related

Define local variables in another function

I've been looking at Magic Constants and Reflection in PHP to see if the following is possible:
function one() {
setVars();
// $node would be in scope
print_r($node);
}
function setVars() {
return $node = '2';
}
Is this a classical programming concept? Reflection seems to be the closest thing. Basically, I just want to define variables in a different scope (the scope/context of the function that called the setVars() function).
For more than one variable try to store them in a array and return the array.
function one() {
$nodeArray = setVars();
print_r($nodeArray );
}
function setVars() {
$nodeArray[] = 1;
$nodeArray[] = 1;
$nodeArray[] = 1;
return $nodeArray;
}
Take a look at extract().
function one() {
$vars = setVars();
extract($vars);
// $node1 would be in scope
print_r($node1);
}
function setVars() {
$node1 = '1';
$node2 = '2';
return compact('node1','node2');
}
It should be said, although this is possible, it often leads to terrible architecture and problems down the line.

Pass variable number of params without call_user_func_array()

I have a code problem which stems from the fact that I am using certain libraries of code I cannot change.
I use the following code to pass execution of any undefined methods to another class, and it works fine but it seems like a waste doubling up.
Any suggestions?
Basically I want to know if it's possible to pass an unknown number of parameters to a method (without using call_user_func_array(), just in case they need to be passed by reference). I am not asking how to use func_get_args(), rather the reverse.
Or should I just allow for a few more arguments in the first logic path (the list() code)?
class Foo {
__construct() {
$this->external = new ClassThatIHaveNoControlOver();
}
function bar($name) {
return 'Hi '.$name;
}
function __call($method, $arguments) {
if (count($arguments) < 3) {
// call_user_func_array won't pass by reference, as required by
// ClassThatIHaveNoControlOver->foobar(), so calling the function
// directly for up to 2 arguments, as I know that foobar() will only
// take 2 arguments
list($first, $second) = $arguments + Array(null, null);
return $this->external->$method($first, $second);
} else {
return call_user_func_array(array($this->external, $method), $arguments);
}
}
}
$foo = new Foo();
$firstName = 'Bob';
$lastName = 'Brown';
echo $foo->bar($firstName); // returns Hi Bob as expected
echo $foo->foobar($firstName, $lastName); // returns whatever
// ClassThatIHaveNoControlOver()->foobar() is meant to return
EDIT
Just to clarify, I know I can use this method to rejig the parameters as references, but that would mean passing everything as a reference, even if the method didn't require it - something I was trying to avoid, but seems unlikely at the moment.
As commented in the thread question post's comments this is an example and not necessarily (likely) best practice.
//Some vars
$foo = "shoe";
$bar = "bucket";
//Array of references
$arr = Array(&$foo, &$bar);
//Show that changing variable value affects array content
$foo = "water";
echo $arr[0];
//Sample function
function fooBar($a)
{
$a[0] = "fire";
}
//Call sample function
call_user_func("fooBar",$arr);
//Show that function changes both array contents and variable value by reference
echo $arr[0];
echo $foo;
Expanding a bit on the discussion, again not the most industry standard approach but it'll do the job.
function pushRefOnArray(&$arr, &$var, $key = false)
{
if(isset($key))
$arr[$key] = &$var;
else
$arr[] = &$var;
}
Essentially you can dynamically build your array and call pushRefToArray() any time you need to pass an item to be passed as reference rather than by value.
You could use something like this:
public function __call($method, $params = array()) {
switch (count($params)) {
case 0:
return $this->external->{$method}();
case 1:
return $this->external->{$method}($params[0]);
case 2:
return $this->external->{$method}($params[0], $params[1]);
case 3:
return $this->external->{$method}($params[0], $params[1], $params[2]);
default:
return call_user_func_array(array(&this->external, $method), $params);
}
}

ArrayAccess in PHP -- assigning to offset by reference

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 :)

Store dynamic number of arguments by reference in object, to be changed later

Ok I've got a unique problem. I have a function being called, and I need this to work with how it's already being called. I've put together some code that shows as close as I can get to the answer:
class some_class {
private $_some_stuff = array();
public function some_method()
{
$args = func_get_args();
foreach($args as &$name)
{
$this->_some_stuff[] =& $name;
}
}
public function other_method()
{
for ($i = 0; $i < count($this->_some_stuff); $i++)
{
$this->_some_stuff[$i] = 'somevalue';
}
}
}
$some_object = new some_class();
$one = 'firstever';
$two = 'secondever';
$some_object->some_method(&$one, &$two);
$some_object->other_method(&$one, &$two);
echo $one;
echo '<br>...<br>';
echo $two;
I need $one and $two at the end to output 'somevalue'. If it's not clear, I need to be able to pass by reference some values into one method of an object and later have a separate method of the object still be able to access those values;
I believe that this works:
public function some_method()
{
$backtrace = debug_backtrace();
$args = $backtrace[0]['args'];
foreach($args as &$name)
{
$this->_some_stuff[] =& $name;
}
}
but as others have said, "how it's already being called" is call-time pass by reference, which is deprecated
Call-time pass-by-reference has been deprecated, because the result is a hard code.
Try to reorganize your application
You can't use func_get_args() because as the manual says it does not return by reference:
Returns an array in which each element is a copy of the corresponding member of the current user-defined function's argument list.
It appears from testing that func_get_arg() has the same behavior.
The only way to instruct PHP to provide an argument by reference to a function is with the & in the function argument list. Since you don't have an argument list, what you want is impossible.
It's also hideous! Pass-by-reference in PHP is fraught with problems and should be avoided.
However, if you are willing to change your some_method() signature, you can do the following:
class some_class {
private $_some_stuff = array();
public function some_method(&$args) // notice we accept a single arg by reference
{
foreach ($args as &$arg) {
$this->_some_stuff[] =& $arg;
}
}
public function other_method()
{
for ($i = 0; $i < count($this->_some_stuff); $i++)
{
$this->_some_stuff[$i] = 'somevalue';
}
}
}
$some_object = new some_class();
$one = 'firstever';
$two = 'secondever';
// now whenever you call this method, use this EXACT PATTERN:
$args = array(&$one, &$two); // this MUST be its own statement on its own line, and MUST have referenced elements!
$some_object->some_method($args); // CANNOT do $some_object->some_method(array(&$one, &$two)); !!!
$some_object->other_method();
var_dump($some_object);
var_dump($args);
var_dump($one);
var_dump($two);
That will do what you want.
Also note that call-time pass-by-reference (thefunc(&$foo);) is depreciated and may not work anymore.

php reference versus value

class Test {
private $arr;
function __construct() {
$this->arr = array('test');
}
function getArr() {
return $this->arr;
}
}
$a = new Test();
$b = $a->getArr();
$b[0][0] = 'a';
$s = $a->getArr();
echo $s[0]
Why does this echo test instead of aest? Does PHP copy the array and the contents of the array when returning it? How do I get an array in which I can change the strings and have it reflected in the object?
By returning and assigning by reference:
class Test {
//...
function &getArr() {
return $this->arr;
}
}
$a = new Test();
$b =& $a->getArr();
$b[0][0] = 'a';
$s = $a->getArr();
echo $s[0];
Does PHP copy the array and the contents of the array when returning it?
From the point of view of the programmer, it works as if returning would copy the value, except when returning by reference. In terms of implementation, there are optimizations that avoid this happens, as long as it has no impact in the behavior of the script.

Categories