- new negotiator + test
authorMichael Wallner <mike@php.net>
Fri, 16 Sep 2005 13:59:19 +0000 (13:59 +0000)
committerMichael Wallner <mike@php.net>
Fri, 16 Sep 2005 13:59:19 +0000 (13:59 +0000)
docs/examples/Simple_Feed_Aggregator.php
http.c
http_functions.c
http_headers_api.c
php_http_headers_api.h
tests/negotiation_001.phpt [new file with mode: 0644]

index c28e83873fa2cb09429dd01d324aec327ba777e5..074c2079e457080576279b1fa01d8596435aac69 100644 (file)
@@ -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 700fa9e33bc6923624bb573d811fe356fcd3300d..528f8bad300a77dac1dfaaaa6c5fbf19abc835fe 100644 (file)
--- 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);
index d3b81ea41cb9ffc44f1bafb274f0c9b4790111e7..63a3423bb52341443cecb476bf8caf2d60926af1 100644 (file)
@@ -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);
 }
 /* }}} */
 
index d573ab27b601aa4d0533cf9d28cc46b70b0adc9c..55be354afbc50feaf56817e64812647511b1b6ac 100644 (file)
 
 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;
 }
 /* }}} */
 
index b02e4c8923c5b6b90de902e35e09f16780537f0f..329d650093e5d545c344ff96483a48895ece0508 100644 (file)
@@ -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 (file)
index 0000000..88e6ec4
--- /dev/null
@@ -0,0 +1,38 @@
+--TEST--
+negotiation
+--SKIPIF--
+<?php
+include 'skip.inc';
+?>
+--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--
+<?php
+echo "-TEST\n";
+$langs = array(
+       array('de', 'en', 'es'),
+);
+$csets = array(
+       array('utf-8', 'iso-8859-1'),
+);
+var_dump(http_negotiate_language($langs[0]));
+print_r(http_negotiate_language($langs[0], true));
+var_dump(http_negotiate_charset($csets[0]));
+print_r(http_negotiate_charset($csets[0], true));
+echo "Done\n";
+--EXPECTF--
+%sTEST
+string(2) "de"
+Array
+(
+    [de] => 500
+    [en] => 0.15
+)
+string(10) "iso-8859-1"
+Array
+(
+    [iso-8859-1] => 1000
+    [utf-8] => 0.7
+)
+Done