Add empty PHP extension skeleton from Pux for

This commit is contained in:
c9s 2014-05-16 22:47:33 +08:00
parent bee02242df
commit ec55b3741a
17 changed files with 3321 additions and 0 deletions

115
php/r3/ct_helper.c Normal file
View file

@ -0,0 +1,115 @@
#include "php.h"
#include "string.h"
#include "main/php_main.h"
#include "Zend/zend_API.h"
#include "zend_exceptions.h"
#include "zend_interfaces.h"
#include "zend_object_handlers.h"
#include "ext/pcre/php_pcre.h"
#include "ext/standard/php_string.h"
#include "ct_helper.h"
#define ZEND_FIND_FUNC(ce, name, name_len, fe) \
zend_hash_find(&ce->function_table, name, name_len, (void **) &fe)
#define ZEND_FIND_FUNC_QUICK(ce, name, name_len, fe) \
zend_hash_quick_find(&ce->function_table, name, name_len, zend_inline_hash_func(name, name_len) , (void **) &fe)
/* {{{ zend_call_method
Only returns the returned zval if retval_ptr != NULL */
ZEND_API zval* zend_call_method_with_3_params(zval **object_pp, zend_class_entry *obj_ce, zend_function **fn_proxy, const char *function_name, int function_name_len, zval **retval_ptr_ptr, int param_count, zval* arg1, zval* arg2, zval* arg3 TSRMLS_DC)
{
int result;
zend_fcall_info fci;
zval z_fname;
zval *retval;
HashTable *function_table;
zval **params[3];
params[0] = &arg1;
params[1] = &arg2;
params[2] = &arg3;
fci.size = sizeof(fci);
/*fci.function_table = NULL; will be read form zend_class_entry of object if needed */
fci.object_ptr = object_pp ? *object_pp : NULL;
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;
if (object_pp) {
fcic.called_scope = Z_OBJCE_PP(object_pp);
} else if (obj_ce &&
!(EG(called_scope) &&
instanceof_function(EG(called_scope), obj_ce TSRMLS_CC))) {
fcic.called_scope = obj_ce;
} else {
fcic.called_scope = EG(called_scope);
}
fcic.object_ptr = object_pp ? *object_pp : NULL;
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;
}
/* }}} */
char * find_place_holder(char *pattern, int pattern_len) {
char needle_char[2] = { ':', 0 };
return php_memnstr(pattern,
needle_char,
1,
pattern + pattern_len);
}

8
php/r3/ct_helper.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef PHP_CT_HELPER_H
#define PHP_CT_HELPER_H 1
ZEND_API zval* zend_call_method_with_3_params(zval **object_pp, zend_class_entry *obj_ce, zend_function **fn_proxy, const char *function_name, int function_name_len, zval **retval_ptr_ptr, int param_count, zval* arg1, zval* arg2, zval* arg3 TSRMLS_DC);
char * find_place_holder(char *pattern, int pattern_len);
#endif

110
php/r3/hash.c Normal file
View file

@ -0,0 +1,110 @@
#include "hash.h"
HashTable * zend_hash_clone_persistent(HashTable* src TSRMLS_DC)
{
zval **tmp;
return my_copy_hashtable(NULL, src, (ht_copy_fun_t) my_copy_zval_ptr, (void*) &tmp, sizeof(zval *), 1 TSRMLS_CC);
}
HashTable * zend_hash_clone(HashTable* src TSRMLS_DC)
{
zval **tmp;
return my_copy_hashtable(NULL, src, (ht_copy_fun_t) my_copy_zval_ptr, (void*) &tmp, sizeof(zval *), 0 TSRMLS_CC);
}
/**
* Recursively copy hash and all its value.
*
* This replaces zend_hash_copy
*/
HashTable * my_copy_hashtable(HashTable *target, HashTable *source, ht_copy_fun_t copy_fn, void *tmp, uint size, int persistent TSRMLS_DC)
{
Bucket *curr = NULL, *prev = NULL , *newp = NULL;
void *new_entry;
int first = 1;
assert(source != NULL);
// allocate persistent memory for target and initialize it.
if (!target) {
target = pemalloc(sizeof(source[0]), persistent);
}
memcpy(target, source, sizeof(source[0]));
target->arBuckets = pemalloc(target->nTableSize * sizeof(Bucket*), persistent);
memset(target->arBuckets, 0, target->nTableSize * sizeof(Bucket*));
target->pInternalPointer = NULL;
target->pListHead = NULL;
// since it's persistent, destructor should be NULL
target->persistent = persistent;
if (! target->persistent) {
target->pDestructor = ZVAL_PTR_DTOR;
}
curr = source->pListHead;
while (curr) {
// hash index
int n = curr->h % target->nTableSize;
// allocate new bucket
// from apc
#ifdef ZEND_ENGINE_2_4
if (!curr->nKeyLength) {
newp = (Bucket*) pemalloc(sizeof(Bucket), persistent);
memcpy(newp, curr, sizeof(Bucket));
} else if (IS_INTERNED(curr->arKey)) {
newp = (Bucket*) pemalloc(sizeof(Bucket), persistent);
memcpy(newp, curr, sizeof(Bucket));
} else {
// ugly but we need to copy
newp = (Bucket*) pemalloc(sizeof(Bucket) + curr->nKeyLength, persistent);
memcpy(newp, curr, sizeof(Bucket) + curr->nKeyLength );
newp->arKey = (const char*)(newp+1);
}
#else
newp = (Bucket*) pecalloc(1, (sizeof(Bucket) + curr->nKeyLength - 1), persistent);
memcpy(newp, curr, sizeof(Bucket) + curr->nKeyLength - 1);
#endif
/* insert 'newp' into the linked list at its hashed index */
if (target->arBuckets[n]) {
newp->pNext = target->arBuckets[n];
newp->pLast = NULL;
newp->pNext->pLast = newp;
} else {
newp->pNext = newp->pLast = NULL;
}
target->arBuckets[n] = newp;
// now we copy the bucket data using our 'copy_fn'
newp->pData = copy_fn(NULL, curr->pData, persistent TSRMLS_CC);
memcpy(&newp->pDataPtr, newp->pData, sizeof(void*));
/* insert 'newp' into the table-thread linked list */
newp->pListLast = prev;
newp->pListNext = NULL;
if (prev) {
prev->pListNext = newp;
}
if (first) {
target->pListHead = newp;
first = 0;
}
prev = newp;
curr = curr->pListNext;
}
target->pListTail = newp;
zend_hash_internal_pointer_reset(target);
// return the newly allocated memory
return target;
}

59
php/r3/hash.h Normal file
View file

@ -0,0 +1,59 @@
#ifndef HASH_H
#define HASH_H 1
#include "php.h"
#include "string.h"
#include "main/php_main.h"
#include "Zend/zend_API.h"
#include "zend_exceptions.h"
#include "zend_interfaces.h"
#include "zend_object_handlers.h"
#include "ext/pcre/php_pcre.h"
#include "ext/standard/php_string.h"
#include "php_r3.h"
#include "r3_mux.h"
#include "r3_functions.h"
#if R3_DEBUG
#define HT_OK 0
#define HT_IS_DESTROYING 1
#define HT_DESTROYED 2
#define HT_CLEANING 3
static void _zend_is_inconsistent(const HashTable *ht, const char *file, int line)
{
if (ht->inconsistent==HT_OK) {
return;
}
switch (ht->inconsistent) {
case HT_IS_DESTROYING:
zend_output_debug_string(1, "%s(%d) : ht=%p is being destroyed", file, line, ht);
break;
case HT_DESTROYED:
zend_output_debug_string(1, "%s(%d) : ht=%p is already destroyed", file, line, ht);
break;
case HT_CLEANING:
zend_output_debug_string(1, "%s(%d) : ht=%p is being cleaned", file, line, ht);
break;
default:
zend_output_debug_string(1, "%s(%d) : ht=%p is inconsistent", file, line, ht);
break;
}
zend_bailout();
}
#define IS_CONSISTENT(a) _zend_is_inconsistent(a, __FILE__, __LINE__);
#define SET_INCONSISTENT(n) ht->inconsistent = n;
#else
#define IS_CONSISTENT(a)
#define SET_INCONSISTENT(n)
#endif
HashTable * zend_hash_clone_persistent(HashTable* src TSRMLS_DC);
HashTable * zend_hash_clone(HashTable* src TSRMLS_DC);
typedef void* (*ht_copy_fun_t)(void*, void*, int TSRMLS_DC);
HashTable * my_copy_hashtable(HashTable *target, HashTable *source, ht_copy_fun_t copy_fn, void *tmp, uint size, int persistent TSRMLS_DC);
#endif

View file

@ -0,0 +1,45 @@
#include "php.h"
#include "string.h"
#include "main/php_main.h"
#include "Zend/zend_API.h"
#include "zend_exceptions.h"
#include "zend_interfaces.h"
#include "zend_object_handlers.h"
#include "ext/pcre/php_pcre.h"
#include "ext/standard/php_string.h"
#include "r3_functions.h"
#include "php_expandable_mux.h"
zend_class_entry *ce_r3_expandable_mux;
const zend_function_entry expandable_mux_methods[] = {
PHP_ABSTRACT_ME(ExpandableMux, expand, NULL)
PHP_FE_END
};
/**
* TODO: Use zend_class_implements to register Controller class.
*
* zend_class_implements(mysqli_result_class_entry TSRMLS_CC, 1, zend_ce_traversable);
*/
static int implement_expandable_mux_interface_handler(zend_class_entry *interface, zend_class_entry *implementor TSRMLS_DC)
{
if (implementor->type == ZEND_USER_CLASS &&
!instanceof_function(implementor, ce_r3_expandable_mux TSRMLS_CC)
) {
zend_error(E_ERROR, "R3\\ExpandableMux can't be implemented by user classes");
}
return SUCCESS;
}
void r3_init_expandable_mux(TSRMLS_D)
{
zend_class_entry ce_interface;
INIT_CLASS_ENTRY(ce_interface, "R3\\ExpandableMux", expandable_mux_methods);
// if(Z_TYPE_PP(current) == IS_OBJECT && instanceof_function(Z_OBJCE_PP(current), curl_CURLFile_class TSRMLS_CC))
ce_r3_expandable_mux = zend_register_internal_interface(&ce_interface TSRMLS_CC);
ce_r3_expandable_mux->interface_gets_implemented = implement_expandable_mux_interface_handler;
}

View file

@ -0,0 +1,21 @@
#ifndef PHP_EXPANDABLE_MUX_H
#define PHP_EXPANDABLE_MUX_H 1
#include "php.h"
#include "string.h"
#include "main/php_main.h"
#include "Zend/zend_API.h"
#include "Zend/zend_variables.h"
#include "zend_exceptions.h"
#include "zend_interfaces.h"
#include "zend_object_handlers.h"
#include "ext/pcre/php_pcre.h"
#include "ext/standard/php_string.h"
#include "php_r3.h"
#include "r3_functions.h"
extern zend_class_entry *ce_r3_expandable_mux;
void r3_init_expandable_mux(TSRMLS_D);
#endif

136
php/r3/php_r3.c Normal file
View file

@ -0,0 +1,136 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "string.h"
#include "main/php_main.h"
#include "Zend/zend_API.h"
#include "Zend/zend_variables.h"
#include "zend_exceptions.h"
#include "zend_interfaces.h"
#include "zend_object_handlers.h"
#include "ext/standard/php_string.h"
#include "php_r3.h"
// #include "ct_helper.h"
// #include "r3_functions.h"
// #include "r3_mux.h"
// #include "php_expandable_mux.h"
// #include "r3_controller.h"
ZEND_DECLARE_MODULE_GLOBALS(r3);
// persistent list entry type for HashTable
int le_mux_hash_table;
// persistent list entry type for boolean
int le_mux_bool;
// persistent list entry type for int
int le_mux_int;
// persistent list entry type for string
int le_mux_string;
zend_class_entry *ce_r3_exception;
// #define DEBUG 1
static const zend_function_entry r3_functions[] = {
PHP_FE(r3_match, NULL)
PHP_FE_END
};
void r3_init_exception(TSRMLS_D) {
zend_class_entry e;
INIT_CLASS_ENTRY(e, "R3Exception", NULL);
ce_r3_exception = zend_register_internal_class_ex(&e, (zend_class_entry*)zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC);
}
void r3_mux_le_hash_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
HashTable *h = (HashTable*) rsrc->ptr;
if (h) {
// zend_hash_destroy(h);
// pefree(h, 1);
}
}
zend_module_entry r3_module_entry = {
STANDARD_MODULE_HEADER,
PHP_R3_EXTNAME,
r3_functions,
PHP_MINIT(r3),
PHP_MSHUTDOWN(r3),
PHP_RINIT(r3),
NULL,
NULL,
PHP_R3_VERSION,
STANDARD_MODULE_PROPERTIES
};
PHP_INI_BEGIN()
PHP_INI_ENTRY("r3.fstat", "0", PHP_INI_ALL, NULL)
// STD_PHP_INI_ENTRY("r3.direction", "1", PHP_INI_ALL, OnUpdateBool, direction, zend_hello_globals, hello_globals)
PHP_INI_END()
#ifdef COMPILE_DL_R3
ZEND_GET_MODULE(r3)
#endif
static void php_r3_init_globals(zend_r3_globals *r3_globals)
{
// r3_globals->persistent_list = (HashTable*)
// array_init(r3_globals->persistent_list);
}
PHP_MINIT_FUNCTION(r3) {
ZEND_INIT_MODULE_GLOBALS(r3, php_r3_init_globals, NULL);
REGISTER_INI_ENTRIES();
// r3_init_mux(TSRMLS_C);
// r3_init_expandable_mux(TSRMLS_C);
// r3_init_controller(TSRMLS_C);
le_mux_hash_table = zend_register_list_destructors_ex(NULL, r3_mux_le_hash_dtor, "hash table", module_number);
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(r3) {
UNREGISTER_INI_ENTRIES();
return SUCCESS;
}
PHP_RINIT_FUNCTION(r3) {
return SUCCESS;
}
/*
* r3_compile(array $routes, string $path);
*/
PHP_FUNCTION(r3_match)
{
zval *z_routes;
char *path;
int path_len;
/* parse parameters */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "as",
&z_routes,
&path, &path_len ) == FAILURE) {
RETURN_FALSE;
}
/*
zval *z_route;
z_route = php_r3_match(z_routes, path, path_len TSRMLS_CC);
if ( z_route != NULL ) {
*return_value = *z_route;
zval_copy_ctor(return_value);
return;
}
*/
RETURN_NULL();
}

98
php/r3/php_r3.h Normal file
View file

@ -0,0 +1,98 @@
#ifndef PHP_R3_H
#define PHP_R3_H 1
#include "php.h"
#include "string.h"
#include "main/php_main.h"
#include "Zend/zend_API.h"
#include "Zend/zend_variables.h"
#include "zend_exceptions.h"
#include "zend_interfaces.h"
#include "zend_object_handlers.h"
#include "ext/standard/php_string.h"
#define PHP_R3_VERSION "1.3.1"
#define PHP_R3_EXTNAME "r3"
#ifdef ZTS
#include "TSRM.h"
#endif
extern int r3_globals_id;
extern int le_mux_hash_table;
// global variable structure
ZEND_BEGIN_MODULE_GLOBALS(r3)
// zval *mux_array;
HashTable * persistent_list;
// zend_bool direction;
ZEND_END_MODULE_GLOBALS(r3)
#ifdef ZTS
#define R3_G(v) TSRMG(r3_globals_id, zend_r3_globals *, v)
#else
#define R3_G(v) (r3_globals.v)
#endif
#define ZEND_HASH_FETCH(hash,key,ret) \
zend_hash_find(hash, key, sizeof(key), (void**)&ret) == SUCCESS
#define PUSH_PARAM(arg) zend_vm_stack_push(arg TSRMLS_CC)
#define POP_PARAM() (void)zend_vm_stack_pop(TSRMLS_C)
#define PUSH_EO_PARAM()
#define POP_EO_PARAM()
#define CALL_METHOD_BASE(classname, name) zim_##classname##_##name
#define CALL_METHOD_HELPER(classname, name, retval, thisptr, num, param) \
PUSH_PARAM(param); PUSH_PARAM((void*)num); \
PUSH_EO_PARAM(); \
CALL_METHOD_BASE(classname, name)(num, retval, NULL, thisptr, 0 TSRMLS_CC); \
POP_EO_PARAM(); \
POP_PARAM(); POP_PARAM();
#define CALL_METHOD(classname, name, retval, thisptr) \
CALL_METHOD_BASE(classname, name)(0, retval, NULL, thisptr, 0 TSRMLS_CC);
#define CALL_METHOD1(classname, name, retval, thisptr, param1) \
CALL_METHOD_HELPER(classname, name, retval, thisptr, 1, param1);
#define CALL_METHOD2(classname, name, retval, thisptr, param1, param2) \
PUSH_PARAM(param1); \
CALL_METHOD_HELPER(classname, name, retval, thisptr, 2, param2); \
POP_PARAM();
#define CALL_METHOD3(classname, name, retval, thisptr, param1, param2, param3) \
PUSH_PARAM(param1); PUSH_PARAM(param2); \
CALL_METHOD_HELPER(classname, name, retval, thisptr, 3, param3); \
POP_PARAM(); POP_PARAM();
PHP_MINIT_FUNCTION(r3);
PHP_MSHUTDOWN_FUNCTION(r3);
PHP_RINIT_FUNCTION(r3);
/*
zval * php_r3_match(zval *z_routes, char *path, int path_len TSRMLS_DC);
zval * call_mux_method(zval * object , char * method_name , int method_name_len, int param_count, zval* arg1, zval* arg2, zval* arg3 TSRMLS_DC);
zend_class_entry ** get_pattern_compiler_ce(TSRMLS_D);
extern zend_class_entry *ce_r3_exception;
*/
extern zend_module_entry r3_module_entry;
void r3_init_exception(TSRMLS_D);
void r3_mux_le_hash_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC);
PHP_FUNCTION(r3_match);
#define phpext_r3_ptr &r3_module_entry
#endif

437
php/r3/r3_controller.c Normal file
View file

@ -0,0 +1,437 @@
#include "string.h"
#include "ctype.h"
#include "php.h"
#include "main/php_main.h"
#include "Zend/zend_API.h"
#include "Zend/zend_variables.h"
#include "zend_exceptions.h"
#include "zend_interfaces.h"
#include "Zend/zend_interfaces.h"
#include "zend_object_handlers.h"
#include "ext/pcre/php_pcre.h"
#include "ext/standard/php_string.h"
#include "ext/standard/php_var.h"
#include "ct_helper.h"
#include "php_r3.h"
#include "r3_mux.h"
#include "r3_functions.h"
#include "php_expandable_mux.h"
#include "r3_controller.h"
#include "annotation/scanner.h"
#include "annotation/annot.h"
zend_class_entry *ce_r3_controller;
const zend_function_entry controller_methods[] = {
PHP_ME(Controller, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
PHP_ME(Controller, expand, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Controller, getActionMethods, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Controller, getActionRoutes, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Controller, before, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Controller, after, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Controller, toJson, NULL, ZEND_ACC_PUBLIC)
// PHP_ME(Controller, __destruct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
PHP_FE_END
};
zend_bool phannot_fetch_argument_value(zval **arg, zval** value TSRMLS_DC) {
zval **expr;
if (zend_hash_find(Z_ARRVAL_PP(arg), "expr", sizeof("expr"), (void**)&expr) == FAILURE ) {
return FAILURE;
}
return zend_hash_find(Z_ARRVAL_PP(expr), "value", sizeof("value"), (void**) value);
}
zend_bool phannot_fetch_argument_type(zval **arg, zval **type TSRMLS_DC) {
zval **expr;
if (zend_hash_find(Z_ARRVAL_PP(arg), "expr", sizeof("expr"), (void**)&expr) == FAILURE ) {
return FAILURE;
}
return zend_hash_find(Z_ARRVAL_PP(expr), "type", sizeof("type"), (void**)type);
}
int strpos(const char *haystack, char *needle)
{
char *p = strstr(haystack, needle);
if (p)
return p - haystack;
return -1;
}
void r3_init_controller(TSRMLS_D) {
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "R3\\Controller", controller_methods);
ce_r3_controller = zend_register_internal_class(&ce TSRMLS_CC);
zend_class_implements(ce_r3_controller TSRMLS_CC, 1, ce_r3_expandable_mux);
}
PHP_METHOD(Controller, __construct) {
}
/**
* Returns [ method, annotations ]
*/
PHP_METHOD(Controller, getActionMethods)
{
// get function table hash
HashTable *function_table = &Z_OBJCE_P(this_ptr)->function_table;
HashPosition pos;
array_init(return_value);
zend_function *mptr;
zend_hash_internal_pointer_reset_ex(function_table, &pos);
const char *fn;
size_t fn_len;
int p;
while (zend_hash_get_current_data_ex(function_table, (void **) &mptr, &pos) == SUCCESS) {
fn = mptr->common.function_name;
fn_len = strlen(mptr->common.function_name);
p = strpos(fn, "Action");
if ( p == -1 || (size_t)p != (fn_len - strlen("Action")) ) {
zend_hash_move_forward_ex(function_table, &pos);
continue;
}
// append the structure [method name, annotations]
zval *new_item;
zval *z_method_annotations;
zval *z_indexed_annotations;
zval *z_comment;
zval *z_file;
zval *z_line_start;
MAKE_STD_ZVAL(new_item);
array_init(new_item);
add_next_index_stringl(new_item, fn, fn_len, 1);
/*
* @var
*
* if there no annotation, we pass an empty array.
*
* The method annotation structure
*
*/
MAKE_STD_ZVAL(z_method_annotations);
array_init(z_method_annotations);
// simplified annotation information is saved to this variable.
MAKE_STD_ZVAL(z_indexed_annotations);
array_init(z_indexed_annotations);
if ( mptr->type == ZEND_USER_FUNCTION && mptr->op_array.doc_comment ) {
ALLOC_ZVAL(z_comment);
ZVAL_STRING(z_comment, mptr->op_array.doc_comment, 1);
ALLOC_ZVAL(z_file);
ZVAL_STRING(z_file, mptr->op_array.filename, 1);
ALLOC_ZVAL(z_line_start);
ZVAL_LONG(z_line_start, mptr->op_array.line_start);
/*
zval *z_line_end;
ALLOC_ZVAL(z_line_end);
ZVAL_LONG(z_line_start, mptr->op_array.line_end);
*/
zval **z_ann = NULL, **z_ann_name = NULL, **z_ann_arguments = NULL, **z_ann_argument = NULL, **z_ann_argument_value = NULL;
// TODO: make phannot_parse_annotations reads comment variable in char* type, so we don't need to create extra zval(s)
if (phannot_parse_annotations(z_method_annotations, z_comment, z_file, z_line_start TSRMLS_CC) == SUCCESS) {
/*
* z_method_annotations is an indexed array, which contains a structure like this:
*
* [
* { name => ... , type => ... , arguments => ... , file => , line => },
* { name => ... , type => ... , arguments => ... , file => , line => },
* { name => ... , type => ... , arguments => ... , file => , line => },
* ]
*
* the actuall structure:
*
* array(2) {
* [0]=>
* array(5) {
* ["type"]=>
* int(300)
* ["name"]=>
* string(5) "Route"
* ["arguments"]=>
* array(1) {
* [0]=>
* array(1) {
* ["expr"]=>
* array(2) {
* ["type"]=>
* int(303)
* ["value"]=>
* string(7) "/delete"
* }
* }
* }
* ["file"]=> string(48) "...."
* ["line"]=> int(54)
* },
* .....
* }
*/
HashPosition annp;
for (
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(z_method_annotations), &annp);
zend_hash_get_current_data_ex(Z_ARRVAL_P(z_method_annotations), (void**)&z_ann, &annp) == SUCCESS;
zend_hash_move_forward_ex(Z_ARRVAL_P(z_method_annotations), &annp)
) {
if (zend_hash_find(Z_ARRVAL_P(*z_ann), "name", sizeof("name"), (void**)&z_ann_name) == FAILURE ) {
continue;
}
if (zend_hash_find(Z_ARRVAL_P(*z_ann), "arguments", sizeof("arguments"), (void**)&z_ann_arguments) == FAILURE ) {
continue;
}
// We currenly only support "@Method()" and "@Route"
if ( strncmp( Z_STRVAL_PP(z_ann_name), "Method", sizeof("Method") - 1 ) != 0
&& strncmp( Z_STRVAL_PP(z_ann_name), "Route", sizeof("Route") - 1 ) != 0 )
{
continue;
}
// read the first argument (we only support for one argument currently, and should support complex syntax later.)
if ( zend_hash_index_find(Z_ARRVAL_PP(z_ann_arguments), 0, (void**) &z_ann_argument) == SUCCESS ) {
if ( phannot_fetch_argument_value(z_ann_argument, (zval**) &z_ann_argument_value TSRMLS_CC) == SUCCESS ) {
Z_ADDREF_PP(z_ann_argument_value);
add_assoc_zval(z_indexed_annotations, Z_STRVAL_PP(z_ann_name), *z_ann_argument_value);
}
}
}
}
}
Z_ADDREF_P(z_indexed_annotations);
add_next_index_zval(new_item, z_indexed_annotations);
add_next_index_zval(return_value, new_item);
zend_hash_move_forward_ex(function_table, &pos);
}
return;
}
char * translate_method_name_to_path(const char *method_name)
{
char *p = strstr(method_name, "Action");
if ( p == NULL ) {
return NULL;
}
if ( strncmp(method_name, "indexAction", strlen("indexAction") ) == 0 ) {
// returns empty string as its path
return strndup("",sizeof("")-1);
}
char * new_path;
// XXX: this might overflow..
new_path = ecalloc( 128 , sizeof(char) );
int len = p - method_name;
char * c = (char*) method_name;
int x = 0;
new_path[x++] = '/';
int new_path_len = 1;
while( len-- ) {
if ( isupper(*c) ) {
new_path[x++] = '/';
new_path[x++] = tolower(*c);
new_path_len += 2;
} else {
new_path[x++] = *c;
new_path_len ++;
}
c++;
}
return new_path;
}
// return path => path pairs
// structure: [[ path, method name ], [ ... ], [ ... ], ... ]
PHP_METHOD(Controller, getActionRoutes)
{
zend_function *fe;
if ( zend_hash_quick_find( &ce_r3_controller->function_table, "getactionmethods", sizeof("getactionmethods"), zend_inline_hash_func(ZEND_STRS("getactionmethods")), (void **) &fe) == FAILURE ) {
php_error(E_ERROR, "getActionMethods method not found");
}
// call export method
zval *rv = NULL;
zend_call_method_with_0_params( &this_ptr, ce_r3_controller, &fe, "getactionmethods", &rv );
HashTable *func_list = Z_ARRVAL_P(rv);
HashPosition pointer;
zval **z_method_name;
zval **z_annotations;
zval **z_doc_method;
zval **z_doc_uri;
zval **item;
array_init(return_value);
for(zend_hash_internal_pointer_reset_ex(func_list, &pointer);
zend_hash_get_current_data_ex(func_list, (void**) &item, &pointer) == SUCCESS;
zend_hash_move_forward_ex(func_list, &pointer))
{
zend_hash_index_find(Z_ARRVAL_PP(item), 0, (void**)&z_method_name);
const char *method_name = Z_STRVAL_PP(z_method_name);
int method_name_len = Z_STRLEN_PP(z_method_name);
char *path = NULL;
zval *z_route_options;
MAKE_STD_ZVAL(z_route_options);
array_init(z_route_options);
if ( zend_hash_index_find(Z_ARRVAL_PP(item), 1, (void**)&z_annotations) == SUCCESS ) {
if (zend_hash_find(Z_ARRVAL_PP(z_annotations), "Route", sizeof("Route"), (void**)&z_doc_uri) == SUCCESS) {
path = estrndup(Z_STRVAL_PP(z_doc_uri), Z_STRLEN_PP(z_doc_uri));
}
if (zend_hash_find(Z_ARRVAL_PP(z_annotations), "Method", sizeof("Method"), (void**)&z_doc_method) == SUCCESS) {
Z_ADDREF_PP(z_doc_method);
add_assoc_zval(z_route_options, "method", *z_doc_method);
}
}
if (!path) {
path = translate_method_name_to_path(method_name);
}
// return structure [ path, method name, http method ]
zval * new_item;
MAKE_STD_ZVAL(new_item);
array_init_size(new_item, 3);
add_next_index_string(new_item, path, 1);
add_next_index_stringl(new_item, method_name, method_name_len, 0);
Z_ADDREF_P(z_route_options);
add_next_index_zval(new_item, z_route_options);
// append to the result array
add_next_index_zval(return_value, new_item);
}
return;
}
PHP_METHOD(Controller, expand)
{
zval *new_mux;
MAKE_STD_ZVAL(new_mux);
object_init_ex(new_mux, ce_r3_mux);
CALL_METHOD(Mux, __construct, new_mux, new_mux);
Z_ADDREF_P(new_mux); // add reference
zval *path_array = NULL;
zend_call_method_with_0_params( &this_ptr, ce_r3_controller, NULL, "getactionroutes", &path_array );
if ( Z_TYPE_P(path_array) != IS_ARRAY ) {
php_error(E_ERROR, "getActionRoutes does not return an array.");
RETURN_FALSE;
}
const char *class_name = NULL;
zend_uint class_name_len;
int dup = zend_get_object_classname(this_ptr, &class_name, &class_name_len TSRMLS_CC);
HashPosition pointer;
zval **path_entry = NULL;
for (
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(path_array), &pointer);
zend_hash_get_current_data_ex(Z_ARRVAL_P(path_array), (void**) &path_entry, &pointer) == SUCCESS;
zend_hash_move_forward_ex(Z_ARRVAL_P(path_array), &pointer)
) {
zval **z_path;
zval **z_method;
zval **z_options = NULL;
if ( zend_hash_index_find(Z_ARRVAL_PP(path_entry), 0, (void**) &z_path) == FAILURE ) {
continue;
}
if ( zend_hash_index_find(Z_ARRVAL_PP(path_entry), 1, (void**) &z_method) == FAILURE ) {
continue;
}
zend_hash_index_find(Z_ARRVAL_PP(path_entry), 2, (void**) &z_options);
zval *z_callback;
MAKE_STD_ZVAL(z_callback);
array_init_size(z_callback, 2);
Z_ADDREF_P(z_callback);
Z_ADDREF_PP(z_method);
add_next_index_stringl(z_callback, class_name, class_name_len, 1);
add_next_index_zval(z_callback, *z_method);
zval *rv = NULL;
zend_call_method_with_3_params(&new_mux, ce_r3_mux, NULL, "add", strlen("add"), &rv, 3, *z_path, z_callback, *z_options TSRMLS_CC);
}
zval *rv = NULL;
zend_call_method_with_0_params(&new_mux, ce_r3_mux, NULL, "sort", &rv );
*return_value = *new_mux;
zval_copy_ctor(return_value);
}
PHP_METHOD(Controller, before) {
}
PHP_METHOD(Controller, after) {
}
PHP_METHOD(Controller, toJson)
{
zval *z_data;
long options = 0;
long depth = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &z_data, &options, &depth) == FAILURE) {
RETURN_FALSE;
}
zval *z_options;
zval *z_depth;
MAKE_STD_ZVAL(z_options);
MAKE_STD_ZVAL(z_depth);
ZVAL_LONG(z_depth, 0);
ZVAL_LONG(z_options, 0);
zval *rv = NULL;
// zend_call_method_with_3_params(NULL, NULL, NULL, "json_encode", sizeof("json_encode"), &rv, 3, z_data, z_options, z_depth TSRMLS_CC );
zend_call_method_with_2_params(NULL, NULL, NULL, "json_encode", &rv, z_data, z_options);
*return_value = *rv;
zval_copy_ctor(return_value);
}

35
php/r3/r3_controller.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef PHP_CONTROLLER_H
#define PHP_CONTROLLER_H 1
#include "php.h"
#include "string.h"
#include "main/php_main.h"
#include "Zend/zend_API.h"
#include "Zend/zend_variables.h"
#include "zend_exceptions.h"
#include "zend_interfaces.h"
#include "zend_object_handlers.h"
#include "ext/pcre/php_pcre.h"
#include "ext/standard/php_string.h"
#include "php_r3.h"
#include "r3_functions.h"
extern zend_class_entry *ce_r3_controller;
void r3_init_controller(TSRMLS_D);
char * translate_method_name_to_path(const char *method_name);
zend_bool phannot_fetch_argument_value(zval **arg, zval** value TSRMLS_DC);
zend_bool phannot_fetch_argument_type(zval **arg, zval **type TSRMLS_DC);
int strpos(const char *haystack, char *needle);
PHP_METHOD(Controller, __construct);
PHP_METHOD(Controller, expand);
PHP_METHOD(Controller, getActionMethods);
PHP_METHOD(Controller, getActionRoutes);
PHP_METHOD(Controller, before);
PHP_METHOD(Controller, after);
PHP_METHOD(Controller, toJson);
#endif

838
php/r3/r3_functions.c Normal file
View file

@ -0,0 +1,838 @@
/*
vim:fdm=marker:et:sw=4:ts=4:sts=4:
*/
#include "php.h"
#include "string.h"
#include "pcre.h"
#include "main/php_main.h"
#include "Zend/zend_compile.h"
#include "Zend/zend_alloc.h"
#include "Zend/zend_operators.h"
#include "Zend/zend_API.h"
#include "zend_exceptions.h"
#include "zend_interfaces.h"
#include "zend_object_handlers.h"
#include "ext/pcre/php_pcre.h"
#include "ext/standard/php_string.h"
#include "php_r3.h"
#include "r3_functions.h"
// #include "r3_persistent.h"
// #include "php_expandable_mux.h"
// #include "hash.h"
/**
* new_dst = ht_copy_fun_t(NULL, src);
*
* Can be our zval copy function
*/
/* {{{ my_copy_zval_ptr */
zval** my_copy_zval_ptr(zval** dst, const zval** src, int persistent TSRMLS_DC)
{
zval* dst_new;
assert(src != NULL);
if (!dst) {
dst = (zval**) pemalloc(sizeof(zval*), persistent);
}
CHECK(dst[0] = (zval*) pemalloc(sizeof(zval), persistent));
CHECK(dst_new = my_copy_zval(*dst, *src, persistent TSRMLS_CC));
if (dst_new != *dst) {
*dst = dst_new;
}
return dst;
}
/* }}} */
/* {{{ my_copy_zval */
zval* my_copy_zval(zval* dst, const zval* src, int persistent TSRMLS_DC)
{
zval **tmp;
assert(dst != NULL);
assert(src != NULL);
memcpy(dst, src, sizeof(zval));
/* deep copies are refcount(1), but moved up for recursive
* arrays, which end up being add_ref'd during its copy. */
Z_SET_REFCOUNT_P(dst, 1);
Z_UNSET_ISREF_P(dst);
switch (src->type & IS_CONSTANT_TYPE_MASK) {
case IS_RESOURCE:
php_error(E_ERROR, "Cannot copy resource");
break;
case IS_BOOL:
case IS_LONG:
case IS_DOUBLE:
case IS_NULL:
break;
case IS_CONSTANT:
case IS_STRING:
if (src->value.str.val) {
dst->value.str.val = pestrndup(src->value.str.val, src->value.str.len, persistent);
}
break;
case IS_ARRAY:
case IS_CONSTANT_ARRAY:
dst->value.ht = my_copy_hashtable(NULL, src->value.ht, (ht_copy_fun_t) my_copy_zval_ptr, (void*) &tmp, sizeof(zval *), persistent TSRMLS_CC);
break;
// XXX: we don't serialize object.
case IS_OBJECT:
php_error(E_ERROR, "Cannot copy Object.");
break;
#ifdef ZEND_ENGINE_2_4
case IS_CALLABLE:
php_error(E_ERROR, "Cannot copy Callable.");
// XXX: we don't serialize callbable object.
break;
#endif
default:
assert(0);
}
return dst;
}
/* }}} */
/* my_zval_copy_ctor_func {{{*/
void my_zval_copy_ctor_func(zval *zvalue ZEND_FILE_LINE_DC)
{
switch (Z_TYPE_P(zvalue) & IS_CONSTANT_TYPE_MASK) {
case IS_RESOURCE: {
TSRMLS_FETCH();
zend_list_addref(zvalue->value.lval);
}
break;
case IS_BOOL:
case IS_LONG:
case IS_NULL:
break;
case IS_CONSTANT:
case IS_STRING:
CHECK_ZVAL_STRING_REL(zvalue);
if (!IS_INTERNED(zvalue->value.str.val)) {
zvalue->value.str.val = (char *) estrndup_rel(zvalue->value.str.val, zvalue->value.str.len);
}
break;
case IS_ARRAY:
case IS_CONSTANT_ARRAY: {
zval *tmp;
HashTable *original_ht = zvalue->value.ht;
HashTable *tmp_ht = NULL;
TSRMLS_FETCH();
if (zvalue->value.ht == &EG(symbol_table)) {
return; /* do nothing */
}
ALLOC_HASHTABLE_REL(tmp_ht);
zend_hash_init(tmp_ht, zend_hash_num_elements(original_ht), NULL, ZVAL_PTR_DTOR, 0);
zend_hash_copy(tmp_ht, original_ht, (copy_ctor_func_t) my_zval_copy_ctor_func, (void *) &tmp, sizeof(zval *));
zvalue->value.ht = tmp_ht;
}
break;
case IS_OBJECT:
{
TSRMLS_FETCH();
Z_OBJ_HT_P(zvalue)->add_ref(zvalue TSRMLS_CC);
}
break;
}
Z_ADDREF_P(zvalue);
}
/*}}}*/
// my_zval_copy_ctor_persistent_func {{{
void my_zval_copy_ctor_persistent_func(zval *zvalue ZEND_FILE_LINE_DC)
{
/*
zval *orig_zvalue;
orig_zvalue = zvalue;
zvalue = pemalloc(sizeof(zval), 1);
*zvalue = *zvalue;
zval_copy_ctor(zvalue);
Z_SET_REFCOUNT_P(zvalue, 1);
Z_UNSET_ISREF_P(zvalue);
// MAKE_COPY_ZVAL(&new_zvalue, zvalue);
SEPARATE_ZVAL(&zvalue);
*/
switch (Z_TYPE_P(zvalue) & IS_CONSTANT_TYPE_MASK) {
case IS_RESOURCE:
case IS_BOOL:
case IS_LONG:
case IS_DOUBLE:
case IS_NULL:
break;
case IS_CONSTANT:
case IS_STRING:
CHECK_ZVAL_STRING_REL(zvalue);
zvalue->value.str.val = (char *) pestrndup(zvalue->value.str.val, zvalue->value.str.len, 1);
break;
case IS_ARRAY:
case IS_CONSTANT_ARRAY: {
zval *tmp;
HashTable *original_ht = zvalue->value.ht;
HashTable *tmp_ht = NULL;
TSRMLS_FETCH();
if (zvalue->value.ht == &EG(symbol_table)) {
return; /* do nothing */
}
tmp_ht = pemalloc(sizeof(HashTable), 1);
zend_hash_init(tmp_ht, zend_hash_num_elements(original_ht), NULL, ZVAL_PTR_DTOR, 1);
zend_hash_copy(tmp_ht, original_ht, (copy_ctor_func_t) my_zval_copy_ctor_persistent_func, (void *) &tmp, sizeof(zval *));
zvalue->value.ht = tmp_ht;
}
break;
case IS_OBJECT:
{
TSRMLS_FETCH();
Z_OBJ_HT_P(zvalue)->add_ref(zvalue TSRMLS_CC);
}
break;
}
Z_SET_REFCOUNT_P(zvalue, 1);
}
// }}}
int _r3_store_mux(char *name, zval * mux TSRMLS_DC)
{
zend_rsrc_list_entry new_le, *le;
// variables for copying mux object properties
char *id_key, *expand_key;
int status, id_key_len, expand_key_len;
id_key_len = spprintf(&id_key, 0, "mux_id_%s", name);
expand_key_len = spprintf(&expand_key, 0, "mux_expand_%s", name);
// Z_ADDREF_P(mux);
// make the hash table persistent
zval *prop, *tmp;
HashTable *routes, *static_routes;
prop = zend_read_property(ce_r3_mux, mux, "routes", sizeof("routes")-1, 1 TSRMLS_CC);
routes = zend_hash_clone_persistent( Z_ARRVAL_P(prop) TSRMLS_CC);
if ( ! routes ) {
php_error(E_ERROR, "Can not clone HashTable");
return FAILURE;
}
r3_persistent_store( name, "routes", le_mux_hash_table, (void*) routes TSRMLS_CC);
return SUCCESS;
prop = zend_read_property(ce_r3_mux, mux, "staticRoutes", sizeof("staticRoutes")-1, 1 TSRMLS_CC);
static_routes = zend_hash_clone_persistent( Z_ARRVAL_P(prop) TSRMLS_CC);
if ( ! static_routes ) {
php_error(E_ERROR, "Can not clone HashTable");
return FAILURE;
}
r3_persistent_store(name, "static_routes", le_mux_hash_table, (void *) static_routes TSRMLS_CC) ;
// copy ID
/*
prop = zend_read_property(ce_r3_mux, mux, "id", sizeof("id")-1, 1 TSRMLS_CC);
tmp = pemalloc(sizeof(zval), 1);
INIT_ZVAL(tmp);
Z_TYPE_P(tmp) = IS_LONG;
Z_LVAL_P(tmp) = Z_LVAL_P(prop);
Z_SET_REFCOUNT_P(tmp, 1);
r3_persistent_store( name, "id", (void*) tmp TSRMLS_CC);
*/
// We cannot copy un-expandable mux object because we don't support recursively copy for Mux object.
prop = zend_read_property(ce_r3_mux, mux, "expand", sizeof("expand")-1, 1 TSRMLS_CC);
if ( ! Z_BVAL_P(prop) ) {
php_error(E_ERROR, "We cannot copy un-expandable mux object because we don't support recursively copy for Mux object.");
}
efree(id_key);
efree(expand_key);
return SUCCESS;
}
/**
* Fetch mux related properties (hash tables) from EG(persistent_list) hash table and
* rebless a new Mux object with these properties.
*
* @return Mux object
*/
zval * _r3_fetch_mux(char *name TSRMLS_DC)
{
zval *z_id, *z_routes, *z_static_routes, *z_submux, *z_routes_by_id, *tmp;
HashTable *routes_hash;
HashTable *static_routes_hash;
// fetch related hash to this mux object.
routes_hash = (HashTable*) r3_persistent_fetch(name, "routes" TSRMLS_CC);
if ( ! routes_hash ) {
return NULL;
}
static_routes_hash = (HashTable*) r3_persistent_fetch(name, "static_routes" TSRMLS_CC);
if ( ! static_routes_hash ) {
return NULL;
}
z_id = (zval*) r3_persistent_fetch(name, "id" TSRMLS_CC);
MAKE_STD_ZVAL(z_routes);
MAKE_STD_ZVAL(z_static_routes);
MAKE_STD_ZVAL(z_routes_by_id);
MAKE_STD_ZVAL(z_submux);
Z_TYPE_P(z_routes) = IS_ARRAY;
Z_TYPE_P(z_static_routes) = IS_ARRAY;
array_init(z_routes_by_id);
array_init(z_submux);
// we need to clone hash table deeply because when Mux object returned to userspace, it will be freed.
Z_ARRVAL_P(z_routes) = zend_hash_clone(routes_hash TSRMLS_CC);
Z_ARRVAL_P(z_static_routes) = zend_hash_clone(static_routes_hash TSRMLS_CC);
// create new object and return to userspace.
zval *new_mux = NULL;
ALLOC_INIT_ZVAL(new_mux);
object_init_ex(new_mux, ce_r3_mux);
// We don't need __construct because we assign the property by ourself.
// CALL_METHOD(Mux, __construct, new_mux, new_mux);
Z_SET_REFCOUNT_P(new_mux, 1);
if ( z_id ) {
Z_ADDREF_P(z_id);
zend_update_property_long(ce_r3_mux, new_mux, "id" , sizeof("id")-1, Z_LVAL_P(z_id) TSRMLS_CC);
}
// persistent mux should always be expanded. (no recursive structure)
zend_update_property_bool(ce_r3_mux , new_mux , "expand" , sizeof("expand")-1 , 1 TSRMLS_CC);
zend_update_property(ce_r3_mux , new_mux , "routes" , sizeof("routes")-1 , z_routes TSRMLS_CC);
zend_update_property(ce_r3_mux , new_mux , "staticRoutes" , sizeof("staticRoutes")-1 , z_static_routes TSRMLS_CC);
zend_update_property(ce_r3_mux, new_mux, "routesById", sizeof("routesById")-1, z_routes_by_id TSRMLS_CC);
zend_update_property(ce_r3_mux, new_mux, "submux", sizeof("submux")-1, z_submux TSRMLS_CC);
return new_mux;
}
int mux_loader(char *path, zval *result TSRMLS_DC)
{
zend_file_handle file_handle;
zend_op_array *op_array;
char realpath[MAXPATHLEN];
if (!VCWD_REALPATH(path, realpath)) {
return FAILURE;
}
file_handle.filename = path;
file_handle.free_filename = 0;
file_handle.type = ZEND_HANDLE_FILENAME;
file_handle.opened_path = NULL;
file_handle.handle.fp = NULL;
op_array = zend_compile_file(&file_handle, ZEND_INCLUDE TSRMLS_CC);
if (op_array && file_handle.handle.stream.handle) {
int dummy = 1;
if (!file_handle.opened_path) {
file_handle.opened_path = path;
}
zend_hash_add(&EG(included_files), file_handle.opened_path, strlen(file_handle.opened_path)+1, (void *)&dummy, sizeof(int), NULL);
}
zend_destroy_file_handle(&file_handle TSRMLS_CC);
if (op_array) {
zval *local_retval_ptr = NULL;
R3_STORE_EG_ENVIRON();
EG(return_value_ptr_ptr) = &local_retval_ptr;
EG(active_op_array) = op_array;
#if ((PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION > 2)) || (PHP_MAJOR_VERSION > 5)
if (!EG(active_symbol_table)) {
zend_rebuild_symbol_table(TSRMLS_C);
}
#endif
zend_execute(op_array TSRMLS_CC);
destroy_op_array(op_array TSRMLS_CC);
efree(op_array);
if (!EG(exception)) {
if (local_retval_ptr) {
if ( result ) {
COPY_PZVAL_TO_ZVAL(*result, local_retval_ptr);
} else {
zval_ptr_dtor(EG(return_value_ptr_ptr));
}
}
}
R3_RESTORE_EG_ENVIRON();
return SUCCESS;
}
return FAILURE;
}
/*
* r3_compile(array $routes, string $path);
*/
PHP_FUNCTION(r3_match)
{
zval *z_routes;
char *path;
int path_len;
/* parse parameters */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "as",
&z_routes,
&path, &path_len ) == FAILURE) {
RETURN_FALSE;
}
zval *z_route;
z_route = php_r3_match(z_routes, path, path_len TSRMLS_CC);
if ( z_route != NULL ) {
*return_value = *z_route;
zval_copy_ctor(return_value);
return;
}
RETURN_NULL();
}
PHP_FUNCTION(r3_store_mux)
{
zval *mux;
char *name;
int name_len;
/* parse parameters */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &name, &name_len, &mux ) == FAILURE) {
RETURN_FALSE;
}
if ( _r3_store_mux(name, mux TSRMLS_CC) == SUCCESS ) {
RETURN_TRUE;
}
RETURN_FALSE;
}
PHP_FUNCTION(r3_persistent_dispatch)
{
char *ns, *filename, *path;
int ns_len, filename_len, path_len;
zval *mux = NULL;
zval *route = NULL;
zval *z_path = NULL;
/* parse parameters */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &ns, &ns_len, &filename, &filename_len, &path, &path_len) == FAILURE) {
RETURN_FALSE;
}
mux = _r3_fetch_mux(ns TSRMLS_CC);
if ( mux == NULL ) {
ALLOC_INIT_ZVAL(mux);
if ( mux_loader(filename, mux TSRMLS_CC) == FAILURE ) {
php_error(E_ERROR, "Can not load Mux object from %s", filename);
}
// TODO: compile mux and sort routes
if ( _r3_store_mux(ns, mux TSRMLS_CC) == FAILURE ) {
php_error(E_ERROR, "Can not store Mux object from %s", filename);
}
}
ALLOC_INIT_ZVAL(z_path);
ZVAL_STRINGL(z_path, path ,path_len, 1); // no copy
// XXX: pass return_value to the method call, so we don't need to copy
route = call_mux_method(mux, "dispatch" , sizeof("dispatch"), 1 , z_path, NULL, NULL TSRMLS_CC);
zval_ptr_dtor(&z_path);
if ( route ) {
*return_value = *route;
zval_copy_ctor(return_value);
return;
}
// route not found
RETURN_FALSE;
}
PHP_FUNCTION(r3_delete_mux)
{
char *name, *persistent_key;
int name_len, persistent_key_len;
zend_rsrc_list_entry *le;
/* parse parameters */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
&name, &name_len) == FAILURE) {
RETURN_FALSE;
}
persistent_key_len = spprintf(&persistent_key, 0, "mux_%s", name);
if ( zend_hash_find(&EG(persistent_list), persistent_key, persistent_key_len + 1, (void**) &le) == SUCCESS ) {
zval_ptr_dtor((zval**) &le->ptr);
zend_hash_del(&EG(persistent_list), persistent_key, persistent_key_len + 1);
efree(persistent_key);
RETURN_TRUE;
}
efree(persistent_key);
RETURN_FALSE;
}
PHP_FUNCTION(r3_fetch_mux)
{
char *name;
int name_len;
zval * mux;
/* parse parameters */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
&name, &name_len) == FAILURE) {
RETURN_FALSE;
}
mux = _r3_fetch_mux(name TSRMLS_CC);
if ( mux ) {
*return_value = *mux;
zval_copy_ctor(return_value);
return;
}
RETURN_FALSE;
}
PHP_FUNCTION(r3_sort_routes)
{
zval *a;
zval *b;
/* parse parameters */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz",
&a,
&b ) == FAILURE) {
RETURN_FALSE;
}
zval **a_pcre;
zval **a_pattern;
zval **a_compiled_pattern;
zval **a_options;
zval **b_pcre;
zval **b_pattern;
zval **b_compiled_pattern;
zval **b_options;
zend_hash_index_find( Z_ARRVAL_P(a) , 0, (void**)&a_pcre);
zend_hash_index_find( Z_ARRVAL_P(b) , 0, (void**)&b_pcre);
zend_hash_index_find( Z_ARRVAL_P(a) , 1, (void**)&a_pattern);
zend_hash_index_find( Z_ARRVAL_P(b) , 1, (void**)&b_pattern);
zend_hash_index_find( Z_ARRVAL_P(a) , 3, (void**)&a_options);
zend_hash_index_find( Z_ARRVAL_P(b) , 3, (void**)&b_options);
// return strlen($a[3]['compiled']) > strlen($b[3]['compiled']);
if ( Z_BVAL_PP(a_pcre) && Z_BVAL_PP(b_pcre) ) {
zend_hash_quick_find( Z_ARRVAL_PP(a_options) , "compiled", strlen("compiled"), zend_inline_hash_func(ZEND_STRS("compiled")), (void**)&a_compiled_pattern);
zend_hash_quick_find( Z_ARRVAL_PP(b_options) , "compiled", strlen("compiled"), zend_inline_hash_func(ZEND_STRS("compiled")), (void**)&b_compiled_pattern);
int a_len = Z_STRLEN_PP(a_compiled_pattern);
int b_len = Z_STRLEN_PP(b_compiled_pattern);
if ( a_len == b_len ) {
RETURN_LONG(0);
} else if ( a_len > b_len ) {
RETURN_LONG(-1);
} else {
RETURN_LONG(1);
}
}
else if ( Z_BVAL_PP(a_pcre) ) {
RETURN_LONG(-1);
}
else if ( Z_BVAL_PP(b_pcre) ) {
RETURN_LONG(1);
}
int a_len = Z_STRLEN_PP(a_pattern);
int b_len = Z_STRLEN_PP(b_pattern);
if ( a_len == b_len ) {
RETURN_LONG(0);
}
else if ( a_len > b_len ) {
RETURN_LONG(-1);
}
else {
RETURN_LONG(1);
}
}
//
// int zend_hash_has_key( )
//
inline zval * php_r3_match(zval *z_routes, char *path, int path_len TSRMLS_DC) {
int current_request_method = 0;
int current_https = 0;
zval * current_http_host = NULL;
HashTable *server_vars_hash = fetch_server_vars_hash(TSRMLS_C);
if ( server_vars_hash ) {
current_request_method = get_current_request_method_const(server_vars_hash TSRMLS_CC);
current_https = get_current_https(server_vars_hash TSRMLS_CC);
current_http_host = get_current_http_host(server_vars_hash TSRMLS_CC);
}
HashPosition z_routes_pointer;
// for iterating routes
zval **z_route_pp;
zval **z_is_pcre_pp; // route[0]
zval **z_pattern_pp; // route[1]
zval **z_callback_pp; // callback @ route[2]
zval **z_route_options_pp; // route[3]
pcre_cache_entry *pce; /* Compiled regular expression */
zval *pcre_ret = NULL;
zval *pcre_subpats = NULL; /* Array for subpatterns */
HashTable * z_routes_hash = Z_ARRVAL_P(z_routes);
ALLOC_INIT_ZVAL(pcre_ret); // this is required.
ALLOC_INIT_ZVAL(pcre_subpats); // also required
for(zend_hash_internal_pointer_reset_ex(z_routes_hash, &z_routes_pointer);
zend_hash_get_current_data_ex(z_routes_hash, (void**) &z_route_pp, &z_routes_pointer) == SUCCESS;
zend_hash_move_forward_ex(z_routes_hash, &z_routes_pointer))
{
zend_hash_index_find( Z_ARRVAL_PP(z_route_pp), 0, (void**) &z_is_pcre_pp);
zend_hash_index_find( Z_ARRVAL_PP(z_route_pp), 1, (void**) &z_pattern_pp);
if ( Z_BVAL_PP(z_is_pcre_pp) ) {
/* Compile regex or get it from cache. */
if ((pce = pcre_get_compiled_regex_cache(Z_STRVAL_PP(z_pattern_pp), Z_STRLEN_PP(z_pattern_pp) TSRMLS_CC)) == NULL) {
zend_throw_exception(zend_exception_get_default(TSRMLS_C), "PCRE pattern compile failed.", 0 TSRMLS_CC);
return NULL;
}
php_pcre_match_impl(pce, path, path_len, pcre_ret, pcre_subpats, 0, 0, 0, 0 TSRMLS_CC);
// not matched ?
if ( ! Z_BVAL_P(pcre_ret) ) {
continue;
}
// tell garbage collector to collect it, we need to use pcre_subpats later.
// check conditions only when route option is provided
if ( zend_hash_index_find( Z_ARRVAL_PP(z_route_pp), 3, (void**) &z_route_options_pp) == SUCCESS ) {
if ( zend_hash_num_elements(Z_ARRVAL_PP(z_route_options_pp)) ) {
if ( 0 == validate_request_method( z_route_options_pp, current_request_method TSRMLS_CC) ) {
continue;
}
if ( 0 == validate_https( z_route_options_pp, current_https TSRMLS_CC) ) {
continue;
}
if ( 0 == validate_domain( z_route_options_pp, current_http_host TSRMLS_CC) ) {
continue;
}
}
}
if ( Z_TYPE_P(pcre_subpats) == IS_NULL ) {
array_init(pcre_subpats);
}
Z_ADDREF_P(pcre_subpats);
add_assoc_zval(*z_route_options_pp , "vars" , pcre_subpats);
return *z_route_pp;
// Apply "default" value to "vars"
/*
foreach( $route['variables'] as $k ) {
if( isset($regs[$k]) ) {
$route['vars'][ $k ] = $regs[$k];
} else {
$route['vars'][ $k ] = $route['default'][ $k ];
}
}
*/
/*
zval **z_route_default;
zval **z_route_subpat_val;
if ( zend_hash_find(z_route_options_pp, "default", sizeof("default"), (void**) &z_route_default ) == FAILURE ) {
HashPosition default_pointer;
HashTable *default_hash;
variables_hash = Z_ARRVAL_PP(z_variables);
// foreach variables as var, check if url contains variable or we should apply default value
for(zend_hash_internal_pointer_reset_ex(variables_hash, &variables_pointer);
zend_hash_get_current_data_ex(variables_hash, (void**) &z_var_name, &variables_pointer) == SUCCESS;
zend_hash_move_forward_ex(variables_hash, &variables_pointer))
{
}
// if ( zend_hash_find(z_route_default, "default", sizeof("default"), (void**) &z_route_default ) == FAILURE ) {
}
*/
} else {
// normal string comparison
// pattern-prefix match
zend_hash_index_find( Z_ARRVAL_PP(z_route_pp), 2, (void**) &z_callback_pp);
if ( ( Z_TYPE_PP(z_callback_pp) == IS_LONG && strncmp(Z_STRVAL_PP( z_pattern_pp ), path, Z_STRLEN_PP(z_pattern_pp) ) == 0 )
|| strcmp(Z_STRVAL_PP( z_pattern_pp ), path ) == 0 )
{
// check conditions
if ( zend_hash_index_find( Z_ARRVAL_PP(z_route_pp), 3, (void**) &z_route_options_pp) == SUCCESS ) {
if ( zend_hash_num_elements(Z_ARRVAL_PP(z_route_options_pp)) ) {
if ( 0 == validate_request_method( z_route_options_pp, current_request_method TSRMLS_CC) ) {
continue;
}
if ( 0 == validate_https( z_route_options_pp, current_https TSRMLS_CC) ) {
continue;
}
if ( 0 == validate_domain( z_route_options_pp, current_http_host TSRMLS_CC) ) {
continue;
}
}
}
// we didn't use the pcre variables
zval_ptr_dtor(&pcre_subpats);
zval_ptr_dtor(&pcre_ret);
return *z_route_pp;
}
}
}
zval_ptr_dtor(&pcre_subpats);
zval_ptr_dtor(&pcre_ret);
return NULL;
}
inline HashTable * fetch_server_vars_hash(TSRMLS_D) {
zval **z_server_hash = NULL;
if ( zend_hash_quick_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), zend_inline_hash_func(ZEND_STRS("_SERVER")), (void **) &z_server_hash) == SUCCESS ) {
return Z_ARRVAL_PP(z_server_hash);
}
return NULL;
}
inline zval * fetch_server_var(HashTable * server_hash, char *key , int key_len TSRMLS_DC) {
zval **rv;
if ( zend_hash_find(server_hash, key, key_len, (void **) &rv) == SUCCESS ) {
return *rv;
}
return NULL;
}
inline zval * get_current_remote_addr(HashTable * server_vars_hash TSRMLS_DC) {
// REMOTE_ADDR
return fetch_server_var(server_vars_hash, "REMOTE_ADDR", sizeof("REMOTE_ADDR") TSRMLS_CC);
}
inline zval * get_current_http_host(HashTable * server_vars_hash TSRMLS_DC) {
return fetch_server_var(server_vars_hash, "HTTP_HOST", sizeof("HTTP_HOST") TSRMLS_CC);
}
inline zval * get_current_request_uri(HashTable * server_vars_hash TSRMLS_DC) {
return fetch_server_var(server_vars_hash, "REQUEST_URI", sizeof("REQUEST_URI") TSRMLS_CC);
}
inline int get_current_https(HashTable * server_vars_hash TSRMLS_DC) {
zval *https = fetch_server_var(server_vars_hash, "HTTPS", sizeof("HTTPS") TSRMLS_CC);
if ( https && Z_BVAL_P(https) ) {
return 1;
}
return 0;
}
inline zval * get_current_request_method(HashTable * server_vars_hash TSRMLS_DC) {
return fetch_server_var(server_vars_hash, "REQUEST_METHOD", sizeof("REQUEST_METHOD") TSRMLS_CC);
}
/* get request method type in constant value. {{{
*/
inline int get_current_request_method_const(HashTable * server_vars_hash TSRMLS_DC) {
char *c_request_method;
zval *z_request_method = get_current_request_method(server_vars_hash TSRMLS_CC);
if ( z_request_method ) {
c_request_method = Z_STRVAL_P(z_request_method);
if ( strncmp("GET", c_request_method , sizeof("GET") ) == 0 ) {
return REQ_METHOD_GET;
} else if ( strncmp("POST", c_request_method , sizeof("POST") ) == 0 ) {
return REQ_METHOD_POST;
} else if ( strncmp("PUT" , c_request_method , sizeof("PUT") ) == 0 ) {
return REQ_METHOD_PUT;
} else if ( strncmp("DELETE", c_request_method, sizeof("DELETE") ) == 0 ) {
return REQ_METHOD_DELETE;
} else if ( strncmp("HEAD", c_request_method, sizeof("HEAD") ) == 0 ) {
return REQ_METHOD_HEAD;
} else if ( strncmp("PATCH", c_request_method, sizeof("PATCH") ) == 0 ) {
return REQ_METHOD_HEAD;
} else if ( strncmp("OPTIONS", c_request_method, sizeof("OPTIONS") ) == 0 ) {
return REQ_METHOD_OPTIONS;
}
}
return 0;
}
// }}}
inline int validate_https(zval **z_route_options_pp, int https TSRMLS_DC)
{
zval **z_route_secure = NULL;
if ( zend_hash_quick_find( Z_ARRVAL_PP(z_route_options_pp) , "secure", sizeof("secure"), zend_inline_hash_func(ZEND_STRS("secure")), (void**) &z_route_secure ) == SUCCESS ) {
// check HTTPS flag
if ( https && ! Z_BVAL_PP(z_route_secure) ) {
return 0;
}
}
return 1;
}
inline int validate_domain(zval **z_route_options_pp, zval * http_host TSRMLS_DC)
{
zval **z_route_domain = NULL;
if ( zend_hash_quick_find( Z_ARRVAL_PP(z_route_options_pp) , "domain", sizeof("domain"), zend_inline_hash_func(ZEND_STRS("domain")), (void**) &z_route_domain ) == SUCCESS ) {
// check HTTP_HOST from $_SERVER
if ( strncmp(Z_STRVAL_PP(z_route_domain), Z_STRVAL_P(http_host), Z_STRLEN_PP(z_route_domain) ) != 0 ) {
return 0;
}
}
return 1;
}
inline int validate_request_method(zval **z_route_options_pp, int current_request_method TSRMLS_DC)
{
zval **z_route_method = NULL;
if ( zend_hash_quick_find( Z_ARRVAL_PP(z_route_options_pp) , "method", sizeof("method"), zend_inline_hash_func(ZEND_STRS("method")), (void**) &z_route_method ) == SUCCESS ) {
if ( Z_TYPE_PP(z_route_method) == IS_LONG && Z_LVAL_PP(z_route_method) != current_request_method ) {
return 0;
}
}
return 1;
}

91
php/r3/r3_functions.h Normal file
View file

@ -0,0 +1,91 @@
#ifndef PHP_R3_FUNCTIONS_H
#define PHP_R3_FUNCTIONS_H 1
#define REQ_METHOD_GET 1
#define REQ_METHOD_POST 2
#define REQ_METHOD_PUT 3
#define REQ_METHOD_DELETE 4
#define REQ_METHOD_PATCH 5
#define REQ_METHOD_HEAD 6
#define REQ_METHOD_OPTIONS 7
#include "php.h"
#include "string.h"
#include "main/php_main.h"
#include "Zend/zend_API.h"
#include "zend_exceptions.h"
#include "zend_interfaces.h"
#include "zend_object_handlers.h"
#include "ext/pcre/php_pcre.h"
#include "ext/standard/php_string.h"
#include "php_r3.h"
#include "r3_mux.h"
#include "r3_functions.h"
extern inline zval * php_r3_match(zval *z_routes, char *path, int path_len TSRMLS_DC);
extern inline int get_current_request_method_const(HashTable * server_vars_hash TSRMLS_DC);
extern inline int get_current_https(HashTable * server_vars_hash TSRMLS_DC);
extern inline HashTable * fetch_server_vars_hash(TSRMLS_D);
extern inline zval * fetch_server_var(HashTable *server_vars_hash, char *key , int key_len TSRMLS_DC);
extern inline zval * get_current_http_host(HashTable * server_vars_hash TSRMLS_DC);
extern inline zval * get_current_request_uri(HashTable * server_vars_hash TSRMLS_DC);
extern inline zval * get_current_request_method(HashTable * server_vars_hash TSRMLS_DC);
extern inline int validate_request_method(zval **z_route_options_pp, int current_request_method TSRMLS_DC);
extern inline int validate_domain(zval **z_route_options_pp, zval * http_host TSRMLS_DC);
extern inline int validate_https(zval **z_route_options_pp, int https TSRMLS_DC);
#if ((PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION > 2)) || (PHP_MAJOR_VERSION > 5)
#define R3_STORE_EG_ENVIRON() \
{ \
zval ** __old_return_value_pp = EG(return_value_ptr_ptr); \
zend_op ** __old_opline_ptr = EG(opline_ptr); \
zend_op_array * __old_op_array = EG(active_op_array);
#define R3_RESTORE_EG_ENVIRON() \
EG(return_value_ptr_ptr) = __old_return_value_pp;\
EG(opline_ptr) = __old_opline_ptr; \
EG(active_op_array) = __old_op_array; \
}
#else
#define R3_STORE_EG_ENVIRON() \
{ \
zval ** __old_return_value_pp = EG(return_value_ptr_ptr); \
zend_op ** __old_opline_ptr = EG(opline_ptr); \
zend_op_array * __old_op_array = EG(active_op_array); \
zend_function_state * __old_func_state = EG(function_state_ptr);
#define R3_RESTORE_EG_ENVIRON() \
EG(return_value_ptr_ptr) = __old_return_value_pp;\
EG(opline_ptr) = __old_opline_ptr; \
EG(active_op_array) = __old_op_array; \
EG(function_state_ptr) = __old_func_state; \
}
#endif
#define CHECK(p) { if ((p) == NULL) return NULL; }
zval* my_copy_zval(zval* dst, const zval* src, int persistent TSRMLS_DC);
zval** my_copy_zval_ptr(zval** dst, const zval** src, int persistent TSRMLS_DC);
zval * _r3_fetch_mux(char *name TSRMLS_DC);
int mux_loader(char *path, zval *result TSRMLS_DC);
int _r3_store_mux(char *name, zval * mux TSRMLS_DC) ;
void my_zval_copy_ctor_persistent_func(zval *zvalue ZEND_FILE_LINE_DC);
PHP_FUNCTION(r3_match);
PHP_FUNCTION(r3_sort_routes);
PHP_FUNCTION(r3_store_mux);
PHP_FUNCTION(r3_fetch_mux);
PHP_FUNCTION(r3_delete_mux);
PHP_FUNCTION(r3_persistent_dispatch);
#endif

1196
php/r3/r3_mux.c Normal file

File diff suppressed because it is too large Load diff

64
php/r3/r3_mux.h Normal file
View file

@ -0,0 +1,64 @@
#ifndef PHP_MUX_H
#define PHP_MUX_H 1
#include "php.h"
#include "string.h"
#include "main/php_main.h"
#include "Zend/zend_API.h"
#include "Zend/zend_variables.h"
#include "zend_exceptions.h"
#include "zend_interfaces.h"
#include "zend_object_handlers.h"
#include "ext/pcre/php_pcre.h"
#include "ext/standard/php_string.h"
#include "php_r3.h"
#include "r3_functions.h"
extern zend_class_entry *ce_r3_mux;
void r3_init_mux(TSRMLS_D);
zend_class_entry ** get_pattern_compiler_ce(TSRMLS_D);
zval * compile_route_pattern(zval *z_pattern, zval *z_options, zend_class_entry **ce_pattern_compiler TSRMLS_DC);
extern inline zval * call_mux_method(zval * object , char * method_name , int method_name_len, int param_count, zval* arg1, zval* arg2, zval* arg3 TSRMLS_DC);
extern inline void mux_add_route(INTERNAL_FUNCTION_PARAMETERS);
PHP_METHOD(Mux, __construct);
PHP_METHOD(Mux, __destruct);
PHP_METHOD(Mux, getId);
PHP_METHOD(Mux, add);
PHP_METHOD(Mux, any);
PHP_METHOD(Mux, length);
PHP_METHOD(Mux, compile);
PHP_METHOD(Mux, sort);
PHP_METHOD(Mux, appendRoute);
PHP_METHOD(Mux, appendPCRERoute);
PHP_METHOD(Mux, setRoutes);
PHP_METHOD(Mux, getRoutes);
PHP_METHOD(Mux, getRoute);
PHP_METHOD(Mux, match);
PHP_METHOD(Mux, dispatch);
PHP_METHOD(Mux, getSubMux);
PHP_METHOD(Mux, getRequestMethodConstant);
PHP_METHOD(Mux, export);
PHP_METHOD(Mux, mount);
PHP_METHOD(Mux, get);
PHP_METHOD(Mux, post);
PHP_METHOD(Mux, put);
PHP_METHOD(Mux, delete);
PHP_METHOD(Mux, head);
PHP_METHOD(Mux, patch);
PHP_METHOD(Mux, options);
PHP_METHOD(Mux, __set_state);
// static method
PHP_METHOD(Mux, generate_id);
#endif

54
php/r3/r3_persistent.c Normal file
View file

@ -0,0 +1,54 @@
#include "php.h"
#include "php_r3.h"
#include "r3_persistent.h"
#include "php_expandable_mux.h"
inline int persistent_store(char *key, int key_len, int list_type, void * val TSRMLS_DC)
{
zend_rsrc_list_entry new_le;
zend_rsrc_list_entry *le;
// store it if it's not in persistent_list
if ( zend_hash_find(&EG(persistent_list), key, key_len + 1, (void**) &le) == SUCCESS ) {
zend_hash_del(&EG(persistent_list), key, key_len + 1);
}
new_le.type = list_type;
new_le.ptr = val;
return zend_hash_update(&EG(persistent_list), key, key_len + 1, (void *) &new_le, sizeof(zend_rsrc_list_entry), NULL);
}
inline void * persistent_fetch(char *key, int key_len TSRMLS_DC)
{
zend_rsrc_list_entry *le;
if ( zend_hash_find(&EG(persistent_list), key, key_len + 1, (void**) &le) == SUCCESS ) {
return le->ptr;
}
return NULL;
}
inline void * r3_persistent_fetch(char *ns, char *key TSRMLS_DC)
{
char *newkey;
int newkey_len;
void *ptr;
newkey_len = spprintf(&newkey, 0, "r3_%s_%s", ns, key);
ptr = persistent_fetch(newkey, newkey_len TSRMLS_CC);
efree(newkey);
return ptr;
}
/*
* Store persistent value with r3 namespace.
*/
inline int r3_persistent_store(char *ns, char *key, int list_type, void * val TSRMLS_DC)
{
char *newkey;
int newkey_len;
int status;
newkey_len = spprintf(&newkey, 0, "r3_%s_%s", ns, key);
status = persistent_store(newkey, newkey_len, list_type, val TSRMLS_CC);
efree(newkey);
return status;
}

10
php/r3/r3_persistent.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef R3_PERSISTENT_H
#define R3_PERSISTENT_H 1
extern inline int persistent_store(char *key, int key_len, int list_type, void * val TSRMLS_DC);
extern inline int r3_persistent_store(char *ns, char *key, int list_type, void * val TSRMLS_DC) ;
extern inline void * persistent_fetch(char *key, int key_len TSRMLS_DC);
extern inline void * r3_persistent_fetch(char *ns, char *key TSRMLS_DC);
#endif

View file

@ -42,3 +42,7 @@
1400245281,6205337.09 1400245281,6205337.09
1400245289,6217947.43 1400245289,6217947.43
1400245311,6423151.81 1400245311,6423151.81
1400251162,5826610.20
1400251172,6039034.62
1400251256,6219533.32
1400251264,6015717.76

1 1400242718 5649455.80
42 1400245281 6205337.09
43 1400245289 6217947.43
44 1400245311 6423151.81
45 1400251162 5826610.20
46 1400251172 6039034.62
47 1400251256 6219533.32
48 1400251264 6015717.76