├── phpstan.tests.neon
├── phpstan.neon
├── .editorconfig
├── psalm.xml
├── src
└── Dflydev
│ └── FigCookies
│ ├── StringUtil.php
│ ├── FigRequestCookies.php
│ ├── Modifier
│ └── SameSite.php
│ ├── Cookie.php
│ ├── FigResponseCookies.php
│ ├── Cookies.php
│ ├── SetCookies.php
│ └── SetCookie.php
├── LICENSE
├── phpcs.xml.dist
├── composer.json
├── .github
└── workflows
│ └── tests.yml
├── README.md
└── CHANGELOG.md
/phpstan.tests.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | level: 4
3 | paths:
4 | - tests
5 |
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | level: 8
3 | paths:
4 | - src
5 | ignoreErrors:
6 | - '#Unsafe usage of new static\(\)\.#'
7 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | ; top-most EditorConfig file
2 | root = true
3 |
4 | # All files.
5 | [*]
6 | end_of_line = LF
7 | indent_style = space
8 | indent_size = 4
9 | charset = utf-8
10 | trim_trailing_whitespace = true
11 | insert_final_newline = true
12 |
--------------------------------------------------------------------------------
/psalm.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/Dflydev/FigCookies/StringUtil.php:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | ./src
16 | ./tests
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dflydev/fig-cookies",
3 | "description": "Cookies for PSR-7 HTTP Message Interface.",
4 | "license": "MIT",
5 | "keywords": ["psr7", "psr-7", "cookies"],
6 | "authors": [
7 | {
8 | "name": "Beau Simensen",
9 | "email": "beau@dflydev.com"
10 | }
11 | ],
12 | "config": {
13 | "allow-plugins": {
14 | "dealerdirect/phpcodesniffer-composer-installer": true,
15 | "phpstan/extension-installer": true
16 | }
17 | },
18 | "require": {
19 | "php": "^7.2 || ^8.0",
20 | "ext-pcre": "*",
21 | "psr/http-message": "^1.0.1 || ^2"
22 | },
23 | "autoload": {
24 | "psr-4": {
25 | "Dflydev\\FigCookies\\": "src/Dflydev/FigCookies"
26 | }
27 | },
28 | "require-dev": {
29 | "phpunit/phpunit": "^7.2.6 || ^9",
30 | "squizlabs/php_codesniffer": "^3.3",
31 | "doctrine/coding-standard": "^8",
32 | "phpstan/phpstan": "^0.12",
33 | "phpstan/phpstan-phpunit": "^0.12.16",
34 | "phpstan/extension-installer": "^1.0",
35 | "scrutinizer/ocular": "^1.8",
36 | "vimeo/psalm": "^4.4"
37 | },
38 | "autoload-dev": {
39 | "psr-4": {
40 | "Dflydev\\FigCookies\\": "tests/Dflydev/FigCookies"
41 | }
42 | },
43 | "extra": {
44 | "branch-alias": {
45 | "dev-main": "3.0.x-dev"
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Dflydev/FigCookies/FigRequestCookies.php:
--------------------------------------------------------------------------------
1 | get($name);
18 |
19 | if ($cookie) {
20 | return $cookie;
21 | }
22 |
23 | return Cookie::create($name, $value);
24 | }
25 |
26 | public static function set(RequestInterface $request, Cookie $cookie): RequestInterface
27 | {
28 | return Cookies::fromRequest($request)
29 | ->with($cookie)
30 | ->renderIntoCookieHeader($request);
31 | }
32 |
33 | public static function modify(RequestInterface $request, string $name, callable $modify): RequestInterface
34 | {
35 | if (! is_callable($modify)) {
36 | throw new InvalidArgumentException('$modify must be callable.');
37 | }
38 |
39 | $cookies = Cookies::fromRequest($request);
40 | $cookie = $modify($cookies->has($name)
41 | ? $cookies->get($name)
42 | : Cookie::create($name));
43 |
44 | return $cookies
45 | ->with($cookie)
46 | ->renderIntoCookieHeader($request);
47 | }
48 |
49 | public static function remove(RequestInterface $request, string $name): RequestInterface
50 | {
51 | return Cookies::fromRequest($request)
52 | ->without($name)
53 | ->renderIntoCookieHeader($request);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Dflydev/FigCookies/Modifier/SameSite.php:
--------------------------------------------------------------------------------
1 | value = $value;
27 | }
28 |
29 | public static function strict(): self
30 | {
31 | return new self(self::STRICT);
32 | }
33 |
34 | public static function lax(): self
35 | {
36 | return new self(self::LAX);
37 | }
38 |
39 | public static function none(): self
40 | {
41 | return new self(self::NONE);
42 | }
43 |
44 | /**
45 | * @throws InvalidArgumentException If the given SameSite string is neither strict nor lax.
46 | */
47 | public static function fromString(string $sameSite): self
48 | {
49 | $lowerCaseSite = strtolower($sameSite);
50 |
51 | if ($lowerCaseSite === 'strict') {
52 | return self::strict();
53 | }
54 |
55 | if ($lowerCaseSite === 'lax') {
56 | return self::lax();
57 | }
58 |
59 | if ($lowerCaseSite === 'none') {
60 | return self::none();
61 | }
62 |
63 | throw new InvalidArgumentException(sprintf(
64 | 'Expected modifier value to be either "strict", "lax", or "none", "%s" given',
65 | $sameSite
66 | ));
67 | }
68 |
69 | public function asString(): string
70 | {
71 | return 'SameSite=' . $this->value;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Dflydev/FigCookies/Cookie.php:
--------------------------------------------------------------------------------
1 | name = $name;
21 | $this->value = $value;
22 | }
23 |
24 | public function getName(): string
25 | {
26 | return $this->name;
27 | }
28 |
29 | public function getValue(): ?string
30 | {
31 | return $this->value;
32 | }
33 |
34 | public function withValue(?string $value = null): Cookie
35 | {
36 | $clone = clone $this;
37 |
38 | $clone->value = $value;
39 |
40 | return $clone;
41 | }
42 |
43 | /**
44 | * Render Cookie as a string.
45 | */
46 | public function __toString(): string
47 | {
48 | return urlencode($this->name) . '=' . urlencode((string) $this->value);
49 | }
50 |
51 | /**
52 | * Create a Cookie.
53 | */
54 | public static function create(string $name, ?string $value = null): Cookie
55 | {
56 | return new static($name, $value);
57 | }
58 |
59 | /**
60 | * Create a list of Cookies from a Cookie header value string.
61 | *
62 | * @return Cookie[]
63 | */
64 | public static function listFromCookieString(string $string): array
65 | {
66 | $cookies = StringUtil::splitOnAttributeDelimiter($string);
67 |
68 | return array_map(static function ($cookiePair) {
69 | return static::oneFromCookiePair($cookiePair);
70 | }, $cookies);
71 | }
72 |
73 | /**
74 | * Create one Cookie from a cookie key/value header value string.
75 | */
76 | public static function oneFromCookiePair(string $string): Cookie
77 | {
78 | [$cookieName, $cookieValue] = StringUtil::splitCookiePair($string);
79 |
80 | $cookie = new static($cookieName);
81 |
82 | if ($cookieValue !== null) {
83 | $cookie = $cookie->withValue($cookieValue);
84 | }
85 |
86 | return $cookie;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/Dflydev/FigCookies/FigResponseCookies.php:
--------------------------------------------------------------------------------
1 | get($name);
18 |
19 | if ($cookie) {
20 | return $cookie;
21 | }
22 |
23 | return SetCookie::create($name, $value);
24 | }
25 |
26 | public static function set(ResponseInterface $response, SetCookie $setCookie): ResponseInterface
27 | {
28 | return SetCookies::fromResponse($response)
29 | ->with($setCookie)
30 | ->renderIntoSetCookieHeader($response);
31 | }
32 |
33 | /**
34 | * @deprecated Do not use this method. Will be removed in v4.0.
35 | *
36 | * If you want to remove a cookie, create it normally and call ->expire()
37 | * on the SetCookie object.
38 | */
39 | public static function expire(ResponseInterface $response, string $cookieName): ResponseInterface
40 | {
41 | return static::set($response, SetCookie::createExpired($cookieName));
42 | }
43 |
44 | public static function modify(ResponseInterface $response, string $name, callable $modify): ResponseInterface
45 | {
46 | if (! is_callable($modify)) {
47 | throw new InvalidArgumentException('$modify must be callable.');
48 | }
49 |
50 | $setCookies = SetCookies::fromResponse($response);
51 | $setCookie = $modify($setCookies->has($name)
52 | ? $setCookies->get($name)
53 | : SetCookie::create($name));
54 |
55 | return $setCookies
56 | ->with($setCookie)
57 | ->renderIntoSetCookieHeader($response);
58 | }
59 |
60 | public static function remove(ResponseInterface $response, string $name): ResponseInterface
61 | {
62 | return SetCookies::fromResponse($response)
63 | ->without($name)
64 | ->renderIntoSetCookieHeader($response);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Dflydev/FigCookies/Cookies.php:
--------------------------------------------------------------------------------
1 | cookies[$cookie->getName()] = $cookie;
27 | }
28 | }
29 |
30 | public function has(string $name): bool
31 | {
32 | return isset($this->cookies[$name]);
33 | }
34 |
35 | public function get(string $name): ?Cookie
36 | {
37 | if (! $this->has($name)) {
38 | return null;
39 | }
40 |
41 | return $this->cookies[$name];
42 | }
43 |
44 | /** @return Cookie[] */
45 | public function getAll(): array
46 | {
47 | return array_values($this->cookies);
48 | }
49 |
50 | public function with(Cookie $cookie): Cookies
51 | {
52 | $clone = clone $this;
53 |
54 | $clone->cookies[$cookie->getName()] = $cookie;
55 |
56 | return $clone;
57 | }
58 |
59 | public function without(string $name): Cookies
60 | {
61 | $clone = clone $this;
62 |
63 | if (! $clone->has($name)) {
64 | return $clone;
65 | }
66 |
67 | unset($clone->cookies[$name]);
68 |
69 | return $clone;
70 | }
71 |
72 | /**
73 | * Render Cookies into a Request.
74 | */
75 | public function renderIntoCookieHeader(RequestInterface $request): RequestInterface
76 | {
77 | $cookieString = implode('; ', $this->cookies);
78 |
79 | $request = $request->withHeader(static::COOKIE_HEADER, $cookieString);
80 |
81 | return $request;
82 | }
83 |
84 | /**
85 | * Create Cookies from a Cookie header value string.
86 | */
87 | public static function fromCookieString(string $string): self
88 | {
89 | return new static(Cookie::listFromCookieString($string));
90 | }
91 |
92 | public static function fromRequest(RequestInterface $request): Cookies
93 | {
94 | $cookieString = $request->getHeaderLine(static::COOKIE_HEADER);
95 |
96 | return static::fromCookieString($cookieString);
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/Dflydev/FigCookies/SetCookies.php:
--------------------------------------------------------------------------------
1 | setCookies[$setCookie->getName()] = $setCookie;
27 | }
28 | }
29 |
30 | public function has(string $name): bool
31 | {
32 | return isset($this->setCookies[$name]);
33 | }
34 |
35 | public function get(string $name): ?SetCookie
36 | {
37 | if (! $this->has($name)) {
38 | return null;
39 | }
40 |
41 | return $this->setCookies[$name];
42 | }
43 |
44 | /** @return SetCookie[] */
45 | public function getAll(): array
46 | {
47 | return array_values($this->setCookies);
48 | }
49 |
50 | public function with(SetCookie $setCookie): SetCookies
51 | {
52 | $clone = clone $this;
53 |
54 | $clone->setCookies[$setCookie->getName()] = $setCookie;
55 |
56 | return $clone;
57 | }
58 |
59 | public function without(string $name): SetCookies
60 | {
61 | $clone = clone $this;
62 |
63 | if (! $clone->has($name)) {
64 | return $clone;
65 | }
66 |
67 | unset($clone->setCookies[$name]);
68 |
69 | return $clone;
70 | }
71 |
72 | /**
73 | * Render SetCookies into a Response.
74 | */
75 | public function renderIntoSetCookieHeader(ResponseInterface $response): ResponseInterface
76 | {
77 | $response = $response->withoutHeader(static::SET_COOKIE_HEADER);
78 | foreach ($this->setCookies as $setCookie) {
79 | $response = $response->withAddedHeader(static::SET_COOKIE_HEADER, (string) $setCookie);
80 | }
81 |
82 | return $response;
83 | }
84 |
85 | /**
86 | * Create SetCookies from a collection of SetCookie header value strings.
87 | *
88 | * @param string[] $setCookieStrings
89 | */
90 | public static function fromSetCookieStrings(array $setCookieStrings): self
91 | {
92 | return new static(array_map(static function (string $setCookieString): SetCookie {
93 | return SetCookie::fromSetCookieString($setCookieString);
94 | }, $setCookieStrings));
95 | }
96 |
97 | /**
98 | * Create SetCookies from a Response.
99 | */
100 | public static function fromResponse(ResponseInterface $response): SetCookies
101 | {
102 | return new static(array_map(static function (string $setCookieString): SetCookie {
103 | return SetCookie::fromSetCookieString($setCookieString);
104 | }, $response->getHeader(static::SET_COOKIE_HEADER)));
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on:
4 | push: ~
5 | pull_request: ~
6 |
7 | jobs:
8 | phpcs:
9 | name: PHPCS
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/checkout@v2
14 |
15 | - uses: shivammathur/setup-php@v2
16 | with:
17 | php-version: 8.0
18 | extensions: curl, mbstring
19 | coverage: none
20 | tools: composer:v2, cs2pr
21 |
22 | - run: composer update --no-progress
23 |
24 | - run: vendor/bin/phpcs -q --report=checkstyle | cs2pr
25 |
26 | phpunit:
27 | name: PHPUnit on ${{ matrix.php }} ${{ matrix.composer-flags }}
28 | runs-on: ubuntu-latest
29 | strategy:
30 | matrix:
31 | php: ['7.3', '7.4']
32 | coverage: [pcov]
33 | composer-flags: ['']
34 | include:
35 | - php: '7.2'
36 | coverage: 'none'
37 | - php: '8.0'
38 | coverage: 'none'
39 | - php: '8.1'
40 | coverage: 'none'
41 | - php: '8.2'
42 | coverage: 'none'
43 | - php: '8.3'
44 | coverage: 'none'
45 | - php: '8.4'
46 | coverage: 'none'
47 | - php: '8.5'
48 | coverage: 'none'
49 |
50 | steps:
51 | - uses: actions/checkout@v2
52 | with:
53 | fetch-depth: 0
54 |
55 | - uses: shivammathur/setup-php@v2
56 | with:
57 | php-version: ${{ matrix.php }}
58 | extensions: curl
59 | coverage: ${{ matrix.coverage }}
60 | tools: composer:v2
61 |
62 | - run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
63 |
64 | - name: "Use PHPUnit 9.3+ on PHP 8+"
65 | run: composer require --no-update --dev phpunit/phpunit:^9.3
66 | if: ${{ matrix.php == '8.0' || matrix.php == '8.1' || matrix.php == '8.2' }}
67 |
68 | - run: composer update --no-progress ${{ matrix.composer-flags }}
69 |
70 | - run: vendor/bin/phpunit --no-coverage
71 | if: ${{ matrix.coverage == 'none' }}
72 |
73 | - run: vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover
74 | if: ${{ matrix.coverage != 'none' }}
75 |
76 | - run: php vendor/bin/ocular code-coverage:upload --format=php-clover coverage.clover
77 | if: ${{ matrix.coverage != 'none' && github.repository == 'dflydev/dflydev-fig-cookies' }}
78 |
79 | phpstan:
80 | name: PHPStan
81 | runs-on: ubuntu-latest
82 |
83 | steps:
84 | - uses: actions/checkout@v2
85 |
86 | - uses: shivammathur/setup-php@v2
87 | with:
88 | php-version: 8.0
89 | extensions: curl
90 | coverage: none
91 | tools: composer:v2
92 |
93 | - run: composer update --no-progress
94 |
95 | - run: vendor/bin/phpstan analyse --no-progress
96 |
97 | - run: vendor/bin/phpstan analyse --no-progress -c phpstan.tests.neon
98 |
99 | psalm:
100 | name: Psalm
101 | runs-on: ubuntu-latest
102 |
103 | steps:
104 | - uses: actions/checkout@v2
105 |
106 | - uses: shivammathur/setup-php@v2
107 | with:
108 | php-version: 8.0
109 | extensions: curl
110 | coverage: none
111 | tools: composer:v2
112 |
113 | - run: composer update --no-progress
114 |
115 | - run: vendor/bin/psalm --no-progress --output-format=github
116 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FIG Cookies
2 |
3 | Managing Cookies for PSR-7 Requests and Responses.
4 |
5 | [](https://packagist.org/packages/dflydev/fig-cookies)
6 | [](https://packagist.org/packages/dflydev/fig-cookies)
7 | [](https://packagist.org/packages/dflydev/fig-cookies)
8 | [](https://packagist.org/packages/dflydev/fig-cookies)
9 |
10 | [](https://travis-ci.org/dflydev/dflydev-fig-cookies)
11 | [](https://scrutinizer-ci.com/g/dflydev/dflydev-fig-cookies/?branch=master)
12 | [](https://scrutinizer-ci.com/g/dflydev/dflydev-fig-cookies/?branch=master)
13 | [](https://codeclimate.com/github/dflydev/dflydev-fig-cookies)
14 |
15 | [](https://gitter.im/dflydev/dflydev-fig-cookies?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
16 |
17 | ## Installation
18 |
19 | ```bash
20 | $> composer require dflydev/fig-cookies
21 | ```
22 |
23 | ## Concepts
24 |
25 | FIG Cookies tackles two problems, managing **Cookie** _Request_ headers and
26 | managing **Set-Cookie** _Response_ headers. It does this by way of introducing
27 | a `Cookies` class to manage collections of `Cookie` instances and a
28 | `SetCookies` class to manage collections of `SetCookie` instances.
29 |
30 | Instantiating these collections looks like this:
31 |
32 | ```php
33 | // Get a collection representing the cookies in the Cookie headers
34 | // of a PSR-7 Request.
35 | $cookies = Dflydev\FigCookies\Cookies::fromRequest($request);
36 |
37 | // Get a collection representing the cookies in the Set-Cookie headers
38 | // of a PSR-7 Response
39 | $setCookies = Dflydev\FigCookies\SetCookies::fromResponse($response);
40 | ```
41 |
42 | After modifying these collections in some way, they are rendered into a
43 | PSR-7 Request or PSR-7 Response like this:
44 |
45 | ```php
46 | // Render the Cookie headers and add them to the headers of a
47 | // PSR-7 Request.
48 | $request = $cookies->renderIntoCookieHeader($request);
49 |
50 | // Render the Set-Cookie headers and add them to the headers of a
51 | // PSR-7 Response.
52 | $response = $setCookies->renderIntoSetCookieHeader($response);
53 | ```
54 |
55 | Like PSR-7 Messages, `Cookie`, `Cookies`, `SetCookie`, and `SetCookies`
56 | are all represented as immutable value objects and all mutators will
57 | return new instances of the original with the requested changes.
58 |
59 | While this style of design has many benefits it can become fairly
60 | verbose very quickly. In order to get around that, FIG Cookies provides
61 | two facades in an attempt to help simplify things and make the whole process
62 | less verbose.
63 |
64 | ## Basic Usage
65 |
66 | The easiest way to start working with FIG Cookies is by using the
67 | `FigRequestCookies` and `FigResponseCookies` classes. They are facades to the
68 | primitive FIG Cookies classes. Their jobs are to make common cookie related
69 | tasks easier and less verbose than working with the primitive classes directly.
70 |
71 | There is overhead on creating `Cookies` and `SetCookies` and rebuilding
72 | requests and responses. Each of the `FigCookies` methods will go through this
73 | process so be wary of using too many of these calls in the same section of
74 | code. In some cases it may be better to work with the primitive FIG Cookies
75 | classes directly rather than using the facades.
76 |
77 | ### Request Cookies
78 |
79 | Requests include cookie information in the **Cookie** request header. The
80 | cookies in this header are represented by the `Cookie` class.
81 |
82 | ```php
83 | use Dflydev\FigCookies\Cookie;
84 |
85 | $cookie = Cookie::create('theme', 'blue');
86 | ```
87 |
88 | To easily work with request cookies, use the `FigRequestCookies` facade.
89 |
90 | #### Get a Request Cookie
91 |
92 | The `get` method will return a `Cookie` instance. If no cookie by the specified
93 | name exists, the returned `Cookie` instance will have a `null` value.
94 |
95 | The optional third parameter to `get` sets the value that should be used if a
96 | cookie does not exist.
97 |
98 | ```php
99 | use Dflydev\FigCookies\FigRequestCookies;
100 |
101 | $cookie = FigRequestCookies::get($request, 'theme');
102 | $cookie = FigRequestCookies::get($request, 'theme', 'default-theme');
103 | ```
104 |
105 | #### Set a Request Cookie
106 |
107 | The `set` method will either add a cookie or replace an existing cookie.
108 |
109 | The `Cookie` primitive is used as the second argument.
110 |
111 | ```php
112 | use Dflydev\FigCookies\FigRequestCookies;
113 |
114 | $request = FigRequestCookies::set($request, Cookie::create('theme', 'blue'));
115 | ```
116 |
117 | #### Modify a Request Cookie
118 |
119 | The `modify` method allows for replacing the contents of a cookie based on the
120 | current cookie with the specified name. The third argument is a `callable` that
121 | takes a `Cookie` instance as its first argument and is expected to return a
122 | `Cookie` instance.
123 |
124 | If no cookie by the specified name exists, a new `Cookie` instance with a
125 | `null` value will be passed to the callable.
126 |
127 | ```php
128 | use Dflydev\FigCookies\FigRequestCookies;
129 |
130 | $modify = function (Cookie $cookie) {
131 | $value = $cookie->getValue();
132 |
133 | // ... inspect current $value and determine if $value should
134 | // change or if it can stay the same. in all cases, a cookie
135 | // should be returned from this callback...
136 |
137 | return $cookie->withValue($value);
138 | }
139 |
140 | $request = FigRequestCookies::modify($request, 'theme', $modify);
141 | ```
142 |
143 | #### Remove a Request Cookie
144 |
145 | The `remove` method removes a cookie from the request headers if it exists.
146 |
147 | ```php
148 | use Dflydev\FigCookies\FigRequestCookies;
149 |
150 | $request = FigRequestCookies::remove($request, 'theme');
151 | ```
152 |
153 | Note that this does not cause the client to remove the cookie. Take a look at
154 | the `SetCookie` class' `expire()` method to do that.
155 |
156 | ### Response Cookies
157 |
158 | Responses include cookie information in the **Set-Cookie** response header. The
159 | cookies in these headers are represented by the `SetCookie` class.
160 |
161 | ```php
162 | use Dflydev\FigCookies\Modifier\SameSite;
163 | use Dflydev\FigCookies\SetCookie;
164 |
165 | $setCookie = SetCookie::create('lu')
166 | ->withValue('Rg3vHJZnehYLjVg7qi3bZjzg')
167 | ->withExpires('Tue, 15-Jan-2013 21:47:38 GMT')
168 | ->withMaxAge(500)
169 | ->rememberForever()
170 | ->withPath('/')
171 | ->withDomain('.example.com')
172 | ->withSecure(true)
173 | ->withHttpOnly(true)
174 | ->withSameSite(SameSite::lax())
175 | ->withPartitioned()
176 | ;
177 | ```
178 |
179 | To easily work with response cookies, use the `FigResponseCookies` facade.
180 |
181 | #### Get a Response Cookie
182 |
183 | The `get` method will return a `SetCookie` instance. If no cookie by the
184 | specified name exists, the returned `SetCookie` instance will have a `null`
185 | value.
186 |
187 | The optional third parameter to `get` sets the value that should be used if a
188 | cookie does not exist.
189 |
190 | ```php
191 | use Dflydev\FigCookies\FigResponseCookies;
192 |
193 | $setCookie = FigResponseCookies::get($response, 'theme');
194 | $setCookie = FigResponseCookies::get($response, 'theme', 'simple');
195 | ```
196 |
197 | #### Set a Response Cookie
198 |
199 | The `set` method will either add a cookie or replace an existing cookie.
200 |
201 | The `SetCookie` primitive is used as the second argument.
202 |
203 | ```php
204 | use Dflydev\FigCookies\FigResponseCookies;
205 |
206 | $response = FigResponseCookies::set($response, SetCookie::create('token')
207 | ->withValue('a9s87dfz978a9')
208 | ->withDomain('example.com')
209 | ->withPath('/firewall')
210 | );
211 | ```
212 |
213 | #### Modify a Response Cookie
214 |
215 | The `modify` method allows for replacing the contents of a cookie based on the
216 | current cookie with the specified name. The third argument is a `callable` that
217 | takes a `SetCookie` instance as its first argument and is expected to return a
218 | `SetCookie` instance.
219 |
220 | If no cookie by the specified name exists, a new `SetCookie` instance with a
221 | `null` value will be passed to the callable.
222 |
223 | ```php
224 | use Dflydev\FigCookies\FigResponseCookies;
225 |
226 | $modify = function (SetCookie $setCookie) {
227 | $value = $setCookie->getValue();
228 |
229 | // ... inspect current $value and determine if $value should
230 | // change or if it can stay the same. in all cases, a cookie
231 | // should be returned from this callback...
232 |
233 | return $setCookie
234 | ->withValue($newValue)
235 | ->withExpires($newExpires)
236 | ;
237 | }
238 |
239 | $response = FigResponseCookies::modify($response, 'theme', $modify);
240 | ```
241 |
242 | #### Remove a Response Cookie
243 |
244 | The `remove` method removes a cookie from the response if it exists.
245 |
246 | ```php
247 | use Dflydev\FigCookies\FigResponseCookies;
248 |
249 | $response = FigResponseCookies::remove($response, 'theme');
250 | ```
251 |
252 | #### Expire a Response Cookie
253 |
254 | The `expire` method sets a cookie with an expiry date in the far past. This
255 | causes the client to remove the cookie.
256 |
257 | Note that in order to expire a cookie, you need to configure its `Set-Cookie`
258 | header just like when you initially wrote the cookie (i.e. same domain/path).
259 | The easiest way to do this is to re-use the same code for configuring the
260 | header when setting as well as expiring the cookie:
261 |
262 | ```php
263 | use Dflydev\FigCookies\FigResponseCookies;
264 | use Dflydev\FigCookies\SetCookie;
265 |
266 | $setCookie = SetCookie::create('ba')
267 | ->withValue('UQdfdafpJJ23k111m')
268 | ->withPath('/')
269 | ->withDomain('.example.com')
270 | ;
271 |
272 | FigResponseCookies::set($response, $setCookie->expire());
273 | ```
274 |
275 | ## FAQ
276 |
277 | ### Do you call `setcookies`?
278 |
279 | No.
280 |
281 | Delivery of the rendered `SetCookie` instances is the responsibility of the
282 | PSR-7 client implementation.
283 |
284 | ### Do you do anything with sessions?
285 |
286 | No.
287 |
288 | It would be possible to build session handling using cookies on top of FIG
289 | Cookies but it is out of scope for this package.
290 |
291 | ### Do you read from `$_COOKIES`?
292 |
293 | No.
294 |
295 | FIG Cookies only pays attention to the `Cookie` headers on
296 | [PSR-7](https://packagist.org/packages/psr/http-message) Request
297 | instances. In the case of `ServerRequestInterface` instances, PSR-7
298 | implementations should be including `$_COOKIES` values in the headers
299 | so in that case FIG Cookies may be interacting with `$_COOKIES`
300 | indirectly.
301 |
302 | ## License
303 |
304 | MIT, see LICENSE.
305 |
306 | ## Community
307 |
308 | Want to get involved? Here are a few ways:
309 |
310 | - Find us in the #dflydev IRC channel on irc.freenode.org.
311 | - Mention [@dflydev](https://twitter.com/dflydev) on Twitter.
312 | - [](https://gitter.im/dflydev/dflydev-fig-cookies?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
313 |
--------------------------------------------------------------------------------
/src/Dflydev/FigCookies/SetCookie.php:
--------------------------------------------------------------------------------
1 | name = $name;
51 | $this->value = $value;
52 | }
53 |
54 | public function getName(): string
55 | {
56 | return $this->name;
57 | }
58 |
59 | public function getValue(): ?string
60 | {
61 | return $this->value;
62 | }
63 |
64 | public function getExpires(): int
65 | {
66 | return $this->expires;
67 | }
68 |
69 | public function getMaxAge(): int
70 | {
71 | return $this->maxAge;
72 | }
73 |
74 | public function getPath(): ?string
75 | {
76 | return $this->path;
77 | }
78 |
79 | public function getDomain(): ?string
80 | {
81 | return $this->domain;
82 | }
83 |
84 | public function getSecure(): bool
85 | {
86 | return $this->secure;
87 | }
88 |
89 | public function getHttpOnly(): bool
90 | {
91 | return $this->httpOnly;
92 | }
93 |
94 | public function getSameSite(): ?SameSite
95 | {
96 | return $this->sameSite;
97 | }
98 |
99 | public function getPartitioned(): bool
100 | {
101 | return $this->partitioned;
102 | }
103 |
104 | public function withValue(?string $value = null): self
105 | {
106 | $clone = clone $this;
107 |
108 | $clone->value = $value;
109 |
110 | return $clone;
111 | }
112 |
113 | /** @param int|DateTimeInterface|string|null $expires */
114 | private function resolveExpires($expires = null): int
115 | {
116 | if ($expires === null) {
117 | return 0;
118 | }
119 |
120 | if ($expires instanceof DateTimeInterface) {
121 | return (int) $expires->getTimestamp();
122 | }
123 |
124 | if (is_numeric($expires)) {
125 | return (int) $expires;
126 | }
127 |
128 | $time = strtotime($expires);
129 |
130 | if (! is_int($time)) {
131 | throw new InvalidArgumentException(sprintf('Invalid expires "%s" provided', $expires));
132 | }
133 |
134 | return $time;
135 | }
136 |
137 | /** @param int|string|DateTimeInterface|null $expires */
138 | public function withExpires($expires = null): self
139 | {
140 | $expires = $this->resolveExpires($expires);
141 |
142 | $clone = clone $this;
143 |
144 | $clone->expires = $expires;
145 |
146 | return $clone;
147 | }
148 |
149 | public function rememberForever(): self
150 | {
151 | return $this->withExpires(new DateTime('+5 years'));
152 | }
153 |
154 | public function expire(): self
155 | {
156 | return $this->withExpires(new DateTime('-5 years'));
157 | }
158 |
159 | public function withMaxAge(?int $maxAge = null): self
160 | {
161 | $clone = clone $this;
162 |
163 | $clone->maxAge = (int) $maxAge;
164 |
165 | return $clone;
166 | }
167 |
168 | public function withPath(?string $path = null): self
169 | {
170 | $clone = clone $this;
171 |
172 | $clone->path = $path;
173 |
174 | return $clone;
175 | }
176 |
177 | public function withDomain(?string $domain = null): self
178 | {
179 | $clone = clone $this;
180 |
181 | $clone->domain = $domain;
182 |
183 | return $clone;
184 | }
185 |
186 | public function withSecure(bool $secure = true): self
187 | {
188 | $clone = clone $this;
189 |
190 | $clone->secure = $secure;
191 |
192 | return $clone;
193 | }
194 |
195 | public function withHttpOnly(bool $httpOnly = true): self
196 | {
197 | $clone = clone $this;
198 |
199 | $clone->httpOnly = $httpOnly;
200 |
201 | return $clone;
202 | }
203 |
204 | public function withSameSite(SameSite $sameSite): self
205 | {
206 | $clone = clone $this;
207 |
208 | $clone->sameSite = $sameSite;
209 |
210 | return $clone;
211 | }
212 |
213 | public function withoutSameSite(): self
214 | {
215 | $clone = clone $this;
216 |
217 | $clone->sameSite = null;
218 |
219 | return $clone;
220 | }
221 |
222 | public function withPartitioned(bool $partitioned = true): self
223 | {
224 | $clone = clone $this;
225 |
226 | $clone->partitioned = $partitioned;
227 | if ($partitioned) {
228 | $clone->secure = true;
229 | }
230 |
231 | return $clone;
232 | }
233 |
234 | public function __toString(): string
235 | {
236 | $cookieStringParts = [
237 | urlencode($this->name) . '=' . urlencode((string) $this->value),
238 | ];
239 |
240 | $cookieStringParts = $this->appendFormattedDomainPartIfSet($cookieStringParts);
241 | $cookieStringParts = $this->appendFormattedPathPartIfSet($cookieStringParts);
242 | $cookieStringParts = $this->appendFormattedExpiresPartIfSet($cookieStringParts);
243 | $cookieStringParts = $this->appendFormattedMaxAgePartIfSet($cookieStringParts);
244 | $cookieStringParts = $this->appendFormattedSecurePartIfSet($cookieStringParts);
245 | $cookieStringParts = $this->appendFormattedHttpOnlyPartIfSet($cookieStringParts);
246 | $cookieStringParts = $this->appendFormattedSameSitePartIfSet($cookieStringParts);
247 | $cookieStringParts = $this->appendFormattedPartitionedPartIfSet($cookieStringParts);
248 |
249 | return implode('; ', $cookieStringParts);
250 | }
251 |
252 | public static function create(string $name, ?string $value = null): self
253 | {
254 | return new static($name, $value);
255 | }
256 |
257 | public static function createRememberedForever(string $name, ?string $value = null): self
258 | {
259 | return static::create($name, $value)->rememberForever();
260 | }
261 |
262 | /**
263 | * @deprecated Do not use this method. Will be removed in v4.0.
264 | *
265 | * If you want to remove a cookie, create it normally and call ->expire()
266 | * on the SetCookie object.
267 | */
268 | public static function createExpired(string $name): self
269 | {
270 | return static::create($name)->expire();
271 | }
272 |
273 | public static function fromSetCookieString(string $string): self
274 | {
275 | $rawAttributes = StringUtil::splitOnAttributeDelimiter($string);
276 |
277 | $rawAttribute = array_shift($rawAttributes);
278 |
279 | if (! is_string($rawAttribute)) {
280 | throw new InvalidArgumentException(sprintf(
281 | 'The provided cookie string "%s" must have at least one attribute',
282 | $string
283 | ));
284 | }
285 |
286 | [$cookieName, $cookieValue] = StringUtil::splitCookiePair($rawAttribute);
287 |
288 | $setCookie = new static($cookieName);
289 |
290 | if ($cookieValue !== null) {
291 | $setCookie = $setCookie->withValue($cookieValue);
292 | }
293 |
294 | while ($rawAttribute = array_shift($rawAttributes)) {
295 | $rawAttributePair = explode('=', $rawAttribute, 2);
296 |
297 | $attributeKey = $rawAttributePair[0];
298 | $attributeValue = count($rawAttributePair) > 1 ? $rawAttributePair[1] : null;
299 |
300 | $attributeKey = strtolower($attributeKey);
301 |
302 | switch ($attributeKey) {
303 | case 'expires':
304 | $setCookie = $setCookie->withExpires($attributeValue);
305 | break;
306 | case 'max-age':
307 | $setCookie = $setCookie->withMaxAge((int) $attributeValue);
308 | break;
309 | case 'domain':
310 | $setCookie = $setCookie->withDomain($attributeValue);
311 | break;
312 | case 'path':
313 | $setCookie = $setCookie->withPath($attributeValue);
314 | break;
315 | case 'secure':
316 | $setCookie = $setCookie->withSecure(true);
317 | break;
318 | case 'httponly':
319 | $setCookie = $setCookie->withHttpOnly(true);
320 | break;
321 | case 'samesite':
322 | $setCookie = $setCookie->withSameSite(SameSite::fromString((string) $attributeValue));
323 | break;
324 | case 'partitioned':
325 | $setCookie = $setCookie->withPartitioned();
326 | break;
327 | }
328 | }
329 |
330 | return $setCookie;
331 | }
332 |
333 | /**
334 | * @param string[] $cookieStringParts
335 | *
336 | * @return string[]
337 | */
338 | private function appendFormattedDomainPartIfSet(array $cookieStringParts): array
339 | {
340 | if ($this->domain) {
341 | $cookieStringParts[] = sprintf('Domain=%s', $this->domain);
342 | }
343 |
344 | return $cookieStringParts;
345 | }
346 |
347 | /**
348 | * @param string[] $cookieStringParts
349 | *
350 | * @return string[]
351 | */
352 | private function appendFormattedPathPartIfSet(array $cookieStringParts): array
353 | {
354 | if ($this->path) {
355 | $cookieStringParts[] = sprintf('Path=%s', $this->path);
356 | }
357 |
358 | return $cookieStringParts;
359 | }
360 |
361 | /**
362 | * @param string[] $cookieStringParts
363 | *
364 | * @return string[]
365 | */
366 | private function appendFormattedExpiresPartIfSet(array $cookieStringParts): array
367 | {
368 | if ($this->expires) {
369 | $cookieStringParts[] = sprintf('Expires=%s', gmdate('D, d M Y H:i:s T', $this->expires));
370 | }
371 |
372 | return $cookieStringParts;
373 | }
374 |
375 | /**
376 | * @param string[] $cookieStringParts
377 | *
378 | * @return string[]
379 | */
380 | private function appendFormattedMaxAgePartIfSet(array $cookieStringParts): array
381 | {
382 | if ($this->maxAge) {
383 | $cookieStringParts[] = sprintf('Max-Age=%s', $this->maxAge);
384 | }
385 |
386 | return $cookieStringParts;
387 | }
388 |
389 | /**
390 | * @param string[] $cookieStringParts
391 | *
392 | * @return string[]
393 | */
394 | private function appendFormattedSecurePartIfSet(array $cookieStringParts): array
395 | {
396 | if ($this->secure) {
397 | $cookieStringParts[] = 'Secure';
398 | }
399 |
400 | return $cookieStringParts;
401 | }
402 |
403 | /**
404 | * @param string[] $cookieStringParts
405 | *
406 | * @return string[]
407 | */
408 | private function appendFormattedHttpOnlyPartIfSet(array $cookieStringParts): array
409 | {
410 | if ($this->httpOnly) {
411 | $cookieStringParts[] = 'HttpOnly';
412 | }
413 |
414 | return $cookieStringParts;
415 | }
416 |
417 | /**
418 | * @param string[] $cookieStringParts
419 | *
420 | * @return string[]
421 | */
422 | private function appendFormattedSameSitePartIfSet(array $cookieStringParts): array
423 | {
424 | if ($this->sameSite === null) {
425 | return $cookieStringParts;
426 | }
427 |
428 | $cookieStringParts[] = $this->sameSite->asString();
429 |
430 | return $cookieStringParts;
431 | }
432 |
433 | /**
434 | * @param string[] $cookieStringParts
435 | *
436 | * @return string[]
437 | */
438 | private function appendFormattedPartitionedPartIfSet(array $cookieStringParts): array
439 | {
440 | if ($this->partitioned) {
441 | $cookieStringParts[] = 'Partitioned';
442 | }
443 |
444 | return $cookieStringParts;
445 | }
446 | }
447 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file, in reverse chronological order by release.
4 |
5 | ## 2.0.0 - 2018-07-11
6 |
7 | ### Added
8 |
9 | - [#32](https://github.com/dflydev/dflydev-fig-cookies/pull/32) all public API of the
10 | project now has extensive parameter and return type declarations.
11 | If you are using the library with `declare(strict_types=1);` in your codebase, you
12 | will need to run static analysis against your code to find possible type incompatibilities
13 | in method calls.
14 | If you are inheriting any of your code from this library, you will need to check
15 | that your type signatures respect variance and covariance with the symbols you are
16 | inheriting from. Here is a full list of the changes:
17 | ```
18 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\Cookie#__construct() changed from no type to a non-contravariant string
19 | [BC] CHANGED: The parameter $value of Dflydev\FigCookies\Cookie#__construct() changed from no type to a non-contravariant ?string
20 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\Cookie#__construct() changed from no type to string
21 | [BC] CHANGED: The parameter $value of Dflydev\FigCookies\Cookie#__construct() changed from no type to ?string
22 | [BC] CHANGED: The return type of Dflydev\FigCookies\Cookie#getName() changed from no type to string
23 | [BC] CHANGED: The return type of Dflydev\FigCookies\Cookie#getValue() changed from no type to ?string
24 | [BC] CHANGED: The return type of Dflydev\FigCookies\Cookie#withValue() changed from no type to Dflydev\FigCookies\Cookie
25 | [BC] CHANGED: The parameter $value of Dflydev\FigCookies\Cookie#withValue() changed from no type to a non-contravariant ?string
26 | [BC] CHANGED: The parameter $value of Dflydev\FigCookies\Cookie#withValue() changed from no type to ?string
27 | [BC] CHANGED: The return type of Dflydev\FigCookies\Cookie#__toString() changed from no type to string
28 | [BC] CHANGED: The return type of Dflydev\FigCookies\Cookie::create() changed from no type to Dflydev\FigCookies\Cookie
29 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\Cookie::create() changed from no type to a non-contravariant string
30 | [BC] CHANGED: The parameter $value of Dflydev\FigCookies\Cookie::create() changed from no type to a non-contravariant ?string
31 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\Cookie::create() changed from no type to string
32 | [BC] CHANGED: The parameter $value of Dflydev\FigCookies\Cookie::create() changed from no type to ?string
33 | [BC] CHANGED: The return type of Dflydev\FigCookies\Cookie::listFromCookieString() changed from no type to array
34 | [BC] CHANGED: The parameter $string of Dflydev\FigCookies\Cookie::listFromCookieString() changed from no type to a non-contravariant string
35 | [BC] CHANGED: The parameter $string of Dflydev\FigCookies\Cookie::listFromCookieString() changed from no type to string
36 | [BC] CHANGED: The return type of Dflydev\FigCookies\Cookie::oneFromCookiePair() changed from no type to Dflydev\FigCookies\Cookie
37 | [BC] CHANGED: The parameter $string of Dflydev\FigCookies\Cookie::oneFromCookiePair() changed from no type to a non-contravariant string
38 | [BC] CHANGED: The parameter $string of Dflydev\FigCookies\Cookie::oneFromCookiePair() changed from no type to string
39 | [BC] CHANGED: The return type of Dflydev\FigCookies\FigRequestCookies::get() changed from no type to Dflydev\FigCookies\Cookie
40 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\FigRequestCookies::get() changed from no type to a non-contravariant string
41 | [BC] CHANGED: The parameter $value of Dflydev\FigCookies\FigRequestCookies::get() changed from no type to a non-contravariant ?string
42 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\FigRequestCookies::get() changed from no type to string
43 | [BC] CHANGED: The parameter $value of Dflydev\FigCookies\FigRequestCookies::get() changed from no type to ?string
44 | [BC] CHANGED: The return type of Dflydev\FigCookies\FigRequestCookies::set() changed from no type to Psr\Http\Message\RequestInterface
45 | [BC] CHANGED: The return type of Dflydev\FigCookies\FigRequestCookies::modify() changed from no type to Psr\Http\Message\RequestInterface
46 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\FigRequestCookies::modify() changed from no type to a non-contravariant string
47 | [BC] CHANGED: The parameter $modify of Dflydev\FigCookies\FigRequestCookies::modify() changed from no type to a non-contravariant callable
48 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\FigRequestCookies::modify() changed from no type to string
49 | [BC] CHANGED: The parameter $modify of Dflydev\FigCookies\FigRequestCookies::modify() changed from no type to callable
50 | [BC] CHANGED: The return type of Dflydev\FigCookies\FigRequestCookies::remove() changed from no type to Psr\Http\Message\RequestInterface
51 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\FigRequestCookies::remove() changed from no type to a non-contravariant string
52 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\FigRequestCookies::remove() changed from no type to string
53 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookies#has() changed from no type to bool
54 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\SetCookies#has() changed from no type to a non-contravariant string
55 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\SetCookies#has() changed from no type to string
56 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookies#get() changed from no type to ?Dflydev\FigCookies\SetCookie
57 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\SetCookies#get() changed from no type to a non-contravariant string
58 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\SetCookies#get() changed from no type to string
59 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookies#getAll() changed from no type to array
60 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookies#with() changed from no type to Dflydev\FigCookies\SetCookies
61 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookies#without() changed from no type to Dflydev\FigCookies\SetCookies
62 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\SetCookies#without() changed from no type to a non-contravariant string
63 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\SetCookies#without() changed from no type to string
64 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookies#renderIntoSetCookieHeader() changed from no type to Psr\Http\Message\ResponseInterface
65 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookies::fromSetCookieStrings() changed from no type to self
66 | [BC] CHANGED: The parameter $setCookieStrings of Dflydev\FigCookies\SetCookies::fromSetCookieStrings() changed from no type to a non-contravariant array
67 | [BC] CHANGED: The parameter $setCookieStrings of Dflydev\FigCookies\SetCookies::fromSetCookieStrings() changed from no type to array
68 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookies::fromResponse() changed from no type to Dflydev\FigCookies\SetCookies
69 | [BC] CHANGED: The return type of Dflydev\FigCookies\StringUtil::splitOnAttributeDelimiter() changed from no type to array
70 | [BC] CHANGED: The parameter $string of Dflydev\FigCookies\StringUtil::splitOnAttributeDelimiter() changed from no type to a non-contravariant string
71 | [BC] CHANGED: The parameter $string of Dflydev\FigCookies\StringUtil::splitOnAttributeDelimiter() changed from no type to string
72 | [BC] CHANGED: The return type of Dflydev\FigCookies\StringUtil::splitCookiePair() changed from no type to array
73 | [BC] CHANGED: The parameter $string of Dflydev\FigCookies\StringUtil::splitCookiePair() changed from no type to a non-contravariant string
74 | [BC] CHANGED: The parameter $string of Dflydev\FigCookies\StringUtil::splitCookiePair() changed from no type to string
75 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie#getName() changed from no type to string
76 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie#getValue() changed from no type to ?string
77 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie#getExpires() changed from no type to int
78 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie#getMaxAge() changed from no type to int
79 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie#getPath() changed from no type to ?string
80 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie#getDomain() changed from no type to ?string
81 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie#getSecure() changed from no type to bool
82 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie#getHttpOnly() changed from no type to bool
83 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie#withValue() changed from no type to self
84 | [BC] CHANGED: The parameter $value of Dflydev\FigCookies\SetCookie#withValue() changed from no type to a non-contravariant ?string
85 | [BC] CHANGED: The parameter $value of Dflydev\FigCookies\SetCookie#withValue() changed from no type to ?string
86 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie#withExpires() changed from no type to self
87 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie#rememberForever() changed from no type to self
88 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie#expire() changed from no type to self
89 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie#withMaxAge() changed from no type to self
90 | [BC] CHANGED: The parameter $maxAge of Dflydev\FigCookies\SetCookie#withMaxAge() changed from no type to a non-contravariant ?int
91 | [BC] CHANGED: The parameter $maxAge of Dflydev\FigCookies\SetCookie#withMaxAge() changed from no type to ?int
92 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie#withPath() changed from no type to self
93 | [BC] CHANGED: The parameter $path of Dflydev\FigCookies\SetCookie#withPath() changed from no type to a non-contravariant ?string
94 | [BC] CHANGED: The parameter $path of Dflydev\FigCookies\SetCookie#withPath() changed from no type to ?string
95 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie#withDomain() changed from no type to self
96 | [BC] CHANGED: The parameter $domain of Dflydev\FigCookies\SetCookie#withDomain() changed from no type to a non-contravariant ?string
97 | [BC] CHANGED: The parameter $domain of Dflydev\FigCookies\SetCookie#withDomain() changed from no type to ?string
98 | [BC] CHANGED: Default parameter value for for parameter $secure of Dflydev\FigCookies\SetCookie#withSecure() changed from NULL to true
99 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie#withSecure() changed from no type to self
100 | [BC] CHANGED: The parameter $secure of Dflydev\FigCookies\SetCookie#withSecure() changed from no type to a non-contravariant bool
101 | [BC] CHANGED: The parameter $secure of Dflydev\FigCookies\SetCookie#withSecure() changed from no type to bool
102 | [BC] CHANGED: Default parameter value for for parameter $httpOnly of Dflydev\FigCookies\SetCookie#withHttpOnly() changed from NULL to true
103 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie#withHttpOnly() changed from no type to self
104 | [BC] CHANGED: The parameter $httpOnly of Dflydev\FigCookies\SetCookie#withHttpOnly() changed from no type to a non-contravariant bool
105 | [BC] CHANGED: The parameter $httpOnly of Dflydev\FigCookies\SetCookie#withHttpOnly() changed from no type to bool
106 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie#__toString() changed from no type to string
107 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie::create() changed from no type to self
108 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\SetCookie::create() changed from no type to a non-contravariant string
109 | [BC] CHANGED: The parameter $value of Dflydev\FigCookies\SetCookie::create() changed from no type to a non-contravariant ?string
110 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\SetCookie::create() changed from no type to string
111 | [BC] CHANGED: The parameter $value of Dflydev\FigCookies\SetCookie::create() changed from no type to ?string
112 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie::createRememberedForever() changed from no type to self
113 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\SetCookie::createRememberedForever() changed from no type to a non-contravariant string
114 | [BC] CHANGED: The parameter $value of Dflydev\FigCookies\SetCookie::createRememberedForever() changed from no type to a non-contravariant ?string
115 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\SetCookie::createRememberedForever() changed from no type to string
116 | [BC] CHANGED: The parameter $value of Dflydev\FigCookies\SetCookie::createRememberedForever() changed from no type to ?string
117 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie::createExpired() changed from no type to self
118 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\SetCookie::createExpired() changed from no type to a non-contravariant string
119 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\SetCookie::createExpired() changed from no type to string
120 | [BC] CHANGED: The return type of Dflydev\FigCookies\SetCookie::fromSetCookieString() changed from no type to self
121 | [BC] CHANGED: The parameter $string of Dflydev\FigCookies\SetCookie::fromSetCookieString() changed from no type to a non-contravariant string
122 | [BC] CHANGED: The parameter $string of Dflydev\FigCookies\SetCookie::fromSetCookieString() changed from no type to string
123 | [BC] CHANGED: The return type of Dflydev\FigCookies\FigResponseCookies::get() changed from no type to Dflydev\FigCookies\SetCookie
124 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\FigResponseCookies::get() changed from no type to a non-contravariant string
125 | [BC] CHANGED: The parameter $value of Dflydev\FigCookies\FigResponseCookies::get() changed from no type to a non-contravariant ?string
126 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\FigResponseCookies::get() changed from no type to string
127 | [BC] CHANGED: The parameter $value of Dflydev\FigCookies\FigResponseCookies::get() changed from no type to ?string
128 | [BC] CHANGED: The return type of Dflydev\FigCookies\FigResponseCookies::set() changed from no type to Psr\Http\Message\ResponseInterface
129 | [BC] CHANGED: The return type of Dflydev\FigCookies\FigResponseCookies::expire() changed from no type to Psr\Http\Message\ResponseInterface
130 | [BC] CHANGED: The parameter $cookieName of Dflydev\FigCookies\FigResponseCookies::expire() changed from no type to a non-contravariant string
131 | [BC] CHANGED: The parameter $cookieName of Dflydev\FigCookies\FigResponseCookies::expire() changed from no type to string
132 | [BC] CHANGED: The return type of Dflydev\FigCookies\FigResponseCookies::modify() changed from no type to Psr\Http\Message\ResponseInterface
133 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\FigResponseCookies::modify() changed from no type to a non-contravariant string
134 | [BC] CHANGED: The parameter $modify of Dflydev\FigCookies\FigResponseCookies::modify() changed from no type to a non-contravariant callable
135 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\FigResponseCookies::modify() changed from no type to string
136 | [BC] CHANGED: The parameter $modify of Dflydev\FigCookies\FigResponseCookies::modify() changed from no type to callable
137 | [BC] CHANGED: The return type of Dflydev\FigCookies\FigResponseCookies::remove() changed from no type to Psr\Http\Message\ResponseInterface
138 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\FigResponseCookies::remove() changed from no type to a non-contravariant string
139 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\FigResponseCookies::remove() changed from no type to string
140 | [BC] CHANGED: The return type of Dflydev\FigCookies\Cookies#has() changed from no type to bool
141 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\Cookies#has() changed from no type to a non-contravariant string
142 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\Cookies#has() changed from no type to string
143 | [BC] CHANGED: The return type of Dflydev\FigCookies\Cookies#get() changed from no type to ?Dflydev\FigCookies\Cookie
144 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\Cookies#get() changed from no type to a non-contravariant string
145 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\Cookies#get() changed from no type to string
146 | [BC] CHANGED: The return type of Dflydev\FigCookies\Cookies#getAll() changed from no type to array
147 | [BC] CHANGED: The return type of Dflydev\FigCookies\Cookies#with() changed from no type to Dflydev\FigCookies\Cookies
148 | [BC] CHANGED: The return type of Dflydev\FigCookies\Cookies#without() changed from no type to Dflydev\FigCookies\Cookies
149 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\Cookies#without() changed from no type to a non-contravariant string
150 | [BC] CHANGED: The parameter $name of Dflydev\FigCookies\Cookies#without() changed from no type to string
151 | [BC] CHANGED: The return type of Dflydev\FigCookies\Cookies#renderIntoCookieHeader() changed from no type to Psr\Http\Message\RequestInterface
152 | [BC] CHANGED: The return type of Dflydev\FigCookies\Cookies::fromCookieString() changed from no type to self
153 | [BC] CHANGED: The parameter $string of Dflydev\FigCookies\Cookies::fromCookieString() changed from no type to a non-contravariant string
154 | [BC] CHANGED: The parameter $string of Dflydev\FigCookies\Cookies::fromCookieString() changed from no type to string
155 | [BC] CHANGED: The return type of Dflydev\FigCookies\Cookies::fromRequest() changed from no type to Dflydev\FigCookies\Cookies
156 | ```
157 |
158 | - [#31](https://github.com/dflydev/dflydev-fig-cookies/pull/31) A new abstraction was
159 | introduced to support `SameSite=Lax` and `SameSite=Strict` `Set-Cookie` header modifiers.
160 |
161 | ### Deprecated
162 |
163 | - Nothing.
164 |
165 | ### Removed
166 |
167 | - Nothing.
168 |
169 | ### Fixed
170 |
171 | - [#32](https://github.com/dflydev/dflydev-fig-cookies/pull/32) `SetCookie#withExpires()`
172 | will now reject any expiry time that cannot be parsed into a timestamp.
173 | - [#32](https://github.com/dflydev/dflydev-fig-cookies/pull/32) A `SetCookie` can no longer
174 | be constructed via `Dflydev\FigCookies\SetCookie::fromSetCookieString('')`: an empty string
175 | will now be rejected.
176 |
--------------------------------------------------------------------------------