Передача по ссылке
Вы можете передавать переменные в функцию по ссылке, и функция сможет изменять свои аргументы. Синтаксис таков:
<?php
function foo(&$var)
{
$var++;
}
$a=5;
foo($a);
// $a здесь равно 6
?>
Замечание: В вызове функции отсутствует знак ссылки - он есть только в определении функции. Этого достаточно для корректной передачи аргументов по ссылке. Начиная с PHP 5.3.0, вы можете получить предупреждение о том, что передача переменной по ссылке устарела, если используете & в foo(&$a);. Начиная с PHP 5.4.0 передача переменной по ссылке стала невозможна, поэтому использование этого приема приведет к фатальной ошибке.
По ссылке можно передавать:
- Переменные, например foo($a)
- Оператор new, например foo(new foobar())
-
Ссылки, возвращаемые функцией, например:
<?php
function foo(&$var)
{
$var++;
}
function &bar()
{
$a = 5;
return $a;
}
foo(bar());
?>
Любое другое выражение не должно передаваться по ссылке, так как результат не определён. Например, следующая передача по ссылке является неправильной:
<?php
function foo(&$var)
{
$var++;
}
function bar() // Операция & отсутствует
{
$a = 5;
return $a;
}
foo(bar()); // Вызывает неисправимую ошибку начиная с PHP 5.0.5
foo($a = 5); // Выражение, а не переменная
foo(5); // Константа, а не переменная
?>
Коментарии
Sometimes we need functions for building or modifying arrays whose elements are to be references to other variables (arrays or objects for instance). In this example, I wrote two functions 'tst' and 'tst1' that perform this task. Note how the functions are written, and how they are used.
<?php
function tst(&$arr, $r) {
// The argument '$arr' is declared to be passed by reference,
// but '$r' is not;
// however, in the function's body, we use a reference to
// the '$r' argument
array_push($arr, &$r);
// Alternatively, this also could be $arr[] = &$r (in this case)
}
$arr0 = array(); // an empty array
$arr1 = array(1,2,3); // the array to be referenced in $arr0
// Note how we call the function:
tst($arr0, &$arr1); // We are passing a reference to '$arr1' in the call !
print_r($arr0); // Contains just the reference to $arr1
array_push($arr0, 5); // we add another element to $arr0
array_push($arr1, 18); // we add another element to $arr1 as well
print_r($arr1);
print_r($arr0); // Changes in $arr1 are reflected in $arr0
// -----------------------------------------
// A simpler way to do this:
function tst1(&$arr, &$r) {
// Both arguments '$arr' and '$r" are declared to be passed by
// reference,
// again, in the function's body, we use a reference to
// the '$r' argument
array_push($arr, &$r);
// Alternatively, this also could be $arr[] = &$r (in this case)
}
$arr0 = array(); // an empty array
$arr1 = array(1,2,3); // the array to be referenced in $arr0
// Note how we call the function:
tst1($arr0, $arr1); // 'tst1' understands '$r' is a reference to '$arr1'
echo "-------- 2nd. alternative ------------ <br>\n";
print_r($arr0); // Contains just the reference to $arr1
array_push($arr0, 5); // we add another element to $arr0
array_push($arr1, 18);
print_r($arr1);
print_r($arr0); // Changes in $arr1 are reflected in $arr0
// This outputs:
// X-Powered-By: PHP/4.1.2
// Content-type: text/html
//
// Array
// (
// [0] => Array
// (
// [0] => 1
// [1] => 2
// [2] => 3
// )
//
// )
// Array
// (
// [0] => 1
// [1] => 2
// [2] => 3
// [3] => 18
// )
// Array
// (
// [0] => Array
// (
// [0] => 1
// [1] => 2
// [2] => 3
// [3] => 18
// )
//
// [1] => 5
// )
// -------- 2nd. alternative ------------
// Array
// (
// [0] => Array
// (
// [0] => 1
// [1] => 2
// [2] => 3
// )
//
// )
// Array
// (
// [0] => 1
// [1] => 2
// [2] => 3
// [3] => 18
// )
// Array
// (
// [0] => Array
// (
// [0] => 1
// [1] => 2
// [2] => 3
// [3] => 18
// )
//
// [1] => 5
// )
?>
In both cases we get the same result.
I hope this is somehow useful
Sergio.
Just a simple note...
<?php
$num = 1;
function blah(&$var)
{
$var++;
}
blah($num);
echo $num; #2
?>
<?php
$num = 1;
function blah()
{
$var =& $GLOBALS["num"];
$var++;
}
blah();
echo $num; #2
?>
Both codes do the same thing! The second code "explains" how passage of parameters by reference works.
PHP has a strange behavior when passing a part of an array by reference, that does not yet exist.
<?php
function func(&$a)
{
// void();
}
$a['one'] =1;
func($a['two']);
?>
var_dump($a) returns
array(2) {
["one"]=>
int(1)
["two"]=>
NULL
}
...which seems to be not intentional!
Some have noticed that reference parameters can not be assigned a default value. It's actually wrong, they can be assigned a value as the other variables, but can't have a "default reference value", for instance this code won't compile :
<?php
function use_reference( $someParam, &$param =& $POST )
{
...
}
?>
But this one will work :
<?php
function use_reference( $someParam, &$param = null )
?>
So here is a workaround to have a default value for reference parameters :
<?php
$array1 = array ( 'test', 'test2' );
function AddTo( $key, $val, &$array = null)
{
if ( $array == null )
{
$array =& $_POST;
}
$array[ $key ] = $val ;
}
AddTo( "indirect test", "test", $array1 );
AddTo( "indirect POST test", "test" );
echo "Array 1 " ;
print_r ( $array1);
echo "_POST ";
print_r( $_POST );
?>
And this scripts output is :
Array 1 Array
(
[0] => test
[1] => test2
[indirect test] => test
)
_POST Array
(
[indirect POST test] => test
)
Of course that means you can only assign default reference to globals or super globals variables.
Have fun
<?php
/*
This function internally swaps the contents between
two simple variables using 'passing by reference'.
Some programming languages have such a swap function
built in, but PHP seems to lack such a function. So,
one was created to fill the need. It only handles
simple, single variables, not arrays, but it is
still a very handy tool to have.
No value is actually returned by this function, but
the contents of the indicated variables will be
exchanged (swapped) after the call.
*/
// ------------------------------------------
// Demo call of the swap(...) function below.
$a = 123.456;
$b = 'abcDEF';
print "<pre>Define:\na = $a\nb = '$b'</pre>";
swap($a,$b);
print "<pre>After swap(a,b):\na = '$a'\nb = $b</pre>";
// -------------------------------
function swap (&$arg1, &$arg2)
{
// Swap contents of indicated variables.
$w=$arg1; $arg1=$arg2; $arg2=$w;
}
?>
The notes indicate that a function variable reference will receive a deprecated warning in the 5.3 series, however when calling the function via call_user_func the operation aborts without fatal error.
This is not a "bug" since it is not likely worth resolving, however should be noted in this documentation.
By removing the ability to include the reference sign on function calls where pass-by-reference is incurred (I.e., function definition uses &), the readability of the code suffers, as one has to look at the function definition to know if the variable being passed is by-ref or not (I.e., potential to be modified). If both function calls and function definitions require the reference sign (I.e., &), readability is improved, and it also lessens the potential of an inadvertent error in the code itself. Going full on fatal error in 5.4.0 now forces everyone to have less readable code. That is, does a function merely use the variable, or potentially modify it...now we have to find the function definition and physically look at it to know, whereas before we would know the intent immediately.
agreed : this change produces less readable code.
additionally, it breaks many existing perfectly working codes which are not portable anymore and in some cases will require complex modifications
another issue regards the fatal error that is produced : how the hell am i supposed to do if i want to allow the user to use a value that is not even in a variable, or the return or a function call, or use call_user_func... this produces many occasions for a code to even break at run time
beware unset() destroys references
$x = 'x';
change( $x );
echo $x; // outputs "x" not "q23" ---- remove the unset() and output is "q23" not "x"
function change( & $x )
{
unset( $x );
$x = 'q23';
return true;
}
The comment by tnestved at yahoo dot com is incorrect as it is based purely on perception and not architecture. The method passing the object should not care whether it is by ref or by val, and nor should the reader.
If we are talking about readability and perception, then the receiving method needs to show that the object coming in is a reference, not an object instance, otherwise the reader is perplexed why the object is not returned.
Good functional headers alleviate all issues in this case.
For anyone wondering, the copy-on-write behaviour just does the Right Thing™ when an array is passed to a function not by-ref which then passes it through to another function by-ref without writing to it. For example:
<?php
function do_sort(array $array) : array {
usort($array, function ($a, $b) {
return strnatcasecmp($a['name'], $b['name']);
});
return $array;
}
$data = [
[
'name' => 'one',
], [
'name' => 'two',
], [
'name' => 'three',
], [
'name' => 'four',
],
];
var_dump($data);
do_sort($data); // does not affect value of $data
var_dump($data);
$data = do_sort($data);
var_dump($data);
Beware of using references with anonymous function and "use" keyword :
If you have a PHP version between 5.3 and < 5.3.10, "use" keyword break the reference :
<?php
function withRef(&$arg) {
echo 'withRef - BEGIN - '.$arg."\n"; // 1
$func = function() use($arg) { /* do nothing, just declare using $arg */ };
$arg = 2;
echo 'withRef - END - '.$arg."\n"; // 2
}
$arg = 1;
echo 'withRef - BEFORE - '.$arg."\n"; // 1
withRef($arg);
// in PHP 5.3 < 5.3.10 : display 1
// in PHP 5.3 >= 5.3.10 : display 2
echo 'withRef - AFTER - '.$arg."\n";
?>
A workaround is to use a copy of the reference variable in "use" keyword :
<?php
...
$arg2 = $arg;
$func = function() use($arg2) { /* do nothing, just declare using $arg2 */ };
<?php
// PHP >= 5.6
// Here we use the 'use' operator to create a variable within the scope of the function. Although it may seem that the newly created variable has something to do with '$x' that is outside the function, we are actually creating a '$x' variable within the function that has nothing to do with the '$x' variable outside the function. We are talking about the same names but different content locations in memory.
$x = 10;
(function() use ($x){
$x = $x*$x;
var_dump($x); // 100
})();
var_dump($x); // 10
// Now the magic happens with using the reference (&). Now we are actually accessing the contents of the '$y' variable that is outside the scope of the function. All the actions that we perform with the variable '$y' within the function will be reflected outside the scope of this same function. Remembering this would be an impure function in the functional paradigm, since we are changing the value of a variable by reference.
$y = 10;
(function() use (&$y){
$y = $y*$y;
var_dump($y); // 100
})();
var_dump($y); // 100
?>
Within a class, passing array elements by reference which don't exist are added to the array as null. Compared to a normal function, this changes the behavior of the function from throwing an error to creating a new (null) entry in the referenced array with a new key.
<?php
class foo {
public $arr = ['a' => 'apple', 'b' => 'banana'];
public function normalFunction($key) {
return $this->arr[$key];
}
public function &referenceReturningFunction($key) {
return $this->arr[$key];
}
}
$bar = new foo();
$var = $bar->normalFunction('beer'); //Notice Error. Undefined index beer
$var = &$bar->referenceReturningFunction('beer'); // No error. The value of $bar is now null
var_dump($bar->arr);
/**
[
"a" => "apple",
"b" => "banana",
"beer" => null,
],
*/
?>
This is in no way a "bug" - the framework is performing as designed, but it took careful thought to figure out what was going on. PHP7.3
function set(&$arr) {
$test = 10;
$arr = &$test;
}
$arr = [1, 2, 3];
set( $arr );
var_dump( $arr ); // [1, 2, 3]
If you changed a reference variable with a new `Address`, the variable it originally pointed to won't change.
I designed a class that can easily pass references.
<?php
#### Problem 1
function problem(&$value)
{
}
problem(1); // cannot be passed by reference
#### Problem 2
class problem2
{
static function __callStatic($name, &$arguments) // cannot take arguments by reference
{
}
}
?>
My solution 👇
<?php
class Reference
{
function __construct(public mixed &$data)
{
}
/**
* create
*
* @param mixed $data
* @return self
*/
static function &create(mixed &$data): self
{
if ($data instanceof self) {
return $data;
}
$r = new self($data);
return $r;
}
/**
* get value
*
* @param mixed $reference
* @return mixed
*/
static function &get(mixed &$reference): mixed
{
if ($reference instanceof self) {
return $reference->data;
}
return $reference;
}
}
#### Problem solving 1 ####
function test($value)
{
$value = &Reference::get($value); // values OR reference
$value = "test-$value";
return $value;
}
echo test(1), PHP_EOL; // test-1
$val = 2;
echo test(Reference::create($val)), PHP_EOL; // test-2
echo $val, PHP_EOL; // test-2
#### Problem solving 2 ####
class TestCall
{
static function __callStatic($name, $arguments)
{
$value = &Reference::get($arguments[0]);
$value = "$name-$value";
return $value;
}
}
echo TestCall::test(3), PHP_EOL; // test-3
$val = 4;
echo TestCall::test(Reference::create($val)), PHP_EOL; // test-4
echo $val, PHP_EOL; // test-4
A reference remains a reference, even if it's an element of an array that is not passed by reference.
An example:
<?php
$c = 3;
$array = array(
'a' => 1,
'b' => 2,
'c' => &$c
);
var_dump($array);
function func($array) {
$array['a'] = 4;
$array['b'] = 5;
$array['c'] = 6;
var_dump($array);
}
func($array);
var_dump($array);
var_dump($c);
?>
This will output the following:
array(3) {
["a"]=>
int(1)
["b"]=>
int(2)
["c"]=>
&int(3)
}
array(3) {
["a"]=>
int(4)
["b"]=>
int(5)
["c"]=>
&int(6)
}
array(3) {
["a"]=>
int(1)
["b"]=>
int(2)
["c"]=>
&int(6)
}
int(6)
You could use this to allow a function to have read-write access to part of the array, while limiting it to read-only access for the rest of the array:
<?php
$config = [
'ro' => [
// ...
],
'rw' => [
// ...
]
];
$copy = $config;
$copy['rw'] = &$config['rw'];
func($copy);
?>
Parameters passed by references can have default values.
You can find out if a variable was actually passed by using func_num_args():
<?php
function refault( & $ref = 'Do I have to be calculated?'){
echo 'NUM ARGS: '. func_num_args()."\n";
echo "ORI VALUE: {$ref}\n";
if( func_num_args() > 0 ) $ref = 'Yes, expensive to calculate result: ' . sleep(1);
else $ref = 'No.';
echo "NEW VALUE: {$ref}\n";
}
$result = 'Do I have to be calculated?';
refault( $result );
echo "RESULT: {$result}\n";
// NUM ARGS: 1
// ORI VALUE: Do I have to be calculated?
// NEW VALUE: Yes, expensive to calculate result: 0
// RESULT: Yes, expensive to calculate result: 0
refault();
// NUM ARGS: 0
// ORI VALUE: Do I have to be calculated?
// NEW VALUE: No.
?>