From 5efb94c93bc4c1d4873706958962acc4313477f3 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Fri, 16 Sep 2005 13:59:19 +0000 Subject: [PATCH] - new negotiator + test --- docs/examples/Simple_Feed_Aggregator.php | 3 + http.c | 9 +- http_functions.c | 75 ++++++++--- http_headers_api.c | 160 +++++++++++++++++------ php_http_headers_api.h | 19 ++- tests/negotiation_001.phpt | 38 ++++++ 6 files changed, 229 insertions(+), 75 deletions(-) create mode 100644 tests/negotiation_001.phpt diff --git a/docs/examples/Simple_Feed_Aggregator.php b/docs/examples/Simple_Feed_Aggregator.php index c28e838..074c207 100644 --- a/docs/examples/Simple_Feed_Aggregator.php +++ b/docs/examples/Simple_Feed_Aggregator.php @@ -110,6 +110,9 @@ class FeedAggregator } $this->saveFeed($this->url2name($r->getUrl()), $body); } + echo $r->getHistory(); } } +$a = new FeedAggregator(); +$a->getFeed('http://www.planet-php.net/rss/'); ?> diff --git a/http.c b/http.c index 700fa9e..528f8ba 100644 --- a/http.c +++ b/http.c @@ -250,12 +250,6 @@ PHP_INI_BEGIN() PHP_INI_END() /* }}} */ -/* {{{ SSL */ -#ifdef HTTP_NEED_SSL - -#endif -/* }}} */ - /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(http) { @@ -326,8 +320,7 @@ PHP_RSHUTDOWN_FUNCTION(http) int i, c = zend_hash_num_elements(&HTTP_G(request).methods.custom); for (i = 0; i < c; ++i) { - zend_printf("RSHUTDOWN: unregistering %d (%d)\n", i, - http_request_method_unregister(HTTP_MAX_REQUEST_METHOD + i)); + http_request_method_unregister(HTTP_MAX_REQUEST_METHOD + i); } #endif http_globals_free(HTTP_GLOBALS); diff --git a/http_functions.c b/http_functions.c index d3b81ea..63a3423 100644 --- a/http_functions.c +++ b/http_functions.c @@ -100,11 +100,54 @@ PHP_FUNCTION(http_absolute_uri) } /* }}} */ -/* {{{ proto string http_negotiate_language(array supported[, string default = 'en-US']) +#define HTTP_DO_NEGOTIATE(type, supported, as_array) \ +{ \ + HashTable *result; \ + if (result = http_negotiate_ ##type(supported)) { \ + if (as_array) { \ + Z_TYPE_P(return_value) = IS_ARRAY; \ + Z_ARRVAL_P(return_value) = result; \ + } else { \ + char *key; \ + uint key_len; \ + ulong idx; \ + \ + if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(result, &key, &key_len, &idx, 1, NULL)) { \ + RETVAL_STRINGL(key, key_len-1, 0); \ + } else { \ + RETVAL_NULL(); \ + } \ + zend_hash_destroy(result); \ + } \ + } else { \ + if (as_array) { \ + zval **value; \ + \ + array_init(return_value); \ + \ + FOREACH_VAL(supported, value) { \ + convert_to_string_ex(value); \ + add_assoc_double(return_value, Z_STRVAL_PP(value), 1.0); \ + } \ + } else { \ + zval **value; \ + \ + zend_hash_internal_pointer_reset(Z_ARRVAL_P(supported)); \ + if (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(supported), (void **) &value)) { \ + RETVAL_ZVAL(*value, 1, 0); \ + } else { \ + RETVAL_NULL(); \ + } \ + } \ + } \ +} + + +/* {{{ proto string http_negotiate_language(array supported[, bool return_quality_array = false]) * * This function negotiates the clients preferred language based on its * Accept-Language HTTP header. It returns the negotiated language or - * the default language if none match. + * the default language (i.e. first array entry) if none match. * * The qualifier is recognized and languages without qualifier are rated highest. * @@ -131,26 +174,21 @@ PHP_FUNCTION(http_absolute_uri) PHP_FUNCTION(http_negotiate_language) { zval *supported; - char *def = NULL; - int def_len = 0; + zend_bool as_array = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|s", &supported, &def, &def_len) != SUCCESS) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &supported, &as_array) != SUCCESS) { RETURN_FALSE; } - - if (!def) { - def = "en-US"; - } - - RETURN_STRING(http_negotiate_language(supported, def), 0); + + HTTP_DO_NEGOTIATE(language, supported, as_array); } /* }}} */ -/* {{{ proto string http_negotiate_charset(array supported[, string default = 'iso-8859-1']) +/* {{{ proto string http_negotiate_charset(array supported) * * This function negotiates the clients preferred charset based on its * Accept-Charset HTTP header. It returns the negotiated charset or - * the default charset if none match. + * the default charset (i.e. first array entry) if none match. * * The qualifier is recognized and charset without qualifier are rated highest. * @@ -178,18 +216,13 @@ PHP_FUNCTION(http_negotiate_language) PHP_FUNCTION(http_negotiate_charset) { zval *supported; - char *def = NULL; - int def_len = 0; + zend_bool as_array = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|s", &supported, &def, &def_len) != SUCCESS) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &supported, &as_array) != SUCCESS) { RETURN_FALSE; } - if (!def) { - def = "iso-8859-1"; - } - - RETURN_STRING(http_negotiate_charset(supported, def), 0); + HTTP_DO_NEGOTIATE(charset, supported, as_array); } /* }}} */ diff --git a/http_headers_api.c b/http_headers_api.c index d573ab2..55be354 100644 --- a/http_headers_api.c +++ b/http_headers_api.c @@ -33,6 +33,10 @@ ZEND_EXTERN_MODULE_GLOBALS(http); +#ifndef HTTP_DBG_NEG +# define HTTP_DBG_NEG 0 +#endif + /* {{{ static int http_sort_q(const void *, const void *) */ static int http_sort_q(const void *a, const void *b TSRMLS_DC) { @@ -52,55 +56,131 @@ static int http_sort_q(const void *a, const void *b TSRMLS_DC) } /* }}} */ -/* {{{ char *http_negotiate_q(char *, HashTable *, char *) */ -PHP_HTTP_API char *_http_negotiate_q(const char *entry, const HashTable *supported, const char *def TSRMLS_DC) +/* {{{ char *http_negotiate_language_func */ +char *_http_negotiate_language_func(const char *test, double *quality, HashTable *supported TSRMLS_DC) { - zval *zaccept, zdelim, zarray, zentries, **zentry, **zsupp; - char *q_ptr = NULL, *key = NULL; - int i = 0; - ulong idx = 0; - double qual; - - HTTP_GSC(zaccept, entry, estrdup(def)); - - array_init(&zarray); - array_init(&zentries); - - Z_STRVAL(zdelim) = ","; - Z_STRLEN(zdelim) = 1; - - php_explode(&zdelim, zaccept, &zarray, -1); - - FOREACH_HASH_VAL(Z_ARRVAL(zarray), zentry) { - if (q_ptr = strrchr(Z_STRVAL_PP(zentry), ';')) { - qual = strtod(q_ptr + 3, NULL); - *q_ptr = 0; - q_ptr = NULL; - } else { - qual = 1000.0 - i++; + zval **value; + const char *dash_test; + + FOREACH_HASH_VAL(supported, value) { +#if 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); } - /* TODO: support primaries only, too */ - FOREACH_HASH_VAL((HashTable *)supported, zsupp) { - if (!strcasecmp(Z_STRVAL_PP(zsupp), Z_STRVAL_PP(zentry))) { - add_assoc_double(&zentries, Z_STRVAL_PP(zsupp), qual); - break; + } + + /* no distinct match found, so try primaries */ + if (dash_test = strchr(test, '-')) { + FOREACH_HASH_VAL(supported, value) { + int len = dash_test - test; +#if HTTP_DBG_NEG + fprintf(stderr, "strncascmp('%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 /= 2; + return Z_STRVAL_PP(value); } } } - zval_dtor(&zarray); - - zend_hash_sort(Z_ARRVAL(zentries), zend_qsort, http_sort_q, 0 TSRMLS_CC); + + return NULL; +} +/* }}} */ - FOREACH_HASH_KEY(Z_ARRVAL(zentries), key, idx) { - if (key) { - key = estrdup(key); - zval_dtor(&zentries); - return key; +/* {{{ char *http_negotiate_charset_func */ +char *_http_negotiate_charset_func(const char *test, double *quality, HashTable *supported TSRMLS_DC) +{ + zval **value; + + FOREACH_HASH_VAL(supported, value) { +#if 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); } } - zval_dtor(&zentries); + + return NULL; +} +/* }}} */ - return estrdup(def); +/* {{{ HashTable *http_negotiate_q(const char *, HashTable *, negotiate_func_t) */ +PHP_HTTP_API HashTable *_http_negotiate_q(const char *header, HashTable *supported, negotiate_func_t neg TSRMLS_DC) +{ + zval *accept; + HashTable *result = NULL; + +#if HTTP_DBG_NEG + fprintf(stderr, "Reading header %s: ", header); +#endif + HTTP_GSC(accept, header, NULL); +#if HTTP_DBG_NEG + fprintf(stderr, "%s\n", Z_STRVAL_P(accept)); +#endif + + if (Z_STRLEN_P(accept)) { + zval ex_arr, ex_del; + + INIT_PZVAL(&ex_del); + INIT_PZVAL(&ex_arr); + ZVAL_STRINGL(&ex_del, ",", 1, 0); + array_init(&ex_arr); + + php_explode(&ex_del, accept, &ex_arr, -1); + + if (zend_hash_num_elements(Z_ARRVAL(ex_arr)) > 0) { + int i = 0; + zval **entry, array; + + INIT_PZVAL(&array); + array_init(&array); + + FOREACH_HASH_VAL(Z_ARRVAL(ex_arr), entry) { + double quality; + char *selected, *identifier; + const char *separator; + +#if HTTP_DBG_NEG + fprintf(stderr, "Checking %s\n", Z_STRVAL_PP(entry)); +#endif + + if (separator = strchr(Z_STRVAL_PP(entry), ';')) { + const char *ptr = separator; + + do { + ++ptr; + } while ((*ptr == ' ') || (*ptr == 'q') || (*ptr == '=')); + + quality = strtod(ptr, NULL); + identifier = estrndup(Z_STRVAL_PP(entry), separator - Z_STRVAL_PP(entry)); + } else { + quality = 1000.0 - i++; + identifier = estrndup(Z_STRVAL_PP(entry), Z_STRLEN_PP(entry)); + } + + if (selected = neg(identifier, &quality, supported TSRMLS_CC)) { + /* don't overwrite previously set with higher quality */ + if (!zend_hash_exists(Z_ARRVAL(array), selected, strlen(selected) + 1)) { + add_assoc_double(&array, selected, quality); + } + } + + efree(identifier); + } + + result = Z_ARRVAL(array); + zend_hash_sort(result, zend_qsort, http_sort_q, 0 TSRMLS_CC); + } + + zval_dtor(&ex_arr); + } + + return result; } /* }}} */ diff --git a/php_http_headers_api.h b/php_http_headers_api.h index b02e4c8..329d650 100644 --- a/php_http_headers_api.h +++ b/php_http_headers_api.h @@ -36,12 +36,19 @@ PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, HashTable *header #define http_get_request_headers_ex(h, p) _http_get_request_headers_ex((h), (p) TSRMLS_CC) PHP_HTTP_API void _http_get_request_headers_ex(HashTable *headers, zend_bool prettify TSRMLS_DC); -#define http_negotiate_language(zsupported, def) http_negotiate_language_ex(Z_ARRVAL_P(zsupported), (def)) -#define http_negotiate_language_ex(supported, def) http_negotiate_q("HTTP_ACCEPT_LANGUAGE", (supported), (def)) -#define http_negotiate_charset(zsupported, def) http_negotiate_charset_ex(Z_ARRVAL_P(zsupported), (def)) -#define http_negotiate_charset_ex(supported, def) http_negotiate_q("HTTP_ACCEPT_CHARSET", (supported), (def)) -#define http_negotiate_q(e, s, d) _http_negotiate_q((e), (s), (d) TSRMLS_CC) -PHP_HTTP_API char *_http_negotiate_q(const char *entry, const HashTable *supported, const char *def TSRMLS_DC); +typedef char *(*negotiate_func_t)(const char *test, double *quality, HashTable *supported TSRMLS_DC); + +#define http_negotiate_language_func _http_negotiate_language_func +extern char *_http_negotiate_language_func(const char *test, double *quality, HashTable *supported TSRMLS_DC); +#define http_negotiate_charset_func _http_negotiate_charset_func +extern char *_http_negotiate_charset_func(const char *test, double *quality, HashTable *supported TSRMLS_DC); + +#define http_negotiate_language(zsupported) http_negotiate_language_ex(Z_ARRVAL_P(zsupported)) +#define http_negotiate_language_ex(supported) http_negotiate_q("HTTP_ACCEPT_LANGUAGE", (supported), http_negotiate_language_func) +#define http_negotiate_charset(zsupported) http_negotiate_charset_ex(Z_ARRVAL_P(zsupported)) +#define http_negotiate_charset_ex(supported) http_negotiate_q("HTTP_ACCEPT_CHARSET", (supported), http_negotiate_charset_func) +#define http_negotiate_q(e, s, n) _http_negotiate_q((e), (s), (n) TSRMLS_CC) +PHP_HTTP_API HashTable *_http_negotiate_q(const char *header, HashTable *supported, negotiate_func_t neg TSRMLS_DC); #define http_get_request_ranges(r, l) _http_get_request_ranges((r), (l) TSRMLS_CC) PHP_HTTP_API http_range_status _http_get_request_ranges(HashTable *ranges, size_t length TSRMLS_DC); diff --git a/tests/negotiation_001.phpt b/tests/negotiation_001.phpt new file mode 100644 index 0000000..88e6ec4 --- /dev/null +++ b/tests/negotiation_001.phpt @@ -0,0 +1,38 @@ +--TEST-- +negotiation +--SKIPIF-- + +--ENV-- +HTTP_ACCEPT_LANGUAGE=de-AT,de-DE;q=0.8,en-GB;q=0.3,en-US;q=0.2 +HTTP_ACCEPT_CHARSET=ISO-8859-1,utf-8;q=0.7,*;q=0.7 +--FILE-- + 500 + [en] => 0.15 +) +string(10) "iso-8859-1" +Array +( + [iso-8859-1] => 1000 + [utf-8] => 0.7 +) +Done -- 2.30.2