3

我正在处理一个非常古老的项目,该项目已从 PHP 4 迁移到 PHP 5.2,我们现在正在将该项目迁移到 PHP 5.4。我们提出了许多参考错误。这是一些困扰我的示例代码。

<?php

class book_shelf 
{
    protected $_elements = array();

    function addBook(&$element){
        $this->_elements[] =& $element;
    }

    function printBooks(){
        print(json_encode($this->_elements));
    }
}

class book 
{
    function __construct($title, $author="unknown") {
        $this->title = $title;
        $this->author = $author;
    }

    public $title = NULL;
    public $author = NULL;
}

function createBookShelf(){
    $bookshelf1 = new book_shelf();

    for ($i = 0; $i < 3; $i++){
        $book1 = new book("Book $i");
        $bookshelf1->addBook($book1);
    }

    $bookshelf1->printBooks();  
}   

createBookShelf();

?>

我希望这可以创建 3 本书。但是元素数组中的每个对象最终都指向最后一个变量。另一方面,如果我通过引用创建一本新书,一切正常。(例如$book1 =& new book("Book $i");)这是示例代码:

<?php

class book_shelf 
{
    protected $_elements = array();

    function addBook(&$element) {
        $this->_elements[] =& $element;
    }

    function printBooks(){
        print(json_encode($this->_elements));
    }
}

class book 
{
    function __construct($title, $author="unknown") {
        $this->title = $title;
        $this->author = $author;
    }

    public $title = NULL;
    public $author = NULL;
}

function createBookShelf() {
    $bookshelf1 = new book_shelf();

    for ($i = 0; $i < 3; $i++){
        $book1 =& new book("Book $i");
        $bookshelf1->addBook($book1);
    }

    $bookshelf1->printBooks();  
}   

createBookShelf();

我的印象是在 PHP 5.4 中通过引用创建对象是不必要的。知道为什么我会得到不同的结果吗?

4

2 回答 2

2

这是一个逻辑错误,与 PHP 版本无关。让我们看一下您的代码的以下片段:

//echo "phpinfo: " . phpinfo();
for ($i = 0; $i < 3; $i++){
    $book1 = new book("Book $i");
    $bookshelf1->addBook($book1);
}

...

function addBook(&$element){
    $this->_elements[] =& $element;
}

怎么了?您通过引用传递 $book 。但是在每个循环中,您都会更改引用的值。在 for 循环结束时,$book 指向最后创建的 book 对象和引用。(也有过这个错误;)

结论:您的 addBook 函数必须如下所示:

function addBook($element){
    $this->_elements[] = $element;
}

并保持添加书籍的代码不变 - 就像上面的例子一样,没有&.


*关于 =& 新 ... *

首先,如果你这样做,你会从 php 5.3 收到以下警告:

不推荐使用:不推荐使用通过引用分配 new 的返回值

因此,您不应该将它用于新代码。

于 2013-02-08T23:42:55.550 回答
1

在您编写时,这最初是 PHP 4 代码。这两个构造:

$book1 =& new book("Book $i");

function addBook(&$element){
    $this->_elements[] =& $element;
}

不再需要。第一个是“new by reference”,这从来都不是正确的做法,但有必要在 PHP 4 中使用对象而不是一直克隆对象

第二个是“按引用传递”,它可以是一个有效的概念,但就 PHP 5 中的对象而言,这不是必需的,并且不应用于作为对象的参数。

内存管理在 PHP 5 和 PHP 5.3 中得到了极大的改进。PHP 5.4 通过发出警告帮助您迁移代码。特别是“新的参考”可以很容易地发现和修复。

旧(PHP 4):

$book1 =& new book("Book $i");

新的(当前 PHP 版本):

$book1 = new book("Book $i");

(不,它不会回来:))。对于通过引用传递的方法,我建议在重构代码时删除引用,并键入提示对象类型:

旧(PHP 4):

function addBook(&$element){
    $this->_elements[] =& $element;
}

新的(当前 PHP 版本):

function addBook(Book $element){
    $this->_elements[] = $element;
}

然后,您不仅解决了一个老问题,而且还确保仅使用预期的类型调用此函数,并且您可以看到它总是要获取一个对象(而不是仅在 PHP 4 中用于对象的变量引用,因为PHP 4 是有限的。这不再需要并且可以(并且应该,扔掉这个旧的东西!)被删除)。

将您的代码库置于版本控制之下,这样您就可以轻松地将更改应用到您的代码库,而不必担心破坏以前工作的任何内容。


我希望这可以创建 3 本书。但是元素数组中的每个对象最终都指向最后一个变量。

这里的实际问题是您使用通过引用传递。当你分配

$book1 = new book("Book $i");

的 refcount$book1为 1 且未标记为参考。现在将它作为引用传递给$bookshelf1->addBook($book1)方法会将其转换为引用计数为 2 的引用,因为在该方法内部,引用再次分配给类的私有成员:

for ($i = 0; $i < 3; $i++)
{
    $book1 = new book("Book $i");
    $bookshelf1->addBook($book1);
}

...

function addBook(&$element){
    $this->_elements[] =& $element;
}

然后,您更改现在创建的参考。

因此,现在当您将具有实例化的代码更改为:

$book1 = & new book("Book $i");

您总是 - 每个循环一次 - 只使用相同的变量名创建一个新的别名(引用)。因为您随后将其作为参考传递,所以它是您指定为参考的新别名。因此Book中的数组指向三个不同的引用——而不是三次指向同一个引用。

我希望这能更好地解释这个问题。这也是我们说的原因:除非您知道自己在做什么,否则不要使用引用。PHP 4 的情况是,许多用户习惯于在没有完全理解其工作原理的情况下使用引用(这是可以理解的,因为在 PHP 中这并不容易,我还需要稍微思考一下,直到我找到一个好的描述来分享这里 -希望这次能奏效:))。显然在这里使用 PHP 5 的好处是您不再需要对对象使用引用,这使得编写和阅读代码更加容易。


有关的:

于 2013-05-11T08:56:08.780 回答