it-swarm.it

Digitare le variabili di casting in PHP, qual è la ragione pratica per farlo?

PHP, come molti di noi sanno, ha digitazione debole . Per coloro che non lo fanno, PHP.net dice:

PHP non richiede (o supporta) la definizione esplicita del tipo nella dichiarazione delle variabili; il tipo di una variabile è determinato dal contesto in cui viene utilizzata la variabile.

Lo adori o lo odi, PHP esegue il rilancio delle variabili al volo. Pertanto, è valido il seguente codice:

$var = "10";
$value = 10 + $var;
var_dump($value); // int(20)

PHP ti consente anche di lanciare esplicitamente una variabile, in questo modo:

$var = "10";
$value = 10 + $var;
$value = (string)$value;
var_dump($value); // string(2) "20"

È tutto fantastico ... ma, per la mia vita, non riesco a concepire una ragione pratica per farlo.

Non ho problemi con la digitazione forte in lingue che lo supportano, come Java. Va bene, e lo capisco perfettamente. Inoltre, sono a conoscenza di - e comprendo appieno l'utilità di - suggerimento sul tipo nei parametri della funzione.

Il problema che ho con il tipo casting è spiegato dalla citazione sopra. Se PHP può scambiare tipi a volontà , può farlo anche dopo che tu forza il cast un tipo; e può farlo al volo quando hai bisogno di un certo tipo in un'operazione. Ciò rende valido quanto segue:

$var = "10";
$value = (int)$var;
$value = $value . ' TaDa!';
var_dump($value); // string(8) "10 TaDa!"

Quindi qual è il punto?


Prendi questo esempio teorico di un mondo in cui il cast di tipi definiti dall'utente ha senso in PHP :

  1. Forzare la variabile di cast $foo come int(int)$foo.
  2. Si tenta di memorizzare un valore di stringa nella variabile $foo.
  3. PHP genera un'eccezione !! ← Questo avrebbe senso. Improvvisamente esiste la ragione per il cast di tipi definiti dall'utente!

Il fatto che PHP cambierà le cose secondo necessità rende vago il punto del cast definito dall'utente. Ad esempio, i seguenti due esempi di codice sono equivalenti:

// example 1
$foo = 0;
$foo = (string)$foo;
$foo = '# of Reasons for the programmer to type cast $foo as a string: ' . $foo;

// example 2
$foo = 0;
$foo = (int)$foo;
$foo = '# of Reasons for the programmer to type cast $foo as a string: ' . $foo;

Un anno dopo aver inizialmente posto questa domanda, indovina chi si è trovato a usare il typecasting in un ambiente pratico? Cordialmente.

Il requisito era di visualizzare i valori in denaro su un sito Web per un menu di ristorante. Il design del sito richiedeva che gli zeri finali fossero tagliati, in modo che il display fosse simile al seguente:

Menu Item 1 .............. $ 4
Menu Item 2 .............. $ 7.5
Menu Item 3 .............. $ 3

Il modo migliore che ho trovato per farlo è sprecare per lanciare la variabile come float:

$price = '7.50'; // a string from the database layer.
echo 'Menu Item 2 .............. $ ' . (float)$price;

PHP taglia gli zeri finali del float e quindi lo rifastisce come una stringa per la concatenazione.

45
Stephen

In un linguaggio debolmente tipizzato, esiste un tipo di casting per rimuovere l'ambiguità nelle operazioni tipizzate, quando altrimenti il ​​compilatore/interprete userebbe l'ordine o altre regole per ipotizzare quale operazione utilizzare.

Normalmente direi PHP segue questo schema, ma dei casi che ho controllato, PHP si è comportato in modo contro intuitivo in ciascuno di essi.

Ecco questi casi, utilizzando JavaScript come linguaggio di confronto.

Concatenazione di stringhe

Ovviamente questo non è un problema in PHP perché ci sono concatenazioni di stringhe separate (.) e aggiunta (+) operatori.

JavaScript
var a = 5;
var b = "10"
var incorrect = a + b; // "510"
var correct = a + Number(b); // 15

Confronto delle stringhe

Spesso nei sistemi informatici "5" è maggiore di "10" perché non lo interpreta come un numero. Non così in PHP, che, anche se entrambi sono stringhe, si rende conto che sono numeri e rimuove la necessità di un cast):

JavaScript
console.log("5" > "10" ? "true" : "false"); // true
PHP
echo "5" > "10" ? "true" : "false";  // false!

Digitazione della firma della funzione

PHP implementa un controllo del tipo bare-bone sulle firme delle funzioni, ma sfortunatamente è così imperfetto che probabilmente è raramente utilizzabile.

Ho pensato che potrei fare qualcosa di sbagliato, ma un commento sui documenti conferma che i tipi predefiniti diversi dall'array non possono essere usati in PHP - sebbene il il messaggio di errore è fuorviante.

PHP
function testprint(string $a) {
    echo $a;
}

$test = 5;
testprint((string)5); // "Catchable fatal error: Argument 1 passed to testprint()
                      //  must be an instance of string, string given" WTF?

E a differenza di qualsiasi altra lingua che conosco, anche se usi un tipo che capisce, null non può più essere passato a quell'argomento (must be an instance of array, null given). Che stupido.

Interpretazione booleana

[ Modifica ]: questo è nuovo. Ho pensato a un altro caso, e di nuovo la logica è invertita da JavaScript.

JavaScript
console.log("0" ? "true" : "false"); // True, as expected. Non-empty string.
PHP
echo "0" ? "true" : "false"; // False! This one probably causes a lot of bugs.

Quindi, in conclusione, l'unico caso utile che mi viene in mente è ... (rullo di tamburi)

Digitare troncamento

In altre parole, quando si dispone di un valore di un tipo (dire stringa) e si desidera interpretarlo come un altro tipo (int) e si desidera forzarlo a diventare uno dei set di valori validi in quel tipo:

$val = "test";
$val2 = "10";
$intval = (int)$val; // 0
$intval2 = (int)$val2; // 10
$boolval = (bool)$intval // false
$boolval2 = (bool)$intval2 // true
$props = (array)$myobject // associative array of $myobject's properties

Non riesco a vedere quale upcasting (a un tipo che comprende più valori) ti guadagnerebbe davvero.

Quindi, mentre non sono d'accordo con l'uso proposto della digitazione (essenzialmente stai proponendo tipizzazione statica , ma con l'ambiguità che solo se fosse force-cast in un tipo sarebbe genera un errore - che causerebbe confusione), penso che sia una buona domanda, perché apparentemente il casting ha molto poco scopo in PHP.

32
Nicole

Stai mescolando i concetti di tipo debole/forte e dinamico/statico.

PHP è debole e dinamico, ma il tuo problema è con il concetto di tipo dinamico. Ciò significa che le variabili non hanno un tipo, i valori sì.

Un "tipo casting" è un'espressione che produce un nuovo valore di un diverso tipo di originale; non fa nulla alla variabile (se ne è coinvolta una).

L'unica situazione in cui scrivo regolarmente valori cast è su parametri SQL numerici. Dovresti disinfettare/sfuggire a qualsiasi valore di input che inserisci in istruzioni SQL o (molto meglio) utilizzare query con parametri. Ma, se vuoi un valore che DEVE essere un numero intero, è molto più semplice semplicemente lanciarlo.

Prendere in considerazione:

function get_by_id ($id) {
   $id = (int)$id;
   $q = "SELECT * FROM table WHERE id=$id LIMIT 1";
   ........
}

se avessi lasciato fuori la prima riga, $id sarebbe un vettore semplice per l'iniezione SQL. Il cast si assicura che sia un numero intero innocuo; qualsiasi tentativo di inserire alcuni SQL comporterebbe semplicemente una query per id=0

15
Javier

Un uso per il cast di tipi in PHP che ho trovato:

Sto sviluppando un'app Android che invia richieste http a PHP su un server per recuperare dati da un database. dati sotto forma di un PHP (o array associativo) e viene restituito come oggetto JSON all'app. Senza il cast di tipo riceverei qualcosa del genere:

{ "user" : { "id" : "1", "name" : "Bob" } }

Ma, usando PHP tipo casting (int) sull'ID dell'utente durante la memorizzazione dell'oggetto PHP, ottengo invece questo restituito all'app:

{ "user" : { "id" : 1, "name" : "Bob" } }

Quindi quando l'oggetto JSON viene analizzato nell'app, mi evita di dover analizzare l'id in un numero intero!

Vedi, molto utile.

4
Ryan

Un esempio sono gli oggetti con un metodo __toString: $str = $obj->__toString(); vs $str = (string) $obj;. La seconda è molto meno digitata e l'elemento extra è la punteggiatura, che impiega più tempo a scrivere. Penso anche che sia più leggibile, anche se altri potrebbero non essere d'accordo.

Un altro sta creando un array a elemento singolo: array($item); vs (array) $item;. Ciò inserirà qualsiasi tipo scalare (intero, risorsa, ecc.) All'interno di un array.
In alternativa, se $item è un oggetto, le sue proprietà diventeranno chiavi dei loro valori. Tuttavia, penso che la conversione oggetto-> array sia un po 'strana: le proprietà private e protette fanno parte dell'array e vengono rinominate. Per citare documentazione PHP : le variabili private hanno il nome della classe anteposto al nome della variabile; le variabili protette hanno un '*' anteposto al nome della variabile.

Un altro uso è convertire i dati GET/POST in tipi appropriati per un database. MySQL può gestirlo da solo, ma penso che i server più conformi ANSI potrebbero rifiutare i dati. Il motivo per cui menziono solo i database è che nella maggior parte degli altri casi, i dati eseguiranno un'operazione su di essi in base al loro tipo ad un certo punto (ad es. Int/float di solito eseguiranno calcoli su di essi, ecc.).

3
Alan Pearce

Può anche essere usato come metodo rapido e sporco per garantire che i dati non attendibili non rompano qualcosa, ad esempio se si utilizza un servizio remoto che ha la convalida della merda e deve accettare solo numeri.

$amount = (float) $_POST['amount'];

if( $amount > 0 ){
    $remoteService->doacalculationwithanumber( $amount );    
}

Ovviamente questo è imperfetto e gestito implicitamente dall'operatore di confronto nell'istruzione if, ma è utile per assicurarti di sapere esattamente cosa sta facendo il tuo codice.

0
Gruffputs

Questo script:

$tags = _GET['tags'];
foreach ($tags as $tag) {
    echo 'tag: ', $tag;
}

funzionerà bene per script.php?tags[]=one ma non riuscirà per script.php?tags=one, perché _GET['tags'] restituisce un array nel primo caso ma non nel secondo. Poiché lo script è scritto in modo da prevedere un array (e hai meno controllo sulla stringa di query inviata allo script), il problema può essere risolto eseguendo il cast del risultato in modo appropriato da _GET:

$tags = (array) _GET['tags'];
foreach ($tags as $tag) {
    echo 'tag: ', $tag;
}
0
beldaz