krz/vox-populi

A Tumblr web client.

clone: git clone https://gitbay.org/krz/vox-populi.git

main: vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php · raw

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