ZendEngine1과 ZendEngine2의 클래스는 좀 많이 다르다.
ZE2를 기준으로 한다. (그나마 클래스라고 부를 수 있지 않은가)





모든 예제의 stdafx.h 는 다음과 같다.
#pragma once

#include "zend_config.w32.h"
#include "php.h"

#include "php_myext.h"









Minimal Class




php_myext.h
#ifndef PHP_MYEXT_H
#define PHP_MYEXT_H

#define PHP_MYEXT_VERSION "1.0"
#define PHP_MYEXT_EXTNAME "myext"

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

extern "C" {
    #ifdef ZTS
    #include "TSRM.h"
    #endif
}

PHP_MINIT_FUNCTION(myext);

extern zend_module_entry myext_module_entry;
#define phpext_myext_ptr &myext_module_entry

#define PHP_MYEXT_CLASS_TESTCLASS_NAME "testClass"
extern zend_class_entry *php_myext_class_testClass_entry;
extern function_entry php_myext_class_testClass_functions[];

#endif



myext.cpp
#include "stdafx.h"

zend_class_entry *php_myext_class_testClass_entry;
function_entry php_myext_class_testClass_functions[] = {
    {NULL, NULL, NULL}
};

zend_module_entry myext_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_MYEXT_EXTNAME,
    NULL,
    PHP_MINIT(myext),
    NULL,
    NULL,
    NULL,
    NULL,
#if ZEND_MODULE_API_NO >= 20010901
    PHP_MYEXT_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};

extern "C" {
    ZEND_GET_MODULE(myext)
}

PHP_MINIT_FUNCTION(myext)
{
    zend_class_entry ce;
    INIT_CLASS_ENTRY(ce, PHP_MYEXT_CLASS_TESTCLASS_NAME, php_myext_class_testClass_functions);
    php_myext_class_testClass_entry = zend_register_internal_class(&ce TSRMLS_CC);

    return SUCCESS;
}



test.php
<?php
if (class_exists('testClass')) {
    $c = new testClass();
    var_dump($c);
    unset($c);
}
else echo "no class\n";

exit("script end\n");






__construct, __destruct


PHP5 클래스의 __construct, __destruct를 포함한 magic method는 여러개다.

__construct(...)
__destruct()
__clone()
__toString()
__get($var)
__set($var, $value)
__call($fname, $args)
__isset($varname)
__unset($varname)

magic method의 function entry 구성은 다음과 같은 형식을 가진다.
argument information이 필요하다.
static
    ZEND_BEGIN_ARG_INFO_EX(php_myext_one_arg, 0, 0, 1)
    ZEND_END_ARG_INFO()
static
    ZEND_BEGIN_ARG_INFO_EX(php_myext_two_args, 0, 0, 2)
    ZEND_END_ARG_INFO()

static function_entry php_myext_class_testClass_functions[] = {
    PHP_ME(myext_class_testClass_entry, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
    PHP_ME(myext_class_testClass_entry, __destruct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
    PHP_ME(myext_class_testClass_entry, __clone, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CLONE)
    PHP_ME(myext_class_testClass_entry, __toString, NULL, ZEND_ACC_PUBLIC)
    PHP_ME(myext_class_testClass_entry, __get, php_myext_one_arg, ZEND_ACC_PUBLIC)
    PHP_ME(myext_class_testClass_entry, __set, php_myext_two_args, ZEND_ACC_PUBLIC)
    PHP_ME(myext_class_testClass_entry, __call, php_myext_two_args, ZEND_ACC_PUBLIC)
    PHP_ME(myext_class_testClass_entry, __isset, php_myext_one_arg, ZEND_ACC_PUBLIC)
    PHP_ME(myext_class_testClass_entry, __unset, php_myext_one_arg, ZEND_ACC_PUBLIC)
    {NULL, NULL, NULL}
};






__construct와 __destruct만을 가지는 클래스는 다음과 같다.

php_myext.h
#ifndef PHP_MYEXT_H
#define PHP_MYEXT_H

#define PHP_MYEXT_VERSION "1.0"
#define PHP_MYEXT_EXTNAME "myext"

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

extern "C" {
    #ifdef ZTS
    #include "TSRM.h"
    #endif
}

PHP_MINIT_FUNCTION(myext);

extern zend_module_entry myext_module_entry;
#define phpext_myext_ptr &myext_module_entry

#define PHP_MYEXT_CLASS_TESTCLASS_NAME "testClass"
extern zend_class_entry *php_myext_class_testClass_entry;
extern function_entry php_myext_class_testClass_functions[];
PHP_METHOD(php_myext_class_testClass_entry, __construct);
PHP_METHOD(php_myext_class_testClass_entry, __destruct);

#endif


myext.cpp
#include "stdafx.h"

static zend_class_entry *php_myext_class_testClass_entry;
static function_entry php_myext_class_testClass_functions[] = {
    PHP_ME(php_myext_class_testClass_entry, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
    PHP_ME(php_myext_class_testClass_entry, __destruct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
    {NULL, NULL, NULL}
};

zend_module_entry myext_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_MYEXT_EXTNAME,
    NULL,
    PHP_MINIT(myext),
    NULL,
    NULL,
    NULL,
    NULL,
#if ZEND_MODULE_API_NO >= 20010901
    PHP_MYEXT_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};

extern "C" {
    ZEND_GET_MODULE(myext)
}

PHP_MINIT_FUNCTION(myext)
{
    zend_class_entry ce;
    INIT_CLASS_ENTRY(ce, PHP_MYEXT_CLASS_TESTCLASS_NAME, php_myext_class_testClass_functions);
    php_myext_class_testClass_entry = zend_register_internal_class(&ce TSRMLS_CC);

    return SUCCESS;
}

PHP_METHOD(php_myext_class_testClass_entry, __construct)
{
    php_printf("__construct\n");
}

PHP_METHOD(php_myext_class_testClass_entry, __destruct)
{
    php_printf("__destruct\n");
}


test.php
<?php
if (class_exists("testClass")) {
    $c = new testClass();
    var_dump($c);
    unset($c);
}
else echo "no class\n";

exit("script end\n");






Access Flags


method (types)
ZEND_ACC_STATIC
ZEND_ACC_ABSTRACT
ZEND_ACC_IMPLEMENTED_ABSTRACT

method (visibility)
ZEND_ACC_PUBLIC
ZEND_ACC_PROTECTED
ZEND_ACC_PRIVATE
ZEND_ACC_PPP_MASK

method (special)
ZEND_ACC_CTOR
ZEND_ACC_DTOR
ZEND_ACC_CLONE

class (types)
ZEND_ACC_IMPLICIT_ABSTRACT_CLASS
ZEND_ACC_EXPLICIT_ABSTRACT_CLASS
ZEND_ACC_FINAl_CLASS
ZEND_ACC_INTERFACE

etc
ZEND_ACC_ALLOW_STATIC
ZEND_ACC_SHADOW
ZEND_ACC_DEPRECATED
ZEND_ACC_CALL_VIA_HANDLER
ZEND_ACC_CHANGED
ZEND_ACC_IMPLICIT_PUBLIC





Methods


public, protected, private, static, abstract Methods를 가지는 클래스
여기서부터는 Windows C++가 아닌, linux C를 기준으로 함.

클래스가 늘어나면 각 클래스를 파일로 분리하고, 매크로를 재정의하는 방법을 사용하는게 좋으므로,
이제부턴 클래스가 1개라도 파일을 분리하여 매크로를 정의하는 방식으로 구성함.


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

if test "$PHP_MYEXT" = "yes"; then
    AC_DEFINE(HAVE_MYEXT, 1, [whether you have myext])
    PHP_NEW_EXTENSION(myext, myext.c myclass.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"

#ifdef ZTS
#include "TSRM.h"
#endif

PHP_MINIT_FUNCTION(myext);

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 "myclass.h"

zend_module_entry myext_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_MYEXT_EXTNAME,
    NULL,
    PHP_MINIT(myext),
    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

PHP_MINIT_FUNCTION(myext)
{
    // 클래스 등록
    PHP_MINIT(myClass)(INIT_FUNC_ARGS_PASSTHRU);

    return SUCCESS;
}



myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H

#include "php.h"

// 클래스 이름
#define CN_MYCLASS "myClass"
// 클래스 엔트리
extern zend_class_entry *myClass_ce;
// 클래스 methods
extern function_entry myClass_functions[];

// 클래스 method 매크로
#define MYCLASS_ME(func, arg_info, flags) PHP_ME(myClass_ce, func, arg_info, flags)
#define MYCLASS_METHOD(func) PHP_METHOD(myClass_ce, func)

// methods
MYCLASS_METHOD(__construct);
MYCLASS_METHOD(__destruct);
MYCLASS_METHOD(func_public);
MYCLASS_METHOD(func_protected);
MYCLASS_METHOD(func_private);
MYCLASS_METHOD(func_static);
MYCLASS_METHOD(func_abstract);
MYCLASS_METHOD(func_abstract_protected);

#endif



myclass.c
#include "myclass.h"

// 클래스 엔트리
zend_class_entry *myClass_ce;

// 클래스 methods
function_entry myClass_functions[] = {
    MYCLASS_ME(__construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
    MYCLASS_ME(__destruct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
    MYCLASS_ME(func_public, NULL, ZEND_ACC_PUBLIC)
    MYCLASS_ME(func_protected, NULL, ZEND_ACC_PROTECTED)
    MYCLASS_ME(func_private, NULL, ZEND_ACC_PRIVATE)
    MYCLASS_ME(func_static, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
    MYCLASS_ME(func_abstract, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT)
    MYCLASS_ME(func_abstract_protected, NULL, ZEND_ACC_PROTECTED|ZEND_ACC_ABSTRACT)
    {NULL, NULL, NULL}
};

// __construct
MYCLASS_METHOD(__construct)
{
    php_printf("__construct<br>");
}

// __destruct
MYCLASS_METHOD(__destruct)
{
    php_printf("__destruct<br>");
}

// func_public
MYCLASS_METHOD(func_public)
{
    php_printf("func_public<br>");
}

// func_protected
MYCLASS_METHOD(func_protected)
{
    php_printf("func_protected<br>");
}

// func_private
MYCLASS_METHOD(func_private)
{
    php_printf("func_private<br>");
}

// func_static
MYCLASS_METHOD(func_static)
{
    php_printf("func_static<br>");
}

// func_abstract
MYCLASS_METHOD(func_abstract)
{
    php_printf("func_abstract<br>");
}

// func_abstract_protected
MYCLASS_MeTHOD(func_abstract_protected)
{
    php_printf("func_abstract_protected<br>");
}

// MINIT - myClass
PHP_MINIT_FUNCTION(myClass)
{
    zend_class_entry ce_myClass;
    INIT_CLASS_ENTRY(ce_myClass, CN_MYCLASS, myClass_functions);
    myClass_ce = zend_register_internal_class(&ce_myClass TSRMLS_CC);

    return SUCCESS;
}




test.php
<?php
class testChild extends testClass
{
    public function test_abstract()
    {
        echo "test_abstract from testChild\n";
    }

    protected function test_abstract_protected()
    {
        echo "test_abstract_protected from testChild\n";
    }

    public function tap()
    {
        $this->test_abstract_protected();
    }
}

testChild::test_static();

$c = new testChild();
$c->test_public();
$c->test_abstract();
$c->tap();
unset($c);

exit("script end\n");










Properties


type, visibility 별 프로퍼티 세팅
MINIT 에서 클래스를 등록시키면서 우선 visibility가 세팅된 property를 등록시키고,
__construct와 RINIT(static properties)에서 초기화하는 방식의 예제.


php_myext.h
#ifndef PHP_MYEXT_H
#define PHP_MYEXT_H 1

#define PHP_MYEXT_VERSION "1.0"
#define PHP_MYEXT_EXTNAME "myext"

#ifdef ZTS
#include "TSRM.h"
#endif

PHP_MINIT_FUNCTION(myext);
PHP_MSHUTDOWN_FUNCTION(myext);
PHP_RINIT_FUNCTION(myext);

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 "myclass.h"


zend_module_entry myext_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_MYEXT_EXTNAME,
    NULL,
    PHP_MINIT(myext),
    PHP_MSHUTDOWN(myext),
    PHP_RINIT(myext),
    NULL,
    NULL,
#if ZEND_MODULE_API_NO >= 20010901
    PHP_MYEXT_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_MYEXT
ZEND_GET_MODULE(myext)
#endif

// MINIT
PHP_MINIT_FUNCTION(myext)
{
    // 클래스 등록
    PHP_MINIT(myClass)(INIT_FUNC_ARGS_PASSTHRU);

    return SUCCESS;
}

// MSHUTDOWN
PHP_MSHUTDOWN_FUNCTION(myext)
{
    // 해제
    PHP_MSHUTDOWN(myClass)(SHUTDOWN_FUNC_ARGS_PASSTHRU);

    return SUCCESS;
}

// RINIT
PHP_RINIT_FUNCTION(myext)
{
    // myClass RINIT
    PHP_RINIT(myClass)(INIT_FUNC_ARGS_PASSTHRU);

    return SUCCESS;
}



myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H

#include "php.h"

// 클래스 이름
#define CN_MYCLASS "myClass"
// 클래스 엔트리
extern zend_class_entry *myClass_ce;
// 클래스 methods
extern function_entry myClass_functions[];

// 클래스 method 매크로
#define MYCLASS_ME(func, arg_info, flags) PHP_ME(myClass_ce, func, arg_info, flags)
#define MYCLASS_METHOD(func) PHP_METHOD(myClass_ce, func)

// 프로퍼티
#define MYCLASS_PROPERTY(name, acc) zend_declare_property_null(myClass_ce, name, strlen(name), acc TSRMLS_CC)

MYCLASS_METHOD(__construct);

// MINIT - myClass
PHP_MINIT_FUNCTION(myClass);
// MSHUTDOWN - myClass
PHP_MSHUTDOWN_FUNCTION(myClass);
// RINIT - myClass
PHP_RINIT_FUNCTION(myClass);

#endif



myclass.c
#include "myclass.h"

// 클래스 엔트리
zend_class_entry *myClass_ce;

// 클래스 methods
function_entry myClass_functions[] = {
    MYCLASS_ME(__construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
    {NULL, NULL, NULL}
};

// __construct
MYCLASS_METHOD(__construct)
{
    // bool (public)
    zend_update_property_bool(myClass_ce, getThis(), "prop_bool", strlen("prop_bool"), 1 TSRMLS_CC);
    // long (public)
    zend_update_property_long(myClass_ce, getThis(), "prop_long", strlen("prop_long"), 123 TSRMLS_CC);
    // double (public)
    zend_update_property_double(myClass_ce, getThis(), "prop_double", strlen("prop_double"), 3.141592 TSRMLS_CC);
    // string (public)
    zend_update_property_string(myClass_ce, getThis(), "prop_string", strlen("prop_string"), "String!" TSRMLS_CC);
    // array (public)
    // 배열을 초기화해서 프로퍼티로 등록시키는 방법.
    zval *z_arr;
    MAKE_STD_ZVAL(z_arr);
    array_init(z_arr);
    zend_update_property(myClass_ce, getThis(), "prop_array", strlen("prop_array"), z_arr TSRMLS_CC);
    // object (public)
    // 클래스를 생성해서 프로퍼티로 등록시키는 방법.
    zval *z_obj;
    zend_class_entry **ce;
    if (zend_lookup_class("DomDocument", strlen("DomDocument"), &ce TSRMLS_CC)==SUCCESS) {
        MAKE_STD_ZVAL(z_obj);
        object_init_ex(z_obj, *ce);
       zend_update_property(myClass_ce, getThis(), "prop_object", strlen("prop_object"), z_obj TSRMLS_CC);
    }
    // long (protected)
    zend_update_property_long(myClass_ce, getThis(), "prop_long_protected", strlen("prop_long_protected"), 321 TSRMLS_CC);
    // long (private)
    zend_update_property_long(myClass_ce, getThis(), "prop_long_private", strlen("prop_long_private"), 231 TSRMLS_CC);
}

// MINIT - myClass
PHP_MINIT_FUNCTION(myClass)
{
    zend_class_entry ce_myClass;
    INIT_CLASS_ENTRY(ce_myClass, CN_MYCLASS, myClass_functions);
    myClass_ce = zend_register_internal_class(&ce_myClass TSRMLS_CC);

    // 일단 모든 프로퍼티들을 null로 등록, 다만 visibility는 미리 설정.
    MYCLASS_PROPERTY("prop_null", ZEND_ACC_PUBLIC);
    MYCLASS_PROPERTY("prop_bool", ZEND_ACC_PUBLIC);
    MYCLASS_PROPERTY("prop_long", ZEND_ACC_PUBLIC);
    MYCLASS_PROPERTY("prop_double", ZEND_ACC_PUBLIC);
    MYCLASS_PROPERTY("prop_string", ZEND_ACC_PUBLIC);
    MYCLASS_PROPERTY("prop_array", ZEND_ACC_PUBLIC);
    MYCLASS_PROPERTY("prop_object", ZEND_ACC_PUBLIC);
    MYCLASS_PROPERTY("prop_long_protected", ZEND_ACC_PROTECTED);
    MYCLASS_PROPERTY("prop_long_private", ZEND_ACC_PRIVATE);
    MYCLASS_PROPERTY("prop_long_static", ZEND_ACC_PUBLIC|ZEND_ACC_STATIC);

    return SUCCESS;
}

// MSHUTDOWN - myClass
PHP_MSHUTDOWN_FUNCTION(myClass)
{
    myClass_ce = NULL;
 
    return SUCCESS;
}

// RINIT - myClass
PHP_RINIT_FUNCTION(myClass)
{
    // long (static)
    zend_update_static_property_long(myClass_ce, "prop_long_static", strlen("prop_long_static"), 3344 TSRMLS_CC);

    return SUCCESS;
}




test.php
<?php
$c = new testClass();
var_dump($c);
var_dump(testClass::$prop_long_static);


결과값
---------- PHP ----------
object(testChild)#1 (9) {
  ["prop_null"]=>
  NULL
  ["prop_bool"]=>
  bool(true)
  ["prop_long"]=>
  int(123)
  ["prop_double"]=>
  float(3.141592)
  ["prop_string"]=>
  string(7) "String!"
  ["prop_array"]=>
  array(0) {
  }
  ["prop_object"]=>
  object(DOMDocument)#3 (0) {
  }
  ["prop_long_protected:protected"]=>
  int(321)
  ["prop_long_private"]=>
  int(231)
}
int(3344)







Constants



좀 간단한 편이다.
MINIT 때 클래스의 HashTable에 써주면 된다.


php_myext.h
#ifndef PHP_MYEXT_H
#define PHP_MYEXT_H

#define PHP_MYEXT_VERSION "1.0"
#define PHP_MYEXT_EXTNAME "myext"

#ifdef ZTS
#include "TSRM.h"
#endif

PHP_MINIT_FUNCTION(myext);

// 모듈엔트리
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 "myclass.h"

zend_module_entry myext_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_MYEXT_EXTNAME,
    NULL,
    PHP_MINIT(myext),
    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

// MINIT
PHP_MINIT_FUNCTION(myext)
{
    // 클래스 등록
    PHP_MINIT(myClass)(INIT_FUNC_ARGS_PASSTHRU);

    return SUCCESS;
}





myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H

#include "php.h"

// 클래스이름
#define CN_MYCLASS "myClass"
// 클래스 엔트리
extern zend_class_entry *myClass_ce;

// MINIT - myClass
PHP_MINIT_FUNCTION(myClass);

#endif




myclass.c
#include "myclass.h"

// 클래스 엔트리
zend_class_entry *myClass_ce;

// MINIT - myClass
PHP_MINIT_FUNCTION(myClass)
{
    // 클래스 등록
    zend_class_entry ce_myClass;
    INIT_CLASS_ENTRY(ce_myClass, CN_MYCLASS, NULL);
    myClass_ce = zend_register_internal_class(&ce_myClass TSRMLS_CC);

    // constants
    zend_declare_class_constant_long(myClass_ce, "MY_LONG", strlen("MY_LONG"), 123 TSRMLS_CC);
    zend_declare_class_constant_double(myClass_ce, "MY_DOUBLE", strlen("MY_DOUBLE"), 3.141592 TSRMLS_CC);
    zend_declare_class_constant_bool(myClass_ce, "MY_BOOL", strlen("MY_BOOL"), 1 TSRMLS_CC);
    zend_declare_class_constant_string(myClass_ce, "MY_STRING", strlen("MY_STRING"), "Hello" TSRMLS_CC);

    return SUCCESS;
}



test.php
var_dump(myClass::MY_LONG);
var_dump(myClass::MY_DOUBLE);
var_dump(myClass::MY_BOOL);
var_dump(myClass::MY_STRING);








Interface



그냥 abstract method로 가득 채우고 class_entry의 ce_flags만 바꿔주면 interface가 됨.




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
  
PHP_MINIT_FUNCTION(myext);
  
// 모듈엔트리
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 "myclass.h"
  
zend_module_entry myext_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_MYEXT_EXTNAME,
    NULL,
    PHP_MINIT(myext),
    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
  
// MINIT
PHP_MINIT_FUNCTION(myext)
{
    // 클래스 등록
    PHP_MINIT(myClass)(INIT_FUNC_ARGS_PASSTHRU);
  
    return SUCCESS;
}




myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
  
#include "php.h"
  
// 클래스이름
#define CN_MYCLASS "myClass"
// 클래스 엔트리
extern zend_class_entry *myClass_ce;
// 클래스 methods
extern function_entry myClass_functions[];
  
// 클래스 method 매크로
#define MYCLASS_ME(func, arg_info, flags) PHP_ME(myClass_ce, func, arg_info, flags)
#define MYCLASS_METHOD(func) PHP_METHOD(myClass_ce, func)
#define MYCLASS_ABSTRACT_ME(func, arg_info) PHP_ABSTRACT_ME(myClass_ce, func, arg_info)
  
// MINIT - myClass
PHP_MINIT_FUNCTION(myClass);
  
#endif




myclass.c
#include "myclass.h"
  
// 클래스 엔트리
zend_class_entry *myClass_ce;
  
// 클래스 methods
function_entry myClass_functions[] = {
    MYCLASS_ABSTRACT_ME(ab_func, NULL)
    {NULL, NULL, NULL}
};
  
  
// MINIT - myClass
PHP_MINIT_FUNCTION(myClass)
{
    zend_class_entry ce_myClass;
    INIT_CLASS_ENTRY(ce_myClass, CN_MYCLASS, myClass_functions);
    // interface로 선언
    myClass_ce = zend_register_internal_interface(&ce_myClass TSRMLS_CC);
  
    return SUCCESS;
}




test.php
<?php
$c = new testClass();

// interface는 instantiate 할 수 없다는 에러가 난다.




extension에서 정의된 interface를 extension에서 implement 하는 방법은 다음과 같음.
my_interface.h, my_interface.c 파일을 추가.



config.m4
PHP_ARG_ENABLE(myext, whether to enable myext support,
[ --enable-myext    Enable myext support])
  
if test "$PHP_MYEXT" = "yes"; then
    AC_DEFINE(HAVE_MYEXT, 1, [Whether you have myext])
    PHP_NEW_EXTENSION(myext, myext.c my_interface.c myclass.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
  
PHP_MINIT_FUNCTION(myext);
  
// 모듈엔트리
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 "my_interface.h"
#include "myclass.h"
  
zend_module_entry myext_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_MYEXT_EXTNAME,
    NULL,
    PHP_MINIT(myext),
    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
  
// MINIT
PHP_MINIT_FUNCTION(myext)
{
    PHP_MINIT(myInterface)(INIT_FUNC_ARGS_PASSTHRU);
    PHP_MINIT(myClass)(INIT_FUNC_ARGS_PASSTHRU);
  
    return SUCCESS;
}






my_interface.h
#ifndef MY_INTERFACE_H
#define MY_INTERFACE_H
  
#include "php.h"
  
// 인터페이스 이름
#define CN_MY_INTERFACE "myInterface"
// 인터페이스 엔트리
extern zend_class_entry *myInterface_ce;
// 인터페이스 methods
extern function_entry myInterface_functions[];
  
// 인터페이스 method 매크로
#define MY_INTERFACE_ABSTRACT_ME(func, arg_info) PHP_ABSTRACT_ME(myInterface_ce, func, arg_info)
  
// MINIT - myInterface
PHP_MINIT_FUNCTION(myInterface);
  
#endif




my_interface.c
#include "my_interface.h"
  
// 인터페이스 엔트리
zend_class_entry *myInterface_ce;
  
// 인터페이스 methods
function_entry myInterface_functions[] = {
    MY_INTERFACE_ABSTRACT_ME(func1, NULL)
    MY_INTERFACE_ABSTRACT_ME(func2, NULL)
    {NULL, NULL, NULL}
};
  
  
// MINIT - myInterface
PHP_MINIT_FUNCTION(myInterface)
{
    zend_class_entry ce_myInterface;
    INIT_CLASS_ENTRY(ce_myInterface, CN_MY_INTERFACE, myInterface_functions);
    // interface로 선언
    myInterface_ce = zend_register_internal_interface(&ce_myInterface TSRMLS_CC);
  
    return SUCCESS;
}




myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
  
#include "php.h"
  
// 클래스이름
#define CN_MYCLASS "myClass"
// 클래스 엔트리
extern zend_class_entry *myClass_ce;
// 클래스 methods
extern function_entry myClass_functions[];
  
// 클래스 method 매크로
#define MYCLASS_ME(func, arg_info, flags) PHP_ME(myClass_ce, func, arg_info, flags)
#define MYCLASS_METHOD(func) PHP_METHOD(myClass_ce, func)
  
// methods
MYCLASS_METHOD(func1);
MYCLASS_METHOD(func2);
  
// MINIT - myClass
PHP_MINIT_FUNCTION(myClass);
  
#endif





myclass.c
#include "myclass.h"
#include "my_interface.h"
  
// 클래스 엔트리
zend_class_entry *myClass_ce;
  
// 클래스 methods
function_entry myClass_functions[] = {
    MYCLASS_ME(func1, NULL, ZEND_ACC_PUBLIC)
    MYCLASS_ME(func2, NULL, ZEND_ACC_PUBLIC)
    {NULL, NULL, NULL}
};
  
// methods
MYCLASS_METHOD(func1)
{
    php_printf("func1<BR>");
}
  
MYCLASS_METHOD(func2)
{
    php_printf("func2<BR>");
}
  
// MINIT - myClass
PHP_MINIT_FUNCTION(myClass)
{
    zend_class_entry ce_myClass;
    INIT_CLASS_ENTRY(ce_myClass, CN_MYCLASS, myClass_functions);
    myClass_ce = zend_register_internal_class(&ce_myClass TSRMLS_CC);
    // implementation
    zend_class_implements(myClass_ce TSRMLS_CC, 1, myInterface_ce);
  
    return SUCCESS;
}




test.php
<?php
$c = new testClass();
var_dump($c->func1(), $c->func2());









상속




zend_register_internal_class_ex 를 사용하여 상속을 구현.
부모의 zend_class_entry를 대입해주면 되므로,
부모는 굳이 extension에서 정의한 클래스가 아니라 user space같은 곳에 정의된 클래스라도 상관없이,
zend_lookup_class 등으로 가져온 zend_class_entry를 이용할 수 있다.

눈여겨 봐야할 부분은 상속의 구현부와 클래스의 method를 호출하는 부분이다. (zend_call_method_with_0_params)

my_parent.h, my_parent.c 파일을 추가.


config.m4
 PHP_ARG_ENABLE(myext, whether to enable myext support,
[ --enable-myext    Enable myext support])
  
if test "$PHP_MYEXT" = "yes"; then
    AC_DEFINE(HAVE_MYEXT, 1, [Whether you have myext])
    PHP_NEW_EXTENSION(myext, myext.c my_parent.c myclass.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
  
PHP_MINIT_FUNCTION(myext);
  
// 모듈엔트리
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 "my_parent.h"
#include "myclass.h"
  
zend_module_entry myext_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_MYEXT_EXTNAME,
    NULL,
    PHP_MINIT(myext),
    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
  
// MINIT
PHP_MINIT_FUNCTION(myext)
{
    PHP_MINIT(myParent)(INIT_FUNC_ARGS_PASSTHRU);
    PHP_MINIT(myClass)(INIT_FUNC_ARGS_PASSTHRU);
  
    return SUCCESS;
}





my_parent.h
#ifndef MY_PARENT_H
#define MY_PARENT_H
  
#include "php.h"
  
// 부모클래스 이름
#define CN_MY_PARENT "myParent"
// 부모클래스 엔트리
extern zend_class_entry *myParent_ce;
// 부모클래스 methods
extern function_entry myParent_functions[];
  
// 부모클래스 method 매크로
#define MY_PARENT_ME(func, arg_info, flags) PHP_ME(myParent_ce, func, arg_info, flags)
#define MY_PARENT_METHOD(func) PHP_METHOD(myParent_ce, func)
  
// methods
MY_PARENT_METHOD(func_public);
MY_PARENT_METHOD(func_protected);
MY_PARENT_METHOD(func_private);
  
// MINIT - myParent
PHP_MINIT_FUNCTION(myParent);
  
#endif




my_parent.c
 #include "my_parent.h"
  
// 부모클래스 엔트리
zend_class_entry *myParent_ce;
  
// 부모클래스 methods
function_entry myParent_functions[] = {
    MY_PARENT_ME(func_public, NULL, ZEND_ACC_PUBLIC)
    MY_PARENT_ME(func_protected, NULL, ZEND_ACC_PROTECTED)
    MY_PARENT_ME(func_private, NULL, ZEND_ACC_PRIVATE)
    {NULL, NULL, NULL}
};
  
// methods
MY_PARENT_METHOD(func_public)
{
    php_printf("func_public (parent)<BR>");
}
  
MY_PARENT_METHOD(func_protected)
{
    php_printf("func_protected (parent)<BR>");
}
  
MY_PARENT_METHOD(func_private)
{
    php_printf("func_private (parent)<BR>");
}
  
// MINIT - myParent
PHP_MINIT_FUNCTION(myParent)
{
    zend_class_entry ce_myParent;
    INIT_CLASS_ENTRY(ce_myParent, CN_MY_PARENT, myParent_functions);
    myParent_ce = zend_register_internal_class(&ce_myParent TSRMLS_CC);
  
    return SUCCESS;
}




myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
  
#include "php.h"
  
// 클래스이름
#define CN_MYCLASS "myClass"
// 클래스 엔트리
extern zend_class_entry *myClass_ce;
// 클래스 methods
extern function_entry myClass_functions[];
  
// 클래스 method 매크로
#define MYCLASS_ME(func, arg_info, flags) PHP_ME(myClass_ce, func, arg_info, flags)
#define MYCLASS_METHOD(func) PHP_METHOD(myClass_ce, func)
  
// methods
MYCLASS_METHOD(func_public);
MYCLASS_METHOD(func_protected);
  
// MINIT - myClass
PHP_MINIT_FUNCTION(myClass);
  
#endif




myclass.c
#include "myclass.h"
#include "my_parent.h"
  
#include "Zend/zend_interfaces.h"
  
// 클래스 엔트리
zend_class_entry *myClass_ce;
  
// 클래스 methods
function_entry myClass_functions[] = {
    MYCLASS_ME(func_public, NULL, ZEND_ACC_PUBLIC)
    MYCLASS_ME(func_protected, NULL, ZEND_ACC_PROTECTED)
    {NULL, NULL, NULL}
};
  
// methods
MYCLASS_METHOD(func_public)
{
    // parent의 func_public() 호출
    zend_call_method_with_0_params(&(getThis()), Z_OBJCE_P(getThis())->parent, NULL, "func_public", NULL);
    php_printf("func_public (child)<BR>");
  
    // 자기 자신의 func_protected() 호출
    zend_call_method_with_0_params(&(getThis()), Z_OBJCE_P(getThis()), NULL, "func_protected", NULL);
    // parent의 func_protected() 호출
    zend_call_method_with_0_params(&(getThis()), Z_OBJCE_P(getThis())->parent, NULL, "func_protected", NULL);
}
  
MYCLASS_METHOD(func_protected)
{
    php_printf("func_protected (child)<BR>");
}
  
// MINIT - myClass
PHP_MINIT_FUNCTION(myClass)
{
    zend_class_entry ce_myClass;
    INIT_CLASS_ENTRY(ce_myClass, CN_MYCLASS, myClass_functions);
    myClass_ce = zend_register_internal_class_ex(&ce_myClass, myParent_ce, CN_MY_PARENT TSRMLS_CC);
  
    return SUCCESS;
}





test.php
$c = new myClass();
$c->func_public();
$c->func_private(); // 부모의 private 함수를 호출할 수 없다는 에러가 남






call method


클래스의 method를 호출하는 방법은 call_user_function() 함수와 유사하다고 할 수 있다.
기본적으로 1개의 함수와 3개의 매크로가 제공된다. (Zend/zend_interfaces.h)


함수
ZEND_API zval* zend_call_method(zval **object_pp, zend_class_entry *obj_ce, zend_function **fn_proxy, char *function_name, int function_name_len, zval **retval_ptr_ptr, int param_count, zval* arg1, zval* arg2 TSRMLS_DC);


파라메터
  파라메터  설명
 zval **object_pp  method를 호출하는 객체의 이중포인터
(=type이 IS_OBJECT인 zval**)
 zend_class_entry *obj_ce  scope.
자기자신 혹은 부모 등 method를 호출하는 클래스.
 zend_function **fn_proxy  함수의 proxy.
이미 function_table에 들어있다면 해당 함수를 proxy로 호출가능.
 char *function_name  호출할 함수이름.
엄청나게 주의해야 할 점은, PHP는 내부적으로 function_table에 함수명을 전부 소문자로 저장하므로, 비록 대소문자가 섞인 형태로 정의된 method라도 소문자로 변환해서 호출해야 함.
 int function_name_len  함수이름의 길이
 zval **retval_ptr_ptr  해당 method의 리턴값이 담길 zval**
 int param_count  해당 method에 들어갈 parameter의 수
(call_user_function과 같음)
 zval *arg1, zval *arg2  해당 method로 넘길 parameters.
중요한 점은 최대 2개까지 넘길 수 있다는 것.
 TSRMLS_DC  TSRM을 위한 변수 매크로



매크로
// 파라메터 0개로 method 호출
#define zend_call_method_with_0_params(obj, obj_ce, fn_proxy, function_name, retval) \
    zend_call_method(obj, obj_ce, fn_proxy, function_name, sizeof(function_name)-1, retval, 0, NULL, NULL TSRMLS_CC)
  
// 파라메터 1개로 method 호출
#define zend_call_method_with_1_params(obj, obj_ce, fn_proxy, function_name, retval, arg1) \
    zend_call_method(obj, obj_ce, fn_proxy, function_name, sizeof(function_name)-1, retval, 1, arg1, NULL TSRMLS_CC)
  
// 파라메터 2개로 method 호출
#define zend_call_method_with_2_params(obj, obj_ce, fn_proxy, function_name, retval, arg1, arg2) \
    zend_call_method(obj, obj_ce, fn_proxy, function_name, sizeof(function_name)-1, retval, 2, arg1, arg2 TSRMLS_CC)



여기서 굉장한 딜레마가 생기는데,
그럼 '파라메터가 3개 이상'인 method는 어떻게 호출할 것인가 하느 문제이다.
딱히 마법같은 방법을 찾지는 못했고 원래 정의되어 있는 zend_call_method 함수를 변형해서 구현했다.
해당 함수를 functions.h, functionsc 에 구현한 예제다.


config.m4
PHP_ARG_ENABLE(myext, whether to enable myext support,
[ --enable-myext    Enable myext support])
  
if test "$PHP_MYEXT" = "yes"; then
    AC_DEFINE(HAVE_MYEXT, 1, [Whether you have myext])
    PHP_NEW_EXTENSION(myext, myext.c functions.c myclass.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
  
PHP_MINIT_FUNCTION(myext);
  
// 모듈엔트리
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 "myclass.h"
  
zend_module_entry myext_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_MYEXT_EXTNAME,
    NULL,
    PHP_MINIT(myext),
    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
  
// MINIT
PHP_MINIT_FUNCTION(myext)
{
    PHP_MINIT(myClass)(INIT_FUNC_ARGS_PASSTHRU);
  
    return SUCCESS;
}




functions.h
#ifndef FUNCTIONS_H
#define FUNCTIONS_H
  
#include "php.h"
  
ZEND_API zval* zend_call_method_with_n_args(zval **object_pp, zend_class_entry *obj_ce, zend_function **fn_proxy, char *function_name, int function_name_len, zval **retval_ptr_ptr, int param_count, zval** args TSRMLS_DC);
#define call_method(object_pp, obj_ce, function_name, retval, param_count, args) \
    zend_call_method_with_n_args(object_pp, obj_ce, NULL, function_name, strlen(function_name), retval, param_count, args TSRMLS_CC)
  
#endif




functions.c
#include "functions.h"
  
zval* zend_call_method_with_n_args(zval **object_pp, zend_class_entry *obj_ce, zend_function **fn_proxy, char *function_name, int function_name_len, zval **retval_ptr_ptr, int param_count, zval** args TSRMLS_DC)
{
 int result;
 zend_fcall_info fci;
 zval z_fname;
 zval *retval;
 HashTable *function_table;
 zval ***params;
 params = (zval***)emalloc(sizeof(zval**)*param_count);
 int i = 0;
 for (i=0; i<param_count; i++) {
  params[i] = &args[i];
 }
 fci.size = sizeof(fci);
 /*fci.function_table = NULL; will be read form zend_class_entry of object if needed */
 fci.object_pp = object_pp;
 fci.function_name = &z_fname;
 fci.retval_ptr_ptr = retval_ptr_ptr ? retval_ptr_ptr : &retval;
 fci.param_count = param_count;
 fci.params = params;
 fci.no_separation = 1;
 fci.symbol_table = NULL;
 if (!fn_proxy && !obj_ce) {
  /* no interest in caching and no information already present that is
   * needed later inside zend_call_function. */
  ZVAL_STRINGL(&z_fname, function_name, function_name_len, 0);
  fci.function_table = !object_pp ? EG(function_table) : NULL;
  result = zend_call_function(&fci, NULL TSRMLS_CC);
 } else {
  zend_fcall_info_cache fcic;
  fcic.initialized = 1;
  if (!obj_ce) {
   obj_ce = object_pp ? Z_OBJCE_PP(object_pp) : NULL;
  }
  if (obj_ce) {
   function_table = &obj_ce->function_table;
  } else {
   function_table = EG(function_table);
  }
  if (!fn_proxy || !*fn_proxy) {
   if (zend_hash_find(function_table, function_name, function_name_len+1, (void **) &fcic.function_handler) == FAILURE) {
    /* error at c-level */
    zend_error(E_CORE_ERROR, "Couldn't find implementation for method %s%s%s", obj_ce ? obj_ce->name : "", obj_ce ? "::" : "", function_name);
   }
   if (fn_proxy) {
    *fn_proxy = fcic.function_handler;
   }
  } else {
   fcic.function_handler = *fn_proxy;
  }
  fcic.calling_scope = obj_ce;
  fcic.object_pp = object_pp;
  result = zend_call_function(&fci, &fcic TSRMLS_CC);
 }
 if (result == FAILURE) {
  /* error at c-level */
  if (!obj_ce) {
   obj_ce = object_pp ? Z_OBJCE_PP(object_pp) : NULL;
  }
  if (!EG(exception)) {
   zend_error(E_CORE_ERROR, "Couldn't execute method %s%s%s", obj_ce ? obj_ce->name : "", obj_ce ? "::" : "", function_name);
  }
 }
 if (!retval_ptr_ptr) {
  if (retval) {
   zval_ptr_dtor(&retval);
  }
  return NULL;
 }
 return *retval_ptr_ptr;
}





myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
  
#include "php.h"
  
// 클래스이름
#define CN_MYCLASS "myClass"
// 클래스 엔트리
extern zend_class_entry *myClass_ce;
// 클래스 methods
extern function_entry myClass_functions[];
  
// 클래스 method 매크로
#define MYCLASS_ME(func, arg_info, flags) PHP_ME(myClass_ce, func, arg_info, flags)
#define MYCLASS_METHOD(func) PHP_METHOD(myClass_ce, func)
  
// methods
MYCLASS_METHOD(call_self_method);
MYCLASS_METHOD(func_arg0);
MYCLASS_METHOD(func_arg1);
MYCLASS_METHOD(func_arg2);
MYCLASS_METHOD(func_arg3);
MYCLASS_METHOD(func_arg4);
  
// MINIT - myClass
PHP_MINIT_FUNCTION(myClass);
  
#endif




myclass.c
#include "myclass.h"
#include "functions.h"
  
#include "Zend/zend_interfaces.h"
  
// 클래스 엔트리
zend_class_entry *myClass_ce;
  
// 클래스 methods
function_entry myClass_functions[] = {
    MYCLASS_ME(call_self_method, NULL, ZEND_ACC_PUBLIC)
    MYCLASS_ME(func_arg0, NULL, ZEND_ACC_PUBLIC)
    MYCLASS_ME(func_arg1, NULL, ZEND_ACC_PUBLIC)
    MYCLASS_ME(func_arg2, NULL, ZEND_ACC_PUBLIC)
    MYCLASS_ME(func_arg3, NULL, ZEND_ACC_PUBLIC)
    MYCLASS_ME(func_arg4, NULL, ZEND_ACC_PUBLIC)
    {NULL, NULL, NULL}
};
  
// methods
MYCLASS_METHOD(call_self_method)
{
    int p1;
    long a1, a2, a3, a4;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|llll", &p1, &a1, &a2, &a3, &a4)==FAILURE) {
        RETURN_NULL();
    }
  
    // 호출할 method 이름 지정
    char* funcname;
    spprintf(&funcname, 0, "func_arg%d", p1);
  
    // 해당 method가 myClass에 있다면 호출
    if (zend_hash_exists(&Z_OBJCE_P(getThis())->function_table, funcname, strlen(funcname)+1)) {
        zval *args[4];
        zval *retval;
  
        // 루프문을 돌리기 위한 삽질
        long a[4];
        a[0] = a1;
        a[1] = a2;
        a[2] = a3;
        a[3] = a4;
  
        // parameter 대입
        int i;
        for (i=0; i<p1; i++) {
            MAKE_STD_ZVAL(args[i]);
            ZVAL_LONG(args[i], a[i]);
        }
  
        // method 호출
        call_method(&getThis(), Z_OBJCE_P(getThis()), funcname, &retval, p1, args);
  
        // 해제
        efree(funcname);
        for (i=0; i<p1; i++) zval_dtor(args[i]);
  
        RETURN_ZVAL(retval, 1, 0);
    }
    // 해당 method가 myClass에 없다면 NULL 반환
    else {
        efree(funcname);
        RETURN_NULL();
    }
}
  
MYCLASS_METHOD(func_arg0)
{
    RETURN_STRING("ret from func_arg0", 1);
}
  
MYCLASS_METHOD(func_arg1)
{
    int p1;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &p1)==FAILURE) {
        RETURN_NULL();
    }
  
    char *ret;
    spprintf(&ret, 0, "ret from func_args1 - %d", p1);
    ZVAL_STRING(return_value, ret, 1);
    efree(ret);
}
  
MYCLASS_METHOD(func_arg2)
{
    int p1, p2;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &p1, &p2)==FAILURE) {
        RETURN_NULL();
    }
  
    char *ret;
    spprintf(&ret, 0, "ret from func_args2 - %d, %d", p1, p2);
    ZVAL_STRING(return_value, ret, 1);
    efree(ret);
}
  
MYCLASS_METHOD(func_arg3)
{
    int p1, p2, p3;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lll", &p1, &p2, &p3)==FAILURE) {
        RETURN_NULL();
    }
  
    char *ret;
    spprintf(&ret, 0, "ret from func_args3 - %d, %d, %d", p1, p2, p3);
    ZVAL_STRING(return_value, ret, 1);
    efree(ret);
}
  
MYCLASS_METHOD(func_arg4)
{
    int p1, p2, p3, p4;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "llll", &p1, &p2, &p3, &p4)==FAILURE) {
        RETURN_NULL();
    }
  
    char *ret;
    spprintf(&ret, 0, "ret from func_args4 - %d, %d, %d, %d", p1, p2, p3, p4);
    ZVAL_STRING(return_value, ret, 1);
    efree(ret);
}
  
// MINIT - myClass
PHP_MINIT_FUNCTION(myClass)
{
    zend_class_entry ce_myClass;
    INIT_CLASS_ENTRY(ce_myClass, CN_MYCLASS, myClass_functions);
    myClass_ce = zend_register_internal_class(&ce_myClass TSRMLS_CC);
  
    return SUCCESS;
}



test.php
$c = new myClass();
var_dump($c->call_self_method(0));
var_dump($c->call_self_method(1, 1));
var_dump($c->call_self_method(2, 1, 2));
var_dump($c->call_self_method(3, 1, 2, 3));
var_dump($c->call_self_method(4, 1, 2, 3, 4));




zend_call_method_with_n_args() 함수는 별 다를 건 없고,
parameters를 구성하는 부분을 zval**의 배열에서 zval***로 바꿔서 parameter의 갯수만큼 동적으로 할당하는 방식으로 변경한 것이다.





'PHP' 카테고리의 다른 글

[PHP Extension] Super Globals  (0) 2010.09.13
[PHP Extension] C++로 제작하기 위한 config.m4  (0) 2010.09.09
[PHP Extension] lxr.php.net (OpenGrok)  (0) 2010.08.05
[PHP Extension] INI 세팅  (0) 2010.08.04
[PHP Extension] Auto Globals  (0) 2010.08.04
Posted by bloodguy
,