深入分析PHP引用(&) |
本文标签:php,引用 引用是什么 引用做什么 <?php $a =& $b; ?> 这意味着 $a 和 $b 指向了同一个变量 。 Note: $a 和 $b 在这里是完全相同的,这并不是 $a 指向了 $b 或者相反,而是 $a 和 $b 指向了同一个地方 。 Note: 如果具有引用的数组被拷贝,其值不会解除引用 。对于数组传值给函数也是如此 。 Note: 如果对一个未定义的变量进行引用赋值、引用参数传递或引用返回,则会自动创建该变量 。 Example #1 对未定义的变量使用引用 <?php function foo(&$var) { } foo($a); // $a is "created" and assigned to null $b = array(); foo($b[b]); var_dump(array_key_exists(b, $b)); // bool(true) $c = new StdClass; foo($c->d); var_dump(property_exists($c, d)); // bool(true) ?> 同样的语法可以用在函数中,它返回引用,以及用在 new 运算符中(PHP 4.0.4 以及以后版本): <?php $bar =& new fooclass(); $foo =& find_var($bar); ?>
Note: 不用 & 运算符导致对象生成了一个拷贝 。如果在类中用 $this,它将作用于该类当前的实例 。没有用 & 的赋值将拷贝这个实例(例如对象)并且 $this 将作用于这个拷贝上,这并不总是想要的结果 。由于性能和内存消耗的问题,通常只想工作在一个实例上面 。 尽管可以用 @ 运算符来抑制构造函数中的任何错误信息,例如用 @new,但用 &new 语句时这不起效果 。这是 Zend 引擎的一个限制并且会导致一个解析错误 。 Warning 如果在一个函数内部给一个声明为 global 的变量赋于一个引用,该引用只在函数内部可见 。可以通过使用 $GLOBALS 数组避免这一点 。 Example #2 在函数内引用全局变量 <?php $var1 = "Example variable"; $var2 = ""; function global_references($use_globals) { global $var1, $var2; if (!$use_globals) { $var2 =& $var1; // visible only inside the function } else { $GLOBALS["var2"] =& $var1; // visible also in global context } } global_references(false); echo "var2 is set to $var2\n"; // var2 is set to global_references(true); echo "var2 is set to $var2\n"; // var2 is set to Example variable ?> 把 global $var; 当成是 $var =& $GLOBALS[var]; 的简写 。从而将其它引用赋给 $var 只改变了本地变量的引用 。 如果在 foreach 语句中给一个具有引用的变量赋值,被引用的对象也被改变 。 Example #3 引用与 foreach 语句 <?php $ref = 0; $row =& $ref; foreach (array(1, 2, 3) as $row) { // do something } echo $ref; // 3 - last element of the iterated array ?> 引用做的第二件事是用引用传递变量 。这是通过在函数内建立一个本地变量并且该变量在呼叫范围内引用了同一个内容来实现的 。例如: <?php function foo(&$var) { $var++; } $a=5; foo($a); ?> 将使 $a 变成 6 。这是因为在 foo 函数中变量 $var 指向了和 $a 指向的同一个内容 。更多详细解释见引用传递 。 引用做的第三件事是引用返回 。 引用不是什么 <?php function foo(&$var) { $var =& $GLOBALS["baz"]; } foo($bar); ?> 这将使 foo 函数中的 $var 变量在函数调用时和 $bar 绑定在一起,但接着又被重新绑定到了 $GLOBALS["baz"] 上面 。不可能通过引用机制将 $bar 在函数调用范围内绑定到别的变量上面,因为在函数 foo 中并没有变量$bar(它被表示为 $var,但是 $var 只有变量内容而没有调用符号表中的名字到值的绑定) 。可以使用引用返回来引用被函数选择的变量 。 引用传递 <?php function foo(&$var) { $var++; } $a=5; foo($a); // $a is 6 here ?> 注意在函数调用时没有引用符号——只有函数定义中有 。光是函数定义就足够使参数通过引用来正确传递了 。在最近版本的 PHP 中如果把 & 用在 foo(&$a); 中会得到一条警告说“Call-time pass-by-reference”已经过时了 。 以下内容可以通过引用传递: 变量,例如 foo($a) <?php function &bar() { $a = 5; return $a; } foo(bar()); ?> 详细解释见引用返回 。 <?php function bar() // Note the missing & { $a = 5; return $a; } foo(bar()); // 自 PHP 5.0.5 起导致致命错误 foo($a = 5) // 表达式,不是变量 foo(5) // 导致致命错误 ?> 这些条件是 PHP 4.0.4 以及以后版本有的 。 引用返回 <?php class foo { public $value = 42; public function &getValue() { return $this->value; } } $obj = new foo; $myValue = &$obj->getValue(); // $myValue is a reference to $obj->value, which is 42. $obj->value = 2; echo $myValue; // prints the new value of $obj->value, i.e. 2. ?> 本例中 getValue 函数所返回的对象的属性将被赋值,而不是拷贝,就和没有用引用语法一样 。 Note: 和参数传递不同,这里必须在两个地方都用 & 符号——指出返回的是一个引用,而不是通常的一个拷贝,同样也指出 $myValue 是作为引用的绑定,而不是通常的赋值 。 Note: 如果试图这样从函数返回引用:return ($this->value);,这将不会起作用,因为在试图返回一个表达式的结果而不是一个引用的变量 。只能从函数返回引用变量——没别的方法 。如果代码试图返回一个动态表达式或 new 运算符的结果,自 PHP 4.4.0 和 PHP 5.1.0 起会发出一条 E_NOTICE 错误 。 <?php function &test(){ static $b=0;//申明一个静态变量 $b=$b+1; echo $b; return $b; } $a=test();//这条语句会输出$b的值为1 $a=5; $a=test();//这条语句会输出$b的值为2 $a=&test();//这条语句会输出$b的值为3 $a=5; $a=test();//这条语句会输出$b的值为6 ?> $a=test()方式调用函数,只是将函数的值赋给$a而已,而$a做任何改变化,都不会影响到函数中的$b,而通过$a=&test()方式调用函数呢, 他的作用是将return $b中的$b变量的内存地址与$a变量的内存地址指向了同一个地方,即产生了相当于这样的效果($a=&b;) 所以改变$a的值,也同时改变了$b的值,所以在执行了 $a=&test(); $a=5; 以后,$b的值变为了5 。 取消引用 <?php $a = 1; $b =& $a; unset($a); ?> 不会 unset $b,只是 $a 。 再拿这个和 Unix 的 unlink 调用来类比一下可能有助于理解 。 引用定位 global 引用 <?php $var =& $GLOBALS["var"]; ?> 这意味着,例如,unset $var 不会 unset 全局变量 。 使用unset($a)与$a=null的结果是不一样的 。如果该块内存只有$a一个映射,那么unset($a)与$a=null等价,该内存的引用计数变为0,被自动回收;如果该块内存有$a和$b两个映射,那么unset($a)将导致$a=null且$b不变的情况,而$a=null会导致$a=$b=null的情况 。 原因:某变量赋值为null,将导致该变量对应的内存块的引用计数直接置为0,被自动回收 。 $this 引用的作用 下面再来个小插曲 php中对于地址的指向(类似指针)功能不是由用户自己来实现的,是由Zend核心实现的,php中引用采用的是“写时拷贝”的原理,就是除非发生写操作,指向同一个地址的变量或者对象是不会被拷贝的 。 通俗的讲 1:如果有下面的代码 <?ph $a="ABC"; $b=$a; ?> 其实此时,$a与$b都是指向同一内存地址,而并不是$a与$b占用不同的内存 。 2:如果在上面的代码基础上再加上如下代码 $a="EFG"; |