関数の作成

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 しなければいけません。

注意:

ArrayObject、そして 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 を設定してからも実行を続けます。

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

add a note add a note

User Contributed Notes 2 notes

up
5
ka5 at ua dot fm
5 years ago
The 'l' spec expects a parameter of the type zend_long, not long anymore.
The 's' spec expects parameters of the type char * and size_t, no int anymore.
(c) https://github.com/php/php-src/blob/PHP-7.0.0/UPGRADING.INTERNALS#L75
up
1
Anton
4 years ago
To handle string parameters you should specify string length:

char *s1;
size_t s1_len;

char *s2;
size_t s2_len;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",
                          &l, &s1, &s1_len, &s2 &s2_len)  != SUCCESS) {
    return;
}
To Top