1

请帮助我了解此症状是否是 PHP 中的意外错误/不良优化/或预期行为。

这是一个小代码:

class Packet {
  public $length;
  public $time;
}

class Stream {
  public $packets = array();

  // Basically what I want is to make sure
  // I put my packet in the array as reference
  // because later in the code where I call this
  // function, I change my Packet object's values
  // and I want this change to appear in the
  // $this->packets array. So I pass by reference.
  // I don't want to copy the Packet object at all!
  public function addPacket(&$packet){
    $this->packets[] = &$packet; //note here the reference operator
  }
}

...

foreach($packets as $packet){
  $myPrettyStream->addPacket($packet);
}

所以我在代码注释中解释了我想要做什么。我的问题是:在我将所有数据包添加到 foreach 中的流后,我的所有Stream::packets数组元素都将包含(引用)到$packet我添加到流中的最后一个。

似乎 PHP在函数调用之间保留了$packet我的函数内部的变量。addPacket(&$packet)(这可能是有意的还是不好的优化?)无论我是&$packet通过引用数组(基本上是引用引用&&$packet)还是简单地引用$packet(&$packet)来传递变量,我认为这不应该是预期的行为。

但是,如果我不在函数内部进行引用,它可以工作:

public function addPacket(&$packet){
  $this->packets[] = $packet;
}

请有人解释为什么这种行为是有意义的!

谢谢!

==================================================== ========================

解决方案

感谢您的正确答案,我模拟了一个行为相同的函数调用:

// try to emulate function call inside iteration
$packet         = $packets[0];                 
$functionPacket = &$packet;            
$packetsItem[0] = &$functionPacket; //functionpacket holds a reference to $packet
var_dump($packetsItem);

echo "\nsecond iteration\n";
unset($functionPacket);
$packet         = $packets[1];                 
$functionPacket = &$packet;            
$packetsItem[1] = &$functionPacket;
var_dump($packetsItem);

output:
array(1) {
  [0]=>
  &object(Packet)#1 (2) {
    ["time"]=>
    int(1)
  }
}
array(2) {
  [0]=>
  &object(Packet)#2 (2) {
    ["time"]=>
    int(2)
  }
  [1]=>
  &object(Packet)#2 (2) {
    ["time"]=>
    int(2)
  }
}
4

2 回答 2

3

PHP 总是通过引用传递对象,因此绝对不需要&,在您的情况下它甚至是有害的(正如您已经意识到的那样;))。

问题是,您“引用引用”而不是对象。这意味着,数组中的每个项目都是$packet来自方法内部的引用,它本身就是$packet来自数组外部的引用。现在,当您更改$packetforeach-loop)时,似乎数组中的每个项目都发生了变化,但实际上它们与以前保持相同的引用。

作为经验法则:如果您需要-arguments&以外的所有内容out,则应该考虑您的设计。如果您需要 out-arguments,那么您也应该考虑一下。通常(不再)不需要这个,它使许多事情(不必要的)变得复杂。特别是:永远不要&用于微优化。

你可以想象,它看起来像这样(即使它可能不那么准确)

// First iteration
$packet --> $packets[0]
// Pass to method
Stream::addPackage():$packet --> $packet --> $packets[0]
// Assign to array
Stream::$packets[0] --> Stream::addPackage():$packet --> $packet --> $packets[0]

// Second iteration (!)
Stream::$packets[0] --> Stream::addPackage():$packet --> $packet --> $packets[1]

请注意,Stream::$packets[0]现在如何“指向”$packets[1]

于 2012-04-06T14:35:23.583 回答
1

http://www.php.net/manual/en/language.references.pass.php

当你通过引用传递一个变量时,你只需要在函数声明中添加&。在函数中,您不需要在变量名前添加 &。

public function addPacket(&$packet){
    $this->packets[] = &$packet; //  should be $this->packets[] = $packet;
}
于 2012-04-06T14:08:26.383 回答