1. 概要
この RFC は、int、float、string、bool の 4 つの新しいスカラー型宣言を追加することを推奨しています。 PHP のメカニズムにより、一貫した使用が維持されます。また、RFC は、同じ PHP ファイル内のすべての関数呼び出しとステートメントの戻り値が「厳密に制約された」スカラー型のステートメント チェックを持つように、新しいオプションの命令 (declare(strict_type=1);) を各 PHP ファイルに追加することを推奨しています。さらに、厳密な型制約をオンにした後、拡張機能または PHP 組み込み関数を呼び出すと、パラメーターの解析が失敗すると E_RECOVERABLE_ERROR レベルのエラーが生成されます。これら 2 つの機能により、RFC は PHP の記述がより正確になり、文書化されることを期待しています。
2. 詳細
スカラー型宣言:
新しい 予約語 は追加されていません。 Int、float、string、bool は型宣言として認識され、クラス/インターフェイス/トレイトなどの名前として使用することは禁止されます。新しいユーザー スカラー型宣言。内部高速パラメーター解析 API を通じて実装されます。
strict_types/declare() ディレクティブ
デフォルトでは、すべての PHP ファイルは弱い型チェック モードになっています。新しい宣言ディレクティブは、strict_types の値 (1 または 0) を指定します。1 は厳密な型チェック モードを示し、関数呼び出しと return ステートメントに適用されます。0 は弱い型チェック モードを示します。
declare(strict_types=1) は、ファイルの最初のステートメントである必要があります。このステートメントがファイル内の他の場所にある場合、コンパイル エラーが生成され、ブロック モードは明示的に禁止されます。
はencodingディレクティブに似ていますが、ticksディレクティブとは異なり、strict_typesディレクティブは指定されたファイルにのみ影響し、(includeなどを介して)それに含まれる他のファイルには影響しません。このディレクティブは実行時にコンパイルされ、変更できません。その仕組みは、関数呼び出しと戻り値の型チェックが型制約に準拠するようにオペコードにフラグを設定することです。
パラメータ型宣言
例えば、このディレクティブはすべての関数呼び出しに影響します。 (厳密には検証モード):
<?php declare(strict_types=1); foo(); // strictly type-checked functioncall function foobar() { foo(); // strictly type-checked function call } class baz { function foobar() { foo(); // strictly type-checked function call } }
コントラスト (弱い検証モード)
<?php foo(); // weakly type-checked function call function foobar() { foo(); // weakly type-checked function call } class baz { function foobar() { foo(); // weakly type-checked function call } } ?>
戻り値の型宣言:
ディレクティブは、同じファイル内のすべての関数の戻り値の型に影響します。たとえば (厳密な検証モード):
<?php declare(strict_types=1); function foobar(): int { return 1.0; // strictly type-checked return } class baz { function foobar(): int { return 1.0; // strictly type-checked return }} ?>
<?php function foobar(): int { return 1.0; // weakly type-checked return } class baz { function foobar(): int { return 1.0; // weakly type-checked return }} ?>
弱い型チェック動作:
一个弱类型校验的函数调用,和PHP7之前的PHP版本是一致的(包括拓展和PHP内置函数)。通常,弱类型校验规则对于新的标量类型声明的处理是相同的,但是,唯一的例外是对NULL的处理。为了和我们现有类、调用、数组的类型声明保持一致,NULL不是默认的,除非它作为一个参数并且被显式赋值为NULL。
为了给不熟悉PHP现有的弱标量参数类型规则的读者,提供简短的总结。表格展示不同类型能够接受和转换的标量类型声明,NULL、arrays和resource不能接受标量类型声明,因此不在表格内。
*只有范围在PHP_INT_MIN和PHP_INT_MAX内的non-NaN float类型可以接受。(PHP7新增,可查看ZPP Failure on Overflow RFC)
?Non-numeric型字符串不被接受,Numeric型字符串跟随字符串的,也可以被接受,但是会产生一个notice。
?仅当它有toString方法时可以。
严格类型校验行为:
严格的类型校验调用拓展或者PHP内置函数,会改变zend_parse_parameters的行为。特别注意,失败的时候,它会产生E_RECOVERABLE_ERROR而不是E_WARNING。它遵循严格类型校验规则,而不是传统的弱类型校验规则。严格类型校验规则是非常直接的:只有当类型和指定类型声明匹配,它才会接受,否则拒绝。
有一个例外的是,宽泛类型转换是允许int变为float的,也就是说参数如果被声明为float类型,但是它仍然可以接受int参数。
<?php declare(strict_types=1); function add(float $a, float $b): float { return $a + $b;} add(1, 2); // float(3) ?>
在这种场景下,我们传递一个int参数给到定义接受float的函数,这个参数将会被转换为float。除此之外的转换,都是不被允许的。
三、例子:
让我们创建一个函数,让2个数相加。
add.php <?php function add(int $a, int $b): int { return $a + $b;} ?>
如果在分开的文件,我们可以调用add函数通过弱类型的方式
<?php require "add.php"; var_dump(add(1,2)); // int(3) // floats are truncated by default var_dump(add(1.5,2.5)); // int(3) //strings convert if there's a number part var_dump(add("1","2")); // int(3) ?>
默认情况下,弱类型声明允许使用转换,传递进去的值会被转换。
<?php require "add.php"; var_dump(add("1foo", "2")); // int(3) // Notice: A non well formed numeric value encountered
但是,通过可选择指令declare开启严格类型校验后,在这个场景下,相同的调用将会失败。
<?php declare(strict_types=1); require "add.php"; var_dump(add(1,2)); // int(3) var_dump(add(1.5,2.5)); // int(3) // Catchable fatal error: Argument 1 passed to add() must be of the type integer, float given
指令影响同一个文件下的所有函数调用,不管这个被调函数是否在这个文件内定义的,都会采用严格类型校验模式。
<?php declare(strict_types=1); $foo = substr(52,1); // Catchable fatal error: substr() expects parameter 1 to be string, integer given
标量类型声明也可以用于返回值的严格类型校验:
<?php function foobar(): int { return 1.0; } var_dump(foobar());// int(1)
在弱类型模式下,float被转为integer。
<?php declare(strict_types=1); function foobar(): int { return 1.0; } var_dump(foobar()); //Catchable fatal error: Return value of foobar() must be of the type integer,float returned
四、背景和理论基础
历史
PHP从PHP5.0开始已经有对支持class和interface参数类型声明,PHP5.1支持array以及PHP5.4支持callable。这些类型声明让PHP在执行的时候传入正确的参数,让函数签名具有更多的信息。
先前曾经想添加标量类型声明,例如Scalar Type Hints with Casts RFC,因为各种原因失败了:
(1)类型转换和校验机制,对于拓展和PHP内置函数不匹配。
(2)它遵循一个弱类型方法。
(3)它的“严格”弱类型修改尝试,既没有满足严格类型的粉丝期望,也没有满足弱类型的粉丝。
这个RFC尝试解决全部问题。
弱类型和强类型
在现代编程语言的实际应用中,有三种主要的方法去检查参数和返回值的类型:
(1)全严格类型检查(也就是不会有类型转换发生)。例如F#、GO、Haskell、Rust和Facebook的Hack的用法。
(2)广泛原始类型检查(“安全”的类型转换会发生)。例如Java、D和Pascal。他们允许广泛原始类型转换(隐式转换),也就是说,一个8-bit的integer可以根据函数参数需要,被隐形转换为一个16-bit的integer,而且int也可以被转换为float的浮点数。其他类型的隐式转换则不被允许。
(3)弱类型检查(允许所有类型转换,可能会引起警告),它被有限制地使用在C、C#、C++和Visual Basic中。它们尝试尽可能“不失败”,完成一次转换。
PHP在zend_parse_parameters的标量内部处理机制是采用了弱类型模式。PHP的对象处理机制采用了广泛类型检查方式,并不追求精确匹配和转换。
每个方法各有其优缺点。
这个提案中,默认采用弱类型校验机制,同时追加一个开关,允许转换为广泛类型校验机制(也就是严格类型校验机制)。
なぜ両方をサポートするのですか?
これまでのところ、スカラー型宣言のほとんどの支持者は、厳密な型チェックと弱い型チェックのどちらか一方だけではなく、両方のサポートを必要としています。この RFC は、弱い型チェックをデフォルトの動作とし、厳密な型チェックを使用するためのオプションのディレクティブを (同じファイル内で) 追加します。この選択の背後には多くの理由があります。
PHP コミュニティの大部分は、完全な static 型を好んでいるようです。ただし、スカラー型宣言に厳密な型チェックを追加すると、いくつかの問題が発生します:
(1) 明らかな矛盾を引き起こします: 拡張機能と PHP 組み込み関数はスカラー型パラメータに対して弱い型チェックを使用しますが、ユーザーのPHP 関数 は厳密な型チェックを使用します。
(2) かなりの数の人が弱い型チェックを好み、この提案に同意せず、その実装を妨げる可能性があります。
(3) 既存のコードが PHP の弱い型を使用しているため、影響を受けます。関数がパラメータにスカラー型宣言を追加する必要がある場合、特にライブラリ ファイルの場合、既存のコード ベースの複雑さが大幅に増加します。
ここには、弱い型チェックを好む人がまだかなりの数いますが、厳密な型チェック宣言を追加したり、弱い型チェック宣言を追加したりすると、いくつかの問題が発生します。 (1) 厳密な型チェックを好むほとんどの人は、この提案を気に入らず、その実装をブロックするでしょう。
(2) 静的分析の機会を制限します。 (おそらく最適化の機会を意味します)
(3) 自動型変換におけるいくつかのデータ損失バグを隠します。
3 番目の解決策が提案されています。これは、弱い型宣言と厳密な型宣言を区別する構文を追加することです。また、いくつかの問題も発生します:
(1) 弱い型付けと厳密な型チェックを好まない人は、それぞれ厳密な型チェックまたは弱い型チェックとして定義されたライブラリを扱うことを余儀なくされます。
(2) strict 宣言の追加と同様、これも元の弱い型の実装や PHP 組み込み関数の拡張と矛盾します。
これら 3 つの解決策によって引き起こされる問題を解決するために、この RFC は 4 番目の解決策を提案しています。つまり、各ファイルで厳密な型チェックまたは弱い型チェックを定義します。それは次の利点をもたらします:
(1) 人々は自分に合った型チェックを選択できます。つまり、このソリューションは厳密な型チェック陣営と弱い型チェック陣営の両方を満たすことを期待しています。
(2) API は、特定の型宣言パターンに強制的に適応することはありません。
(3) ファイルはデフォルトで弱い型チェックスキームを使用するため、既存のコードベースはコード構造を破壊することなくスカラー型宣言を追加できます。コード ベースで型宣言を段階的に追加したり、特定のモジュールに対してのみ追加したりすることもできます。
(4) スカラー型宣言を定義するために必要な構文は 1 つだけです。
(5) 厳密な型チェックを好む人は通常、この機能をユーザー定義関数だけでなく拡張機能や PHP 組み込み関数でも使用します。言い換えれば、PHP ユーザーは、厳密なスカラー宣言の矛盾のない統一されたメカニズムを得ることができます。
(6) 厳密型検査モードでは、拡張機能および PHP 組み込み関数によって生成される型検証失敗のエラー レベルは、ユーザー定義関数によって生成されるエラー レベルと一致し、両方とも E_RECOVERABLE_ERROR です。
(7) 厳密に型指定されたコードと弱く型指定されたコードを単一のコード ベースにシームレスに統合できます。
以上がPHP7のスカラー型宣言RFCのサンプルコードの詳細説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。