PHP8では、内部関数のパラメータの型が厳格になりました。(戻り値も。)
厳格とは、『今までは適当でも動かしてたけど、これからはエラーを返すよ?』ということ。
PHP8はデータの厳格化がポイントなのでその一環。
曖昧さを許さないプログラミングをしていた人にとっては当たり前の話です。
2020年11月26日、5年ぶりにPHPのメジャーバージョンが上がりました。PHP8です。
メジャーアップデートだけに変更点も大きいです。内部関数のパラメータの型を厳格にしたのもそう。
こういうところを見ていきます。
内部関数とは?
内部関数とは、PHPに用意されている標準関数のこと。
PHP8から関数のパラメータ(引数)で型宣言しているものは厳格になり、型が合わないと致命的なエラー(Uncaught TypeError)になります。
PHP8から、内部関数のパラメータの型の曖昧さを無くしていこうという動きがあって、PHP7では曖昧さを許容していたものがPHP8では厳格に変更されています。
PHP7までは動いたり警告で済んでいたものがエラーになるということ。
これはもう、エラーにならないよう直すしかありません。
PHP7までも、独自のコーディング規約によって曖昧な型宣言を許さないようにしている人もいるでしょう。
そういう人にとっては、あまり影響はないはずです。ただ、内部関数の型と一致しているという前提ですが。
関数はパラメータだけでなく、戻り値の型宣言もあります。
その戻り値も厳格になります。
厳格な型宣言とは?
PHPの変数は型宣言をせずに使えます。この自由さがPHPの強みでもあり弱点でもありました。
C系言語やJavaを使ってい人からすると危険極まりない仕様に見える有様。
(データの型に自由度があるとバグになりやすい。)
そこでPHPにも型宣言が登場します。
関数の型宣言
ここは説明よりサンプルコードを見てもらいます。
<?php
/**
* function: パラメータ型宣言無し, 戻り値型宣言無し
*/
function sum1($a, $b) {
return $a + $b;
}
/**
* function: パラメータ型宣言あり, 戻り値型宣言あり
*/
function sum2(int $a, int $b): int {
return $a + $b;
}
echo "plus1:" . sum1(1,2) . PHP_EOL;
echo "plus2:" . sum2(1,2) . PHP_EOL;
echo "plus1 str:" . sum1("1","2") . PHP_EOL;
echo "plus2 str:" . sum2("1","2") . PHP_EOL;
plus1:3
plus2:3
plus1 str:3
plus2 str:3
説明はいらないですね? sum1()とsum2()は同じ機能の関数ですが、sum2()は、パラメータと戻り値の型をintにしています。
このままでは結果は同じ。
型の厳格化
型宣言しただけではあまり意味がありません。これをルール化して強制させます。
<?php
declare(strict_types=1); // <--- 追加
/**
* function: パラメータ型宣言無し, 戻り値型宣言無し
*/
function sum1($a, $b) {
return $a + $b;
}
/**
* function: パラメータ型宣言あり, 戻り値型宣言あり
*/
function sum2(int $a, int $b): int {
return $a + $b;
}
echo "plus1:" . sum1(1,2) . PHP_EOL;
echo "plus2:" . sum2(1,2) . PHP_EOL;
echo "plus1 str:" . sum1("1","2") . PHP_EOL;
echo "plus2 str:" . sum2("1","2") . PHP_EOL;
PHP Fatal error: Uncaught TypeError: Argument 1 passed to sum2() must be of the type int, string given, called in /workspace/Main.php on line 21 and defined in /workspace/Main.php:14
Stack trace:
#0 /workspace/Main.php(21): sum2()
#1 {main}
thrown in /workspace/Main.php on line 14
declareをつけると、型が合わないときエラーになります。ここではサンプルを出しませんが、クラスのプロパティにも型宣言ができます。
PHP8では型宣言とdeclareを内部関数につける作業を行いました。この機能自体はPHP4から存在します。
元々あった機能を使って厳格化の作業をしたんですね?
sum2()では、returnにintじゃない値を入れると同じエラーになります。
戻り値も厳格になっている証拠。
PHP7までの厳格な型の問題点
PHP7までも厳格な型が使えましたが問題がありました。declareをつけたPHPファイルの中だけでしか厳格にならないので、型宣言したからといって厳格になっているかは分かりません。
エラーになって厳格だったと分かります。
(ログをこまめにチェックしている人は警告で分かる。ログレベルを低くすると分からない。)
また型が一致しないとき、戻り値でNULLやfalseを返したりして、エラーなのか無効値なのか分かりづらいこともありました。統一もされていません。
PHP8では型宣言したものは厳格にしてエラーになるように統一しています。
『型がちがったらエラーを返すよ?』にすることで分かりやすくしました。
すべてを厳格にしているかというとそうとは言い切れない。とくに戻り値は。
かといって、曖昧さを期待してはいけない。
PHP8にして動く人、動かない人
PHP7から8にアップグレードして問題の出る人、出ない人、はっきりと分かれると思います。
元々、データの型を意識してプログラミングしていた人はほぼ無傷でしょう。決められた内部関数の型が思ってたのとちがうところだけ修正すればいいはずです。
エラーを直す作業はそこまで多くないはず。
型の曖昧さを存分に使いまくっていた人は要注意。先の見えないエラー修正を強いられるかもしれません。
あとは使ってるフレームワークなどの作り方にもよります。これらを使ってる場合は、PHP8に対応したバージョンのリリースを待つのが無難。
そのあとにカスタマイズした部分だけを修正しましょう。
ひとつ気になるのは、データベースを使っている場合、DBとの型が一致しなくなるとちょっとキツイかな?
そのへんを調整する作業が出てきます。
カスタム関数のパラメータ
自分で作るカスタム関数でも型の厳格化が行われます。
内部関数とは? で、PHPで用意された標準関数と言いましたが、内部関数にはカスタム関数も入ってる気がする。
<?php
$a = '-10';
$b = ' -10';
$c = '-10 ';
$d = '-1 0';
function sample( int $val ) {
var_dump( $val );
}
var_dump( '[' . $a . '] param auto cast: ' );
sample( $a );
var_dump( '[' . $b . '] param auto cast: ' );
sample( $b );
var_dump( '[' . $c . '] param auto cast: ' );
sample( $c );
var_dump( '[' . $d . '] param auto cast: ' );
sample( $d );
string(23) "[-10] param auto cast: "
int(-10)
string(24) "[ -10] param auto cast: "
int(-10)
string(24) "[-10 ] param auto cast: "
PHP Notice: A non well formed numeric value encountered in /home/vagrant/sample.php on line 8
int(-10)
string(24) "[-1 0] param auto cast: "
PHP Notice: A non well formed numeric value encountered in /home/vagrant/sample.php on line 8
int(-1)
string(23) "[-10] param auto cast: "
int(-10)
string(24) "[ -10] param auto cast: "
int(-10)
string(24) "[-10 ] param auto cast: "
int(-10)
string(24) "[-1 0] param auto cast: "
PHP Fatal error: Uncaught TypeError: sample(): Argument #1 ($val) must be of type int, string given, called in /home/vagrant/sample.php on line 22 and defined in /home/vagrant/sample.php:8
Stack trace:
#0 /home/vagrant/sample.php(22): sample()
#1 {main}
thrown in /home/vagrant/sample.php on line 8
『型宣言をしたときの自動キャストはどうなるんだ?』と思ったので確認したところ、別の発見が。
カスタム関数で、数値文字列が不正のものを渡したときだけエラーになりました。
これもPHP8で変わった厳格な型宣言です。
ちなみに、実験の目的、型宣言付きパラメータの自動キャストは行われる模様。
ただし、declare(strict_types=1); を付けると、自動キャストの前にパラメータの厳密なチェックが行われ、エラーを返します。
(自動キャストできない。)