PHP类型转换 - 好还是坏?

时间:2010-09-12 15:05:31

标签: php types

在使用C和Java完成一些工作之后,我对PHP中的狂野西部法则越来越恼火。我真正觉得PHP缺乏的是严格的数据类型。 string('0')==(int)0 ==(boolean)false的事实是一个例子。

您不能依赖函数返回的数据类型。您既不能强制函数的参数属于特定类型,这可能会导致非严格比较,从而导致意外情况。一切都可以照顾,但它仍然会出现意想不到的错误。

对方法收到的类型参数进行类型转换是好还是坏?对回归进行强制转换是好事吗?

IE

public function doo($foo, $bar) {
   $foo = (int)$foo;
   $bar = (float)$bar;
   $result = $bar + $foo;
   return (array)$result;
}

这个例子非常愚蠢,我还没有测试过,但我认为每个人都有这个想法。有没有理由让PHP-god按照自己的意愿转换数据类型,除了让那些不了解数据类型的人使用PHP?

5 个答案:

答案 0 :(得分:22)

无论好坏,宽松打字都是“PHP Way”。许多内置函数和大多数语言结构都可以在你给它们的任何类型上运行 - 默默地(通常是危险地)将它们投射到幕后以使事物(某种程度)适合它们。

我自己来自Java / C / C ++背景,PHP的宽松打字模型一直是我的沮丧之源。但多年来我发现,如果我必须编写PHP,我可以通过接受PHP的“松散”而不是打击它来更好地完成它(即更干净,更安全,更可测试的代码);因为它,我结束了一只更快乐的猴子。

Casting确实是我的技术的基础 - 而且(恕我直言)它是一致地构建干净,可读的PHP代码的唯一方法,它以一种易于理解,可测试,确定的方式处理混合类型的参数。

主要观点(您也清楚地理解)是,在PHP中,您不能简单地假设参数是您期望的类型。这样做可能会产生严重后果,直到您的应用程序投入生产后才可能发现。

为了说明这一点:

<?php

function displayRoomCount( $numBoys, $numGirls ) {
  // we'll assume both args are int

  // check boundary conditions
  if( ($numBoys < 0) || ($numGirls < 0) ) throw new Exception('argument out of range');

  // perform the specified logic
  $total = $numBoys + $numGirls;
  print( "{$total} people: {$numBoys} boys, and {$numGirls} girls \n" );
}

displayRoomCount(0, 0);   // (ok) prints: "0 people: 0 boys, and 0 girls" 

displayRoomCount(-10, 20);  // (ok) throws an exception

displayRoomCount("asdf", 10);  // (wrong!) prints: "10 people: asdf boys, and 10 girls"

解决此问题的一种方法是限制函数可以接受的类型,在检测到无效类型时抛出异常。其他人已经提到了这种方法。它对我的Java / C / C ++美学很有吸引力,我在PHP中遵循这种方法多年。简而言之,它没有任何问题,但它确实违背了“PHP方式”,过了一段时间,开始感觉像是在上游游泳。

作为替代方案,转换提供了一种简单而干净的方法来确保函数对所有可能的输入具有确定性,而无需编写特定的逻辑来处理每种不同的类型。

使用强制转换,我们的示例现在变为:

<?php

function displayRoomCount( $numBoys, $numGirls ) {
  // we cast to ensure that we have the types we expect
  $numBoys = (int)$numBoys;
  $numGirls = (int)$numGirls;

  // check boundary conditions
  if( ($numBoys < 0) || ($numGirls < 0) ) throw new Exception('argument out of range');

  // perform the specified logic
  $total = $numBoys + $numGirls;
  print( "{$total} people: {$numBoys} boys, and {$numGirls} girls \n" );
}

displayRoomCount("asdf", 10);  // (ok now!) prints: "10 people: 0 boys, and 10 girls"

该功能现在按预期运行。事实上,很容易证明函数的行为现在已经为所有可能的输入定义良好。这是因为对于所有可能的输入,铸造操作都是明确定义的;演员们确保我们一直在使用整数;并编写函数的其余部分,以便为所有可能的整数定义明确。

Rules for type-casting in PHP are documented here,(请参阅页面中间的特定类型链接 - 例如:“转换为整数”)。

这种方法的另一个好处是,该函数现在的行为方式与其他PHP内置函数和语言结构一致。例如:

// assume $db_row read from a database of some sort
displayRoomCount( $db_row['boys'], $db_row['girls'] ); 
尽管$db_row['boys']$db_row['girls']实际上是包含数值的字符串,但

才能正常工作。这与普通的PHP开发人员(不知道C,C ++或Java)希望它能够工作的方式一致。


至于转换返回值:除非您知道您有一个可能的混合类型变量,并且您希望始终确保返回值是特定类型,否则这样做几乎没有意义。在代码的中间点更常见,而不是从函数返回的位置。

一个实际的例子:

<?php

function getParam( $name, $idx=0 ) {
  $name = (string)$name;
  $idx = (int)$idx;

  if($name==='') return null;
  if($idx<0) $idx=0;

  // $_REQUEST[$name] could be null, or string, or array
  // this depends on the web request that came in.  Our use of
  // the array cast here, lets us write generic logic to deal with them all
  //
  $param = (array)$_REQUEST[$name];

  if( count($param) <= $idx) return null;
  return $param[$idx];
}

// here, the cast is used to ensure that we always get a string
// even if "fullName" was missing from the request, the cast will convert
// the returned NULL value into an empty string.
$full_name = (string)getParam("fullName");

你明白了。


有几个问题要注意

  • PHP的强制转换机制不够智能,无法优化“无操作”强制转换。因此,始终会导致变量的副本生成。在大多数情况下,这不是问题,但如果你经常使用这种方法,你应该把它放在脑海中。因此,强制转换可能会导致引用和大型数组出现意外问题。有关详细信息,请参阅PHP Bug Report #50894

  • 在php中,一个太大(或太小)不能表示为整数类型的整数将自动表示为float(如果需要,则表示为double)。这意味着($big_int + $big_int)的结果实际上可以是浮点数,如果将其转换为int,则得到的数字将是乱码。所以,如果你正在构建需要对大整数进行操作的函数,你应该记住这一点,并考虑其他一些方法。


对于长篇文章感到抱歉,但这是我深入考虑的一个主题,多年来,我已经积累了相当多的知识(和意见)。把它放在这里,我希望有人会觉得它有用。

答案 1 :(得分:4)

PHP的下一个版本(可能是5.4)will support scalar type hinting in arguments

但除此之外:动态类型转换确实不是你应该讨厌和避免的事情。大多数情况下它会按预期工作。如果没有,请通过检查某种类型的is_*来修复它,使用严格的比较,...,...

答案 2 :(得分:2)

您可以将type hinting用于复杂类型。如果您需要比较值+类型,可以使用“===”进行比较。

(0 === false) => results in false
(0 == false) => results in true

你也写return (array)$result;没有意义。如果您希望返回类型为数组,那么在这种情况下您想要的是return array($result)

答案 3 :(得分:2)

我认为这不好,但我会更进一步:对复杂类型使用type hinting,如果简单类型不是您期望的类型,则抛出异常。通过这种方式,您可以让客户了解转换的任何成本/问题(例如从int转移精度 - &gt;浮点数或浮点数 - &gt; int)。

你在上面的代码中使用转换为数组会产生误导 - 你应该创建一个包含一个值的新数组。

所有人都说,上面的例子变成了:

public function doo($foo, $bar) {
   if (!is_int($foo)) throw new InvalidArgumentException();
   if (!is_float($bar)) throw new InvalidArgumentException();
   $result = $bar + $foo;
   return array($result);
}

答案 4 :(得分:0)

不,因为你不知道你到底会有什么,所以对你进行类型转换是不好的。我个人建议使用intval()floatval()等功能