Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
namespace GuzzleHttp\Cookie;
4
 
5
use Psr\Http\Message\RequestInterface;
6
use Psr\Http\Message\ResponseInterface;
7
 
8
/**
9
 * Cookie jar that stores cookies as an array
10
 */
11
class CookieJar implements CookieJarInterface
12
{
13
    /**
14
     * @var SetCookie[] Loaded cookie data
15
     */
16
    private $cookies = [];
17
 
18
    /**
19
     * @var bool
20
     */
21
    private $strictMode;
22
 
23
    /**
24
     * @param bool  $strictMode  Set to true to throw exceptions when invalid
25
     *                           cookies are added to the cookie jar.
26
     * @param array $cookieArray Array of SetCookie objects or a hash of
27
     *                           arrays that can be used with the SetCookie
28
     *                           constructor
29
     */
30
    public function __construct(bool $strictMode = false, array $cookieArray = [])
31
    {
32
        $this->strictMode = $strictMode;
33
 
34
        foreach ($cookieArray as $cookie) {
35
            if (!($cookie instanceof SetCookie)) {
36
                $cookie = new SetCookie($cookie);
37
            }
38
            $this->setCookie($cookie);
39
        }
40
    }
41
 
42
    /**
43
     * Create a new Cookie jar from an associative array and domain.
44
     *
45
     * @param array  $cookies Cookies to create the jar from
46
     * @param string $domain  Domain to set the cookies to
47
     */
48
    public static function fromArray(array $cookies, string $domain): self
49
    {
50
        $cookieJar = new self();
51
        foreach ($cookies as $name => $value) {
52
            $cookieJar->setCookie(new SetCookie([
53
                'Domain'  => $domain,
54
                'Name'    => $name,
55
                'Value'   => $value,
56
                'Discard' => true
57
            ]));
58
        }
59
 
60
        return $cookieJar;
61
    }
62
 
63
    /**
64
     * Evaluate if this cookie should be persisted to storage
65
     * that survives between requests.
66
     *
67
     * @param SetCookie $cookie              Being evaluated.
68
     * @param bool      $allowSessionCookies If we should persist session cookies
69
     */
70
    public static function shouldPersist(SetCookie $cookie, bool $allowSessionCookies = false): bool
71
    {
72
        if ($cookie->getExpires() || $allowSessionCookies) {
73
            if (!$cookie->getDiscard()) {
74
                return true;
75
            }
76
        }
77
 
78
        return false;
79
    }
80
 
81
    /**
82
     * Finds and returns the cookie based on the name
83
     *
84
     * @param string $name cookie name to search for
85
     *
86
     * @return SetCookie|null cookie that was found or null if not found
87
     */
88
    public function getCookieByName(string $name): ?SetCookie
89
    {
90
        foreach ($this->cookies as $cookie) {
91
            if ($cookie->getName() !== null && \strcasecmp($cookie->getName(), $name) === 0) {
92
                return $cookie;
93
            }
94
        }
95
 
96
        return null;
97
    }
98
 
99
    /**
100
     * @inheritDoc
101
     */
102
    public function toArray(): array
103
    {
104
        return \array_map(static function (SetCookie $cookie): array {
105
            return $cookie->toArray();
106
        }, $this->getIterator()->getArrayCopy());
107
    }
108
 
109
    /**
110
     * @inheritDoc
111
     */
112
    public function clear(?string $domain = null, ?string $path = null, ?string $name = null): void
113
    {
114
        if (!$domain) {
115
            $this->cookies = [];
116
            return;
117
        } elseif (!$path) {
118
            $this->cookies = \array_filter(
119
                $this->cookies,
120
                static function (SetCookie $cookie) use ($domain): bool {
121
                    return !$cookie->matchesDomain($domain);
122
                }
123
            );
124
        } elseif (!$name) {
125
            $this->cookies = \array_filter(
126
                $this->cookies,
127
                static function (SetCookie $cookie) use ($path, $domain): bool {
128
                    return !($cookie->matchesPath($path) &&
129
                        $cookie->matchesDomain($domain));
130
                }
131
            );
132
        } else {
133
            $this->cookies = \array_filter(
134
                $this->cookies,
135
                static function (SetCookie $cookie) use ($path, $domain, $name) {
136
                    return !($cookie->getName() == $name &&
137
                        $cookie->matchesPath($path) &&
138
                        $cookie->matchesDomain($domain));
139
                }
140
            );
141
        }
142
    }
143
 
144
    /**
145
     * @inheritDoc
146
     */
147
    public function clearSessionCookies(): void
148
    {
149
        $this->cookies = \array_filter(
150
            $this->cookies,
151
            static function (SetCookie $cookie): bool {
152
                return !$cookie->getDiscard() && $cookie->getExpires();
153
            }
154
        );
155
    }
156
 
157
    /**
158
     * @inheritDoc
159
     */
160
    public function setCookie(SetCookie $cookie): bool
161
    {
162
        // If the name string is empty (but not 0), ignore the set-cookie
163
        // string entirely.
164
        $name = $cookie->getName();
165
        if (!$name && $name !== '0') {
166
            return false;
167
        }
168
 
169
        // Only allow cookies with set and valid domain, name, value
170
        $result = $cookie->validate();
171
        if ($result !== true) {
172
            if ($this->strictMode) {
173
                throw new \RuntimeException('Invalid cookie: ' . $result);
174
            }
175
            $this->removeCookieIfEmpty($cookie);
176
            return false;
177
        }
178
 
179
        // Resolve conflicts with previously set cookies
180
        foreach ($this->cookies as $i => $c) {
181
            // Two cookies are identical, when their path, and domain are
182
            // identical.
183
            if ($c->getPath() != $cookie->getPath() ||
184
                $c->getDomain() != $cookie->getDomain() ||
185
                $c->getName() != $cookie->getName()
186
            ) {
187
                continue;
188
            }
189
 
190
            // The previously set cookie is a discard cookie and this one is
191
            // not so allow the new cookie to be set
192
            if (!$cookie->getDiscard() && $c->getDiscard()) {
193
                unset($this->cookies[$i]);
194
                continue;
195
            }
196
 
197
            // If the new cookie's expiration is further into the future, then
198
            // replace the old cookie
199
            if ($cookie->getExpires() > $c->getExpires()) {
200
                unset($this->cookies[$i]);
201
                continue;
202
            }
203
 
204
            // If the value has changed, we better change it
205
            if ($cookie->getValue() !== $c->getValue()) {
206
                unset($this->cookies[$i]);
207
                continue;
208
            }
209
 
210
            // The cookie exists, so no need to continue
211
            return false;
212
        }
213
 
214
        $this->cookies[] = $cookie;
215
 
216
        return true;
217
    }
218
 
219
    public function count(): int
220
    {
221
        return \count($this->cookies);
222
    }
223
 
224
    /**
225
     * @return \ArrayIterator<int, SetCookie>
226
     */
227
    public function getIterator(): \ArrayIterator
228
    {
229
        return new \ArrayIterator(\array_values($this->cookies));
230
    }
231
 
232
    public function extractCookies(RequestInterface $request, ResponseInterface $response): void
233
    {
234
        if ($cookieHeader = $response->getHeader('Set-Cookie')) {
235
            foreach ($cookieHeader as $cookie) {
236
                $sc = SetCookie::fromString($cookie);
237
                if (!$sc->getDomain()) {
238
                    $sc->setDomain($request->getUri()->getHost());
239
                }
240
                if (0 !== \strpos($sc->getPath(), '/')) {
241
                    $sc->setPath($this->getCookiePathFromRequest($request));
242
                }
243
                if (!$sc->matchesDomain($request->getUri()->getHost())) {
244
                    continue;
245
                }
246
                // Note: At this point `$sc->getDomain()` being a public suffix should
247
                // be rejected, but we don't want to pull in the full PSL dependency.
248
                $this->setCookie($sc);
249
            }
250
        }
251
    }
252
 
253
    /**
254
     * Computes cookie path following RFC 6265 section 5.1.4
255
     *
256
     * @link https://tools.ietf.org/html/rfc6265#section-5.1.4
257
     */
258
    private function getCookiePathFromRequest(RequestInterface $request): string
259
    {
260
        $uriPath = $request->getUri()->getPath();
261
        if ('' === $uriPath) {
262
            return '/';
263
        }
264
        if (0 !== \strpos($uriPath, '/')) {
265
            return '/';
266
        }
267
        if ('/' === $uriPath) {
268
            return '/';
269
        }
270
        $lastSlashPos = \strrpos($uriPath, '/');
271
        if (0 === $lastSlashPos || false === $lastSlashPos) {
272
            return '/';
273
        }
274
 
275
        return \substr($uriPath, 0, $lastSlashPos);
276
    }
277
 
278
    public function withCookieHeader(RequestInterface $request): RequestInterface
279
    {
280
        $values = [];
281
        $uri = $request->getUri();
282
        $scheme = $uri->getScheme();
283
        $host = $uri->getHost();
284
        $path = $uri->getPath() ?: '/';
285
 
286
        foreach ($this->cookies as $cookie) {
287
            if ($cookie->matchesPath($path) &&
288
                $cookie->matchesDomain($host) &&
289
                !$cookie->isExpired() &&
290
                (!$cookie->getSecure() || $scheme === 'https')
291
            ) {
292
                $values[] = $cookie->getName() . '='
293
                    . $cookie->getValue();
294
            }
295
        }
296
 
297
        return $values
298
            ? $request->withHeader('Cookie', \implode('; ', $values))
299
            : $request;
300
    }
301
 
302
    /**
303
     * If a cookie already exists and the server asks to set it again with a
304
     * null value, the cookie must be deleted.
305
     */
306
    private function removeCookieIfEmpty(SetCookie $cookie): void
307
    {
308
        $cookieValue = $cookie->getValue();
309
        if ($cookieValue === null || $cookieValue === '') {
310
            $this->clear(
311
                $cookie->getDomain(),
312
                $cookie->getPath(),
313
                $cookie->getName()
314
            );
315
        }
316
    }
317
}