PHP zawsze zmienia oryginalne wartości tablicowe

143

Jestem bardzo nowy w wielowymiarowych tablicach i bardzo mnie to niepokoi.

Moja tablica wygląda następująco:

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

Następnie dostałem funkcję sprawdzającą, czy te dane wejściowe są wypełnione, jeśli są wymagane.

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

Teraz moim problemem jest ta linia

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

Chcę zmienić zawartość oryginalnej tablicy, ponieważ zwracam to, ale jak uzyskać nazwę bieżącej tablicy (nazwy w tym przykładzie) w mojej pętli foreach?

Jeppe
źródło
1
Bez względu na to, jak nowy jesteś (lub byłeś) - to jest coś, co możesz przeczytać w dokumentacji PHP: php.net/manual/en/control-structures.foreach.php
Nikolay Ivanov

Odpowiedzi:

261

W PHP przekazywanie przez referencję ( &) jest ... kontrowersyjne. Nie radzę go używać, chyba że wiesz, dlaczego go potrzebujesz i przetestujesz wyniki.

Poleciłbym wykonanie następujących czynności:

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

Zasadniczo używaj, $fieldgdy potrzebujesz wartości i $fields[$key]kiedy musisz zmienić dane.

Vlad Preda
źródło
Świetnie, to działa! Najpierw próbowałem czegoś takiego, ale chyba coś schrzaniłem :) Teraz użyję twojego przykładu tysiąc razy i nigdy nie zapomnę! :)
Jeppe
Cieszę się, że pomogło. Polecam również przeczytanie artykułu, do którego podlinkowałem, a także oficjalnej dokumentacji dla foreach ( php.net/manual/ro/control-structures.foreach.php )
Vlad Preda
4
Konkluzja: jeśli zamierzasz zmienić tablicę / zmienną - powinieneś użyć referencji. Jest szybszy, czystszy i bardziej czytelny.
Lulu,
2
Jestem ciekaw, dlaczego przekazywanie przez odniesienie w a foreachpowinno być kontrowersyjne? To nie jest tak, że jest to wywołanie funkcji z ukrytymi efektami ubocznymi czy cokolwiek.
UncaAlby
1
Dziękuję za stwierdzenie, że „przekazywanie przez odniesienie (&) jest ... kontrowersyjne”, a nie „nie przekazuj przez odniesienie” lub „przekazywanie przez odniesienie jest złe”. Mniejsze prawdopodobieństwo rozpoczęcia wojny z płomieniami. :)
Sean the Bean
163

Zastosowanie &:

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

&przekazuje wartość tablicy jako odniesienie i nie tworzy nowego wystąpienia zmiennej. Dlatego jeśli zmienisz odniesienie, oryginalna wartość ulegnie zmianie.

http://php.net/manual/en/language.references.pass.php

Edit 2018
Wydaje się, że ta odpowiedź jest faworyzowana przez wiele osób w Internecie, dlatego postanowiłem dodać więcej informacji i słów przestrogi.
Chociaż przekazywanie przez odniesienie w foreach(lub funkcjach) jest czystym i krótkim rozwiązaniem, dla wielu początkujących może to być niebezpieczna pułapka.

  1. Pętle w PHP nie mają własnego zakresu. - @Mark Amery

    Może to stanowić poważny problem, gdy zmienne są ponownie używane w tym samym zakresie. Kolejne pytanie SO dobrze ilustruje, dlaczego może to być problem.

  2. Ponieważ foreach opiera się na wewnętrznym wskaźniku tablicy w PHP 5, zmiana go w pętli może prowadzić do nieoczekiwanego zachowania. - Dokumentacja PHP dla foreach

    Cofnięcie rekordu lub zmiana wartości skrótu (klucza) podczas iteracji w tej samej pętli może prowadzić do potencjalnie nieoczekiwanych zachowań w PHP <7. Problem staje się jeszcze bardziej skomplikowany, gdy sama tablica jest referencją.

  3. Wydajność dla wszystkich.
    Ogólnie PHP preferuje przekazywanie wartości ze względu na funkcję kopiowania przy zapisie. Oznacza to, że wewnętrznie PHP nie utworzy zduplikowanych danych, chyba że ich kopia będzie wymagać zmiany. Dyskusyjne jest, czy przekazanie przez odniesienie foreachzapewniłoby poprawę wydajności. Jak zawsze, musisz przetestować swój konkretny scenariusz i określić, która opcja zużywa mniej pamięci i czasu procesora. Aby uzyskać więcej informacji, zobacz post dotyczący SO, do którego link NikiC zamieszcza poniżej.

  4. Czytelność kodu.
    Tworzenie referencji w PHP to coś, co szybko wymyka się spod kontroli. Jeśli jesteś nowicjuszem i nie masz pełnej kontroli nad tym, co robisz, najlepiej trzymać się z dala od odniesień. Więcej informacji o &operatorze znajdziesz w tym przewodniku: Odniesienie - Co ten symbol oznacza w PHP?
    Dla tych, którzy chcą dowiedzieć się więcej o tej części języka PHP : Wyjaśnienie referencji PHP

Bardzo ładne techniczne wyjaśnienie autorstwa @NikiC wewnętrznej logiki pętli foreach PHP:
Jak właściwie działa PHP „foreach”?

Dharman
źródło
Oprócz wymienionych problemów zalecam dodanie unset($value);po foreachnawiasie zamykającym, aby zmienna przez odniesienie nie była już dostępna po iteracji. 3v4l.org/2V2AQ
fyrye
15

Użyj foreach($fields as &$field){- dzięki czemu będziesz pracować z oryginalną tablicą.

Tutaj jest więcej o przekazywaniu przez odniesienie.

k102
źródło
@RBA pls odnoszą się do powyższych odpowiedzi - mają znacznie więcej szczegółów i aktualizacji - od jakiegoś czasu nie używam php, więc nie jestem świadomy żadnych aktualizacji na ten temat
k102
1
function checkForm(& $fields){
    foreach($fields as $field){
        if($field['required'] && strlen($_POST[$field['name']]) <= 0){
            $fields[$field]['value'] = "Some error";
        }
    }
    return $fields;
}

To właśnie sugerowałbym przekazać przez odniesienie

Sagar Kadam
źródło
Technika ta służy do zmiany wartości oryginalnej zmiennej. ponieważ PHP obsługuje technikę przekazywania wartości. Musimy dodać znak
``
1
Więc dlaczego wciąż wracasz $fields?
MAZux
To jest najgorsza z trzech sugerowanych odpowiedzi. Ktokolwiek natknie się na tę odpowiedź, nie projektuj swoich funkcji w celu zmiany danych i zwrócenia ich. Dla oryginalnego autora: Nie
podaliście
-6

Spróbuj tego

function checkForm($fields){
        foreach($fields as $field){
            if($field['required'] && strlen($_POST[$field['name']]) <= 0){
                $field['value'] = "Some error";
            }
        }
        return $field;
    }
Nirmal Ram
źródło
3
Nie rób tego. Widzę co najmniej dwie rzeczy nie tak z twoim kodem: przypisanie do pola $ nie działa (tablica $ fields nigdy nie jest modyfikowana, gdy to robisz), a zwracane pole $ zwraca pojedyncze pole, a nie tablicę.
Staplerfahrer