## 関数のパラメーターと引数

関数のパラメーターは、関数のシグネチャの中で宣言します。 引数のリストにより関数へ情報を渡すことができます。 このリストは、カンマで区切られた式のリストです。 引数の評価は、関数が実際にコールされる前に、 左から右の順番で行われ、 評価された結果が関数のパラメーターに代入されます (先行評価)。

PHP は、値渡し(デフォルト)、 リファレンス渡し、 デフォルト引数値 をサポートしています。また、 可変長引数リスト や、 名前付き引数 もサポートしています。

> 注意:

> PHP 7.3.0 以降では、 関数コールの引数リストの末尾にも、カンマを付加できるようになっています:

>     <?php
>     $v = foo(
>         $arg_1,
>         $arg_2,
>     );

PHP 8.0.0 以降では、関数のパラメーターリストの最後にカンマをつけることができます。 このカンマは無視されます。 これは、パラメータリストや変数名が長かったりした場合に、 パラメータを縦に並べるのに便利です。

例1 関数のパラメーターリストの最後にカンマを付ける

    <?php
    function takes_many_args(
        $first_arg,
        $second_arg,
        $a_very_long_argument_name,
        $arg_with_default = 5,
        $again = 'a default string', // この最後のカンマは、8.0.0 より前では許されません。
    )
    {
        // ...
    }

### 引数のリファレンス渡し

デフォルトで、関数の引数は値で渡されます。(このため、関数の内部で 引数の値を変更しても関数の外側では値は変化しません。)関数がその引 数を修正できるようにするには、その引数をリファレンス渡しとする必要があり ます。

関数の引数を常にリファレンス渡しとしたい場合には、関数定義において アンパサンド(&) をパラメーター名の前に付加することができます。

例2 関数の引数のリファレンス渡し

    <?php
    function add_some_extra(&$string)
    {
        $string .= 'and something extra.';
    }
    $str = 'This is a string, ';
    add_some_extra($str);
    echo $str;    // 出力は 'This is a string, and something extra.' となります

リファレンス渡しが想定されているところに、定数式を引数として渡すとエラーになります。

### デフォルトのパラメーター値

関数は、変数に代入する記法に似たやり方で、 デフォルト値をパラメーターに定義することができます。 デフォルト値はパラメーターに引数が指定されなかった場合に使われますが、 `null` を渡した場合はデフォルト値を代入 しない ことに特に注意して下さい。

例3 関数におけるデフォルトパラメータの使用法

    <?php
    function makecoffee($type = "cappuccino")
    {
        return "Making a cup of $type.\n";
    }
    echo makecoffee();
    echo makecoffee(null);
    echo makecoffee("espresso");

上の例の出力は以下となります。

    Making a cup of cappuccino.
    Making a cup of .
    Making a cup of espresso.

デフォルトパラメータの値には、スカラー値、 配列、および特殊な型 `null` を指定できます。 PHP 8.1.0 以降では、 new ClassName() 記法を使ってオブジェクトも指定できます。

例4 スカラー型以外をデフォルト値として使用する

    <?php
    function makecoffee($types = array("cappuccino"), $coffeeMaker = NULL)
    {
        $device = is_null($coffeeMaker) ? "hands" : $coffeeMaker;
        return "Making a cup of ".join(", ", $types)." with $device.\n";
    }
    echo makecoffee();
    echo makecoffee(array("cappuccino", "lavazza"), "teapot");

例5 オブジェクトをデフォルト値として使用する(PHP 8.1.0 以降)

    <?php
    class DefaultCoffeeMaker {
        public function brew() {
            return 'Making coffee.';
        }
    }
    class FancyCoffeeMaker {
        public function brew() {
            return 'Crafting a beautiful coffee just for you.';
        }
    }
    function makecoffee($coffeeMaker = new DefaultCoffeeMaker)
    {
        return $coffeeMaker->brew();
    }
    echo makecoffee();
    echo makecoffee(new FancyCoffeeMaker);

デフォルト値は、定数式である必要があり、 (例えば) 変数やクラスのメンバーであってはなりません。

デフォルト値を有するパラメータは、 デフォルト値がないパラメータの右側に全てある必要があることに注意して下さい。 そうでない場合、デフォルト値を指定していても、 呼び出し時に省略できません。 次の簡単なコードを見てみましょう。

例6 関数のパラメーターのデフォルト値の 間違った使用法

    <?php
    function makeyogurt($container = "bowl", $flavour)
    {
        return "Making a $container of $flavour yogurt.\n";
    }

    echo makeyogurt("raspberry"); // $container に "raspberry" を指定します。$flavour ではありません。

上の例の出力は以下となります。

    Deprecated: makeyogurt(): Optional parameter $container declared before required parameter $flavour is implicitly treated as a required parameter in script on line 2

    Fatal error: Uncaught ArgumentCountError: Too few arguments to function makeyogurt(), 1 passed in script on line 7 and exactly 2 expected in script:2
    Stack trace:
    #0 script(7): makeyogurt('raspberry')
    #1 {main}
      thrown in script on line 2

ここで、上の例を次のコードと比べてみましょう。

例7 関数のパラメーターのデフォルト値の 正しい使用法

    <?php
    function makeyogurt($flavour, $container = "bowl")
    {
        return "Making a $container of $flavour yogurt.\n";
    }

    echo makeyogurt("raspberry"); // $flavour に "raspberry" を指定します。

上の例の出力は以下となります。

    Making a bowl of raspberry yogurt.

PHP 8.0.0 以降では、 デフォルト値を指定したパラメータを複数スキップするために、 名前付き引数 が使えます。

例8 関数のパラメータのデフォルト値の 正しい使用法

    <?php
    function makeyogurt($container = "bowl", $flavour = "raspberry", $style = "Greek")
    {
        return "Making a $container of $flavour $style yogurt.\n";
    }

    echo makeyogurt(style: "natural");

上の例の出力は以下となります。

    Making a bowl of raspberry natural yogurt.

PHP 8.0.0 以降では、 デフォルト値を指定したパラメーターの後に、 必須のパラメーターを宣言することは 推奨されません。 なぜなら、そのデフォルト値は絶対に使われないからです。 これは、デフォルト値を削除することで解決できます。 このルールの唯一の例外は、 `Type $param = null` と書かれたパラメーターです。 `null` をデフォルトにすることは、 型が暗黙のうちに nullable であることを示しています。 この使い方はPHP 8.4.0で非推奨となり、代わりに明示的な nullable 型 を使用する必要があります。

例9 デフォルト値を指定したパラメーターは、必須のパラメーターの後に宣言する

    <?php
    function foo($a = [], $b) {} // デフォルト値が使われないため、PHP 8.0.0 以降は推奨されません
    function foo($a, $b) {}      // 上のコードと機能的には同じですが、推奨されない警告は発生しません。

    // PHP 8.1.0以降、$a は暗黙的に必須（必須のパラメータの前にあるため）ですが、
    // デフォルトのパラメータ値が null であるため、暗黙的に nullable とみなされます（PHP 8.4.0で非推奨）。
    function bar(A $a = null, $b) {}
    function bar(?A $a, $b) {}       // 推奨される書き方です。

> 注意: PHP 7.1.0 以降では、 デフォルト値を指定しないでパラメータを省略すると、 ArgumentCountError がスローされるようになりました。 これより前のバージョンでは、警告が発生していました。

> 注意: リファレンス渡しのパラメーターにもデフォルト値を指定できます。

### 可変長引数リスト

PHP は `...` を使った可変長引数をユーザー定義関数でサポートしています。

パラメーターリストに `...` トークンを含めることで、 その関数が可変長の引数を受け取ることを示せます。 引数は、指定した変数に配列として渡されます:

例10 `...` を使った、可変長引数へのアクセス

    <?php
    function sum(...$numbers) {
        $acc = 0;
        foreach ($numbers as $n) {
            $acc += $n;
        }
        return $acc;
    }

    echo sum(1, 2, 3, 4);

上の例の出力は以下となります。

    10

関数を呼び出すときに `...` を使うと、 配列や Traversable を実装した変数やリテラルを引数リストに展開することができます。

例11 引数での `...` の使用例

    <?php
    function add($a, $b) {
        return $a + $b;
    }

    echo add(...[1, 2])."\n";

    $a = [1, 2];
    echo add(...$a);

上の例の出力は以下となります。

    3
    3

通常のパラメーターを、`...` の前に指定することもできます。 この場合は、通常の引数リストにマッチしなかったのこりの引数が `...` による配列に追加されます。

`...` トークンの前に、 型宣言 を付加することもできます。 型宣言がある場合、`...` が取り込むすべての引数はその型に一致していなければいけません。

例12 型宣言つきの可変長引数

    <?php
    function total_intervals($unit, DateInterval ...$intervals) {
        $time = 0;
        foreach ($intervals as $interval) {
            $time += $interval->$unit;
        }
        return $time;
    }

    $a = new DateInterval('P1D');
    $b = new DateInterval('P2D');
    echo total_intervals('d', $a, $b).' days';

    // これは失敗します。null は DateInterval オブジェクトではないからです。
    echo total_intervals('d', null);

上の例の出力は以下となります。

    3 days
    Fatal error: Uncaught TypeError: total_intervals(): Argument #2 must be of type DateInterval, null given, called in script on line 15 and defined in script:2
    Stack trace:
    #0 script(15): total_intervals('d', NULL)
    #1 {main}
      thrown in script on line 2

可変長引数の リファレンス渡し もできます。その場合は、`...` の前にアンパサンド (`&`) を付加します。

### 名前付き引数

既にある、位置を指定した引数を拡張する形で、PHP 8.0.0 から名前付き引数が導入されました。 名前付き引数は、位置ではなく、名前ベースで引数を渡すことを可能にします。 これによって、引数の意味が自己文書化(self-documenting)され、 引数を任意の順番で渡せるようになり、任意のデフォルト値を持つ引数をスキップできるようになります。

名前付き引数は、パラメータの名前の後にコロンを付けたものを、値の前に付けることで指定します。 パラメータの名前に予約語を使うことも許されています。 パラメータの名前は識別子でなければならず、動的に指定することは出来ません。

例13 名前付き引数の文法

    <?php
    myFunction(paramName: $value);
    array_foobar(array: $value);

    // 以下の書き方はサポートされていません
    function_name($variableStoringParamName: $value);

例14 位置を指定した引数と、名前付き引数

    <?php
    // 位置を指定した引数の場合:
    array_fill(0, 100, 50);

    // 名前付き引数の場合:
    array_fill(start_index: 0, count: 100, value: 50);

名前付き引数は、渡す順番は関係ありません。

例15 上と同じ例を、引数の順番を変えて渡す

    <?php
    array_fill(value: 50, count: 100, start_index: 0);

名前付き引数は、位置を指定した引数と組み合わせることが出来ます。 そうした場合、名前付き引数は、位置を指定した引数の後に置かなければいけません。 オプションの引数だけをいくつか、指定することもできます。 その場合も、名前付き引数の順番は関係ありません。

例16 位置を指定した引数と、名前付き引数を組み合わせる

    <?php
    htmlspecialchars($string, double_encode: false);
    // 上記は、以下と同等です。
    htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, 'UTF-8', false);

同じ名前の引数を複数回渡すと、Error 例外が発生します。

例17 同じ名前の引数を複数回渡すと、Error がスローされる

    <?php
    function foo($param) { ... }

    foo(param: 1, param: 2);
    // Error: Named parameter $param overwrites previous argument

    foo(1, param: 2);
    // Error: Named parameter $param overwrites previous argument

PHP 8.1.0 以降では、 引数を ... で展開した後に 名前付き引数を指定することができます。 名前付き引数は、展開済みの引数を上書きしては いけません。

例18 引数を展開した後に、名前付き引数を使う

    <?php
    function foo($a, $b, $c = 3, $d = 4) {
      return $a + $b + $c + $d;
    }

    var_dump(foo(...[1, 2], d: 40)); // 46
    var_dump(foo(...['b' => 2, 'a' => 1], d: 40)); // 46

    var_dump(foo(...[1, 2], b: 20)); // Fatal error. Named parameter $b overwrites previous argument

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open