+--------------------------------------------------------------------+
*/
-#include "php_http.h"
+#include "php_http_api.h"
-#include <ext/standard/php_string.h>
+static int php_http_negotiate_sort(const void *a, const void *b TSRMLS_DC)
+{
+ zval result, *first, *second;
-#ifndef PHP_HTTP_DBG_NEG
-# define PHP_HTTP_DBG_NEG 0
-#endif
+ first = *((zval **) (*((Bucket **) a))->pData);
+ second= *((zval **) (*((Bucket **) b))->pData);
-char *php_http_negotiate_language_func(const char *test, double *quality, HashTable *supported TSRMLS_DC)
+ if (numeric_compare_function(&result, first, second TSRMLS_CC) != SUCCESS) {
+ return 0;
+ }
+ return (Z_LVAL(result) > 0 ? -1 : (Z_LVAL(result) < 0 ? 1 : 0));
+}
+
+#define M_PRI 5
+#define M_SEC 2
+#define M_ANY 1
+#define M_NOT 0
+#define M_ALL -1
+static inline unsigned php_http_negotiate_match(const char *param_str, size_t param_len, const char *supported_str, size_t supported_len, const char *sep_str, size_t sep_len)
{
- zval **value;
- HashPosition pos;
- const char *dash_test;
+ int match = M_NOT;
+
+ if (param_len == supported_len && !strncasecmp(param_str, supported_str, param_len)) {
+ /* that was easy */
+ match = M_ALL;
+ } else if (sep_str && sep_len) {
+ const char *param_sec = php_http_locate_str(param_str, param_len, sep_str, sep_len);
+ size_t param_pri_len = param_sec ? param_sec - param_str : param_len;
+ const char *supported_sec = php_http_locate_str(supported_str, supported_len, sep_str, sep_len);
+ size_t supported_pri_len = supported_sec ? supported_sec - supported_str : supported_len;
+ size_t cmp_len = MIN(param_pri_len, supported_pri_len);
+
+ if (((*param_str == '*') || (*supported_str == '*'))
+ || ((param_pri_len == supported_pri_len) && !strncasecmp(param_str, supported_str, param_pri_len))
+ || ((!param_sec || !supported_sec) && cmp_len && !strncasecmp(param_str, supported_str, cmp_len))
+ ) {
+ match += M_PRI;
+ }
- FOREACH_HASH_VAL(pos, supported, value) {
-#if PHP_HTTP_DBG_NEG
- fprintf(stderr, "strcasecmp('%s', '%s')\n", Z_STRVAL_PP(value), test);
-#endif
- if (!strcasecmp(Z_STRVAL_PP(value), test)) {
- return Z_STRVAL_PP(value);
+ if (param_sec && supported_sec && !strcasecmp(param_sec, supported_sec)) {
+ match += M_SEC;
}
- }
- /* no distinct match found, so try primaries */
- if ((dash_test = strchr(test, '-'))) {
- FOREACH_HASH_VAL(pos, supported, value) {
- int len = dash_test - test;
-#if PHP_HTTP_DBG_NEG
- fprintf(stderr, "strncasecmp('%s', '%s', %d)\n", Z_STRVAL_PP(value), test, len);
-#endif
- if ( (!strncasecmp(Z_STRVAL_PP(value), test, len)) &&
- ( (Z_STRVAL_PP(value)[len] == '\0') ||
- (Z_STRVAL_PP(value)[len] == '-'))) {
- *quality *= .9;
- return Z_STRVAL_PP(value);
- }
+ if ((param_sec && *(param_sec + sep_len) == '*')
+ || (supported_sec && *(supported_sec + sep_len) == '*')
+ || ((*param_str == '*') || (*supported_str == '*'))
+ ) {
+ match += M_ANY;
}
}
-
- return NULL;
+#if 0
+ fprintf(stderr, "match: %s == %s => %u\n", supported_str, param_str, match);
+#endif
+ return match;
}
-
-char *php_http_negotiate_default_func(const char *test, double *quality, HashTable *supported TSRMLS_DC)
+static int php_http_negotiate_reduce(void *p TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
{
- zval **value;
+ unsigned best_match = 0;
HashPosition pos;
- (void) quality;
-
- FOREACH_HASH_VAL(pos, supported, value) {
-#if PHP_HTTP_DBG_NEG
- fprintf(stderr, "strcasecmp('%s', '%s')\n", Z_STRVAL_PP(value), test);
-#endif
- if (!strcasecmp(Z_STRVAL_PP(value), test)) {
- return Z_STRVAL_PP(value);
+ php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+ zval **q = NULL, **val, *supported = php_http_ztyp(IS_STRING, *(zval **)p);
+ HashTable *params = va_arg(args, HashTable *);
+ HashTable *result = va_arg(args, HashTable *);
+ const char *sep_str = va_arg(args, const char *);
+ size_t sep_len = va_arg(args, size_t);
+
+ FOREACH_HASH_KEYVAL(pos, params, key, val) {
+ if (key.type == HASH_KEY_IS_STRING) {
+ unsigned match = php_http_negotiate_match(key.str, key.len-1, Z_STRVAL_P(supported), Z_STRLEN_P(supported), sep_str, sep_len);
+
+ if (match > best_match) {
+ best_match = match;
+ q = val;
+ }
}
}
- return NULL;
-}
-
-
-static int php_http_negotiate_sort(const void *a, const void *b TSRMLS_DC)
-{
- zval result, *first, *second;
-
- first = *((zval **) (*((Bucket **) a))->pData);
- second= *((zval **) (*((Bucket **) b))->pData);
-
- if (numeric_compare_function(&result, first, second TSRMLS_CC) != SUCCESS) {
- return 0;
+ if (q && Z_DVAL_PP(q) > 0) {
+ Z_ADDREF_PP(q);
+ zend_hash_update(result, Z_STRVAL_P(supported), Z_STRLEN_P(supported) + 1, (void *) q, sizeof(zval *), NULL);
}
- return (Z_LVAL(result) > 0 ? -1 : (Z_LVAL(result) < 0 ? 1 : 0));
-}
+ zval_ptr_dtor(&supported);
+ return ZEND_HASH_APPLY_KEEP;
+}
-PHP_HTTP_API HashTable *php_http_negotiate(const char *value, HashTable *supported, php_http_negotiate_func_t neg TSRMLS_DC)
+PHP_HTTP_API HashTable *php_http_negotiate(const char *value_str, size_t value_len, HashTable *supported, const char *primary_sep_str, size_t primary_sep_len TSRMLS_DC)
{
HashTable *result = NULL;
- if (*value) {
- zval ex_arr, ex_del, ex_val;
-
- INIT_PZVAL(&ex_del);
- INIT_PZVAL(&ex_arr);
- INIT_PZVAL(&ex_val);
- ZVAL_STRINGL(&ex_del, ",", 1, 0);
- ZVAL_STRING(&ex_val, value, 1);
- array_init(&ex_arr);
-
- php_explode(&ex_del, &ex_val, &ex_arr, INT_MAX);
-
- if (zend_hash_num_elements(Z_ARRVAL(ex_arr)) > 0) {
- int i = 0;
- HashPosition pos;
- zval **entry, array;
-
- INIT_PZVAL(&array);
- array_init(&array);
-
- if (!neg) {
- neg = php_http_negotiate_default_func;
+ if (value_str && value_len) {
+ unsigned i = 0;
+ zval arr, **val, **arg, **zq;
+ HashPosition pos;
+ HashTable params;
+ php_http_array_hashkey_t key = php_http_array_hashkey_init(1);
+ php_http_params_opts_t opts;
+
+ zend_hash_init(¶ms, 10, NULL, ZVAL_PTR_DTOR, 0);
+ php_http_params_opts_default_get(&opts);
+ opts.input.str = estrndup(value_str, value_len);
+ opts.input.len = value_len;
+ php_http_params_parse(¶ms, &opts TSRMLS_CC);
+ efree(opts.input.str);
+
+ INIT_PZVAL(&arr);
+ array_init(&arr);
+
+ FOREACH_HASH_KEYVAL(pos, ¶ms, key, val) {
+ double q;
+
+ if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("arguments"), (void *) &arg)
+ && IS_ARRAY == Z_TYPE_PP(arg)
+ && SUCCESS == zend_hash_find(Z_ARRVAL_PP(arg), ZEND_STRS("q"), (void *) &zq)) {
+ zval *tmp = php_http_ztyp(IS_DOUBLE, *zq);
+
+ q = Z_DVAL_P(tmp);
+ zval_ptr_dtor(&tmp);
+ } else {
+ q = 1.0 - ++i / 100.0;
}
- FOREACH_HASH_VAL(pos, Z_ARRVAL(ex_arr), entry) {
- int ident_len;
- double quality;
- char *selected, *identifier, *freeme;
- const char *separator;
-
-#if PHP_HTTP_DBG_NEG
- fprintf(stderr, "Checking %s\n", Z_STRVAL_PP(entry));
-#endif
-
- if ((separator = strchr(Z_STRVAL_PP(entry), ';'))) {
- const char *ptr = separator;
-
- while (*++ptr && !PHP_HTTP_IS_CTYPE(digit, *ptr) && '.' != *ptr);
-
- quality = zend_strtod(ptr, NULL);
- identifier = estrndup(Z_STRVAL_PP(entry), ident_len = separator - Z_STRVAL_PP(entry));
- } else {
- quality = 1000.0 - i++;
- identifier = estrndup(Z_STRVAL_PP(entry), ident_len = Z_STRLEN_PP(entry));
- }
- freeme = identifier;
-
- while (PHP_HTTP_IS_CTYPE(space, *identifier)) {
- ++identifier;
- --ident_len;
- }
- while (ident_len && PHP_HTTP_IS_CTYPE(space, identifier[ident_len - 1])) {
- identifier[--ident_len] = '\0';
- }
-
- if ((selected = neg(identifier, &quality, supported TSRMLS_CC))) {
- /* don't overwrite previously set with higher quality */
- if (!zend_symtable_exists(Z_ARRVAL(array), selected, strlen(selected) + 1)) {
- add_assoc_double(&array, selected, quality);
- }
- }
-
- efree(freeme);
+ if (key.type == HASH_KEY_IS_STRING) {
+ add_assoc_double_ex(&arr, key.str, key.len, q);
+ } else {
+ add_index_double(&arr, key.num, q);
}
- result = Z_ARRVAL(array);
- zend_hash_sort(result, zend_qsort, php_http_negotiate_sort, 0 TSRMLS_CC);
+ STR_FREE(key.str);
}
- zval_dtor(&ex_arr);
- zval_dtor(&ex_val);
+#if 0
+ zend_print_zval_r(&arr, 1 TSRMLS_CC);
+#endif
+
+ ALLOC_HASHTABLE(result);
+ zend_hash_init(result, zend_hash_num_elements(supported), NULL, ZVAL_PTR_DTOR, 0);
+ zend_hash_apply_with_arguments(supported TSRMLS_CC, php_http_negotiate_reduce, 4, Z_ARRVAL(arr), result, primary_sep_str, primary_sep_len);
+ zend_hash_destroy(¶ms);
+ zval_dtor(&arr);
+ zend_hash_sort(result, zend_qsort, php_http_negotiate_sort, 0 TSRMLS_CC);
}
return result;