2008-11-11 14 views
9

Digamos que tenemos una firma de método comoPHP por referencia parámetros y nulo por defecto

public static function explodeDn($dn, array &$keys = null, array &$vals = null, 
    $caseFold = self::ATTR_CASEFOLD_NONE) 

podemos llamar fácilmente el método por omisión de todos los parámetros después de $dn:

$dn=Zend_Ldap_Dn::explodeDn('CN=Alice Baker,CN=Users,DC=example,DC=com'); 

También podemos llamar el método con 3 parámetros:

$dn=Zend_Ldap_Dn::explodeDn('CN=Alice Baker,CN=Users,DC=example,DC=com', $k, $v); 

y con 4 parámetros:

$dn=Zend_Ldap_Dn::explodeDn('CN=Alice Baker,CN=Users,DC=example,DC=com', $k, $v, 
    Zend_Ldap_Dn::ATTR_CASEFOLD_UPPER); 

Pero por qué es imposible llamar al método con la siguiente combinación de parámetros, por ejemplo:

$dn=Zend_Ldap_Dn::explodeDn('CN=Alice Baker,CN=Users,DC=example,DC=com', $k, null, 
    Zend_Ldap_Dn::ATTR_CASEFOLD_UPPER); 
$dn=Zend_Ldap_Dn::explodeDn('CN=Alice Baker,CN=Users,DC=example,DC=com', null, $v); 

¿Cuál es la diferencia entre pasar null al método y confiando en el valor por defecto? ¿Esta restricción está escrita en el manual? ¿Se puede eludir?

Respuesta

22

Es porque no puede tener una referencia a nulo.

Puede tener una referencia a una variable que contiene nulo; eso es exactamente lo que hace el valor predeterminado. O puede pasar nulo como un valor literal, pero dado que desea un parámetro de salida, esto no es posible aquí.

1

sólo para confirmar lo que Tomalak declaró here:

las siguientes obras:

$k=array(); 
$v=null; 
$dn=Zend_Ldap_Dn::explodeDn('CN=Alice Baker,CN=Users,DC=example,DC=com', $k, $v, 
    Zend_Ldap_Dn::ATTR_CASEFOLD_UPPER); 

No es agradable - pero la explicación es clara y comprensible.

4

@ Tomalak

En realidad, el valor por defecto crea una variable sin ninguna referencia en cuestión. Que es algo que simplemente no puedes patear cuando pasas algo.

lo que encuentro para ilustrar las razones es el siguiente ejemplo (que no he probado):

function foo (&$ref = NULL) { 
    $args = func_get_args(); 
    echo var_export($ref, TRUE).' - '.var_export($args, TRUE); 
} 
$bar = NULL; 
foo();  // NULL - array() 
foo($bar); // NULL - array(0 => NULL) 

En mi opinión, PHP debe ofrecer una manera de no pasar ciertos parámetros, como con
foo($p1, , , $p4); o una sintaxis similar en lugar de pasar NULL.
Pero no es así, por lo que debe usar variables ficticias.

+0

Esa es una idea de sintaxis interesante ... personalmente me gustaría ver la posibilidad de poner parámetros de funciones con nombre en la llamada, p. 'doSomething ($ var1, @ optionalParam4 = $ var2);'. –

5

Acabo de enterarme de esto, y estoy bastante en shock o_O!

Esto es lo que el PHP documentation dice: "Hacer una taza de capuchino"

function makecoffee($type = "cappuccino") 
{ 
    return "Making a cup of $type.\n"; 
} 
echo makecoffee(); // returns "Making a cup of cappuccino." 
echo makecoffee(null); // returns "Making a cup of ." 
echo makecoffee("espresso"); // returns "Making a cup of espresso." 

lo que habría esperado makecoffee(null) volver. Una solución alternativa que he utilizado es comprobar dentro de la función si el argumento es nulo:

function makecoffee($type = null) 
{ 
    if (is_null($type)){ 
     $type = "capuccino"; 
    } 
    return "Making a cup of $type.\n"; 
} 

ahora makecoffee(null) devuelve "hacer una taza de capuchino."

(Me di cuenta que en realidad no resuelve la cuestión relacionada con el Zend, pero podría ser útil para algunos ...)

10

Si bien se debe crear una variable ficticia para los argumentos de-ref si quieres pasa NULL explícitamente, no tienes que crear esa variable en una línea separada. Se puede utilizar una expresión de asignación como $ ficticia = NULL directamente como un argumento de función:

function foo (&$ref = NULL) { 

    if (is_null($ref)) $ref="bar"; 
    echo "$ref\n";   
} 

foo($dummy = NULL); //this works! 
+4

Esto me da advertencias de modo estrictas: 'Estándares estrictos de PHP: solo las variables deben pasarse por referencia en (snip) /test.php en la línea 9' – asmecher

+0

Si la variable' $ dummy' aún no existe, entonces será nulo cuando lo pases así: 'foo ($ dummy)'. Si está buscando reutilizar esa variable 'ficticia', y quiere un trazador de líneas único, siempre puede a) asegurarse de que no cambie de nulo dentro de su función o b) hacer esto: '($ dummy = null) y foo ($ dummy) '. –

0

Como @aschmecher señaló en un comentario en @Szczepan's answer here, haciendo func($var = null) genera un aviso estrictos estándares.

Una solución

Aquí es un método que no genere ningún tipo de advertencias:

<?php 
error_reporting(E_ALL | E_STRICT); 

function doIt(&$x = null) { 
    if($x !== null) echo "x not null: $x\n"; 
    $x = 2; 
} 

function &dummyRef() { 
    $dummyRef = null; 
    return $dummyRef; 
} 

doIt(dummyRef()); 

doIt(dummyRef()); 

Explicación

En lugar de pasar en una variable, se pasa en el resultado de una función que devuelve una referencia. La segunda llamada al doIt(dummy()) es verificar que el valor de referencia $dummy no persista entre llamadas. Esto contrasta con la creación de una variable de forma explícita, en donde uno tiene que recordar para borrar cualquier valor acumulado:

$dummyRef = null; 
doIt($dummyRef); 
doIt($dummyRef); // second call would print 'x not null: 2' 

Aplicación

Así, en el ejemplo de la OP, sería:

$dn = Zend_Ldap_Dn::explodeDn(
    'CN=Alice Baker,CN=Users,DC=example,DC=com', 
    $k, 
    dummyRef(), 
    Zend_Ldap_Dn::ATTR_CASEFOLD_UPPER 
); 

Consideraciones adicionales

Una cosa que podría preocuparme i s si este método crea una pérdida de memoria. La siguiente prueba muestra que no es así:

<?php 
function doItObj(&$x = null) { 
    if(gettype($x) !== "object") echo "x not null: $x\n"; 
    $x = 2; 
} 

function &dummyObjRef() { 
    $dummyObjRef = new StdClass(); 
    return $dummyObjRef; 
} 

echo "memory before: " . memory_get_usage(true) . "\n"; 

for($i = 0; $i < 1000000; $i++) { 
    doItObj(dummyObjRef()); 
} 

echo "memory after: " . memory_get_usage(true) . "\n"; 

echo "\n$i\n"; 

En mi sistema (usando PHP 5.6.4), ambas llamadas a memory_get_usage mostró ~ 262 KB.

Cuestiones relacionadas