为啥 PHP 中需要类型提示?

Posted

技术标签:

【中文标题】为啥 PHP 中需要类型提示?【英文标题】:Why is type hinting necessary in PHP?为什么 PHP 中需要类型提示? 【发布时间】:2016-11-13 12:30:31 【问题描述】:

我无法理解 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() 中的小类型提示,代码都会执行其操作;

所以,这让我相信类型提示只是一种编码约定,以确保只使用某些类而不需要生成功能代码,这是正确的吗?

如上所述,类型提示是否可以在您与团队合作时创建标准

我错过了什么吗?

【问题讨论】:

哇,在这里我试图让它变得更复杂。谢谢! 类型提示可以帮助您快速捕获错误,方法是在您向它传递一个它不能接受的参数时抱怨,而不是让它接受它,然后在它试图将它用作类型对象时抛出神秘错误X 而实际上它是 Y 类型的对象。 咳咳,依赖注入也是。事实上,您不必在方法内实例化一个类。 另见:***.com/questions/536514/… 类型提示和返回类型,可以帮助智能编辑器。 【参考方案1】:

类型提示不是必需的,但它可以让您发现某些类型的错误。例如,您可能有一个需要整数的函数或方法。 PHP 会将happily convert“数字查找字符串”转换为整数,这可能会导致难以调试行为。如果您在代码中指定您特别需要一个整数,这可以首先防止这些类型的错误。许多程序员认为以这种方式保护他们的代码是一种最佳做法。

作为一个具体的例子,让我们看一下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 恰恰相反,这是一个很好的例子因为例子当前有效。 sing() 函数旨在与Song 一起使用,而不是与它共享功能的任何其他东西一起使用。在某个时候更改 Test 类是完全合理的,因为它只是一个测试,但突然代码会在一个完全不同的地方中断(sing() 函数)。类型提示通过在发生实际错误的地方抛出错误来防止这种情况发生:将错误传递给sing(),并且只是巧合。 话虽如此,这也是一个很好的例子,说明为什么在可能存在兼容行为的不同实现时类型提示应该使用interfaces。在这里,SongTest 都满足 sing() 的需求,因此编写此示例的最佳方法是针对 Singable 接口的类型提示,然后可以由 Song 和 @ 实现987654344@。如果Test 稍后更改并且不再兼容,则更改将破坏接口要求。这当然需要更改对 getter 方法的变量访问。 你说的是真的,但你的答案没有显示防止问题的类型提示 - 只会导致一个问题!在您给出的示例中,工作代码是没有类型提示的代码;添加类型提示时代码会中断。您的回答没有证明它可能有助于的假设的未来情景。鉴于声明“如果您在代码中指定您特别需要一个整数,这可以首先防止这些类型的错误”,如果您的答案证明了这一点会更好,因为目前它没有。【参考方案2】:

类型提示是一个自然过程。起初,这似乎是额外的工作,但随着您的项目在 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

【讨论】:

【参考方案3】:

无论有没有类型提示,您的代码都会运行良好。

类型提示只是强制您将特定类型的内容传递给该函数/方法。

例如

function displayNames($names)

    foreach ($names as $n) 
        echo $n;
    

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

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

然而,PHP 允许用户传入任何内容,例如字符串、int、bool、对象等。当它到达displayNames 函数内部的foreach 时,这些都不会按照您的预期执行.

添加array 类型提示会强制执行此函数期望$names 是一个数组这一事实。

function displayNames(array $names)

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

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

你应该知道

低于 7 的 PHP 版本只允许类(包括接口)和数组的类型提示。要为stringintbool 等输入提示,您需要使用 PHP 7。

【讨论】:

【参考方案4】:

有必要吗? 没有

这是最佳实践吗? 是的

哪个 PHP 版本支持它? PHP 5.6 仅用于类,PHP 7+ 也允许对基元(int、string、boolean 等)进行类型提示。

它有什么用处?

    通过 IDE 获取提示(取决于 IDE)。 标准化团队项目中的编码风格。 减少logic errors。 从 PHP 作为有损类型语言转变为强类型 PHP,但保留使用有损或强类型语言的选项在开发人员手中。

【讨论】:

【参考方案5】:

类型提示就像名称中的提示一样。它允许开发人员查看哪些类可以作为参数传递,并防止它们传递错误的参数。如果您与不止一个人在同一个项目上工作和/或代码用于开源,这将特别有用。

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

【讨论】:

【参考方案6】:

使您的代码更稳定,尤其是在项目很大的情况下

代码更易读、更干净、更专业

让您在 IDE 中自动完成

像 PHPStan 这样的代码嗅探器可以找到你调用方法的位置 参数不好

当你来到一个新项目时,你可以更容易地理解代码和方法是如何工作的

没有类型提示,如果你用错误的对象调用你的方法,什么都不会发生,因为 $song->title$song->lyrics 会返回 null ;当有类型提示时抛出异常

【讨论】:

以上是关于为啥 PHP 中需要类型提示?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 PHP 的类型提示认为我的类是字符串?

为啥使用suphp类型之后网站后台登陆老是提示验证错误呢?

为啥 PhpStorm 会报“参数类型不匹配”错误?

为啥我们需要在 bindParam() 中指定参数类型?

PHP Traversable 类型提示

为啥类型提示不能提高此功能的性能?