Función de fusión para PHP?

131

Muchos lenguajes de programación tienen una función de fusión (por ejemplo , devuelve el primer valor no NULL ). PHP, lamentablemente en 2009, no lo hace.

¿Cuál sería una buena manera de implementar uno en PHP hasta que PHP mismo obtenga una función de fusión?

mikl
fuente
11
Relacionado: el nuevo operador de fusión nula ?? para PHP 7.
kojiro
Puede encontrar más información sobre el operador de fusión nula aquí: stackoverflow.com/questions/33666256/…
Peter
1
Solo para tener en cuenta, PHP7 implementó esta función
Grzegorz
@Grzegorz: Un operador no es una función, o dónde encontró esa función nueva en PHP 7;)
hakre
Por función no quise decir función;) Característica. No fui precisa. Gracias :)
Grzegorz

Respuestas:

194

Hay un nuevo operador en php 5.3 que hace esto: ?:

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

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

// B
echo false ?: 'B';

// B
echo null ?: 'B';

Fuente: http://www.php.net/ChangeLog-5.php#5.3.0

Kevin
fuente
25
¿Qué pasa con los accesos directos ternarios múltiples, sería algo así como "echo $ a?: $ B?: $ C?: $ D;" ¿trabajo?
ChrisR
55
No funciona como se esperaba para las matrices. Por ejemplo, al intentar verificar si un elemento de matriz indefinido es falsey, se producirá un error. $input['properties']['range_low'] ?: '?'
Keyo
55
Debería recibir un aviso de índice indefinido independientemente del uso del operador de fusión.
Kevin
2
Múltiples argumentos falsey devuelven el último argumento, array() ?: null ?: falseretornos false. El operador es realmente cuerdo.
Brad Koch
66
Tenga en cuenta que esto no solo acepta la fusión no nula como en otros idiomas, sino cualquier valor, que se convertirá implícitamente en un booleano. Así que asegúrate de
repasar
65

PHP 7 introdujo un operador de fusión real :

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

Si el valor anterior ??no existe o es nullel valor posterior a?? toma.

La mejora con respecto al ?:operador mencionado es que ??también maneja variables indefinidas sin lanzar un E_NOTICE.

flori
fuente
¡Finalmente no más isset () y empty () por todo el lugar!
George Kagan
77
@timeNomad que aún necesitará está vacío, solo comprueba si es nulo
Nabeel Khan
La única forma de obtener una "fusión falsa" segura es usar un poco de ambos:($_GET['doesNotExist'] ?? null) ?: 'fallback'
Nathan Baulch
La ventaja de ?:over ??, sin embargo, es que fusiona también valores vacíos, lo que ??no funciona. De manera similar al comportamiento del operador lógico OR en JavaScript (es decir $val || 'default'), creo que ?:es una forma más práctica de fusión si en nuestra práctica finalmente nos encontramos manejando tanto vacíos como nulos de la misma manera (es decir $val ?: 'default'). Y si desea forzar el problema aún más y tragar E_NOTICE, podría argumentar esto incluso:echo @$val ?: 'default';
Matt Borja
29

Primer hit para "php coalesce" en 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
fuente
9
Ahorre un poco de ram y no duplique los args en una matriz, solo haga foreach (func_get_args () como $ arg) {}
TravisO
17
@ [Alfred, Ciaran]: eres incorrecto. foreach () evalúa el primer argumento solo una vez, para obtener una matriz, y luego itera sobre ella.
gahooa
66
Poner func_get_args () dentro del foreach (aquí como $ arg) no cambiará nada desde el punto de vista del rendimiento.
Savageman
77
@Savageman ... exactamente ... si está pensando en exprimir este milisegundo de rendimiento o unos pocos bytes de memoria de su aplicación, probablemente esté viendo el cuello de botella de rendimiento / memoria incorrecto
ChrisR
44
Irónicamente, este es ahora el primer éxito de "php coalesce" en Google.
Will Shaver
18

Realmente me gusta el operador? Desafortunadamente, aún no está implementado en mi entorno de producción. Entonces uso el equivalente de esto:

function coalesce() {
  return array_shift(array_filter(func_get_args()));
}
Ethan Kent
fuente
1
esta es una fusión 'verdadera', usando array_filter para deshacerse de todo lo que se evalúa como falso (incluido nulo) en los n argumentos pasados. Supongo que usar shift en lugar del primer elemento de la matriz es de alguna manera más robusto, pero eso parte no lo sé. ver: php.net/manual/en/…
Adam Tolley
3
Me gusta, pero tengo que estar de acuerdo con @hakre: coalescese supone que devuelve el primer argumento no nulo que encuentra, que incluiría FALSE. Sin FALSEembargo, esta función se descartará , probablemente no lo que op tiene en mente (al menos no lo que quisiera de una coalescefunción).
Madbreaks
1
Solo las variables deben pasarse por referencia
Ben Sinclair
9

Vale la pena señalar que, debido al tratamiento de PHP de variables no inicializadas e índices de matriz, cualquier tipo de función de fusión es de uso limitado. Me encantaría poder hacer esto:

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

Pero esto, en la mayoría de los casos, hará que PHP produzca un error con un E_NOTICE. La única forma segura de probar la existencia de una variable antes de usarla es usarla directamente en empty () o isset (). El operador ternario sugerido por Kevin es la mejor opción si sabe que se sabe que todas las opciones de su fusión están inicializadas.

Andrés
fuente
En este caso, las uniones de matriz funcionan bastante bien ( $getstuff = $_GET+$_SESSION+array('id'=>null);$id=$getstuff['id'];).
Brilliand
@Quill, ¿qué se supone que significa eso? ¿La solución sugerida con referencia?
Ben Sinclair
PHP 7 presenta el nuevo y encantador operador ternario isset?? para hacer que esta operación muy común sea más concisa.
botimer
6

Asegúrese de identificar exactamente cómo desea que esta función funcione con ciertos tipos. PHP tiene una amplia variedad de verificación de tipos o funciones similares, así que asegúrese de saber cómo funcionan. Este es un ejemplo de comparación de is_null () y 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>';
}

Como puede ver, empty () devuelve verdadero para todos estos, pero is_null () solo lo hace para 2 de ellos.

Peter Bailey
fuente
2

Estoy ampliando la respuesta publicada por Ethan Kent . Esa respuesta descartará los argumentos no nulos que se evalúan como falsos debido al funcionamiento interno de array_filter , que no es lo que coalescenormalmente hace una función. Por ejemplo:

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

Ups

Para superar esto, se requiere un segundo argumento y una definición de función. La función invocable es responsable de decir array_filtersi se agrega o no el valor de matriz actual a la matriz de resultados:

// "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'));
}

Sería bueno si pudieras simplemente pasar isseto 'isset'como el segundo argumento array_filter, pero no tanta suerte.

Madbreaks
fuente
0

Actualmente estoy usando esto, pero me pregunto si no podría mejorarse con algunas de las nuevas características en PHP 5.

function coalesce() {
  $args = func_get_args();
  foreach ($args as $arg) {
    if (!empty($arg)) {
    return $arg;
    }
  }
  return $args[0];
}
mikl
fuente
0

PHP 5.3+, con cierres:

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

Demostración: https://eval.in/187365

Paulo Freitas
fuente
Solo las variables deben pasarse por referencia
Ben Sinclair
Sí, rompí las estrictas reglas para la demostración, solo para hacerlo simple. :)
Paulo Freitas