Parametri PHP per riferimento e null predefinito
-
07-07-2019 - |
Domanda
Supponiamo di avere una firma del metodo come
public static function explodeDn($dn, array &$keys = null, array &$vals = null,
$caseFold = self::ATTR_CASEFOLD_NONE)
possiamo facilmente chiamare il metodo omettendo tutti i parametri dopo $ dn
:
$dn=Zend_Ldap_Dn::explodeDn('CN=Alice Baker,CN=Users,DC=example,DC=com');
Possiamo anche chiamare il metodo con 3 parametri:
$dn=Zend_Ldap_Dn::explodeDn('CN=Alice Baker,CN=Users,DC=example,DC=com', $k, $v);
e con 4 parametri:
$dn=Zend_Ldap_Dn::explodeDn('CN=Alice Baker,CN=Users,DC=example,DC=com', $k, $v,
Zend_Ldap_Dn::ATTR_CASEFOLD_UPPER);
Ma perché è impossibile chiamare il metodo con la seguente combinazione di parametri, ad esempio:
$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 è la differenza tra passare null
al metodo e fare affidamento sul valore predefinito? Questo vincolo è scritto nel manuale? Può essere eluso?
Soluzione
È perché non puoi avere un riferimento a null.
Puoi avere un riferimento a una variabile che contiene null - questo è esattamente ciò che fa il valore predefinito. Oppure puoi passare a null come valore letterale, ma poiché vuoi un parametro out questo non è possibile qui.
Altri suggerimenti
Sebbene sia necessario creare una variabile fittizia per argomenti by-ref se si desidera passare NULL in modo esplicito, non è necessario creare quella variabile su una riga separata. È possibile utilizzare un'espressione di assegnazione come $ dummy = NULL direttamente come argomento della funzione:
function foo (&$ref = NULL) {
if (is_null($ref)) $ref="bar";
echo "$ref\n";
}
foo($dummy = NULL); //this works!
L'ho appena scoperto da solo e sono piuttosto scioccato o_O!
Questo è ciò che Documentazione PHP dice:
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."
Mi sarei aspettato che makecoffee (null)
restituisse " Fare una tazza di cappuccino. " ;.
Una soluzione che ho usato è quella di verificare all'interno della funzione se l'argomento è null:
function makecoffee($type = null)
{
if (is_null($type)){
$type = "capuccino";
}
return "Making a cup of $type.\n";
}
Ora makecoffee (null)
restituisce " Preparare una tazza di cappuccino. "
(Mi rendo conto che in realtà questo non risolve la domanda relativa a Zend, ma potrebbe essere utile per alcuni ...)
@ Tomalak
In realtà, il valore predefinito crea una variabile senza alcun riferimento. Che è qualcosa che semplicemente non puoi dare il via quando passi qualcosa.
Quello che trovo per illustrare i motivi è il seguente esempio (che non ho testato):
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)
Secondo me, PHP dovrebbe offrire un modo per NON passare determinati parametri, come con
foo ($ p1,, $ p4);
o sintassi simile invece di passare NULL.
Ma non è così, quindi devi usare variabili fittizie.
Come ha sottolineato @aschmecher in un commento su
@ La risposta di Szczepan qui , facendo func ($ var = null)
genera un severo avviso sugli standard.
Una soluzione
Ecco un metodo che non genera tali avvisi:
<?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());
Spiegazione
Al posto del passaggio in una variabile, passiamo al risultato di una funzione che restituisce un riferimento. La seconda chiamata a doIt (dummy ())
è verificare che il valore di riferimento $ dummy
non sia persistente tra le chiamate. Ciò contrasta con la creazione esplicita di una variabile, in cui è necessario ricordare di cancellare qualsiasi valore accumulato:
$dummyRef = null;
doIt($dummyRef);
doIt($dummyRef); // second call would print 'x not null: 2'
Applicazioni
Quindi nell'esempio del PO sarebbe:
$dn = Zend_Ldap_Dn::explodeDn(
'CN=Alice Baker,CN=Users,DC=example,DC=com',
$k,
dummyRef(),
Zend_Ldap_Dn::ATTR_CASEFOLD_UPPER
);
Ulteriori considerazioni
Una cosa di cui potrei preoccuparmi è se questo metodo crea una perdita di memoria. Il seguente test mostra che non funziona:
<?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";
Sul mio sistema (usando PHP 5.6.4), entrambe le chiamate a memory_get_usage hanno mostrato ~ 262 KB.