PHP Velho Oeste 2024

Ссылка внутри конструктора

Создание ссылок в конструкторе может привести к конфузу. Эта секция - своего рода памятка "как избежать проблем".

<?php
class Foo {
    function 
Foo($name) {
        
// создание ссылки внутри глобального массива $globalref
        
global $globalref;
        
$globalref[] = &$this;
        
// устанавливаем имя
        
$this->setName($name);
        
// и печатаем его
        
$this->echoName();
    }

    function 
echoName() {
        echo 
"<br />"$this->name;
    }
 
    function 
setName($name) {
        
$this->name $name;
    }
}
?>

Давайте проверим, какая разница между созданной копированием (=) переменной $bar1 и $bar2, созданной по ссылке (=&)...

<?php
$bar1 
= new Foo('set in constructor');
$bar1->echoName();
$globalref[0]->echoName();

/* вывод:
set in constructor
set in constructor
set in constructor */

$bar2 =& new Foo('set in constructor');
$bar2->echoName();
$globalref[1]->echoName();

/* output:
set in constructor
set in constructor
set in constructor */
?>

На первый взгляд никакой разницы, но факт в том, что $bar1 и $globalref[0] _НЕ_ связаны, они НЕ являются одной и той-же переменной. Это потому, что "new" возвращает не ссылку, а копию.

Замечание: При возврате копии вместо ссылки нет никакой потери производительности (с PHP 4 и выше используется подсчет ссылок). Напротив, чаще всего лучше просто работать с копиями вместо ссылок, потому что создание ссылки требует времени, тогда как создание копии создание копий практически не занимает времени (за исключением когда работа идет с огромным массивом или объектом, которые изменяются и это изменение должно отразиться на всех остальных. Тогда, конечно, лучше использовать ссылки).

Для доказательства того, что написано выше, посмотрим код ниже.
<?php
// теперь мы изменим name. какого поведения вы ожидаете?
// вы ждете, что и $bar1 и $globalref[0] изменят name...
$bar1->setName('set from outside');

// как объяснено выше - не в этот раз.
$bar1->echoName();
$globalref[0]->echoName();

/* вывод:
set from outside
set in constructor */

// давайте повторим этот же фокус с $bar2 и $globalref[1]
$bar2->setName('set from outside');

// повезло! Они не только идентичны, они являются одной и той же переменной,
// так что $bar2->name и $globalref[1]->name являются одним и тем же
$bar2->echoName();
$globalref[1]->echoName();

/* вывод:
set from outside
set from outside */
?>

Еще один, последний, пример. Попробуйте в нем разобраться.

<?php
class {
    function 
A($i) {
        
$this->value $i;
        
// почему нам тут ссылка не нужна?
        
$this->= new B($this);
    }

    function 
createRef() {
        
$this->= new B($this);
    }

    function 
echoValue() {
        echo 
"<br />","class ",get_class($this),': ',$this->value;
    }
}


class 
{
    function 
B(&$a) {
        
$this->= &$a;
    }

    function 
echoValue() {
        echo 
"<br />","class ",get_class($this),': ',$this->a->value;
    }
}

// почему использование простой копии даст нежелательный
// результат в строке помеченной звездочкой
$a =& new A(10);
$a->createRef();

$a->echoValue();
$a->b->echoValue();
$a->c->echoValue();

$a->value 11;

$a->echoValue();
$a->b->echoValue(); // *
$a->c->echoValue();

?>

Результат выполнения данного примера:

class A: 10
class B: 10
class B: 10
class A: 11
class B: 11
class B: 11

add a note add a note

User Contributed Notes

There are no user contributed notes for this page.
To Top