PHP
基礎

PHPでは関数の引数の個数がチェックされない

More than 1 year has passed since last update.

PHPの再勉強中。

今までさらっと流してきたけれど、PHPの関数やメソッドの引数の扱いがちょっと変わっているのに今更ながらに気がつきました。

<?php

# 引数を二つとる関数を定義
function two_args_func($a, $b)
{
  echo "{$a}:{$b}" . PHP_EOL;
}

# 関数を呼び出す。
two_args_func("hoge", "fuga"); # => hoge:fuga

# 引数を一つだけで呼び出してみる
two_args_func("hoge"); # => hoge: 

# 三つの引数で呼び出してみる
two_args_func("hoge","fuga","piyo"); # => hoge:fuga

引数を2つとる関数を定義して、引数の数を変えて呼び出してみました。常識的にはエラーになる筈ですが、PHPではエラーになりません。引数の数が足りない場合は、"Missing argument"のWarningが発生しますが致命的なエラーにはならず、2つ目の仮引数の値が未定義のまま処理が継続します。また、仮引数が多い分にはWarningすら発生しないようです。

これはおそらく可変長引数をサポートするための仕様なんだと思われますが、キチンと引数の個数チェックが行われる他の言語に慣れてしまっているとちょっと戸惑います。

実際に渡された引数の数は、func_num_args()関数を呼び出すことで取得できるので、
これを利用して関数側で引数の数をチェックすることはできます。

function two_args_func($a, $b)
{
    $args_count = func_num_args();
    if ($args_count == 2)
    {
        echo "{$a}:{$b}" . PHP_EOL;
    }
    else
        throw new Exception("Invalid number of arguments."); 
}

また、渡された引数の値は関数定義の仮引数の有無に関わらず、func_get_arg()関数、func_get_args()関数で取得することが可能です、

<?php

function func($a)
{
    $arg1 = func_get_arg(0);
    $arg2 = func_get_arg(1);
    echo "a:{$a} arg1:{$arg1} arg2:{$arg2}" . PHP_EOL;
}

func("hoge","fuga");  # => a:hoge arg1:hoge arg2:fuga

func_get_arg()関数、func_get_args()関数は引数の値を返します。引数が値渡しである場合はとくに問題はありませんが、参照渡しの場合でも参照ではなく値が返されるので注意が必要です。

<?php
function func1(&$n)
{
   $n *= 2;
}

function func2(&$n)
{
  $arg = func_get_arg(0);
  $arg *= 2;
}

$a = 1;
func1($a);
echo $a . PHP_EOL;  # => 2
func2($a);
echo $a . PHP_EOL;  # => 2

例えば上の例だと、func1()func2()ともに引数は参照渡しで渡されますが、仮引数を利用するfunc1()では呼び出し元の変数$aの値を変更してしまうのに対して、func_get_arg()を利用するfunc2()では呼び出し元の変数$aの値は変化しません。

また、func_num_args()関数やfunc_get_arg()関数などはデフォルト引数については感知しないという特性があります。

<?php

function func_with_default_arg($a = 100)
{
    $args_count = func_num_args();
    echo "Number of args:{$args_count} / Value:{$a}" . PHP_EOL;
}

func_with_default_arg();     # => Number of args:0 / Value:100
func_with_default_arg(200);  # => Number of args:1 / Value:200

デフォルト付きの引数が省略された場合、仮引数にはデフォルト値が設定されますが、func_get_arg()関数ではデフォルトの値を取得することはできません。

PHPでは、func_num_args()関数やfunc_get_arg()関数を利用して可変長引数をサポートしているので、可変長引数のための特別な構文は存在しませんが、その代わりに関数呼び出し時に引数の個数がチェックされないのだとしたら、何だか納得しずらいです。

関数の引数の個数をチェックしない(或いは可変長引数のための特別な構文を導入しない)ことに積極的な理由があるんでしょうか?


追記 22:30

参照渡しの記述が完全に勘違いでしたので書き直しました。
また、PHP5.6では可変長引数用の構文が用意されるとのことです。