Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 * This file is part of the Symfony package.
|
Chris@0
|
5 *
|
Chris@0
|
6 * (c) Fabien Potencier <fabien@symfony.com>
|
Chris@0
|
7 *
|
Chris@0
|
8 * For the full copyright and license information, please view the LICENSE
|
Chris@0
|
9 * file that was distributed with this source code.
|
Chris@0
|
10 */
|
Chris@0
|
11
|
Chris@0
|
12 namespace Symfony\Component\BrowserKit;
|
Chris@0
|
13
|
Chris@0
|
14 /**
|
Chris@0
|
15 * CookieJar.
|
Chris@0
|
16 *
|
Chris@0
|
17 * @author Fabien Potencier <fabien@symfony.com>
|
Chris@0
|
18 */
|
Chris@0
|
19 class CookieJar
|
Chris@0
|
20 {
|
Chris@0
|
21 protected $cookieJar = array();
|
Chris@0
|
22
|
Chris@0
|
23 public function set(Cookie $cookie)
|
Chris@0
|
24 {
|
Chris@0
|
25 $this->cookieJar[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie;
|
Chris@0
|
26 }
|
Chris@0
|
27
|
Chris@0
|
28 /**
|
Chris@0
|
29 * Gets a cookie by name.
|
Chris@0
|
30 *
|
Chris@0
|
31 * You should never use an empty domain, but if you do so,
|
Chris@0
|
32 * this method returns the first cookie for the given name/path
|
Chris@0
|
33 * (this behavior ensures a BC behavior with previous versions of
|
Chris@0
|
34 * Symfony).
|
Chris@0
|
35 *
|
Chris@0
|
36 * @param string $name The cookie name
|
Chris@0
|
37 * @param string $path The cookie path
|
Chris@0
|
38 * @param string $domain The cookie domain
|
Chris@0
|
39 *
|
Chris@0
|
40 * @return Cookie|null A Cookie instance or null if the cookie does not exist
|
Chris@0
|
41 */
|
Chris@0
|
42 public function get($name, $path = '/', $domain = null)
|
Chris@0
|
43 {
|
Chris@0
|
44 $this->flushExpiredCookies();
|
Chris@0
|
45
|
Chris@0
|
46 if (!empty($domain)) {
|
Chris@0
|
47 foreach ($this->cookieJar as $cookieDomain => $pathCookies) {
|
Chris@0
|
48 if ($cookieDomain) {
|
Chris@0
|
49 $cookieDomain = '.'.ltrim($cookieDomain, '.');
|
Chris@0
|
50 if ($cookieDomain != substr('.'.$domain, -strlen($cookieDomain))) {
|
Chris@0
|
51 continue;
|
Chris@0
|
52 }
|
Chris@0
|
53 }
|
Chris@0
|
54
|
Chris@0
|
55 foreach ($pathCookies as $cookiePath => $namedCookies) {
|
Chris@0
|
56 if ($cookiePath != substr($path, 0, strlen($cookiePath))) {
|
Chris@0
|
57 continue;
|
Chris@0
|
58 }
|
Chris@0
|
59 if (isset($namedCookies[$name])) {
|
Chris@0
|
60 return $namedCookies[$name];
|
Chris@0
|
61 }
|
Chris@0
|
62 }
|
Chris@0
|
63 }
|
Chris@0
|
64
|
Chris@0
|
65 return;
|
Chris@0
|
66 }
|
Chris@0
|
67
|
Chris@0
|
68 // avoid relying on this behavior that is mainly here for BC reasons
|
Chris@0
|
69 foreach ($this->cookieJar as $cookies) {
|
Chris@0
|
70 if (isset($cookies[$path][$name])) {
|
Chris@0
|
71 return $cookies[$path][$name];
|
Chris@0
|
72 }
|
Chris@0
|
73 }
|
Chris@0
|
74 }
|
Chris@0
|
75
|
Chris@0
|
76 /**
|
Chris@0
|
77 * Removes a cookie by name.
|
Chris@0
|
78 *
|
Chris@0
|
79 * You should never use an empty domain, but if you do so,
|
Chris@0
|
80 * all cookies for the given name/path expire (this behavior
|
Chris@0
|
81 * ensures a BC behavior with previous versions of Symfony).
|
Chris@0
|
82 *
|
Chris@0
|
83 * @param string $name The cookie name
|
Chris@0
|
84 * @param string $path The cookie path
|
Chris@0
|
85 * @param string $domain The cookie domain
|
Chris@0
|
86 */
|
Chris@0
|
87 public function expire($name, $path = '/', $domain = null)
|
Chris@0
|
88 {
|
Chris@0
|
89 if (null === $path) {
|
Chris@0
|
90 $path = '/';
|
Chris@0
|
91 }
|
Chris@0
|
92
|
Chris@0
|
93 if (empty($domain)) {
|
Chris@0
|
94 // an empty domain means any domain
|
Chris@0
|
95 // this should never happen but it allows for a better BC
|
Chris@0
|
96 $domains = array_keys($this->cookieJar);
|
Chris@0
|
97 } else {
|
Chris@0
|
98 $domains = array($domain);
|
Chris@0
|
99 }
|
Chris@0
|
100
|
Chris@0
|
101 foreach ($domains as $domain) {
|
Chris@0
|
102 unset($this->cookieJar[$domain][$path][$name]);
|
Chris@0
|
103
|
Chris@0
|
104 if (empty($this->cookieJar[$domain][$path])) {
|
Chris@0
|
105 unset($this->cookieJar[$domain][$path]);
|
Chris@0
|
106
|
Chris@0
|
107 if (empty($this->cookieJar[$domain])) {
|
Chris@0
|
108 unset($this->cookieJar[$domain]);
|
Chris@0
|
109 }
|
Chris@0
|
110 }
|
Chris@0
|
111 }
|
Chris@0
|
112 }
|
Chris@0
|
113
|
Chris@0
|
114 /**
|
Chris@0
|
115 * Removes all the cookies from the jar.
|
Chris@0
|
116 */
|
Chris@0
|
117 public function clear()
|
Chris@0
|
118 {
|
Chris@0
|
119 $this->cookieJar = array();
|
Chris@0
|
120 }
|
Chris@0
|
121
|
Chris@0
|
122 /**
|
Chris@0
|
123 * Updates the cookie jar from a response Set-Cookie headers.
|
Chris@0
|
124 *
|
Chris@0
|
125 * @param array $setCookies Set-Cookie headers from an HTTP response
|
Chris@0
|
126 * @param string $uri The base URL
|
Chris@0
|
127 */
|
Chris@0
|
128 public function updateFromSetCookie(array $setCookies, $uri = null)
|
Chris@0
|
129 {
|
Chris@0
|
130 $cookies = array();
|
Chris@0
|
131
|
Chris@0
|
132 foreach ($setCookies as $cookie) {
|
Chris@0
|
133 foreach (explode(',', $cookie) as $i => $part) {
|
Chris@0
|
134 if (0 === $i || preg_match('/^(?P<token>\s*[0-9A-Za-z!#\$%\&\'\*\+\-\.^_`\|~]+)=/', $part)) {
|
Chris@0
|
135 $cookies[] = ltrim($part);
|
Chris@0
|
136 } else {
|
Chris@0
|
137 $cookies[count($cookies) - 1] .= ','.$part;
|
Chris@0
|
138 }
|
Chris@0
|
139 }
|
Chris@0
|
140 }
|
Chris@0
|
141
|
Chris@0
|
142 foreach ($cookies as $cookie) {
|
Chris@0
|
143 try {
|
Chris@0
|
144 $this->set(Cookie::fromString($cookie, $uri));
|
Chris@0
|
145 } catch (\InvalidArgumentException $e) {
|
Chris@0
|
146 // invalid cookies are just ignored
|
Chris@0
|
147 }
|
Chris@0
|
148 }
|
Chris@0
|
149 }
|
Chris@0
|
150
|
Chris@0
|
151 /**
|
Chris@0
|
152 * Updates the cookie jar from a Response object.
|
Chris@0
|
153 *
|
Chris@0
|
154 * @param Response $response A Response object
|
Chris@0
|
155 * @param string $uri The base URL
|
Chris@0
|
156 */
|
Chris@0
|
157 public function updateFromResponse(Response $response, $uri = null)
|
Chris@0
|
158 {
|
Chris@0
|
159 $this->updateFromSetCookie($response->getHeader('Set-Cookie', false), $uri);
|
Chris@0
|
160 }
|
Chris@0
|
161
|
Chris@0
|
162 /**
|
Chris@0
|
163 * Returns not yet expired cookies.
|
Chris@0
|
164 *
|
Chris@0
|
165 * @return Cookie[] An array of cookies
|
Chris@0
|
166 */
|
Chris@0
|
167 public function all()
|
Chris@0
|
168 {
|
Chris@0
|
169 $this->flushExpiredCookies();
|
Chris@0
|
170
|
Chris@0
|
171 $flattenedCookies = array();
|
Chris@0
|
172 foreach ($this->cookieJar as $path) {
|
Chris@0
|
173 foreach ($path as $cookies) {
|
Chris@0
|
174 foreach ($cookies as $cookie) {
|
Chris@0
|
175 $flattenedCookies[] = $cookie;
|
Chris@0
|
176 }
|
Chris@0
|
177 }
|
Chris@0
|
178 }
|
Chris@0
|
179
|
Chris@0
|
180 return $flattenedCookies;
|
Chris@0
|
181 }
|
Chris@0
|
182
|
Chris@0
|
183 /**
|
Chris@0
|
184 * Returns not yet expired cookie values for the given URI.
|
Chris@0
|
185 *
|
Chris@0
|
186 * @param string $uri A URI
|
Chris@0
|
187 * @param bool $returnsRawValue Returns raw value or urldecoded value
|
Chris@0
|
188 *
|
Chris@0
|
189 * @return array An array of cookie values
|
Chris@0
|
190 */
|
Chris@0
|
191 public function allValues($uri, $returnsRawValue = false)
|
Chris@0
|
192 {
|
Chris@0
|
193 $this->flushExpiredCookies();
|
Chris@0
|
194
|
Chris@0
|
195 $parts = array_replace(array('path' => '/'), parse_url($uri));
|
Chris@0
|
196 $cookies = array();
|
Chris@0
|
197 foreach ($this->cookieJar as $domain => $pathCookies) {
|
Chris@0
|
198 if ($domain) {
|
Chris@0
|
199 $domain = '.'.ltrim($domain, '.');
|
Chris@0
|
200 if ($domain != substr('.'.$parts['host'], -strlen($domain))) {
|
Chris@0
|
201 continue;
|
Chris@0
|
202 }
|
Chris@0
|
203 }
|
Chris@0
|
204
|
Chris@0
|
205 foreach ($pathCookies as $path => $namedCookies) {
|
Chris@0
|
206 if ($path != substr($parts['path'], 0, strlen($path))) {
|
Chris@0
|
207 continue;
|
Chris@0
|
208 }
|
Chris@0
|
209
|
Chris@0
|
210 foreach ($namedCookies as $cookie) {
|
Chris@0
|
211 if ($cookie->isSecure() && 'https' != $parts['scheme']) {
|
Chris@0
|
212 continue;
|
Chris@0
|
213 }
|
Chris@0
|
214
|
Chris@0
|
215 $cookies[$cookie->getName()] = $returnsRawValue ? $cookie->getRawValue() : $cookie->getValue();
|
Chris@0
|
216 }
|
Chris@0
|
217 }
|
Chris@0
|
218 }
|
Chris@0
|
219
|
Chris@0
|
220 return $cookies;
|
Chris@0
|
221 }
|
Chris@0
|
222
|
Chris@0
|
223 /**
|
Chris@0
|
224 * Returns not yet expired raw cookie values for the given URI.
|
Chris@0
|
225 *
|
Chris@0
|
226 * @param string $uri A URI
|
Chris@0
|
227 *
|
Chris@0
|
228 * @return array An array of cookie values
|
Chris@0
|
229 */
|
Chris@0
|
230 public function allRawValues($uri)
|
Chris@0
|
231 {
|
Chris@0
|
232 return $this->allValues($uri, true);
|
Chris@0
|
233 }
|
Chris@0
|
234
|
Chris@0
|
235 /**
|
Chris@0
|
236 * Removes all expired cookies.
|
Chris@0
|
237 */
|
Chris@0
|
238 public function flushExpiredCookies()
|
Chris@0
|
239 {
|
Chris@0
|
240 foreach ($this->cookieJar as $domain => $pathCookies) {
|
Chris@0
|
241 foreach ($pathCookies as $path => $namedCookies) {
|
Chris@0
|
242 foreach ($namedCookies as $name => $cookie) {
|
Chris@0
|
243 if ($cookie->isExpired()) {
|
Chris@0
|
244 unset($this->cookieJar[$domain][$path][$name]);
|
Chris@0
|
245 }
|
Chris@0
|
246 }
|
Chris@0
|
247 }
|
Chris@0
|
248 }
|
Chris@0
|
249 }
|
Chris@0
|
250 }
|