Kategorien
{{login}}
|
Hilfsfunktion zur Typenkonversion
An anderer Stelle habe ich mich ja schon über die etwas undurchsichtige Typenkonversion von PHP beklagt. Einige Eigenheiten kann man sich zwar merken und entsprechend berücksichtigen, in anderen Fällen ist die implizit-automatische Typenkonversion schlicht zu undurchsichtig.
Nehme ich einmal an, ich übergebe den $_GET-Parameter "id" an ein Skript, der für einen Primärschlüssel in der Datenbank steht. Da ich weiss, dass es sich bei "id" um eine Zahl handeln muss, kann ich den Wert zu int casten, bevor ich ihn verwende:
$id = (int)$_GET["id"];
Damit stelle ich sicher, dass kein Angreifer id verwenden kann, um z. B. meine Datenbank per SQL-Injection zu schädigen.
Das Vorgehen von (int) bleibt aber unvorhersehbar und hängt von dem übergebenen Wert ab. "2" wird 2. "idiot" wird 0. "2'0" wird 2. Den Originalwert mit der konvertierten Form zu vergleichen, bringt nichts:
$id==$_GET["id"]
Denn in diesem Fall wird PHP für den Vergleich eine implizite Konversion vornehmen, die das gleiche ergibt wie meine eigene, also ergibt dieser Vergleich TRUE.
$id===$_GET["id"]
Der typentreue Vergleich ergibt jedoch auf jeden Fall FALSE, da der Originalwert ja "string" geblieben ist.
Der Trick liegt darin, den neuen Wert doppelt zu konvertieren, zunächst zu int, und dann wieder zu string.
"idiot"=> wird zu int 0 => wird zu string "0". Wenn dann der Originalwert mit dem doppelt konvertierten Wert verglichen wird, stimmen sie nicht mehr überein, sofern die Konvertierung einen Informationsverlust mit sich gebracht hat.
$double = (int)$_GET["id"];
$double = (string)$double;
$double==$_GET["id"]; // FALSE
Nachfolgend eine Funktion, die die Arbeit übernimmt. Bitte einige Besonderheiten berücksichtigen:
- cast zu array liefert immer FALSE, es sei denn, die Variable ist bereits ein array
- cast zu resource oder object wurde nicht berücksichtigt (woher soll cast auch die ressource herzaubern?)
- Die Behandlung von NULL, 0 etc. ist strenger als in PHP: NULL zu int ergibt falsch, da ich NULL und 0 als unterschiedliche Werte ansehe.
<?php
function is_castable($var, $type)
{
// These types cannot be cast to or from without getting erratic results
$uncastable = array("array", "object", "resource");
if(in_array($type, $uncastable) and gettype($var)!=$type)
{return false;}
if(in_array($type, $uncastable) and gettype($var)==$type)
{return true;}
$types = array("int", "integer", "double", "float", "null", "bool", "string", "array");
if(!in_array($type, $types))
{trigger_error("Invalid type", E_USER_ERROR);}
$double = $var;
// doublecast $var - to type, then back to string
$double = (string)settype($double, $type);
// TRUE if $var and $double still have the same content, regardless of type
if($var==$double)
{return true;}
return false;
}
if(is_castable(1, "string"))
{echo "yes<br />n";} // yes
else
{echo "no<br />n";}
if(is_castable(1.5, "integer"))
{echo "yes<br />n";}
else
{echo "no<br />n";} // no
if(is_castable(1, "float"))
{echo "yes<br />n";} // yes
else
{echo "no<br />n";}
if(is_castable(0, "null"))
{echo "yes<br />n";}
else
{echo "no<br />n";} // no
if(is_castable(array(1,2), "array"))
{echo "yes<br />n";} // yes
else
{echo "no<br />n";}
if(is_castable(array(1,2), "string"))
{echo "yes<br />n";}
else
{echo "no<br />n";} // no
?>
KommentierenBitte beachten: Kommentare sind nicht sofort sichtbar, sondern werden erst nach einer kurzen Prüfung freigegeben, sofern keine rechtliche Beanstandung vorliegt. Rechtlich bedenkliche Inhalte werden entweder entschärft oder nicht veröffentlicht.
|