r3/php/r3/r3_functions.c
c9s c3ef959539 Add route namespace
Summary:

    type route now becomes R3Route

Related issue #63
2015-11-18 14:16:26 +08:00

839 lines
27 KiB
C

/*
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_R3Route = php_r3_match(z_routes, path, path_len TSRMLS_CC);
if ( z_R3Route != 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 *R3Route = 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
R3Route = call_mux_method(mux, "dispatch" , sizeof("dispatch"), 1 , z_path, NULL, NULL TSRMLS_CC);
zval_ptr_dtor(&z_path);
if ( R3Route ) {
*return_value = *route;
zval_copy_ctor(return_value);
return;
}
// R3Route 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 R3Route 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;
}