前回 PHP の型宣言が暗黙の型変換をしてしまってそんなものかーと思っていたのですが、厳密にするオプションが有ることを教えてもらったので記録に残しておきます。
環境
- PHP
v.8.1.2
(onlinephp.io で実行)
declare(strict_types=1);
宣言を行うと厳密な方チェックができる
declare(strict_types=1);
宣言なし (default)
<?php function myFunc(int $x = null): void { $foo = $x ?? 'default'; var_export($foo); } myFunc(10); // => 10 myFunc('1'); // => 1 myFunc(null); // => 'default' myFunc(true); // => 1 myFunc(false); // => 0 myFunc('-2.5'); // => -2 myFunc(''); // Fatal error: Uncaught TypeError: myFunc(): Argument #1 ($x) must be of type ?int, string given myFunc([]); // Fatal error: Uncaught TypeError: myFunc(): Argument #1 ($x) must be of type ?int, array given class Bar { public $x = 1; }; $bar = new Bar(); myFunc($bar); // Fatal error: Uncaught TypeError: myFunc(): Argument #1 ($x) must be of type ?int, Bar given
文字列の数字や float 型、Bool値など暗黙の型変換ができる値がすり抜けてしまう
declare(strict_types=1);
宣言あり
<?php declare(strict_types=1); function myFunc(int $x = null): void { $foo = $x ?? 'default'; var_export($foo); } myFunc(10); // => 10 myFunc('1'); // Fatal error: Uncaught TypeError: myFunc(): Argument #1 ($x) must be of type ?int, string given myFunc(null); // => 'default' myFunc(true); // Fatal error: Uncaught TypeError: myFunc(): Argument #1 ($x) must be of type ?int, bool given myFunc(false); // Fatal error: Uncaught TypeError: myFunc(): Argument #1 ($x) must be of type ?int, bool given myFunc('-2.5'); // Fatal error: Uncaught TypeError: myFunc(): Argument #1 ($x) must be of type ?int, float given myFunc(''); // Fatal error: Uncaught TypeError: myFunc(): Argument #1 ($x) must be of type ?int, string given myFunc([]); // Fatal error: Uncaught TypeError: myFunc(): Argument #1 ($x) must be of type ?int, array given class Bar { public $x = 1; }; $bar = new Bar(); myFunc($bar); // Fatal error: Uncaught TypeError: myFunc(): Argument #1 ($x) must be of type ?int, Bar given
null
は通りますが、int 型以外の引数はエラーになりました!
型宣言を使う場合は基本的に declare(strict_types=1);
宣言を行うのが良さそうです
declare(strict_types=1);
の影響範囲について
1. declare(strict_types=1);
宣言はファイルの先頭でなければならない
<?php function myFunc(int $x = null): void { $foo = $x ?? 'default'; var_export($foo); echo "\n"; } declare(strict_types=1); // Fatal error: strict_types declaration must be the very first statement in the script
2. declare(strict_types=1);
は宣言されたファイル内でのみ有効
- strict モードは宣言されたファイル内でのみ有効になる
- strict モード宣言のあるファイルでも、呼び出し側のファイルが strict モードで無ければ厳密な型チェックは行われない
=> 呼び出し元の指定が優先される
呼び出し元が strict モードでない場合、読み込んだファイルが strict モードでも厳密な型チェックは行われない
function.php
<?php declare(strict_types=1); function add(int $x, int $y): int { return $x + $y; }
index.php
<?php include 'functions.php'; echo add(1.5, 2.5); // => 3
呼び出し元が strict モードなら、読み込んだファイルが strict モードでなくとも厳密な型チェックが行われる
function.php
<?php function add(int $x, int $y): int { return $x + $y; }
index.php
<?php declare(strict_types=1); include 'functions.php'; echo add(1.5, 2.5); // Fatal error: Uncaught TypeError: add(): Argument #1 ($x) must be of type int, float given
PHP は include / require をしているファイルにコードが挿入されて strict
モードの指定は大本のファイルのでの指定が残ると捉えると理解しやすそう
Note that the
include
construct loads the code from another file into a file. And you’ll learn more about it in the later tutorial.
When you call a function defined in a file with strict typing (functions.php
) from a file without strict typing (index.php
), PHP will respect the preference of the caller (index.php
). That means it’s up to the caller to decide whether to use the strict mode or not. In this case, theindex.php
won’t execute in the strict mode.
When you include code from another file, PHP uses the mode of the caller.
cf. PHP strict_types
Note. strict モードのファイルが中間にある場合
strict モードの指定は呼び出し元の指定が優先されるとあり、Qiitaの記事 に多重でファイルを読み込んだ場合、関数の実行ファイルに declare(strict_types=1);
があれば呼び出し元に strict モード宣言がなくともエラーになったとあったので複数ファイルが作成できる paiza.io の PHP v8.1.9
で試してみました
b.php
<?php function add(int $x, int $y): int { return $x + $y; }
a.php
(strict モード)
<?php declare(strict_types=1); include_once('b.php'); function add_square(int $x, int $y): int { $sum = add($x, $y); return pow($sum, 2); }
Main.php
<?php include_once('a.php'); echo phpversion(); // => 8.1.9 echo add_square(2, 3); // => 25 echo add_square(2.0, 3.0); // => 25 echo add_square(true, false); // => 1
👇実行結果サンプル
Qiita の記事の例だと strict 宣言のある a.php
からの呼び出しで add_square(2.0, 3.0)
がエラーになると思ったのですが、そのまま実行されました。PHP のバージョンで挙動が異なるのでしょうか?
今回は詳細には調べませんが、phptutorial.net に書かれているように最終的な呼び出し元 今回は Main.php
の指定が優先される。という認識で良いのかな〜と思いました。
まとめ
PHP でプロジェクトを作成するときはファイルの先頭に declare(strict_types=1);
宣言を書いて厳密な型チェックを行う方が幸せになれそう!
厳密な型チェックの存在を教えてくれた いち(@ichiart4) さんありがとう〜♡♡♡ (アイカツ!のフレンズ優秀な方が多すぎてありがたい〜)
おわり ₍ ᐢ. ̫ .ᐢ ₎
[参考]
- PHP strict_types
- The way declare(strict_types=1) works in PHP - DEV Community 👩💻👨💻
- php 7.2 - What do strict types do in PHP? - Stack Overflow
- declare(strict_types=1)の効力範囲について - Qiita