Вызовите время прохождения по ссылке, два уровня ссылки?

Резюме

Я пытаюсь найти способ изменить ссылочную переменную на два уровня вверх, избегая при этом Deprecated: Call-time pass-by-reference has been deprecated

Исследования, которые я сделал

Я просмотрел это и this, и кажется, что call_user_func_array может отключить предупреждение, однако я думаю, что что-то упускаю.

Проблема

Я использую MongoDB с PHP, следующий метод принадлежит модели и просто проверяет схему ввода, которая передается в нее по ссылке, перед ее сохранением.

// $this->collection is the MongoCollection object
public function save(&$entry) {

    if( empty($entry) ) return false;
    if( !$this->checkSchema($entry) ) $this->throwDbError('Schema violation in `' . get_class($this) . '`');

    try { return $this->collection->save(&$entry); } // <---- want to avoid using &
    catch (Exception $e) { return $this->throwDbError($e); }

}

MongoCollection::save ($this->collection->save) добавит поле _id к $entry с новым идентификатором документа. Однако это изменение не отражается на $entry, который передается в вышеприведенный метод, если только я не передам ему время вызова по ссылке. (По сути, я хочу, чтобы MongoCollection::save мог изменять $entry на два уровня выше)

Хорошо, это мой лучший способ объяснить проблему, дайте мне знать, если вам нужны разъяснения.


person Nathan Kot    schedule 12.10.2012    source источник
comment
// ‹---- хотите избежать использования & --- поэтому удалите его. Поместите курсор прямо перед знаком & и нажмите «Удалить». Серьёзно, в php5 смысла нет. Вы можете только указать, что переменная должна передаваться как ссылка только в объявлении функции.   -  person zerkms    schedule 12.10.2012
comment
я знаю, что могу, но тогда запись $, которая передается в метод выше, не добавляет к ней [_id] (что мне нужно)   -  person Nathan Kot    schedule 12.10.2012
comment
@NathanKot Кажется, $entry должен быть объектом. Либо так, либо помните, что методы могут иметь возвращаемое значение;)   -  person KingCrunch    schedule 12.10.2012
comment
@Nathan Kot: тогда это просто ошибка драйвера mongodb   -  person zerkms    schedule 12.10.2012
comment
@KingCrunch: да, я тоже об этом подумал, но нет - php.net /manual/en/mongocollection.save.php   -  person zerkms    schedule 12.10.2012
comment
@Nathan Kot: как насчет этого: php.net/manual/en/mongocollection. insert.php см. пример №1   -  person zerkms    schedule 12.10.2012
comment
@KingCrunch спасибо, я думал о возврате массива, но это может привести к ошибкам в дальнейшем, поскольку MongoCollection::save просто изменяет _id, поэтому я хочу отразить его функциональность.   -  person Nathan Kot    schedule 12.10.2012
comment
@zerkms save () позволяет создавать новые документы, что мне и нужно, вставка также изменяет _id, но я предполагаю, что у него будет та же проблема?   -  person Nathan Kot    schedule 12.10.2012
comment
@Nathan Kot: вы проверили пример № 1? О, только что проверил ваше местоположение - привет ;-)   -  person zerkms    schedule 12.10.2012
comment
@zerkms аааа, извините, пропустил это хммммм, поэтому передача по ссылке означает, что _id не изменяется, поэтому я понимаю, почему передача ссылки не работает, но почему передача ссылки работает?   -  person Nathan Kot    schedule 12.10.2012
comment
@Nathan Kot: это работает для save(), а не для insert()   -  person zerkms    schedule 12.10.2012
comment
@zerkms о, привет! хаха я из Окленда   -  person Nathan Kot    schedule 12.10.2012
comment
@Nathan Kot: ну, в любом случае, просто не используйте передачу по ссылке в вызове функции, это просто неправильно. И insert() должно работать.   -  person zerkms    schedule 12.10.2012
comment
@zerkms да, избегая передачи по ссылке, но все еще не уверен, как решить с помощью insert(), в любом случае уже поздно, поэтому я посмотрю на это завтра, спасибо за помощь!   -  person Nathan Kot    schedule 12.10.2012
comment
@Nathan Kot: о, да ладно! Это в примере №1 $collection->insert($a); var_dump($a['_id']);   -  person zerkms    schedule 12.10.2012
comment
@zerkms хммм хорошо, теперь я думаю, что мы на другой странице, save() на самом деле также изменяет _id (хотя и не задокументировано?), вы можете найти это здесь во втором ответе, пример № 1 показывает мне, что он не будет изменять ссылки, поэтому неудивительно, что передача указанной записи $ не будет работать , но я не уверен, почему передача в качестве ссылки изменяет _id   -  person Nathan Kot    schedule 12.10.2012
comment
@Nathan Kot: если вы действительно заинтересованы - возьмите исходники и посмотрите, почему это так. Если бы я был на вашем месте, мне бы хватило только рабочего кода (как только там наступит час ночи) ;-)   -  person zerkms    schedule 12.10.2012


Ответы (1)


MongoCollection::save() и MongoCollection::insert() могут изменять свои аргументы, устанавливая ключ _id, хотя это не кажется для save() (скоро исправлю). Внутри оба метода модифицируют необработанный zval, переданный функциям C. Если бы мне пришлось угадывать, это потому, что указание первого аргумента в качестве ссылки сделало бы невозможным передачу литералов массива. Таким образом, расширение в любом случае обманывает и изменяет аргумент с побочным эффектом невозможности изменить что-то, переданное по ссылке.

Я протестировал следующий код, который, кажется, работает за счет копирования аргумента массива в вашем методе сохранения:

public function save(&$entry)
{
    if (empty($entry)) {
        return false;
    }

    if (!$this->checkSchema($entry)) {
        $this->throwDbError('Schema violation in `' . get_class($this) . '`');
    }

    try {
        $entryCopy = $entry;
        $saveResult = $this->collection->save($entryCopy);

        if (!isset($entry['_id']) && isset($entryCopy['_id']) {
            $entry['_id'] = $entryCopy['_id'];
        }

        return $saveResult; 
    } catch (Exception $e) {
        return $this->throwDbError($e);
    }
}

Я полагаю, вы всегда можете скопировать свойство _id обратно в $entry, если хотите. В качестве альтернативы вы можете скопировать массив и просто инициализировать $entry[_id] новым MongoId. экземпляр, если он еще не установлен. По сути, это то, что драйвер делает для вас при вставке документа без _id.

person jmikola    schedule 12.10.2012
comment
это потому, что указание первого аргумента в качестве ссылки сделало бы невозможным передачу литералов массива. Это кажется дерьмовой причиной отказаться от корректности - person newacct; 13.10.2012
comment
@jmikola спасибо за ваш ответ, это подход (хотя и неэлегантный), который я в итоге выбрал, но думаю, что я изменю его на ваше второе предложение, где я инициализирую MongoID вручную - person Nathan Kot; 13.10.2012