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 "ext/pcre/php_pcre.h" /* for pcre */
23 #include "Zend/zend_smart_str.h" /* for smart_str */
24 #include "Zend/zend_interfaces.h" /* for zend_class_serialize_deny */
25
26 #include "php_yaf.h"
27 #include "yaf_namespace.h"
28 #include "yaf_exception.h"
29 #include "yaf_request.h"
30
31 #include "yaf_router.h"
32 #include "routes/yaf_route_interface.h"
33 #include "routes/yaf_route_rewrite.h"
34
35 #include "ext/standard/php_string.h"
36
37 zend_class_entry *yaf_route_rewrite_ce;
38 static zend_object_handlers yaf_route_rewrite_obj_handlers;
39
40 /** {{{ ARG_INFO
41 */
42 ZEND_BEGIN_ARG_INFO_EX(yaf_route_rewrite_construct_arginfo, 0, 0, 2)
43 ZEND_ARG_INFO(0, match)
44 ZEND_ARG_ARRAY_INFO(0, route, 0)
45 ZEND_ARG_ARRAY_INFO(0, verify, 1)
ZEND_END_ARG_INFO()46 ZEND_END_ARG_INFO()
47
48 ZEND_BEGIN_ARG_INFO_EX(yaf_route_rewrite_match_arginfo, 0, 0, 1)
49 ZEND_ARG_INFO(0, uri)
50 ZEND_END_ARG_INFO()
51 /* }}} */
52
53 static HashTable *yaf_route_rewrite_get_properties(yaf_object *object) /* {{{ */ {
54 zval rv;
55 HashTable *ht;
56 yaf_route_rewrite_object *rewrite = (yaf_route_rewrite_object*)(yaf_strip_obj(object));
57
58 if (!rewrite->properties) {
59 ALLOC_HASHTABLE(rewrite->properties);
60 zend_hash_init(rewrite->properties, 4, NULL, ZVAL_PTR_DTOR, 0);
61
62 ht = rewrite->properties;
63 ZVAL_STR_COPY(&rv, rewrite->match);
64 zend_hash_str_add(ht, "match:protected", sizeof("match:protected") - 1, &rv);
65
66 ZVAL_ARR(&rv, rewrite->router);
67 Z_TRY_ADDREF(rv);
68 zend_hash_str_add(ht, "route:protected", sizeof("route:protected") - 1, &rv);
69
70 if (rewrite->verify) {
71 ZVAL_ARR(&rv, rewrite->verify);
72 Z_TRY_ADDREF(rv);
73 } else {
74 ZVAL_NULL(&rv);
75 }
76 zend_hash_str_add(ht, "verify:protected", sizeof("verify:protected") - 1, &rv);
77 }
78
79 return rewrite->properties;
80 }
81 /* }}} */
82
yaf_route_rewrite_new(zend_class_entry * ce)83 static zend_object *yaf_route_rewrite_new(zend_class_entry *ce) /* {{{ */ {
84 yaf_route_rewrite_object *rewrite = emalloc(sizeof(yaf_route_rewrite_object));
85
86 zend_object_std_init(&rewrite->std, ce);
87 rewrite->std.handlers = &yaf_route_rewrite_obj_handlers;
88
89 rewrite->match = NULL;
90 rewrite->router = NULL;
91 rewrite->verify = NULL;
92 rewrite->properties = NULL;
93
94 return &rewrite->std;
95 }
96 /* }}} */
97
yaf_route_rewrite_object_free(zend_object * object)98 static void yaf_route_rewrite_object_free(zend_object *object) /* {{{ */ {
99 yaf_route_rewrite_object *rewrite = (yaf_route_rewrite_object*)object;
100
101 if (rewrite->match) {
102 zend_string_release(rewrite->match);
103 }
104
105 if (rewrite->router) {
106 if ((GC_DELREF(rewrite->router) == 0)) {
107 GC_REMOVE_FROM_BUFFER(rewrite->router);
108 zend_array_destroy(rewrite->router);
109 }
110 }
111
112 if (rewrite->verify) {
113 if ((GC_DELREF(rewrite->verify) == 0)) {
114 GC_REMOVE_FROM_BUFFER(rewrite->verify);
115 zend_array_destroy(rewrite->verify);
116 }
117 }
118
119 if (rewrite->properties) {
120 if ((GC_DELREF(rewrite->properties) == 0)) {
121 GC_REMOVE_FROM_BUFFER(rewrite->properties);
122 zend_array_destroy(rewrite->properties);
123 }
124 }
125
126 zend_object_std_dtor(&rewrite->std);
127 }
128 /* }}} */
129
yaf_route_rewrite_init(yaf_route_rewrite_object * rewrite,zend_string * match,zval * router,zval * verify)130 static void yaf_route_rewrite_init(yaf_route_rewrite_object *rewrite, zend_string *match, zval *router, zval *verify) /* {{{ */ {
131 rewrite->match = zend_string_copy(match);
132
133 if (router) {
134 rewrite->router = zend_array_dup(Z_ARRVAL_P(router));
135 } else {
136 rewrite->router = NULL;
137 }
138
139 if (verify) {
140 rewrite->verify = zend_array_dup(Z_ARRVAL_P(verify));
141 } else {
142 rewrite->verify = NULL;
143 }
144 }
145 /* }}} */
146
yaf_route_rewrite_instance(yaf_route_t * route,zend_string * match,zval * router,zval * verify)147 void yaf_route_rewrite_instance(yaf_route_t *route, zend_string *match, zval *router, zval *verify) /* {{{ */ {
148 zend_object *rewrite = yaf_route_rewrite_new(yaf_route_rewrite_ce);
149
150 yaf_route_rewrite_init((yaf_route_rewrite_object*)rewrite, match, router, verify);
151
152 ZVAL_OBJ(route, rewrite);
153 }
154 /* }}} */
155
yaf_route_rewrite_match(yaf_route_rewrite_object * rewrite,const char * uri,size_t len,zval * ret)156 static int yaf_route_rewrite_match(yaf_route_rewrite_object *rewrite, const char *uri, size_t len, zval *ret) /* {{{ */ {
157 char *pos, *m;
158 uint32_t l;
159 pcre_cache_entry *pce_regexp;
160 smart_str pattern = {0};
161
162
163 ZEND_ASSERT(rewrite->match);
164
165 smart_str_appendc(&pattern, YAF_ROUTE_REGEX_DILIMITER);
166 smart_str_appendc(&pattern, '^');
167
168 m = ZSTR_VAL(rewrite->match);
169 l = ZSTR_LEN(rewrite->match);
170 while (l) {
171 if (*m == '*') {
172 smart_str_appendl(&pattern, "(?P<__yaf_route_rest>.*)", sizeof("(?P<__yaf_route_rest>.*)") -1);
173 break;
174 } else {
175 uint32_t len;
176 pos = memchr(m, YAF_ROUTER_URL_DELIMIETER, l);
177 if (pos) {
178 len = pos - m;
179 l -= len;
180 } else {
181 len = l;
182 l = 0;
183 }
184 if (*m == ':') {
185 smart_str_appendl(&pattern, "(?P<", sizeof("(?P<") -1 );
186 smart_str_appendl(&pattern, m + 1, len - 1);
187 smart_str_appendl(&pattern, ">[^", sizeof(">[^") - 1);
188 smart_str_appendc(&pattern, YAF_ROUTER_URL_DELIMIETER);
189 smart_str_appendl(&pattern, "]+)", sizeof("]+)") - 1);
190 } else {
191 smart_str_appendl(&pattern, m, len);
192 }
193 if (pos) {
194 smart_str_appendc(&pattern, YAF_ROUTER_URL_DELIMIETER);
195 smart_str_appendc(&pattern, '+');
196 m = ++pos;
197 l--;
198 }
199 }
200 }
201
202 smart_str_appendc(&pattern, YAF_ROUTE_REGEX_DILIMITER);
203 smart_str_appendc(&pattern, 'i');
204 smart_str_0(&pattern);
205 pce_regexp = pcre_get_compiled_regex_cache(pattern.s);
206 smart_str_free(&pattern);
207
208 if (pce_regexp) {
209 zval matches, subparts;
210
211 smart_str_free(&pattern);
212
213 ZVAL_NULL(&subparts);
214
215 #if PHP_VERSION_ID < 70400
216 php_pcre_match_impl(pce_regexp, (char*)uri, len, &matches, &subparts /* subpats */,
217 0/* global */, 0/* ZEND_NUM_ARGS() >= 4 */, 0/*flags PREG_OFFSET_CAPTURE*/, 0/* start_offset */);
218 #else
219 {
220 zend_string *tmp = zend_string_init(uri, len, 0);
221 php_pcre_match_impl(pce_regexp, tmp, &matches, &subparts /* subpats */,
222 0/* global */, 0/* ZEND_NUM_ARGS() >= 4 */, 0/*flags PREG_OFFSET_CAPTURE*/, 0/* start_offset */);
223 zend_string_release(tmp);
224 }
225 #endif
226
227 if (!zend_hash_num_elements(Z_ARRVAL(subparts))) {
228 zval_ptr_dtor(&subparts);
229 return 0;
230 } else {
231 zval *pzval;
232 zend_string *key;
233 HashTable *ht;
234
235 array_init(ret);
236
237 ht = Z_ARRVAL(subparts);
238 ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, pzval) {
239 if (key) {
240 if (zend_string_equals_literal(key, "__yaf_route_rest")) {
241 zval params;
242 yaf_router_parse_parameters(Z_STRVAL_P(pzval), Z_STRLEN_P(pzval), ¶ms);
243 zend_hash_copy(Z_ARRVAL_P(ret), Z_ARRVAL(params), (copy_ctor_func_t) zval_add_ref);
244 zval_ptr_dtor(¶ms);
245 } else {
246 Z_ADDREF_P(pzval);
247 zend_hash_update(Z_ARRVAL_P(ret), key, pzval);
248 }
249 }
250 } ZEND_HASH_FOREACH_END();
251
252 zval_ptr_dtor(&subparts);
253 return 1;
254 }
255 }
256
257 return 0;
258 }
259 /* }}} */
260
yaf_route_rewrite_route(yaf_route_t * route,yaf_request_t * req)261 int yaf_route_rewrite_route(yaf_route_t *route, yaf_request_t *req) /* {{{ */ {
262 zval args;
263 const char *req_uri;
264 size_t req_uri_len;
265 yaf_request_object *request = Z_YAFREQUESTOBJ_P(req);
266 yaf_route_rewrite_object *rewrite = Z_YAFROUTEREWRITEOBJ_P(route);
267
268 if (request->base_uri) {
269 req_uri = yaf_request_strip_base_uri(request->uri, request->base_uri, &req_uri_len);
270 } else {
271 req_uri = ZSTR_VAL(request->uri);
272 req_uri_len = ZSTR_LEN(request->uri);
273 }
274
275 if (UNEXPECTED(req_uri_len == 0)) {
276 return 0;
277 }
278
279 if (EXPECTED(yaf_route_rewrite_match(rewrite, req_uri, req_uri_len, &args))) {
280 zval *module, *controller, *action;
281
282 ZEND_ASSERT(rewrite->router);
283 if ((module = zend_hash_str_find(rewrite->router, ZEND_STRL("module"))) != NULL && IS_STRING == Z_TYPE_P(module)) {
284 if (Z_STRVAL_P(module)[0] != ':') {
285 yaf_request_set_module(request, Z_STR_P(module));
286 } else {
287 zval *m;
288 if ((m = zend_hash_str_find(Z_ARRVAL(args), Z_STRVAL_P(module) + 1, Z_STRLEN_P(module) - 1)) && IS_STRING == Z_TYPE_P(m)) {
289 yaf_request_set_module(request, Z_STR_P(m));
290 }
291 }
292 }
293
294 if ((controller = zend_hash_str_find(rewrite->router, ZEND_STRL("controller"))) && IS_STRING == Z_TYPE_P(controller)) {
295 if (Z_STRVAL_P(controller)[0] != ':') {
296 yaf_request_set_controller(request, Z_STR_P(controller));
297 } else {
298 zval *c;
299 if ((c = zend_hash_str_find(Z_ARRVAL(args), Z_STRVAL_P(controller) + 1, Z_STRLEN_P(controller) - 1)) && IS_STRING == Z_TYPE_P(c)) {
300 yaf_request_set_controller(request, Z_STR_P(c));
301 }
302 }
303 }
304
305 if ((action = zend_hash_str_find(rewrite->router, ZEND_STRL("action"))) && IS_STRING == Z_TYPE_P(action)) {
306 if (Z_STRVAL_P(action)[0] != ':') {
307 yaf_request_set_action(request, Z_STR_P(action));
308 } else {
309 zval *a;
310 if ((a = zend_hash_str_find(Z_ARRVAL(args), Z_STRVAL_P(action) + 1, Z_STRLEN_P(action) - 1)) && IS_STRING == Z_TYPE_P(a)) {
311 yaf_request_set_action(request, Z_STR_P(a));
312 }
313 }
314 }
315
316 yaf_request_set_params_multi(request, &args);
317 zval_ptr_dtor(&args);
318
319 return 1;
320 }
321
322 return 0;
323 }
324 /* }}} */
325
yaf_route_rewrite_assemble(yaf_route_rewrite_object * rewrite,zval * info,zval * query)326 zend_string *yaf_route_rewrite_assemble(yaf_route_rewrite_object *rewrite, zval *info, zval *query) /* {{{ */ {
327 zval pidents, *zv;
328 char *seg, *pmatch, *ptrptr;
329 zend_string *key, *inter, *uri, *val;
330 size_t seg_len;
331 smart_str query_str = {0};
332 smart_str wildcard = {0};
333 char token[2] = {YAF_ROUTER_URL_DELIMIETER, 0};
334
335 array_init(&pidents);
336
337 uri = zend_string_copy(rewrite->match);
338 pmatch = estrndup(ZSTR_VAL(rewrite->match), ZSTR_LEN(rewrite->match));
339 zend_hash_copy(Z_ARRVAL(pidents), Z_ARRVAL_P(info), (copy_ctor_func_t) zval_add_ref);
340
341 seg = php_strtok_r(pmatch, token, &ptrptr);
342 while (seg) {
343 seg_len = strlen(seg);
344 if (seg_len) {
345 if (*(seg) == '*') {
346 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(pidents), key, zv) {
347 if (key) {
348 if (IS_STRING == Z_TYPE_P(zv)) {
349 smart_str_appendl(&wildcard, ZSTR_VAL(key) + 1, ZSTR_LEN(key) - 1);
350 smart_str_appendc(&wildcard, YAF_ROUTER_URL_DELIMIETER);
351 smart_str_appendl(&wildcard, Z_STRVAL_P(zv), Z_STRLEN_P(zv));
352 smart_str_appendc(&wildcard, YAF_ROUTER_URL_DELIMIETER);
353 }
354 }
355 } ZEND_HASH_FOREACH_END();
356 smart_str_0(&wildcard);
357 inter = php_str_to_str(ZSTR_VAL(uri), ZSTR_LEN(uri),
358 "*", 1, ZSTR_VAL(wildcard.s), ZSTR_LEN(wildcard.s));
359 zend_string_release(uri);
360 uri = inter;
361 break;
362 }
363
364 if (*(seg) == ':') {
365 if ((zv = zend_hash_str_find(Z_ARRVAL_P(info), seg, seg_len)) != NULL) {
366 val = zval_get_string(zv);
367 inter = php_str_to_str(ZSTR_VAL(uri),
368 ZSTR_LEN(uri), seg, seg_len, ZSTR_VAL(val), ZSTR_LEN(val));
369 zend_string_release(val);
370 zend_string_release(uri);
371 zend_hash_str_del(Z_ARRVAL(pidents), seg, seg_len);
372 uri = inter;
373 }
374 }
375 }
376 seg = php_strtok_r(NULL, token, &ptrptr);
377 }
378
379 smart_str_free(&wildcard);
380 efree(pmatch);
381 zval_ptr_dtor(&pidents);
382
383 if (query && IS_ARRAY == Z_TYPE_P(query)) {
384 HashTable *ht = Z_ARRVAL_P(query);
385
386 smart_str_appendc(&query_str, '?');
387 ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, zv) {
388 if (key) {
389 val = zval_get_string(zv);
390 smart_str_appendl(&query_str, ZSTR_VAL(key), ZSTR_LEN(key));
391 smart_str_appendc(&query_str, '=');
392 smart_str_appendl(&query_str, ZSTR_VAL(val), ZSTR_LEN(val));
393 smart_str_appendc(&query_str, '&');
394 zend_string_release(val);
395 }
396 } ZEND_HASH_FOREACH_END();
397 }
398
399 if (query_str.s) {
400 size_t orig_len = ZSTR_LEN(uri);
401 ZSTR_LEN(query_str.s)--; /* get rid of the tail & */
402 smart_str_0(&query_str);
403 uri = zend_string_realloc(uri, ZSTR_LEN(uri) + ZSTR_LEN(query_str.s), 0);
404 memcpy(ZSTR_VAL(uri) + orig_len, ZSTR_VAL(query_str.s), ZSTR_LEN(query_str.s));
405 ZSTR_VAL(uri)[ZSTR_LEN(uri)] = '\0';
406 smart_str_free(&query_str);
407 }
408
409 return uri;
410 }
411 /* }}} */
412
413 /** {{{ proto public Yaf_Route_Rewrite::route(Yaf_Request_Abstract $request)
414 */
PHP_METHOD(yaf_route_rewrite,route)415 PHP_METHOD(yaf_route_rewrite, route) {
416 yaf_request_t *request;
417
418 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &request, yaf_request_ce) == FAILURE) {
419 return;
420 }
421
422 RETURN_BOOL(yaf_route_rewrite_route(getThis(), request));
423 }
424 /** }}} */
425
426 /** {{{ proto public Yaf_Route_Rewrite::match(string $uri)
427 */
PHP_METHOD(yaf_route_rewrite,match)428 PHP_METHOD(yaf_route_rewrite, match) {
429 zend_string *uri;
430
431 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &uri) == FAILURE) {
432 return;
433 }
434
435 if (ZSTR_LEN(uri) == 0) {
436 RETURN_FALSE;
437 }
438
439 if (!yaf_route_rewrite_match(Z_YAFROUTEREWRITEOBJ_P(getThis()), ZSTR_VAL(uri), ZSTR_LEN(uri), return_value)) {
440 RETURN_FALSE;
441 }
442 }
443 /** }}} */
444
445 /** {{{ proto public Yaf_Route_Rewrite::__construct(string $match, array $route, array $verify, string $reverse = NULL)
446 */
PHP_METHOD(yaf_route_rewrite,__construct)447 PHP_METHOD(yaf_route_rewrite, __construct) {
448 zend_string *match;
449 zval *route, *verify = NULL;
450
451 if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "Sa|a", &match, &route, &verify) == FAILURE) {
452 return;
453 }
454
455 yaf_route_rewrite_init(Z_YAFROUTEREWRITEOBJ_P(getThis()), match, route, verify);
456 }
457 /** }}} */
458
459 /** {{{ proto public Yaf_Route_rewrite::assemble(array $info[, array $query = NULL])
460 */
PHP_METHOD(yaf_route_rewrite,assemble)461 PHP_METHOD(yaf_route_rewrite, assemble) {
462 zend_string *str;
463 zval *info, *query = NULL;
464
465 if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|a", &info, &query) == FAILURE) {
466 return;
467 }
468
469 if ((str = yaf_route_rewrite_assemble(Z_YAFROUTEREWRITEOBJ_P(getThis()), info, query))) {
470 RETURN_STR(str);
471 }
472 }
473 /* }}} */
474
475 /** {{{ yaf_route_rewrite_methods
476 */
477 zend_function_entry yaf_route_rewrite_methods[] = {
478 PHP_ME(yaf_route_rewrite, __construct, yaf_route_rewrite_construct_arginfo, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
479 PHP_ME(yaf_route_rewrite, match, yaf_route_rewrite_match_arginfo, ZEND_ACC_PUBLIC)
480 PHP_ME(yaf_route_rewrite, route, yaf_route_route_arginfo, ZEND_ACC_PUBLIC)
481 PHP_ME(yaf_route_rewrite, assemble, yaf_route_assemble_arginfo, ZEND_ACC_PUBLIC)
482 {NULL, NULL, NULL}
483 };
484 /* }}} */
485
486 /** {{{ YAF_STARTUP_FUNCTION
487 */
YAF_STARTUP_FUNCTION(route_rewrite)488 YAF_STARTUP_FUNCTION(route_rewrite) {
489 zend_class_entry ce;
490 YAF_INIT_CLASS_ENTRY(ce, "Yaf_Route_Rewrite", "Yaf\\Route\\Rewrite", yaf_route_rewrite_methods);
491 yaf_route_rewrite_ce = zend_register_internal_class_ex(&ce, NULL);
492 yaf_route_rewrite_ce->ce_flags |= ZEND_ACC_FINAL;
493 yaf_route_rewrite_ce->create_object = yaf_route_rewrite_new;
494 yaf_route_rewrite_ce->serialize = zend_class_serialize_deny;
495 yaf_route_rewrite_ce->unserialize = zend_class_unserialize_deny;
496
497 zend_class_implements(yaf_route_rewrite_ce, 1, yaf_route_ce);
498
499 memcpy(&yaf_route_rewrite_obj_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
500 yaf_route_rewrite_obj_handlers.free_obj = yaf_route_rewrite_object_free;
501 yaf_route_rewrite_obj_handlers.clone_obj = NULL;
502 yaf_route_rewrite_obj_handlers.get_gc = yaf_fake_get_gc;
503 yaf_route_rewrite_obj_handlers.get_properties = yaf_route_rewrite_get_properties;
504
505 return SUCCESS;
506 }
507 /* }}} */
508
509 /*
510 * Local variables:
511 * tab-width: 4
512 * c-basic-offset: 4
513 * End:
514 * vim600: noet sw=4 ts=4 fdm=marker
515 * vim<600: noet sw=4 ts=4
516 */
517
518