Extension을 제작하다보면 PHP에 이미 정의되어 있는 함수를 이용해야 할 때가 있다.

방법은 2가지다.
하나는 PHP 소스 내에서 관련 함수들을 조합해서 호출하는 방법이고,
나머지 하나는 PHP의 정의된 함수가 모여있는 EG(function_table)을 이용하는 방법이다.

다음은 2가지 방법을 이용해 md5() 함수를 호출하는 예제이다.
(functions.h, functions.c 파일을 추가했다.)



config.m4
PHP_ARG_ENABLE(myext, whether to enable myext support,
[ --enable-myext enable myext support])

if test "$PHP_TEST" = "yes"; then
    AC_DEFINE(HAVE_MYEXT, 1, [whether you have myext])
    PHP_NEW_EXTENSION(myext, myext.c functions.c, $ext_shared)
fi




php_myext.h
#ifndef PHP_MYEXT_H
#define PHP_MYEXT_H

#define PHP_MYEXT_VERSION "1.0" // 버전
#define PHP_MYEXT_EXTNAME "myext" // 이름

// Thread Safe Resource Manager
#ifdef ZTS
#include "TSRM.h"
#endif

// MD5
PHP_FUNCTION(myext_md5_1);
PHP_FUNCTION(myext_md5_2);

// 모듈엔트리
extern zend_module_entry myext_module_entry;
#define phpext_myext_ptr &myext_module_entry

#endif




myext.c
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_myext.h"
#include "ext/standard/info.h"

#include "functions.h"

// function entry
static function_entry myext_functions[] = {
    PHP_FE(myext_md5_1, NULL)
    PHP_FE(myext_md5_2, NULL)
    {NULL, NULL, NULL}
};

zend_module_entry myext_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_MYEXT_EXTNAME,
    myext_functions, // function_entry
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
#if ZEND_MODULE_API_NO >= 20010901
    PHP_MYEXT_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_MYEXT
ZEND_GET_MODULE(myext)
#endif

// MD5
PHP_FUNCTION(myext_md5_1)
{
    char *p1;
    int p1_len;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &p1, &p1_len)==FAILURE) {
        RETURN_NULL();
    }
    RETURN_STRING(md5_1(p1), 1);
}

PHP_FUNCTION(myext_md5_2)
{
    char *p1;
    int p1_len;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &p1, &p1_len)==FAILURE) {
        RETURN_NULL();
    }
    RETURN_STRING(md5_2(p1), 1);
}




functions.h
#ifndef FUNCTIONS_H
#define FUNCTIONS_H

#include "php.h"

char *_md5_1(char *msg, zend_bool raw_output TSRMLS_DC);
char *_md5_2(char *msg, zend_bool raw_output TSRMLS_DC);

#define md5_1(msg) _md5_1(msg, 0 TSRMLS_CC)
#define md5_2(msg) _md5_2(msg, 0 TSRMLS_CC)

#endif





functions.c
#include "functions.h"
#include
"ext/standard/md5.h"

char *_md5_1(char *msg, zend_bool raw_output TSRMLS_DC)
{
    unsigned char digest[16];
    char md5str[33];
    PHP_MD5_CTX context;

    PHP_MD5Init(&context);
    PHP_MD5Update(&context, (unsigned char*)msg, strlen(msg));
    PHP_MD5Final(digest, &context);
    if (raw_output) {
        return (char*)digest;
    }
    else {
        md5str[0] = '\0';
        make_digest_ex(md5str, digest, 16);
        char *result = NULL;
        spprintf(&result, 0, "%s", md5str);
        return result;
    }
}

char *_md5_2(char *msg, zend_bool raw_output TSRMLS_DC)
{
    zval *funcname, *p1, *p2, *args[2], *ret;
    MAKE_STD_ZVAL(funcname);
    ZVAL_STRING(funcname, "md5", 1);

    // 각 파라메터 세팅
    MAKE_STD_ZVAL(p1);
    MAKE_STD_ZVAL(p2);
    ZVAL_STRING(p1, msg, 1);
    ZVAL_BOOL(p2, 0);

    // 각 파라메터를 args 배열에 대입
    args[0] = p1;
    args[1] = p2;
 
    // 리턴값 초기화
    MAKE_STD_ZVAL(ret);

    // 함수 호출
    call_user_function(EG(function_table), NULL, funcname, ret, 2, args TSRMLS_CC);

    zval_dtor(funcname);
    zval_dtor(p1);
    zval_dtor(p2);

    return Z_STRVAL_P(ret);
}




test.php
var_dump(md5('abcd'));
var_dump(myext_md5_1('abcd'));
var_dump(myext_md5_2('abcd'));



위처럼 호출하면 아래와 같이 동일한 결과물이 나올 것이다.
string 'e2fc714c4727ee9395f324cd2e7f331f' (length=32)
string 'e2fc714c4727ee9395f324cd2e7f331f' (length=32)
string 'e2fc714c4727ee9395f324cd2e7f331f' (length=32)



_md5_1() 함수는 ext/standard/md5.h 에 정의되어 있는 C 소스를 그대로 이용하는 방법이고,
_md5_2() 함수는 EG(function_table)에 정의되어 있는 md5()를 호출하는 방법이다.

사용하고자 하는 built-in 함수들이 각자 지 맘대로의 형태를 가지고 있으므로 적절하게 사용하자.

_md5_2() 의 경우 call_user_function()을 이용해서 함수를 호출하여 결과값을 zval *ret 에 담는 것이 포인트이다.



call_user_function()
정의는 다음과 같다.
int call_user_function(HashTable *function_table, zval **object_pp, zval *function_name, zval *retval_ptr, zend_uint param_count, zval *params[] TSRMLS_DC)

파라메터 설명
 파라메터  설명
 HashTable *function_table  함수들이 들어있는 HashTable
 zval **object_pp  함수가 정의된 scope.
만약 클래스의 멤버함수를 호출할 경우 이 자리를 NULL로 할 수 없다.
 zval *function_name  호출할 함수의 이름
 zval *reval_ptr  함수의 리턴값이 저장될 zval*
 zend_uint param_count  함수호출시 넘길 파라메터의 수
 zval *params[]  파라메터를 담고 있는 zval* 의 배열
 TSRMLS_DC  Thread Safety를 위한 변수 매크로

'PHP' 카테고리의 다른 글

[PHP] PHP Core - PHP interpreter  (0) 2010.09.14
[PHP] mod_php5 (SAPI for Apache)  (0) 2010.09.14
[PHP Extension] Super Globals  (0) 2010.09.13
[PHP Extension] C++로 제작하기 위한 config.m4  (0) 2010.09.09
[PHP Extension] 클래스 (Class)  (0) 2010.08.06
Posted by bloodguy
,