为什么在PHP中需要类型提示?

38
我对PHP中类型提示的重要性感到困惑。
显然,在PHP中,“类型提示”可以定义为:
“类型提示”强制你只传递特定类型的对象。这可以防止你传递不兼容的值,并在团队协作时创建标准。
那么,基本上,类型提示并不是必须的,才能使代码正常工作吗?
我有以下代码试图理解正在发生什么...

Index.php

<?php
include 'Song.php';

$song_object = new Song;

$song_object->title = "Beat it!";
$song_object->lyrics = "It doesn't matter who's wrong or right... just beat it!";


function sing(Song $song)
{
    echo "Singing the song called " . $song->title;
    echo "<p>" . $song->lyrics . "</p>";
}

sing($song_object);

Song.php

<?php

class Song
{
    public $title;
    public $lyrics;
}

代码在函数sing()中是否使用小的类型提示,都能正常运行。

enter image description here

因此,我认为类型提示只是一种编码惯例,用于确保仅使用某些类,而不需要生成功能代码,这正确吗?

正如上面的引语所示,类型提示在团队合作中有助于创建标准。

我是否遗漏了什么?


哇,我一直在试图让它变得更复杂。谢谢! - Jethro Hazelhurst
5
类型提示可以帮助您快速捕获错误,当您传递了一个它不能接受的参数时,它会抱怨而不是接受它并在尝试将其用作类型为X的对象时抛出神秘的错误,而实际上它是一个类型为Y的对象。 - apokryfos
3
嗯,依赖注入也可以。这样,你就不必在方法内部实例化一个类。 - Chay22
1
参见:https://dev59.com/63RB5IYBdhLWcg3wuZU1 - Progrock
4
类型提示和返回类型可以帮助智能编辑器。 - Progrock
显示剩余2条评论
7个回答

42

类型提示并非必须,但它可以帮助您捕获某些类型的错误。例如,您可能有一个需要整数的函数或方法。PHP会愉快地将“看起来像数字的字符串”转换为整数,这可能导致难以调试的行为。如果您在代码中指定了您需要一个整数,这可以防止这些类型的错误在第一时间发生。许多程序员认为以这种方式保护他们的代码是最佳实践。

作为这种操作的具体示例,让我们看一下您的index.php文件的更新版本:

index.php

<?php
include 'Song.php';
include 'Test.php';

$song_object = new Song;
$test_object = new Test;

$song_object->title = "Beat it!";
$song_object->lyrics = "It doesn't matter who's wrong or right... just beat it!";

$test_object->title = "Test it!";
$test_object->lyrics = "It doesn't matter who's wrong or right... just test it!";


function sing(Song $song)
{
    echo "Singing the song called " . $song->title;
    echo "<p>" . $song->lyrics . "</p>";
}

sing($song_object);
sing($test_object);

除了新的Test.php文件外,我还添加了:

Test.php

<?php

class Test
{
    public $title;
    public $lyrics;
}

当我现在运行index.php时,会出现以下错误: 输出:
Singing the song called Beat it!<p>It doesn't matter who's wrong or right...
just beat it!</p>PHP Catchable fatal error:  Argument 1 passed to sing() must
be an instance of Song, instance of Test given, called in test/index.php on
line 22 and defined in test/index.php on line 15

Catchable fatal error: Argument 1 passed to sing() must be an instance of
Song, instance of Test given, called in test/index.php on line 22 and defined
in test/index.php on line 15

这是 PHP 提醒我在调用 "sing()" 函数时,使用了错误类型的类。这个提示很有用,即使上面的示例可以工作,但"Test"类可能与 "Song" 类不同。这可能会导致以后难以调试的错误。以这种方式使用提示,开发人员可以在它们引起问题之前防止类型错误。这在像 PHP 这样的语言中特别有用,该语言常常渴望自动转换数据类型。

这是一个非常糟糕的例子,因为这两个类是兼容的,因此使用Test代替Song没有任何问题。在这种情况下,类型提示实际上是一种障碍,会导致代码崩溃,而没有它,代码将正常工作。您的观点(是有效的)最好用需要保护的示例来说明。就目前而言,这感觉像是展示类型提示的缺点之一。 - HappyDog
2
@HappyDog,恰恰相反,这是一个很好的例子,特别是因为这个例子目前是有效的。sing() 函数旨在与 Song 一起使用,而不是与其他任何可能共享功能的东西一起使用。在某些时候更改 Test 类是完全合理的,因为它只是一个测试,但是突然代码会在完全不同的地方(sing() 函数)中断。类型提示通过在实际错误发生的地方抛出错误来防止这种情况:将错误的参数传递给 sing() 并只有偶然才能正常工作。 - scenia
话虽如此,这也是为什么类型提示应该在可能存在不同实现的兼容行为时使用“接口”的好例子。在这里,“Song”和“Test”都满足“sing()”所需的要求,因此编写此示例的最佳方法是针对“Singable”接口进行类型提示,然后可以由“Song”和“Test”都实现它。如果以后更改了“Test”并且不再兼容,则更改将破坏接口要求。当然,这需要将变量访问更改为getter方法。 - scenia
你说的没错,但是你的回答并没有展示类型提示如何防止问题 - 只会导致问题!在你给出的例子中,没有类型提示的代码是可行的;当添加类型提示时,代码就会出错。你的回答并没有展示类型提示可能有帮助的假设未来场景。鉴于“如果你在代码中指定你需要一个整数,这可以在第一时间防止那些错误”,如果你的回答能够展示这一点,那将更好,因为目前它并没有展示。 - HappyDog

10

类型提示是一个自然的过程。起初它可能看起来像是额外的工作,但随着你在PHP中的项目增长,它非常有帮助。它可以提高代码的可读性,并使错误控制和严格的编程规范更容易应用。

最初,您必须实现“契约”,其中契约是一个PHP接口,可以“锁定”常量和关键公共方法及其参数,如下所示:

interface SongInterface {
    //...
}

class Song implements SongInterface
{
    public $title;
    public $lyrics;
    //...
}

然后继续实际执行部分:
$song = (object) new Song;

$song->title = (string) "Beat it!";
$song->lyrics = (string) "It doesn't matter who's wrong or right... just beat it!";


function sing(SongInterface $song): string
{
    $html = (string)  "Singing the song called " . $song->title
    . "<p>" . $song->lyrics . "</p>";

    return htmlentities($html, ENT_QUOTES, 'utf-8');
}

echo sing($song);

使用接口时,您只需定义函数,然后在一个歌曲类中实现它。这样,您就可以始终注入另一个歌曲类(具有更新的功能和更改),而不会破坏应用程序。查看oop接口:http://php.net/manual/en/language.oop5.interfaces.php 自php7以来,您还可以定义函数的返回类型。 https://wiki.php.net/rfc/return_types

6

您的代码无论是否使用类型提示都可以正常运行。

类型提示只是强制要求您向该函数/方法传递特定类型的参数。

例如:

function displayNames($names)
{
    foreach ($names as $n) {
        echo $n;
    }
}

假设用户按照以下方式传入数组,则此函数将正常工作。

displayNames(array('Tom', 'John', 'Frank'));

然而,PHP 允许用户传递任何东西,比如字符串、整数、布尔值、对象等。当它们到达 displayNames 函数内部的 foreach 时,它们都不能按照你的预期表现。

添加一个 array 类型提示可以强制确保此函数期望 $names 参数为数组。

function displayNames(array $names)
{
    // We now KNOW that $names is an array.
    foreach ($names as $n) {
        echo $n;
    }
}

数组是一个简单的例子,但正如您所述,也可以为对象类型提示。在这种情况下,它允许开发人员仅接受一个数据库连接实例,在例如doSqlQuery()方法中。

需要注意的是

PHP版本低于7仅允许类(包括接口)和数组进行类型提示。如果要为stringintbool等类型进行类型提示,则需要使用PHP 7。


4

这是必要的吗?不是

这是最佳实践吗?

哪个PHP版本支持它?PHP 5.6仅支持类,PHP 7+允许原语(int,string,boolean等)进行类型提示。

它有什么用处?

  1. 通过IDE(取决于IDE)获得提示。
  2. 在团队项目中标准化编码风格。
  3. 减少逻辑错误
  4. 从PHP成为损失型类型语言转移,到某种程度上强类型化的PHP,但使开发人员保持使用损失或强类型的选择.

3

类型提示就像其名称所暗示的那样,更多的是一种提示。它允许开发人员查看可以作为参数传递的类,并防止他们传递错误的参数。如果你不仅与一个人在同一个项目上工作,而且/或者代码是面向开源的,那么这尤其有帮助。

在php7中,增加了对字符串、整数、(混合类型)和数组的类型提示,因此您不仅需要使用类,还可以使用标准的php类型。


1
使您的代码更加稳定,特别是在项目较大时。 代码更易读、更干净、更专业。 在IDE中提供自动完成功能。 像PHPStan这样的代码嗅探器可以找到您调用具有错误参数的方法的位置。 当您接手一个新项目时,您可以更轻松地理解代码以及方法的工作原理。 如果没有类型提示,如果您使用错误的对象调用了您的方法,则什么也不会发生,因为$song->title和$song->lyrics将返回null;而有了类型提示,就会抛出异常。

0

它使阅读代码变得可预测,就像您知道参数的类型以及函数返回的类型(如果有)。很多时候,在阅读不是您自己编写的代码并跟踪到函数声明时,您经常会问自己“等等,参数1或参数X的类型是什么?”当您想要了解参数是什么时,您不希望一遍又一遍地放置调试器(例如var_dump())。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接