Сообщение об ошибке «Строгие стандарты: только переменные должны передаваться по ссылке»

81
$el = array_shift($instance->find(..))

Приведенный выше код каким-то образом сообщает о предупреждении о строгих стандартах, но это не так:

function get_arr(){
    return array(1, 2);
}
$el = array_shift(get_arr());

Так когда же он все равно сообщит о предупреждении?

user198729
источник
1
Что возвращает $ instance-> find (..)?
Silver Light
2
Вот решение: stackoverflow.com/questions/9848295/…
ajaristi 07
Я думаю, что примеры (или логика) могут быть неправильными в вопросе, поскольку второй пример ( get_arr()функция) действительно создает уведомление о строгих стандартах (проверено PHP 5.2 и PHP 5.5).
MrWhite

Ответы:

93

Рассмотрим следующий код:

error_reporting(E_STRICT);
class test {
    function test_arr(&$a) {
        var_dump($a);
    }
    function get_arr() {
        return array(1, 2);
    }
}

$t = new test;
$t->test_arr($t->get_arr());

Это приведет к следующему результату:

Strict Standards: Only variables should be passed by reference in `test.php` on line 14
array(2) {
  [0]=>
  int(1)
  [1]=>
  int(2)
}

Причина? test::get_arr()Метод не является переменной и при строгом режиме это будет генерировать предупреждение. Такое поведение крайне не интуитивно понятно, поскольку get_arr()метод возвращает значение массива.

Чтобы обойти эту ошибку в строгом режиме, измените подпись метода, чтобы он не использовал ссылку:

function test_arr($a) {
    var_dump($a);
}

Поскольку вы не можете изменить подпись, array_shiftвы также можете использовать промежуточную переменную:

$inter = get_arr();
$el = array_shift($inter);
leepowers
источник
7
@ user198729: Я тоже искал объяснение или исправление и обнаружил, что вы можете использовать current () для первого элемента. Увы, end () не работает для последнего, поскольку он «продвигает внутренний указатель на последний элемент». current (array_reverse (somefunction ())) работает (да, это глупо)
MSpreij
1
Использование currentпредполагает, что указатель массива находится на первом элементе. В большинстве случаев это может быть верным предположением, но его следует остерегаться.
cmbuckley 03
1
@leepowers Конечно, тогда возникла бы та же проблема, что и array_shift()в том, что он ожидает, что ссылка на изменение :-)
cmbuckley
1
@ user198729 Вы можете избежать $intermediateзначения, используя дополнительную пару круглых скобок. $el = array_shift( ( get_arr() ) );. См. Stackoverflow.com/questions/9848295/…
Хлоя,
1
@Chloe Это самое блестящее решение, которое я видел для упрощения кода !! Спасибо!
hargobind
7

$instance->find() возвращает ссылку на переменную.

Вы получаете отчет, когда пытаетесь использовать эту ссылку в качестве аргумента функции, не сохраняя ее предварительно в переменной.

Это помогает предотвратить утечки памяти и, вероятно, станет ошибкой в ​​следующих версиях PHP.

Ваш второй блок кода вызовет ошибку, если он напишет так (обратите внимание &на подпись функции):

function &get_arr(){
    return array(1, 2);
}
$el = array_shift(get_arr());

Итак, быстрое (и не очень хорошее) исправление:

$el = array_shift($tmp = $instance->find(..));

Обычно вы сначала присваиваете временную переменную и отправляете ее в качестве аргумента.

Саги
источник
Теперь должно работать (проверил). Чтобы вернуть ссылку, вы должны объявить ее в подписи метода, а не в операторе возврата (моя ошибка).
Sagi
Нет, я не могу изменить подпись. Промежуточная переменная @pygorex1 может решить эту проблему, но это выглядит избыточным, не так ли?
user198729 01
Я знаю, что вы не можете изменить подпись, просто объяснил, как это происходит. Вы должны использовать временную (= промежуточную) переменную, но вы можете сделать это в той же строке. Посмотрите на мой второй фрагмент кода.
Sagi
4
Я попробовал ваш второй фрагмент, но он не работает. Он работает только в отдельной строке
user198729 01
3
Конечно. Присвоение возвращает присвоенное значение . array_shift($tmp = $instance->find(..))не присваивает значение $instance->find(..)для $tmpи затем передает значение задания на array_shift()- , который не то же самое, передавая $tmpсебя, так не лучше исходной ситуации без уступки.
phils
6

Причина ошибки - использование внутренней функции структур данных программирования PHP, array_shift () [php.net/end].

Функция принимает в качестве параметра массив. Хотя в прототипе « array_shift()руководства» указан амперсанд, в расширенном определении этой функции нет какой-либо предупреждающей документации, а также нет никакого очевидного объяснения того, что параметр на самом деле передается по ссылке.

Возможно, это / понятно /. Однако я не понял, поэтому мне было сложно определить причину ошибки.

Воспроизвести код:

function get_arr()
{
    return array(1, 2);
}
$array = get_arr();
$el = array_shift($array);
Биджу Би Адур
источник
3

Этот код:

$monthly_index = array_shift(unpack('H*', date('m/Y')));

Необходимо заменить на:

$date_time = date('m/Y');
$unpack = unpack('H*', $date_time);
array_shift($unpack);
user6031348
источник
3

Второй фрагмент тоже не работает, вот почему.

array_shift- функция-модификатор, изменяющая свой аргумент. Поэтому он ожидает, что его параметр будет ссылкой, и вы не можете ссылаться на то, что не является переменной. См. Объяснения Расмуса здесь: Строгие стандарты: только переменные должны передаваться по ссылке

user187291
источник
0

Что ж, в таких очевидных случаях вы всегда можете указать PHP подавлять сообщения, используя символ «@» перед функцией.

$monthly_index = @array_shift(unpack('H*', date('m/Y')));

Возможно, такой способ подавления всех ошибок - не лучшая практика программирования , но в определенных случаях (например, в этом) это удобно и приемлемо.

В результате, я уверен, ваш друг "системный администратор" будет доволен менее загрязненным error.log.

Хулио Марчи
источник
Я не знаю, кто проголосовал против этого ответа, но представленное решение ДЕЙСТВИТЕЛЬНО работает и является стандартной техникой PHP. Действительно разочаровывает ... В следующий раз я могу больше не отвечать на вопрос ... :(
Хулио Марчи
5
Я предполагаю, что это произошло потому, что подавление сообщения об ошибке не устраняет проблему с кодом. Что вы будете делать, когда этот тип ошибки изменится с E_STRICT на E_ERROR в будущей версии PHP, и ваш код теперь не запускается, а также не вызывает никаких ошибок / вывода?
Люк
@TinoDidriksen, я понимаю и согласен с причинами, по которым я рекомендую избегать некоторых «вредных привычек», особенно для нового поколения. Однако существует ресурс, который можно использовать, когда (и если) он безопасен и применим в предлагаемом контексте. Если бы подавитель ошибок "@" был отменен, он был бы удален из самого языка. То же, что и eval (может быть злом, но у него есть свои цели). Я против не использования каких-то ресурсов, а обобщения советов. В частности, для предложенного случая его использование не повредит даже для целей отладки.
Хулио Марчи