Why is there a difference between a regular object instance and a referenced object instance?

I am working on a very old project that has migrated from PHP 4 to PHP 5.2 and we are now migrating the project to PHP 5.4. We have come up with a number of benchmark errors. Here is some sample code that is listening for me.

<?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();

?>

      

I would expect this to create 3 separate books. But instead, each object in the element array ends up pointing to the last variable. On the other hand, if I create a new workbook by reference, everything works correctly. (for example $book1 =& new book("Book $i");

). Here's some sample code:

<?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();

      

I was under the impression that object creation by reference is not required in PHP 5.4. Any idea why I am getting different results?

+3


source to share


2 answers


This is a logic error, not a PHP related version. Let's take a look at the following code snippets:

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

...

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

      

What's happening? You are passing $ book by reference. But in every loop you change the value of the link. At the end of the loop, $ book points to the most recently created book object and therefore links. (there was this error too);

Conclusion: your addBook function should look like this:

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

      

and keep the code that adds the workbook unchanged - which means in the above example without &

.




* About = & new ... *

First, if you do this, you will receive the following php 5.3 warning:

Deprecated: assignment of return value to new by reference is deprecated

Therefore, you shouldn't use it for new code.

+2


source


As you write, this is PHP 4 source code. These two constructs:

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

      

and

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

      

are no longer needed. The first one is "new by reference", which was never correct, but needed to get something working in PHP 4 with objects, not cloning objects all the time

The second is "pass by reference", which may be a valid concept, but it is not required for objects in PHP 5 and should not be used for parameters that are objects.

Memory management has improved significantly in PHP 5 and then again in PHP 5.3. PHP 5.4 helps you port your code by providing warnings. Especially "new by reference" can be easily noticed and captured.

Old (PHP 4):

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

      

New (current PHP versions):

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

      

(and no, it won't come back :)). For the methods you pass by reference, I suggest when you refactor your code, remove the references and also put in a type like:

Old (PHP 4):

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

      

New (current PHP versions):

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

      



Then you will not only fix the old problem, but also make sure that this function is only called with the intended type and you can see that it should always take an object (and not a variable reference that was only used in PHP 4 for objects, because that PHP 4 was limited. This is no longer needed and can (and should remove that old jerk!) be removed).

Place your codebase under version control so you can easily apply changes to your codebase without fear of destroying anything previously working.


I would expect this to create 3 separate books. But instead, each object in the array of elements ends up pointing to the last variable.

The real problem here is that you are using link pass. When you appoint

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

      

The count $book1

is one and not tagged as a link. By passing it now as a reference to a method $bookshelf1->addBook($book1)

, it will turn it into a reference with a refcount of 2, because inside that method, the reference is again assigned to a private member of the class:

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

...

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

      

Then you will change the link you created.

So now when you change your code with an instance:

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

      

You always - once per loop - create a new alias (link) with only the same variable name. Since you are passing this as a link, it is a new alias that you assign as a link. So the array inside the book points to three different links - not three times to the same link.

Hopefully this explains the problem better. Also, this is the reason we say: prohibit the use of links unless you know what you are doing. The situation with PHP 4 was that many users used links without fully understanding how it works (which is understandable, because it is not that easy in PHP, I also need to fool my head a little until I find a good description to share here - hope it worked this time :)). The advantage of using PHP 5 here is clearly that you no longer need to use object references, making it easier to write and read code.


on this topic:

+1


source







All Articles