Setting Cookies with PHP

Let’s start with a little background information.

The internet works by sending packets. These are fun-sized messages that contain data and information about the data. The packet data information is a “header,” which tells the recipient what type of data it is receiving. Think of it as giving out candy on Halloween. The wrapper tells the kids that you just gave them a KitKat and not some Skittles or Lifesavers.

Cookies are HTTP headers used to store information, and they are usually related to the websites you visit. The browser holds them, so when you revisit the site, there is some information about your visit that can be used by the server to customize your experience.

Cookies, by definition, are web technology. PHP has to work with browsers to create and delete cookies, but your server can’t manage them. That is up to the browsers. By separating that management piece from the servers, older browsers could achieve stateless communication while keeping some state for future requests.

If you want to set a cookie with PHP, the official PHP group site uses setcookie. setcookie abstracts most of the formatting onto the function’s signature.

PHP version under 7.3:

setcookie ( string $name [, string $value = "" [, int $expires = 0 [, string $path = "" [, string $domain = "" [, bool $secure = FALSE [, bool $httponly = FALSE ]]]]]] ) : bool

PHP version 7.3 and over:

setcookie ( string $name [, string $value = "" [, array $options = [] ]] ) : bool

First of all, I’m not too fond of it.

Don’t use functions that have different signatures. Two different signatures create an issue for anyone else who doesn’t write the code in the first place. Using two signatures, we ask other devs to be aware of one extra signature when reading or debugging our code. Our code should not increase the cognitive load; it should aim to decrease it.

The different signatures give us a rare glimpse of a function update. The new signature of setcookie is better. Passing an $options map allows the devs to extend this function without appending more parameters. Optional values like expiration, domain, and secure are now key/value pairs, and any new options won’t require changes to the function, but just the array that gets passed onto it.

I am willing to bet that they created this new signature once they figured that the function did not support the SameSite option on the old version. By the way, the SameSite default value is true. web.dev has a good write-up. For us who are still learning about cookies, SameSite is an optional parameter that allows cookies to be part of different subdomains. For example, we may set a cookie on site A to see if the user selects dark-mode. Then, we may only check for the dark-mode cookie on our site B if SameSite is set to false and the cookie is set to secured. This SameSite secured change happened recently, by the way.

The issues with cognitive load doesn’t end there. The return adds even more confusion:

If output exists prior to calling this function, setcookie() will fail and return false. If setcookie() successfully runs, it will return true. This does not indicate whether the user accepted the cookie.

The boolean variable that the function returns doesn’t indicate if the function successfully added the header. This behavior is understandable once we learn how cookies work since cookies are part of the client, but it is a misleading return.

My suggestion to alleviate the setcookie cognitive load issue is not to use it at all. This function is an abstraction of the header function. There are a few better cookie management libraries. PHP-Cookie comes to mind. I use the header function, in which the only parameter is a string based on the RFC 2616 specification. Using the header function sets the correct expectation to the devs reading the code. Using the Set-Cookie string, I communicate the exact expected behavior of the header call.

$cookie_name = rawurlencode($cookie_name);
$cookie_value = "test";
$domain = _mysite.com
$max_age = 31536000; // one year

$cookie_str = "Set-Cookie: $cookie_name=$cookie_value; Path=/; Domain=$domain; Max-Age=$max_age; SameSite=None; Secure";

header($cookie_str);

I honestly implore you to try out modern API solutions to solve your state issues. Cookies are OK for straightforward content, but there is no reason for them if the information is sensitive. For more secure browser storage solutions, try a modern API like Web Storage