正如我们大多数人所知,PHP 弱类型. 。对于那些不这样做的人,PHP.net 说:

PHP 不要求(或支持)变量声明中显式类型定义;变量的类型由使用该变量的上下文确定。

不管你喜欢还是讨厌,PHP 都会动态地重新转换变量。因此,以下代码是有效的:

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

PHP 还允许您显式转换变量,如下所示:

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

这一切都很酷...但是,我无论如何也想不出这样做的实际理由。

对于支持强类型的语言(例如 Java),我没有任何问题。很好,我完全理解。另外,我知道 - 并完全理解 - 的用处 类型提示 在函数参数中。

上面的引用解释了我在类型转换方面遇到的问题。如果 PHP 可以交换类型 随意, ,即使在您之后它也可以这样做 强制施法 一种;它可以这样做 即时 当您在操作中需要某种类型时。这使得以下内容有效:

$var = "10";
$value = (int)$var;
$value = $value . ' TaDa!';
var_dump($value); // string(8) "10 TaDa!"

那么有什么意义呢?


以这个理论上的例子为例,用户定义的类型转换 在 PHP 中有意义:

  1. 您强制转换变量 $foo 作为 int(int)$foo.
  2. 您尝试在变量中存储字符串值 $foo.
  3. PHP 抛出异常!!← 这是有道理的。突然之间,用户定义类型转换的原因就存在了!

事实上 PHP 会根据需要进行切换,这使得用户定义类型转换的意义变得模糊。例如,以下两个代码示例是等效的:

// 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;

在最初提出这个问题一年后,猜猜谁发现自己在实际环境中使用类型转换?敬上。

要求是在网站上显示餐厅菜单的货币价值。该站点的设计要求修剪尾随零,以便显示如下所示:

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

我发现最好的方法是将变量转换为浮点数:

$price = '7.50'; // a string from the database layer.
echo 'Menu Item 2 .............. $ ' . (float)$price;

PHP 修剪浮点数的尾随零,然后将浮点数重新转换为字符串以进行串联。

有帮助吗?

解决方案

在一种弱化的语言中,存在类型铸造以消除键入操作中的歧义,否则编译器/解释器将使用订单或其他规则来假设要使用哪种操作。

通常,我会说PHP遵循这种模式,但是在我检查过的情况下,PHP在每种情况下都表现出违反直觉。

以下是这些情况,使用JavaScript作为比较语言。

字符串串联

显然,这不是PHP的问题,因为有独立的 字符串串联 (.)和加法(+)操作员。

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

字符串比较

通常,在计算机系统中,“ 5”大于“ 10”,因为它不会将其解释为数字。在PHP中并非如此,即使 两个都 是字符串,意识到它们是数字,并且消除了对演员的需求):

JavaScript
console.log("5" > "10" ? "true" : "false"); // true
php
echo "5" > "10" ? "true" : "false";  // false!

功能签名键入

PHP在功能签名上进行了裸露的类型检查,但不幸的是,它是如此有缺陷,可能很少可用。

我以为我可能做错了什么,但是 评论文档 确认在PHP功能签名中不能使用以外的内置类型 - 尽管错误消息具有误导性。

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?

而且,与我知道的其他任何语言不同,即使您使用了它理解的类型,NULL也无法再传递给该参数(must be an instance of array, null given)。多么愚蠢。

布尔解释

[编辑]:这是新的。我想到了另一种情况,逻辑再次从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.

因此总而言之,我唯一能想到的是...(鼓声)

类型截断

换句话说,当您具有一种类型的值(例如字符串)时,您想将其解释为另一种类型(int),并且要强迫它成为该类型中的有效值集之一:

$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

我看不到什么是升级(对于包含更多值的类型)确实会赢得您的能力。

因此,尽管我不同意您建议的打字使用(您本质上是在提议 静态打字, ,但只有当时才有歧义 力量 变成类型会丢失错误 - 这会引起混乱),我认为这是一个很好的问题,因为显然铸造有 非常 PHP几乎没有目的。

其他提示

您正在混合弱/强,动态/静态类型的概念。

PHP是弱且动态的,但是您的问题与动态类型的概念有关。这意味着,变量没有类型,值可以。

“类型铸造”是一种产生不同类型原始类型的新值的表达。它对变量没有任何作用(如果涉及的话)。

我经常键入铸造值的一种情况是在数字SQL参数上。您应该对您插入SQL语句的任何输入值进行消毒/逃脱任何输入值,或者(更好)使用参数化查询。但是,如果您想要一些必须是整数的价值,那么施放它就容易得多。

考虑:

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

如果我忽略了第一行, $id 将是SQL注入的简单矢量。演员们确保这是一个无害的整数。任何插入某些SQL的尝试都会导致查询 id=0

我发现的PHP中类型铸造的一种用途:

我正在开发一个Android应用程序,该应用程序向服务器上的PHP脚本提出HTTP请求,以从数据库中检索数据。该脚本以PHP对象(或关联数组)的形式存储数据,并作为JSON对象返回到该应用程序。没有类型的铸造,我会收到这样的东西:

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

但是,使用PHP类型铸造 (int) 在用户的ID上存储PHP对象时,我将其返回到应用程序:

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

然后,当JSON对象在应用程序中解析时,它使我不得不将ID解析为整数!

看,非常有用。

一个示例是具有 __toString 方法的对象:$str = $obj->__toString();$str = (string) $obj;. 。第二次打字要少得多,额外的东西是标点符号,打字需要更长的时间。我还认为它更具可读性,尽管其他人可能不同意。

另一个是制作一个单元素数组:array($item);(array) $item;. 。这会将任何标量类型(整数、资源等)放入数组中。
或者,如果 $item 是一个对象,它的属性将成为其值的关键。但是,我确实认为对象->数组转换有点奇怪:private 和 protected 属性是数组的一部分,并被重命名。引用 PHP 文档: :私有变量的变量名前面带有类名;受保护的变量在变量名前有一个“*”。

另一个用途是将 GET/POST 数据转换为适合数据库的类型。MySQL 可以自己处理这个问题,但我认为更符合 ANSI 的服务器可能会拒绝数据。我只提到数据库的原因是,在大多数其他情况下,数据将在某个时刻根据其类型对其执行操作(即int/floats 通常会对其进行计算,等等)。

这个脚本:

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

会很好 script.php?tags[]=one 但会失败 script.php?tags=one, , 因为 _GET['tags'] 在第一种情况下返回数组,但在第二种情况下不返回。由于脚本被编写为期望数组(并且您对发送给脚本的查询字符串的控制权更少),因此可以通过适当地从中施放结果来解决问题 _GET:

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

它也可以用作快速而肮脏的方法,以确保不受信任的数据不会破坏某些内容,例如使用具有废话验证并且只能接受数字的远程服务。

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

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

显然,这是有缺陷的,也由IF语句中的比较操作员隐含地处理,但有助于确保您确切知道代码在做什么。

我经常看到的一种“使用”重新计算变量的一种“使用”是在从外部来源检索数据(用户输入或数据库)时。它可以让编码器 (请注意,我没有说开发人员) 忽略(或甚至不学习)不同来源可用的不同数据类型。

一个编码器 (请注意,我没有说开发人员) 我继承和仍然维护的代码似乎并不知道 存在字符串之间的区别 "20"$_GET 超级变量,到整数操作之间 20 + 20 当她将其添加到数据库中的值中时。她只幸运的是PHP使用 . 用于字符串串联而不是 + 像其他所有语言一样,因为我看到了她的代码“添加”两个字符串(a varcahr 从mysql和一个值 $_GET)并获得一个int。

这是一个实用的例子吗?只有从某种意义上说,它才能使编码员不知道他们正在使用哪些数据类型。 我个人讨厌它。

许可以下: CC-BY-SA归因
scroll top