Digitare le variabili di casting in PHP, qual è il motivo pratico per farlo?

43

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

PHP does not require (or support) explicit type definition in variable declaration; a variable's type is determined by the context in which the variable is used.

Lo ami o lo odi, PHP ri-calcola le variabili al volo. Quindi, il seguente codice è valido:

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

PHP ti permette anche di castare esplicitamente una variabile, in questo modo:

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

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

Non ho problemi a digitare con forza in linguaggi che lo supportano, come Java. Va bene, e lo capisco perfettamente. Inoltre, sono a conoscenza - e comprendo appieno l'utilità di - tipo suggerimento nei parametri di funzione .

Il problema che ho con il casting di tipo è spiegato dalla citazione sopra. Se PHP può scambiare i tipi at-will , può farlo anche dopo aver forzare il cast un tipo; e può farlo on-the-fly quando è necessario un determinato 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 tipo di definizione definita dall'utente ha senso in PHP :

  1. Forza la variabile cast $foo come int(int)$foo .
  2. Si tenta di memorizzare un valore stringa nella variabile $foo .
  3. PHP lancia un'eccezione !! ← Avrebbe senso. All'improvviso esiste il motivo per il tipo di casting definito dall'utente!

Il fatto che PHP cambierà le cose in base alle necessità rende vago il punto del tipo 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 fatto questa domanda originariamente, indovina chi si è trovato a utilizzare typecasting in un ambiente pratico? Cordiali saluti.

L'obbligo era di visualizzare i valori monetari su un sito web per un menu del ristorante. Il design del sito richiedeva di tagliare gli zeri finali, in modo che il display assomigliasse a quanto segue:

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

Il modo migliore che ho trovato per fare questo è sprecare il cast della 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 ricompone il float come stringa per la concatenazione.

    
posta Stephen 07.12.2010 - 14:44
fonte

7 risposte

30

In un linguaggio tipizzato debolmente, il cast di tipo esiste per rimuovere l'ambiguità nelle operazioni digitate, quando altrimenti il compilatore / interprete userebbe l'ordine o altre regole per fare un'ipotesi di quale operazione usare.

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

Ecco i casi, utilizzando JavaScript come linguaggio di confronto.

Concatenamento di stringhe

Ovviamente questo non è un problema in PHP perché esistono operatori separati concatenazione delle stringhe ( . ) e additivi ( + ).

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

Confronto tra 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, realizza 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!

Scrittura della firma della funzione

PHP implementa un semplice controllo dei tipi sulle firme delle funzioni, ma sfortunatamente è talmente imperfetto che probabilmente è raramente utilizzabile.

Ho pensato che potrei fare qualcosa di sbagliato, ma un commento sui documenti conferma che i tipi built-in diversi dall'array non possono essere utilizzati nelle firme delle funzioni PHP, sebbene il messaggio di errore sia 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 si utilizza un tipo che comprende, 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 ad un altro caso, e ancora una volta 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 a cui posso pensare è ... (drumroll)

Tipo troncamento

In altre parole, quando si ha un valore di un tipo (ad esempio una stringa) e si desidera interpretarlo come un altro tipo (int) e si desidera forzarlo a diventare uno dell'insieme di valori valido 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 (per un tipo che comprende più valori) ti farebbe davvero guadagnare.

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

    
risposta data 07.12.2010 - 22:03
fonte
15

Stai mescolando i concetti di tipo debole / strong 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 dell'originale; non fa nulla alla variabile (se ne è coinvolto uno).

L'unica situazione in cui digito regolarmente i valori cast è su parametri SQL numerici. Dovresti disinfettare / sfuggire qualsiasi valore di input inserito nelle istruzioni SQL o (molto meglio) utilizzare query parametrizzate. Ma, se vuoi un valore che DEVE essere un numero intero, è molto più semplice limitarlo.

Si consideri:

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

Se ho omesso la prima riga, $id sarebbe un vettore facile per l'iniezione SQL. Il cast si assicura che sia un numero innocuo; qualsiasi tentativo di inserire alcuni SQL comporterebbe semplicemente una query per id=0

    
risposta data 07.12.2010 - 15:23
fonte
4

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

Sto sviluppando un'app per Android che effettua richieste HTTP a script PHP su un server per recuperare i dati da un database. Lo script memorizza i dati sotto forma di un oggetto PHP (o array associativo) e viene restituito come oggetto JSON all'app. Senza digitare casting avrei ricevuto qualcosa del genere:

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

Ma, usando il cast di tipo PHP (int) sull'ID dell'utente quando lo memorizzi, l'oggetto PHP viene restituito all'app:

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

Quindi quando l'oggetto JSON viene analizzato nell'app, mi salva dall'esprimere l'id a un intero!

Vedi, molto utile.

    
risposta data 02.12.2013 - 21:42
fonte
3

Un esempio sono gli oggetti con un metodo __toString: $str = $obj->__toString(); vs %codice%. C'è molto meno digitando nel secondo, e la roba extra è la punteggiatura, che richiede più tempo per digitare. Penso anche che sia più leggibile, anche se altri potrebbero non essere d'accordo.

Un altro sta creando un array a elemento singolo: $str = (string) $obj; rispetto a array($item); . Questo inserirà qualsiasi tipo scalare (numero intero, risorsa, ecc.) All'interno di un array.
Altrativamente, se (array) $item; è un oggetto, le sue proprietà diventeranno chiavi dei loro valori. Tuttavia, penso che la conversione degli array di object- > sia un po 'strana: le proprietà private e protette fanno parte dell'array e vengono rinominate. Per citare la 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 è la conversione di dati GET / POST in tipi appropriati per un database. MySQL è in grado di gestirlo da sé, ma penso che più server ANSI-compatibili possano 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 esso in base al relativo tipo (ad esempio, int / floats di solito eseguirà calcoli su di essi, ecc.).

    
risposta data 07.12.2010 - 15:45
fonte
0

Questo script:

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

funzionerà correttamente per script.php?tags[]=one ma fallirà 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 aspettarsi un array (e hai meno controllo sulla stringa di query inviata allo script), il problema può essere risolto gettando il risultato da _GET :

$tags = (array) _GET['tags'];
foreach ($tags as $tag) {
    echo 'tag: ', $tag;
}
    
risposta data 03.01.2011 - 04:20
fonte
0

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

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

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

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

    
risposta data 07.12.2010 - 17:12
fonte
-2

Un "uso" di variabili di riscrittura di PHP al volo che vedo spesso in uso è quando si recuperano dati da fonti esterne (input dell'utente o database). Permette ai codificatori (si noti che non ho detto sviluppatori) di ignorare (o nemmeno di imparare) i diversi tipi di dati disponibili da diverse fonti.

Un codificatore (si noti che non ho detto sviluppatore) il cui codice ho ereditato e che ancora mantengo non sembra sapere che esiste una differenza tra la stringa "20" che è restituito nella variabile eccellente $_GET , tra l'operazione integer 20 + 20 quando lo aggiunge al valore nel database. È fortunata che PHP usi . per la concatenazione delle stringhe e non + come ogni altro linguaggio, perché ho visto il suo codice "aggiungere" due stringhe (un varcahr da MySQL e un valore da $_GET ) e ottenere un int.

Questo è un esempio pratico? Solo nel senso che lascia passare i programmatori senza sapere con quali tipi di dati stanno lavorando. Personalmente lo odio.

    
risposta data 22.03.2014 - 16:41
fonte

Leggi altre domande sui tag