PHP foreach изменяет исходные значения массива


151

Я новичок в многомерных массивах, и это меня сильно беспокоит.

Мой массив выглядит следующим образом:

$fields = array(
    "names" => array(
         "type"         => "text",
         "class"        => "name",
         "name"         => "name",
         "text_before"  => "name",
         "value"        => "",
         "required"     => true,
    )
)

Затем у меня есть функция, проверяющая, заполнены ли эти поля, если они требуются.

function checkForm($fields){
    foreach($fields as $field){
        if($field['required'] && strlen($_POST[$field['name']]) <= 0){
            $fields[$field]['value'] = "Some error";
        }
    }
    return $fields;
}

Теперь моя проблема в этой строке

$fields[$field]['value'] = "Some error";

Я хочу изменить содержимое исходного массива, поскольку я возвращаю его, но как мне получить имя текущего массива (имена в этом примере) в моем цикле foreach?



1
Неважно, насколько вы новичок (или раньше) - это то, что вы можете прочитать в документации PHP: php.net/manual/en/control-structures.foreach.php
Николай Иванов

Ответы:


279

В PHP передача по ссылке ( &) ... спорна. Я рекомендую не использовать его, если вы не знаете, зачем он вам нужен, и не проверяете результаты.

Я бы порекомендовал сделать следующее:

foreach ($fields as $key => $field) {
    if ($field['required'] && strlen($_POST[$field['name']]) <= 0) {
        $fields[$key]['value'] = "Some error";
    }
}

Поэтому в основном используйте, $fieldкогда вам нужны значения и $fields[$key]когда вам нужно изменить данные.


Отлично, это работает! Сначала попробовал что-то вроде этого, но, думаю, я где-то облажался :) Теперь я буду использовать ваш пример тысячу раз и никогда не забуду! :)
Jeppe

Рад, что помог. Кроме того, я рекомендую прочитать статью, на которую я указал , а также официальную документацию для foreach ( php.net/manual/ro/control-structures.foreach.php )
Влад Преда

4
Итог: если вы собираетесь менять массив / переменную - тогда вам следует использовать ссылку. Это быстрее, чище и читабельнее.
Лулу

2
Мне любопытно, почему переход по ссылке в a foreachдолжен вызывать споры? Это не похоже на вызов функции со скрытыми побочными эффектами или чем-то еще.
UncaAlby

1
Спасибо за то, что вы сказали, что «передача по ссылке (&) ... спорна», а не «не передавать по ссылке» или «передача по ссылке - зло». Меньше шансов начать пламенную войну. :)
Шон Бин

169

Использование &:

foreach($arr as &$value) {
    $value = $newVal;
}

&передает значение массива как ссылку и не создает новый экземпляр переменной. Таким образом, если вы измените ссылку, исходное значение изменится.

Документация PHP для передачи по ссылке

Редактировать 2018

Этот ответ, похоже, одобряется многими людьми в Интернете, поэтому я решил добавить больше информации и предостережения.
Хотя передача по ссылке в foreach(или функциях) является простым и коротким решением, для многих новичков это может быть опасной ловушкой.

  1. Циклы в PHP не имеют собственной области действия. - @Mark Amery

    Это может стать серьезной проблемой, если переменные повторно используются в одной и той же области. Другой вопрос SO хорошо иллюстрирует, почему это может быть проблемой .

  2. Поскольку foreach полагается на указатель внутреннего массива в PHP 5, его изменение в цикле может привести к неожиданному поведению. - Документация PHP для foreach.

    Удаление записи или изменение значения хеш-функции (ключа) во время итерации того же цикла может привести к потенциально неожиданному поведению в PHP <7. Проблема становится еще более сложной, когда сам массив является ссылкой.

  3. Производительность по каждому.
    В общем, PHP предпочитает передачу по значению из-за функции копирования при записи. Это означает, что внутренне PHP не будет создавать повторяющиеся данные, если только их копию не нужно изменить. Спорный вопрос, foreachдаст ли передача по ссылке в улучшении производительности. Как всегда, вам необходимо протестировать свой конкретный сценарий и определить, какой вариант использует меньше памяти и процессорного времени. Для получения дополнительной информации см. Сообщение SO, указанное ниже NikiC.

  4. Читаемость кода.
    Создание ссылок в PHP - это то, что быстро выходит из-под контроля. Если вы новичок и не полностью контролируете то, что делаете, лучше держаться подальше от рекомендаций. Дополнительные сведения об &операторе см. В этом руководстве: Справка - что означает этот символ в PHP?
    Для тех, кто хочет узнать больше об этой части языка PHP: Объяснение ссылок на PHP

Очень хорошее техническое объяснение внутренней логики foreachциклов PHP от @NikiC :
как на самом деле работает PHP foreach?


2
Помимо перечисленных проблем, я рекомендую добавить unset($value);после foreachзакрывающей скобки, чтобы гарантировать, что переменная по ссылке больше не доступна после итерации. 3v4l.org/2V2AQ
Уилл Б.

15

Используйте foreach($fields as &$field){- так вы будете работать с исходным массивом.

Здесь больше о переходе по ссылке.


@RBA, пожалуйста, обратитесь к ответам выше - у них гораздо больше деталей и обновлений - я давно не использую php, поэтому не
знаю

1
function checkForm(& $fields){
    foreach($fields as $field){
        if($field['required'] && strlen($_POST[$field['name']]) <= 0){
            $fields[$field]['value'] = "Some error";
        }
    }
    return $fields;
}

Это то, что я бы посоветовал передать по ссылке


Эта техника используется для изменения значения исходной переменной. поскольку PHP поддерживает метод передачи по значению. Нам нужно добавить символ '&' перед переменной, чтобы указать, что значение будет передано по ссылке
Сагар Кадам,

1
Тогда зачем еще возвращаться $fields?
MAZux

Это худший ответ из трех предложенных. Для тех, кто наткнется на этот ответ, пожалуйста, не создавайте свои функции для изменения данных на месте и их возврата. Для первоначального автора: Вы не предоставили никаких объяснений, почему это решение будет лучше, чем другие, или как оно вообще работает ..
Дхарман

-6

Попробуй это

function checkForm($fields){
        foreach($fields as $field){
            if($field['required'] && strlen($_POST[$field['name']]) <= 0){
                $field['value'] = "Some error";
            }
        }
        return $field;
    }

3
Не делайте этого. Я вижу как минимум две ошибки в вашем коде: присвоение $ field не работает (массив $ fields никогда не изменяется, когда вы это делаете), а return $ field возвращает единственное поле, а не массив.
Степлерфахрер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.