add brotli encoding
[m6w6/ext-http] / src / php_http_params.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2014, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #include "php_http_api.h"
14
15 static php_http_params_token_t def_param_sep = {",", 1}, *def_param_sep_ptr[] = {&def_param_sep, NULL};
16 static php_http_params_token_t def_arg_sep = {";", 1}, *def_arg_sep_ptr[] = {&def_arg_sep, NULL};
17 static php_http_params_token_t def_val_sep = {"=", 1}, *def_val_sep_ptr[] = {&def_val_sep, NULL};
18 static php_http_params_opts_t def_opts = {
19 {NULL, 0},
20 def_param_sep_ptr,
21 def_arg_sep_ptr,
22 def_val_sep_ptr,
23 {{0}},
24 PHP_HTTP_PARAMS_DEFAULT
25 };
26
27 php_http_params_opts_t *php_http_params_opts_default_get(php_http_params_opts_t *opts)
28 {
29 if (!opts) {
30 opts = emalloc(sizeof(*opts));
31 }
32
33 memcpy(opts, &def_opts, sizeof(def_opts));
34
35 return opts;
36 }
37
38 typedef struct php_http_params_state {
39 php_http_params_token_t input;
40 php_http_params_token_t param;
41 php_http_params_token_t arg;
42 php_http_params_token_t val;
43 struct {
44 zval *param;
45 zval *args;
46 zval *val;
47 } current;
48 unsigned quotes:1;
49 unsigned escape:1;
50 unsigned rfc5987:1;
51 } php_http_params_state_t;
52
53 static inline void sanitize_escaped(zval *zv)
54 {
55 if (Z_STRVAL_P(zv)[0] == '"' && Z_STRVAL_P(zv)[Z_STRLEN_P(zv) - 1] == '"') {
56 size_t deq_len = Z_STRLEN_P(zv) - 2;
57 char *deq = estrndup(Z_STRVAL_P(zv) + 1, deq_len);
58
59 zval_dtor(zv);
60 ZVAL_STR(zv, php_http_cs2zs(deq, deq_len));
61 }
62
63 php_stripcslashes(Z_STR_P(zv));
64 }
65
66 static inline zend_string *quote_string(zend_string *zs, zend_bool force)
67 {
68 size_t len = (zs)->len;
69
70 zs = php_addcslashes(zs, 0, ZEND_STRL("\0..\37\173\\\""));
71
72 if (force || len != (zs)->len || strpbrk((zs)->val, "()<>@,;:\"[]?={} ")) {
73 int len = (zs)->len + 2;
74
75 zs = zend_string_extend(zs, len, 0);
76
77 memmove(&(zs)->val[1], (zs)->val, (zs)->len);
78 (zs)->val[0] = '"';
79 (zs)->val[len-1] = '"';
80 (zs)->val[len] = '\0';
81
82 zend_string_forget_hash_val(zs);
83 }
84
85 return zs;
86 }
87
88 /* if (Z_TYPE_P(zv) == IS_STRING) {
89 size_t len = Z_STRLEN_P(zv);
90 zend_string *stripped = php_addcslashes(Z_STR_P(zv), 0,
91 ZEND_STRL("\0..\37\173\\\""));
92
93 if (len != stripped->len || strpbrk(stripped->val, "()<>@,;:\"[]?={} ")) {
94 size_t len = stripped->len + 2;
95 char *str = emalloc(len + 1);
96
97 str[0] = '"';
98 memcpy(&str[1], stripped->val, stripped->len);
99 str[len-1] = '"';
100 str[len] = '\0';
101
102 zval_dtor(zv);
103 zend_string_release(stripped);
104 ZVAL_STR(zv, php_http_cs2zs(str, len));
105 } else {
106 zval_dtor(zv);
107 ZVAL_STR(zv, stripped);
108 }
109 */
110
111 static inline void prepare_escaped(zval *zv)
112 {
113 if (Z_TYPE_P(zv) == IS_STRING) {
114 zend_string *str = quote_string(Z_STR_P(zv), 0);
115
116 zval_dtor(zv);
117 ZVAL_STR(zv, str);
118 } else {
119 zval_dtor(zv);
120 ZVAL_EMPTY_STRING(zv);
121 }
122 }
123
124 static inline void sanitize_urlencoded(zval *zv)
125 {
126 Z_STRLEN_P(zv) = php_url_decode(Z_STRVAL_P(zv), Z_STRLEN_P(zv));
127 }
128
129 static inline void prepare_urlencoded(zval *zv)
130 {
131 zend_string *str = php_raw_url_encode(Z_STRVAL_P(zv), Z_STRLEN_P(zv));
132
133 zval_dtor(zv);
134 ZVAL_STR(zv, str);
135 }
136
137 static void sanitize_dimension(zval *zv)
138 {
139 zval arr, tmp, *cur = &arr;
140 char *var = NULL, *ptr = Z_STRVAL_P(zv), *end = Z_STRVAL_P(zv) + Z_STRLEN_P(zv);
141 long level = 0;
142
143 array_init(&arr);
144
145 while (ptr < end) {
146 if (!var) {
147 var = ptr;
148 }
149
150 switch (*ptr) {
151 case '[':
152 if (++level > PG(max_input_nesting_level)) {
153 zval_ptr_dtor(&arr);
154 php_error_docref(NULL, E_WARNING, "Max input nesting level of %ld exceeded", (long) PG(max_input_nesting_level));
155 return;
156 }
157 if (ptr - var == 0) {
158 ++var;
159 break;
160 }
161 /* no break */
162
163 case ']':
164
165 ZVAL_NULL(&tmp);
166 convert_to_array(cur);
167
168 if (ptr - var) {
169 char chr = *ptr;
170 *ptr = '\0';
171 cur = zend_symtable_str_update(Z_ARRVAL_P(cur), var, ptr - var, &tmp);
172 *ptr = chr;
173 } else {
174 cur = zend_hash_next_index_insert(Z_ARRVAL_P(cur), &tmp);
175 }
176
177 var = NULL;
178 break;
179 }
180
181 ++ptr;
182 }
183
184 if (zend_hash_num_elements(Z_ARRVAL(arr))) {
185 zval_dtor(zv);
186 ZVAL_COPY_VALUE(zv, &arr);
187 } else {
188 zval_ptr_dtor(&arr);
189 }
190 }
191
192 static inline void shift_key(php_http_buffer_t *buf, char *key_str, size_t key_len, const char *ass, size_t asl, unsigned flags);
193 static inline void shift_val(php_http_buffer_t *buf, zval *zvalue, const char *vss, size_t vsl, unsigned flags);
194
195 static void prepare_dimension(php_http_buffer_t *buf, php_http_buffer_t *keybuf, zval *zvalue, const char *pss, size_t psl, const char *vss, size_t vsl, unsigned flags)
196 {
197 HashTable *ht = HASH_OF(zvalue);
198 php_http_arrkey_t key;
199 zval *val;
200 php_http_buffer_t prefix;
201
202 if (!HT_IS_RECURSIVE(ht)) {
203 HT_PROTECT_RECURSION(ht);
204 php_http_buffer_init(&prefix);
205 php_http_buffer_append(&prefix, keybuf->data, keybuf->used);
206
207 ZEND_HASH_FOREACH_KEY_VAL_IND(ht, key.h, key.key, val)
208 {
209 if (key.key && !*key.key->val) {
210 /* only public properties */
211 continue;
212 }
213
214 php_http_buffer_appends(&prefix, "[");
215 if (key.key) {
216 php_http_buffer_append(&prefix, key.key->val, key.key->len);
217 } else {
218 php_http_buffer_appendf(&prefix, "%lu", key.h);
219 }
220 php_http_buffer_appends(&prefix, "]");
221
222 if (Z_TYPE_P(val) == IS_ARRAY || Z_TYPE_P(val) == IS_OBJECT) {
223 prepare_dimension(buf, &prefix, val, pss, psl, vss, vsl, flags);
224 } else {
225 zend_string *cpy = zval_get_string(val);
226 zval tmp;
227
228 ZVAL_STR(&tmp, cpy);
229 shift_key(buf, prefix.data, prefix.used, pss, psl, flags);
230 shift_val(buf, &tmp, vss, vsl, flags);
231 zend_string_release(cpy);
232 }
233
234 php_http_buffer_cut(&prefix, keybuf->used, prefix.used - keybuf->used);
235 }
236 ZEND_HASH_FOREACH_END();
237 HT_UNPROTECT_RECURSION(ht);
238
239 php_http_buffer_dtor(&prefix);
240 }
241 }
242
243 static inline void sanitize_key(unsigned flags, const char *str, size_t len, zval *zv, zend_bool *rfc5987)
244 {
245 char *eos;
246 zend_string *zs = zend_string_init(str, len, 0);
247
248 zval_dtor(zv);
249 ZVAL_STR(zv, php_trim(zs, NULL, 0, 3));
250 zend_string_release(zs);
251
252 if (flags & PHP_HTTP_PARAMS_ESCAPED) {
253 sanitize_escaped(zv);
254 }
255
256 if (!Z_STRLEN_P(zv)) {
257 return;
258 }
259
260 if (flags & PHP_HTTP_PARAMS_RFC5987) {
261 eos = &Z_STRVAL_P(zv)[Z_STRLEN_P(zv)-1];
262 if (*eos == '*') {
263 *eos = '\0';
264 *rfc5987 = 1;
265 Z_STRLEN_P(zv) -= 1;
266 }
267 }
268
269 if (flags & PHP_HTTP_PARAMS_URLENCODED) {
270 sanitize_urlencoded(zv);
271 }
272
273 if (flags & PHP_HTTP_PARAMS_DIMENSION) {
274 sanitize_dimension(zv);
275 }
276 }
277
278 static inline void sanitize_rfc5987(zval *zv, char **language, zend_bool *latin1)
279 {
280 char *ptr;
281
282 /* examples:
283 * iso-8850-1'de'bl%f6der%20schei%df%21
284 * utf-8'de-DE'bl%c3%b6der%20schei%c3%9f%21
285 */
286
287 switch (Z_STRVAL_P(zv)[0]) {
288 case 'I':
289 case 'i':
290 if (!strncasecmp(Z_STRVAL_P(zv), "iso-8859-1", lenof("iso-8859-1"))) {
291 *latin1 = 1;
292 ptr = Z_STRVAL_P(zv) + lenof("iso-8859-1");
293 break;
294 }
295 /* no break */
296 case 'U':
297 case 'u':
298 if (!strncasecmp(Z_STRVAL_P(zv), "utf-8", lenof("utf-8"))) {
299 *latin1 = 0;
300 ptr = Z_STRVAL_P(zv) + lenof("utf-8");
301 break;
302 }
303 /* no break */
304 default:
305 return;
306 }
307
308 /* extract language */
309 if (*ptr == '\'') {
310 for (*language = ++ptr; *ptr && *ptr != '\''; ++ptr);
311 if (!*ptr) {
312 *language = NULL;
313 return;
314 }
315 *language = estrndup(*language, ptr - *language);
316
317 /* remainder */
318 ptr = estrdup(++ptr);
319 zval_dtor(zv);
320 ZVAL_STR(zv, php_http_cs2zs(ptr, strlen(ptr)));
321 }
322 }
323
324 static inline void sanitize_rfc5988(char *str, size_t len, zval *zv)
325 {
326 zend_string *zs = zend_string_init(str, len, 0);
327
328 zval_dtor(zv);
329 ZVAL_STR(zv, php_trim(zs, " ><", 3, 3));
330 zend_string_release(zs);
331 }
332
333 static inline void prepare_rfc5988(zval *zv)
334 {
335 if (Z_TYPE_P(zv) != IS_STRING) {
336 zval_dtor(zv);
337 ZVAL_EMPTY_STRING(zv);
338 }
339 }
340
341 static void utf8encode(zval *zv)
342 {
343 size_t pos, len = 0;
344 unsigned char *ptr = (unsigned char *) Z_STRVAL_P(zv);
345
346 while (*ptr) {
347 if (*ptr++ >= 0x80) {
348 ++len;
349 }
350 ++len;
351 }
352
353 ptr = safe_emalloc(1, len, 1);
354 for (len = 0, pos = 0; len <= Z_STRLEN_P(zv); ++len, ++pos) {
355 ptr[pos] = Z_STRVAL_P(zv)[len];
356 if ((ptr[pos]) >= 0x80) {
357 ptr[pos + 1] = 0x80 | (ptr[pos] & 0x3f);
358 ptr[pos] = 0xc0 | ((ptr[pos] >> 6) & 0x1f);
359 ++pos;
360 }
361 }
362 zval_dtor(zv);
363 ZVAL_STR(zv, php_http_cs2zs((char *) ptr, pos-1));
364 }
365
366 static inline void sanitize_value(unsigned flags, const char *str, size_t len, zval *zv, zend_bool rfc5987)
367 {
368 char *language = NULL;
369 zend_bool latin1 = 0;
370 zend_string *zs = zend_string_init(str, len, 0);
371
372 zval_dtor(zv);
373 ZVAL_STR(zv, php_trim(zs, NULL, 0, 3));
374 zend_string_release(zs);
375
376 if (rfc5987) {
377 sanitize_rfc5987(zv, &language, &latin1);
378 }
379
380 if (flags & PHP_HTTP_PARAMS_ESCAPED) {
381 sanitize_escaped(zv);
382 }
383
384 if ((flags & PHP_HTTP_PARAMS_URLENCODED) || (rfc5987 && language)) {
385 sanitize_urlencoded(zv);
386 }
387
388 if (rfc5987 && language) {
389 zval tmp;
390
391 if (latin1) {
392 utf8encode(zv);
393 }
394
395 ZVAL_COPY_VALUE(&tmp, zv);
396 array_init(zv);
397 add_assoc_zval(zv, language, &tmp);
398 efree(language);
399 }
400 }
401
402 static inline void prepare_key(unsigned flags, char *old_key, size_t old_len, char **new_key, size_t *new_len)
403 {
404 zval zv;
405
406 ZVAL_STRINGL(&zv, old_key, old_len);
407
408 if (flags & PHP_HTTP_PARAMS_URLENCODED) {
409 prepare_urlencoded(&zv);
410 }
411
412 if (flags & PHP_HTTP_PARAMS_ESCAPED) {
413 if (flags & PHP_HTTP_PARAMS_RFC5988) {
414 prepare_rfc5988(&zv);
415 } else {
416 prepare_escaped(&zv);
417 }
418 }
419
420 *new_key = estrndup(Z_STRVAL(zv), Z_STRLEN(zv));
421 *new_len = Z_STRLEN(zv);
422 zval_ptr_dtor(&zv);
423 }
424
425 static inline void prepare_value(unsigned flags, zval *zv)
426 {
427 if (flags & PHP_HTTP_PARAMS_URLENCODED) {
428 prepare_urlencoded(zv);
429 }
430
431 if (flags & PHP_HTTP_PARAMS_ESCAPED) {
432 prepare_escaped(zv);
433 }
434 }
435
436 static void merge_param(HashTable *params, zval *zdata, zval **current_param, zval **current_args)
437 {
438 zval *ptr, *zdata_ptr;
439 php_http_arrkey_t hkey = {0};
440
441 #if 0
442 {
443 zval tmp;
444 INIT_PZVAL_ARRAY(&tmp, params);
445 fprintf(stderr, "params = ");
446 zend_print_zval_r(&tmp, 1);
447 fprintf(stderr, "\n");
448 }
449 #endif
450
451 zend_hash_get_current_key(Z_ARRVAL_P(zdata), &hkey.key, &hkey.h);
452
453 if ((hkey.key && !zend_hash_exists(params, hkey.key))
454 || (!hkey.key && !zend_hash_index_exists(params, hkey.h))
455 ) {
456 zval tmp, arg, *args;
457
458 /* create the entry if it doesn't exist */
459 ptr = zend_hash_get_current_data(Z_ARRVAL_P(zdata));
460 Z_TRY_ADDREF_P(ptr);
461 array_init(&tmp);
462 add_assoc_zval_ex(&tmp, ZEND_STRL("value"), ptr);
463
464 array_init(&arg);
465 args = zend_hash_str_update(Z_ARRVAL(tmp), "arguments", lenof("arguments"), &arg);
466 *current_args = args;
467
468 if (hkey.key) {
469 ptr = zend_hash_update(params, hkey.key, &tmp);
470 } else {
471 ptr = zend_hash_index_update(params, hkey.h, &tmp);
472 }
473 } else {
474 /* merge */
475 if (hkey.key) {
476 ptr = zend_hash_find(params, hkey.key);
477 } else {
478 ptr = zend_hash_index_find(params, hkey.h);
479 }
480
481 zdata_ptr = zdata;
482
483 if (Z_TYPE_P(ptr) == IS_ARRAY
484 && (ptr = zend_hash_str_find(Z_ARRVAL_P(ptr), "value", lenof("value")))
485 && (zdata_ptr = zend_hash_get_current_data(Z_ARRVAL_P(zdata_ptr)))
486 ) {
487 /*
488 * params = [arr => [value => [0 => 1]]]
489 * ^- ptr
490 * zdata = [arr => [0 => NULL]]
491 * ^- zdata_ptr
492 */
493 zval *test_ptr;
494
495 while (Z_TYPE_P(zdata_ptr) == IS_ARRAY && (test_ptr = zend_hash_get_current_data(Z_ARRVAL_P(zdata_ptr)))) {
496 if (Z_TYPE_P(test_ptr) == IS_ARRAY && Z_TYPE_P(ptr) == IS_ARRAY) {
497 zval *tmp_ptr = ptr;
498
499 /* now find key in ptr */
500 if (HASH_KEY_IS_STRING == zend_hash_get_current_key(Z_ARRVAL_P(zdata_ptr), &hkey.key, &hkey.h)) {
501 if ((ptr = zend_hash_find(Z_ARRVAL_P(ptr), hkey.key))) {
502 zdata_ptr = test_ptr;
503 } else {
504 ptr = tmp_ptr;
505 Z_TRY_ADDREF_P(test_ptr);
506 ptr = zend_hash_update(Z_ARRVAL_P(ptr), hkey.key, test_ptr);
507 break;
508 }
509 } else {
510 if ((ptr = zend_hash_index_find(Z_ARRVAL_P(ptr), hkey.h))) {
511 zdata_ptr = test_ptr;
512 } else if (hkey.h) {
513 ptr = tmp_ptr;
514 Z_TRY_ADDREF_P(test_ptr);
515 ptr = zend_hash_index_update(Z_ARRVAL_P(ptr), hkey.h, test_ptr);
516 break;
517 } else {
518 ptr = tmp_ptr;
519 Z_TRY_ADDREF_P(test_ptr);
520 ptr = zend_hash_next_index_insert(Z_ARRVAL_P(ptr), test_ptr);
521 break;
522 }
523 }
524 } else {
525 /* this is the leaf */
526 Z_TRY_ADDREF_P(test_ptr);
527 if (Z_TYPE_P(ptr) != IS_ARRAY) {
528 zval_dtor(ptr);
529 array_init(ptr);
530 }
531 if (HASH_KEY_IS_STRING == zend_hash_get_current_key(Z_ARRVAL_P(zdata_ptr), &hkey.key, &hkey.h)) {
532 ptr = zend_hash_update(Z_ARRVAL_P(ptr), hkey.key, test_ptr);
533 } else if (hkey.h) {
534 ptr = zend_hash_index_update(Z_ARRVAL_P(ptr), hkey.h, test_ptr);
535 } else {
536 ptr = zend_hash_next_index_insert(Z_ARRVAL_P(ptr), test_ptr);
537 }
538 break;
539 }
540 }
541
542 }
543 }
544
545 /* bubble up */
546 while (Z_TYPE_P(ptr) == IS_ARRAY) {
547 zval *tmp = zend_hash_get_current_data(Z_ARRVAL_P(ptr));
548
549 if (tmp) {
550 ptr = tmp;
551 } else {
552 break;
553 }
554 }
555 *current_param = ptr;
556 }
557
558 static void push_param(HashTable *params, php_http_params_state_t *state, const php_http_params_opts_t *opts)
559 {
560 if (state->val.str) {
561 if (!state->current.val) {
562 return;
563 } else if (0 < (state->val.len = state->input.str - state->val.str)) {
564 sanitize_value(opts->flags, state->val.str, state->val.len, state->current.val, state->rfc5987);
565 } else {
566 ZVAL_EMPTY_STRING(state->current.val);
567 }
568 state->rfc5987 = 0;
569 } else if (state->arg.str) {
570 if (0 < (state->arg.len = state->input.str - state->arg.str)) {
571 zval val, key;
572 zend_bool rfc5987 = 0;
573
574 ZVAL_NULL(&key);
575 sanitize_key(opts->flags, state->arg.str, state->arg.len, &key, &rfc5987);
576 state->rfc5987 = rfc5987;
577 if (Z_TYPE(key) == IS_STRING && Z_STRLEN(key)) {
578 ZVAL_TRUE(&val);
579
580 if (rfc5987) {
581 zval *rfc;
582
583 if ((rfc = zend_hash_str_find(Z_ARRVAL_P(state->current.args), ZEND_STRL("*rfc5987*")))) {
584 state->current.val = zend_symtable_str_update(Z_ARRVAL_P(rfc), Z_STRVAL(key), Z_STRLEN(key), &val);
585 } else {
586 zval tmp;
587
588 array_init_size(&tmp, 1);
589 state->current.val = zend_symtable_str_update(Z_ARRVAL(tmp), Z_STRVAL(key), Z_STRLEN(key), &val);
590 zend_symtable_str_update(Z_ARRVAL_P(state->current.args), ZEND_STRL("*rfc5987*"), &tmp);
591 }
592 } else {
593 state->current.val = zend_symtable_str_update(Z_ARRVAL_P(state->current.args), Z_STRVAL(key), Z_STRLEN(key), &val);
594 }
595 }
596 zval_dtor(&key);
597 }
598 } else if (state->param.str) {
599 if (0 < (state->param.len = state->input.str - state->param.str)) {
600 zval prm, arg, val, key;
601 zend_bool rfc5987 = 0;
602
603 ZVAL_NULL(&key);
604 if (opts->flags & PHP_HTTP_PARAMS_RFC5988) {
605 sanitize_rfc5988(state->param.str, state->param.len, &key);
606 } else {
607 sanitize_key(opts->flags, state->param.str, state->param.len, &key, &rfc5987);
608 state->rfc5987 = rfc5987;
609 }
610 if (Z_TYPE(key) == IS_ARRAY) {
611 merge_param(params, &key, &state->current.val, &state->current.args);
612 } else if (Z_TYPE(key) == IS_STRING && Z_STRLEN(key)) {
613 // FIXME: array_init_size(&prm, 2);
614 array_init(&prm);
615
616 if (!Z_ISUNDEF(opts->defval)) {
617 ZVAL_COPY_VALUE(&val, &opts->defval);
618 zval_copy_ctor(&val);
619 } else {
620 ZVAL_TRUE(&val);
621 }
622 if (rfc5987 && (opts->flags & PHP_HTTP_PARAMS_RFC5987)) {
623 state->current.val = zend_hash_str_update(Z_ARRVAL(prm), "*rfc5987*", lenof("*rfc5987*"), &val);
624 } else {
625 state->current.val = zend_hash_str_update(Z_ARRVAL(prm), "value", lenof("value"), &val);
626 }
627 // FIXME: array_init_size(&arg, 3);
628 array_init(&arg);
629 state->current.args = zend_hash_str_update(Z_ARRVAL(prm), "arguments", lenof("arguments"), &arg);
630 state->current.param = zend_symtable_str_update(params, Z_STRVAL(key), Z_STRLEN(key), &prm);
631 }
632 zval_ptr_dtor(&key);
633 }
634 }
635 }
636
637 static inline zend_bool check_str(const char *chk_str, size_t chk_len, const char *sep_str, size_t sep_len) {
638 return 0 < sep_len && chk_len >= sep_len && *chk_str == *sep_str && !memcmp(chk_str + 1, sep_str + 1, sep_len - 1);
639 }
640
641 static size_t check_sep(php_http_params_state_t *state, php_http_params_token_t **separators)
642 {
643 php_http_params_token_t **sep = separators;
644
645 if (state->quotes || state->escape) {
646 return 0;
647 }
648
649 if (sep) while (*sep) {
650 if (check_str(state->input.str, state->input.len, (*sep)->str, (*sep)->len)) {
651 return (*sep)->len;
652 }
653 ++sep;
654 }
655 return 0;
656 }
657
658 static void skip_sep(size_t skip, php_http_params_state_t *state, php_http_params_token_t **param, php_http_params_token_t **arg, php_http_params_token_t **val)
659 {
660 size_t sep_len;
661
662 state->input.str += skip;
663 state->input.len -= skip;
664
665 while ( (param && (sep_len = check_sep(state, param)))
666 || (arg && (sep_len = check_sep(state, arg)))
667 || (val && (sep_len = check_sep(state, val)))
668 ) {
669 state->input.str += sep_len;
670 state->input.len -= sep_len;
671 }
672 }
673
674 HashTable *php_http_params_parse(HashTable *params, const php_http_params_opts_t *opts)
675 {
676 php_http_params_state_t state = {{NULL,0}, {NULL,0}, {NULL,0}, {NULL,0}, {NULL,NULL,NULL}, 0, 0};
677
678 state.input.str = opts->input.str;
679 state.input.len = opts->input.len;
680
681 if (!params) {
682 ALLOC_HASHTABLE(params);
683 ZEND_INIT_SYMTABLE(params);
684 }
685
686 while (state.input.len) {
687 if ((opts->flags & PHP_HTTP_PARAMS_RFC5988) && !state.arg.str) {
688 if (*state.input.str == '<') {
689 state.quotes = 1;
690 } else if (*state.input.str == '>') {
691 state.quotes = 0;
692 }
693 } else if (*state.input.str == '"' && !state.escape) {
694 state.quotes = !state.quotes;
695 } else {
696 state.escape = (*state.input.str == '\\');
697 }
698
699 if (!state.param.str) {
700 /* initialize */
701 skip_sep(0, &state, opts->param, opts->arg, opts->val);
702 state.param.str = state.input.str;
703 } else {
704 size_t sep_len;
705 /* are we at a param separator? */
706 if (0 < (sep_len = check_sep(&state, opts->param))) {
707 push_param(params, &state, opts);
708
709 skip_sep(sep_len, &state, opts->param, opts->arg, opts->val);
710
711 /* start off with a new param */
712 state.param.str = state.input.str;
713 state.param.len = 0;
714 state.arg.str = NULL;
715 state.arg.len = 0;
716 state.val.str = NULL;
717 state.val.len = 0;
718
719 continue;
720
721 } else
722 /* are we at an arg separator? */
723 if (0 < (sep_len = check_sep(&state, opts->arg))) {
724 push_param(params, &state, opts);
725
726 skip_sep(sep_len, &state, NULL, opts->arg, opts->val);
727
728 /* continue with a new arg */
729 state.arg.str = state.input.str;
730 state.arg.len = 0;
731 state.val.str = NULL;
732 state.val.len = 0;
733
734 continue;
735
736 } else
737 /* are we at a val separator? */
738 if (0 < (sep_len = check_sep(&state, opts->val))) {
739 /* only handle separator if we're not already reading in a val */
740 if (!state.val.str) {
741 push_param(params, &state, opts);
742
743 skip_sep(sep_len, &state, NULL, NULL, opts->val);
744
745 state.val.str = state.input.str;
746 state.val.len = 0;
747
748 continue;
749 }
750 }
751 }
752
753 if (state.input.len) {
754 ++state.input.str;
755 --state.input.len;
756 }
757 }
758 /* finalize */
759 push_param(params, &state, opts);
760
761 return params;
762 }
763
764 static inline void shift_key(php_http_buffer_t *buf, char *key_str, size_t key_len, const char *ass, size_t asl, unsigned flags)
765 {
766 char *str;
767 size_t len;
768
769 if (buf->used) {
770 php_http_buffer_append(buf, ass, asl);
771 }
772
773 prepare_key(flags, key_str, key_len, &str, &len);
774 php_http_buffer_append(buf, str, len);
775 efree(str);
776 }
777
778 static inline void shift_rfc5987(php_http_buffer_t *buf, zval *zvalue, const char *vss, size_t vsl, unsigned flags)
779 {
780 HashTable *ht = HASH_OF(zvalue);
781 zval *zdata, tmp;
782 zend_string *zs;
783 php_http_arrkey_t key = {0};
784
785 if ((zdata = zend_hash_get_current_data(ht))
786 && HASH_KEY_NON_EXISTENT != zend_hash_get_current_key(ht, &key.key, &key.h)
787 ) {
788 php_http_arrkey_stringify(&key, NULL);
789 php_http_buffer_appendf(buf, "*%.*sutf-8'%.*s'",
790 (int) (vsl > INT_MAX ? INT_MAX : vsl), vss,
791 (int) (key.key->len > INT_MAX ? INT_MAX : key.key->len), key.key->val);
792 php_http_arrkey_dtor(&key);
793
794 if (Z_TYPE_P(zdata) == IS_INDIRECT) {
795 zdata = Z_INDIRECT_P(zdata);
796 }
797 zs = zval_get_string(zdata);
798 ZVAL_STR(&tmp, zs);
799 prepare_value(flags | PHP_HTTP_PARAMS_URLENCODED, &tmp);
800 php_http_buffer_append(buf, Z_STRVAL(tmp), Z_STRLEN(tmp));
801 zval_ptr_dtor(&tmp);
802 }
803 }
804
805 static inline void shift_rfc5988(php_http_buffer_t *buf, char *key_str, size_t key_len, const char *ass, size_t asl, unsigned flags)
806 {
807 char *str;
808 size_t len;
809
810 if (buf->used) {
811 php_http_buffer_append(buf, ass, asl);
812 }
813
814 prepare_key(flags, key_str, key_len, &str, &len);
815 php_http_buffer_appends(buf, "<");
816 php_http_buffer_append(buf, str, len);
817 php_http_buffer_appends(buf, ">");
818 efree(str);
819 }
820
821 static inline void shift_rfc5988_val(php_http_buffer_t *buf, zval *zv, const char *vss, size_t vsl, unsigned flags)
822 {
823 zend_string *str, *zs = zval_get_string(zv);
824
825 str = quote_string(zs, 1);
826 zend_string_release(zs);
827
828 php_http_buffer_append(buf, vss, vsl);
829 php_http_buffer_append(buf, str->val, str->len);
830 zend_string_release(str);
831 }
832
833 static inline void shift_val(php_http_buffer_t *buf, zval *zvalue, const char *vss, size_t vsl, unsigned flags)
834 {
835 zval tmp;
836 zend_string *zs;
837
838 switch (Z_TYPE_P(zvalue)) {
839 case IS_TRUE:
840 break;
841
842 case IS_FALSE:
843 php_http_buffer_append(buf, vss, vsl);
844 php_http_buffer_appends(buf, "0");
845 break;
846
847 default:
848 zs = zval_get_string(zvalue);
849
850 ZVAL_STR(&tmp, zs);
851 prepare_value(flags, &tmp);
852 php_http_buffer_append(buf, vss, vsl);
853 php_http_buffer_append(buf, Z_STRVAL(tmp), Z_STRLEN(tmp));
854
855 zval_ptr_dtor(&tmp);
856 break;
857 }
858 }
859
860 static void shift_arg(php_http_buffer_t *buf, char *key_str, size_t key_len, zval *zvalue, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags)
861 {
862 if (Z_TYPE_P(zvalue) == IS_ARRAY || Z_TYPE_P(zvalue) == IS_OBJECT) {
863 php_http_arrkey_t key;
864 HashTable *ht = HASH_OF(zvalue);
865 zval *val;
866 zend_bool rfc5987 = !strcmp(key_str, "*rfc5987*");
867
868 if (!rfc5987) {
869 shift_key(buf, key_str, key_len, ass, asl, flags);
870 }
871 ZEND_HASH_FOREACH_KEY_VAL_IND(ht, key.h, key.key, val)
872 {
873 /* did you mean recursion? */
874 php_http_arrkey_stringify(&key, NULL);
875 if (rfc5987 && (Z_TYPE_P(val) == IS_ARRAY || Z_TYPE_P(val) == IS_OBJECT)) {
876 shift_key(buf, key.key->val, key.key->len, ass, asl, flags);
877 shift_rfc5987(buf, val, vss, vsl, flags);
878 } else {
879 shift_arg(buf, key.key->val, key.key->len, val, ass, asl, vss, vsl, flags);
880 }
881 php_http_arrkey_dtor(&key);
882 }
883 ZEND_HASH_FOREACH_END();
884 } else {
885 shift_key(buf, key_str, key_len, ass, asl, flags);
886
887 if (flags & PHP_HTTP_PARAMS_RFC5988) {
888 switch (key_len) {
889 case lenof("rel"):
890 case lenof("title"):
891 case lenof("anchor"):
892 /* some args must be quoted */
893 if (0 <= php_http_select_str(key_str, 3, "rel", "title", "anchor")) {
894 shift_rfc5988_val(buf, zvalue, vss, vsl, flags);
895 return;
896 }
897 break;
898 }
899 }
900
901 shift_val(buf, zvalue, vss, vsl, flags);
902 }
903 }
904
905 static void shift_param(php_http_buffer_t *buf, char *key_str, size_t key_len, zval *zvalue, const char *pss, size_t psl, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags, zend_bool rfc5987)
906 {
907 if (Z_TYPE_P(zvalue) == IS_ARRAY || Z_TYPE_P(zvalue) == IS_OBJECT) {
908 /* treat as arguments, unless we care for dimensions or rfc5987 */
909 if (flags & PHP_HTTP_PARAMS_DIMENSION) {
910 php_http_buffer_t *keybuf = php_http_buffer_from_string(key_str, key_len);
911 prepare_dimension(buf, keybuf, zvalue, pss, psl, vss, vsl, flags);
912 php_http_buffer_free(&keybuf);
913 } else if (rfc5987) {
914 shift_key(buf, key_str, key_len, pss, psl, flags);
915 shift_rfc5987(buf, zvalue, vss, vsl, flags);
916 } else {
917 shift_arg(buf, key_str, key_len, zvalue, ass, asl, vss, vsl, flags);
918 }
919 } else {
920 if (flags & PHP_HTTP_PARAMS_RFC5988) {
921 shift_rfc5988(buf, key_str, key_len, pss, psl, flags);
922 } else {
923 shift_key(buf, key_str, key_len, pss, psl, flags);
924 }
925 shift_val(buf, zvalue, vss, vsl, flags);
926 }
927 }
928
929 php_http_buffer_t *php_http_params_to_string(php_http_buffer_t *buf, HashTable *params, const char *pss, size_t psl, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags)
930 {
931 zval *zparam;
932 php_http_arrkey_t key;
933 zend_bool rfc5987 = 0;
934
935 if (!buf) {
936 buf = php_http_buffer_init(NULL);
937 }
938
939 ZEND_HASH_FOREACH_KEY_VAL(params, key.h, key.key, zparam)
940 {
941 zval *zvalue, *zargs;
942
943 if (Z_TYPE_P(zparam) != IS_ARRAY) {
944 zvalue = zparam;
945 } else {
946 if (!(zvalue = zend_hash_str_find(Z_ARRVAL_P(zparam), ZEND_STRL("value")))) {
947 if (!(zvalue = zend_hash_str_find(Z_ARRVAL_P(zparam), ZEND_STRL("*rfc5987*")))) {
948 zvalue = zparam;
949 } else {
950 rfc5987 = 1;
951 }
952 }
953 }
954
955 php_http_arrkey_stringify(&key, NULL);
956 shift_param(buf, key.key->val, key.key->len, zvalue, pss, psl, ass, asl, vss, vsl, flags, rfc5987);
957 php_http_arrkey_dtor(&key);
958
959 if (Z_TYPE_P(zparam) == IS_ARRAY) {
960 zval *tmp = zend_hash_str_find(Z_ARRVAL_P(zparam), ZEND_STRL("arguments"));
961
962 if (tmp) {
963 zvalue = tmp;
964 } else if (zvalue == zparam) {
965 continue;
966 } else {
967 zvalue = zparam;
968 }
969 }
970
971 if (Z_TYPE_P(zvalue) == IS_ARRAY) {
972 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(zvalue), key.h, key.key, zargs)
973 {
974 if (zvalue == zparam && key.key && zend_string_equals_literal(key.key, "value")) {
975 continue;
976 }
977
978 php_http_arrkey_stringify(&key, NULL);
979 shift_arg(buf, key.key->val, key.key->len, zargs, ass, asl, vss, vsl, flags);
980 php_http_arrkey_dtor(&key);
981 }
982 ZEND_HASH_FOREACH_END();
983 }
984 }
985 ZEND_HASH_FOREACH_END();
986
987 php_http_buffer_shrink(buf);
988 php_http_buffer_fix(buf);
989
990 return buf;
991 }
992
993 php_http_params_token_t **php_http_params_separator_init(zval *zv)
994 {
995 zval *sep, ztmp;
996 php_http_params_token_t **ret, **tmp;
997
998 if (!zv) {
999 return NULL;
1000 }
1001
1002 ZVAL_DUP(&ztmp, zv);
1003 zv = &ztmp;
1004 convert_to_array(zv);
1005
1006 ret = ecalloc(zend_hash_num_elements(Z_ARRVAL_P(zv)) + 1, sizeof(*ret));
1007
1008 tmp = ret;
1009 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zv), sep)
1010 {
1011 zend_string *zs = zval_get_string(sep);
1012
1013 if (zs->len) {
1014 *tmp = emalloc(sizeof(**tmp));
1015 (*tmp)->str = estrndup(zs->val, (*tmp)->len = zs->len);
1016 ++tmp;
1017 }
1018 zend_string_release(zs);
1019 }
1020 ZEND_HASH_FOREACH_END();
1021
1022 zval_ptr_dtor(&ztmp);
1023
1024 *tmp = NULL;
1025 return ret;
1026 }
1027
1028 void php_http_params_separator_free(php_http_params_token_t **separator)
1029 {
1030 php_http_params_token_t **sep = separator;
1031 if (sep) {
1032 while (*sep) {
1033 PTR_FREE((*sep)->str);
1034 efree(*sep);
1035 ++sep;
1036 }
1037 efree(separator);
1038 }
1039 }
1040
1041 static zend_class_entry *php_http_params_class_entry;
1042 zend_class_entry *php_http_params_get_class_entry(void)
1043 {
1044 return php_http_params_class_entry;
1045 }
1046
1047 ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams___construct, 0, 0, 0)
1048 ZEND_ARG_INFO(0, params)
1049 ZEND_ARG_INFO(0, param_sep)
1050 ZEND_ARG_INFO(0, arg_sep)
1051 ZEND_ARG_INFO(0, val_sep)
1052 ZEND_ARG_INFO(0, flags)
1053 ZEND_END_ARG_INFO();
1054 PHP_METHOD(HttpParams, __construct)
1055 {
1056 zval *zparams = NULL, *param_sep = NULL, *arg_sep = NULL, *val_sep = NULL;
1057 zend_long flags = PHP_HTTP_PARAMS_DEFAULT;
1058 zend_error_handling zeh;
1059 zend_string *zs;
1060
1061 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|z!/z/z/z/l", &zparams, &param_sep, &arg_sep, &val_sep, &flags), invalid_arg, return);
1062
1063 zend_replace_error_handling(EH_THROW, php_http_get_exception_runtime_class_entry(), &zeh);
1064 {
1065 switch (ZEND_NUM_ARGS()) {
1066 case 5:
1067 zend_update_property_long(php_http_params_class_entry, getThis(), ZEND_STRL("flags"), flags);
1068 /* no break */
1069 case 4:
1070 zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("val_sep"), val_sep);
1071 /* no break */
1072 case 3:
1073 zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("arg_sep"), arg_sep);
1074 /* no break */
1075 case 2:
1076 zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("param_sep"), param_sep);
1077 /* no break */
1078 }
1079
1080 if (zparams) {
1081 switch (Z_TYPE_P(zparams)) {
1082 case IS_OBJECT:
1083 case IS_ARRAY:
1084 convert_to_array(zparams);
1085 zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), zparams);
1086 break;
1087 default:
1088 zs = zval_get_string(zparams);
1089 if (zs->len) {
1090 zval tmp;
1091
1092 php_http_params_opts_t opts = {
1093 {zs->val, zs->len},
1094 php_http_params_separator_init(zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("param_sep"), 0, &tmp)),
1095 php_http_params_separator_init(zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("arg_sep"), 0, &tmp)),
1096 php_http_params_separator_init(zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("val_sep"), 0, &tmp)),
1097 {{0}}, flags
1098 };
1099
1100 array_init(&tmp);
1101 php_http_params_parse(Z_ARRVAL(tmp), &opts);
1102 zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), &tmp);
1103 zval_ptr_dtor(&tmp);
1104
1105 php_http_params_separator_free(opts.param);
1106 php_http_params_separator_free(opts.arg);
1107 php_http_params_separator_free(opts.val);
1108 }
1109 zend_string_release(zs);
1110 break;
1111 }
1112 } else {
1113 zval tmp;
1114
1115 array_init(&tmp);
1116 zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), &tmp);
1117 zval_ptr_dtor(&tmp);
1118 }
1119 }
1120 zend_restore_error_handling(&zeh);
1121 }
1122
1123 ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_toArray, 0, 0, 0)
1124 ZEND_END_ARG_INFO();
1125 PHP_METHOD(HttpParams, toArray)
1126 {
1127 zval zparams_tmp, *zparams;
1128
1129 if (SUCCESS != zend_parse_parameters_none()) {
1130 return;
1131 }
1132 zparams = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0, &zparams_tmp);
1133 RETURN_ZVAL(zparams, 1, 0);
1134 }
1135
1136 ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_toString, 0, 0, 0)
1137 ZEND_END_ARG_INFO();
1138 PHP_METHOD(HttpParams, toString)
1139 {
1140 zval *tmp, *zparams, *zpsep, *zasep, *zvsep;
1141 zval zparams_tmp, flags_tmp, psep_tmp, asep_tmp, vsep_tmp;
1142 zend_string *psep, *asep, *vsep;
1143 long flags;
1144 php_http_buffer_t buf;
1145
1146 zparams = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0, &zparams_tmp);
1147 convert_to_array_ex(zparams);
1148 flags = zval_get_long(zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("flags"), 0, &flags_tmp));
1149
1150 zpsep = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("param_sep"), 0, &psep_tmp);
1151 if (Z_TYPE_P(zpsep) == IS_ARRAY && (tmp = zend_hash_get_current_data(Z_ARRVAL_P(zpsep)))) {
1152 psep = zval_get_string(tmp);
1153 } else {
1154 psep = zval_get_string(zpsep);
1155 }
1156 zasep = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("arg_sep"), 0, &asep_tmp);
1157 if (Z_TYPE_P(zasep) == IS_ARRAY && (tmp = zend_hash_get_current_data(Z_ARRVAL_P(zasep)))) {
1158 asep = zval_get_string(tmp);
1159 } else {
1160 asep = zval_get_string(zasep);
1161 }
1162 zvsep = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("val_sep"), 0, &vsep_tmp);
1163 if (Z_TYPE_P(zvsep) == IS_ARRAY && (tmp = zend_hash_get_current_data(Z_ARRVAL_P(zvsep)))) {
1164 vsep = zval_get_string(tmp);
1165 } else {
1166 vsep = zval_get_string(zvsep);
1167 }
1168
1169 php_http_buffer_init(&buf);
1170 php_http_params_to_string(&buf, Z_ARRVAL_P(zparams), psep->val, psep->len, asep->val, asep->len, vsep->val, vsep->len, flags);
1171
1172 zend_string_release(psep);
1173 zend_string_release(asep);
1174 zend_string_release(vsep);
1175
1176 RETVAL_STR(php_http_cs2zs(buf.data, buf.used));
1177 }
1178
1179 ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_offsetExists, 0, 0, 1)
1180 ZEND_ARG_INFO(0, name)
1181 ZEND_END_ARG_INFO();
1182 PHP_METHOD(HttpParams, offsetExists)
1183 {
1184 zend_string *name;
1185 zval zparams_tmp, *zparam, *zparams;
1186
1187 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name)) {
1188 return;
1189 }
1190
1191 zparams = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0, &zparams_tmp);
1192
1193 if (Z_TYPE_P(zparams) == IS_ARRAY && (zparam = zend_symtable_find(Z_ARRVAL_P(zparams), name))) {
1194 RETVAL_BOOL(Z_TYPE_P(zparam) != IS_NULL);
1195 } else {
1196 RETVAL_FALSE;
1197 }
1198 }
1199
1200 ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_offsetGet, 0, 0, 1)
1201 ZEND_ARG_INFO(0, name)
1202 ZEND_END_ARG_INFO();
1203 PHP_METHOD(HttpParams, offsetGet)
1204 {
1205 zend_string *name;
1206 zval zparams_tmp, *zparam, *zparams;
1207
1208 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name)) {
1209 return;
1210 }
1211
1212 zparams = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0, &zparams_tmp);
1213
1214 if (Z_TYPE_P(zparams) == IS_ARRAY && (zparam = zend_symtable_find(Z_ARRVAL_P(zparams), name))) {
1215 RETVAL_ZVAL(zparam, 1, 0);
1216 }
1217 }
1218
1219 ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_offsetUnset, 0, 0, 1)
1220 ZEND_ARG_INFO(0, name)
1221 ZEND_END_ARG_INFO();
1222 PHP_METHOD(HttpParams, offsetUnset)
1223 {
1224 zend_string *name;
1225 zval zparams_tmp, *zparams;
1226
1227 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name)) {
1228 return;
1229 }
1230
1231 zparams = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0, &zparams_tmp);
1232
1233 if (Z_TYPE_P(zparams) == IS_ARRAY) {
1234 zend_symtable_del(Z_ARRVAL_P(zparams), name);
1235 }
1236 }
1237
1238 ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_offsetSet, 0, 0, 2)
1239 ZEND_ARG_INFO(0, name)
1240 ZEND_ARG_INFO(0, value)
1241 ZEND_END_ARG_INFO();
1242 PHP_METHOD(HttpParams, offsetSet)
1243 {
1244 zend_string *name;
1245 zval zparams_tmp, *zparam, *zparams, *nvalue;
1246
1247 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "Sz", &name, &nvalue)) {
1248 return;
1249 }
1250
1251 zparams = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0, &zparams_tmp);
1252 convert_to_array(zparams);
1253
1254 if (name->len) {
1255 if (Z_TYPE_P(nvalue) == IS_ARRAY) {
1256 if ((zparam = zend_symtable_find(Z_ARRVAL_P(zparams), name))) {
1257 convert_to_array(zparam);
1258 array_join(Z_ARRVAL_P(nvalue), Z_ARRVAL_P(zparam), 0, 0);
1259 } else {
1260 Z_TRY_ADDREF_P(nvalue);
1261 add_assoc_zval_ex(zparams, name->val, name->len, nvalue);
1262 }
1263 } else {
1264 zval tmp;
1265
1266 if ((zparam = zend_symtable_find(Z_ARRVAL_P(zparams), name))) {
1267 ZVAL_DUP(&tmp, zparam);
1268 convert_to_array(&tmp);
1269 } else {
1270 array_init(&tmp);
1271 }
1272
1273 Z_TRY_ADDREF_P(nvalue);
1274 add_assoc_zval_ex(&tmp, ZEND_STRL("value"), nvalue);
1275 add_assoc_zval_ex(zparams, name->val, name->len, &tmp);
1276 }
1277 } else {
1278 zval arr;
1279 zend_string *zs = zval_get_string(nvalue);
1280
1281 array_init(&arr);
1282 add_assoc_bool_ex(&arr, ZEND_STRL("value"), 1);
1283 add_assoc_zval_ex(zparams, zs->val, zs->len, &arr);
1284 zend_string_release(zs);
1285 }
1286 }
1287
1288 static zend_function_entry php_http_params_methods[] = {
1289 PHP_ME(HttpParams, __construct, ai_HttpParams___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR|ZEND_ACC_FINAL)
1290
1291 PHP_ME(HttpParams, toArray, ai_HttpParams_toArray, ZEND_ACC_PUBLIC)
1292 PHP_ME(HttpParams, toString, ai_HttpParams_toString, ZEND_ACC_PUBLIC)
1293 ZEND_MALIAS(HttpParams, __toString, toString, ai_HttpParams_toString, ZEND_ACC_PUBLIC)
1294
1295 PHP_ME(HttpParams, offsetExists, ai_HttpParams_offsetExists, ZEND_ACC_PUBLIC)
1296 PHP_ME(HttpParams, offsetUnset, ai_HttpParams_offsetUnset, ZEND_ACC_PUBLIC)
1297 PHP_ME(HttpParams, offsetSet, ai_HttpParams_offsetSet, ZEND_ACC_PUBLIC)
1298 PHP_ME(HttpParams, offsetGet, ai_HttpParams_offsetGet, ZEND_ACC_PUBLIC)
1299
1300 EMPTY_FUNCTION_ENTRY
1301 };
1302
1303 PHP_MINIT_FUNCTION(http_params)
1304 {
1305 zend_class_entry ce = {0};
1306
1307 INIT_NS_CLASS_ENTRY(ce, "http", "Params", php_http_params_methods);
1308 php_http_params_class_entry = zend_register_internal_class(&ce);
1309 php_http_params_class_entry->create_object = php_http_params_object_new;
1310 zend_class_implements(php_http_params_class_entry, 1, zend_ce_arrayaccess);
1311
1312 zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("DEF_PARAM_SEP"), ZEND_STRL(","));
1313 zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("DEF_ARG_SEP"), ZEND_STRL(";"));
1314 zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("DEF_VAL_SEP"), ZEND_STRL("="));
1315 zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("COOKIE_PARAM_SEP"), ZEND_STRL(""));
1316
1317 zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_RAW"), PHP_HTTP_PARAMS_RAW);
1318 zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_ESCAPED"), PHP_HTTP_PARAMS_ESCAPED);
1319 zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_URLENCODED"), PHP_HTTP_PARAMS_URLENCODED);
1320 zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_DIMENSION"), PHP_HTTP_PARAMS_DIMENSION);
1321 zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_RFC5987"), PHP_HTTP_PARAMS_RFC5987);
1322 zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_RFC5988"), PHP_HTTP_PARAMS_RFC5988);
1323 zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_DEFAULT"), PHP_HTTP_PARAMS_DEFAULT);
1324 zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_QUERY"), PHP_HTTP_PARAMS_QUERY);
1325
1326 zend_declare_property_null(php_http_params_class_entry, ZEND_STRL("params"), ZEND_ACC_PUBLIC);
1327 zend_declare_property_stringl(php_http_params_class_entry, ZEND_STRL("param_sep"), ZEND_STRL(","), ZEND_ACC_PUBLIC);
1328 zend_declare_property_stringl(php_http_params_class_entry, ZEND_STRL("arg_sep"), ZEND_STRL(";"), ZEND_ACC_PUBLIC);
1329 zend_declare_property_stringl(php_http_params_class_entry, ZEND_STRL("val_sep"), ZEND_STRL("="), ZEND_ACC_PUBLIC);
1330 zend_declare_property_long(php_http_params_class_entry, ZEND_STRL("flags"), PHP_HTTP_PARAMS_DEFAULT, ZEND_ACC_PUBLIC);
1331
1332 return SUCCESS;
1333 }
1334
1335 /*
1336 * Local variables:
1337 * tab-width: 4
1338 * c-basic-offset: 4
1339 * End:
1340 * vim600: noet sw=4 ts=4 fdm=marker
1341 * vim<600: noet sw=4 ts=4
1342 */
1343