@@ -23,14 +23,14 @@ class Cookie
23
23
const SAMESITE_STRICT = 'strict ' ;
24
24
25
25
protected $ name ;
26
- protected $ value ;
27
- protected $ domain ;
28
- protected $ expire ;
29
- protected $ path ;
30
- protected $ secure ;
31
- protected $ httpOnly ;
32
-
33
- private $ raw ;
26
+ protected $ value = null ;
27
+ protected $ domain = null ;
28
+ protected $ expire = 0 ;
29
+ protected $ path = ' / ' ;
30
+ protected $ secure = null ;
31
+ protected $ httpOnly = true ;
32
+
33
+ private $ raw = false ;
34
34
private $ sameSite ;
35
35
private $ secureDefault = false ;
36
36
@@ -90,46 +90,17 @@ public static function create(string $name, string $value = null, $expire = 0, ?
90
90
*/
91
91
public function __construct (string $ name , string $ value = null , $ expire = 0 , ?string $ path = '/ ' , string $ domain = null , bool $ secure = null , bool $ httpOnly = true , bool $ raw = false , ?string $ sameSite = 'lax ' )
92
92
{
93
- // from PHP source code
94
- if ($ raw && false !== strpbrk ($ name , self ::$ reservedCharsList )) {
95
- throw new \InvalidArgumentException (sprintf ('The cookie name "%s" contains invalid characters. ' , $ name ));
96
- }
97
-
98
- if (empty ($ name )) {
99
- throw new \InvalidArgumentException ('The cookie name cannot be empty. ' );
100
- }
101
-
102
- // convert expiration time to a Unix timestamp
103
- if ($ expire instanceof \DateTimeInterface) {
104
- $ expire = $ expire ->format ('U ' );
105
- } elseif (!is_numeric ($ expire )) {
106
- $ expire = strtotime ($ expire );
107
-
108
- if (false === $ expire ) {
109
- throw new \InvalidArgumentException ('The cookie expiration time is not valid. ' );
110
- }
111
- }
93
+ $ this ->raw = $ raw ;
112
94
95
+ $ this ->validateName ($ name );
113
96
$ this ->name = $ name ;
114
97
$ this ->value = $ value ;
98
+ $ this ->expire = $ this ->normalizeExpiresTime ($ expire );
99
+ $ this ->path<
F438
/span> = $ this ->normalizePath ($ path );
115
100
$ this ->domain = $ domain ;
116
- $ this ->expire = 0 < $ expire ? (int ) $ expire : 0 ;
117
- $ this ->path = empty ($ path ) ? '/ ' : $ path ;
118
101
$ this ->secure = $ secure ;
119
102
$ this ->httpOnly = $ httpOnly ;
120
- $ this ->raw = $ raw ;
121
-
122
- if ('' === $ sameSite ) {
123
- $ sameSite = null ;
124
- } elseif (null !== $ sameSite ) {
125
- $ sameSite = strtolower ($ sameSite );
126
- }
127
-
128
- if (!\in_array ($ sameSite , [self ::SAMESITE_LAX , self ::SAMESITE_STRICT , self ::SAMESITE_NONE , null ], true )) {
129
- throw new \InvalidArgumentException ('The "sameSite" parameter value is not valid. ' );
130
- }
131
-
132
- $ this ->sameSite = $ sameSite ;
103
+ $ this ->sameSite = $ this ->normalizeSameSite ($ sameSite );
133
104
}
134
105
135
106
/**
@@ -190,6 +161,33 @@ public function getName()
190
161
return $ this ->name ;
191
162
}
192
163
164
+ /**
165
+ * Creates a cookie copy with a new name.
166
+ *
167
+ * @throws \InvalidArgumentException
168
+ */
169
+ public function withName (string $ name ): self
170
+ {
171
+ $ this ->validateName ($ name );
172
+
173
+ $ cookie = clone $ this ;
174
+ $ cookie ->name = $ name ;
175
+
176
+ return $ cookie ;
177
+ }
178
+
179
+ private function validateName (string $ name ): void
180
+ {
181
+ // from PHP source code
182
+ if ($ this ->isRaw () && false !== strpbrk ($ name , self ::$ reservedCharsList )) {
183
+ throw new \InvalidArgumentException (sprintf ('The cookie name "%s" contains invalid characters. ' , $ name ));
184
+ }
185
+
186
+ if (empty ($ name )) {
187
+ throw new \InvalidArgumentException ('The cookie name cannot be empty. ' );
188
+ }
189
+ }
190
+
193
191
/**
194
192
* Gets the value of the cookie.
195
193
*
@@ -200,6 +198,17 @@ public function getValue()
200
198
return $ this ->value ;
201
199
}
202
200
201
+ /**
202
+ * Creates a cookie copy with a new value.
203
+ */
204
+ public function withValue (string $ value = null ): self
205
+ {
206
+ $ cookie = clone $ this ;
207
+ $ cookie ->value = $ value ;
208
+
209
+ return $ cookie ;
210
+ }
211
+
203
212
/**
204
213
* Gets the domain that the cookie is available to.
205
214
*
@@ -210,6 +219,17 @@ public function getDomain()
210
219
return $ this ->domain ;
211
220
}
212
221
222
+ /**
223
+ * Creates a cookie copy with a new domain that the cookie is available to.
224
+ */
225
+ public function withDomain (string $ domain = null ): self
226
+ {
227
+ $ cookie = clone $ this ;
228
+ $ cookie ->domain = $ domain ;
229
+
230
+ return $ cookie ;
231
+ }
232
+
213
233
/**
214
234
* Gets the time the cookie expires.
215
235
*
@@ -220,6 +240,39 @@ public function getExpiresTime()
220
240
return $ this ->expire ;
221
241
}
222
242
243
+ /**
244
+ * Creates a cookie copy with a new time the cookie expires.
245
+ *
246
+ * @param int|string|\DateTimeInterface $expire
247
+ *
248
+ * @throws \InvalidArgumentException
249
+ */
250
+ public function withExpiresTime ($ expire = 0 ): self
251
+ {
252
+ $ expire = $ this ->normalizeExpiresTime ($ expire );
253
+
254
+ $ cookie = clone $ this ;
255
+ $ cookie ->expire = $ expire ;
256
+
257
+ return $ cookie ;
258
+ }
259
+
260
+ private function normalizeExpiresTime ($ expire = 0 ): int
261
+ {
262
+ // convert expiration time to a Unix timestamp
263
+ if ($ expire instanceof \DateTimeInterface) {
264
+ $ expire = $ expire ->format ('U ' );
265
+ } elseif (!is_numeric ($ expire )) {
266
+ $ expire = strtotime ($ expire );
267
+
268
+ if (false === $ expire ) {
269
+ throw new \InvalidArgumentException ('The cookie expiration time is not valid. ' );
270
+ }
271
+ }
272
+
273
+ return 0 < $ expire ? (int ) $ expire : 0 ;
274
+ }
275
+
223
276
/**
224
277
* Gets the max-age attribute.
225
278
*
@@ -242,6 +295,22 @@ public function getPath()
242
295
return $ this ->path ;
243
296
}
244
297
298
+ /**
299
+ * Creates a cookie copy with a new path on the server in which the cookie will be available on.
300
+ */
301
+ public function withPath (?string $ path ): self
302
+ {
303
+ $ cookie = clone $ this ;
304
+ $ cookie ->path = $ this ->normalizePath ($ path );
305
+
306
+ return $ cookie ;
307
+ }
308
+
309
+ private function normalizePath (?string $ path ): string
310
+ {
311
+ return empty ($ path ) ? '/ ' : $ path ;
312
+ }
313
+
245
314
/**
246
315
* Checks whether the cookie should only be transmitted over a secure HTTPS connection from the client.
247
316
*
@@ -252,6 +321,17 @@ public function isSecure()
252
321
return $ this ->secure ?? $ this ->secureDefault ;
253
322
}
254
323
324
+ /**
325
+ * Creates a cookie copy that only be transmitted over a secure HTTPS connection from the client.
326
+ */
327
+ public function withSecure (bool $ secure = null ): self
328
+ {
329
+ $ cookie = clone $ this ;
330
+ $ cookie ->secure = $ secure ;
331
+
332
+ return $ cookie ;
333
+ }
334
+
255
335
/**
256
336
* Checks whether the cookie will be made accessible only through the HTTP protocol.
257
337
*
@@ -262,6 +342,17 @@ public function isHttpOnly()
262
342
return $ this ->httpOnly ;
263
343
}
264
344
345
+ /**
346
+ * Creates a cookie copy that be accessible only through the HTTP protocol.
347
+ */
348
+ public function withHttpOnly (bool $ httpOnly = true ): self
349
+ {
350
+ $ cookie = clone $ this ;
351
+ $ cookie ->httpOnly = $ httpOnly ;
352
+
353
+ return $ cookie ;
354
+ }
355
+
265
356
/**
266
357
* Whether this cookie is about to be cleared.
267
358
*
@@ -282,6 +373,17 @@ public function isRaw()
282
373
return $ this ->raw ;
283
374
}
284
375
376
+ /**
377
+ * Creates a cookie copy that uses url encoding.
378
+ */
379
+ public function withRaw (bool $ raw = false ): self
380
+ {
381
+ $ cookie = clone $ this ;
382
+ $ cookie ->raw = $ raw ;
383
+
384
+ return $ cookie ;
385
+ }
386
+
285
387
/**
286
388
* Gets the SameSite attribute.
287
389
*
@@ -292,6 +394,36 @@ public function getSameSite()
292
394
return $ this ->sameSite ;
293
395
}
294
396
397
+ /**
398
+ * Creates a cookie copy with SameSite attribute.
399
+ *
400
+ * @throws \InvalidArgumentException
401
+ */
402
+ public function withSameSite (?string $ sameSite = 'lax ' ): self
403
+ {
404
+ $ sameSite = $ this ->normalizeSameSite ($ sameSite );
405
+
406
+ $ cookie = clone $ this ;
407
+ $ cookie ->sameSite = $ sameSite ;
408
+
409
+ return $ cookie ;
410
+ }
411
+
412
+ private function normalizeSameSite (?string $ sameSite = 'lax ' ): ?string
413
+ {
414
+ if ('' === $ sameSite ) {
415
+ $ sameSite = null ;
416
+ } elseif (null !== $ sameSite ) {
417
+ $ sameSite = strtolower ($ sameSite );
418
+ }
419
+
420
+ if (!\in_array ($ sameSite , [self ::SAMESITE_LAX , self ::SAMESITE_STRICT , self ::SAMESITE_NONE , null ], true )) {
421
+ throw new \InvalidArgumentException ('The "sameSite" parameter value is not valid. ' );
422
+ }
423
+
424
+ return $ sameSite ;
425
+ }
426
+
295
427
/**
296
428
* @param bool $default The default value of the "secure" flag when it is set to null
297
429
*/
0 commit comments