Funkcja koalescencji dla PHP?

131

Wiele języków programowania ma funkcję koalescencji (zwraca pierwszą wartość inną niż NULL, przykład ). Niestety w 2009 roku PHP nie.

Jaki byłby dobry sposób na zaimplementowanie go w PHP, dopóki sam PHP nie otrzyma funkcji łączenia?

mikl
źródło
11
Związane z: nowy operator koalescencji zerowej ?? dla PHP 7.
kojiro
Więcej informacji na temat zerowego operatora coalesce można znaleźć tutaj - stackoverflow.com/questions/33666256/ ...
Peter
1
Warto zauważyć, że PHP7 zaimplementowało tę funkcję
Grzegorz
@Grzegorz: Operator nie jest funkcją, albo gdzie znalazłeś tę funkcję jako nową w PHP 7;)
hakre
Przez funkcję nie miałem na myśli funkcji;) Cecha. Nie byłem dokładny. Dziękuję :)
Grzegorz

Odpowiedzi:

194

W php 5.3 jest nowy operator, który robi to: ?:

// A
echo 'A' ?: 'B';

// B
echo '' ?: 'B';

// B
echo false ?: 'B';

// B
echo null ?: 'B';

Źródło: http://www.php.net/ChangeLog-5.php#5.3.0

Kevin
źródło
25
A co z wieloma trójskładnikowymi skrótami, czy coś w stylu „echo $ a?: $ B?: $ C?: $ D;” praca?
ChrisR
5
Nie działa zgodnie z oczekiwaniami w przypadku tablic. Na przykład próba sprawdzenia, czy niezdefiniowany element tablicy jest fałszywa, spowoduje błąd. $input['properties']['range_low'] ?: '?'
Keyo,
5
Powinieneś otrzymać powiadomienie o niezdefiniowanym indeksie niezależnie od korzystania z operatora łączenia.
Kevin,
2
Wiele argumentów falsey zwraca ostatni argument, array() ?: null ?: falsezwraca false. Operator jest rzeczywiście rozsądny.
Brad Koch
6
Należy pamiętać, że nie akceptuje to tylko wartości niezerowej, takiej jak coalesce w innych językach, ale dowolnej wartości, która zostanie niejawnie przekonwertowana na wartość logiczną. Więc upewnij się, że odświeżyłeś swoje zasady castingu
DanMan
65

PHP 7 wprowadził prawdziwy operator koalescencji :

echo $_GET['doesNotExist'] ?? 'fallback'; // prints 'fallback'

Jeśli wartość przed wartością ??nie istnieje lub jest nullwartością po ??wykonaniu.

Ulepszenie w stosunku do wspomnianego ?:operatora polega na tym, że ??obsługuje on również niezdefiniowane zmienne bez rzucania E_NOTICE.

flori
źródło
Wreszcie koniec z isset () i empty () w każdym miejscu!
George Kagan
7
@timeNomad, którego nadal będziesz potrzebować, jest pusty, sprawdza tylko wartość null
Nabeel Khan
Jedynym sposobem na bezpieczne „falsy-coalesce” jest użycie obu:($_GET['doesNotExist'] ?? null) ?: 'fallback'
Nathan Baulch,
Zaletą ?:Over jest ??jednak to, że łączy również puste wartości, co ??nie działa. Podobnie do zachowania operatora logicznego OR w JavaScript (tj. $val || 'default'), Uważałbym ?:za bardziej praktyczną formę łączenia, jeśli w naszej praktyce ostatecznie radzimy sobie z pustymi i zerowymi wartościami w ten sam sposób (tj $val ?: 'default'.). A jeśli chcesz pogłębić problem i przełknąć E_NOTICE, możesz nawet spierać się o to:echo @$val ?: 'default';
Matt Borja
29

Pierwsze trafienie w wyszukiwarkę „php coalesce” w Google.

function coalesce() {
  $args = func_get_args();
  foreach ($args as $arg) {
    if (!empty($arg)) {
      return $arg;
    }
  }
  return NULL;
}

http://drupial.com/content/php-coalesce

Will Shaver
źródło
9
Zaoszczędź trochę pamięci RAM i nie powielaj argumentów w tablicy, po prostu wykonaj foreach (func_get_args () jako $ arg) {}
TravisO
17
@ [Alfred, Ciaran] - mylisz się. foreach () oblicza pierwszy argument tylko raz, aby pobrać tablicę, a następnie wykonuje iterację po niej.
gahooa
6
Umieszczenie func_get_args () wewnątrz foreach (tutaj jako $ arg) nie zmieni niczego z punktu widzenia wydajności.
Savageman
7
@Savageman ... dokładnie ... jeśli myślisz o wyciśnięciu tej milisekundy wydajności lub kilku bajtów pamięci z aplikacji, prawdopodobnie patrzysz na niewłaściwe wąskie gardło wydajności / pamięci
ChrisR
4
Jak na ironię, jest to teraz pierwsze trafienie w wyszukiwarkę „php coalesce”.
Will Shaver
18

Bardzo podoba mi się operator?:. Niestety nie został jeszcze wdrożony na moim środowisku produkcyjnym. Więc używam odpowiednika tego:

function coalesce() {
  return array_shift(array_filter(func_get_args()));
}
Ethan Kent
źródło
1
jest to łączenie „zgodne z prawdą”, przy użyciu funkcji array_filter, aby pozbyć się wszystkiego, co daje wartość false (w tym null) w przekazanych n argumentach. Domyślam się, że użycie shift zamiast pierwszego elementu w tablicy jest bardziej niezawodne, ale część nie wiem. patrz: php.net/manual/en/…
Adam Tolley
3
Podoba mi się, ale muszę się zgodzić z @hakre - coalescema zwrócić pierwszy napotkany argument o wartości innej niż NULL , który powinien zawierać FALSE. Ta funkcja FALSEjednak odrzuci , prawdopodobnie nie to, co op ma na myśli (przynajmniej nie to, czego chciałbym od coalescefunkcji).
Madbreaks
1
Tylko zmienne powinny być przekazywane przez odniesienie
pronebird
10

Warto zauważyć, że ze względu na sposób traktowania przez PHP niezinicjalizowanych zmiennych i indeksów tablicowych, wszelkiego rodzaju funkcje koalescencji mają ograniczone zastosowanie. Bardzo chciałbym móc to zrobić:

$id = coalesce($_GET['id'], $_SESSION['id'], null);

Ale w większości przypadków spowoduje to błąd PHP z E_NOTICE. Jedynym bezpiecznym sposobem sprawdzenia istnienia zmiennej przed jej użyciem jest użycie jej bezpośrednio w empty () lub isset (). Operator trójskładnikowy sugerowany przez Kevina jest najlepszą opcją, jeśli wiesz, że wszystkie opcje w Twojej koalescencji są inicjowane.

Andrzej
źródło
W tym przypadku unii tablicowe działają całkiem nieźle ( $getstuff = $_GET+$_SESSION+array('id'=>null);$id=$getstuff['id'];).
Brilliand
@Quill co to ma znaczyć? Czy zaproponowałeś rozwiązanie w odniesieniu do odniesienia?
pronebird
PHP 7 wprowadza piękny, nowy operator trójskładnikowy isset , ??aby uczynić tę bardzo powszechną operację bardziej zwięzłą.
botimer
6

Upewnij się, że dokładnie zidentyfikowałeś, jak chcesz, aby ta funkcja działała z określonymi typami. PHP ma szeroką gamę funkcji sprawdzania typu lub podobnych, więc upewnij się, że wiesz, jak one działają. To jest przykładowe porównanie is_null () i empty ()

$testData = array(
  'FALSE'   => FALSE
  ,'0'      => 0
  ,'"0"'    => "0"  
  ,'NULL'   => NULL
  ,'array()'=> array()
  ,'new stdClass()' => new stdClass()
  ,'$undef' => $undef
);

foreach ( $testData as $key => $var )
{
  echo "$key " . (( empty( $var ) ) ? 'is' : 'is not') . " empty<br>";
  echo "$key " . (( is_null( $var ) ) ? 'is' : 'is not')  . " null<br>";
  echo '<hr>';
}

Jak widać, empty () zwraca prawdę dla wszystkich z nich, ale is_null () robi to tylko dla dwóch z nich.

Peter Bailey
źródło
2

Rozwijam odpowiedź zamieszczoną przez Ethana Kenta . Ta odpowiedź spowoduje odrzucenie niezerowych argumentów, których wynikiem jest fałsz ze względu na wewnętrzne działanie array_filter , co nie jest tym, co coalescezwykle robi funkcja. Na przykład:

echo 42 === coalesce(null, 0, 42) ? 'Oops' : 'Hooray';

Ups

Aby temu zaradzić, wymagany jest drugi argument i definicja funkcji. Funkcja wywoływalna jest odpowiedzialna za array_filterokreślenie, czy dodać bieżącą wartość tablicy do tablicy wyników:

// "callable"
function not_null($i){
    return !is_null($i);  // strictly non-null, 'isset' possibly not as much
}

function coalesce(){
    // pass callable to array_filter
    return array_shift(array_filter(func_get_args(), 'not_null'));
}

Byłoby miło, gdybyś mógł po prostu przekazać issetlub 'isset'jako drugi argument array_filter, ale nie ma takiego szczęścia.

Madbreaks
źródło
0

Obecnie tego używam, ale zastanawiam się, czy nie można go ulepszyć za pomocą niektórych nowych funkcji PHP 5.

function coalesce() {
  $args = func_get_args();
  foreach ($args as $arg) {
    if (!empty($arg)) {
    return $arg;
    }
  }
  return $args[0];
}
mikl
źródło
0

PHP 5.3+, z zamknięciami:

function coalesce()
{
    return array_shift(array_filter(func_get_args(), function ($value) {
        return !is_null($value);
    }));
}

Demo: https://eval.in/187365

Paulo Freitas
źródło
Tylko zmienne powinny być przekazywane przez odniesienie
pronebird
Tak, złamałem surowe zasady dotyczące wersji demonstracyjnej, żeby było to proste. :)
Paulo Freitas