Subversion Repositories cheapmusic

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
103 - 1
<?php
2
namespace GuzzleHttp\Cookie;
3
 
4
/**
5
 * Set-Cookie object
6
 */
7
class SetCookie
8
{
9
    /** @var array */
10
    private static $defaults = [
11
        'Name'     => null,
12
        'Value'    => null,
13
        'Domain'   => null,
14
        'Path'     => '/',
15
        'Max-Age'  => null,
16
        'Expires'  => null,
17
        'Secure'   => false,
18
        'Discard'  => false,
19
        'HttpOnly' => false
20
    ];
21
 
22
    /** @var array Cookie data */
23
    private $data;
24
 
25
    /**
26
     * Create a new SetCookie object from a string
27
     *
28
     * @param string $cookie Set-Cookie header string
29
     *
30
     * @return self
31
     */
32
    public static function fromString($cookie)
33
    {
34
        // Create the default return array
35
        $data = self::$defaults;
36
        // Explode the cookie string using a series of semicolons
37
        $pieces = array_filter(array_map('trim', explode(';', $cookie)));
38
        // The name of the cookie (first kvp) must include an equal sign.
39
        if (empty($pieces) || !strpos($pieces[0], '=')) {
40
            return new self($data);
41
        }
42
 
43
        // Add the cookie pieces into the parsed data array
44
        foreach ($pieces as $part) {
45
 
46
            $cookieParts = explode('=', $part, 2);
47
            $key = trim($cookieParts[0]);
48
            $value = isset($cookieParts[1])
49
                ? trim($cookieParts[1], " \n\r\t\0\x0B")
50
                : true;
51
 
52
            // Only check for non-cookies when cookies have been found
53
            if (empty($data['Name'])) {
54
                $data['Name'] = $key;
55
                $data['Value'] = $value;
56
            } else {
57
                foreach (array_keys(self::$defaults) as $search) {
58
                    if (!strcasecmp($search, $key)) {
59
                        $data[$search] = $value;
60
                        continue 2;
61
                    }
62
                }
63
                $data[$key] = $value;
64
            }
65
        }
66
 
67
        return new self($data);
68
    }
69
 
70
    /**
71
     * @param array $data Array of cookie data provided by a Cookie parser
72
     */
73
    public function __construct(array $data = [])
74
    {
75
        $this->data = array_replace(self::$defaults, $data);
76
        // Extract the Expires value and turn it into a UNIX timestamp if needed
77
        if (!$this->getExpires() && $this->getMaxAge()) {
78
            // Calculate the Expires date
79
            $this->setExpires(time() + $this->getMaxAge());
80
        } elseif ($this->getExpires() && !is_numeric($this->getExpires())) {
81
            $this->setExpires($this->getExpires());
82
        }
83
    }
84
 
85
    public function __toString()
86
    {
87
        $str = $this->data['Name'] . '=' . $this->data['Value'] . '; ';
88
        foreach ($this->data as $k => $v) {
89
            if ($k !== 'Name' && $k !== 'Value' && $v !== null && $v !== false) {
90
                if ($k === 'Expires') {
91
                    $str .= 'Expires=' . gmdate('D, d M Y H:i:s \G\M\T', $v) . '; ';
92
                } else {
93
                    $str .= ($v === true ? $k : "{$k}={$v}") . '; ';
94
                }
95
            }
96
        }
97
 
98
        return rtrim($str, '; ');
99
    }
100
 
101
    public function toArray()
102
    {
103
        return $this->data;
104
    }
105
 
106
    /**
107
     * Get the cookie name
108
     *
109
     * @return string
110
     */
111
    public function getName()
112
    {
113
        return $this->data['Name'];
114
    }
115
 
116
    /**
117
     * Set the cookie name
118
     *
119
     * @param string $name Cookie name
120
     */
121
    public function setName($name)
122
    {
123
        $this->data['Name'] = $name;
124
    }
125
 
126
    /**
127
     * Get the cookie value
128
     *
129
     * @return string
130
     */
131
    public function getValue()
132
    {
133
        return $this->data['Value'];
134
    }
135
 
136
    /**
137
     * Set the cookie value
138
     *
139
     * @param string $value Cookie value
140
     */
141
    public function setValue($value)
142
    {
143
        $this->data['Value'] = $value;
144
    }
145
 
146
    /**
147
     * Get the domain
148
     *
149
     * @return string|null
150
     */
151
    public function getDomain()
152
    {
153
        return $this->data['Domain'];
154
    }
155
 
156
    /**
157
     * Set the domain of the cookie
158
     *
159
     * @param string $domain
160
     */
161
    public function setDomain($domain)
162
    {
163
        $this->data['Domain'] = $domain;
164
    }
165
 
166
    /**
167
     * Get the path
168
     *
169
     * @return string
170
     */
171
    public function getPath()
172
    {
173
        return $this->data['Path'];
174
    }
175
 
176
    /**
177
     * Set the path of the cookie
178
     *
179
     * @param string $path Path of the cookie
180
     */
181
    public function setPath($path)
182
    {
183
        $this->data['Path'] = $path;
184
    }
185
 
186
    /**
187
     * Maximum lifetime of the cookie in seconds
188
     *
189
     * @return int|null
190
     */
191
    public function getMaxAge()
192
    {
193
        return $this->data['Max-Age'];
194
    }
195
 
196
    /**
197
     * Set the max-age of the cookie
198
     *
199
     * @param int $maxAge Max age of the cookie in seconds
200
     */
201
    public function setMaxAge($maxAge)
202
    {
203
        $this->data['Max-Age'] = $maxAge;
204
    }
205
 
206
    /**
207
     * The UNIX timestamp when the cookie Expires
208
     *
209
     * @return mixed
210
     */
211
    public function getExpires()
212
    {
213
        return $this->data['Expires'];
214
    }
215
 
216
    /**
217
     * Set the unix timestamp for which the cookie will expire
218
     *
219
     * @param int $timestamp Unix timestamp
220
     */
221
    public function setExpires($timestamp)
222
    {
223
        $this->data['Expires'] = is_numeric($timestamp)
224
            ? (int) $timestamp
225
            : strtotime($timestamp);
226
    }
227
 
228
    /**
229
     * Get whether or not this is a secure cookie
230
     *
231
     * @return null|bool
232
     */
233
    public function getSecure()
234
    {
235
        return $this->data['Secure'];
236
    }
237
 
238
    /**
239
     * Set whether or not the cookie is secure
240
     *
241
     * @param bool $secure Set to true or false if secure
242
     */
243
    public function setSecure($secure)
244
    {
245
        $this->data['Secure'] = $secure;
246
    }
247
 
248
    /**
249
     * Get whether or not this is a session cookie
250
     *
251
     * @return null|bool
252
     */
253
    public function getDiscard()
254
    {
255
        return $this->data['Discard'];
256
    }
257
 
258
    /**
259
     * Set whether or not this is a session cookie
260
     *
261
     * @param bool $discard Set to true or false if this is a session cookie
262
     */
263
    public function setDiscard($discard)
264
    {
265
        $this->data['Discard'] = $discard;
266
    }
267
 
268
    /**
269
     * Get whether or not this is an HTTP only cookie
270
     *
271
     * @return bool
272
     */
273
    public function getHttpOnly()
274
    {
275
        return $this->data['HttpOnly'];
276
    }
277
 
278
    /**
279
     * Set whether or not this is an HTTP only cookie
280
     *
281
     * @param bool $httpOnly Set to true or false if this is HTTP only
282
     */
283
    public function setHttpOnly($httpOnly)
284
    {
285
        $this->data['HttpOnly'] = $httpOnly;
286
    }
287
 
288
    /**
289
     * Check if the cookie matches a path value.
290
     *
291
     * A request-path path-matches a given cookie-path if at least one of
292
     * the following conditions holds:
293
     *
294
     * - The cookie-path and the request-path are identical.
295
     * - The cookie-path is a prefix of the request-path, and the last
296
     *   character of the cookie-path is %x2F ("/").
297
     * - The cookie-path is a prefix of the request-path, and the first
298
     *   character of the request-path that is not included in the cookie-
299
     *   path is a %x2F ("/") character.
300
     *
301
     * @param string $requestPath Path to check against
302
     *
303
     * @return bool
304
     */
305
    public function matchesPath($requestPath)
306
    {
307
        $cookiePath = $this->getPath();
308
 
309
        // Match on exact matches or when path is the default empty "/"
310
        if ($cookiePath === '/' || $cookiePath == $requestPath) {
311
            return true;
312
        }
313
 
314
        // Ensure that the cookie-path is a prefix of the request path.
315
        if (0 !== strpos($requestPath, $cookiePath)) {
316
            return false;
317
        }
318
 
319
        // Match if the last character of the cookie-path is "/"
320
        if (substr($cookiePath, -1, 1) === '/') {
321
            return true;
322
        }
323
 
324
        // Match if the first character not included in cookie path is "/"
325
        return substr($requestPath, strlen($cookiePath), 1) === '/';
326
    }
327
 
328
    /**
329
     * Check if the cookie matches a domain value
330
     *
331
     * @param string $domain Domain to check against
332
     *
333
     * @return bool
334
     */
335
    public function matchesDomain($domain)
336
    {
337
        // Remove the leading '.' as per spec in RFC 6265.
338
        // http://tools.ietf.org/html/rfc6265#section-5.2.3
339
        $cookieDomain = ltrim($this->getDomain(), '.');
340
 
341
        // Domain not set or exact match.
342
        if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) {
343
            return true;
344
        }
345
 
346
        // Matching the subdomain according to RFC 6265.
347
        // http://tools.ietf.org/html/rfc6265#section-5.1.3
348
        if (filter_var($domain, FILTER_VALIDATE_IP)) {
349
            return false;
350
        }
351
 
352
        return (bool) preg_match('/\.' . preg_quote($cookieDomain) . '$/', $domain);
353
    }
354
 
355
    /**
356
     * Check if the cookie is expired
357
     *
358
     * @return bool
359
     */
360
    public function isExpired()
361
    {
362
        return $this->getExpires() && time() > $this->getExpires();
363
    }
364
 
365
    /**
366
     * Check if the cookie is valid according to RFC 6265
367
     *
368
     * @return bool|string Returns true if valid or an error message if invalid
369
     */
370
    public function validate()
371
    {
372
        // Names must not be empty, but can be 0
373
        $name = $this->getName();
374
        if (empty($name) && !is_numeric($name)) {
375
            return 'The cookie name must not be empty';
376
        }
377
 
378
        // Check if any of the invalid characters are present in the cookie name
379
        if (preg_match(
380
            '/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5c\x7b\x7d\x7f]/',
381
            $name)
382
        ) {
383
            return 'Cookie name must not contain invalid characters: ASCII '
384
                . 'Control characters (0-31;127), space, tab and the '
385
                . 'following characters: ()<>@,;:\"/?={}';
386
        }
387
 
388
        // Value must not be empty, but can be 0
389
        $value = $this->getValue();
390
        if (empty($value) && !is_numeric($value)) {
391
            return 'The cookie value must not be empty';
392
        }
393
 
394
        // Domains must not be empty, but can be 0
395
        // A "0" is not a valid internet domain, but may be used as server name
396
        // in a private network.
397
        $domain = $this->getDomain();
398
        if (empty($domain) && !is_numeric($domain)) {
399
            return 'The cookie domain must not be empty';
400
        }
401
 
402
        return true;
403
    }
404
}