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, ¶ms);
196 yaf_request_set_params_multi(request, ¶ms);
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