親クラスの動的メソッド（参照渡し）を呼び出したい！

**※ご注意：この記事はチャピ（ChatGPTのこと）に教えてもらって大変勉強になった・助かった内容のメモ記事です。※**

Qiita記事もAIに書いてもらう時代なんや！！！！！！

# 親クラスのメソッドを参照渡しで動的に呼び出す方法

親クラスのメソッドが動的に決まる場合は、`call_user_func_array()` や `ReflectionMethod` を使うのが適切です。しかし、`call_user_func_array()` は参照渡しに対応していないため、`ReflectionMethod` を使う方法が最も確実です。

---

## **解決策：ReflectionMethod を使った動的メソッド呼び出し**
以下のように `ReflectionMethod` を使えば、参照渡しの引数 (`&$outputData`, `&$errors`, `&$warnings`) も問題なく処理できます。

### **修正後のコード**
```php
public function process($inputData, &$outputData, &$errors, &$warnings) {
    // 動的に親クラスのメソッドを取得
    $method = new ReflectionMethod(get_parent_class($this), 'process');
    $method->setAccessible(true);

    // 引数を準備（参照渡しが必要なものは & を付ける）
    $args = [
        $inputData,
        &$outputData,   // 参照渡し
        &$errors,       // 参照渡し
        &$warnings,     // 参照渡し
    ];

    // 親クラスのメソッドを動的に呼び出す
    $result = $method->invokeArgs($this, $args);

    return $result;
}
```

---

## **なぜ `call_user_func_array()` を使わないのか？**
`call_user_func_array()` は **参照渡しの引数を適切に処理できない** ため、以下のように書いても `$outputData` などが正しく渡らず、エラーになる可能性があります。

```php
call_user_func_array(['parent', 'process'], [$inputData, &$outputData, &$errors, &$warnings]); 
// ↑ これでは参照渡しが無効になってしまう
```

`ReflectionMethod` を使うことで **参照渡しの問題を回避しつつ、動的な親クラスのメソッドを呼び出せる** ため、今回のケースではベストな解決策です。

---

## **この方法で解決すること**
✅ 親クラスの `process()` メソッドを **動的に呼び出せる**  
✅ `&$outputData`, `&$errors`, `&$warnings` など **参照渡しの引数も正しく処理できる**  
✅ `ReflectionMethod` を使うことで **可読性を損なわずにシンプルに実装できる**  

### **どうして `ReflectionMethod` を使えば解決するのか？**  

まず、なぜエラーが出たのかを理解し、それを `ReflectionMethod` がどのように解決しているのかを順番に説明します。  

---

## **❌ 最初のエラーの原因**
あなたの最初のコードでは `call_user_func()` を使って親クラスの `process()` メソッドを呼び出していましたが、次のようなエラーが出ていました。

```
PHP Warning:  Parameter 2 to ParentClass::process() expected to be a reference, value given
```

### **エラーの意味**
このエラーメッセージは、「関数（メソッド）の2番目の引数は **参照（&付き）で渡さなければならない** のに、普通の値が渡されてしまった」ということを示しています。

例えば、次のようなコードを考えてみてください。

```php
function testFunc(&$value) {
    $value = "変更されました";
}

$var = "元の値";
testFunc($var);
echo $var;  // "変更されました" になる
```

ここでは `$value` を `&`（アンパサンド）付きで受け取っているので、`testFunc()` の中で変更した値が `$var` に反映されます。

ところが、`call_user_func()` や `call_user_func_array()` は、こうした「参照渡し」を正しく処理できないという **PHP の仕様** があります。そのため、**親クラスの `process()` メソッドが参照を期待しているのに、ただの値として渡ってしまい、エラーになった** のです。

---

## **✅ `ReflectionMethod` を使うとどうして解決するのか？**

`ReflectionMethod` は PHP の「リフレクション（Reflection）」という機能を使って **関数やクラスの情報を取得し、実行する方法** です。

これを使うことで、次のことができます：
1. **親クラスの `process()` メソッドを取得する**  
2. **「参照渡し」に対応したまま、メソッドを実行できる**

---

## **📌 `ReflectionMethod` を使ったコードの解説**
修正後のコードをもう一度見てみましょう。

```php
public function process($inputData, &$outputData, &$errors, &$warnings) {
    // ① 親クラスの 'process' メソッドの情報を取得
    $method = new ReflectionMethod(get_parent_class($this), 'process');
    $method->setAccessible(true);

    // ② 引数を参照付きで用意する
    $args = [
        $inputData,
        &$outputData,   // 参照渡し
        &$errors,       // 参照渡し
        &$warnings,     // 参照渡し
    ];

    // ③ 親クラスのメソッドを実行
    $result = $method->invokeArgs($this, $args);

    return $result;
}
```

### **① `ReflectionMethod` で親クラスのメソッドを取得**
```php
$method = new ReflectionMethod(get_parent_class($this), 'process');
```
- `get_parent_class($this)` は **このクラスの親クラスの名前を取得する** 関数です。
- `new ReflectionMethod(親クラス名, 'process')` で、親クラスの `process()` メソッドの情報を取得します。

---

### **② 引数を「参照付き」で用意する**
```php
$args = [
    $inputData,
    &$outputData,   // 参照渡し
    &$errors,       // 参照渡し
    &$warnings,     // 参照渡し
];
```
- ここで、**`&$outputData` などをちゃんと参照として渡す** ことで、親クラスの `process()` メソッドの中で変更した内容が正しく反映されるようになります。

---

### **③ `invokeArgs()` で親の `process()` を実行**
```php
$result = $method->invokeArgs($this, $args);
```
- `invokeArgs($this, $args)` を使うことで、取得した `process()` メソッドを **参照渡しのまま** 実行できます。
- `$this` は **このオブジェクトを使ってメソッドを呼び出す** という意味です。
- `$args` には、用意した引数（`&$outputData` など）が含まれています。

---

## **❓ なぜ `call_user_func_array()` ではダメなの？**
`call_user_func_array()` でも関数を動的に呼び出すことはできますが、**「参照渡し」の情報が失われてしまう** という問題があります。

例えば：
```php
call_user_func_array(['parent', 'process'], [$inputData, &$outputData, &$errors, &$warnings]); 
```
このコードでは、`&$outputData` などが **「値」として渡されてしまう** ため、`process()` の中で変更しても反映されません。

一方、`ReflectionMethod` を使えば **参照渡しのまま親のメソッドを呼び出せる** ので、期待通りの動作になります。

---

## **🔎 まとめ**
| 方法 | 参照渡しできる？ | 親の動的メソッドを呼べる？ | 備考 |
|------|---------------|-----------------|------|
| `call_user_func()` | ❌ できない | ✅ できる | 参照渡しができずエラーになる |
| `call_user_func_array()` | ❌ できない | ✅ できる | 参照渡しの情報が失われる |
| `ReflectionMethod::invokeArgs()` | ✅ できる | ✅ できる | 正しく動作する！ |

---

## **🎯 最後に**
- `ReflectionMethod` を使うことで、**「参照渡し」のまま動的に親クラスのメソッドを呼び出す** ことができる。
- `call_user_func()` や `call_user_func_array()` では **参照渡しがうまく動かない** ので、この場合は使えない。
- `ReflectionMethod::invokeArgs()` を使えば、**親のメソッドを柔軟に呼び出せる** し、**参照渡しも問題なく処理できる**。

このように、「PHP の仕様（`call_user_func()` は参照を渡せない）」を理解し、それを回避するために `ReflectionMethod` を使うことで、問題を解決できるのです！ 😃