PHP:最初のPHP拡張を作成する

4014 ワード

今日はPHP拡張の入門文章をいくつか見ましたが、とても良い文章ですが、読むのはやはり大変です.主に、ライフサイクル、メモリ割り当て、SAPIという概念が分からないからです.そしてコードの中はマクロだらけで、ソースコードを調べなければ、天書を読むのと変わらない.だからソースコードの角度からhelloを分析することを記録したいです.cこのファイルの過程、他の文章の中にある私は繰り返しません.私は先にコードを置いて、ここには元の文章とは異なる部分があります.
次の3つのファイルが含まれているフォルダを勝手に作成します.
config.m4
PHP_ARG_ENABLE(hello, whether to enable Hello
World support,
[ --enable-hello   Enable Hello World support])
if test "$PHP_HELLO" = "yes"; then
  AC_DEFINE(HAVE_HELLO, 1, [Whether you have Hello World])
  PHP_NEW_EXTENSION(hello, hello.c, $ext_shared)
fi

php_hello.h
#ifndef PHP_HELLO_H
#define PHP_HELLO_H 1
#define PHP_HELLO_WORLD_VERSION "1.0"
#define PHP_HELLO_WORLD_EXTNAME "hello"

PHP_FUNCTION(hello_world);

extern zend_module_entry hello_module_entry;
#define phpext_hello_ptr &hello_module_entry

#endif

hello.c
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_hello.h"

//             
static zend_function_entry hello_functions[] = {
    PHP_FE(hello_world, NULL)
    {NULL, NULL, NULL}
};

//          
//     ,       ,    ,    
zend_module_entry hello_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_HELLO_WORLD_EXTNAME,
    hello_functions,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
#if ZEND_MODULE_API_NO >= 20010901
    PHP_HELLO_WORLD_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};

//         Dynamic Loading,    
#ifdef COMPILE_DL_HELLO
ZEND_GET_MODULE(hello)
#endif

//       
PHP_FUNCTION(hello_world)
{
    RETURN_STRING("Hello World", 1);
}

次のコマンドを実行して、拡張を検証します.
phpize
./configure
make
php -dextension=modules/hello.so -r "echo hello_world();"

原文と違いここではzend_を使いますfunction_function_ではなくentryentry.これはPHPのバージョンと関係があります.そうしないとコンパイルが間違ってしまいます.
まずこのコードを見てみましょう
static zend_function_entry hello_functions[] = {
    PHP_FE(hello_world, NULL)
    {NULL, NULL, NULL}
};

PHP公式文書を見ると、そう書く可能性もあります.
static zend_function_entry hello_functions[] = {
    PHP_FE(hello_world, NULL)
    ZEND_FE_END
};

結局、あなたが理解できないマクロの定義を見つければいいのですが、zend_を探してみましょう.function_entry、私のソースコードは下のパスの下にあるので、私は先にcdを行きます.
/usr/local/Cellar/php56/5.6.32_8/include/php

次のコマンドでzend_を探しますfunction_entryの定義.
grep -rnw . -e 'zend_function_entry'

発見はzend_API.hこの書類.
./Zend/zend_API.h:41:} zend_function_entry;

ソースコードは、関数の情報を格納する構造体です.
typedef struct _zend_function_entry {
        const char *fname;
        void (*handler)(INTERNAL_FUNCTION_PARAMETERS);
        const struct _zend_arg_info *arg_info;
        zend_uint num_args;
        zend_uint flags;
} zend_function_entry;

続いてPHP_を見ますFE,同様に次のコマンドを用いる.
grep -rnw . -e 'PHP_FE'

次の結果が得られます.
./main/php.h:352:#define PHP_FE         ZEND_FE

ZEND_を探してFE、次は書かないで、最後にこの定義だと調べました.
#define ZEND_FENTRY(zend_name, name, arg_info, flags)   { #zend_name, name, arg_info, (zend_uint) (sizeof(arg_info)/sizeo
f(struct _zend_arg_info)-1), flags }, 
#define ZEND_FE(name, arg_info)                                         ZEND_FENTRY(name, ZEND_FN(name), arg_info, 0)

実は関数構造体に関する情報であり、以前のzend_function_entry対応.
zend_を見てmodule_entryというコードは、実は作り方も上を参照して、#if ZEND_MODULE_API_NO>=20010901はapiバージョンが20010901(これは年月日)以上であるか否かを判断するためのものであり、それより大きい場合はSTANDARD_をコンパイル期間に含めるMODULE_HEADERというマクロ、これらの情報は以下のソースファイルで見つけることができます.ZEND_MODULE_API_NOこのマクロもこのファイルに定義されています.
zend_modules.h

最後にこれを言います
//         Dynamic Loading,    
#ifdef COMPILE_DL_HELLO
ZEND_GET_MODULE(hello)
#endif

実は私もよくわかりませんがZEND_を調べましたGET_MODULEというマクロは、実行時にZendエンジンにモジュールの名前を取得するための関数です.今日はたぶんここまでで、次回は関数の帯数の場合を書きます.