PHP 7.0 から利用できるようになるスカラーの型宣言の RFC をざっと翻訳したものです。PHP 7.1 では戻り値に void 型が指定できるようになります ([RFC]https://wiki.php.net/rfc/void_return_type() 2015年12月7日追記)。
要約
この RFC は4種類のスカラー型、int、float、string と bool の新しい型宣言の追加を提案します。これらの型宣言は PHP 関数が使う既存のメカニズムと同じようにふるまいます。
この RFC はファイル単位でオプションの新しいディレクティブである declare(strict_types=1);
をさらに追加することを提案します。このディレクティブによって、エクステンションとビルトインの PHP 関数を含む、すべての関数呼び出しと戻り値の宣言は "strict" なスカラー型の宣言に対して型チェックがなされます。さらに、このディレクティブを伴うエクステンションとビルトインの PHP 関数の呼び出しによって、パラメーター解析が失敗した段階において、E_RECOVERABLE_ERROR が生成され、これらの関数とユーザーランドの型宣言との協調がもたらされます。
これら2つの機能によって、PHP のプログラムは正しく動き、読み書きの前提知識を減らすことができます (self-documenting)。
詳細
スカラー型の宣言
新しい予約語は追加しません。int
、float
、string
、bool
の名前は型宣言の用途に許可され、(use と class_alias も含めて) クラス/インターフェース/トレイトの名前に使うことが禁止されます。
内部において新しいユーザーランドのスカラー型の宣言は Fast Parameter Parsing API 関数を呼び出すことで実装されます。
strict_types declare() ディレクティブ
デフォルトでは、すべての PHP ファイルは weak 型チェックモードです。新しい declare()
のディレクティブである strict_types
は 1
もしくは 0
のどちらかをとります。1
であれば、ファイルにおける関数呼び出しと戻り値の宣言に strict 型チェックモードが使われます。
declare(strict_types=1)
ディレクティブはファイルの最初の箇所で宣言しなければなりません。ファイルのほかの場所にある場合、コンパイルエラーが生成されます。block モードも明確に無効です (declare(strict_types=1);
が唯一認められる形式です)。
encoding
ディレクティブのように、しかし ticks
ディレクティブとは異なり、strict_types
ディレクティブはそれが使われるファイルにのみ影響を与え、そのファイルをインクルードするファイルないし、そのファイルによってインクルードされるほかのファイルには影響を与えません。
このディレクティブは完全にコンパイル時におけるもので、実行時にはコントロールできません。(パラメーターの型宣言に対して)関数呼び出しのためのオペコードでのフラッグをセットすることで機能し、(戻り値の型宣言に対して)型チェックを返します。
パラメーターの型宣言
このディレクティブは、任意の関数呼び出しおよびその関数の範囲内にある関数およびメソッドを含めて、影響を及ぼします。例です (strict モード):
<?php
declare(strict_types=1);
foo(); // strictly type-checked function call
function foobar() {
foo(); // strictly type-checked function call
}
class baz {
function foobar() {
foo(); // strictly type-checked function call
}
}
上記のコードと見比べてみましょう (weak モード):
<?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
}
}
呼び出された関数が宣言されたファイルで strict もしくは weak 型チェックが使われているかは関係ありません。型チェックモードは関数が呼び出されたファイルに依存します。
戻り値の型宣言
このディレクティブはファイルの範囲になる関数もしくはメソッドの戻り値の宣言に影響します。例です (strict モード):
<?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
}
}
パラメーターの型宣言とは異なり、戻りの型の型チェックが依存するのは、関数が定義されたファイルであり、関数が呼び出されたファイルではありません。これは、間違った型を渡すことは呼び出される側 (callee) の問題である一方で、間違った型を返すことは呼び出す側 (caller) の問題だからです。
weak な型チェックのふるまい
PHP エクステンションもしくは組み込みの関数への weak な型チェックを伴う呼び出しは以前の PHP のバージョンとまったく同じふるまいをします。
新しいスカラーの型宣言に対する weak 型チェックのルールは PHP のエクステンションと組み込みの関数と同じです。これの唯一の例外は NULL の扱いです。クラス、callable、と配列に対する既存の型宣言との一貫性を保つために、それがパラメーターであり、デフォルトの値に NULL が指定されないかぎり、NULL はデフォルトで受けつけません。
PHP の既存の weak なスカラーのパラメーターの型ルールに慣れ親しんでいない読者の資料として、次の手短なまとめを提供します。
次の表はスカラーの型宣言に対してどの型が受け入れられ、変換されるのかを示しています。スカラーの型宣言に対して NULL、配列とリソースはけっして受け入れられませんので、表には含まれていません。
型宣言 | int | float | string | bool | object |
---|---|---|---|---|---|
int | yes | yes* | yes† | yes | no |
float | yes | yes | yes† | yes | no |
string | yes | yes | yes | yes | yes‡ |
bool | yes | yes | yes | yes | no |
*PHP_INT_MIN と PHP_INT_MAX あいだの 非 NaN 浮動小数型のみが受け入れられます。(PHP 7 で新しく導入されました。Overflow RFC の ZPP Failure をご参照ください)
†数字ではない文字列は受け入れられません。末尾に文字が付属する数字は受け入れられますが、notice が生成されます。
‡__toString
メソッドがある場合のみです。
strict な型チェックのふるまい
strict な型チェックによるエクステンションもしくは PHP 組み込み関数の呼び出しは zend_parse_parameters
のふるまいを変えます。とりわけ、失敗時に E_WARNING
ではなく E_RECOVERABLE_ERROR
が生成され、スカラー型のパラメーターに対して、伝統的で weak な型チェックルールの代わりに strict な型チェックルールに従います。
strict な型チェックのルールはとても直感的です。値の型が型宣言で指定したものとマッチすれば、受け入れられ、そうでなければ受け入れられません。
これらの strict な型チェックルールはユーザーランドのスカラー型の宣言、エクステンションと PHP の組み込み関数に対して使われます。
1つの例外は int から float に対するプリミティブな変換を広げることが認められることです。このことは、float を宣言するパラメーターは int も受け入れるということを意味します。
<?php
declare(strict_types=1);
function add(float $a, float $b): float {
return $a + $b;
}
add(1, 2); // float(3)
このケースにおいて、float を受け入れる関数に int を渡しています。パラメーターは float に変換されます (広げられます)。
ほかの変換は認められません。
strict モードにおけるエラーハンドラのふるまい
現在、エラーハンドラーを使うことでエラーチェックの失敗を回避できます。
<?php
declare(strict_types=1);
set_error_handler(function() {
return true;
});
function foo(int $abc) {
var_dump($abc);
}
foo("test"); // string(4) "test"
?>
このふるまいは strict な型づけの目的をだめにする可能性があります。
ですので、この RFC では (今日の内部関数のように) 型のミスマッチエラーがある場合、strict モードにおいて関数実行を回避することを提案します。型チェックが通る場合、このふるまいが engine_exceptions によって優先されるため、実装は完全ではありません。それゆえ、この RFC の投票が終了するまで実装を待つことにします。
例
2つの整数を足し合わせる関数をつくってみましょう。
<?php
function add(int $a, int $b): int {
return $a + $b;
}
独立したファイルで、weak 型づけを使って 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)
有意義にある文脈にあれば、引数の型は整数に「変換」されます。
デフォルトでは、あるていどの変換を認める weak な型宣言が使われるので、変換可能な値を渡すことも可能であり、エクステンションと組み込みの PHP 関数のように、値は変換されます。
<?php
require "add.php";
var_dump(add("1 foo", "2")); // int(3)
// Notice: A non well formed numeric value encountered
しかしながら、オプションのディレクティブで、strict 型を有効にすることもできます。このモードでは、同じ関数呼び出しが失敗します。
<?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
このディレクティブはファイルすべての関数呼び出しに影響を与えます。呼びされた関数が宣言されたファイルで strict な型チェックが使われているかは関係ありません。
ユーザーランドの関数に加えて、strict な型チェックモードはエクステンションと組み込みの PHP 関数にも影響を与えます。
<?php
declare(strict_types=1);
$foo = substr(52, 1);
// Catchable fatal error: substr() expects parameter 1 to be string, integer given
スカラーの型宣言は戻り値に対しても機能します。strict な型チェックモードも同じです。
<?php
function foobar(): int {
return 1.0;
}
var_dump(foobar()); // int(1)
weak モードでは、float は整数型にキャストされます。
<?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 5.1 でインターフェースとクラス名に対するパラメーターの型宣言が導入され、PHP 5.4 で callable 型が導入されました。これらの型宣言によって、正しく型づけされた引数が関数に渡されることが保証され、関数のシグネチャがよりくわしい情報を提供するようになります。残念ながら、PHP のスカラー型は型として扱われてきませんでした。
Scalar Type Hints with Casts RFC のように、スカラー型の宣言を追加するさまざまな試みがありましたが、さまざまな理由で失敗しました。
- 型変換とバリデーションがエクステンションと組み込みの PHP 関数のものにマッチしない
- weak な型アプローチに従う
- strict もしくは weak な型づけの支持者をなだめるために失敗したより「strict」で weak 型づけ
この RFC はすべての問題を説明することを試みます。
weak な型づけと strict 型づけ
モダンなプログラミング言語において、パラメーターと戻り値の型をチェックする方法として、3つの主要なアプローチがあります。
- 厳密な strict 型チェック (変換は起きません)。F#、Go、Haskell、Rust と Facebook の Hack などの言語で採用されます。
- プリミティブな型チェックを広げる (「安全な」変換が起きます)。これは Java、D と Pascal のような言語に採用されます。これらは暗黙に起こる Widening-Primitive-Conversion を認めます。このことは8ビットの整数が暗黙のうちに16ビットの整数を要求する引数に渡すことができることを意味します。そして、整数を浮動小数を要求する引数にも渡すことができます。ほかの変換は暗黙のうちには認められません。
- weak な型チェック (すべての変換が認められ、できるかぎりの警告が提示されます)、これは C、C#、C++ と Visual Basic などのかぎられた領域で使われます。このアプローチでは「決して失敗しない」ことが試され、変換において推論が常になされます。
zend_parser_parameters のおける内部の扱いは伝統的に weak モードに従いました。PHP のオブジェクトの扱い (内部と外部の両方) において、チェックを広げる形式が使われます。つまり、正確な一致は要求されませんが、子要素は認められます (反変性とも呼ばれます)。
それぞれのアプローチには長所と短所があります。
この RFC では、内部とユーザー関数に関して、デフォルトでは weak な型チェックをもとにすることを提案します。Widening type checking (提案では strict モードと呼びます) 切り替える機能も追加します。
なぜ両方?
これまでのところ、スカラー型の宣言の推奨者の多くは strict な型チェックと weak な型チェックのどちらかを求めてきました。特定のアプローチもしくはそのほかのアプローチを選び出す代わりに この RFC では weak な型チェックをデフォルトに選び、1つのファイルの範囲内で strict な型チェックを使うオプションのディレクティブを追加します。この選択の背景にはいくつかの理由があります。
PHP コミュニティに大きな影響力をもつ人達 (significant portion) は厳密な strict な型に好意的のようです。しかしながら、
strict な型チェックによるスカラーの型宣言はいくつかの問題を引き起こします。
- 歴然たる非整合性を生み出します: エクステンションと組み込みの PHP 関数はスカラーで型づけされたパラメーターに対して weak な型チェックを使い、ユーザーランドの PHP 関数はスカラー型で宣言されたパラメーターに対して strict な型チェックを使うことになります。
- weak な型チェックに好意的な膨大なユーザーがそのような提案を受け入れないでしょうし、拒絶するでしょう。
- パラメーターにスカラーな型宣言を追加した関数を呼び出すと PHP の weak な型づけを (おそらくは無意識のうちに) 利用する既存のコードが壊れます。既存のコードベース、とりわけライブラリにおいて、関数のパラメーターにスカラーの型宣言を追加する作業が複雑になります。
weak な型チェックに好意的な人達もたくさんいます。しかし、strict な型チェックの宣言のように、weak な型チェックつきの型宣言も問題を引き起こします。
- strict な型チェックを好む大勢の人達がそのような提案に好意的ではなく、拒否されてしまうでしょう。
- 静的コード解析の機会を制限します。
- 自動的な型変換がデータの損失につながる区別しにくいバグを見えなくしてしまう可能性があります。
3番目のアプローチ、異なる構文で weak と strict チェックによる型宣言を追加する方法も提案されてきました。この方法も独自の問題を抱えています。
- weak もしくは strict 型チェックを好まない人はそれぞれ strict もしくは weak な型チェックされるライブラリに対処することを強制されます。
- strict な宣言を追加するように、この方法も一様に weak であるエクステンションと組み込みの PHP 関数に非整合性をもたらします。
これら3つのアプローチの問題を避けるために、この RFC では4番目のアプローチを提案します。ファイル単位の strict もしくは weak な型チェックです。このアプローチには次の長所があります。
- それぞれに最適な型チェックモデルを選ぶことができます。このことはこのアプローチが strict と weak の型チェックの陣営の両方をうまく納得させることができることを意味します。
- API はユーザーのもとで型宣言のモデルを強制しません。
- デフォルトではファイルは weak 型チェックのアプローチを使うので、(ライブラリを含む) 既存のコードベースの関数はそれらの関数を呼び出すコードを壊すことなく、スカラーの型宣言が利用できます。このことによって、少しずつ型宣言を追加することができます。これは「gradual typing」と知られます。
- スカラーの型宣言に対して単一の構文だけ必要になります。
- strict 型チェックを好む人はユーザーランドの関数だけでなく、エクステンションと組み込み PHP 関数に対しても利用できます。このことはユーザーが統一した1つモデルを得ることになり、strict 限定のスカラー宣言に生み出される非整合性を避けることができます。
- strict な型チェックモードにおいて、エクステンションと組み込みの PHP 関数に対して型チェックが失敗するときに生み出されるエラーレベルは最終的にユーザーランド関数に対して生み出されるエラーレベル、と最終的に一貫性を保ちます。両方とも E_RECOVERABLE_ERROR が生成されます。
- 単一のコードベースで strict と weak なコードのシームレスな統合を可能にします。
型宣言の選択
リソースに対する型宣言は追加されません。既存のエクステンションに対して、たとえば GMP で経験したように、リソースからオブジェクトへの移行を妨げるからです。
争点
この提案のまわりには議論されてきた質問がたくさんあります。それらをわかりやすく並べ、同時にこの RFC のスタンスを示します。
この提案は妥協である
この提案が妥協であるという人もいますが、strict と weak な型づけの両方の支持者を納得させるための試みです。
現在のポジション
この提案は妥協ではありません。PHP で strict な型づけを利用できるようにする試みです。型づけされていない PHP と strict な型づけされたコードを橋渡しするメカニズム、「weakな」ブリッジ、が要求されます (さもなければ明示的なキャストが必要です)。この提案は strict と weak な型づけを単一で密接に統合され一貫性のあるシステムに統合します。
ceil() のような内部関数が予期しない型を返す
現在、ceil()
は float を返します。これはあいまいなふるまいをするため、次のような失敗につながる可能性があります。
<?php
declare(strict_types=1);
function foobar(float $abc): int {
return ceil($abc + 1);
}
foobar(123.0);
戻り値はクラッシュします。
問題を解決する方法は2つあります。
-
ceil()
の型を 99%のユースケースに沿うint
に変更する。 - ユーザーにそれぞれの関数で int 型にキャストしてもらう。
現在の立場
この提案ではユーザーのキャストが正しい方向に向かうという立場をとります。99%のユースケースをサポートするために内部関数の戻り値を変更することはもはやサポートされていない1%のユースケースに対して状況を疑いなく悪くするからです。
キャストはコンパイラと読み手の両方が何が起き、何が期待されるのかの意図を明確に伝えます。
"37" を int 型として受け入れるべき
現在、“37” + 1
を実行すると int(38)
が得られます。
Many proponents of weak な型づけの支持者の多くは 整数のような文字列が int に型づけされた関数に渡したいと望むでしょう。
反対に、strict な型づけの推奨者の多くはこの型チェックが型ではなく値に依存するので、要件を満たしていないことを指摘するでしょう。それゆえ、型チェックではなく、ランタイムの値チェックです。strict な型を使うことの長所が失われます。
現在の立場
この提案では numeric strings が受け入れられるのは weak モード限定で宣言された場合のみに限定します。strict モードでは、型はすべて評価されます。
整数は strict な float 引数として受け入れるべき
この RFC の初期のリビジョンでは、整数は float で宣言された関数に受けつけされませんでした。このことは次のコードが失敗することを意味します。number_format()
が第1引数に float を期待するからです。
int_vs_float.php
<?php
declare(strict_types=1);
echo number_format(50);
現在の立場
Java、D と Pascal に沿って、この提案は widening-conversion ルールを実装します。このことは整数は浮動小数点の引数にも受け入れられます (例は上記を参照)。
しかしながら、引数を関数に渡す場合、narrowing conversions (float→int) が動かないことを意味します。
注: Java を仕様をご覧いただければ、narrowing conversions が言及されていることがわかります。割り当てもしくは明示的なキャストにおいてのみ認められます。ですので、この提案が進めるケースには当てはまりません。
weak モードで "10 Birds" が int パラメーターに渡されら エラーにすべき
現在、malformed numeric string で notice が表示されます。weak な型づけの支持者のなかには “10 birds” に警告もしくは recoverable エラーを表示させたい人もいるでしょう。
現在の立場
この提案は内部関数ですでに実装された weak な変換ルールを基本的には変更しません。単純にユーザーランドに表出させるだけです。
それゆえ、weak 型のエラーのふるまいの変更は対象外とします。
Int -> Float 変換はロスレスではない
64ビットのプラットフォームでは、integers > 2^53 が float を正確にあらわしません。subtle なデータの損失になるので、関数呼び出しに対して subtle な問題になる可能性があります。
<?php
declare(strict_types=1);
echo number_format((1<<61)+1);
上記のコードは 2,305,843,009,213,693,952 になります。整数表現の末尾は 953 になので出力は不正確です ( number_format
が float を受け入れるからです)。
現在の立場
現在、この RFC はこのことが受け入れることができるという立場をとっています。理由は2つあります。
- これは現在におけるふるまいだからです: http://3v4l.org/0IolN
- たくさんの値に影響を及ぼさないからです。
関数のオペレーションに重要な影響を及ぼす場合、関数は numeric 型 (int と float の union) を受け入れるように修正し、任意の大きなデータをサポートするように2つのあいだの論理的なスイッチをつくるべきです。
加えて、stict な型づけの主流のプログラミング言語の多く (Java、C#、と Pascal) はこのようなふるまいをします。したがって予期しないふるまいではありません。
Int->Float 例外は strict モードを "Flawed" にする
int→float widening exception は strict モードのコンセプトが flaw であることを示すということを指摘する人たちがいます。
現在の立場
strict モードの長所は個別の受理ルールから独立していることです。strict な型づけは引数の値ではなく型に依存するからです。
weak な型宣言で静的な解析が可能である
weak な型宣言を静的に解析できることを主張する人がいます。
現在の立場
この提案は weak な型宣言は型ではなく渡される値に依存し、静的な解析は堅牢ではないという立場をとります。
静的な解析エンジンは2つのふるまいの1つを実行する必要があるからです:
- 動くかもしれないが、文字列を int パラメーターに渡すときに警告がない。
- 動くかもしれないが、文字列を int パラメーターに渡すときに警告がある。
エラーは事前に補足されないので、1番目の選択肢は無意味です。完全な機能をもつコード (fully functional code) が不適切に表示する可能性があるので、2番目の選択肢は理想的ではありません。
それゆえ、堅牢な静的解析は weak モードで不可能です (チェックは値に依存します)。
エラーは回復可能なエラーの代わりに例外を使うべき
型のミスマッチは回復可能なエラーの代わりに例外として投げられるべきです。
現在の立場
Zend の現在のコーディング標準では例外はオブジェクトのコンテキスト外部で使われないようにすることが認められています。それゆえ、型エラーは一貫性のためにすべての場所で回復可能なエラーを使わなければなりません (型エラーはメソッド外部で使われる可能性があるからです)。
標準の変更はこの提案の範囲の外になります。
Nullable と Union 型
union 型: int|float もしくは nullable 型: int を認めることに関心があります。
現在の立場
これら両方はたんにスカラー型よりも影響を及ぼすので、両方とも今回の提案では範囲の外として考えられています。
numeric 型があるべきである
新しい union 型の有用性は提起されてきました (int|float のビルトインの union 型になる)。
現在の立場
int を受け入れるために float パラメーターを変更することで numeric 型の必要性が低くなるので、この提案では、numeric 型は導入しません。
内部関数が型づけにオプトインすべきである
この提案では strict な型づけをする内部関数を追加します。このことは (ArgInfo など経由で)、内部関数がこの型づけに「オプトイン」すべきであると望む開発者があらわれることにつながります。
これによって、開発者が自分達の API を strict に呼び出したいもしくはそうではないことを選び、また、そのようにふるまいを調整できるようになります。
現在の立場
この提案は関数が呼び出される方法は関数の呼び手しだいであるという立場をとります。それゆえ、これらの型を利用するために、zend_parse_parameters() 経由で公開される既存の型でじゅうぶんです。
PHP コアにおいて、内部関数が mixed 型を受け取る場所はいくつかあります (z パラメーター から ZPP)。内部関数に「weak」であることの機能が必要であれば、z 型指定子を使うことですでにそうなっており、(ユーザーランドで可能であるのと同じように) 独自のロジックを実装しています。すでに型が存在するのであれば、変換はすでに行われています。唯一の違いはこの提案は既存の変換へのコントロールを呼び手のコードに委ねることです。
それゆえ、この提案では、内部開発者が strict な型づけにオプトインできるようにすることは認めません。
declare() の代わりに「use strict」を使わないのか?
strict な型づけに「切り替える」ための代替形式を何人もの人達が提案してきました。次のようなものがあります。
- use strict;
現在の立場
エンジンスイッチのこのスタイルのために宣言システムは簡潔にデザインされました。加えて、将来において、追加の「strict」なふるまいを拡張するための余地が残されています。
代替案として提案されたものを使う場合には問題もあります。
これは新しい構文で、潜在的に、「strictness」が適用される what まわりがあいまいです。将来の互換性に制限をつけることになります。
加えて、=strict; 4 ?> でファイルが始まる場合、潜在的にあいまいです。ファイルに対して strict モードを設定し、4 を出力しますか?もしくは「strict」という定数を出力するのですか?もちろん、これは <?php の後だけに続くというルールで解決しますが、恣意的で潜在的に紛らわしくなります。
以前の PHP のバージョンで実行する場合、これはコードを露出する潜在的な脆弱性を提供する機会を与えてしまいます (<?php-strict の開きタグは適切に解釈されないからです)。
- use strict;
ランタイムに影響を及ぼす名前空間の再利用は嫌なものです。ブロックモードの期待されるふるまいは言うまでもありません。
<?php
namespace Foo {
use strict;
}
namespace {
bar();
}
?>
bar() は strict モードで呼び出されるのかでしょうか?それも strict ではないモードなのでしょうか?
コメントはランタイムのふるまいに影響を及ぼしません。HHVM は PHP と互換性を維持しつつ、ふるまいに影響を及ぼす必要があるので、使っています。我々にはこの問題がありません。
- declare(strict=true) (v0.3 とまったく同じ)
ブロックにおける宣言の奇妙なふるまいなどについて、多くの人が反対してきました。これはスコープを配慮しないので、1つの関数でこれを呼び出すことはそのファイルですべての将来の関数に透過的に影響を及ぼすことになります。
- strict namespace
<?php
strict namespace Foo {
}
namespace {
bar();
}
?>
これは上記の use strict と同じ問題を抱えます。しかしながら、名前空間が strict であることを暗示しているように見えます。
declare でブロックモードを認めないのか
PHP 5.x で declare() はブロックモードをサポートします。
<?php
declare(ticks=1) {
ticks_code();
}
「strict blocks」のサポートは役に立つかもしれません。
現在の立場
strict な「ブロック」を認めると複数の「型モード」を使う単一のファイルがある状況を作り出してしまう可能性があります。このことによって、可読性が阻害され、型つきのコードに取り組むことがむずかしくなります。
それゆえ、この提案では、最初の行を除いて、ファイルの任意の場所での型モードの変更を明確に無効にします。最初の行のみが型変更を認められるので、ブロックモードは意味をなしません (ファイルにあるのは単独のブロックのみ存在するからです)。
加えて、技術的な制限によってむずかしくなります: 制限について説明したメール
内部関数は戻り値の型宣言をもたない
現在、内部関数は戻り値の型宣言をもたず、null を返すもしくはエラーになることがあります。このことは複数の関数を一緒に連ねる型安全性に制限を課すことになります。
現在の立場
この提案は戻り値の型を内部関数に追加することを必須とはしません。後方互換性を壊すことなく、型システムをより頑強なものにするものを将来の提案で追加する余地があります。
null のサポートを追加しないのか?
ほかのプリミティブに加えて null のサポートを追加することを望む人達がいます。
現在の立場
union 型なしでは、null はパラメーターに意味をなしません。戻り値の型に関して。戻り値の型に関して役に立つ見解は1つになるでしょう。すなわち、void に対して提案によって現在取り扱われているものです。
MIXED を追加しないのか?
ほかのパラメーターに加えて、mixed 型のサポートを追加することを望む人達がいます。
現在の立場
現在、すべての関数を完全に型づけする権限はありません (strict モードでも)。それゆえ、mixed パラメーターと 型を宣言していないパラメーターの機能的な違いはありません。このため、mixed 型はこの提案の範囲外です。
INI 設定の項目をデフォルトモードに追加しないのか?
デフォルトモードを「weak」から「strict」に切り替える機能が問われてきました (ini もしくはコンパイル時のフラグなど)。
現在の立場
この提案の意見は ini 設定のようにスイッチを修正するふるまいはポータビリティを阻害し、よくデザインされた言語は「グローバル設定」にもとづいてふるまいを変えるべきではないというものです。この提案では strict モードの切り替えはファイル単位の設定にとどめておきます。
型エイリアスはサポートされるべきではない
オリジナルの提案では2つの型エイリアス integer と boolean を追加していました。
現在の立場
この提案では、1つの明確な型があるべきという立場をとります。よって、エイリアスはサポートされません。
この提案は複数の投票の選択肢を用意すべきである
この提案は3つもしくは4つの投票の選択肢 (No、Weak Only、Strict Only、Weak + Strict) を用意すべきだと提案する人達がいます。
現在の立場
https://wiki.php.net/rfc/reserve_more_types_in_php_7 これは2つのパートで構成される提案ではありません。この提案は一緒に機能するように設計されています。そうことなので、(weak 限定もしくは strict 限定) のどちらも、ほかの部分なしには成立しません。
全体としてこの提案のみに投票できることが意味をなしますので、この RFC が提供する投票の選択肢は Yes と No です。
整数のオーバーフロー
現在、ある種の整数オペレーションは少数浮動点の値へのオーバフローになります。次のコードを考えてみましょう。
<?php
var_dump(2 ** 61); // int(2305843009213693952)
var_dump(2 ** 64); // float(1.844674407371E+19)
整数を期待する別の関数に演算の結果を渡すとき、これはエラーになります。
この問題に対処する主な方法は2つあります。
- float を認める → 整数拡張 (integer promotion ) もしくは縮小変換 (narrowing)
- 実行時エラー
現在の立場
「narrowing」(浮動小数点を切り捨ててもっとも近い整数にする)を認めるとバグを検出することがむずかしくなります。
オーバーフローが起きた実行時エラーはそれを行うためにもっとも適切です。
この提案の見解では、オーバフローの状況では実行時のエラーを生成すべきというものです。
オーバーフローの安全性が必要であれば、GMP のようなライブラリを使うべきです。
後方互換性のない変更
int、float、string と bool は class/interface/trait の名前 (use と class_alias を含む)として認められません。
スカラー型の宣言に対する weak な型チェックルールは受け取る値に対してとても寛容で、演算に関して PHP の型変換と似たふるまいをし、後方互換性を壊すことなく、既存のユーザーランドライブラリにスカラーの型宣言を追加することは可能です。
strict な型チェックモードはデフォルトでオフで、明示的に使われなければならないので、後方互換性を壊しません。
提案される PHP のバージョン
この提案は PHP 7.0 をターゲットとします。
RFC の影響
既存のエクステンション
パラメーターに対するスカラーの型宣言のリフレーションをサポートするため、ext/reflection をアップデートする必要があります。型宣言の情報を統一的なリフレクション API に統合するために RFC のフォローアップに委ねられます。
影響を受けない PHP の機能
キャスト演算子のふるまいには影響は及びません。
strict な型チェックモードが使われないとき (デフォルトです)、ビルトイン関数とエクステンションの関数の呼び出しは以前の PHP のバージョンとまったく同じふるまいをします。
将来のスコープ
スカラーの型宣言が保証することは渡された引数が関数の本体 (少なくとも初期の段階) の範囲である型になることなので、これは最適化のために Zend Engine で使われます。
たとえば、ある関数が float 型で宣言された2つの引数をとり、それを使って算術を行う場合、算術演算のためにオペランドの型をチェックする必要はありません。筆者が理解するところでは、HHVM はすでにその主の最適化を実行しており、この RFC から利益を得るでしょう。
リファレンス
- スカラーの型宣言に関するメーリングリストでの以前の議論: 1、2、3、4
- Java の型変換ルール
- C# の型変換ルール