]> granicus.if.org Git - php/commitdiff
Support for samesite cookies with array syntax
authorPedro Magalhães <mail@pmmaga.net>
Thu, 19 Jul 2018 01:40:39 +0000 (02:40 +0100)
committerChristoph M. Becker <cmbecker69@gmx.de>
Tue, 31 Jul 2018 10:40:26 +0000 (12:40 +0200)
Allows using an alternative array argument with
support for the samesite option on the following
functions:
setcookie
setrawcookie
session_set_cookie_params

ext/session/session.c
ext/session/tests/session_get_cookie_params_basic.phpt
ext/session/tests/session_set_cookie_params_variation3.phpt
ext/session/tests/session_set_cookie_params_variation5.phpt
ext/session/tests/session_set_cookie_params_variation6.phpt
ext/session/tests/session_set_cookie_params_variation7.phpt [new file with mode: 0644]
ext/standard/basic_functions.c
ext/standard/head.c
ext/standard/head.h
ext/standard/tests/network/setcookie.phpt
ext/standard/tests/network/setcookie_error.phpt [new file with mode: 0644]

index 730cca39b969020b9072ee0acec6ae38f5a6c11f..c7d54b0ee739704b4fce14b4bf4e905fd0514aaa 100644 (file)
@@ -1664,21 +1664,31 @@ PHPAPI void session_adapt_url(const char *url, size_t urllen, char **new, size_t
    * Userspace exported functions *
    ******************************** */
 
-/* {{{ proto void session_set_cookie_params(int lifetime [, string path [, string domain [, bool secure[, bool httponly[, string samesite]]]]])
+/* {{{ proto bool session_set_cookie_params(int lifetime [, string path [, string domain [, bool secure[, bool httponly]]]])
+                  session_set_cookie_params(array options)
    Set session cookie parameters */
 static PHP_FUNCTION(session_set_cookie_params)
 {
-       zval *lifetime;
-       zend_string *path = NULL, *domain = NULL, *samesite = NULL;
-       int argc = ZEND_NUM_ARGS();
-       zend_bool secure = 0, httponly = 0;
+       zval *lifetime_or_options = NULL;
+       zend_string *lifetime = NULL, *path = NULL, *domain = NULL, *samesite = NULL;
+       zend_bool secure = 0, secure_null = 1;
+       zend_bool httponly = 0, httponly_null = 1;
        zend_string *ini_name;
+       int result;
+       int found = 0;
 
-       if (!PS(use_cookies) ||
-               zend_parse_parameters(argc, "z|SSbbS", &lifetime, &path, &domain, &secure, &httponly, &samesite) == FAILURE) {
+       if (!PS(use_cookies)) {
                return;
        }
 
+       ZEND_PARSE_PARAMETERS_START(1, 5)
+               Z_PARAM_ZVAL(lifetime_or_options)
+               Z_PARAM_OPTIONAL
+               Z_PARAM_STR(path)
+               Z_PARAM_STR(domain)
+               Z_PARAM_BOOL_EX(secure, secure_null, 1, 0)
+               Z_PARAM_BOOL_EX(httponly, httponly_null, 1, 0)
+       ZEND_PARSE_PARAMETERS_END();
 
        if (PS(session_status) == php_session_active) {
                php_error_docref(NULL, E_WARNING, "Cannot change session cookie parameters when session is active");
@@ -1690,55 +1700,108 @@ static PHP_FUNCTION(session_set_cookie_params)
                RETURN_FALSE;
        }
 
-       convert_to_string_ex(lifetime);
+       if (Z_TYPE_P(lifetime_or_options) == IS_ARRAY) {
+               zend_string *key;
+               zval *value;
+
+               ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(lifetime_or_options), key, value) {
+                       if (key) {
+                               ZVAL_DEREF(value);
+                               if(!strcasecmp("lifetime", ZSTR_VAL(key))) {
+                                       lifetime = zval_get_string(value);
+                                       found++;
+                               } else if(!strcasecmp("path", ZSTR_VAL(key))) {
+                                       path = zval_get_string(value);
+                                       found++;
+                               } else if(!strcasecmp("domain", ZSTR_VAL(key))) {
+                                       domain = zval_get_string(value);
+                                       found++;
+                               } else if(!strcasecmp("secure", ZSTR_VAL(key))) {
+                                       secure = zval_is_true(value);
+                                       secure_null = 0;
+                                       found++;
+                               } else if(!strcasecmp("httponly", ZSTR_VAL(key))) {
+                                       httponly = zval_is_true(value);
+                                       httponly_null = 0;
+                                       found++;
+                               } else if(!strcasecmp("samesite", ZSTR_VAL(key))) {
+                                       samesite = zval_get_string(value);
+                                       found++;
+                               } else {
+                                       php_error_docref(NULL, E_WARNING, "Unrecognized key '%s' found in the options array", ZSTR_VAL(key));
+                               }
+                       } else {
+                               php_error_docref(NULL, E_WARNING, "Numeric key found in the options array");
+                       }
+               } ZEND_HASH_FOREACH_END();
 
-       ini_name = zend_string_init("session.cookie_lifetime", sizeof("session.cookie_lifetime") - 1, 0);
-       if (zend_alter_ini_entry(ini_name,  Z_STR_P(lifetime), PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) {
-               zend_string_release_ex(ini_name, 0);
-               RETURN_FALSE;
+               if (found == 0) {
+                       php_error_docref(NULL, E_WARNING, "No valid keys were found in the options array");
+                       RETURN_FALSE;
+               }
+       } else {
+               lifetime = zval_get_string(lifetime_or_options);
        }
-       zend_string_release_ex(ini_name, 0);
 
+       if (lifetime) {
+               ini_name = zend_string_init("session.cookie_lifetime", sizeof("session.cookie_lifetime") - 1, 0);
+               result = zend_alter_ini_entry(ini_name, lifetime, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
+               zend_string_release(lifetime);
+               zend_string_release_ex(ini_name, 0);
+               if (result == FAILURE) {
+                       RETURN_FALSE;
+               }
+       }
        if (path) {
                ini_name = zend_string_init("session.cookie_path", sizeof("session.cookie_path") - 1, 0);
-               if (zend_alter_ini_entry(ini_name, path, PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) {
-                       zend_string_release_ex(ini_name, 0);
-                       RETURN_FALSE;
+               result = zend_alter_ini_entry(ini_name, path, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
+               if (found > 0) {
+                       zend_string_release(path);
                }
                zend_string_release_ex(ini_name, 0);
+               if (result == FAILURE) {
+                       RETURN_FALSE;
+               }
        }
        if (domain) {
                ini_name = zend_string_init("session.cookie_domain", sizeof("session.cookie_domain") - 1, 0);
-               if (zend_alter_ini_entry(ini_name, domain, PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) {
-                       zend_string_release_ex(ini_name, 0);
-                       RETURN_FALSE;
+               result = zend_alter_ini_entry(ini_name, domain, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
+               if (found > 0) {
+                       zend_string_release(domain);
                }
                zend_string_release_ex(ini_name, 0);
+               if (result == FAILURE) {
+                       RETURN_FALSE;
+               }
        }
-
-       if (argc > 3) {
+       if (!secure_null) {
                ini_name = zend_string_init("session.cookie_secure", sizeof("session.cookie_secure") - 1, 0);
-               if (zend_alter_ini_entry_chars(ini_name, secure ? "1" : "0", 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) {
-                       zend_string_release_ex(ini_name, 0);
+               result = zend_alter_ini_entry_chars(ini_name, secure ? "1" : "0", 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
+               zend_string_release_ex(ini_name, 0);
+               if (result == FAILURE) {
                        RETURN_FALSE;
                }
-               zend_string_release_ex(ini_name, 0);
        }
-       if (argc > 4) {
+       if (!httponly_null) {
                ini_name = zend_string_init("session.cookie_httponly", sizeof("session.cookie_httponly") - 1, 0);
-               if (zend_alter_ini_entry_chars(ini_name, httponly ? "1" : "0", 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) {
-                       zend_string_release_ex(ini_name, 0);
+               result = zend_alter_ini_entry_chars(ini_name, httponly ? "1" : "0", 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
+               zend_string_release_ex(ini_name, 0);
+               if (result == FAILURE) {
                        RETURN_FALSE;
                }
+       }
+       if (samesite) {
+               ini_name = zend_string_init("session.cookie_samesite", sizeof("session.cookie_samesite") - 1, 0);
+               result = zend_alter_ini_entry(ini_name, samesite, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
+               if (found > 0) {
+                       zend_string_release(samesite);
+               }
                zend_string_release_ex(ini_name, 0);
+               if (result == FAILURE) {
+                       RETURN_FALSE;
+               }
        }
 
-    if (argc > 5) {
-        ini_name = zend_string_init("session.cookie_samesite", sizeof("session.cookie_samesite") - 1, 0);
-        zend_alter_ini_entry(ini_name, samesite, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
-        zend_string_release(ini_name);
-    }
-
        RETURN_TRUE;
 }
 /* }}} */
@@ -2638,12 +2701,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_session_cache_expire, 0, 0, 0)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_set_cookie_params, 0, 0, 1)
-       ZEND_ARG_INFO(0, lifetime)
+       ZEND_ARG_INFO(0, lifetime_or_options)
        ZEND_ARG_INFO(0, path)
        ZEND_ARG_INFO(0, domain)
        ZEND_ARG_INFO(0, secure)
        ZEND_ARG_INFO(0, httponly)
-       ZEND_ARG_INFO(0, samesite)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO(arginfo_session_class_open, 0)
index 9dcde4ec350b2b96dce49cb9d2344941a80c0c15..6188e06e6df8dcb03c842e857afa5b25b9fcad0f 100644 (file)
@@ -23,9 +23,17 @@ ob_start();
 echo "*** Testing session_get_cookie_params() : basic functionality ***\n";
 
 var_dump(session_get_cookie_params());
-var_dump(session_set_cookie_params(3600, "/path", "blah", FALSE, FALSE, "foo"));
+var_dump(session_set_cookie_params(3600, "/path", "blah", FALSE, FALSE));
 var_dump(session_get_cookie_params());
-var_dump(session_set_cookie_params(1234567890, "/guff", "foo", TRUE, TRUE, "blah"));
+var_dump(session_set_cookie_params(1234567890, "/guff", "foo", TRUE, TRUE));
+var_dump(session_get_cookie_params());
+var_dump(session_set_cookie_params([
+  "lifetime" => 123,
+  "path" => "/bar",
+  "domain" => "baz",
+  "secure" => FALSE,
+  "httponly" => FALSE,
+  "samesite" => "please"]));
 var_dump(session_get_cookie_params());
 
 echo "Done";
@@ -60,7 +68,7 @@ array(6) {
   ["httponly"]=>
   bool(false)
   ["samesite"]=>
-  string(3) "foo"
+  string(0) ""
 }
 bool(true)
 array(6) {
@@ -75,6 +83,21 @@ array(6) {
   ["httponly"]=>
   bool(true)
   ["samesite"]=>
-  string(4) "blah"
+  string(0) ""
+}
+bool(true)
+array(6) {
+  ["lifetime"]=>
+  int(123)
+  ["path"]=>
+  string(4) "/bar"
+  ["domain"]=>
+  string(3) "baz"
+  ["secure"]=>
+  bool(false)
+  ["httponly"]=>
+  bool(false)
+  ["samesite"]=>
+  string(6) "please"
 }
 Done
index e4e26b91b2ace26d1ef98866cfab157290de12c8..17d1e6a7713d02c7d5cc034f62034806d55e7e41 100644 (file)
@@ -10,7 +10,7 @@ session.cookie_domain=foo
 ob_start();
 
 /* 
- * Prototype : void session_set_cookie_params(int $lifetime [, string $path [, string $domain [, bool $secure [, bool $httponly[, string $samesite]]]]])
+ * Prototype : void session_set_cookie_params(int $lifetime [, string $path [, string $domain [, bool $secure [, bool $httponly]]]])
  * Description : Set the session cookie parameters
  * Source code : ext/session/session.c 
  */
index f3374d0daaad13dd5097bb94d61d5e839b09fc57..ffdd29db2d48cfb1cd8ab8d132dbcc61aac93c8b 100644 (file)
@@ -10,7 +10,7 @@ session.cookie_httponly=TRUE
 ob_start();
 
 /* 
- * Prototype : void session_set_cookie_params(int $lifetime [, string $path [, string $domain [, bool $secure [, bool $httponly[, string $samesite]]]]])
+ * Prototype : void session_set_cookie_params(int $lifetime [, string $path [, string $domain [, bool $secure [, bool $httponly]]]])
  * Description : Set the session cookie parameters
  * Source code : ext/session/session.c 
  */
index 7fe1e009eea6d9486315a45ee418a2dde3ef49a3..b94380d37062c6523ba97f07aabe107ff839a915 100644 (file)
@@ -10,7 +10,7 @@ session.cookie_samesite=test
 ob_start();
 
 /*
- * Prototype : void session_set_cookie_params(int $lifetime [, string $path [, string $domain [, bool $secure [, bool $samesite[, string $samesite]]]]])
+ * Prototype : void session_set_cookie_params(array $options)
  * Description : Set the session cookie parameters
  * Source code : ext/session/session.c
  */
@@ -18,15 +18,15 @@ ob_start();
 echo "*** Testing session_set_cookie_params() : variation ***\n";
 
 var_dump(ini_get("session.cookie_samesite"));
-var_dump(session_set_cookie_params(3600, "/path", "blah", FALSE, FALSE, "nothing"));
+var_dump(session_set_cookie_params(["samesite" => "nothing"]));
 var_dump(ini_get("session.cookie_samesite"));
 var_dump(session_start());
 var_dump(ini_get("session.cookie_samesite"));
-var_dump(session_set_cookie_params(3600, "/path", "blah", FALSE, TRUE, "test"));
+var_dump(session_set_cookie_params(["samesite" => "test"]));
 var_dump(ini_get("session.cookie_samesite"));
 var_dump(session_destroy());
 var_dump(ini_get("session.cookie_samesite"));
-var_dump(session_set_cookie_params(3600, "/path", "blah", FALSE, FALSE, "other"));
+var_dump(session_set_cookie_params(["samesite" => "other"]));
 var_dump(ini_get("session.cookie_samesite"));
 
 echo "Done";
diff --git a/ext/session/tests/session_set_cookie_params_variation7.phpt b/ext/session/tests/session_set_cookie_params_variation7.phpt
new file mode 100644 (file)
index 0000000..ebd9b71
--- /dev/null
@@ -0,0 +1,60 @@
+--TEST--
+Test session_set_cookie_params() function : array parameter variation
+--INI--
+session.cookie_lifetime=0
+session.cookie_path="/"
+session.cookie_domain=""
+session.cookie_secure=0
+session.cookie_httponly=0
+session.cookie_samesite=""
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--FILE--
+<?php
+
+ob_start();
+
+/*
+ * Prototype : void session_set_cookie_params(array $options)
+ * Description : Set the session cookie parameters
+ * Source code : ext/session/session.c
+ */
+
+echo "*** Testing session_set_cookie_params() : array parameter variation ***\n";
+
+// Invalid cases
+var_dump(session_set_cookie_params([]));
+var_dump(session_set_cookie_params(["unknown_key" => true]));
+
+var_dump(ini_get("session.cookie_secure"));
+var_dump(ini_get("session.cookie_samesite"));
+var_dump(session_set_cookie_params(["secure" => true, "samesite" => "please"]));
+var_dump(ini_get("session.cookie_secure"));
+var_dump(ini_get("session.cookie_samesite"));
+
+var_dump(ini_get("session.cookie_lifetime"));
+var_dump(session_set_cookie_params(["lifetime" => 42]));
+var_dump(ini_get("session.cookie_lifetime"));
+
+echo "Done";
+ob_end_flush();
+?>
+--EXPECTF--
+*** Testing session_set_cookie_params() : array parameter variation ***
+
+Warning: session_set_cookie_params(): No valid keys were found in the options array in %s
+bool(false)
+
+Warning: session_set_cookie_params(): Unrecognized key 'unknown_key' found in the options array in %s
+
+Warning: session_set_cookie_params(): No valid keys were found in the options array in %s
+bool(false)
+string(1) "0"
+string(0) ""
+bool(true)
+string(1) "1"
+string(6) "please"
+string(1) "0"
+bool(true)
+string(2) "42"
+Done
index 44f02f9b7ba6e51279231299e93c1ff2009d07c3..160e510831fb6f7071cdfaabe052154906264513 100644 (file)
@@ -1431,23 +1431,21 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_INFO_EX(arginfo_setcookie, 0, 0, 1)
        ZEND_ARG_INFO(0, name)
        ZEND_ARG_INFO(0, value)
-       ZEND_ARG_INFO(0, expires)
+       ZEND_ARG_INFO(0, expires_or_options)
        ZEND_ARG_INFO(0, path)
        ZEND_ARG_INFO(0, domain)
        ZEND_ARG_INFO(0, secure)
        ZEND_ARG_INFO(0, httponly)
-       ZEND_ARG_INFO(0, samesite)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_setrawcookie, 0, 0, 1)
        ZEND_ARG_INFO(0, name)
        ZEND_ARG_INFO(0, value)
-       ZEND_ARG_INFO(0, expires)
+       ZEND_ARG_INFO(0, expires_or_options)
        ZEND_ARG_INFO(0, path)
        ZEND_ARG_INFO(0, domain)
        ZEND_ARG_INFO(0, secure)
        ZEND_ARG_INFO(0, httponly)
-       ZEND_ARG_INFO(0, samesite)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_headers_sent, 0, 0, 0)
index c0df1fec5e6e1a6b10df711b793b44f1dc90f7dd..5a9f09b1e72674613c32226cba48c9613bb257dd 100644 (file)
@@ -79,7 +79,7 @@ PHPAPI int php_header(void)
        }
 }
 
-PHPAPI int php_setcookie(zend_string *name, zend_string *value, time_t expires, zend_string *path, zend_string *domain, int secure, int url_encode, int httponly, zend_string *samesite)
+PHPAPI int php_setcookie(zend_string *name, zend_string *value, time_t expires, zend_string *path, zend_string *domain, int secure, int httponly, zend_string *samesite, int url_encode)
 {
        char *cookie;
        size_t len = sizeof("Set-Cookie: ");
@@ -206,61 +206,154 @@ PHPAPI int php_setcookie(zend_string *name, zend_string *value, time_t expires,
        return result;
 }
 
+static int php_head_parse_cookie_options_array(zval *options, zend_long *expires, zend_string **path, zend_string **domain, zend_bool *secure, zend_bool *httponly, zend_string **samesite) {
+       int found = 0;
+       zend_string *key;
+       zval *value;
+
+       ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(options), key, value) {
+               if (key) {
+                       ZVAL_DEREF(value);
+                       if(!strcasecmp("expires", ZSTR_VAL(key))) {
+                               *expires = zval_get_long(value);
+                               found++;
+                       } else if(!strcasecmp("path", ZSTR_VAL(key))) {
+                               *path = zval_get_string(value);
+                               found++;
+                       } else if(!strcasecmp("domain", ZSTR_VAL(key))) {
+                               *domain = zval_get_string(value);
+                               found++;
+                       } else if(!strcasecmp("secure", ZSTR_VAL(key))) {
+                               *secure = zval_is_true(value);
+                               found++;
+                       } else if(!strcasecmp("httponly", ZSTR_VAL(key))) {
+                               *httponly = zval_is_true(value);
+                               found++;
+                       } else if(!strcasecmp("samesite", ZSTR_VAL(key))) {
+                               *samesite = zval_get_string(value);
+                               found++;
+                       } else {
+                               php_error_docref(NULL, E_WARNING, "Unrecognized key '%s' found in the options array", ZSTR_VAL(key));
+                       }
+               } else {
+                       php_error_docref(NULL, E_WARNING, "Numeric key found in the options array");
+               }
+       } ZEND_HASH_FOREACH_END();
+
+       /* Array is not empty but no valid keys were found */
+       if (found == 0 && zend_hash_num_elements(Z_ARRVAL_P(options)) > 0) {
+               php_error_docref(NULL, E_WARNING, "No valid options were found in the given array");
+               return 0;
+       }
+
+       return 1;
+}
 
-/* php_set_cookie(name, value, expires, path, domain, secure) */
-/* {{{ proto bool setcookie(string name [, string value [, int expires [, string path [, string domain [, bool secure[, bool httponly[, string samesite]]]]]]])
+/* {{{ proto bool setcookie(string name [, string value [, int expires [, string path [, string domain [, bool secure[, bool httponly]]]]]])
+                  setcookie(string name [, string value [, array options]])
    Send a cookie */
 PHP_FUNCTION(setcookie)
 {
+       zval *expires_or_options = NULL;
        zend_string *name, *value = NULL, *path = NULL, *domain = NULL, *samesite = NULL;
        zend_long expires = 0;
-       zend_bool secure = 0, httponly = 0;
+       zend_bool secure = 0, httponly = 0, options_array = 0;
 
-       ZEND_PARSE_PARAMETERS_START(1, 8)
+       ZEND_PARSE_PARAMETERS_START(1, 7)
                Z_PARAM_STR(name)
                Z_PARAM_OPTIONAL
                Z_PARAM_STR(value)
-               Z_PARAM_LONG(expires)
+               Z_PARAM_ZVAL(expires_or_options)
                Z_PARAM_STR(path)
                Z_PARAM_STR(domain)
                Z_PARAM_BOOL(secure)
                Z_PARAM_BOOL(httponly)
-               Z_PARAM_STR(samesite)
        ZEND_PARSE_PARAMETERS_END();
 
-       if (php_setcookie(name, value, expires, path, domain, secure, 1, httponly, samesite) == SUCCESS) {
+       if (expires_or_options) {
+               if (Z_TYPE_P(expires_or_options) == IS_ARRAY) {
+                       options_array = 1;
+                       if (!php_head_parse_cookie_options_array(expires_or_options, &expires, &path, &domain, &secure, &httponly, &samesite)) {
+                               RETVAL_FALSE;
+                               goto cleanup;
+                       }
+               } else {
+                       expires = Z_LVAL_P(expires_or_options);
+               }
+       }
+
+       if (php_setcookie(name, value, expires, path, domain, secure, httponly, samesite, 1) == SUCCESS) {
                RETVAL_TRUE;
        } else {
                RETVAL_FALSE;
        }
+
+cleanup:
+       if (options_array) {
+               if (path) {
+                       zend_string_release(path);
+               }
+               if (domain) {
+                       zend_string_release(domain);
+               }
+               if (samesite) {
+                       zend_string_release(samesite);
+               }
+       }
 }
 /* }}} */
 
-/* {{{ proto bool setrawcookie(string name [, string value [, int expires [, string path [, string domain [, bool secure[, bool httponly[, string samesite]]]]]]])
+/* {{{ proto bool setrawcookie(string name [, string value [, int expires [, string path [, string domain [, bool secure[, bool httponly]]]]]])
+                  setrawcookie(string name [, string value [, array options]])
    Send a cookie with no url encoding of the value */
 PHP_FUNCTION(setrawcookie)
 {
+       zval *expires_or_options = NULL;
        zend_string *name, *value = NULL, *path = NULL, *domain = NULL, *samesite = NULL;
        zend_long expires = 0;
-       zend_bool secure = 0, httponly = 0;
+       zend_bool secure = 0, httponly = 0, options_array = 0;
 
-       ZEND_PARSE_PARAMETERS_START(1, 8)
+       ZEND_PARSE_PARAMETERS_START(1, 7)
                Z_PARAM_STR(name)
                Z_PARAM_OPTIONAL
                Z_PARAM_STR(value)
-               Z_PARAM_LONG(expires)
+               Z_PARAM_ZVAL(expires_or_options)
                Z_PARAM_STR(path)
                Z_PARAM_STR(domain)
                Z_PARAM_BOOL(secure)
                Z_PARAM_BOOL(httponly)
-               Z_PARAM_STR(samesite)
        ZEND_PARSE_PARAMETERS_END();
 
-       if (php_setcookie(name, value, expires, path, domain, secure, 0, httponly, samesite) == SUCCESS) {
+       if (expires_or_options) {
+               if (Z_TYPE_P(expires_or_options) == IS_ARRAY) {
+                       options_array = 1;
+                       if (!php_head_parse_cookie_options_array(expires_or_options, &expires, &path, &domain, &secure, &httponly, &samesite)) {
+                               RETVAL_FALSE;
+                               goto cleanup;
+                       }
+               } else {
+                       expires = Z_LVAL_P(expires_or_options);
+               }
+       }
+
+       if (php_setcookie(name, value, expires, path, domain, secure, httponly, samesite, 0) == SUCCESS) {
                RETVAL_TRUE;
        } else {
                RETVAL_FALSE;
        }
+
+cleanup:
+       if (options_array) {
+               if (path) {
+                       zend_string_release(path);
+               }
+               if (domain) {
+                       zend_string_release(domain);
+               }
+               if (samesite) {
+                       zend_string_release(samesite);
+               }
+       }
 }
 /* }}} */
 
index 725b176ec4b8402408343bef3c826107d6e5df64..5402082d1ad71bfdae922ddeedde1b5c4114b668 100644 (file)
@@ -37,6 +37,6 @@ PHP_FUNCTION(headers_list);
 PHP_FUNCTION(http_response_code);
 
 PHPAPI int php_header(void);
-PHPAPI int php_setcookie(zend_string *name, zend_string *value, time_t expires, zend_string *path, zend_string *domain, int secure, int url_encode, int httponly, zend_string *samesite);
+PHPAPI int php_setcookie(zend_string *name, zend_string *value, time_t expires, zend_string *path, zend_string *domain, int secure, int httponly, zend_string *samesite, int url_encode);
 
 #endif
index 167e65f0a73577bcd85cb8b5e80198cebcbdc4ee..d41bed01f4e9e3866817ef9e3c6aff10dd575ed5 100644 (file)
@@ -17,6 +17,8 @@ setcookie('name', 'value', 0, '', 'domain.tld');
 setcookie('name', 'value', 0, '', '', TRUE);
 setcookie('name', 'value', 0, '', '', FALSE, TRUE);
 
+setcookie('name', 'value', ['expires' => $tsp]);
+setcookie('name', 'value', ['expires' => $tsn, 'path' => '/path/', 'domain' => 'domain.tld', 'secure' => true, 'httponly' => true, 'samesite' => 'Strict']);
 
 $expected = array(
        'Set-Cookie: name=deleted; expires='.date('D, d-M-Y H:i:s', 1).' GMT; Max-Age=0',
@@ -30,7 +32,9 @@ $expected = array(
        'Set-Cookie: name=value; path=/path/',
        'Set-Cookie: name=value; domain=domain.tld',
        'Set-Cookie: name=value; secure',
-       'Set-Cookie: name=value; HttpOnly'
+       'Set-Cookie: name=value; HttpOnly',
+       'Set-Cookie: name=value; expires='.date('D, d-M-Y H:i:s', $tsp).' GMT; Max-Age=5',
+       'Set-Cookie: name=value; expires='.date('D, d-M-Y H:i:s', $tsn).' GMT; Max-Age=0; path=/path/; domain=domain.tld; secure; HttpOnly; SameSite=Strict'
 );
 
 $headers = headers_list();
diff --git a/ext/standard/tests/network/setcookie_error.phpt b/ext/standard/tests/network/setcookie_error.phpt
new file mode 100644 (file)
index 0000000..1cbdf9e
--- /dev/null
@@ -0,0 +1,26 @@
+--TEST--
+setcookie() array variant error tests
+--INI--
+date.timezone=UTC
+--FILE--
+<?php
+
+ob_start();
+
+// Unrecognized key and no valid keys
+setcookie('name', 'value', ['unknown_key' => 'only']);
+// Numeric key and no valid keys
+setcookie('name', 'value', [0 => 'numeric_key']);
+// Unrecognized key
+setcookie('name', 'value', ['path' => '/path/', 'foo' => 'bar']);
+
+--EXPECTF--
+Warning: setcookie(): Unrecognized key 'unknown_key' found in the options array in %s
+
+Warning: setcookie(): No valid options were found in the given array in %s
+
+Warning: setcookie(): Numeric key found in the options array in %s
+
+Warning: setcookie(): No valid options were found in the given array in %s
+
+Warning: setcookie(): Unrecognized key 'foo' found in the options array in %s