PHP parâmetros de referência e nula padrão
-
07-07-2019 - |
Pergunta
Vamos dizer que temos uma assinatura do método como
public static function explodeDn($dn, array &$keys = null, array &$vals = null,
$caseFold = self::ATTR_CASEFOLD_NONE)
podemos facilmente chamar o método omitindo todos os parâmetros após $dn
:
$dn=Zend_Ldap_Dn::explodeDn('CN=Alice Baker,CN=Users,DC=example,DC=com');
Também pode chamar o método com 3 parâmetros:
$dn=Zend_Ldap_Dn::explodeDn('CN=Alice Baker,CN=Users,DC=example,DC=com', $k, $v);
e com 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);
Mas por que é impossível para chamar o método com a seguinte combinação de parâmetros, por exemplo:
$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);
Qual é a diferença entre passando null
ao método e contando com o valor padrão? É essa restrição escrito no manual? ele pode ser contornado?
Solução
É porque você não pode ter uma referência a null.
Você pode ter uma referência a uma variável que contém nulo - que é exatamente o que o valor padrão faz. Ou você pode passar em null como um valor literal -. Mas desde que você quer um parâmetro de saída não é possível aqui
Outras dicas
Enquanto você deve criar uma variável dummy para os argumentos por-ref se você quer passar NULL explicitamente, você não tem que criar essa variável em uma linha separada. Você pode usar uma expressão de atribuição como $ manequim = NULL diretamente como um argumento da função:
function foo (&$ref = NULL) {
if (is_null($ref)) $ref="bar";
echo "$ref\n";
}
foo($dummy = NULL); //this works!
Eu só descobri isso mesmo, e eu sou muito em choque o_O!
Este é o que o PHP documentação diz:
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."
Eu teria esperado makecoffee(null)
voltar "Fazendo uma xícara de cappuccino.".
Uma solução alternativa que tenho usado é verificar dentro da função se o argumento for nulo:
function makecoffee($type = null)
{
if (is_null($type)){
$type = "capuccino";
}
return "Making a cup of $type.\n";
}
Agora makecoffee(null)
retorna "Fazendo uma xícara de cappuccino."
(Sei que isso não realmente resolver a questão Zend-relacionado, mas pode ser útil para alguns ...)
@ Tomalak
Na verdade, o valor padrão cria uma variável sem qualquer referência envolvidos. Que é algo que você simplesmente não pode começar quando você passar algo.
O que eu acho para ilustrar as razões é o seguinte exemplo (que eu não fiz teste):
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)
Na minha opinião, PHP deve oferecer uma maneira de não passar determinados parâmetros, como com
foo($p1, , , $p4);
ou sintaxe semelhante em vez de passar NULL.
Mas isso não acontecer, então você tem que usar variáveis ??binárias.
Como @aschmecher apontadas em um comentário sobre
resposta @ de Szczepan aqui , fazendo func($var = null)
gera um aviso padrões rigorosos.
Uma solução
Aqui está um método que não gera quaisquer avisos:
<?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());
Explicação
No lugar de passar em uma variável, passamos no resultado de uma função que retorna uma referência. A segunda chamada para doIt(dummy())
é verificar se o valor $dummy
referência não persiste entre as chamadas. Isto contrasta com a criação de uma variável explicitamente, onde é preciso lembrar-se de limpar qualquer valor acumulado:
$dummyRef = null;
doIt($dummyRef);
doIt($dummyRef); // second call would print 'x not null: 2'
Aplicação
Assim, no exemplo do OP, seria:
$dn = Zend_Ldap_Dn::explodeDn(
'CN=Alice Baker,CN=Users,DC=example,DC=com',
$k,
dummyRef(),
Zend_Ldap_Dn::ATTR_CASEFOLD_UPPER
);
Considerações adicionais
Uma coisa que eu poderia se preocupar é se esse método cria um vazamento de memória. As seguintes mostras de teste que ele faz não:
<?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";
No meu sistema (usando PHP 5.6.4), ambas as chamadas para memory_get_usage mostrou ~ 262 KB.