r3/php/r3/r3_mux.c

1196 lines
43 KiB
C

#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 "ext/standard/php_var.h"
#include "ext/standard/php_smart_str.h"
#include "ext/standard/php_array.h"
#include "php_r3.h"
#include "ct_helper.h"
#include "r3_functions.h"
#include "r3_controller.h"
#include "r3_mux.h"
zend_class_entry *ce_r3_mux;
const zend_function_entry mux_methods[] = {
PHP_ME(Mux, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
PHP_ME(Mux, __destruct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
PHP_ME(Mux, getId, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, add, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, any, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, compile, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, sort, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, dispatch, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, length, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, mount, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, appendRoute, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, appendPCRERoute, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, match, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, getRoutes, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, setRoutes, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, getRoute, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, getSubMux, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, getRequestMethodConstant, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, export, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, get, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, post, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, put, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, delete, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, head, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, patch, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, options, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Mux, __set_state, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
PHP_ME(Mux, generate_id, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
PHP_FE_END
};
void r3_init_mux(TSRMLS_D) {
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "R3\\Mux", mux_methods);
ce_r3_mux = zend_register_internal_class(&ce TSRMLS_CC);
zend_declare_property_null(ce_r3_mux, "id", strlen("id"), ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_null(ce_r3_mux, "routes", strlen("routes"), ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_null(ce_r3_mux, "routesById", strlen("routesById"), ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_null(ce_r3_mux, "staticRoutes", strlen("staticRoutes"), ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_null(ce_r3_mux, "submux", strlen("submux"), ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_bool(ce_r3_mux, "expand", strlen("expand"), 1, ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_long(ce_r3_mux, "id_counter", strlen("id_counter"), 0, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC TSRMLS_CC);
}
zend_class_entry ** get_pattern_compiler_ce(TSRMLS_D) {
zend_class_entry **ce_pattern_compiler = NULL;
if ( zend_lookup_class( "R3\\PatternCompiler", strlen("R3\\PatternCompiler") , &ce_pattern_compiler TSRMLS_CC) == FAILURE ) {
php_error(E_ERROR, "Class R3\\PatternCompiler not found.");
return NULL;
}
if ( ce_pattern_compiler == NULL || *ce_pattern_compiler == NULL ) {
php_error(E_ERROR, "Class R3\\PatternCompiler not found.");
return NULL;
}
return ce_pattern_compiler;
}
// Returns compiled route zval
zval * compile_route_pattern(zval *z_pattern, zval *z_options, zend_class_entry **ce_pattern_compiler TSRMLS_DC)
{
// zend_class_entry **ce_pattern_compiler;
if ( ce_pattern_compiler == NULL ) {
ce_pattern_compiler = get_pattern_compiler_ce(TSRMLS_C);
if ( ce_pattern_compiler == NULL ) {
return NULL;
}
}
zval *z_compiled_route = NULL; // will be an array
zend_call_method( NULL, *ce_pattern_compiler, NULL, "compile", strlen("compile"), &z_compiled_route, 2, z_pattern, z_options TSRMLS_CC );
if ( z_compiled_route == NULL ) {
return NULL;
} else if ( Z_TYPE_P(z_compiled_route) == IS_NULL ) {
zval_ptr_dtor(&z_compiled_route);
return NULL;
}
if ( Z_TYPE_P(z_compiled_route) != IS_ARRAY ) {
zval_ptr_dtor(&z_compiled_route);
return NULL;
}
return z_compiled_route;
}
static void var_export(zval *return_value, zval *what TSRMLS_DC)
{
smart_str buf = {0};
php_var_export_ex(&what, 0, &buf TSRMLS_CC);
smart_str_0 (&buf);
ZVAL_STRINGL(return_value, buf.c, buf.len, 0);
}
PHP_METHOD(Mux, __construct) {
zval *z_routes = NULL, *z_routes_by_id , *z_submux = NULL, *z_static_routes = NULL;
MAKE_STD_ZVAL(z_routes);
MAKE_STD_ZVAL(z_routes_by_id);
MAKE_STD_ZVAL(z_static_routes);
MAKE_STD_ZVAL(z_submux);
array_init(z_routes);
array_init(z_routes_by_id);
array_init(z_static_routes);
array_init(z_submux);
zend_update_property(ce_r3_mux, this_ptr, "routes", sizeof("routes")-1, z_routes TSRMLS_CC);
zend_update_property(ce_r3_mux, this_ptr, "routesById", sizeof("routesById")-1, z_routes_by_id TSRMLS_CC);
zend_update_property(ce_r3_mux, this_ptr, "staticRoutes", sizeof("staticRoutes")-1, z_static_routes TSRMLS_CC);
zend_update_property(ce_r3_mux, this_ptr, "submux", sizeof("submux")-1, z_submux TSRMLS_CC);
}
PHP_METHOD(Mux, __destruct) {
zval * val;
val = zend_read_property(ce_r3_mux, getThis(), "routes", sizeof("routes")-1, 1 TSRMLS_CC);
zval_ptr_dtor(&val);
val = zend_read_property(ce_r3_mux, getThis(), "routesById", sizeof("routesById")-1, 1 TSRMLS_CC);
zval_ptr_dtor(&val);
val = zend_read_property(ce_r3_mux, getThis(), "staticRoutes", sizeof("staticRoutes")-1, 1 TSRMLS_CC);
zval_ptr_dtor(&val);
val = zend_read_property(ce_r3_mux, getThis(), "submux", sizeof("submux")-1, 1 TSRMLS_CC);
zval_ptr_dtor(&val);
}
PHP_METHOD(Mux, generate_id) {
zval * z_counter = NULL;
long counter = 0;
z_counter = zend_read_static_property(ce_r3_mux, "id_counter", strlen("id_counter") , 0 TSRMLS_CC);
if ( z_counter != NULL ) {
counter = Z_LVAL_P(z_counter);
}
counter++;
Z_LVAL_P(z_counter) = counter;
RETURN_LONG(counter);
}
PHP_METHOD(Mux, __set_state) {
zval *z_array;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &z_array) == FAILURE) {
RETURN_FALSE;
}
object_init_ex(return_value, ce_r3_mux);
// XXX: put this back if we have problem
// CALL_METHOD(Mux, __construct, return_value, return_value);
zval **z_id = NULL;
zval **z_routes = NULL;
zval **z_static_routes = NULL;
zval **z_routes_by_id = NULL;
zval **z_submux = NULL;
zval **z_expand = NULL;
if ( zend_hash_quick_find(Z_ARRVAL_P(z_array), "id", sizeof("id"), zend_inline_hash_func(ZEND_STRS("id")), (void**)&z_id) == SUCCESS ) {
zend_update_property(ce_r3_mux, return_value, "id", sizeof("id")-1, *z_id TSRMLS_CC);
}
if ( zend_hash_quick_find(Z_ARRVAL_P(z_array), "routes", sizeof("routes"), zend_inline_hash_func(ZEND_STRS("routes")), (void**)&z_routes) == SUCCESS ) {
Z_ADDREF_PP(z_routes);
zend_update_property(ce_r3_mux, return_value, "routes", sizeof("routes")-1, *z_routes TSRMLS_CC);
}
if ( zend_hash_quick_find(Z_ARRVAL_P(z_array), "staticRoutes", sizeof("staticRoutes"), zend_inline_hash_func(ZEND_STRS("staticRoutes")), (void**)&z_static_routes) == SUCCESS ) {
Z_ADDREF_PP(z_static_routes);
zend_update_property(ce_r3_mux, return_value, "staticRoutes", sizeof("staticRoutes")-1, *z_static_routes TSRMLS_CC);
}
if ( zend_hash_quick_find(Z_ARRVAL_P(z_array), "routesById", sizeof("routesById"), zend_inline_hash_func(ZEND_STRS("routesById")), (void**)&z_routes_by_id) == SUCCESS ) {
Z_ADDREF_PP(z_routes_by_id);
zend_update_property(ce_r3_mux, return_value, "routesById", sizeof("routesById")-1, *z_routes_by_id TSRMLS_CC);
}
if ( zend_hash_quick_find(Z_ARRVAL_P(z_array), "submux", sizeof("submux"), zend_inline_hash_func(ZEND_STRS("submux")), (void**)&z_submux) == SUCCESS ) {
Z_ADDREF_PP(z_submux);
zend_update_property(ce_r3_mux, return_value, "submux", sizeof("submux")-1, *z_submux TSRMLS_CC);
}
if ( zend_hash_quick_find(Z_ARRVAL_P(z_array), "expand", sizeof("expand"), zend_inline_hash_func(ZEND_STRS("expand")), (void**)&z_expand) == SUCCESS ) {
zend_update_property(ce_r3_mux, return_value, "expand", sizeof("expand")-1, *z_expand TSRMLS_CC);
}
}
/**
* get_mux_function_entry("method", sizeof("method"), zend_inline_hash_func(ZEND_STRS("pattern")));
*/
inline zend_function * get_mux_function_entry(char * method_name, int method_name_len, ulong h) {
zend_function *fe;
if ( zend_hash_quick_find( &ce_r3_mux->function_table, method_name, method_name_len, h, (void **) &fe) == SUCCESS ) {
return fe;
}
php_error(E_ERROR, "%s method not found", method_name);
return NULL;
}
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)
{
zend_function *fe;
if ( zend_hash_find( &ce_r3_mux->function_table, method_name, method_name_len, (void **) &fe) == FAILURE ) {
php_error(E_ERROR, "%s method not found", method_name);
}
// call export method
zval *z_retval = NULL;
zend_call_method_with_3_params( &object, ce_r3_mux, &fe, method_name, method_name_len, &z_retval, param_count, arg1, arg2, arg3 TSRMLS_CC );
return z_retval;
}
PHP_METHOD(Mux, get) {
zval *z_pattern = NULL, *z_callback = NULL, *z_options = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|a", &z_pattern, &z_callback, &z_options) == FAILURE) {
RETURN_FALSE;
}
if ( z_options == NULL ) {
MAKE_STD_ZVAL(z_options);
array_init_size(z_options, 1);
} else if ( Z_TYPE_P(z_options) == IS_NULL ) {
array_init_size(z_options, 1);
}
// $options['method'] = REQ_METHOD_GET;
add_assoc_long(z_options, "method", REQ_METHOD_GET);
// $this->add($pattern, $callback, $options);
zval * z_retval = call_mux_method( this_ptr, "add" , sizeof("add"), 3 , z_pattern, z_callback, z_options TSRMLS_CC);
if ( z_retval ) {
*return_value = *z_retval;
zval_copy_ctor(return_value);
}
// zval_ptr_dtor(&z_retval);
}
PHP_METHOD(Mux, put) {
zval *z_pattern = NULL, *z_callback = NULL, *z_options = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|a", &z_pattern, &z_callback, &z_options) == FAILURE) {
RETURN_FALSE;
}
if ( z_options == NULL ) {
MAKE_STD_ZVAL(z_options);
array_init_size(z_options, 1);
} else if ( Z_TYPE_P(z_options) == IS_NULL ) {
array_init_size(z_options, 1);
}
add_assoc_long(z_options, "method", REQ_METHOD_PUT);
// $this->add($pattern, $callback, $options);
zval * z_retval = call_mux_method( getThis(), "add" , sizeof("add"), 3 , z_pattern, z_callback, z_options TSRMLS_CC);
if ( z_retval ) {
*return_value = *z_retval;
zval_copy_ctor(return_value);
}
// zval_ptr_dtor(&z_retval);
}
PHP_METHOD(Mux, delete) {
zval *z_pattern = NULL, *z_callback = NULL, *z_options = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|a", &z_pattern, &z_callback, &z_options) == FAILURE) {
RETURN_FALSE;
}
if ( z_options == NULL ) {
MAKE_STD_ZVAL(z_options);
array_init_size(z_options, 1);
} else if ( Z_TYPE_P(z_options) == IS_NULL ) {
array_init_size(z_options, 1);
}
add_assoc_long(z_options, "method", REQ_METHOD_DELETE);
// $this->add($pattern, $callback, $options);
zval * z_retval = call_mux_method( getThis(), "add" , sizeof("add"), 3 , z_pattern, z_callback, z_options TSRMLS_CC);
if ( z_retval ) {
*return_value = *z_retval;
zval_copy_ctor(return_value);
}
// zval_ptr_dtor(&z_retval);
}
PHP_METHOD(Mux, post) {
zval *z_pattern = NULL, *z_callback = NULL, *z_options = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|a", &z_pattern, &z_callback, &z_options) == FAILURE) {
RETURN_FALSE;
}
if ( z_options == NULL ) {
MAKE_STD_ZVAL(z_options);
array_init_size(z_options, 1);
} else if ( Z_TYPE_P(z_options) == IS_NULL ) {
array_init_size(z_options, 1);
}
add_assoc_long(z_options, "method", REQ_METHOD_POST);
// $this->add($pattern, $callback, $options);
zval * z_retval = call_mux_method( getThis(), "add" , sizeof("add"), 3 , z_pattern, z_callback, z_options TSRMLS_CC);
if ( z_retval ) {
*return_value = *z_retval;
zval_copy_ctor(return_value);
}
}
PHP_METHOD(Mux, patch) {
zval *z_pattern = NULL, *z_callback = NULL, *z_options = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|a", &z_pattern, &z_callback, &z_options) == FAILURE) {
RETURN_FALSE;
}
if ( z_options == NULL ) {
MAKE_STD_ZVAL(z_options);
array_init_size(z_options, 1);
} else if ( Z_TYPE_P(z_options) == IS_NULL ) {
array_init_size(z_options, 1);
}
add_assoc_long(z_options, "method", REQ_METHOD_PATCH);
// $this->add($pattern, $callback, $options);
zval * z_retval = call_mux_method( getThis(), "add" , sizeof("add"), 3 , z_pattern, z_callback, z_options TSRMLS_CC);
if ( z_retval ) {
*return_value = *z_retval;
zval_copy_ctor(return_value);
}
}
PHP_METHOD(Mux, head) {
zval *z_pattern = NULL, *z_callback = NULL, *z_options = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|a", &z_pattern, &z_callback, &z_options) == FAILURE) {
RETURN_FALSE;
}
if ( z_options == NULL ) {
MAKE_STD_ZVAL(z_options);
array_init_size(z_options, 1);
} else if ( Z_TYPE_P(z_options) == IS_NULL ) {
array_init_size(z_options, 1);
}
add_assoc_long(z_options, "method", REQ_METHOD_HEAD);
// $this->add($pattern, $callback, $options);
zval * z_retval = call_mux_method( getThis(), "add" , sizeof("add"), 3 , z_pattern, z_callback, z_options TSRMLS_CC);
if ( z_retval ) {
*return_value = *z_retval;
zval_copy_ctor(return_value);
}
}
PHP_METHOD(Mux, options) {
zval *z_pattern = NULL, *z_callback = NULL, *z_options = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|a", &z_pattern, &z_callback, &z_options) == FAILURE) {
RETURN_FALSE;
}
if ( z_options == NULL ) {
MAKE_STD_ZVAL(z_options);
array_init_size(z_options, 1);
} else if ( Z_TYPE_P(z_options) == IS_NULL ) {
array_init_size(z_options, 1);
}
add_assoc_long(z_options, "method", REQ_METHOD_OPTIONS);
// $this->add($pattern, $callback, $options);
zval * z_retval = call_mux_method( getThis(), "add" , sizeof("add"), 3 , z_pattern, z_callback, z_options TSRMLS_CC);
if ( z_retval ) {
*return_value = *z_retval;
zval_copy_ctor(return_value);
}
}
PHP_METHOD(Mux, mount) {
char *pattern;
int pattern_len;
zval *z_mux = NULL;
zval *z_options = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|a", &pattern, &pattern_len, &z_mux, &z_options) == FAILURE) {
RETURN_FALSE;
}
if ( Z_TYPE_P(z_mux) == IS_NULL ) {
RETURN_FALSE;
}
if ( Z_TYPE_P(z_mux) == IS_OBJECT ) {
if ( instanceof_function( Z_OBJCE_P(z_mux), ce_r3_controller TSRMLS_CC) ) {
zval *rv;
zend_call_method(&z_mux, ce_r3_controller, NULL, "expand", strlen("expand"), &rv, 0, NULL, NULL TSRMLS_CC );
z_mux = rv;
}
}
Z_ADDREF_P(z_mux);
if ( z_options == NULL ) {
MAKE_STD_ZVAL(z_options);
array_init(z_options);
} else if ( Z_TYPE_P(z_options) == IS_NULL ) {
array_init(z_options);
}
zend_class_entry **ce_pattern_compiler = get_pattern_compiler_ce(TSRMLS_C);
if ( ce_pattern_compiler == NULL ) {
RETURN_FALSE;
}
zval *z_routes;
zval *z_expand;
zval *z_mux_routes;
z_routes = zend_read_property( ce_r3_mux, getThis(), "routes", sizeof("routes")-1, 1 TSRMLS_CC);
z_expand = zend_read_property( ce_r3_mux, getThis(), "expand", sizeof("expand")-1, 1 TSRMLS_CC);
// TODO: merge routesById and staticRoutes properties
if ( Z_BVAL_P(z_expand) ) {
// fetch routes from $mux
//
z_mux_routes = zend_read_property( ce_r3_mux, z_mux, "routes", sizeof("routes")-1, 1 TSRMLS_CC);
HashPosition route_pointer;
HashTable *mux_routes_hash;
mux_routes_hash = Z_ARRVAL_P(z_mux_routes);
zval **z_mux_route;
// iterate mux
for(zend_hash_internal_pointer_reset_ex(mux_routes_hash, &route_pointer);
zend_hash_get_current_data_ex(mux_routes_hash, (void**) &z_mux_route, &route_pointer) == SUCCESS;
zend_hash_move_forward_ex(mux_routes_hash, &route_pointer))
{
// zval for new route
zval *z_new_routes;
// zval for route item
zval **z_is_pcre; // route[0]
zval **z_route_pattern;
zval **z_route_callback;
zval **z_route_options;
zval **z_route_original_pattern; // for PCRE pattern
if ( zend_hash_index_find( Z_ARRVAL_PP(z_mux_route), 0, (void**) &z_is_pcre) == FAILURE ) {
continue;
}
if ( zend_hash_index_find( Z_ARRVAL_PP(z_mux_route), 1, (void**) &z_route_pattern) == FAILURE ) {
continue;
}
if ( zend_hash_index_find( Z_ARRVAL_PP(z_mux_route), 2, (void**) &z_route_callback) == FAILURE ) {
continue;
}
if ( zend_hash_index_find( Z_ARRVAL_PP(z_mux_route), 3, (void**) &z_route_options) == FAILURE ) {
continue;
}
// Z_ADDREF_P(z_route_callback);
// Z_ADDREF_P(z_route_options); // reference it so it will not be recycled.
MAKE_STD_ZVAL(z_new_routes);
array_init(z_new_routes);
if ( Z_BVAL_PP(z_is_pcre) ) {
// $newPattern = $pattern . $route[3]['pattern'];
if ( zend_hash_quick_find( Z_ARRVAL_PP(z_route_options), "pattern", sizeof("pattern"), zend_inline_hash_func(ZEND_STRS("pattern")), (void**) &z_route_original_pattern) == FAILURE ) {
php_error( E_ERROR, "Can not compile pattern, original pattern not found");
}
char new_pattern[120] = { 0 };
int new_pattern_len;
strncat( new_pattern, pattern , pattern_len );
strncat( new_pattern, Z_STRVAL_PP(z_route_original_pattern) , Z_STRLEN_PP(z_route_original_pattern) );
new_pattern_len = pattern_len + Z_STRLEN_PP(z_route_original_pattern);
zval *z_new_pattern = NULL;
MAKE_STD_ZVAL(z_new_pattern);
ZVAL_STRINGL(z_new_pattern, new_pattern, new_pattern_len, 1);
// TODO: merge options
// $routeArgs = PatternCompiler::compile($newPattern,
// array_merge_recursive($route[3], $options) );
zval *z_compiled_route = compile_route_pattern(z_new_pattern, *z_route_options, ce_pattern_compiler TSRMLS_CC);
if ( z_compiled_route == NULL || Z_TYPE_P(z_compiled_route) == IS_NULL ) {
php_error( E_ERROR, "Cannot compile pattern: %s", new_pattern);
}
zval **z_compiled_route_pattern;
if ( zend_hash_quick_find( Z_ARRVAL_P(z_compiled_route) , "compiled", sizeof("compiled"), zend_inline_hash_func(ZEND_STRS("compiled")), (void**)&z_compiled_route_pattern) == FAILURE ) {
php_error( E_ERROR, "compiled pattern not found: %s", new_pattern);
}
zend_hash_quick_update( Z_ARRVAL_P(z_compiled_route), "pattern", sizeof("pattern"), zend_inline_hash_func(ZEND_STRS("pattern")), &z_new_pattern, sizeof(zval *), NULL);
Z_ADDREF_PP(z_compiled_route_pattern);
Z_ADDREF_PP(z_route_callback);
Z_ADDREF_P(z_compiled_route);
Z_ADDREF_P(z_new_routes);
// create new route and append to mux->routes
add_index_bool(z_new_routes, 0 , 1); // pcre flag == false
add_index_zval(z_new_routes, 1, *z_compiled_route_pattern);
add_index_zval(z_new_routes, 2 , *z_route_callback);
add_index_zval(z_new_routes, 3, z_compiled_route);
add_next_index_zval(z_routes, z_new_routes);
} else {
// $this->routes[] = array(
// false,
// $pattern . $route[1],
// $route[2],
// $options,
// );
char new_pattern[120] = { 0 };
strncat(new_pattern, pattern, pattern_len);
strncat(new_pattern, Z_STRVAL_PP(z_route_pattern), Z_STRLEN_PP(z_route_pattern) );
int new_pattern_len = pattern_len + Z_STRLEN_PP(z_route_pattern);
// Merge the mount options with the route options
zval *z_new_route_options;
MAKE_STD_ZVAL(z_new_route_options);
array_init(z_new_route_options);
php_array_merge(Z_ARRVAL_P(z_new_route_options), Z_ARRVAL_P(z_options), 0 TSRMLS_CC);
php_array_merge(Z_ARRVAL_P(z_new_route_options), Z_ARRVAL_P(*z_route_options), 0 TSRMLS_CC);
Z_ADDREF_PP(z_route_callback);
Z_ADDREF_P(z_new_route_options);
Z_ADDREF_P(z_new_routes);
/* make the array: [ pcreFlag, pattern, callback, options ] */
add_index_bool(z_new_routes, 0 , 0); // pcre flag == false
add_index_stringl(z_new_routes, 1 , new_pattern , new_pattern_len, 1);
add_index_zval( z_new_routes, 2 , *z_route_callback);
add_index_zval( z_new_routes, 3 , z_new_route_options);
add_next_index_zval(z_routes, z_new_routes);
}
}
} else {
zend_function *fe_getid = NULL; // method entry
zend_function *fe_add = NULL; // method entry
if ( zend_hash_quick_find(&ce_r3_mux->function_table, "getid", sizeof("getid"), zend_inline_hash_func(ZEND_STRS("getid")), (void **) &fe_getid) == FAILURE ) {
php_error(E_ERROR, "Cannot call method Mux::getid()");
RETURN_FALSE;
}
if ( zend_hash_quick_find(&ce_r3_mux->function_table, "add", sizeof("add"), zend_inline_hash_func(ZEND_STRS("add")), (void **) &fe_add) == FAILURE ) {
php_error(E_ERROR, "Cannot call method Mux::add()");
RETURN_FALSE;
}
// $muxId = $mux->getId();
// $this->add($pattern, $muxId, $options);
// $this->submux[ $muxId ] = $mux;
// zval *z_mux_id = call_mux_method(z_mux, "getid", sizeof("getid") , );
zval *z_mux_id = NULL;
zend_call_method( &z_mux, ce_r3_mux, &fe_getid, "getid", strlen("getid"), &z_mux_id, 0, NULL, NULL TSRMLS_CC );
if ( z_mux_id == NULL || Z_TYPE_P(z_mux_id) == IS_NULL ) {
php_error(E_ERROR, "Mux id is required. got NULL.");
}
// create pattern
zval *z_pattern = NULL;
MAKE_STD_ZVAL(z_pattern);
ZVAL_STRINGL(z_pattern, pattern, pattern_len, 1); // duplicate
zval *z_retval = NULL;
zend_call_method_with_3_params( &this_ptr, ce_r3_mux, &fe_add, "add",
strlen("add"), &z_retval, 3, z_pattern, z_mux_id, z_options TSRMLS_CC);
zval *z_submux_array = zend_read_property( ce_r3_mux, this_ptr , "submux", sizeof("submux") - 1, 1 TSRMLS_CC);
add_index_zval(z_submux_array, Z_LVAL_P(z_mux_id) , z_mux);
// release zvals
zval_ptr_dtor(&z_mux_id);
// zval_ptr_dtor(&z_pattern);
}
}
PHP_METHOD(Mux, getSubMux) {
long submux_id = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &submux_id ) == FAILURE) {
RETURN_FALSE;
}
zval *z_submux_array;
zval **z_submux;
z_submux_array = zend_read_property( Z_OBJCE_P(this_ptr), this_ptr, "submux", sizeof("submux")-1, 1 TSRMLS_CC);
if ( zend_hash_index_find( Z_ARRVAL_P(z_submux_array), submux_id , (void**) &z_submux) == SUCCESS ) {
*return_value = **z_submux;
zval_copy_ctor(return_value);
return;
}
RETURN_FALSE;
}
PHP_METHOD(Mux, getRequestMethodConstant) {
char *req_method = NULL, *mthit, *mthp;
long req_method_const = 0;
long req_method_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &req_method, &req_method_len) != FAILURE) {
mthit = mthp = estrndup(req_method, strlen(req_method));
while(( *mthit = toupper(*mthit))) mthit++;
if (strcmp(mthp, "GET") == 0) {
req_method_const = REQ_METHOD_GET;
} else if (strcmp(mthp, "POST") == 0) {
req_method_const = REQ_METHOD_POST;
} else if (strcmp(mthp, "PUT") == 0) {
req_method_const = REQ_METHOD_PUT;
} else if (strcmp(mthp, "DELETE") == 0) {
req_method_const = REQ_METHOD_DELETE;
} else if (strcmp(mthp, "HEAD") == 0) {
req_method_const = REQ_METHOD_HEAD;
} else if (strcmp(mthp, "OPTIONS") == 0) {
req_method_const = REQ_METHOD_OPTIONS;
} else if (strcmp(mthp, "PATCH") == 0) {
req_method_const = REQ_METHOD_PATCH;
}
efree(req_method);
}
RETURN_LONG(req_method_const);
}
PHP_METHOD(Mux, getRoute) {
char * route_id = NULL;
int route_id_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &route_id, &route_id_len ) == FAILURE) {
RETURN_FALSE;
}
zval *z_routes_by_id = NULL;
zval **z_route = NULL;
z_routes_by_id = zend_read_property( ce_r3_mux , this_ptr, "routesById", sizeof("routesById")-1, 1 TSRMLS_CC);
// php_var_dump(&z_routes_by_id, 1 TSRMLS_CC);
if ( zend_hash_find( Z_ARRVAL_P(z_routes_by_id) , route_id, route_id_len + 1, (void**) &z_route ) == SUCCESS ) {
*return_value = **z_route;
zval_copy_ctor(return_value);
return;
}
RETURN_NULL();
}
PHP_METHOD(Mux, setRoutes) {
zval * routes;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &routes ) == FAILURE) {
RETURN_FALSE;
}
zend_update_property(ce_r3_mux , this_ptr, "routes", sizeof("routes")-1, routes TSRMLS_CC);
RETURN_TRUE;
}
PHP_METHOD(Mux, getRoutes) {
zval *z_routes;
z_routes = zend_read_property(ce_r3_mux, this_ptr, "routes", sizeof("routes")-1, 1 TSRMLS_CC);
*return_value = *z_routes;
zval_copy_ctor(return_value);
}
PHP_METHOD(Mux, export) {
smart_str buf = {0};
php_var_export_ex(&this_ptr, 0, &buf TSRMLS_CC);
smart_str_0 (&buf);
RETVAL_STRINGL(buf.c, buf.len, 0);
}
PHP_METHOD(Mux, getId) {
zval *z_id;
long counter = 0;
z_id = zend_read_property(ce_r3_mux, getThis(), "id", sizeof("id")-1, 1 TSRMLS_CC);
if ( z_id != NULL && Z_TYPE_P(z_id) != IS_NULL ) {
RETURN_LONG( Z_LVAL_P(z_id) );
}
zval *rv = NULL;
zend_call_method( NULL, ce_r3_mux, NULL, "generate_id", strlen("generate_id"), &rv, 0, NULL, NULL TSRMLS_CC );
if ( rv ) {
counter = Z_LVAL_P(rv);
zend_update_property_long(ce_r3_mux, this_ptr, "id" , sizeof("id") - 1, counter TSRMLS_CC);
zval_ptr_dtor(&rv);
}
RETURN_LONG(counter);
}
PHP_METHOD(Mux, length) {
zval *z_routes;
z_routes = zend_read_property(ce_r3_mux, this_ptr, "routes", sizeof("routes")-1, 1 TSRMLS_CC);
long length = zend_hash_num_elements( Z_ARRVAL_P(z_routes) );
RETURN_LONG(length);
}
PHP_METHOD(Mux, sort) {
zval *z_routes;
z_routes = zend_read_property(Z_OBJCE_P(this_ptr) , this_ptr, "routes", sizeof("routes")-1, 1 TSRMLS_CC);
zval *retval_ptr = NULL;
zval *z_sort_callback = NULL;
MAKE_STD_ZVAL(z_sort_callback);
ZVAL_STRING( z_sort_callback, "r3_sort_routes" , 1 );
Z_SET_ISREF_P(z_routes);
zend_call_method( NULL, NULL, NULL, "usort", strlen("usort"), &retval_ptr, 2,
z_routes, z_sort_callback TSRMLS_CC );
zval_ptr_dtor(&z_sort_callback);
if (retval_ptr) {
zval_ptr_dtor(&retval_ptr);
}
}
PHP_METHOD(Mux, compile) {
char *filename;
int filename_len;
zend_bool sort_before_compile = 1;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &filename, &filename_len, &sort_before_compile) == FAILURE) {
RETURN_FALSE;
}
if (sort_before_compile) {
zval *z_routes;
z_routes = zend_read_property(ce_r3_mux, this_ptr, "routes", sizeof("routes")-1, 1 TSRMLS_CC);
Z_SET_ISREF_P(z_routes);
// duplicated code to sort method
zval *rv = NULL;
zval *z_sort_callback = NULL;
MAKE_STD_ZVAL(z_sort_callback);
ZVAL_STRING( z_sort_callback, "r3_sort_routes" , 1 );
zend_call_method( NULL, NULL, NULL, "usort", strlen("usort"), &rv, 2,
z_routes, z_sort_callback TSRMLS_CC );
zval_ptr_dtor(&z_sort_callback); // recycle sort callback zval
// php_error(E_ERROR,"route sort failed.");
// zend_update_property(ce_r3_mux, getThis(), "routes", sizeof("routes")-1, z_routes TSRMLS_CC);
}
// $code = '<?php return ' . $this->export() . ';';
// get export method function entry
zend_function *fe_export;
if ( zend_hash_quick_find( &Z_OBJCE_P(this_ptr)->function_table, "export", sizeof("export"), zend_inline_hash_func(ZEND_STRS("export")), (void **) &fe_export) == FAILURE ) {
php_error(E_ERROR, "export method not found");
}
// call export method
zval *compiled_code = NULL;
zend_call_method( &this_ptr, Z_OBJCE_P(this_ptr) , &fe_export, "export", strlen("export"), &compiled_code, 0, NULL, NULL TSRMLS_CC );
if ( compiled_code == NULL || Z_TYPE_P(compiled_code) == IS_NULL ) {
php_error(E_ERROR, "Cannot compile routes.");
}
int buf_len = Z_STRLEN_P(compiled_code) + strlen("<?php return ;") + 1;
char *buf = (char* ) ecalloc(buf_len, sizeof(char));
strncat(buf, "<?php return ", strlen("<?php return ") );
strncat(buf, Z_STRVAL_P(compiled_code), Z_STRLEN_P(compiled_code));
strncat(buf, ";", 1);
*(buf + buf_len - 1) = '\0';
// memcpytrncat(buf, "\0", 1);
zval *z_code = NULL;
zval *z_filename = NULL;
zval *retval = NULL;
MAKE_STD_ZVAL(z_code);
MAKE_STD_ZVAL(z_filename);
ZVAL_STRING(z_code, buf, 1);
// CHECK_ZVAL_STRING(z_code);
ZVAL_STRINGL(z_filename, filename, filename_len, 1);
zend_call_method( NULL, NULL, NULL, "file_put_contents", strlen("file_put_contents"), &retval, 2, z_filename, z_code TSRMLS_CC );
zval_ptr_dtor(&z_filename);
zval_ptr_dtor(&z_code);
zval_ptr_dtor(&compiled_code);
if (retval) {
*return_value = *retval;
zval_copy_ctor(return_value);
}
}
PHP_METHOD(Mux, dispatch) {
char *path;
int path_len;
zval *z_path;
zval *z_return_route = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &path, &path_len) == FAILURE) {
RETURN_FALSE;
}
MAKE_STD_ZVAL(z_path);
ZVAL_STRINGL(z_path, path, path_len, 1);
zend_function *fe; // method entry
zend_hash_quick_find( &ce_r3_mux->function_table, "match", sizeof("match"), zend_inline_hash_func(ZEND_STRS("match")), (void **) &fe);
zend_call_method( &this_ptr, ce_r3_mux, &fe, "match", strlen("match"), &z_return_route, 1, z_path, NULL TSRMLS_CC );
if ( ! z_return_route || Z_TYPE_P(z_return_route) == IS_NULL ) {
zval_ptr_dtor(&z_path);
RETURN_NULL();
}
// read data from matched route
zval **z_pcre;
zval **z_pattern;
zval **z_callback;
zval **z_options;
zend_hash_index_find( Z_ARRVAL_P(z_return_route) , 0 , (void**) &z_pcre );
zend_hash_index_find( Z_ARRVAL_P(z_return_route) , 1 , (void**) &z_pattern );
zend_hash_index_find( Z_ARRVAL_P(z_return_route) , 2 , (void**) &z_callback );
zend_hash_index_find( Z_ARRVAL_P(z_return_route) , 3 , (void**) &z_options );
zval *z_submux_array = NULL;
zval **z_submux = NULL;
zval *z_retval = NULL;
// dispatch to submux if the callback is an ID.
if ( Z_TYPE_PP(z_callback) == IS_LONG ) {
z_submux_array = zend_read_property( ce_r3_mux, this_ptr, "submux", sizeof("submux")-1, 1 TSRMLS_CC);
if ( z_submux_array == NULL ) {
zend_throw_exception(ce_r3_exception, "submux property is null", 0 TSRMLS_CC);
return;
}
if ( zend_hash_index_find( Z_ARRVAL_P(z_submux_array), Z_LVAL_PP(z_callback) , (void**) &z_submux) == FAILURE ) {
zend_throw_exception(ce_r3_exception, "submux not found", 0 TSRMLS_CC);
return;
}
if ( z_submux == NULL || *z_submux == NULL ) {
zend_throw_exception(ce_r3_exception, "submux not found", 0 TSRMLS_CC);
return;
}
// php_var_dump(z_submux, 1 TSRMLS_CC);
// $matchedString = $route[3]['vars'][0];
// return $submux->dispatch(substr($path, strlen($matchedString))
if ( Z_BVAL_PP(z_pcre) ) {
zval **z_route_vars = NULL;
zval **z_route_vars_0 = NULL;
zval *z_substr;
if ( zend_hash_quick_find( Z_ARRVAL_PP(z_options) , "vars", sizeof("vars"), zend_inline_hash_func(ZEND_STRS("vars")), (void**) &z_route_vars ) == FAILURE ) {
php_error(E_ERROR, "require route vars");
RETURN_FALSE;
}
if ( zend_hash_index_find( Z_ARRVAL_PP(z_options) , 0 , (void**) &z_route_vars_0 ) == FAILURE ) {
php_error(E_ERROR, "require route vars[0]");
RETURN_FALSE;
}
MAKE_STD_ZVAL(z_substr);
ZVAL_STRING(z_substr, path + Z_STRLEN_PP(z_route_vars_0), 1);
z_retval = call_mux_method( *z_submux, "dispatch" , sizeof("dispatch"), 1 , z_substr, NULL, NULL TSRMLS_CC);
zval_ptr_dtor(&z_substr);
if (z_retval) {
Z_ADDREF_P(z_retval);
*return_value = *z_retval;
zval_copy_ctor(return_value);
}
// zval_ptr_dtor(&z_return_route);
RETURN_FALSE;
return;
} else {
zval *z_substr;
MAKE_STD_ZVAL(z_substr);
ZVAL_STRING(z_substr, path + Z_STRLEN_PP(z_pattern), 1);
// return $submux->dispatch(
// substr($path, strlen($route[1]))
// );
z_retval = call_mux_method( *z_submux, "dispatch" , sizeof("dispatch"), 1 , z_substr, NULL, NULL TSRMLS_CC);
zval_ptr_dtor(&z_substr);
if ( z_retval ) {
Z_ADDREF_P(z_retval);
*return_value = *z_retval;
zval_copy_ctor(return_value);
}
// zval_ptr_dtor(&z_return_route);
return;
}
}
if ( z_return_route ) {
*return_value = *z_return_route;
zval_copy_ctor(return_value);
}
zval_ptr_dtor(&z_path);
zval_ptr_dtor(&z_return_route);
return;
}
PHP_METHOD(Mux, match) {
char *path;
int path_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &path, &path_len) == FAILURE) {
RETURN_FALSE;
}
zval **z_route_pp = NULL;
zval *z_route = NULL;
if ( zend_hash_find( Z_ARRVAL_P( zend_read_property(ce_r3_mux, this_ptr, "staticRoutes", sizeof("staticRoutes") - 1, 1 TSRMLS_CC) ), path, path_len, (void**)&z_route_pp) == SUCCESS ) {
if ( Z_TYPE_PP(z_route_pp) != IS_NULL ) {
*return_value = **z_route_pp;
Z_ADDREF_PP(z_route_pp);
zval_copy_ctor(return_value);
return;
}
}
z_route = php_r3_match(zend_read_property(ce_r3_mux , this_ptr , "routes", sizeof("routes")-1, 1 TSRMLS_CC), path, path_len TSRMLS_CC);
if ( z_route != NULL ) {
*return_value = *z_route;
zval_copy_ctor(return_value);
Z_ADDREF_P(z_route);
return;
}
RETURN_NULL();
}
PHP_METHOD(Mux, appendRoute) {
char *pattern;
int pattern_len;
zval *z_callback;
zval *z_options;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|a", &pattern, &pattern_len, &z_callback, &z_options) == FAILURE) {
RETURN_FALSE;
}
zval *z_routes;
zval *z_new_routes;
if ( z_options == NULL ) {
MAKE_STD_ZVAL(z_options);
array_init(z_options);
}
if ( Z_TYPE_P(z_options) == IS_NULL ) {
array_init(z_options);
}
z_routes = zend_read_property(Z_OBJCE_P(this_ptr), this_ptr, "routes", sizeof("routes")-1, 1 TSRMLS_CC);
MAKE_STD_ZVAL(z_new_routes);
array_init_size(z_new_routes, 4);
add_index_bool(z_new_routes, 0 , 0); // pcre flag == false
add_index_stringl( z_new_routes, 1 , pattern , pattern_len, 1);
add_index_zval( z_new_routes, 2 , z_callback);
add_index_zval( z_new_routes, 3 , z_options);
add_next_index_zval(z_routes, z_new_routes);
}
PHP_METHOD(Mux, appendPCRERoute) {
char *pattern;
int pattern_len;
zval *z_callback;
zval *z_options;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|a", &pattern, &pattern_len, &z_callback, &z_options) == FAILURE) {
RETURN_FALSE;
}
if ( z_options == NULL ) {
MAKE_STD_ZVAL(z_options);
array_init(z_options);
} else if ( Z_TYPE_P(z_options) == IS_NULL ) {
array_init(z_options);
}
zval *z_pattern = NULL;
zval *z_routes;
MAKE_STD_ZVAL(z_pattern);
ZVAL_STRINGL(z_pattern, pattern, pattern_len, 1);
z_routes = zend_read_property(Z_OBJCE_P(this_ptr), getThis(), "routes", sizeof("routes")-1, 1 TSRMLS_CC);
zend_class_entry **ce_pattern_compiler = get_pattern_compiler_ce(TSRMLS_C);
if ( ce_pattern_compiler == NULL ) {
RETURN_FALSE;
}
zval *rv = NULL;
zend_call_method( NULL, *ce_pattern_compiler, NULL, "compile", strlen("compile"), &rv, 1, z_pattern, NULL TSRMLS_CC );
if ( rv == NULL || Z_TYPE_P(rv) == IS_NULL ) {
zend_throw_exception(ce_r3_exception, "Can not compile route pattern", 0 TSRMLS_CC);
RETURN_FALSE;
}
add_next_index_zval(z_routes, rv);
}
inline void mux_add_route(INTERNAL_FUNCTION_PARAMETERS)
{
char *pattern;
int pattern_len;
zval *z_callback = NULL;
zval *z_options = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|a", &pattern, &pattern_len, &z_callback, &z_options) == FAILURE) {
RETURN_FALSE;
}
// $pcre = strpos($pattern,':') !== false;
char *found = find_place_holder(pattern, pattern_len);
if ( z_options == NULL ) {
MAKE_STD_ZVAL(z_options);
array_init(z_options);
} else if ( Z_TYPE_P(z_options) == IS_NULL ) {
// make it as an array
array_init(z_options);
}
// Generalize callback variable
if ( Z_TYPE_P(z_callback) == IS_STRING ) {
if ( strpos( Z_STRVAL_P(z_callback), ":" ) != -1 ) {
zval *delim;
MAKE_STD_ZVAL(delim);
ZVAL_STRINGL(delim, ":" , 1 , 1);
zval *rv;
MAKE_STD_ZVAL(rv);
array_init(rv);
php_explode(delim, z_callback, rv, 2);
z_callback = rv;
// zval_copy_ctor(z_callback);
zval_ptr_dtor(&delim);
}
}
zval * z_routes = zend_read_property(ce_r3_mux, this_ptr, "routes", sizeof("routes")-1, 1 TSRMLS_CC);
// PCRE pattern here
if ( found ) {
zval *z_pattern = NULL;
MAKE_STD_ZVAL(z_pattern);
ZVAL_STRINGL(z_pattern, pattern, pattern_len, 1);
zval *z_compiled_route = compile_route_pattern(z_pattern, z_options, NULL TSRMLS_CC);
if ( z_compiled_route == NULL ) {
zend_throw_exception(ce_r3_exception, "Unable to compile route pattern.", 0 TSRMLS_CC);
RETURN_FALSE;
}
zval_ptr_dtor(&z_pattern);
zval **z_compiled_route_pattern;
if ( zend_hash_quick_find( Z_ARRVAL_P(z_compiled_route) , "compiled", sizeof("compiled"), zend_inline_hash_func(ZEND_STRS("compiled")), (void**)&z_compiled_route_pattern) == FAILURE ) {
zend_throw_exception(ce_r3_exception, "Unable to find compiled pattern.", 0 TSRMLS_CC);
RETURN_FALSE;
}
Z_ADDREF_P(z_callback);
zval *z_new_routes;
MAKE_STD_ZVAL(z_new_routes);
array_init_size(z_new_routes, 4);
add_index_bool(z_new_routes, 0 , 1); // pcre flag == false
add_index_zval(z_new_routes, 1, *z_compiled_route_pattern);
add_index_zval(z_new_routes, 2 , z_callback);
add_index_zval(z_new_routes, 3, z_compiled_route);
add_next_index_zval(z_routes, z_new_routes);
zval **z_route_id;
if ( zend_hash_quick_find( Z_ARRVAL_P(z_options) , "id", sizeof("id"), zend_inline_hash_func(ZEND_STRS("id")), (void**)&z_route_id) == SUCCESS ) {
zval * z_routes_by_id = zend_read_property(ce_r3_mux, this_ptr, "routesById", sizeof("routesById")-1, 1 TSRMLS_CC);
add_assoc_zval(z_routes_by_id, Z_STRVAL_PP(z_route_id), z_new_routes);
}
} else {
Z_ADDREF_P(z_options); // reference it so it will not be recycled.
Z_ADDREF_P(z_callback);
zval *z_new_route;
MAKE_STD_ZVAL(z_new_route);
array_init_size(z_new_route, 4);
/* make the array: [ pcreFlag, pattern, callback, options ] */
add_index_bool(z_new_route, 0 , 0); // pcre flag == false
add_index_stringl( z_new_route, 1 , pattern, pattern_len , 1 );
add_index_zval( z_new_route, 2 , z_callback);
add_index_zval( z_new_route, 3 , z_options);
add_next_index_zval(z_routes, z_new_route);
// if there is no option specified in z_options, we can add the route to our static route hash
if ( zend_hash_num_elements(Z_ARRVAL_P(z_options)) ) {
zval * z_static_routes = zend_read_property(ce_r3_mux, this_ptr, "staticRoutes", sizeof("staticRoutes")-1, 1 TSRMLS_CC);
if ( z_static_routes ) {
add_assoc_zval(z_static_routes, pattern, z_new_route);
}
}
zval **z_route_id;
if ( zend_hash_quick_find( Z_ARRVAL_P(z_options) , "id", sizeof("id"), zend_inline_hash_func(ZEND_STRS("id")), (void**)&z_route_id) == SUCCESS ) {
zval * z_routes_by_id = zend_read_property(ce_r3_mux, this_ptr, "routesById", sizeof("routesById")-1, 1 TSRMLS_CC);
/*
zval *id_route = NULL;
ALLOC_ZVAL(id_route);
ZVAL_COPY_VALUE(id_route, z_new_route);
zval_copy_ctor(id_route);
add_assoc_zval(z_routes_by_id, Z_STRVAL_PP(z_route_id), id_route);
*/
add_assoc_zval(z_routes_by_id, Z_STRVAL_PP(z_route_id), z_new_route);
}
}
}
PHP_METHOD(Mux, add) {
mux_add_route(INTERNAL_FUNCTION_PARAM_PASSTHRU);
}
PHP_METHOD(Mux, any) {
mux_add_route(INTERNAL_FUNCTION_PARAM_PASSTHRU);
}