ZendEngine1과 ZendEngine2의 클래스는 좀 많이 다르다.
ZE2를 기준으로 한다. (그나마 클래스라고 부를 수 있지 않은가)
모든 예제의 stdafx.h 는 다음과 같다.
#pragma once
#include "zend_config.w32.h"
#include "php.h"
#include "php_myext.h"
#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
#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;
}
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");
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}
};
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
#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");
}
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");
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
[ --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
#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;
}
#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
#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;
}
// 클래스 엔트리
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");
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
#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;
}
#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
#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;
}
// 클래스 엔트리
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);
$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)
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
#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;
}
#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
#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;
}
// 클래스 엔트리
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);
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 할 수 없다는 에러가 난다.
$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());
$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 |