1 /*
2   +----------------------------------------------------------------------+
3   | Yet Another Framework                                                |
4   +----------------------------------------------------------------------+
5   | This source file is subject to version 3.01 of the PHP license,      |
6   | that is bundled with this package in the file LICENSE, and is        |
7   | available through the world-wide-web at the following url:           |
8   | http://www.php.net/license/3_01.txt                                  |
9   | If you did not receive a copy of the PHP license and are unable to   |
10   | obtain it through the world-wide-web, please send a note to          |
11   | license@php.net so we can mail you a copy immediately.               |
12   +----------------------------------------------------------------------+
13   | Author: Xinchen Hui  <laruence@php.net>                              |
14   +----------------------------------------------------------------------+
15 */
16 
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20 
21 #include "php.h"
22 #include "Zend/zend_smart_str.h" /* for smart_str */
23 #include "Zend/zend_interfaces.h" /* for zend_class_serialize_deny */
24 
25 #include "php_yaf.h"
26 #include "yaf_namespace.h"
27 #include "yaf_exception.h"
28 #include "yaf_request.h"
29 
30 #include "yaf_router.h"
31 #include "routes/yaf_route_interface.h"
32 #include "routes/yaf_route_map.h"
33 
34 zend_class_entry *yaf_route_map_ce;
35 static zend_object_handlers yaf_route_map_obj_handlers;
36 
37 /** {{{ ARG_INFO
38  */
39 ZEND_BEGIN_ARG_INFO_EX(yaf_route_map_construct_arginfo, 0, 0, 0)
40     ZEND_ARG_INFO(0, controller_prefer)
41 	ZEND_ARG_INFO(0, delimiter)
ZEND_END_ARG_INFO()42 ZEND_END_ARG_INFO()
43 /* }}} */
44 
45 static HashTable *yaf_route_map_get_properties(yaf_object *object) /* {{{ */ {
46 	zval rv;
47 	HashTable *ht;
48 	yaf_route_map_object *map = (yaf_route_map_object*)(yaf_strip_obj(object));
49 
50 	if (!map->properties) {
51 		ALLOC_HASHTABLE(map->properties);
52 		zend_hash_init(map->properties, 2, NULL, ZVAL_PTR_DTOR, 0);
53 		YAF_ALLOW_VIOLATION(map->properties);
54 
55 		ht = map->properties;
56 
57 		ZVAL_BOOL(&rv, map->flags & YAF_ROUTE_MAP_CTL_PREFER);
58 		zend_hash_str_add(ht, "ctl_prefer:protected", sizeof("ctl_prefer:protected") - 1, &rv);
59 
60 		if (map->delim) {
61 			ZVAL_STR_COPY(&rv, map->delim);
62 		} else {
63 			ZVAL_NULL(&rv);
64 		}
65 		zend_hash_str_add(ht, "delimiter:protected", sizeof("delimiter:protected") - 1, &rv);
66 	}
67 
68 	return map->properties;
69 }
70 /* }}} */
71 
yaf_route_map_new(zend_class_entry * ce)72 static zend_object *yaf_route_map_new(zend_class_entry *ce) /* {{{ */ {
73 	yaf_route_map_object *map = emalloc(sizeof(yaf_route_map_object));
74 
75 	zend_object_std_init(&map->std, ce);
76 
77 	map->std.handlers = &yaf_route_map_obj_handlers;
78 	map->delim = NULL;
79 	map->properties = NULL;
80 
81 	return &map->std;
82 }
83 /* }}} */
84 
yaf_route_map_object_free(zend_object * object)85 static void yaf_route_map_object_free(zend_object *object) /* {{{ */ {
86 	yaf_route_map_object *map = (yaf_route_map_object*)object;
87 
88 	if (map->delim) {
89 		zend_string_release(map->delim);
90 	}
91 
92 	if (map->properties) {
93 		if (GC_DELREF(map->properties) == 0) {
94 			GC_REMOVE_FROM_BUFFER(map->properties);
95 			zend_array_destroy(map->properties);
96 		}
97 	}
98 
99 	zend_object_std_dtor(&map->std);
100 }
101 /* }}} */
102 
yaf_route_map_init(yaf_route_map_object * map,zend_bool ctl_prefer,zend_string * delim)103 void yaf_route_map_init(yaf_route_map_object *map, zend_bool ctl_prefer, zend_string *delim) /* {{{ */{
104 	map->flags = ctl_prefer? YAF_ROUTE_MAP_CTL_PREFER : 0;
105 	if (delim && ZSTR_LEN(delim)) {
106 		map->delim = zend_string_copy(delim);
107 	} else {
108 		map->delim = NULL;
109 	}
110 }
111 /* }}} */
112 
yaf_route_map_instance(yaf_route_t * route,zend_bool ctl_prefer,zend_string * delim)113 void yaf_route_map_instance(yaf_route_t *route, zend_bool ctl_prefer, zend_string *delim) /* {{{ */{
114 	zend_object *map = yaf_route_map_new(yaf_route_map_ce);
115 
116 	yaf_route_map_init((yaf_route_map_object*)map, ctl_prefer, delim);
117 
118 	ZVAL_OBJ(route, map);
119 }
120 /* }}} */
121 
yaf_route_map_build(const char * src,size_t len,zend_bool ctl)122 static inline zend_string *yaf_route_map_build(const char *src, size_t len, zend_bool ctl) /* {{{ */ {
123 	unsigned char *str = (unsigned char*)src;
124 	unsigned char *end = str + len;
125 
126 	while (*str == YAF_ROUTER_URL_DELIMIETER) {
127 		str++;
128 	}
129 
130 	if (str < end) {
131 		unsigned char *p, *q, *e;
132 		zend_string *result = zend_string_alloc(end - str, 0);
133 
134 		zend_str_tolower_copy(ZSTR_VAL(result), str, end - str);
135 		p = q = (unsigned char*)ZSTR_VAL(result);
136 		e = p + ZSTR_LEN(result);
137 
138 		if (ctl) {
139 			*p++ = toupper(*q++);
140 		} else {
141 			*p++, *q++;
142 		}
143 		while (q < e) {
144 			if (*q == YAF_ROUTER_URL_DELIMIETER) {
145 				while (*(++q) == YAF_ROUTER_URL_DELIMIETER);
146 				if (UNEXPECTED(*q == '\0')) {
147 					break;
148 				}
149 				*p++ = '_';
150 				if (ctl) {
151 					*p++ = toupper(*q++);
152 				} else {
153 					*p++ = *q++;
154 				}
155 			} else {
156 				*p++ = *q++;
157 			}
158 		}
159 		*p = '\0';
160 
161 		ZSTR_LEN(result) = p - (unsigned char*)ZSTR_VAL(result);
162 
163 		return result;
164 	}
165 
166 	return NULL;
167 }
168 /* }}} */
169 
yaf_route_map_route(yaf_route_t * route,yaf_request_t * req)170 int yaf_route_map_route(yaf_route_t *route, yaf_request_t *req) /* {{{ */ {
171 	size_t len, query_len;
172 	const char *uri, *query, *p;
173 	yaf_request_object *request = Z_YAFREQUESTOBJ_P(req);
174 	yaf_route_map_object *map = Z_YAFROUTEMAPOBJ_P(route);
175 
176 	if (request->base_uri) {
177 		uri = yaf_request_strip_base_uri(request->uri, request->base_uri, &len);
178 	} else {
179 		uri = ZSTR_VAL(request->uri);
180 		len = ZSTR_LEN(request->uri);
181 	}
182 
183 	if (UNEXPECTED(map->delim)) {
184 		if ((query = strstr(uri, ZSTR_VAL(map->delim))) && *(query - 1) == YAF_ROUTER_URL_DELIMIETER) {
185 			const char *rest = query + ZSTR_LEN(map->delim);
186 
187 			while (*rest == YAF_ROUTER_URL_DELIMIETER) {
188 				rest++;
189 			}
190 			if (*rest != '\0') {
191 				zval params;
192 				query_len = len - (rest - uri);
193 				len = query - uri;
194 				query = rest;
195 				yaf_router_parse_parameters(query, query_len, &params);
196 				yaf_request_set_params_multi(request, &params);
197 				zend_array_destroy(Z_ARR(params));
198 			} else {
199 				len = query - uri;
200 			}
201 		}
202 	}
203 
204 	if (len) {
205 		if (map->flags & YAF_ROUTE_MAP_CTL_PREFER) {
206 			zend_string *result = yaf_route_map_build(uri, len, 1);
207 			if (result) {
208 				/* avoding double realloc */
209 				if (UNEXPECTED(request->controller)) {
210 					zend_string_release(request->controller);
211 				}
212 				request->controller = result;
213 			}
214 		} else {
215 			zend_string *result = yaf_route_map_build(uri, len, 0);
216 			if (result) {
217 				/* avoding double realloc */
218 				if (UNEXPECTED(request->action)) {
219 					zend_string_release(request->action);
220 				}
221 				request->action = result;
222 			}
223 		}
224 	}
225 
226 	return 1;
227 }
228 /* }}} */
229 
230 /** {{{ proto public Yaf_Route_Simple::route(Yaf_Request $req)
231 */
PHP_METHOD(yaf_route_map,route)232 PHP_METHOD(yaf_route_map, route) {
233 	yaf_request_t *request;
234 
235 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &request, yaf_request_ce) == FAILURE) {
236 		return;
237 	} else {
238 		RETURN_BOOL(yaf_route_map_route(getThis(), request));
239 	}
240 }
241 /* }}} */
242 
yaf_route_map_assemble(yaf_route_t * route,zval * info,zval * query)243 zend_string * yaf_route_map_assemble(yaf_route_t *route, zval *info, zval *query) /* {{{ */ {
244 	zval *zv;
245 	char *seg, *pname;
246 	size_t seg_len;
247 	char *ptrptr = NULL;
248 	smart_str uri = {0};
249 	yaf_route_map_object *map = Z_YAFROUTEMAPOBJ_P(route);
250 
251 	if (map->flags & YAF_ROUTE_MAP_CTL_PREFER) {
252 		if ((zv = zend_hash_str_find(Z_ARRVAL_P(info), ZEND_STRL(YAF_ROUTE_ASSEMBLE_ACTION_FORMAT))) && Z_TYPE_P(zv) == IS_STRING) {
253 			pname = estrndup(Z_STRVAL_P(zv), Z_STRLEN_P(zv));
254 		} else {
255 			yaf_trigger_error(YAF_ERR_TYPE_ERROR, "%s",
256 					"Undefined the 'action' parameter for the 1st parameter");
257 			return NULL;
258 		}
259 	} else {
260 		if ((zv = zend_hash_str_find(Z_ARRVAL_P(info), ZEND_STRL(YAF_ROUTE_ASSEMBLE_CONTROLLER_FORMAT))) && Z_TYPE_P(zv) == IS_STRING) {
261 			pname = estrndup(Z_STRVAL_P(zv), Z_STRLEN_P(zv));
262 		} else {
263 			yaf_trigger_error(YAF_ERR_TYPE_ERROR, "%s",
264 					"Undefined the 'controller' parameter for the 1st parameter");
265 			return NULL;
266 		}
267 	}
268 
269 	seg = php_strtok_r(pname, "_", &ptrptr);
270 	while (seg) {
271 		seg_len = strlen(seg);
272 		if (seg_len) {
273 			smart_str_appendc(&uri, '/');
274 			smart_str_appendl(&uri, seg, seg_len);
275 		}
276 		seg = php_strtok_r(NULL, "_", &ptrptr);
277 	}
278 	efree(pname);
279 
280 	if (query && IS_ARRAY == Z_TYPE_P(query)) {
281 		zend_string *key, *val;
282 		if (zend_hash_num_elements(Z_ARRVAL_P(query))) {
283 			if (map->delim) {
284 				smart_str_appendc(&uri, '/');
285 				smart_str_appendl(&uri, ZSTR_VAL(map->delim), ZSTR_LEN(map->delim));
286 				smart_str_appendc(&uri, '/');
287 			} else {
288 				smart_str_appendc(&uri, '?');
289 			}
290 
291 			ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(query), key, zv) {
292 				if (key) {
293 					val = zval_get_string(zv);
294 					if (map->delim) {
295 						smart_str_appendl(&uri, ZSTR_VAL(key), ZSTR_LEN(key));
296 						smart_str_appendc(&uri, '/');
297 						smart_str_appendl(&uri, ZSTR_VAL(val), ZSTR_LEN(val));
298 						smart_str_appendc(&uri, '/');
299 					} else {
300 						smart_str_appendl(&uri, ZSTR_VAL(key), ZSTR_LEN(key));
301 						smart_str_appendc(&uri, '=');
302 						smart_str_appendl(&uri, ZSTR_VAL(val), ZSTR_LEN(val));
303 						smart_str_appendc(&uri, '&');
304 					}
305 					zend_string_release(val);
306 				}
307 			} ZEND_HASH_FOREACH_END();
308 			ZSTR_LEN(uri.s)--;
309 		}
310 	}
311 	smart_str_0(&uri);
312 
313 	return uri.s;
314 }
315 /* }}} */
316 
317 /** {{{ proto public Yaf_Route_Simple::__construct(bool $controller_prefer=FALSE, string $delimer = '#!')
318 */
PHP_METHOD(yaf_route_map,__construct)319 PHP_METHOD(yaf_route_map, __construct) {
320 	zend_string *delim	= NULL;
321 	zend_bool ctl_prefer = 0;
322 
323 	if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "|bS!", &ctl_prefer, &delim) == FAILURE) {
324 		return;
325 	}
326 
327 	yaf_route_map_init(Z_YAFROUTEMAPOBJ_P(getThis()), ctl_prefer, delim);
328 }
329 /* }}} */
330 
331 /** {{{ proto public Yaf_Route_Map::assemble(array $info[, array $query = NULL])
332 */
PHP_METHOD(yaf_route_map,assemble)333 PHP_METHOD(yaf_route_map, assemble) {
334 	zval *info, *query = NULL;
335 
336     if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|a", &info, &query) == FAILURE) {
337         return;
338     } else {
339 		zend_string *str;
340         if ((str = yaf_route_map_assemble(getThis(), info, query)) != NULL) {
341 			RETURN_STR(str);
342 		}
343 		RETURN_NULL();
344     }
345 }
346 /* }}} */
347 
348 /** {{{ yaf_route_map_methods
349 */
350 zend_function_entry yaf_route_map_methods[] = {
351 	PHP_ME(yaf_route_map, __construct, yaf_route_map_construct_arginfo, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
352 	PHP_ME(yaf_route_map, route, yaf_route_route_arginfo, ZEND_ACC_PUBLIC)
353 	PHP_ME(yaf_route_map, assemble, yaf_route_assemble_arginfo, ZEND_ACC_PUBLIC)
354 	{NULL, NULL, NULL}
355 };
356 /* }}} */
357 
358 /** {{{ YAF_STARTUP_FUNCTION
359 */
YAF_STARTUP_FUNCTION(route_map)360 YAF_STARTUP_FUNCTION(route_map) {
361 	zend_class_entry ce;
362 
363 	YAF_INIT_CLASS_ENTRY(ce, "Yaf_Route_Map", "Yaf\\Route\\Map", yaf_route_map_methods);
364 	yaf_route_map_ce = zend_register_internal_class(&ce);
365 	yaf_route_map_ce->create_object = yaf_route_map_new;
366 	yaf_route_map_ce->ce_flags |= ZEND_ACC_FINAL;
367 	yaf_route_map_ce->serialize = zend_class_serialize_deny;
368 	yaf_route_map_ce->unserialize = zend_class_unserialize_deny;
369 
370 	zend_class_implements(yaf_route_map_ce, 1, yaf_route_ce);
371 
372 	memcpy(&yaf_route_map_obj_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
373 	yaf_route_map_obj_handlers.free_obj = yaf_route_map_object_free;
374 	yaf_route_map_obj_handlers.clone_obj = NULL;
375 	yaf_route_map_obj_handlers.get_gc = yaf_fake_get_gc;
376 	yaf_route_map_obj_handlers.get_properties = yaf_route_map_get_properties;
377 
378 
379 	return SUCCESS;
380 }
381 /* }}} */
382 
383 /*
384  * Local variables:
385  * tab-width: 4
386  * c-basic-offset: 4
387  * End:
388  * vim600: noet sw=4 ts=4 fdm=marker
389  * vim<600: noet sw=4 ts=4
390  */
391