繁体   English   中英

PHP类型转换 - 好还是坏?

[英]PHP Typecasting - Good or bad?

经过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?

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

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

转换真的是我的技术的基础 - 而且(恕我直言)它是一致的构建干净,可读的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"

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

这里记录了PHP中的类型转换规则 ,(请参阅页面中间的类型特定链接 - 例如:“转换为整数”)。

这种方法的另一个好处是,该函数现在的行为方式与其他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错误报告#50894

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


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

下一版本的PHP(可能是5.4) 将支持参数中的标量类型提示

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

您可以对复杂类型使用类型提示 如果需要比较值+类型,可以使用“ === ”进行比较。

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

你也写return (array)$result; 这毫无意义。 在这种情况下,如果希望返回类型为数组,则需要return array($result)

我不认为这很糟糕,但我会更进一步:对复杂类型使用类型提示 ,如果简单类型不是您期望的类型,则抛出异常。 这样,您可以让客户了解转换的任何成本/问题(例如从int - > float或float - > 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);
}

不,因为你不知道你最终会拥有什么,所以对于类型转换并不好。 我个人建议使用诸如intval()floatval()等函数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM