类型戏法

PHP 在变量定义中不需要(或不支持)明示的类型定义;变量类型是根据使用该变量的上下文所决定的。也就是说,如果把一个字符串值赋给变量 varvar 就成了一个字符串。如果又把一个整型值赋给 var,那它就成了一个整数。

PHP 的自动类型转换的一个例子是加号“+”。如果任何一个运算数是浮点数,则所有的运算数都被当成浮点数,结果也是浮点数。否则运算数会被解释为整数,结果也是整数。注意这并没有改变这些运算数本身的类型;改变的仅是这些运算数如何被求值。

<?php
$foo
= "0";  // $foo is string (ASCII 48)
$foo += 2;   // $foo is now an integer (2)
$foo = $foo + 1.3;  // $foo is now a float (3.3)
$foo = 5 + "10 Little Piggies"; // $foo is integer (15)
$foo = 5 + "10 Small Pigs";     // $foo is integer (15)
?>

如果上面两个例子看上去古怪的话,参见字符串转换为数值

如果要强制将一个变量当作某种类型来求值,参见类型强制转换一节。如果要改变一个变量的类型,参见 settype()

如果想要测试本节中任何例子的话,可以用 var_dump() 函数。

注: 数组的自动转换行为目前没有定义。

<?php
$a
= "1";     // $a 是字符串
$a[0] = "f";  // 是字符串偏移量吗?结果会是什么?
?>

由于一些历史原因,PHP 支持通过偏移量进行的字符串索引,这和数组索引的语法一样。以上的例子就产生了一个问题:$a 应该变成一个第一个元素是“f”的数组呢,还是“f”成了字符串 $a 的第一个字符?

目前版本的 PHP 将以上第二个赋值理解成字符串的偏移量标识,即 $a 变成了 "f",尽管如此,这种自动转换的地结果应该被认为未定义。PHP 4 引入了新的花括号语法来访问字符串的字符,请使用该语法来替代以上的操作:

<?php
$a    
= "abc"; // $a 为一个字符串
$a{1} = "f";   // $a 目前为 "afc"
?>

请参阅访问和修改字符串中的字符一节以获取更多信息。

类型强制转换

PHP 中的类型强制转换和 C 中的非常像:在要转换的变量之前加上用括号括起来的目标类型。

<?php
$foo
= 10;   // $foo is an integer
$bar = (boolean) $foo;   // $bar is a boolean
?>

允许的强制转换有:

  • (int),(integer) - 转换成整型

  • (bool),(boolean) - 转换成布尔型

  • (float),(double),(real) - 转换成浮点型

  • (string) - 转换成字符串

  • (array) - 转换成数组

  • (object) - 转换成对象

注意在括号内允许有空格和制表符,所以下面两个例子功能相同:

<?php
$foo
= (int) $bar;
$foo = ( int ) $bar;
?>

注: 为了将一个变量还原为字符串,还可以将变量放置在双引号中。

<?php
$foo
= 10;            // $foo is an integer
$str = "$foo";        // $str is a string
$fst = (string) $foo; // $fst is also a string

// This prints out that "they are the same"
if ($fst === $str) {
    echo
"they are the same";
}
?>

当在某些类型之间强制转换时确切地会发生什么可能不是很明显。更多信息见如下小节:


add a note add a note User Contributed Notes
frederick_ricaforte at yahoo dot com
18-Jul-2006 03:38
<?php
// I got a string:
$str= "0000";

var_dump($str);
echo
"\\n$str is ".(is_numeric($str)?"numeric":"not numeric");
echo
"\\n$str>0 , ".(($str>0)?"yes":"no");

/** take a note of this **/
echo "\\n$str=='000' , ".(($str=="000")?"yes":"no");
/** endnote **/

echo "\\n$str==0 , ".(($str==0)?"yes":"no");
echo
"\\n$str<0 , ".(($str<0)?"yes":"no");
$au = $str*1;
echo
"\\n$str*1 is equal to ".$au;
echo
"\\n";
var_dump($au);
?>
______________________________
string(4) "0000"

0000 is numeric
0000>0 , no
0000=='000' , yes
0000==0 , yes
0000<0 , no
0000*1 is equal to 0
int(0)
______________________________
0000=='000' , yes
Seems like in type juggling, numeric test has more priority over string comparison.

Frederick Ricaforte
jsingh dot oberoi at gmail dot com
13-May-2006 06:23
Reply to note dated 25-May-2005 05:35 :

In PHP 5.1.4, cast only affects the destination type and not the source type.

So,

<?php

$a
= "abc";
$bb = (int)$a;
echo
$a;

?>

gives as output:
abc
miracle at 1oo-percent dot de
20-Feb-2006 09:26
If you want to convert a string automatically to float or integer (e.g. "0.234" to float and "123" to int), simply add 0 to the string - PHP will do the rest.

e.g.

$val = 0 + "1.234";
(type of $val is float now)

$val = 0 + "123";
(type of $val is integer now)
23-Jun-2005 08:47
If you have a boolean, performing increments on it won't do anything despite it being 1.  This is a case where you have to use a cast.

<html>
<body> <!-- don't want w3.org to get mad... -->
<?php
$bar
= TRUE;
?>
I have <?=$bar?> bar.
<?php
$bar
++;
?>
I now have <?=$bar?> bar.
<?php
$bar
= (int) $bar;
$bar++;
?>
I finally have <?=$bar?> bar.
</body>
</html>

That will print

I have 1 bar.
I now have 1 bar.
I finally have 2 bar.
26-May-2005 01:35
It appears in PHP 5.0.0, that casting will convert the variable to the specified type, not return a copy of the variable converted to that type.

ie:

$str = "foo";
$int = (int) $str;
echo "str: $str, int: $int";
// prints out "0, 0" instead of "foo, 0".

How it works with classes, I don't know.
toma at smartsemantics dot com
10-Mar-2005 10:24
In my much of my coding I have found it necessary to type-cast between objects of different class types.

More specifically, I often want to take information from a database, convert it into the class it was before it was inserted, then have the ability to call its class functions as well.

The following code is much shorter than some of the previous examples and seems to suit my purposes.  It also makes use of some regular expression matching rather than string position, replacing, etc.  It takes an object ($obj) of any type and casts it to an new type ($class_type).  Note that the new class type must exist:

function ClassTypeCast(&$obj,$class_type){
   if(class_exists($class_type,true)){
       $obj = unserialize(preg_replace"/^O:[0-9]+:\"[^\"]+\":/i",
         "O:".strlen($class_type).":\"".$class_type."\":", serialize($obj)));
   }
}
Raja
10-Feb-2005 07:05
Uneven division of an integer variable by another integer variable will result in a float by automatic conversion -- you do not have to cast the variables to floats in order to avoid integer truncation (as you would in C, for example):

$dividend = 2;
$divisor = 3;
$quotient = $dividend/$divisor;
print $quotient; // 0.66666666666667
memandeemail at gmail dot com
09-Dec-2004 09:29
/**
   * @return bool
   * @param array[byreference] $values
   * @desc Convert an array or any value to Escalar Object [not tested in large scale]
   */
   function setobject(&$values) {
       $values = (object) $values;
       foreach ($values as $tkey => $val) {
           if (is_array($val)) {
               setobject($val);
               $values->$tkey = $val;
           }
       }
       return (bool) $values;
   }
tom5025_ at hotmail dot com
25-Aug-2004 04:27
function strhex($string)
{
   $hex="";
   for ($i=0;$i<strlen($string);$i++)
       $hex.=dechex(ord($string[$i]));
   return $hex;
}
function hexstr($hex)
{
   $string="";
   for ($i=0;$i<strlen($hex)-1;$i+=2)
       $string.=chr(hexdec($hex[$i].$hex[$i+1]));
   return $string;
}

to convert hex to str and vice versa
dimo dot vanchev at bianor dot com
10-Mar-2004 11:02
For some reason the code-fix posted by philip_snyder at hotmail dot com [27-Feb-2004 02:08]
didn't work for me neither with long_class_names nor with short_class_names. I'm using PHP v4.3.5 for Linux.
Anyway here's what I wrote to solve the long_named_classes problem:

<?php
function typecast($old_object, $new_classname) {
   if(
class_exists($new_classname)) {
      
$old_serialized_object = serialize($old_object);
      
$old_object_name_length = strlen(get_class($old_object));
      
$subtring_offset = $old_object_name_length + strlen($old_object_name_length) + 6;
      
$new_serialized_object  = 'O:' . strlen($new_classname) . ':"' . $new_classname . '":';
      
$new_serialized_object .= substr($old_serialized_object, $subtring_offset);
       return
unserialize($new_serialized_object);
     } else {
         return
false;
     }
}
?>
philip_snyder at hotmail dot com
27-Feb-2004 11:08
Re: the typecasting between classes post below... fantastic, but slightly flawed. Any class name longer than 9 characters becomes a problem... SO here's a simple fix:

function typecast($old_object, $new_classname) {
  if(class_exists($new_classname)) {
   // Example serialized object segment
   // O:5:"field":9:{s:5:...  <--- Class: Field
   $old_serialized_prefix  = "O:".strlen(get_class($old_object));
   $old_serialized_prefix .= ":\"".get_class($old_object)."\":";

   $old_serialized_object = serialize($old_object);
   $new_serialized_object = 'O:'.strlen($new_classname).':"'.$new_classname . '":';
   $new_serialized_object .= substr($old_serialized_object,strlen($old_serialized_prefix));
   return unserialize($new_serialized_object);
  }
  else
   return false;
}

Thanks for the previous code. Set me in the right direction to solving my typecasting problem. ;)
post_at_henribeige_dot_de
04-May-2003 12:37
If you want to do not only typecasting between basic data types but between classes, try this function. It converts any class into another. All variables that equal name in both classes will be copied.

function typecast($old_object, $new_classname) {
  if(class_exists($new_classname)) {
   $old_serialized_object = serialize($old_object);
   $new_serialized_object = 'O:' . strlen($new_classname) . ':"' . $new_classname . '":' .
                             substr($old_serialized_object, $old_serialized_object[2] + 7);
   return unserialize($new_serialized_object);
  }
  else
   return false;
}

Example:

class A {
  var $secret;
  function A($secret) {$this->secret = $secret;}
  function output() {echo("Secret class A: " . $this->secret);}
}

class B extends A {
  var $secret;
  function output() {echo("Secret class B: " . strrev($this->secret));}
}

$a = new A("Paranoia");
$b = typecast($a, "B");

$a->output();
$b->output();
echo("Classname \$a: " . get_class($a) . "Classname \$b: " . get_class($b));

Output of the example code above:

Secret class A: Paranoia
Secret class B: aionaraP
Classname $a: a
Classname $b: b
yury at krasu dot ru
27-Nov-2002 05:24
incremental operator ("++") doesn't make type conversion from boolean to int, and if an variable is boolean and equals TRUE than after ++ operation it remains as TRUE, so:

$a = TRUE;
echo ($a++).$a;  // prints "11"
29-Aug-2002 01:26
Printing or echoing a FALSE boolean value or a NULL value results in an empty string:
(string)TRUE //returns "1"
(string)FALSE //returns ""
echo TRUE; //prints "1"
echo FALSE; //prints nothing!
amittai at NOSPAMamittai dot com
21-Aug-2002 02:30
Uneven division of an integer variable by another integer variable will result in a float by automatic conversion -- you do not have to cast the variables to floats in order to avoid integer truncation (as you would in C, for example):

$dividend = 2;
$divisor = 3;
$quotient = $dividend/$divisor;
print $quotient; // 0.66666666666667

Amittai Aviram