2009-03-27 9 views

Respuesta

32

PHP no admite parámetros con nombre para funciones per se. Sin embargo, hay algunas maneras de evitar esto:

  1. Utilice una matriz como único argumento para la función. Entonces puedes extraer valores de la matriz. Esto permite usar argumentos con nombre en la matriz.
  2. Si desea permitir un número opcional de argumentos según el contexto, puede usar func_num_args y func_get_args en lugar de especificar los parámetros válidos en la definición de la función. Luego, según el número de argumentos, las longitudes de cadena, etc., puede determinar qué hacer.
  3. Pasa un valor nulo a cualquier argumento que no quieras especificar. Realmente no lo estoy pasando, pero funciona.
  4. Si está trabajando en un contexto de objeto, puede utilizar el método mágico __call() para manejar estos tipos de solicitudes, de modo que pueda enrutar a métodos privados según los argumentos que se hayan pasado.
+0

Aunque no hay soporte implícito, existen mejores soluciones que las sugeridas por la respuesta aceptada. Vea la solución de Walf a continuación. Hay más detalles si lo necesitas en mi solución (¡que juro que llegué de manera independiente!) – DJDave

3

No. La forma habitual de hacer esto es con algunas heurísticas para determinar qué parámetro se ha implicado, como longitud de la cadena, mecanografía, etc.

En términos generales, se había escriba la función para tomar los parámetros en el orden de más requerido a menos requerido.

+0

La heurística solo funciona de manera confiable cuando los parámetros son inconfundibles por tipo, como lo hace jQuery. Otros métodos, como la longitud de la cuerda, es un accidente que está por ocurrir. – Walf

3

La manera que desea: no.

Usted podría tomar alguna marca especial, como NULL tener en cuenta que el valor no se suministra:

function foo($firstName, $lastName = 'doe') { 
    if (is_null($firstName)) 
     $firstName = 'john'; 
    echo $firstName . " " . $lastName; 
} 

foo(null, 'smith'); 
6

Si tiene varios parámetros opcionales, una solución consiste en pasar un solo parámetro que es un hash-matriz:

function foo(array $params = array()) { 
    echo $params['firstName'] . " " . $params['lastName']; 
} 

foo(array('lastName'=>'smith')); 

Por supuesto, en esta solución no hay validación de que los campos de la matriz de hash están presentes, o deletreados correctamente. Todo depende de usted para validar.

+0

¡Maldición, me acabas de ganar! – Teifion

-1

No, no hay pero se puede usar una matriz:

function foo ($nameArray) { 
    // Work out which values are missing? 
    echo $nameArray['firstName'] . " " . $nameArray['lastName']; 
} 

foo(array('lastName'=>'smith')); 
+1

Variable no ajustada !! –

+0

need => not = –

+0

¡Vaya! No he codificado PHP durante unas semanas, he estado usando demasiado Python. – Teifion

-1

Lamentablemente lo que estamos tratando de hacer no tiene forma de "azúcar sintáctica" de hacerlo. Son todas formas variadas de WTF.

Si necesita una función que toma un número indefinido de parámetros arbitrarios,

function foo() { 
    $args = func_get_args(); 
    # $args = arguments in order 
} 

hará el truco. Trate de evitar usarlo demasiado, porque para Php esto es un poco mágico.

De forma opcional, podría aplicar valores predeterminados y hacer cosas extrañas según el recuento de parámetros.

function foo(){ 
    $args = func_get_args(); 
    if(count($args) < 1){ 
     return "John Smith"; 
    } 
    if(count($args) < 2) { 
     return "John " .$args[0]; 
    } 
    return $args[0] . " " . $args[1]; 
} 

Además, se puede emular opcionalmente parámetros de estilo de Perl,

function params_collect($arglist){ 
    $config = array(); 
    for($i = 0; $i < count($arglist); $i+=2){ 
     $config[$i] = $config[$i+1]; 
    } 
    return $config; 
} 
function param_default($config, $param, $default){ 
    if(!isset($config[$param])){ 
      $config[$param] = $default; 
    } 
    return $config; 
} 

function foo(){ 
    $args = func_get_args(); 
    $config = params_collect($args); 
    $config = param_default($config, 'firstname' , 'John'); 
    $config = param_default($config, 'lastname' , 'Smith'); 
    return $config['firstname'] . ' ' . $config['lastname']; 
} 

foo('firstname' , 'john', 'lastname' , 'bob'); 
foo('lastname' , 'bob', 'firstname', 'bob'); 
foo('firstname' , 'smith'); 
foo('lastname', 'john'); 

Por supuesto, sería ser más fácil usar un argumento de matriz aquí, pero usted está permitido tener la opción (incluso malas maneras) de hacer cosas.

señaló que esto es más agradable en Perl porque puede hacer simplemente foo (firstname => 'john');

+0

Nota para mí: la aplicación de la lógica de Perl a PHP hace que las cosas vayan bien. Quédate con Perl y nunca mires atrás. –

21

Una variación de la técnica de matriz que permite una fácil configuración de valores por defecto:

function foo($arguments) { 
    $defaults = array(
    'firstName' => 'john', 
    'lastName' => 'doe', 
); 

    $arguments = array_merge($defaults, $arguments); 

    echo $arguments['firstName'] . ' ' . $arguments['lastName']; 
} 

Uso:

foo(array('lastName' => 'smith')); // output: john smith 
+0

Este $ default y array_merge está en mi código PHP. +1 – gnud

+1

Para matrices asociativas, '$ arguments + = $ defaults;' es más limpio. – Walf

9

Se podría refactorizar su código levemente:

function foo($firstName = NULL, $lastName = NULL) 
{ 
    if (is_null($firstName)) 
    { 
     $firstName = 'john'; 
    } 
    if (is_null($lastName)) 
    { 
     $lastName = 'doe'; 
    } 

    echo $firstName . " " . $lastName; 
} 

foo(); // john doe 
foo('bill'); // bill doe 
foo(NULL,'smith'); // john smith 
foo('bill','smith'); // bill smith 
+0

Muchas gracias.:) Este es el mejor método para hacer esto ... ¿Están todos los demás ciegos? –

1

Existen algunas implementaciones de estilo 'opciones' mencionadas aquí. Ninguno hasta el momento es muy a prueba de balas si planea usarlos como estándar. Prueba este patrón:

function some_func($required_parameter, &$optional_reference_parameter = null, $options = null) { 
    $options_default = array(
     'foo' => null, 
    ); 
    extract($options ? array_intersect_key($options, $options_default) + $options_default : $options_default); 
    unset($options, $options_default); 

    //do stuff like 
    if ($foo !== null) { 
     bar(); 
    } 
} 

Esto le da la función de las variables locales (solo $foo en este ejemplo) e impide la creación de cualquier variable que no tienen un defecto. Esto es para que nadie pueda sobrescribir accidentalmente otros parámetros u otras variables dentro de la función.

1

Ojalá esta solución hubiera estado en SO cuando comencé a usar PHP hace 2.5 años. Funciona muy bien en los ejemplos que he creado, y no veo por qué no debe ser completamente extensible. Ofrece las siguientes ventajas con respecto a los propuestos anteriormente:

(i) todos los accesos a los parámetros dentro de la función es mediante variables con nombre, como si los parámetros fueron totalmente declarados, en lugar de requerir acceso a matriz

(ii) es muy rápido y fácil de adaptar las funciones existentes

(iii) solo se requiere una línea de código adicional para cualquier función (además de la necesidad ineludible de definir sus parámetros predeterminados, que estaría haciendo en la función de firma de todos modos, pero en su lugar los defines en una matriz). El crédito por la línea adicional se debe totalmente a Bill Karwin. Esta línea es idéntica para cada función.

Método

definir su función con sus parámetros obligatorios, y un conjunto opcional

declara su parámetros opcionales como variables locales

El quid: reemplazar el valor por defecto pre-declarado de los parámetros opcionales usando aquellos que ha pasado a través de la matriz.

extract(array_merge($arrDefaults, array_intersect_key($arrOptionalParams, $arrDefaults))); 

Llame a la función, pasando sus parámetros obligatorios, y sólo los parámetros opcionales que se requieren

Por ejemplo,

function test_params($a, $b, $arrOptionalParams = array()) { 

$arrDefaults = array('c' => 'sat', 

        'd' => 'mat'); 

extract(array_merge($arrDefaults, array_intersect_key($arrOptionalParams, $arrDefaults))); 

echo "$a $b $c on the $d"; 

} 

y luego llamarlo como esto

test_params('The', 'dog', array('c' => 'stood', 'd' => 'donkey')); 
test_params('The', 'cat', array('d' => 'donkey')); 
test_params('A', 'dog', array('c' => 'stood')); 

Resultados:

El perro se puso sobre el asno

El gato se sentó en el burro

Un perro se puso en el tapete

+0

[Esa solución estaba en SO hace 1.5 años, sin embargo]] (http://stackoverflow.com/a/23005144/315024) – Walf

+0

Reconocido - Lo vi hace dos semanas y le di un punto. Confieso que no volví a leer todas las "soluciones" antes de publicar el mío. Si tengo permiso, agregaré un comentario a la respuesta aceptada que apunte a su solución. – DJDave

1

Los argumentos que se deben transmitir en orden por posición, no se puede saltar un parámetro por se; Tendrás que proporcionar el valor del parámetro predeterminado para omitirlo. Podría decirse que eso frustra el propósito de lo que estás tratando de lograr.

sin reescribir su función para aceptar parámetros de forma diferente, aquí es una forma de tiempo de llamada para evitar este:

$func = 'foo'; 
$args = ['lastName' => 'Smith']; 

$ref = new ReflectionFunction($func); 
$ref->invokeArgs(array_map(function (ReflectionParameter $param) use ($args) { 
    if (array_key_exists($param->getName(), $args)) { 
     return $args[$param->getName()]; 
    } 
    if ($param->isOptional()) { 
     return $param->getDefaultValue(); 
    } 
    throw new InvalidArgumentException("{$param->getName()} is not optional"); 
}, $ref->getParameters())); 

En otras palabras, usted está utilizando la reflexión de inspeccionar los parámetros de la función y asignarlos a los los parámetros disponibles por nombre, omitiendo los parámetros opcionales con su valor predeterminado. Sí, esto es feo y engorroso. Se podría utilizar este ejemplo para crear una función como:

call_func_with_args_by_name('foo', ['lastName' => 'Smith']); 
+1

respuesta mejor, relevante y precisa – Offenso

0

Si esto se utiliza muy a menudo, acaba de definir una nueva función especializada:

function person($firstName = 'john', $lastName = 'doe') { 
    return $firstName . " " . $lastName; 
} 

function usualFirtNamedPerson($lastName = 'doe') { 
    return person('john', $lastName); 
} 

print(usualFirtNamedPerson('smith')); --> john smith 

Tenga en cuenta que también se puede cambiar el valor por defecto de $ apellido en el proceso si lo desea.

Cuando se estima que una función nueva se ha sobrerrollado, simplemente llame a su función con todos los parámetros. Si quiere dejarlo más claro, puede almacenar sus literales en una variable con nombre de aleta o usar comentarios.

$firstName = 'Zeno'; 
$lastName = 'of Elea'; 
print(person($firstName, $lastName)); 
print(person(/* $firstName = */ 'Bertrand', /* $lastName = */ 'Russel')); 

Ok, esto no es tan corta y elegante como person($lastName='Lennon'), pero parece que no se puede tener en PHP. Y esa no es la manera más sexy de codificarlo, con truco de súper metaprogramación o lo que sea, pero ¿qué solución preferiría encontrar en un proceso de mantenimiento?

Cuestiones relacionadas