Возврат ссылки на объект в PHP - PullRequest
2 голосов
/ 19 октября 2010

Я использую PHP 5.2.14 и PearLog 1.12.3. Последняя документация из одноэлементного метода в Log.php (PEARLog) гласит:

Вы ДОЛЖНЫ вызывать этот метод с помощью Синтаксис $ var = & Log :: singleton (). Без амперсанда (&) перед имя метода, вы не получите ссылка; вы получите копию.

Однако при этом выдается следующее предупреждение:

СТРОГО УВЕДОМЛЕНИЕ: Только переменные должны назначаться по ссылке


Источник этой функции:

public static function singleton($handler, $name = '', $ident = '',
                                 $conf = array(), $level = PEAR_LOG_DEBUG)
{
    static $instances;
    if (!isset($instances)) $instances = array();

    $signature = serialize(array($handler, $name, $ident, $conf, $level));
    if (!isset($instances[$signature])) {
        $instances[$signature] = Log::factory($handler, $name, $ident,
                                              $conf, $level);
    }

    return $instances[$signature];
}

Если я удалю & и использую только:

$var = Log::singleton()

тогда я больше не получаю предупреждение. Кроме того, если я сделаю

$var = Log::singleton();
$var2 = Log::singleton();

тогда $ var === var2 оценивается как true.


Вопрос : Что правильно: документация по API или предупреждение? (Если функция возвращает объект, разве это не ссылка в любом случае? Зачем мне амперсанд?

Ответы [ 3 ]

10 голосов
/ 19 октября 2010

Способ передачи объектов в PHP5 кардинально изменился. В PHP4 они всегда передавались по значению, что означает, что функция или метод, который возвращал объект, фактически передавали копию объекта обратно. Это привело к использованию оператора «&», заставляющего функцию возвращать объект по ссылке. В PHP5 объекты всегда передаются по ссылке. Чтобы создать копию объекта, вы должны использовать оператор клона.

Из-за очень быстрого просмотра источника пакета журнала видно, что он поддерживает совместимость с PHP4. Я не думаю, что вам нужен амперсанд. PHP5 вернет ссылку на объект. Ваш тест '$ var === $ var2' доказал, что метод возвращает объект и что этот объект является ссылкой на один объект. Если бы они были копиями объекта, то при сравнении идентичности было бы установлено значение false.

4 голосов
/ 19 октября 2010

Предупреждение правильное и документация API устарела, объекты возвращаются по ссылке начиная с PHP5.

3 голосов
/ 19 октября 2010

Способ работы со ссылками немного изменился в PHP в PHP 5. Теперь они хотят, чтобы вызываемая функция решила возвращаться по ссылке или по значению, а не по вызывающему. Но обычно PHP может разобраться с этим сам по себе - как в вашем случае он обнаруживает, что эти два объекта являются одним и тем же.

Для получения дополнительной информации о вашем E_STRICT, ознакомьтесь с руководством: http://www.php.net/manual/en/language.references.whatdo.php и как должна быть реализована функция груши: http://www.php.net/manual/en/language.references.return.php. (По моему мнению, большинство частей груши устарели, Zend Framework теперь покрывает большую часть груши.)

Редактировать: Большой пример ссылок:

error_reporting(E_STRICT);
ini_set('display_errors', 1);

class Settings
{
    private static $_instance;

    public static function getInstance()
    {
        if(self::$_instance == null)
        {
            self::$_instance = new Settings();
        }

        return self::$_instance;
    }

    public static function &getInstanceByRef()
    {
        if(self::$_instance == null)
        {
            self::$_instance = new Settings();
        }

        return self::$_instance;
    }

    private $counter = 0;

    private function Settings()
    {
    }

    public function getCounter()
    {
        return $this->counter;
    }

    public function setCounter($value)
    {
        $this->counter = $value;
    }
}

$settings1 = Settings::getInstance();
$settings2 = Settings::getInstance();

echo $settings1->getCounter(); // 0
echo $settings2->getCounter(); // 0

$settings1->setCounter(42);

echo $settings1->getCounter(); // 42
echo $settings2->getCounter(); // 42

$settings3 = &Settings::getInstanceByRef(); // ref to private static $_instance !
$settings4 = &Settings::getInstanceByRef(); // ref to private static $_instance !

echo $settings3->getCounter(); // 42
echo $settings4->getCounter(); // 42

$settings3 = 5;
$settings5 = Settings::getInstance();
echo $settings5; // 5 

Как вы можете видеть, даже без защиты getInstance обрабатывается как ссылка. Если вы хотите использовать ссылки, и вызывающая сторона, и вызываемая функция должны быть помечены как ссылки.

В качестве предупреждения: возврат по ссылке может привести к трудностям поиска ошибок: возврат по ссылке позволяет мне перезаписать закрытый экземпляр, содержащий var. Ожидаемое поведение в PHP: $ settings3 равно 5, но не статический статический $ _instance; Это может привести к очень непредсказуемому коду.

...