Add empty PHP extension skeleton from Pux for
This commit is contained in:
parent
bee02242df
commit
ec55b3741a
17 changed files with 3321 additions and 0 deletions
115
php/r3/ct_helper.c
Normal file
115
php/r3/ct_helper.c
Normal 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
8
php/r3/ct_helper.h
Normal 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
110
php/r3/hash.c
Normal 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
59
php/r3/hash.h
Normal 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
|
45
php/r3/php_expandable_mux.c
Normal file
45
php/r3/php_expandable_mux.c
Normal 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;
|
||||
}
|
||||
|
21
php/r3/php_expandable_mux.h
Normal file
21
php/r3/php_expandable_mux.h
Normal 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
136
php/r3/php_r3.c
Normal 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
98
php/r3/php_r3.h
Normal 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
437
php/r3/r3_controller.c
Normal 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
35
php/r3/r3_controller.h
Normal 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
838
php/r3/r3_functions.c
Normal 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
91
php/r3/r3_functions.h
Normal 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
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
64
php/r3/r3_mux.h
Normal 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
54
php/r3/r3_persistent.c
Normal 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
10
php/r3/r3_persistent.h
Normal 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
|
|
@ -42,3 +42,7 @@
|
|||
1400245281,6205337.09
|
||||
1400245289,6217947.43
|
||||
1400245311,6423151.81
|
||||
1400251162,5826610.20
|
||||
1400251172,6039034.62
|
||||
1400251256,6219533.32
|
||||
1400251264,6015717.76
|
||||
|
|
|
Loading…
Reference in a new issue