# 関数の作成

PHP では関数とメソッドはだいたい同じ形式で、メソッドは単にスコープが限定された関数でしかありません。 つまり、そのメソッドが属するクラスエントリのスコープになるということです。 クラスエントリについては、このマニュアルの別のページで説明します。 このセクションの目的は、関数やメソッドの内部構造を紹介することです。 関数を定義したり、変数を受け取ったり、PHP プログラマーに値を返したりする方法を説明します。

関数の内部構造は、それほど単純ではありません。

    PHP_FUNCTION(hackers_function) {
      /* 受け取る引数をここで指定します */
      long number;

      /* 引数を受け取ります */
      if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &number) != SUCCESS) {
          return;
      }

      /* 何かの作業をします */
      number *= 2;

      /* 戻り値を設定します */
      RETURN_LONG(number);
    }

プリプロセッサの `PHP_FUNCTION(hackers_function)` 命令は、 次のような宣言に展開されます。

    void zif_hackers_function(INTERNAL_FUNCTION_PARAMETERS)

`INTERNAL_FUNCTION_PARAMETERS` はマクロとして定義されており、 次の表のようになります。

INTERNAL_FUNCTION_PARAMETERS
型と名前 | 説明 | アクセス用マクロ
--- | --- | ---
`int ht` | ユーザーから実際に渡されたパラメータの数 | `ZEND_NUM_ARGS()`
`zval *return_value` | PHP 変数へのポインタ。ここに、ユーザーへの戻り値を設定します。 デフォルトの型は `IS_NULL` です。 | `RETVAL_*`, `RETURN_*`
`zval **return_value_ptr` | PHP に参照を返すときには、ここに変数へのポインタを設定します。 参照を返すことはお勧めしません。 |
`zval *this_ptr` | メソッド呼び出しの場合は、`$this` オブジェクトを保持する PHP 変数を指します。 | `getThis()`
`int return_value_used` | 返り値を呼び出し元が使うかどうかを示すフラグ。 |

明確にするために、完全に展開した `PHP_FUNCTION(hackers_function)` のドキュメントを示します。

    void zif_hackers_function(int ht, zval* return_value, zval** return_value_ptr, zval* this_ptr, int return_value_used)

`this_ptr` の存在を奇妙に感じるかもしれません。クラスの詳細は後ほど説明するとして、ここでは `PHP_METHOD(MyClass, hackersFunction)` の結果が次のような宣言になることを示しておけば十分でしょう。

    void zim_MyClass_hackersFunction(INTERNAL_FUNCTION_PARAMETERS)

`hackers_function` は特に凝ったことをするわけではなく、`zend_parse_parameters` API を使って受け取った数値を二倍して、それをエンジンに返すだけです。 普通は、単に入力を二倍するだけなどではなく、もっと複雑な何かを行うことでしょう。 ここでは、説明のために、できるだけシンプルにとどめました。 関数に入った際に `return_value` を確保し、`null` に初期化されます。 これで、PHP の関数のデフォルトの返り値が `null` となります。

`Hacker` が正しい引数として指定する内容を `zend_parse_parameters` が受け取れず、受け取った引数を `type_spec` を満たす形式に変換できなかった場合は、エラーを発生させます。 そして、`Hacker` はその場で `return` しなければいけません。

> 注意:

> `Array`、`Object`、そして `Resource` は変換できません。

パラメータのプロトタイプのパース
`int zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, ...)`
`int zend_parse_parameters_ex(int flags, int num_args TSRMLS_DC, char *type_spec, ...)`
`int zend_parse_parameter(int flags, int arg_num TSRMLS_DC, zval **arg, const char *spec, ...)`

> 注意:

> `zend_parse_parameter` はバージョン 5.5 以降で使用可能で、`zend_parse_parameters_ex` と同じような挙動です。 ただ、引数をスタックから読み込むのではなく、変換対象の zval を受け取って、その場で変換します。

> 注意:

> `flags` はマスクとして扱うことを想定しています。現時点で有効なフラグは `ZEND_PARSE_PARAMS_QUIET` だけです (これは、警告を抑制します)。

これらの API 関数から受け取る可変引数は、C の変数のアドレスであることが期待されています。 また、`zend_parse_parameters` API 関数の出力であると考えなければいけません。

型指定子
指定子 | 型 | ローカル
--- | --- | ---
a | `array` | `zval*`
A | `array` あるいは `object` | `zval*`
b | `boolean` | `zend_bool`
C | `class` | `zend_class_entry*`
d | `double` | `double`
f | `function` | `zend_fcall_info*`, `zend_fcall_info_cache*`
h | `array` | `HashTable*`
H | `array` あるいは `object` | `HashTable*`
l | `long` | `long`
L | `long` (LONG_MAX/LONG_MIN の範囲に収まるもの) | `long`
o | `object` | `zval*`
O | `object` (指定した `zend_class_entry` のもの) | `zval*`, `zend_class_entry*`
p | `string` (有効なパス) | `char*`, `int`
r | `resource` | `zval*`
s | `string` | `char*`, `int`
z | `mixed` | `zval*`
Z | `mixed` | `zval**`

> 注意:

> 型指定子が `O` の場合、ローカルの `zend_class_entry*` は `zend_parse_parameter` への入力 (型指定子の一部) と見なされます。

高度な型指定子
指定子 | 説明
--- | ---
* | 直前の型の引数が 0 個以上
+ | 直前の型の引数が 1 個以上
| | 残りのパラメータは任意である
/ | 後に続くパラメータを `SEPARATE_ZVAL_IF_NOT_REF` します。
! | 直前のパラメータが、指定した型あるいは `null` のどちらかになるものとします。 'b'、'l'、'd' の場合は、対応する `bool*`、`long*`、`double*` の後に zend_bool* 型の引数を追加する必要があります。`null` を受け取った場合は、ここに `true` が設定されます。

> 注意:

> パラメータのパースについての詳細は、ソース配布物に含まれる `README.PARAMETER_PARSING_API` を参照ください。

一度 `Hacker` の関数が実行されたら、実行するよう実装されているかどうかにかかわらず、エンジン向けの `return_value` を設定します。 `RETURN_` マクロと `RETVAL_` マクロは `Z_*_P` の単なるラッパーで、`return_value` と組み合わせて使います。

> 注意:

> `RETURN_` マクロはその場で関数の実行を終了します (つまり、`return;` します) が、 `RETVAL_` は、`return_value` を設定してからも実行を続けます。

これで、関数の仕組みについて、それなりに理解できたことでしょう。 メソッドの仕組みについても、ある程度は理解できたはずです。