maxは引数の値の中で最大の値を返す関数です。これは例えば次のように使えます。
<?php
// 3つの乱数の中で最大のものを返す
var_dump(max(
rand(1, 100),
rand(1, 100),
rand(1, 100),
));
// int(78)
maxが引数に取れる値の型は自由です。次のような比較もエラーになりません。
<?php
var_dump(max(
2,
'a',
[],
new stdClass(),
true,
null,
));
// object(stdClass)#1 (0) {
// }
これは極端なケースですが、次のようなケースは手なりにコードを書いていると比較的起きえます。
<?php
var_dump([
"max(0, '0')" => max(0, '0'),
"max('0', 0)" => max('0', 0),
"max(null, 0)" => max(null, 0),
"max(0, null)" => max(0, null),
"max(null, '0')" => max(null, '0'),
"max('0', null)" => max('0', null),
]);
// array(6) {
// ["max(0, '0')"] => int(0)
// ["max('0', 0)"] => string(1) "0"
// ["max(null, 0)"] => NULL
// ["max(0, null)"] => int(0)
// ["max(null, '0')"] => string(1) "0"
// ["max('0', null)"] => string(1) "0"
//}
同じ値のセットを引数としているのにも関わらず異なる結果が返ってきています。これは異なる型を受け取りながらもそのまま処理する仕様とPHPの暗黙の型変換の組み合わせによって起きています。このような状態が現れるうるコードは不具合につながりやすいです。実際PHPのドキュメントにも次のようにあります。
PHP: max – Manual
警告異なる型の値を引数として渡す際には注意しましょう。
max() が予期せぬ結果を返す可能性があるからです。
このような挙動が起きないようにするためには型を制御した関数を作り、その中でmaxを実行するのが効果的です。例えば次のコードのようにできます。
/**
* 指定された型の引数の最大値を返す
* @param mixed[] ...$args
* @return mixed
*/
function type_safe_max(...$args): mixed
{
if (count($args) === 0) {
throw new InvalidArgumentException('引数が空です。最低1つの引数を指定してください。');
}
$type = gettype($args[0]);
foreach($args as $arg) {
if(gettype($arg) !== $type) {
throw new InvalidArgumentException('引数の型が一致しません。' . $type . '型の引数を指定してください');
}
}
return max($args);
}
// 使用例
var_dump(type_safe_max(0, 1)); // int(1)
var_dump(type_safe_max('0', '1'));// string(1) "1"
var_dump(type_safe_max(null,0));// PHP Fatal error: Uncaught InvalidArgumentException: 引数の型が一致しません
これは引数の中の型が一致している場合にのみ動作するmaxです。他には特定の型へのキャストを明示するなどして作ることもできます。