aboutsummaryrefslogtreecommitdiffhomepage
path: root/assets/php/vendor/symfony/http-foundation
diff options
context:
space:
mode:
authormarvin-borner@live.com2018-04-10 21:50:16 +0200
committermarvin-borner@live.com2018-04-10 21:54:48 +0200
commitfc9401f04a3aca5abb22f87ebc210de8afe11d32 (patch)
treeb0b310f3581764ec3955f4e496a05137a32951c3 /assets/php/vendor/symfony/http-foundation
parent286d643180672f20526f3dc3bd19d7b751e2fa97 (diff)
Initial Commit
Diffstat (limited to 'assets/php/vendor/symfony/http-foundation')
-rw-r--r--assets/php/vendor/symfony/http-foundation/.gitignore3
-rw-r--r--assets/php/vendor/symfony/http-foundation/AcceptHeader.php168
-rw-r--r--assets/php/vendor/symfony/http-foundation/AcceptHeaderItem.php209
-rw-r--r--assets/php/vendor/symfony/http-foundation/ApacheRequest.php43
-rw-r--r--assets/php/vendor/symfony/http-foundation/BinaryFileResponse.php359
-rw-r--r--assets/php/vendor/symfony/http-foundation/CHANGELOG.md159
-rw-r--r--assets/php/vendor/symfony/http-foundation/Cookie.php289
-rw-r--r--assets/php/vendor/symfony/http-foundation/Exception/ConflictingHeadersException.php21
-rw-r--r--assets/php/vendor/symfony/http-foundation/Exception/RequestExceptionInterface.php21
-rw-r--r--assets/php/vendor/symfony/http-foundation/Exception/SuspiciousOperationException.php20
-rw-r--r--assets/php/vendor/symfony/http-foundation/ExpressionRequestMatcher.php47
-rw-r--r--assets/php/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php28
-rw-r--r--assets/php/vendor/symfony/http-foundation/File/Exception/FileException.php21
-rw-r--r--assets/php/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php28
-rw-r--r--assets/php/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php20
-rw-r--r--assets/php/vendor/symfony/http-foundation/File/Exception/UploadException.php21
-rw-r--r--assets/php/vendor/symfony/http-foundation/File/File.php136
-rw-r--r--assets/php/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php94
-rw-r--r--assets/php/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php27
-rw-r--r--assets/php/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php85
-rw-r--r--assets/php/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php69
-rw-r--r--assets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php808
-rw-r--r--assets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php142
-rw-r--r--assets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php35
-rw-r--r--assets/php/vendor/symfony/http-foundation/File/Stream.php28
-rw-r--r--assets/php/vendor/symfony/http-foundation/File/UploadedFile.php266
-rw-r--r--assets/php/vendor/symfony/http-foundation/FileBag.php144
-rw-r--r--assets/php/vendor/symfony/http-foundation/HeaderBag.php331
-rw-r--r--assets/php/vendor/symfony/http-foundation/IpUtils.php156
-rw-r--r--assets/php/vendor/symfony/http-foundation/JsonResponse.php220
-rw-r--r--assets/php/vendor/symfony/http-foundation/LICENSE19
-rw-r--r--assets/php/vendor/symfony/http-foundation/ParameterBag.php234
-rw-r--r--assets/php/vendor/symfony/http-foundation/README.md14
-rw-r--r--assets/php/vendor/symfony/http-foundation/RedirectResponse.php109
-rw-r--r--assets/php/vendor/symfony/http-foundation/Request.php2154
-rw-r--r--assets/php/vendor/symfony/http-foundation/RequestMatcher.php178
-rw-r--r--assets/php/vendor/symfony/http-foundation/RequestMatcherInterface.php27
-rw-r--r--assets/php/vendor/symfony/http-foundation/RequestStack.php103
-rw-r--r--assets/php/vendor/symfony/http-foundation/Response.php1298
-rw-r--r--assets/php/vendor/symfony/http-foundation/ResponseHeaderBag.php340
-rw-r--r--assets/php/vendor/symfony/http-foundation/ServerBag.php102
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php148
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php72
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php153
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php161
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Flash/FlashBag.php152
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php93
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Session.php273
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/SessionBagInterface.php46
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/SessionBagProxy.php82
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/SessionInterface.php180
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php168
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php120
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php124
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php255
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php55
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php24
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php76
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php910
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php103
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php92
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php168
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php256
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php152
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php445
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php59
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php122
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.php40
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php85
-rw-r--r--assets/php/vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php137
-rw-r--r--assets/php/vendor/symfony/http-foundation/StreamedResponse.php144
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php113
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php103
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/ApacheRequestTest.php93
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php352
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/CookieTest.php223
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php69
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/File/FakeFile.php45
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/File/FileTest.php180
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension1
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/directory/.empty0
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/other-file.example0
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/testbin0 -> 35 bytes
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/test.gifbin0 -> 35 bytes
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php90
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php273
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/FileBagTest.php175
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/HeaderBagTest.php205
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/IpUtilsTest.php104
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/JsonResponseTest.php257
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/ParameterBagTest.php194
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/RedirectResponseTest.php97
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/RequestMatcherTest.php151
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/RequestStackTest.php70
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/RequestTest.php2329
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php363
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/ResponseTest.php1003
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/ResponseTestCase.php89
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/ServerBagTest.php170
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php186
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php182
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php161
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php132
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/SessionTest.php242
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php61
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc151
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected17
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php8
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.expected14
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php8
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected24
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php10
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.expected20
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php24
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected15
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php8
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected24
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php13
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php135
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php139
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php333
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php77
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php38
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php59
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php411
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php189
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php97
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php139
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php131
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php127
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php277
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php96
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php113
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/NativeProxyTest.php38
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php124
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/StreamedResponseTest.php126
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng31
-rw-r--r--assets/php/vendor/symfony/http-foundation/Tests/schema/iana-registry.rng198
-rw-r--r--assets/php/vendor/symfony/http-foundation/composer.json38
-rw-r--r--assets/php/vendor/symfony/http-foundation/phpunit.xml.dist31
140 files changed, 24467 insertions, 0 deletions
diff --git a/assets/php/vendor/symfony/http-foundation/.gitignore b/assets/php/vendor/symfony/http-foundation/.gitignore
new file mode 100644
index 0000000..c49a5d8
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/.gitignore
@@ -0,0 +1,3 @@
+vendor/
+composer.lock
+phpunit.xml
diff --git a/assets/php/vendor/symfony/http-foundation/AcceptHeader.php b/assets/php/vendor/symfony/http-foundation/AcceptHeader.php
new file mode 100644
index 0000000..d174026
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/AcceptHeader.php
@@ -0,0 +1,168 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Represents an Accept-* header.
+ *
+ * An accept header is compound with a list of items,
+ * sorted by descending quality.
+ *
+ * @author Jean-François Simon <contact@jfsimon.fr>
+ */
+class AcceptHeader
+{
+ /**
+ * @var AcceptHeaderItem[]
+ */
+ private $items = array();
+
+ /**
+ * @var bool
+ */
+ private $sorted = true;
+
+ /**
+ * @param AcceptHeaderItem[] $items
+ */
+ public function __construct(array $items)
+ {
+ foreach ($items as $item) {
+ $this->add($item);
+ }
+ }
+
+ /**
+ * Builds an AcceptHeader instance from a string.
+ *
+ * @param string $headerValue
+ *
+ * @return self
+ */
+ public static function fromString($headerValue)
+ {
+ $index = 0;
+
+ return new self(array_map(function ($itemValue) use (&$index) {
+ $item = AcceptHeaderItem::fromString($itemValue);
+ $item->setIndex($index++);
+
+ return $item;
+ }, preg_split('/\s*(?:,*("[^"]+"),*|,*(\'[^\']+\'),*|,+)\s*/', $headerValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE)));
+ }
+
+ /**
+ * Returns header value's string representation.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return implode(',', $this->items);
+ }
+
+ /**
+ * Tests if header has given value.
+ *
+ * @param string $value
+ *
+ * @return bool
+ */
+ public function has($value)
+ {
+ return isset($this->items[$value]);
+ }
+
+ /**
+ * Returns given value's item, if exists.
+ *
+ * @param string $value
+ *
+ * @return AcceptHeaderItem|null
+ */
+ public function get($value)
+ {
+ return isset($this->items[$value]) ? $this->items[$value] : null;
+ }
+
+ /**
+ * Adds an item.
+ *
+ * @return $this
+ */
+ public function add(AcceptHeaderItem $item)
+ {
+ $this->items[$item->getValue()] = $item;
+ $this->sorted = false;
+
+ return $this;
+ }
+
+ /**
+ * Returns all items.
+ *
+ * @return AcceptHeaderItem[]
+ */
+ public function all()
+ {
+ $this->sort();
+
+ return $this->items;
+ }
+
+ /**
+ * Filters items on their value using given regex.
+ *
+ * @param string $pattern
+ *
+ * @return self
+ */
+ public function filter($pattern)
+ {
+ return new self(array_filter($this->items, function (AcceptHeaderItem $item) use ($pattern) {
+ return preg_match($pattern, $item->getValue());
+ }));
+ }
+
+ /**
+ * Returns first item.
+ *
+ * @return AcceptHeaderItem|null
+ */
+ public function first()
+ {
+ $this->sort();
+
+ return !empty($this->items) ? reset($this->items) : null;
+ }
+
+ /**
+ * Sorts items by descending quality.
+ */
+ private function sort()
+ {
+ if (!$this->sorted) {
+ uasort($this->items, function (AcceptHeaderItem $a, AcceptHeaderItem $b) {
+ $qA = $a->getQuality();
+ $qB = $b->getQuality();
+
+ if ($qA === $qB) {
+ return $a->getIndex() > $b->getIndex() ? 1 : -1;
+ }
+
+ return $qA > $qB ? -1 : 1;
+ });
+
+ $this->sorted = true;
+ }
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/AcceptHeaderItem.php b/assets/php/vendor/symfony/http-foundation/AcceptHeaderItem.php
new file mode 100644
index 0000000..c69dbbb
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/AcceptHeaderItem.php
@@ -0,0 +1,209 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Represents an Accept-* header item.
+ *
+ * @author Jean-François Simon <contact@jfsimon.fr>
+ */
+class AcceptHeaderItem
+{
+ private $value;
+ private $quality = 1.0;
+ private $index = 0;
+ private $attributes = array();
+
+ /**
+ * @param string $value
+ * @param array $attributes
+ */
+ public function __construct($value, array $attributes = array())
+ {
+ $this->value = $value;
+ foreach ($attributes as $name => $value) {
+ $this->setAttribute($name, $value);
+ }
+ }
+
+ /**
+ * Builds an AcceptHeaderInstance instance from a string.
+ *
+ * @param string $itemValue
+ *
+ * @return self
+ */
+ public static function fromString($itemValue)
+ {
+ $bits = preg_split('/\s*(?:;*("[^"]+");*|;*(\'[^\']+\');*|;+)\s*/', $itemValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
+ $value = array_shift($bits);
+ $attributes = array();
+
+ $lastNullAttribute = null;
+ foreach ($bits as $bit) {
+ if (($start = substr($bit, 0, 1)) === ($end = substr($bit, -1)) && ('"' === $start || '\'' === $start)) {
+ $attributes[$lastNullAttribute] = substr($bit, 1, -1);
+ } elseif ('=' === $end) {
+ $lastNullAttribute = $bit = substr($bit, 0, -1);
+ $attributes[$bit] = null;
+ } else {
+ $parts = explode('=', $bit);
+ $attributes[$parts[0]] = isset($parts[1]) && strlen($parts[1]) > 0 ? $parts[1] : '';
+ }
+ }
+
+ return new self(($start = substr($value, 0, 1)) === ($end = substr($value, -1)) && ('"' === $start || '\'' === $start) ? substr($value, 1, -1) : $value, $attributes);
+ }
+
+ /**
+ * Returns header value's string representation.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ $string = $this->value.($this->quality < 1 ? ';q='.$this->quality : '');
+ if (count($this->attributes) > 0) {
+ $string .= ';'.implode(';', array_map(function ($name, $value) {
+ return sprintf(preg_match('/[,;=]/', $value) ? '%s="%s"' : '%s=%s', $name, $value);
+ }, array_keys($this->attributes), $this->attributes));
+ }
+
+ return $string;
+ }
+
+ /**
+ * Set the item value.
+ *
+ * @param string $value
+ *
+ * @return $this
+ */
+ public function setValue($value)
+ {
+ $this->value = $value;
+
+ return $this;
+ }
+
+ /**
+ * Returns the item value.
+ *
+ * @return string
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Set the item quality.
+ *
+ * @param float $quality
+ *
+ * @return $this
+ */
+ public function setQuality($quality)
+ {
+ $this->quality = $quality;
+
+ return $this;
+ }
+
+ /**
+ * Returns the item quality.
+ *
+ * @return float
+ */
+ public function getQuality()
+ {
+ return $this->quality;
+ }
+
+ /**
+ * Set the item index.
+ *
+ * @param int $index
+ *
+ * @return $this
+ */
+ public function setIndex($index)
+ {
+ $this->index = $index;
+
+ return $this;
+ }
+
+ /**
+ * Returns the item index.
+ *
+ * @return int
+ */
+ public function getIndex()
+ {
+ return $this->index;
+ }
+
+ /**
+ * Tests if an attribute exists.
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public function hasAttribute($name)
+ {
+ return isset($this->attributes[$name]);
+ }
+
+ /**
+ * Returns an attribute by its name.
+ *
+ * @param string $name
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function getAttribute($name, $default = null)
+ {
+ return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
+ }
+
+ /**
+ * Returns all attributes.
+ *
+ * @return array
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * Set an attribute.
+ *
+ * @param string $name
+ * @param string $value
+ *
+ * @return $this
+ */
+ public function setAttribute($name, $value)
+ {
+ if ('q' === $name) {
+ $this->quality = (float) $value;
+ } else {
+ $this->attributes[$name] = (string) $value;
+ }
+
+ return $this;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/ApacheRequest.php b/assets/php/vendor/symfony/http-foundation/ApacheRequest.php
new file mode 100644
index 0000000..84803eb
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/ApacheRequest.php
@@ -0,0 +1,43 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Request represents an HTTP request from an Apache server.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ApacheRequest extends Request
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function prepareRequestUri()
+ {
+ return $this->server->get('REQUEST_URI');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function prepareBaseUrl()
+ {
+ $baseUrl = $this->server->get('SCRIPT_NAME');
+
+ if (false === strpos($this->server->get('REQUEST_URI'), $baseUrl)) {
+ // assume mod_rewrite
+ return rtrim(dirname($baseUrl), '/\\');
+ }
+
+ return $baseUrl;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/BinaryFileResponse.php b/assets/php/vendor/symfony/http-foundation/BinaryFileResponse.php
new file mode 100644
index 0000000..1010223
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/BinaryFileResponse.php
@@ -0,0 +1,359 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+use Symfony\Component\HttpFoundation\File\File;
+use Symfony\Component\HttpFoundation\File\Exception\FileException;
+
+/**
+ * BinaryFileResponse represents an HTTP response delivering a file.
+ *
+ * @author Niklas Fiekas <niklas.fiekas@tu-clausthal.de>
+ * @author stealth35 <stealth35-php@live.fr>
+ * @author Igor Wiedler <igor@wiedler.ch>
+ * @author Jordan Alliot <jordan.alliot@gmail.com>
+ * @author Sergey Linnik <linniksa@gmail.com>
+ */
+class BinaryFileResponse extends Response
+{
+ protected static $trustXSendfileTypeHeader = false;
+
+ /**
+ * @var File
+ */
+ protected $file;
+ protected $offset;
+ protected $maxlen;
+ protected $deleteFileAfterSend = false;
+
+ /**
+ * @param \SplFileInfo|string $file The file to stream
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ * @param bool $public Files are public by default
+ * @param null|string $contentDisposition The type of Content-Disposition to set automatically with the filename
+ * @param bool $autoEtag Whether the ETag header should be automatically set
+ * @param bool $autoLastModified Whether the Last-Modified header should be automatically set
+ */
+ public function __construct($file, $status = 200, $headers = array(), $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
+ {
+ parent::__construct(null, $status, $headers);
+
+ $this->setFile($file, $contentDisposition, $autoEtag, $autoLastModified);
+
+ if ($public) {
+ $this->setPublic();
+ }
+ }
+
+ /**
+ * @param \SplFileInfo|string $file The file to stream
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ * @param bool $public Files are public by default
+ * @param null|string $contentDisposition The type of Content-Disposition to set automatically with the filename
+ * @param bool $autoEtag Whether the ETag header should be automatically set
+ * @param bool $autoLastModified Whether the Last-Modified header should be automatically set
+ *
+ * @return static
+ */
+ public static function create($file = null, $status = 200, $headers = array(), $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
+ {
+ return new static($file, $status, $headers, $public, $contentDisposition, $autoEtag, $autoLastModified);
+ }
+
+ /**
+ * Sets the file to stream.
+ *
+ * @param \SplFileInfo|string $file The file to stream
+ * @param string $contentDisposition
+ * @param bool $autoEtag
+ * @param bool $autoLastModified
+ *
+ * @return $this
+ *
+ * @throws FileException
+ */
+ public function setFile($file, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
+ {
+ if (!$file instanceof File) {
+ if ($file instanceof \SplFileInfo) {
+ $file = new File($file->getPathname());
+ } else {
+ $file = new File((string) $file);
+ }
+ }
+
+ if (!$file->isReadable()) {
+ throw new FileException('File must be readable.');
+ }
+
+ $this->file = $file;
+
+ if ($autoEtag) {
+ $this->setAutoEtag();
+ }
+
+ if ($autoLastModified) {
+ $this->setAutoLastModified();
+ }
+
+ if ($contentDisposition) {
+ $this->setContentDisposition($contentDisposition);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Gets the file.
+ *
+ * @return File The file to stream
+ */
+ public function getFile()
+ {
+ return $this->file;
+ }
+
+ /**
+ * Automatically sets the Last-Modified header according the file modification date.
+ */
+ public function setAutoLastModified()
+ {
+ $this->setLastModified(\DateTime::createFromFormat('U', $this->file->getMTime()));
+
+ return $this;
+ }
+
+ /**
+ * Automatically sets the ETag header according to the checksum of the file.
+ */
+ public function setAutoEtag()
+ {
+ $this->setEtag(base64_encode(hash_file('sha256', $this->file->getPathname(), true)));
+
+ return $this;
+ }
+
+ /**
+ * Sets the Content-Disposition header with the given filename.
+ *
+ * @param string $disposition ResponseHeaderBag::DISPOSITION_INLINE or ResponseHeaderBag::DISPOSITION_ATTACHMENT
+ * @param string $filename Optionally use this UTF-8 encoded filename instead of the real name of the file
+ * @param string $filenameFallback A fallback filename, containing only ASCII characters. Defaults to an automatically encoded filename
+ *
+ * @return $this
+ */
+ public function setContentDisposition($disposition, $filename = '', $filenameFallback = '')
+ {
+ if ('' === $filename) {
+ $filename = $this->file->getFilename();
+ }
+
+ if ('' === $filenameFallback && (!preg_match('/^[\x20-\x7e]*$/', $filename) || false !== strpos($filename, '%'))) {
+ $encoding = mb_detect_encoding($filename, null, true) ?: '8bit';
+
+ for ($i = 0, $filenameLength = mb_strlen($filename, $encoding); $i < $filenameLength; ++$i) {
+ $char = mb_substr($filename, $i, 1, $encoding);
+
+ if ('%' === $char || ord($char) < 32 || ord($char) > 126) {
+ $filenameFallback .= '_';
+ } else {
+ $filenameFallback .= $char;
+ }
+ }
+ }
+
+ $dispositionHeader = $this->headers->makeDisposition($disposition, $filename, $filenameFallback);
+ $this->headers->set('Content-Disposition', $dispositionHeader);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function prepare(Request $request)
+ {
+ if (!$this->headers->has('Content-Type')) {
+ $this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream');
+ }
+
+ if ('HTTP/1.0' !== $request->server->get('SERVER_PROTOCOL')) {
+ $this->setProtocolVersion('1.1');
+ }
+
+ $this->ensureIEOverSSLCompatibility($request);
+
+ $this->offset = 0;
+ $this->maxlen = -1;
+
+ if (false === $fileSize = $this->file->getSize()) {
+ return $this;
+ }
+ $this->headers->set('Content-Length', $fileSize);
+
+ if (!$this->headers->has('Accept-Ranges')) {
+ // Only accept ranges on safe HTTP methods
+ $this->headers->set('Accept-Ranges', $request->isMethodSafe(false) ? 'bytes' : 'none');
+ }
+
+ if (self::$trustXSendfileTypeHeader && $request->headers->has('X-Sendfile-Type')) {
+ // Use X-Sendfile, do not send any content.
+ $type = $request->headers->get('X-Sendfile-Type');
+ $path = $this->file->getRealPath();
+ // Fall back to scheme://path for stream wrapped locations.
+ if (false === $path) {
+ $path = $this->file->getPathname();
+ }
+ if ('x-accel-redirect' === strtolower($type)) {
+ // Do X-Accel-Mapping substitutions.
+ // @link http://wiki.nginx.org/X-accel#X-Accel-Redirect
+ foreach (explode(',', $request->headers->get('X-Accel-Mapping', '')) as $mapping) {
+ $mapping = explode('=', $mapping, 2);
+
+ if (2 === count($mapping)) {
+ $pathPrefix = trim($mapping[0]);
+ $location = trim($mapping[1]);
+
+ if (substr($path, 0, strlen($pathPrefix)) === $pathPrefix) {
+ $path = $location.substr($path, strlen($pathPrefix));
+ break;
+ }
+ }
+ }
+ }
+ $this->headers->set($type, $path);
+ $this->maxlen = 0;
+ } elseif ($request->headers->has('Range')) {
+ // Process the range headers.
+ if (!$request->headers->has('If-Range') || $this->hasValidIfRangeHeader($request->headers->get('If-Range'))) {
+ $range = $request->headers->get('Range');
+
+ list($start, $end) = explode('-', substr($range, 6), 2) + array(0);
+
+ $end = ('' === $end) ? $fileSize - 1 : (int) $end;
+
+ if ('' === $start) {
+ $start = $fileSize - $end;
+ $end = $fileSize - 1;
+ } else {
+ $start = (int) $start;
+ }
+
+ if ($start <= $end) {
+ if ($start < 0 || $end > $fileSize - 1) {
+ $this->setStatusCode(416);
+ $this->headers->set('Content-Range', sprintf('bytes */%s', $fileSize));
+ } elseif (0 !== $start || $end !== $fileSize - 1) {
+ $this->maxlen = $end < $fileSize ? $end - $start + 1 : -1;
+ $this->offset = $start;
+
+ $this->setStatusCode(206);
+ $this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize));
+ $this->headers->set('Content-Length', $end - $start + 1);
+ }
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ private function hasValidIfRangeHeader($header)
+ {
+ if ($this->getEtag() === $header) {
+ return true;
+ }
+
+ if (null === $lastModified = $this->getLastModified()) {
+ return false;
+ }
+
+ return $lastModified->format('D, d M Y H:i:s').' GMT' === $header;
+ }
+
+ /**
+ * Sends the file.
+ *
+ * {@inheritdoc}
+ */
+ public function sendContent()
+ {
+ if (!$this->isSuccessful()) {
+ return parent::sendContent();
+ }
+
+ if (0 === $this->maxlen) {
+ return $this;
+ }
+
+ $out = fopen('php://output', 'wb');
+ $file = fopen($this->file->getPathname(), 'rb');
+
+ stream_copy_to_stream($file, $out, $this->maxlen, $this->offset);
+
+ fclose($out);
+ fclose($file);
+
+ if ($this->deleteFileAfterSend) {
+ unlink($this->file->getPathname());
+ }
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @throws \LogicException when the content is not null
+ */
+ public function setContent($content)
+ {
+ if (null !== $content) {
+ throw new \LogicException('The content cannot be set on a BinaryFileResponse instance.');
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return false
+ */
+ public function getContent()
+ {
+ return false;
+ }
+
+ /**
+ * Trust X-Sendfile-Type header.
+ */
+ public static function trustXSendfileTypeHeader()
+ {
+ self::$trustXSendfileTypeHeader = true;
+ }
+
+ /**
+ * If this is set to true, the file will be unlinked after the request is send
+ * Note: If the X-Sendfile header is used, the deleteFileAfterSend setting will not be used.
+ *
+ * @param bool $shouldDelete
+ *
+ * @return $this
+ */
+ public function deleteFileAfterSend($shouldDelete)
+ {
+ $this->deleteFileAfterSend = $shouldDelete;
+
+ return $this;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/CHANGELOG.md b/assets/php/vendor/symfony/http-foundation/CHANGELOG.md
new file mode 100644
index 0000000..ee5b6ce
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/CHANGELOG.md
@@ -0,0 +1,159 @@
+CHANGELOG
+=========
+
+3.4.0
+-----
+
+ * implemented PHP 7.0's `SessionUpdateTimestampHandlerInterface` with a new
+ `AbstractSessionHandler` base class and a new `StrictSessionHandler` wrapper
+ * deprecated the `WriteCheckSessionHandler`, `NativeSessionHandler` and `NativeProxy` classes
+ * deprecated setting session save handlers that do not implement `\SessionHandlerInterface` in `NativeSessionStorage::setSaveHandler()`
+ * deprecated using `MongoDbSessionHandler` with the legacy mongo extension; use it with the mongodb/mongodb package and ext-mongodb instead
+ * deprecated `MemcacheSessionHandler`; use `MemcachedSessionHandler` instead
+
+3.3.0
+-----
+
+ * the `Request::setTrustedProxies()` method takes a new `$trustedHeaderSet` argument,
+ see http://symfony.com/doc/current/components/http_foundation/trusting_proxies.html for more info,
+ * deprecated the `Request::setTrustedHeaderName()` and `Request::getTrustedHeaderName()` methods,
+ * added `File\Stream`, to be passed to `BinaryFileResponse` when the size of the served file is unknown,
+ disabling `Range` and `Content-Length` handling, switching to chunked encoding instead
+ * added the `Cookie::fromString()` method that allows to create a cookie from a
+ raw header string
+
+3.1.0
+-----
+
+ * Added support for creating `JsonResponse` with a string of JSON data
+
+3.0.0
+-----
+
+ * The precedence of parameters returned from `Request::get()` changed from "GET, PATH, BODY" to "PATH, GET, BODY"
+
+2.8.0
+-----
+
+ * Finding deep items in `ParameterBag::get()` is deprecated since version 2.8 and
+ will be removed in 3.0.
+
+2.6.0
+-----
+
+ * PdoSessionHandler changes
+ - implemented different session locking strategies to prevent loss of data by concurrent access to the same session
+ - [BC BREAK] save session data in a binary column without base64_encode
+ - [BC BREAK] added lifetime column to the session table which allows to have different lifetimes for each session
+ - implemented lazy connections that are only opened when a session is used by either passing a dsn string
+ explicitly or falling back to session.save_path ini setting
+ - added a createTable method that initializes a correctly defined table depending on the database vendor
+
+2.5.0
+-----
+
+ * added `JsonResponse::setEncodingOptions()` & `JsonResponse::getEncodingOptions()` for easier manipulation
+ of the options used while encoding data to JSON format.
+
+2.4.0
+-----
+
+ * added RequestStack
+ * added Request::getEncodings()
+ * added accessors methods to session handlers
+
+2.3.0
+-----
+
+ * added support for ranges of IPs in trusted proxies
+ * `UploadedFile::isValid` now returns false if the file was not uploaded via HTTP (in a non-test mode)
+ * Improved error-handling of `\Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler`
+ to ensure the supplied PDO handler throws Exceptions on error (as the class expects). Added related test cases
+ to verify that Exceptions are properly thrown when the PDO queries fail.
+
+2.2.0
+-----
+
+ * fixed the Request::create() precedence (URI information always take precedence now)
+ * added Request::getTrustedProxies()
+ * deprecated Request::isProxyTrusted()
+ * [BC BREAK] JsonResponse does not turn a top level empty array to an object anymore, use an ArrayObject to enforce objects
+ * added a IpUtils class to check if an IP belongs to a CIDR
+ * added Request::getRealMethod() to get the "real" HTTP method (getMethod() returns the "intended" HTTP method)
+ * disabled _method request parameter support by default (call Request::enableHttpMethodParameterOverride() to
+ enable it, and Request::getHttpMethodParameterOverride() to check if it is supported)
+ * Request::splitHttpAcceptHeader() method is deprecated and will be removed in 2.3
+ * Deprecated Flashbag::count() and \Countable interface, will be removed in 2.3
+
+2.1.0
+-----
+
+ * added Request::getSchemeAndHttpHost() and Request::getUserInfo()
+ * added a fluent interface to the Response class
+ * added Request::isProxyTrusted()
+ * added JsonResponse
+ * added a getTargetUrl method to RedirectResponse
+ * added support for streamed responses
+ * made Response::prepare() method the place to enforce HTTP specification
+ * [BC BREAK] moved management of the locale from the Session class to the Request class
+ * added a generic access to the PHP built-in filter mechanism: ParameterBag::filter()
+ * made FileBinaryMimeTypeGuesser command configurable
+ * added Request::getUser() and Request::getPassword()
+ * added support for the PATCH method in Request
+ * removed the ContentTypeMimeTypeGuesser class as it is deprecated and never used on PHP 5.3
+ * added ResponseHeaderBag::makeDisposition() (implements RFC 6266)
+ * made mimetype to extension conversion configurable
+ * [BC BREAK] Moved all session related classes and interfaces into own namespace, as
+ `Symfony\Component\HttpFoundation\Session` and renamed classes accordingly.
+ Session handlers are located in the subnamespace `Symfony\Component\HttpFoundation\Session\Handler`.
+ * SessionHandlers must implement `\SessionHandlerInterface` or extend from the
+ `Symfony\Component\HttpFoundation\Storage\Handler\NativeSessionHandler` base class.
+ * Added internal storage driver proxy mechanism for forward compatibility with
+ PHP 5.4 `\SessionHandler` class.
+ * Added session handlers for custom Memcache, Memcached and Null session save handlers.
+ * [BC BREAK] Removed `NativeSessionStorage` and replaced with `NativeFileSessionHandler`.
+ * [BC BREAK] `SessionStorageInterface` methods removed: `write()`, `read()` and
+ `remove()`. Added `getBag()`, `registerBag()`. The `NativeSessionStorage` class
+ is a mediator for the session storage internals including the session handlers
+ which do the real work of participating in the internal PHP session workflow.
+ * [BC BREAK] Introduced mock implementations of `SessionStorage` to enable unit
+ and functional testing without starting real PHP sessions. Removed
+ `ArraySessionStorage`, and replaced with `MockArraySessionStorage` for unit
+ tests; removed `FilesystemSessionStorage`, and replaced with`MockFileSessionStorage`
+ for functional tests. These do not interact with global session ini
+ configuration values, session functions or `$_SESSION` superglobal. This means
+ they can be configured directly allowing multiple instances to work without
+ conflicting in the same PHP process.
+ * [BC BREAK] Removed the `close()` method from the `Session` class, as this is
+ now redundant.
+ * Deprecated the following methods from the Session class: `setFlash()`, `setFlashes()`
+ `getFlash()`, `hasFlash()`, and `removeFlash()`. Use `getFlashBag()` instead
+ which returns a `FlashBagInterface`.
+ * `Session->clear()` now only clears session attributes as before it cleared
+ flash messages and attributes. `Session->getFlashBag()->all()` clears flashes now.
+ * Session data is now managed by `SessionBagInterface` to better encapsulate
+ session data.
+ * Refactored session attribute and flash messages system to their own
+ `SessionBagInterface` implementations.
+ * Added `FlashBag`. Flashes expire when retrieved by `get()` or `all()`. This
+ implementation is ESI compatible.
+ * Added `AutoExpireFlashBag` (default) to replicate Symfony 2.0.x auto expire
+ behaviour of messages auto expiring after one page page load. Messages must
+ be retrieved by `get()` or `all()`.
+ * Added `Symfony\Component\HttpFoundation\Attribute\AttributeBag` to replicate
+ attributes storage behaviour from 2.0.x (default).
+ * Added `Symfony\Component\HttpFoundation\Attribute\NamespacedAttributeBag` for
+ namespace session attributes.
+ * Flash API can stores messages in an array so there may be multiple messages
+ per flash type. The old `Session` class API remains without BC break as it
+ will allow single messages as before.
+ * Added basic session meta-data to the session to record session create time,
+ last updated time, and the lifetime of the session cookie that was provided
+ to the client.
+ * Request::getClientIp() method doesn't take a parameter anymore but bases
+ itself on the trustProxy parameter.
+ * Added isMethod() to Request object.
+ * [BC BREAK] The methods `getPathInfo()`, `getBaseUrl()` and `getBasePath()` of
+ a `Request` now all return a raw value (vs a urldecoded value before). Any call
+ to one of these methods must be checked and wrapped in a `rawurldecode()` if
+ needed.
diff --git a/assets/php/vendor/symfony/http-foundation/Cookie.php b/assets/php/vendor/symfony/http-foundation/Cookie.php
new file mode 100644
index 0000000..4519a6a
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Cookie.php
@@ -0,0 +1,289 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Represents a cookie.
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+class Cookie
+{
+ protected $name;
+ protected $value;
+ protected $domain;
+ protected $expire;
+ protected $path;
+ protected $secure;
+ protected $httpOnly;
+ private $raw;
+ private $sameSite;
+
+ const SAMESITE_LAX = 'lax';
+ const SAMESITE_STRICT = 'strict';
+
+ /**
+ * Creates cookie from raw header string.
+ *
+ * @param string $cookie
+ * @param bool $decode
+ *
+ * @return static
+ */
+ public static function fromString($cookie, $decode = false)
+ {
+ $data = array(
+ 'expires' => 0,
+ 'path' => '/',
+ 'domain' => null,
+ 'secure' => false,
+ 'httponly' => false,
+ 'raw' => !$decode,
+ 'samesite' => null,
+ );
+ foreach (explode(';', $cookie) as $part) {
+ if (false === strpos($part, '=')) {
+ $key = trim($part);
+ $value = true;
+ } else {
+ list($key, $value) = explode('=', trim($part), 2);
+ $key = trim($key);
+ $value = trim($value);
+ }
+ if (!isset($data['name'])) {
+ $data['name'] = $decode ? urldecode($key) : $key;
+ $data['value'] = true === $value ? null : ($decode ? urldecode($value) : $value);
+ continue;
+ }
+ switch ($key = strtolower($key)) {
+ case 'name':
+ case 'value':
+ break;
+ case 'max-age':
+ $data['expires'] = time() + (int) $value;
+ break;
+ default:
+ $data[$key] = $value;
+ break;
+ }
+ }
+
+ return new static($data['name'], $data['value'], $data['expires'], $data['path'], $data['domain'], $data['secure'], $data['httponly'], $data['raw'], $data['samesite']);
+ }
+
+ /**
+ * @param string $name The name of the cookie
+ * @param string|null $value The value of the cookie
+ * @param int|string|\DateTimeInterface $expire The time the cookie expires
+ * @param string $path The path on the server in which the cookie will be available on
+ * @param string|null $domain The domain that the cookie is available to
+ * @param bool $secure Whether the cookie should only be transmitted over a secure HTTPS connection from the client
+ * @param bool $httpOnly Whether the cookie will be made accessible only through the HTTP protocol
+ * @param bool $raw Whether the cookie value should be sent with no url encoding
+ * @param string|null $sameSite Whether the cookie will be available for cross-site requests
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function __construct($name, $value = null, $expire = 0, $path = '/', $domain = null, $secure = false, $httpOnly = true, $raw = false, $sameSite = null)
+ {
+ // from PHP source code
+ if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
+ throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name));
+ }
+
+ if (empty($name)) {
+ throw new \InvalidArgumentException('The cookie name cannot be empty.');
+ }
+
+ // convert expiration time to a Unix timestamp
+ if ($expire instanceof \DateTimeInterface) {
+ $expire = $expire->format('U');
+ } elseif (!is_numeric($expire)) {
+ $expire = strtotime($expire);
+
+ if (false === $expire) {
+ throw new \InvalidArgumentException('The cookie expiration time is not valid.');
+ }
+ }
+
+ $this->name = $name;
+ $this->value = $value;
+ $this->domain = $domain;
+ $this->expire = 0 < $expire ? (int) $expire : 0;
+ $this->path = empty($path) ? '/' : $path;
+ $this->secure = (bool) $secure;
+ $this->httpOnly = (bool) $httpOnly;
+ $this->raw = (bool) $raw;
+
+ if (null !== $sameSite) {
+ $sameSite = strtolower($sameSite);
+ }
+
+ if (!in_array($sameSite, array(self::SAMESITE_LAX, self::SAMESITE_STRICT, null), true)) {
+ throw new \InvalidArgumentException('The "sameSite" parameter value is not valid.');
+ }
+
+ $this->sameSite = $sameSite;
+ }
+
+ /**
+ * Returns the cookie as a string.
+ *
+ * @return string The cookie
+ */
+ public function __toString()
+ {
+ $str = ($this->isRaw() ? $this->getName() : urlencode($this->getName())).'=';
+
+ if ('' === (string) $this->getValue()) {
+ $str .= 'deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; max-age=-31536001';
+ } else {
+ $str .= $this->isRaw() ? $this->getValue() : rawurlencode($this->getValue());
+
+ if (0 !== $this->getExpiresTime()) {
+ $str .= '; expires='.gmdate('D, d-M-Y H:i:s T', $this->getExpiresTime()).'; max-age='.$this->getMaxAge();
+ }
+ }
+
+ if ($this->getPath()) {
+ $str .= '; path='.$this->getPath();
+ }
+
+ if ($this->getDomain()) {
+ $str .= '; domain='.$this->getDomain();
+ }
+
+ if (true === $this->isSecure()) {
+ $str .= '; secure';
+ }
+
+ if (true === $this->isHttpOnly()) {
+ $str .= '; httponly';
+ }
+
+ if (null !== $this->getSameSite()) {
+ $str .= '; samesite='.$this->getSameSite();
+ }
+
+ return $str;
+ }
+
+ /**
+ * Gets the name of the cookie.
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Gets the value of the cookie.
+ *
+ * @return string|null
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Gets the domain that the cookie is available to.
+ *
+ * @return string|null
+ */
+ public function getDomain()
+ {
+ return $this->domain;
+ }
+
+ /**
+ * Gets the time the cookie expires.
+ *
+ * @return int
+ */
+ public function getExpiresTime()
+ {
+ return $this->expire;
+ }
+
+ /**
+ * Gets the max-age attribute.
+ *
+ * @return int
+ */
+ public function getMaxAge()
+ {
+ return 0 !== $this->expire ? $this->expire - time() : 0;
+ }
+
+ /**
+ * Gets the path on the server in which the cookie will be available on.
+ *
+ * @return string
+ */
+ public function getPath()
+ {
+ return $this->path;
+ }
+
+ /**
+ * Checks whether the cookie should only be transmitted over a secure HTTPS connection from the client.
+ *
+ * @return bool
+ */
+ public function isSecure()
+ {
+ return $this->secure;
+ }
+
+ /**
+ * Checks whether the cookie will be made accessible only through the HTTP protocol.
+ *
+ * @return bool
+ */
+ public function isHttpOnly()
+ {
+ return $this->httpOnly;
+ }
+
+ /**
+ * Whether this cookie is about to be cleared.
+ *
+ * @return bool
+ */
+ public function isCleared()
+ {
+ return $this->expire < time();
+ }
+
+ /**
+ * Checks if the cookie value should be sent with no url encoding.
+ *
+ * @return bool
+ */
+ public function isRaw()
+ {
+ return $this->raw;
+ }
+
+ /**
+ * Gets the SameSite attribute.
+ *
+ * @return string|null
+ */
+ public function getSameSite()
+ {
+ return $this->sameSite;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Exception/ConflictingHeadersException.php b/assets/php/vendor/symfony/http-foundation/Exception/ConflictingHeadersException.php
new file mode 100644
index 0000000..5fcf5b4
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Exception/ConflictingHeadersException.php
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Exception;
+
+/**
+ * The HTTP request contains headers with conflicting information.
+ *
+ * @author Magnus Nordlander <magnus@fervo.se>
+ */
+class ConflictingHeadersException extends \UnexpectedValueException implements RequestExceptionInterface
+{
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Exception/RequestExceptionInterface.php b/assets/php/vendor/symfony/http-foundation/Exception/RequestExceptionInterface.php
new file mode 100644
index 0000000..478d0dc
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Exception/RequestExceptionInterface.php
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Exception;
+
+/**
+ * Interface for Request exceptions.
+ *
+ * Exceptions implementing this interface should trigger an HTTP 400 response in the application code.
+ */
+interface RequestExceptionInterface
+{
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Exception/SuspiciousOperationException.php b/assets/php/vendor/symfony/http-foundation/Exception/SuspiciousOperationException.php
new file mode 100644
index 0000000..ae7a5f1
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Exception/SuspiciousOperationException.php
@@ -0,0 +1,20 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Exception;
+
+/**
+ * Raised when a user has performed an operation that should be considered
+ * suspicious from a security perspective.
+ */
+class SuspiciousOperationException extends \UnexpectedValueException implements RequestExceptionInterface
+{
+}
diff --git a/assets/php/vendor/symfony/http-foundation/ExpressionRequestMatcher.php b/assets/php/vendor/symfony/http-foundation/ExpressionRequestMatcher.php
new file mode 100644
index 0000000..e9c8441
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/ExpressionRequestMatcher.php
@@ -0,0 +1,47 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
+
+/**
+ * ExpressionRequestMatcher uses an expression to match a Request.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ExpressionRequestMatcher extends RequestMatcher
+{
+ private $language;
+ private $expression;
+
+ public function setExpression(ExpressionLanguage $language, $expression)
+ {
+ $this->language = $language;
+ $this->expression = $expression;
+ }
+
+ public function matches(Request $request)
+ {
+ if (!$this->language) {
+ throw new \LogicException('Unable to match the request as the expression language is not available.');
+ }
+
+ return $this->language->evaluate($this->expression, array(
+ 'request' => $request,
+ 'method' => $request->getMethod(),
+ 'path' => rawurldecode($request->getPathInfo()),
+ 'host' => $request->getHost(),
+ 'ip' => $request->getClientIp(),
+ 'attributes' => $request->attributes->all(),
+ )) && parent::matches($request);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php b/assets/php/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php
new file mode 100644
index 0000000..3b8e41d
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\Exception;
+
+/**
+ * Thrown when the access on a file was denied.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class AccessDeniedException extends FileException
+{
+ /**
+ * @param string $path The path to the accessed file
+ */
+ public function __construct($path)
+ {
+ parent::__construct(sprintf('The file %s could not be accessed', $path));
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/File/Exception/FileException.php b/assets/php/vendor/symfony/http-foundation/File/Exception/FileException.php
new file mode 100644
index 0000000..fad5133
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/File/Exception/FileException.php
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\Exception;
+
+/**
+ * Thrown when an error occurred in the component File.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class FileException extends \RuntimeException
+{
+}
diff --git a/assets/php/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php b/assets/php/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php
new file mode 100644
index 0000000..bfcc37e
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\Exception;
+
+/**
+ * Thrown when a file was not found.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class FileNotFoundException extends FileException
+{
+ /**
+ * @param string $path The path to the file that was not found
+ */
+ public function __construct($path)
+ {
+ parent::__construct(sprintf('The file "%s" does not exist', $path));
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php b/assets/php/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php
new file mode 100644
index 0000000..0444b87
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php
@@ -0,0 +1,20 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\Exception;
+
+class UnexpectedTypeException extends FileException
+{
+ public function __construct($value, $expectedType)
+ {
+ parent::__construct(sprintf('Expected argument of type %s, %s given', $expectedType, is_object($value) ? get_class($value) : gettype($value)));
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/File/Exception/UploadException.php b/assets/php/vendor/symfony/http-foundation/File/Exception/UploadException.php
new file mode 100644
index 0000000..7074e76
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/File/Exception/UploadException.php
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\Exception;
+
+/**
+ * Thrown when an error occurred during file upload.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class UploadException extends FileException
+{
+}
diff --git a/assets/php/vendor/symfony/http-foundation/File/File.php b/assets/php/vendor/symfony/http-foundation/File/File.php
new file mode 100644
index 0000000..e2a6768
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/File/File.php
@@ -0,0 +1,136 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileException;
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
+use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser;
+
+/**
+ * A file in the file system.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class File extends \SplFileInfo
+{
+ /**
+ * Constructs a new file from the given path.
+ *
+ * @param string $path The path to the file
+ * @param bool $checkPath Whether to check the path or not
+ *
+ * @throws FileNotFoundException If the given path is not a file
+ */
+ public function __construct($path, $checkPath = true)
+ {
+ if ($checkPath && !is_file($path)) {
+ throw new FileNotFoundException($path);
+ }
+
+ parent::__construct($path);
+ }
+
+ /**
+ * Returns the extension based on the mime type.
+ *
+ * If the mime type is unknown, returns null.
+ *
+ * This method uses the mime type as guessed by getMimeType()
+ * to guess the file extension.
+ *
+ * @return string|null The guessed extension or null if it cannot be guessed
+ *
+ * @see ExtensionGuesser
+ * @see getMimeType()
+ */
+ public function guessExtension()
+ {
+ $type = $this->getMimeType();
+ $guesser = ExtensionGuesser::getInstance();
+
+ return $guesser->guess($type);
+ }
+
+ /**
+ * Returns the mime type of the file.
+ *
+ * The mime type is guessed using a MimeTypeGuesser instance, which uses finfo(),
+ * mime_content_type() and the system binary "file" (in this order), depending on
+ * which of those are available.
+ *
+ * @return string|null The guessed mime type (e.g. "application/pdf")
+ *
+ * @see MimeTypeGuesser
+ */
+ public function getMimeType()
+ {
+ $guesser = MimeTypeGuesser::getInstance();
+
+ return $guesser->guess($this->getPathname());
+ }
+
+ /**
+ * Moves the file to a new location.
+ *
+ * @param string $directory The destination folder
+ * @param string $name The new file name
+ *
+ * @return self A File object representing the new file
+ *
+ * @throws FileException if the target file could not be created
+ */
+ public function move($directory, $name = null)
+ {
+ $target = $this->getTargetFile($directory, $name);
+
+ if (!@rename($this->getPathname(), $target)) {
+ $error = error_get_last();
+ throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error['message'])));
+ }
+
+ @chmod($target, 0666 & ~umask());
+
+ return $target;
+ }
+
+ protected function getTargetFile($directory, $name = null)
+ {
+ if (!is_dir($directory)) {
+ if (false === @mkdir($directory, 0777, true) && !is_dir($directory)) {
+ throw new FileException(sprintf('Unable to create the "%s" directory', $directory));
+ }
+ } elseif (!is_writable($directory)) {
+ throw new FileException(sprintf('Unable to write in the "%s" directory', $directory));
+ }
+
+ $target = rtrim($directory, '/\\').DIRECTORY_SEPARATOR.(null === $name ? $this->getBasename() : $this->getName($name));
+
+ return new self($target, false);
+ }
+
+ /**
+ * Returns locale independent base name of the given path.
+ *
+ * @param string $name The new file name
+ *
+ * @return string containing
+ */
+ protected function getName($name)
+ {
+ $originalName = str_replace('\\', '/', $name);
+ $pos = strrpos($originalName, '/');
+ $originalName = false === $pos ? $originalName : substr($originalName, $pos + 1);
+
+ return $originalName;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php b/assets/php/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php
new file mode 100644
index 0000000..263fb32
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php
@@ -0,0 +1,94 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\MimeType;
+
+/**
+ * A singleton mime type to file extension guesser.
+ *
+ * A default guesser is provided.
+ * You can register custom guessers by calling the register()
+ * method on the singleton instance:
+ *
+ * $guesser = ExtensionGuesser::getInstance();
+ * $guesser->register(new MyCustomExtensionGuesser());
+ *
+ * The last registered guesser is preferred over previously registered ones.
+ */
+class ExtensionGuesser implements ExtensionGuesserInterface
+{
+ /**
+ * The singleton instance.
+ *
+ * @var ExtensionGuesser
+ */
+ private static $instance = null;
+
+ /**
+ * All registered ExtensionGuesserInterface instances.
+ *
+ * @var array
+ */
+ protected $guessers = array();
+
+ /**
+ * Returns the singleton instance.
+ *
+ * @return self
+ */
+ public static function getInstance()
+ {
+ if (null === self::$instance) {
+ self::$instance = new self();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Registers all natively provided extension guessers.
+ */
+ private function __construct()
+ {
+ $this->register(new MimeTypeExtensionGuesser());
+ }
+
+ /**
+ * Registers a new extension guesser.
+ *
+ * When guessing, this guesser is preferred over previously registered ones.
+ */
+ public function register(ExtensionGuesserInterface $guesser)
+ {
+ array_unshift($this->guessers, $guesser);
+ }
+
+ /**
+ * Tries to guess the extension.
+ *
+ * The mime type is passed to each registered mime type guesser in reverse order
+ * of their registration (last registered is queried first). Once a guesser
+ * returns a value that is not NULL, this method terminates and returns the
+ * value.
+ *
+ * @param string $mimeType The mime type
+ *
+ * @return string The guessed extension or NULL, if none could be guessed
+ */
+ public function guess($mimeType)
+ {
+ foreach ($this->guessers as $guesser) {
+ if (null !== $extension = $guesser->guess($mimeType)) {
+ return $extension;
+ }
+ }
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php b/assets/php/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php
new file mode 100644
index 0000000..d19a0e5
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php
@@ -0,0 +1,27 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\MimeType;
+
+/**
+ * Guesses the file extension corresponding to a given mime type.
+ */
+interface ExtensionGuesserInterface
+{
+ /**
+ * Makes a best guess for a file extension, given a mime type.
+ *
+ * @param string $mimeType The mime type
+ *
+ * @return string The guessed extension or NULL, if none could be guessed
+ */
+ public function guess($mimeType);
+}
diff --git a/assets/php/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php b/assets/php/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php
new file mode 100644
index 0000000..c2ac676
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php
@@ -0,0 +1,85 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\MimeType;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
+
+/**
+ * Guesses the mime type with the binary "file" (only available on *nix).
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
+{
+ private $cmd;
+
+ /**
+ * The $cmd pattern must contain a "%s" string that will be replaced
+ * with the file name to guess.
+ *
+ * The command output must start with the mime type of the file.
+ *
+ * @param string $cmd The command to run to get the mime type of a file
+ */
+ public function __construct($cmd = 'file -b --mime %s 2>/dev/null')
+ {
+ $this->cmd = $cmd;
+ }
+
+ /**
+ * Returns whether this guesser is supported on the current OS.
+ *
+ * @return bool
+ */
+ public static function isSupported()
+ {
+ return '\\' !== DIRECTORY_SEPARATOR && function_exists('passthru') && function_exists('escapeshellarg');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function guess($path)
+ {
+ if (!is_file($path)) {
+ throw new FileNotFoundException($path);
+ }
+
+ if (!is_readable($path)) {
+ throw new AccessDeniedException($path);
+ }
+
+ if (!self::isSupported()) {
+ return;
+ }
+
+ ob_start();
+
+ // need to use --mime instead of -i. see #6641
+ passthru(sprintf($this->cmd, escapeshellarg($path)), $return);
+ if ($return > 0) {
+ ob_end_clean();
+
+ return;
+ }
+
+ $type = trim(ob_get_clean());
+
+ if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\.]+)#i', $type, $match)) {
+ // it's not a type, but an error message
+ return;
+ }
+
+ return $match[1];
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php b/assets/php/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php
new file mode 100644
index 0000000..9b42835
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php
@@ -0,0 +1,69 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\MimeType;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
+
+/**
+ * Guesses the mime type using the PECL extension FileInfo.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
+{
+ private $magicFile;
+
+ /**
+ * @param string $magicFile A magic file to use with the finfo instance
+ *
+ * @see http://www.php.net/manual/en/function.finfo-open.php
+ */
+ public function __construct($magicFile = null)
+ {
+ $this->magicFile = $magicFile;
+ }
+
+ /**
+ * Returns whether this guesser is supported on the current OS/PHP setup.
+ *
+ * @return bool
+ */
+ public static function isSupported()
+ {
+ return function_exists('finfo_open');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function guess($path)
+ {
+ if (!is_file($path)) {
+ throw new FileNotFoundException($path);
+ }
+
+ if (!is_readable($path)) {
+ throw new AccessDeniedException($path);
+ }
+
+ if (!self::isSupported()) {
+ return;
+ }
+
+ if (!$finfo = new \finfo(FILEINFO_MIME_TYPE, $this->magicFile)) {
+ return;
+ }
+
+ return $finfo->file($path);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php b/assets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php
new file mode 100644
index 0000000..77bf51b
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php
@@ -0,0 +1,808 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\MimeType;
+
+/**
+ * Provides a best-guess mapping of mime type to file extension.
+ */
+class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
+{
+ /**
+ * A map of mime types and their default extensions.
+ *
+ * This list has been placed under the public domain by the Apache HTTPD project.
+ * This list has been updated from upstream on 2013-04-23.
+ *
+ * @see http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
+ */
+ protected $defaultExtensions = array(
+ 'application/andrew-inset' => 'ez',
+ 'application/applixware' => 'aw',
+ 'application/atom+xml' => 'atom',
+ 'application/atomcat+xml' => 'atomcat',
+ 'application/atomsvc+xml' => 'atomsvc',
+ 'application/ccxml+xml' => 'ccxml',
+ 'application/cdmi-capability' => 'cdmia',
+ 'application/cdmi-container' => 'cdmic',
+ 'application/cdmi-domain' => 'cdmid',
+ 'application/cdmi-object' => 'cdmio',
+ 'application/cdmi-queue' => 'cdmiq',
+ 'application/cu-seeme' => 'cu',
+ 'application/davmount+xml' => 'davmount',
+ 'application/docbook+xml' => 'dbk',
+ 'application/dssc+der' => 'dssc',
+ 'application/dssc+xml' => 'xdssc',
+ 'application/ecmascript' => 'ecma',
+ 'application/emma+xml' => 'emma',
+ 'application/epub+zip' => 'epub',
+ 'application/exi' => 'exi',
+ 'application/font-tdpfr' => 'pfr',
+ 'application/gml+xml' => 'gml',
+ 'application/gpx+xml' => 'gpx',
+ 'application/gxf' => 'gxf',
+ 'application/hyperstudio' => 'stk',
+ 'application/inkml+xml' => 'ink',
+ 'application/ipfix' => 'ipfix',
+ 'application/java-archive' => 'jar',
+ 'application/java-serialized-object' => 'ser',
+ 'application/java-vm' => 'class',
+ 'application/javascript' => 'js',
+ 'application/json' => 'json',
+ 'application/jsonml+json' => 'jsonml',
+ 'application/lost+xml' => 'lostxml',
+ 'application/mac-binhex40' => 'hqx',
+ 'application/mac-compactpro' => 'cpt',
+ 'application/mads+xml' => 'mads',
+ 'application/marc' => 'mrc',
+ 'application/marcxml+xml' => 'mrcx',
+ 'application/mathematica' => 'ma',
+ 'application/mathml+xml' => 'mathml',
+ 'application/mbox' => 'mbox',
+ 'application/mediaservercontrol+xml' => 'mscml',
+ 'application/metalink+xml' => 'metalink',
+ 'application/metalink4+xml' => 'meta4',
+ 'application/mets+xml' => 'mets',
+ 'application/mods+xml' => 'mods',
+ 'application/mp21' => 'm21',
+ 'application/mp4' => 'mp4s',
+ 'application/msword' => 'doc',
+ 'application/mxf' => 'mxf',
+ 'application/octet-stream' => 'bin',
+ 'application/oda' => 'oda',
+ 'application/oebps-package+xml' => 'opf',
+ 'application/ogg' => 'ogx',
+ 'application/omdoc+xml' => 'omdoc',
+ 'application/onenote' => 'onetoc',
+ 'application/oxps' => 'oxps',
+ 'application/patch-ops-error+xml' => 'xer',
+ 'application/pdf' => 'pdf',
+ 'application/pgp-encrypted' => 'pgp',
+ 'application/pgp-signature' => 'asc',
+ 'application/pics-rules' => 'prf',
+ 'application/pkcs10' => 'p10',
+ 'application/pkcs7-mime' => 'p7m',
+ 'application/pkcs7-signature' => 'p7s',
+ 'application/pkcs8' => 'p8',
+ 'application/pkix-attr-cert' => 'ac',
+ 'application/pkix-cert' => 'cer',
+ 'application/pkix-crl' => 'crl',
+ 'application/pkix-pkipath' => 'pkipath',
+ 'application/pkixcmp' => 'pki',
+ 'application/pls+xml' => 'pls',
+ 'application/postscript' => 'ai',
+ 'application/prs.cww' => 'cww',
+ 'application/pskc+xml' => 'pskcxml',
+ 'application/rdf+xml' => 'rdf',
+ 'application/reginfo+xml' => 'rif',
+ 'application/relax-ng-compact-syntax' => 'rnc',
+ 'application/resource-lists+xml' => 'rl',
+ 'application/resource-lists-diff+xml' => 'rld',
+ 'application/rls-services+xml' => 'rs',
+ 'application/rpki-ghostbusters' => 'gbr',
+ 'application/rpki-manifest' => 'mft',
+ 'application/rpki-roa' => 'roa',
+ 'application/rsd+xml' => 'rsd',
+ 'application/rss+xml' => 'rss',
+ 'application/rtf' => 'rtf',
+ 'application/sbml+xml' => 'sbml',
+ 'application/scvp-cv-request' => 'scq',
+ 'application/scvp-cv-response' => 'scs',
+ 'application/scvp-vp-request' => 'spq',
+ 'application/scvp-vp-response' => 'spp',
+ 'application/sdp' => 'sdp',
+ 'application/set-payment-initiation' => 'setpay',
+ 'application/set-registration-initiation' => 'setreg',
+ 'application/shf+xml' => 'shf',
+ 'application/smil+xml' => 'smi',
+ 'application/sparql-query' => 'rq',
+ 'application/sparql-results+xml' => 'srx',
+ 'application/srgs' => 'gram',
+ 'application/srgs+xml' => 'grxml',
+ 'application/sru+xml' => 'sru',
+ 'application/ssdl+xml' => 'ssdl',
+ 'application/ssml+xml' => 'ssml',
+ 'application/tei+xml' => 'tei',
+ 'application/thraud+xml' => 'tfi',
+ 'application/timestamped-data' => 'tsd',
+ 'application/vnd.3gpp.pic-bw-large' => 'plb',
+ 'application/vnd.3gpp.pic-bw-small' => 'psb',
+ 'application/vnd.3gpp.pic-bw-var' => 'pvb',
+ 'application/vnd.3gpp2.tcap' => 'tcap',
+ 'application/vnd.3m.post-it-notes' => 'pwn',
+ 'application/vnd.accpac.simply.aso' => 'aso',
+ 'application/vnd.accpac.simply.imp' => 'imp',
+ 'application/vnd.acucobol' => 'acu',
+ 'application/vnd.acucorp' => 'atc',
+ 'application/vnd.adobe.air-application-installer-package+zip' => 'air',
+ 'application/vnd.adobe.formscentral.fcdt' => 'fcdt',
+ 'application/vnd.adobe.fxp' => 'fxp',
+ 'application/vnd.adobe.xdp+xml' => 'xdp',
+ 'application/vnd.adobe.xfdf' => 'xfdf',
+ 'application/vnd.ahead.space' => 'ahead',
+ 'application/vnd.airzip.filesecure.azf' => 'azf',
+ 'application/vnd.airzip.filesecure.azs' => 'azs',
+ 'application/vnd.amazon.ebook' => 'azw',
+ 'application/vnd.americandynamics.acc' => 'acc',
+ 'application/vnd.amiga.ami' => 'ami',
+ 'application/vnd.android.package-archive' => 'apk',
+ 'application/vnd.anser-web-certificate-issue-initiation' => 'cii',
+ 'application/vnd.anser-web-funds-transfer-initiation' => 'fti',
+ 'application/vnd.antix.game-component' => 'atx',
+ 'application/vnd.apple.installer+xml' => 'mpkg',
+ 'application/vnd.apple.mpegurl' => 'm3u8',
+ 'application/vnd.aristanetworks.swi' => 'swi',
+ 'application/vnd.astraea-software.iota' => 'iota',
+ 'application/vnd.audiograph' => 'aep',
+ 'application/vnd.blueice.multipass' => 'mpm',
+ 'application/vnd.bmi' => 'bmi',
+ 'application/vnd.businessobjects' => 'rep',
+ 'application/vnd.chemdraw+xml' => 'cdxml',
+ 'application/vnd.chipnuts.karaoke-mmd' => 'mmd',
+ 'application/vnd.cinderella' => 'cdy',
+ 'application/vnd.claymore' => 'cla',
+ 'application/vnd.cloanto.rp9' => 'rp9',
+ 'application/vnd.clonk.c4group' => 'c4g',
+ 'application/vnd.cluetrust.cartomobile-config' => 'c11amc',
+ 'application/vnd.cluetrust.cartomobile-config-pkg' => 'c11amz',
+ 'application/vnd.commonspace' => 'csp',
+ 'application/vnd.contact.cmsg' => 'cdbcmsg',
+ 'application/vnd.cosmocaller' => 'cmc',
+ 'application/vnd.crick.clicker' => 'clkx',
+ 'application/vnd.crick.clicker.keyboard' => 'clkk',
+ 'application/vnd.crick.clicker.palette' => 'clkp',
+ 'application/vnd.crick.clicker.template' => 'clkt',
+ 'application/vnd.crick.clicker.wordbank' => 'clkw',
+ 'application/vnd.criticaltools.wbs+xml' => 'wbs',
+ 'application/vnd.ctc-posml' => 'pml',
+ 'application/vnd.cups-ppd' => 'ppd',
+ 'application/vnd.curl.car' => 'car',
+ 'application/vnd.curl.pcurl' => 'pcurl',
+ 'application/vnd.dart' => 'dart',
+ 'application/vnd.data-vision.rdz' => 'rdz',
+ 'application/vnd.dece.data' => 'uvf',
+ 'application/vnd.dece.ttml+xml' => 'uvt',
+ 'application/vnd.dece.unspecified' => 'uvx',
+ 'application/vnd.dece.zip' => 'uvz',
+ 'application/vnd.denovo.fcselayout-link' => 'fe_launch',
+ 'application/vnd.dna' => 'dna',
+ 'application/vnd.dolby.mlp' => 'mlp',
+ 'application/vnd.dpgraph' => 'dpg',
+ 'application/vnd.dreamfactory' => 'dfac',
+ 'application/vnd.ds-keypoint' => 'kpxx',
+ 'application/vnd.dvb.ait' => 'ait',
+ 'application/vnd.dvb.service' => 'svc',
+ 'application/vnd.dynageo' => 'geo',
+ 'application/vnd.ecowin.chart' => 'mag',
+ 'application/vnd.enliven' => 'nml',
+ 'application/vnd.epson.esf' => 'esf',
+ 'application/vnd.epson.msf' => 'msf',
+ 'application/vnd.epson.quickanime' => 'qam',
+ 'application/vnd.epson.salt' => 'slt',
+ 'application/vnd.epson.ssf' => 'ssf',
+ 'application/vnd.eszigno3+xml' => 'es3',
+ 'application/vnd.ezpix-album' => 'ez2',
+ 'application/vnd.ezpix-package' => 'ez3',
+ 'application/vnd.fdf' => 'fdf',
+ 'application/vnd.fdsn.mseed' => 'mseed',
+ 'application/vnd.fdsn.seed' => 'seed',
+ 'application/vnd.flographit' => 'gph',
+ 'application/vnd.fluxtime.clip' => 'ftc',
+ 'application/vnd.framemaker' => 'fm',
+ 'application/vnd.frogans.fnc' => 'fnc',
+ 'application/vnd.frogans.ltf' => 'ltf',
+ 'application/vnd.fsc.weblaunch' => 'fsc',
+ 'application/vnd.fujitsu.oasys' => 'oas',
+ 'application/vnd.fujitsu.oasys2' => 'oa2',
+ 'application/vnd.fujitsu.oasys3' => 'oa3',
+ 'application/vnd.fujitsu.oasysgp' => 'fg5',
+ 'application/vnd.fujitsu.oasysprs' => 'bh2',
+ 'application/vnd.fujixerox.ddd' => 'ddd',
+ 'application/vnd.fujixerox.docuworks' => 'xdw',
+ 'application/vnd.fujixerox.docuworks.binder' => 'xbd',
+ 'application/vnd.fuzzysheet' => 'fzs',
+ 'application/vnd.genomatix.tuxedo' => 'txd',
+ 'application/vnd.geogebra.file' => 'ggb',
+ 'application/vnd.geogebra.tool' => 'ggt',
+ 'application/vnd.geometry-explorer' => 'gex',
+ 'application/vnd.geonext' => 'gxt',
+ 'application/vnd.geoplan' => 'g2w',
+ 'application/vnd.geospace' => 'g3w',
+ 'application/vnd.gmx' => 'gmx',
+ 'application/vnd.google-earth.kml+xml' => 'kml',
+ 'application/vnd.google-earth.kmz' => 'kmz',
+ 'application/vnd.grafeq' => 'gqf',
+ 'application/vnd.groove-account' => 'gac',
+ 'application/vnd.groove-help' => 'ghf',
+ 'application/vnd.groove-identity-message' => 'gim',
+ 'application/vnd.groove-injector' => 'grv',
+ 'application/vnd.groove-tool-message' => 'gtm',
+ 'application/vnd.groove-tool-template' => 'tpl',
+ 'application/vnd.groove-vcard' => 'vcg',
+ 'application/vnd.hal+xml' => 'hal',
+ 'application/vnd.handheld-entertainment+xml' => 'zmm',
+ 'application/vnd.hbci' => 'hbci',
+ 'application/vnd.hhe.lesson-player' => 'les',
+ 'application/vnd.hp-hpgl' => 'hpgl',
+ 'application/vnd.hp-hpid' => 'hpid',
+ 'application/vnd.hp-hps' => 'hps',
+ 'application/vnd.hp-jlyt' => 'jlt',
+ 'application/vnd.hp-pcl' => 'pcl',
+ 'application/vnd.hp-pclxl' => 'pclxl',
+ 'application/vnd.hydrostatix.sof-data' => 'sfd-hdstx',
+ 'application/vnd.ibm.minipay' => 'mpy',
+ 'application/vnd.ibm.modcap' => 'afp',
+ 'application/vnd.ibm.rights-management' => 'irm',
+ 'application/vnd.ibm.secure-container' => 'sc',
+ 'application/vnd.iccprofile' => 'icc',
+ 'application/vnd.igloader' => 'igl',
+ 'application/vnd.immervision-ivp' => 'ivp',
+ 'application/vnd.immervision-ivu' => 'ivu',
+ 'application/vnd.insors.igm' => 'igm',
+ 'application/vnd.intercon.formnet' => 'xpw',
+ 'application/vnd.intergeo' => 'i2g',
+ 'application/vnd.intu.qbo' => 'qbo',
+ 'application/vnd.intu.qfx' => 'qfx',
+ 'application/vnd.ipunplugged.rcprofile' => 'rcprofile',
+ 'application/vnd.irepository.package+xml' => 'irp',
+ 'application/vnd.is-xpr' => 'xpr',
+ 'application/vnd.isac.fcs' => 'fcs',
+ 'application/vnd.jam' => 'jam',
+ 'application/vnd.jcp.javame.midlet-rms' => 'rms',
+ 'application/vnd.jisp' => 'jisp',
+ 'application/vnd.joost.joda-archive' => 'joda',
+ 'application/vnd.kahootz' => 'ktz',
+ 'application/vnd.kde.karbon' => 'karbon',
+ 'application/vnd.kde.kchart' => 'chrt',
+ 'application/vnd.kde.kformula' => 'kfo',
+ 'application/vnd.kde.kivio' => 'flw',
+ 'application/vnd.kde.kontour' => 'kon',
+ 'application/vnd.kde.kpresenter' => 'kpr',
+ 'application/vnd.kde.kspread' => 'ksp',
+ 'application/vnd.kde.kword' => 'kwd',
+ 'application/vnd.kenameaapp' => 'htke',
+ 'application/vnd.kidspiration' => 'kia',
+ 'application/vnd.kinar' => 'kne',
+ 'application/vnd.koan' => 'skp',
+ 'application/vnd.kodak-descriptor' => 'sse',
+ 'application/vnd.las.las+xml' => 'lasxml',
+ 'application/vnd.llamagraphics.life-balance.desktop' => 'lbd',
+ 'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe',
+ 'application/vnd.lotus-1-2-3' => '123',
+ 'application/vnd.lotus-approach' => 'apr',
+ 'application/vnd.lotus-freelance' => 'pre',
+ 'application/vnd.lotus-notes' => 'nsf',
+ 'application/vnd.lotus-organizer' => 'org',
+ 'application/vnd.lotus-screencam' => 'scm',
+ 'application/vnd.lotus-wordpro' => 'lwp',
+ 'application/vnd.macports.portpkg' => 'portpkg',
+ 'application/vnd.mcd' => 'mcd',
+ 'application/vnd.medcalcdata' => 'mc1',
+ 'application/vnd.mediastation.cdkey' => 'cdkey',
+ 'application/vnd.mfer' => 'mwf',
+ 'application/vnd.mfmp' => 'mfm',
+ 'application/vnd.micrografx.flo' => 'flo',
+ 'application/vnd.micrografx.igx' => 'igx',
+ 'application/vnd.mif' => 'mif',
+ 'application/vnd.mobius.daf' => 'daf',
+ 'application/vnd.mobius.dis' => 'dis',
+ 'application/vnd.mobius.mbk' => 'mbk',
+ 'application/vnd.mobius.mqy' => 'mqy',
+ 'application/vnd.mobius.msl' => 'msl',
+ 'application/vnd.mobius.plc' => 'plc',
+ 'application/vnd.mobius.txf' => 'txf',
+ 'application/vnd.mophun.application' => 'mpn',
+ 'application/vnd.mophun.certificate' => 'mpc',
+ 'application/vnd.mozilla.xul+xml' => 'xul',
+ 'application/vnd.ms-artgalry' => 'cil',
+ 'application/vnd.ms-cab-compressed' => 'cab',
+ 'application/vnd.ms-excel' => 'xls',
+ 'application/vnd.ms-excel.addin.macroenabled.12' => 'xlam',
+ 'application/vnd.ms-excel.sheet.binary.macroenabled.12' => 'xlsb',
+ 'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm',
+ 'application/vnd.ms-excel.template.macroenabled.12' => 'xltm',
+ 'application/vnd.ms-fontobject' => 'eot',
+ 'application/vnd.ms-htmlhelp' => 'chm',
+ 'application/vnd.ms-ims' => 'ims',
+ 'application/vnd.ms-lrm' => 'lrm',
+ 'application/vnd.ms-officetheme' => 'thmx',
+ 'application/vnd.ms-pki.seccat' => 'cat',
+ 'application/vnd.ms-pki.stl' => 'stl',
+ 'application/vnd.ms-powerpoint' => 'ppt',
+ 'application/vnd.ms-powerpoint.addin.macroenabled.12' => 'ppam',
+ 'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'pptm',
+ 'application/vnd.ms-powerpoint.slide.macroenabled.12' => 'sldm',
+ 'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'ppsm',
+ 'application/vnd.ms-powerpoint.template.macroenabled.12' => 'potm',
+ 'application/vnd.ms-project' => 'mpp',
+ 'application/vnd.ms-word.document.macroenabled.12' => 'docm',
+ 'application/vnd.ms-word.template.macroenabled.12' => 'dotm',
+ 'application/vnd.ms-works' => 'wps',
+ 'application/vnd.ms-wpl' => 'wpl',
+ 'application/vnd.ms-xpsdocument' => 'xps',
+ 'application/vnd.mseq' => 'mseq',
+ 'application/vnd.musician' => 'mus',
+ 'application/vnd.muvee.style' => 'msty',
+ 'application/vnd.mynfc' => 'taglet',
+ 'application/vnd.neurolanguage.nlu' => 'nlu',
+ 'application/vnd.nitf' => 'ntf',
+ 'application/vnd.noblenet-directory' => 'nnd',
+ 'application/vnd.noblenet-sealer' => 'nns',
+ 'application/vnd.noblenet-web' => 'nnw',
+ 'application/vnd.nokia.n-gage.data' => 'ngdat',
+ 'application/vnd.nokia.n-gage.symbian.install' => 'n-gage',
+ 'application/vnd.nokia.radio-preset' => 'rpst',
+ 'application/vnd.nokia.radio-presets' => 'rpss',
+ 'application/vnd.novadigm.edm' => 'edm',
+ 'application/vnd.novadigm.edx' => 'edx',
+ 'application/vnd.novadigm.ext' => 'ext',
+ 'application/vnd.oasis.opendocument.chart' => 'odc',
+ 'application/vnd.oasis.opendocument.chart-template' => 'otc',
+ 'application/vnd.oasis.opendocument.database' => 'odb',
+ 'application/vnd.oasis.opendocument.formula' => 'odf',
+ 'application/vnd.oasis.opendocument.formula-template' => 'odft',
+ 'application/vnd.oasis.opendocument.graphics' => 'odg',
+ 'application/vnd.oasis.opendocument.graphics-template' => 'otg',
+ 'application/vnd.oasis.opendocument.image' => 'odi',
+ 'application/vnd.oasis.opendocument.image-template' => 'oti',
+ 'application/vnd.oasis.opendocument.presentation' => 'odp',
+ 'application/vnd.oasis.opendocument.presentation-template' => 'otp',
+ 'application/vnd.oasis.opendocument.spreadsheet' => 'ods',
+ 'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots',
+ 'application/vnd.oasis.opendocument.text' => 'odt',
+ 'application/vnd.oasis.opendocument.text-master' => 'odm',
+ 'application/vnd.oasis.opendocument.text-template' => 'ott',
+ 'application/vnd.oasis.opendocument.text-web' => 'oth',
+ 'application/vnd.olpc-sugar' => 'xo',
+ 'application/vnd.oma.dd2+xml' => 'dd2',
+ 'application/vnd.openofficeorg.extension' => 'oxt',
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',
+ 'application/vnd.openxmlformats-officedocument.presentationml.slide' => 'sldx',
+ 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx',
+ 'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx',
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx',
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx',
+ 'application/vnd.osgeo.mapguide.package' => 'mgp',
+ 'application/vnd.osgi.dp' => 'dp',
+ 'application/vnd.osgi.subsystem' => 'esa',
+ 'application/vnd.palm' => 'pdb',
+ 'application/vnd.pawaafile' => 'paw',
+ 'application/vnd.pg.format' => 'str',
+ 'application/vnd.pg.osasli' => 'ei6',
+ 'application/vnd.picsel' => 'efif',
+ 'application/vnd.pmi.widget' => 'wg',
+ 'application/vnd.pocketlearn' => 'plf',
+ 'application/vnd.powerbuilder6' => 'pbd',
+ 'application/vnd.previewsystems.box' => 'box',
+ 'application/vnd.proteus.magazine' => 'mgz',
+ 'application/vnd.publishare-delta-tree' => 'qps',
+ 'application/vnd.pvi.ptid1' => 'ptid',
+ 'application/vnd.quark.quarkxpress' => 'qxd',
+ 'application/vnd.realvnc.bed' => 'bed',
+ 'application/vnd.recordare.musicxml' => 'mxl',
+ 'application/vnd.recordare.musicxml+xml' => 'musicxml',
+ 'application/vnd.rig.cryptonote' => 'cryptonote',
+ 'application/vnd.rim.cod' => 'cod',
+ 'application/vnd.rn-realmedia' => 'rm',
+ 'application/vnd.rn-realmedia-vbr' => 'rmvb',
+ 'application/vnd.route66.link66+xml' => 'link66',
+ 'application/vnd.sailingtracker.track' => 'st',
+ 'application/vnd.seemail' => 'see',
+ 'application/vnd.sema' => 'sema',
+ 'application/vnd.semd' => 'semd',
+ 'application/vnd.semf' => 'semf',
+ 'application/vnd.shana.informed.formdata' => 'ifm',
+ 'application/vnd.shana.informed.formtemplate' => 'itp',
+ 'application/vnd.shana.informed.interchange' => 'iif',
+ 'application/vnd.shana.informed.package' => 'ipk',
+ 'application/vnd.simtech-mindmapper' => 'twd',
+ 'application/vnd.smaf' => 'mmf',
+ 'application/vnd.smart.teacher' => 'teacher',
+ 'application/vnd.solent.sdkm+xml' => 'sdkm',
+ 'application/vnd.spotfire.dxp' => 'dxp',
+ 'application/vnd.spotfire.sfs' => 'sfs',
+ 'application/vnd.stardivision.calc' => 'sdc',
+ 'application/vnd.stardivision.draw' => 'sda',
+ 'application/vnd.stardivision.impress' => 'sdd',
+ 'application/vnd.stardivision.math' => 'smf',
+ 'application/vnd.stardivision.writer' => 'sdw',
+ 'application/vnd.stardivision.writer-global' => 'sgl',
+ 'application/vnd.stepmania.package' => 'smzip',
+ 'application/vnd.stepmania.stepchart' => 'sm',
+ 'application/vnd.sun.xml.calc' => 'sxc',
+ 'application/vnd.sun.xml.calc.template' => 'stc',
+ 'application/vnd.sun.xml.draw' => 'sxd',
+ 'application/vnd.sun.xml.draw.template' => 'std',
+ 'application/vnd.sun.xml.impress' => 'sxi',
+ 'application/vnd.sun.xml.impress.template' => 'sti',
+ 'application/vnd.sun.xml.math' => 'sxm',
+ 'application/vnd.sun.xml.writer' => 'sxw',
+ 'application/vnd.sun.xml.writer.global' => 'sxg',
+ 'application/vnd.sun.xml.writer.template' => 'stw',
+ 'application/vnd.sus-calendar' => 'sus',
+ 'application/vnd.svd' => 'svd',
+ 'application/vnd.symbian.install' => 'sis',
+ 'application/vnd.syncml+xml' => 'xsm',
+ 'application/vnd.syncml.dm+wbxml' => 'bdm',
+ 'application/vnd.syncml.dm+xml' => 'xdm',
+ 'application/vnd.tao.intent-module-archive' => 'tao',
+ 'application/vnd.tcpdump.pcap' => 'pcap',
+ 'application/vnd.tmobile-livetv' => 'tmo',
+ 'application/vnd.trid.tpt' => 'tpt',
+ 'application/vnd.triscape.mxs' => 'mxs',
+ 'application/vnd.trueapp' => 'tra',
+ 'application/vnd.ufdl' => 'ufd',
+ 'application/vnd.uiq.theme' => 'utz',
+ 'application/vnd.umajin' => 'umj',
+ 'application/vnd.unity' => 'unityweb',
+ 'application/vnd.uoml+xml' => 'uoml',
+ 'application/vnd.vcx' => 'vcx',
+ 'application/vnd.visio' => 'vsd',
+ 'application/vnd.visionary' => 'vis',
+ 'application/vnd.vsf' => 'vsf',
+ 'application/vnd.wap.wbxml' => 'wbxml',
+ 'application/vnd.wap.wmlc' => 'wmlc',
+ 'application/vnd.wap.wmlscriptc' => 'wmlsc',
+ 'application/vnd.webturbo' => 'wtb',
+ 'application/vnd.wolfram.player' => 'nbp',
+ 'application/vnd.wordperfect' => 'wpd',
+ 'application/vnd.wqd' => 'wqd',
+ 'application/vnd.wt.stf' => 'stf',
+ 'application/vnd.xara' => 'xar',
+ 'application/vnd.xfdl' => 'xfdl',
+ 'application/vnd.yamaha.hv-dic' => 'hvd',
+ 'application/vnd.yamaha.hv-script' => 'hvs',
+ 'application/vnd.yamaha.hv-voice' => 'hvp',
+ 'application/vnd.yamaha.openscoreformat' => 'osf',
+ 'application/vnd.yamaha.openscoreformat.osfpvg+xml' => 'osfpvg',
+ 'application/vnd.yamaha.smaf-audio' => 'saf',
+ 'application/vnd.yamaha.smaf-phrase' => 'spf',
+ 'application/vnd.yellowriver-custom-menu' => 'cmp',
+ 'application/vnd.zul' => 'zir',
+ 'application/vnd.zzazz.deck+xml' => 'zaz',
+ 'application/voicexml+xml' => 'vxml',
+ 'application/widget' => 'wgt',
+ 'application/winhlp' => 'hlp',
+ 'application/wsdl+xml' => 'wsdl',
+ 'application/wspolicy+xml' => 'wspolicy',
+ 'application/x-7z-compressed' => '7z',
+ 'application/x-abiword' => 'abw',
+ 'application/x-ace-compressed' => 'ace',
+ 'application/x-apple-diskimage' => 'dmg',
+ 'application/x-authorware-bin' => 'aab',
+ 'application/x-authorware-map' => 'aam',
+ 'application/x-authorware-seg' => 'aas',
+ 'application/x-bcpio' => 'bcpio',
+ 'application/x-bittorrent' => 'torrent',
+ 'application/x-blorb' => 'blb',
+ 'application/x-bzip' => 'bz',
+ 'application/x-bzip2' => 'bz2',
+ 'application/x-cbr' => 'cbr',
+ 'application/x-cdlink' => 'vcd',
+ 'application/x-cfs-compressed' => 'cfs',
+ 'application/x-chat' => 'chat',
+ 'application/x-chess-pgn' => 'pgn',
+ 'application/x-conference' => 'nsc',
+ 'application/x-cpio' => 'cpio',
+ 'application/x-csh' => 'csh',
+ 'application/x-debian-package' => 'deb',
+ 'application/x-dgc-compressed' => 'dgc',
+ 'application/x-director' => 'dir',
+ 'application/x-doom' => 'wad',
+ 'application/x-dtbncx+xml' => 'ncx',
+ 'application/x-dtbook+xml' => 'dtb',
+ 'application/x-dtbresource+xml' => 'res',
+ 'application/x-dvi' => 'dvi',
+ 'application/x-envoy' => 'evy',
+ 'application/x-eva' => 'eva',
+ 'application/x-font-bdf' => 'bdf',
+ 'application/x-font-ghostscript' => 'gsf',
+ 'application/x-font-linux-psf' => 'psf',
+ 'application/x-font-otf' => 'otf',
+ 'application/x-font-pcf' => 'pcf',
+ 'application/x-font-snf' => 'snf',
+ 'application/x-font-ttf' => 'ttf',
+ 'application/x-font-type1' => 'pfa',
+ 'application/x-font-woff' => 'woff',
+ 'application/x-freearc' => 'arc',
+ 'application/x-futuresplash' => 'spl',
+ 'application/x-gca-compressed' => 'gca',
+ 'application/x-glulx' => 'ulx',
+ 'application/x-gnumeric' => 'gnumeric',
+ 'application/x-gramps-xml' => 'gramps',
+ 'application/x-gtar' => 'gtar',
+ 'application/x-hdf' => 'hdf',
+ 'application/x-install-instructions' => 'install',
+ 'application/x-iso9660-image' => 'iso',
+ 'application/x-java-jnlp-file' => 'jnlp',
+ 'application/x-latex' => 'latex',
+ 'application/x-lzh-compressed' => 'lzh',
+ 'application/x-mie' => 'mie',
+ 'application/x-mobipocket-ebook' => 'prc',
+ 'application/x-ms-application' => 'application',
+ 'application/x-ms-shortcut' => 'lnk',
+ 'application/x-ms-wmd' => 'wmd',
+ 'application/x-ms-wmz' => 'wmz',
+ 'application/x-ms-xbap' => 'xbap',
+ 'application/x-msaccess' => 'mdb',
+ 'application/x-msbinder' => 'obd',
+ 'application/x-mscardfile' => 'crd',
+ 'application/x-msclip' => 'clp',
+ 'application/x-msdownload' => 'exe',
+ 'application/x-msmediaview' => 'mvb',
+ 'application/x-msmetafile' => 'wmf',
+ 'application/x-msmoney' => 'mny',
+ 'application/x-mspublisher' => 'pub',
+ 'application/x-msschedule' => 'scd',
+ 'application/x-msterminal' => 'trm',
+ 'application/x-mswrite' => 'wri',
+ 'application/x-netcdf' => 'nc',
+ 'application/x-nzb' => 'nzb',
+ 'application/x-pkcs12' => 'p12',
+ 'application/x-pkcs7-certificates' => 'p7b',
+ 'application/x-pkcs7-certreqresp' => 'p7r',
+ 'application/x-rar-compressed' => 'rar',
+ 'application/x-rar' => 'rar',
+ 'application/x-research-info-systems' => 'ris',
+ 'application/x-sh' => 'sh',
+ 'application/x-shar' => 'shar',
+ 'application/x-shockwave-flash' => 'swf',
+ 'application/x-silverlight-app' => 'xap',
+ 'application/x-sql' => 'sql',
+ 'application/x-stuffit' => 'sit',
+ 'application/x-stuffitx' => 'sitx',
+ 'application/x-subrip' => 'srt',
+ 'application/x-sv4cpio' => 'sv4cpio',
+ 'application/x-sv4crc' => 'sv4crc',
+ 'application/x-t3vm-image' => 't3',
+ 'application/x-tads' => 'gam',
+ 'application/x-tar' => 'tar',
+ 'application/x-tcl' => 'tcl',
+ 'application/x-tex' => 'tex',
+ 'application/x-tex-tfm' => 'tfm',
+ 'application/x-texinfo' => 'texinfo',
+ 'application/x-tgif' => 'obj',
+ 'application/x-ustar' => 'ustar',
+ 'application/x-wais-source' => 'src',
+ 'application/x-x509-ca-cert' => 'der',
+ 'application/x-xfig' => 'fig',
+ 'application/x-xliff+xml' => 'xlf',
+ 'application/x-xpinstall' => 'xpi',
+ 'application/x-xz' => 'xz',
+ 'application/x-zip-compressed' => 'zip',
+ 'application/x-zmachine' => 'z1',
+ 'application/xaml+xml' => 'xaml',
+ 'application/xcap-diff+xml' => 'xdf',
+ 'application/xenc+xml' => 'xenc',
+ 'application/xhtml+xml' => 'xhtml',
+ 'application/xml' => 'xml',
+ 'application/xml-dtd' => 'dtd',
+ 'application/xop+xml' => 'xop',
+ 'application/xproc+xml' => 'xpl',
+ 'application/xslt+xml' => 'xslt',
+ 'application/xspf+xml' => 'xspf',
+ 'application/xv+xml' => 'mxml',
+ 'application/yang' => 'yang',
+ 'application/yin+xml' => 'yin',
+ 'application/zip' => 'zip',
+ 'audio/adpcm' => 'adp',
+ 'audio/basic' => 'au',
+ 'audio/midi' => 'mid',
+ 'audio/mp4' => 'mp4a',
+ 'audio/mpeg' => 'mpga',
+ 'audio/ogg' => 'oga',
+ 'audio/s3m' => 's3m',
+ 'audio/silk' => 'sil',
+ 'audio/vnd.dece.audio' => 'uva',
+ 'audio/vnd.digital-winds' => 'eol',
+ 'audio/vnd.dra' => 'dra',
+ 'audio/vnd.dts' => 'dts',
+ 'audio/vnd.dts.hd' => 'dtshd',
+ 'audio/vnd.lucent.voice' => 'lvp',
+ 'audio/vnd.ms-playready.media.pya' => 'pya',
+ 'audio/vnd.nuera.ecelp4800' => 'ecelp4800',
+ 'audio/vnd.nuera.ecelp7470' => 'ecelp7470',
+ 'audio/vnd.nuera.ecelp9600' => 'ecelp9600',
+ 'audio/vnd.rip' => 'rip',
+ 'audio/webm' => 'weba',
+ 'audio/x-aac' => 'aac',
+ 'audio/x-aiff' => 'aif',
+ 'audio/x-caf' => 'caf',
+ 'audio/x-flac' => 'flac',
+ 'audio/x-matroska' => 'mka',
+ 'audio/x-mpegurl' => 'm3u',
+ 'audio/x-ms-wax' => 'wax',
+ 'audio/x-ms-wma' => 'wma',
+ 'audio/x-pn-realaudio' => 'ram',
+ 'audio/x-pn-realaudio-plugin' => 'rmp',
+ 'audio/x-wav' => 'wav',
+ 'audio/xm' => 'xm',
+ 'chemical/x-cdx' => 'cdx',
+ 'chemical/x-cif' => 'cif',
+ 'chemical/x-cmdf' => 'cmdf',
+ 'chemical/x-cml' => 'cml',
+ 'chemical/x-csml' => 'csml',
+ 'chemical/x-xyz' => 'xyz',
+ 'image/bmp' => 'bmp',
+ 'image/x-ms-bmp' => 'bmp',
+ 'image/cgm' => 'cgm',
+ 'image/g3fax' => 'g3',
+ 'image/gif' => 'gif',
+ 'image/ief' => 'ief',
+ 'image/jpeg' => 'jpeg',
+ 'image/pjpeg' => 'jpeg',
+ 'image/ktx' => 'ktx',
+ 'image/png' => 'png',
+ 'image/prs.btif' => 'btif',
+ 'image/sgi' => 'sgi',
+ 'image/svg+xml' => 'svg',
+ 'image/tiff' => 'tiff',
+ 'image/vnd.adobe.photoshop' => 'psd',
+ 'image/vnd.dece.graphic' => 'uvi',
+ 'image/vnd.dvb.subtitle' => 'sub',
+ 'image/vnd.djvu' => 'djvu',
+ 'image/vnd.dwg' => 'dwg',
+ 'image/vnd.dxf' => 'dxf',
+ 'image/vnd.fastbidsheet' => 'fbs',
+ 'image/vnd.fpx' => 'fpx',
+ 'image/vnd.fst' => 'fst',
+ 'image/vnd.fujixerox.edmics-mmr' => 'mmr',
+ 'image/vnd.fujixerox.edmics-rlc' => 'rlc',
+ 'image/vnd.ms-modi' => 'mdi',
+ 'image/vnd.ms-photo' => 'wdp',
+ 'image/vnd.net-fpx' => 'npx',
+ 'image/vnd.wap.wbmp' => 'wbmp',
+ 'image/vnd.xiff' => 'xif',
+ 'image/webp' => 'webp',
+ 'image/x-3ds' => '3ds',
+ 'image/x-cmu-raster' => 'ras',
+ 'image/x-cmx' => 'cmx',
+ 'image/x-freehand' => 'fh',
+ 'image/x-icon' => 'ico',
+ 'image/x-mrsid-image' => 'sid',
+ 'image/x-pcx' => 'pcx',
+ 'image/x-pict' => 'pic',
+ 'image/x-portable-anymap' => 'pnm',
+ 'image/x-portable-bitmap' => 'pbm',
+ 'image/x-portable-graymap' => 'pgm',
+ 'image/x-portable-pixmap' => 'ppm',
+ 'image/x-rgb' => 'rgb',
+ 'image/x-tga' => 'tga',
+ 'image/x-xbitmap' => 'xbm',
+ 'image/x-xpixmap' => 'xpm',
+ 'image/x-xwindowdump' => 'xwd',
+ 'message/rfc822' => 'eml',
+ 'model/iges' => 'igs',
+ 'model/mesh' => 'msh',
+ 'model/vnd.collada+xml' => 'dae',
+ 'model/vnd.dwf' => 'dwf',
+ 'model/vnd.gdl' => 'gdl',
+ 'model/vnd.gtw' => 'gtw',
+ 'model/vnd.mts' => 'mts',
+ 'model/vnd.vtu' => 'vtu',
+ 'model/vrml' => 'wrl',
+ 'model/x3d+binary' => 'x3db',
+ 'model/x3d+vrml' => 'x3dv',
+ 'model/x3d+xml' => 'x3d',
+ 'text/cache-manifest' => 'appcache',
+ 'text/calendar' => 'ics',
+ 'text/css' => 'css',
+ 'text/csv' => 'csv',
+ 'text/html' => 'html',
+ 'text/n3' => 'n3',
+ 'text/plain' => 'txt',
+ 'text/prs.lines.tag' => 'dsc',
+ 'text/richtext' => 'rtx',
+ 'text/rtf' => 'rtf',
+ 'text/sgml' => 'sgml',
+ 'text/tab-separated-values' => 'tsv',
+ 'text/troff' => 't',
+ 'text/turtle' => 'ttl',
+ 'text/uri-list' => 'uri',
+ 'text/vcard' => 'vcard',
+ 'text/vnd.curl' => 'curl',
+ 'text/vnd.curl.dcurl' => 'dcurl',
+ 'text/vnd.curl.scurl' => 'scurl',
+ 'text/vnd.curl.mcurl' => 'mcurl',
+ 'text/vnd.dvb.subtitle' => 'sub',
+ 'text/vnd.fly' => 'fly',
+ 'text/vnd.fmi.flexstor' => 'flx',
+ 'text/vnd.graphviz' => 'gv',
+ 'text/vnd.in3d.3dml' => '3dml',
+ 'text/vnd.in3d.spot' => 'spot',
+ 'text/vnd.sun.j2me.app-descriptor' => 'jad',
+ 'text/vnd.wap.wml' => 'wml',
+ 'text/vnd.wap.wmlscript' => 'wmls',
+ 'text/vtt' => 'vtt',
+ 'text/x-asm' => 's',
+ 'text/x-c' => 'c',
+ 'text/x-fortran' => 'f',
+ 'text/x-pascal' => 'p',
+ 'text/x-java-source' => 'java',
+ 'text/x-opml' => 'opml',
+ 'text/x-nfo' => 'nfo',
+ 'text/x-setext' => 'etx',
+ 'text/x-sfv' => 'sfv',
+ 'text/x-uuencode' => 'uu',
+ 'text/x-vcalendar' => 'vcs',
+ 'text/x-vcard' => 'vcf',
+ 'video/3gpp' => '3gp',
+ 'video/3gpp2' => '3g2',
+ 'video/h261' => 'h261',
+ 'video/h263' => 'h263',
+ 'video/h264' => 'h264',
+ 'video/jpeg' => 'jpgv',
+ 'video/jpm' => 'jpm',
+ 'video/mj2' => 'mj2',
+ 'video/mp4' => 'mp4',
+ 'video/mpeg' => 'mpeg',
+ 'video/ogg' => 'ogv',
+ 'video/quicktime' => 'qt',
+ 'video/vnd.dece.hd' => 'uvh',
+ 'video/vnd.dece.mobile' => 'uvm',
+ 'video/vnd.dece.pd' => 'uvp',
+ 'video/vnd.dece.sd' => 'uvs',
+ 'video/vnd.dece.video' => 'uvv',
+ 'video/vnd.dvb.file' => 'dvb',
+ 'video/vnd.fvt' => 'fvt',
+ 'video/vnd.mpegurl' => 'mxu',
+ 'video/vnd.ms-playready.media.pyv' => 'pyv',
+ 'video/vnd.uvvu.mp4' => 'uvu',
+ 'video/vnd.vivo' => 'viv',
+ 'video/webm' => 'webm',
+ 'video/x-f4v' => 'f4v',
+ 'video/x-fli' => 'fli',
+ 'video/x-flv' => 'flv',
+ 'video/x-m4v' => 'm4v',
+ 'video/x-matroska' => 'mkv',
+ 'video/x-mng' => 'mng',
+ 'video/x-ms-asf' => 'asf',
+ 'video/x-ms-vob' => 'vob',
+ 'video/x-ms-wm' => 'wm',
+ 'video/x-ms-wmv' => 'wmv',
+ 'video/x-ms-wmx' => 'wmx',
+ 'video/x-ms-wvx' => 'wvx',
+ 'video/x-msvideo' => 'avi',
+ 'video/x-sgi-movie' => 'movie',
+ 'video/x-smv' => 'smv',
+ 'x-conference/x-cooltalk' => 'ice',
+ );
+
+ /**
+ * {@inheritdoc}
+ */
+ public function guess($mimeType)
+ {
+ return isset($this->defaultExtensions[$mimeType]) ? $this->defaultExtensions[$mimeType] : null;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php b/assets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php
new file mode 100644
index 0000000..e3ef45e
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php
@@ -0,0 +1,142 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\MimeType;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
+
+/**
+ * A singleton mime type guesser.
+ *
+ * By default, all mime type guessers provided by the framework are installed
+ * (if available on the current OS/PHP setup).
+ *
+ * You can register custom guessers by calling the register() method on the
+ * singleton instance. Custom guessers are always called before any default ones.
+ *
+ * $guesser = MimeTypeGuesser::getInstance();
+ * $guesser->register(new MyCustomMimeTypeGuesser());
+ *
+ * If you want to change the order of the default guessers, just re-register your
+ * preferred one as a custom one. The last registered guesser is preferred over
+ * previously registered ones.
+ *
+ * Re-registering a built-in guesser also allows you to configure it:
+ *
+ * $guesser = MimeTypeGuesser::getInstance();
+ * $guesser->register(new FileinfoMimeTypeGuesser('/path/to/magic/file'));
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class MimeTypeGuesser implements MimeTypeGuesserInterface
+{
+ /**
+ * The singleton instance.
+ *
+ * @var MimeTypeGuesser
+ */
+ private static $instance = null;
+
+ /**
+ * All registered MimeTypeGuesserInterface instances.
+ *
+ * @var array
+ */
+ protected $guessers = array();
+
+ /**
+ * Returns the singleton instance.
+ *
+ * @return self
+ */
+ public static function getInstance()
+ {
+ if (null === self::$instance) {
+ self::$instance = new self();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Resets the singleton instance.
+ */
+ public static function reset()
+ {
+ self::$instance = null;
+ }
+
+ /**
+ * Registers all natively provided mime type guessers.
+ */
+ private function __construct()
+ {
+ if (FileBinaryMimeTypeGuesser::isSupported()) {
+ $this->register(new FileBinaryMimeTypeGuesser());
+ }
+
+ if (FileinfoMimeTypeGuesser::isSupported()) {
+ $this->register(new FileinfoMimeTypeGuesser());
+ }
+ }
+
+ /**
+ * Registers a new mime type guesser.
+ *
+ * When guessing, this guesser is preferred over previously registered ones.
+ */
+ public function register(MimeTypeGuesserInterface $guesser)
+ {
+ array_unshift($this->guessers, $guesser);
+ }
+
+ /**
+ * Tries to guess the mime type of the given file.
+ *
+ * The file is passed to each registered mime type guesser in reverse order
+ * of their registration (last registered is queried first). Once a guesser
+ * returns a value that is not NULL, this method terminates and returns the
+ * value.
+ *
+ * @param string $path The path to the file
+ *
+ * @return string The mime type or NULL, if none could be guessed
+ *
+ * @throws \LogicException
+ * @throws FileNotFoundException
+ * @throws AccessDeniedException
+ */
+ public function guess($path)
+ {
+ if (!is_file($path)) {
+ throw new FileNotFoundException($path);
+ }
+
+ if (!is_readable($path)) {
+ throw new AccessDeniedException($path);
+ }
+
+ if (!$this->guessers) {
+ $msg = 'Unable to guess the mime type as no guessers are available';
+ if (!FileinfoMimeTypeGuesser::isSupported()) {
+ $msg .= ' (Did you enable the php_fileinfo extension?)';
+ }
+ throw new \LogicException($msg);
+ }
+
+ foreach ($this->guessers as $guesser) {
+ if (null !== $mimeType = $guesser->guess($path)) {
+ return $mimeType;
+ }
+ }
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php b/assets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php
new file mode 100644
index 0000000..f8c3ad2
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php
@@ -0,0 +1,35 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File\MimeType;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
+
+/**
+ * Guesses the mime type of a file.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+interface MimeTypeGuesserInterface
+{
+ /**
+ * Guesses the mime type of the file with the given path.
+ *
+ * @param string $path The path to the file
+ *
+ * @return string The mime type or NULL, if none could be guessed
+ *
+ * @throws FileNotFoundException If the file does not exist
+ * @throws AccessDeniedException If the file could not be read
+ */
+ public function guess($path);
+}
diff --git a/assets/php/vendor/symfony/http-foundation/File/Stream.php b/assets/php/vendor/symfony/http-foundation/File/Stream.php
new file mode 100644
index 0000000..69ae74c
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/File/Stream.php
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File;
+
+/**
+ * A PHP stream of unknown size.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class Stream extends File
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getSize()
+ {
+ return false;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/File/UploadedFile.php b/assets/php/vendor/symfony/http-foundation/File/UploadedFile.php
new file mode 100644
index 0000000..082d8d5
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/File/UploadedFile.php
@@ -0,0 +1,266 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileException;
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser;
+
+/**
+ * A file uploaded through a form.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ * @author Florian Eckerstorfer <florian@eckerstorfer.org>
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class UploadedFile extends File
+{
+ private $test = false;
+ private $originalName;
+ private $mimeType;
+ private $size;
+ private $error;
+
+ /**
+ * Accepts the information of the uploaded file as provided by the PHP global $_FILES.
+ *
+ * The file object is only created when the uploaded file is valid (i.e. when the
+ * isValid() method returns true). Otherwise the only methods that could be called
+ * on an UploadedFile instance are:
+ *
+ * * getClientOriginalName,
+ * * getClientMimeType,
+ * * isValid,
+ * * getError.
+ *
+ * Calling any other method on an non-valid instance will cause an unpredictable result.
+ *
+ * @param string $path The full temporary path to the file
+ * @param string $originalName The original file name of the uploaded file
+ * @param string|null $mimeType The type of the file as provided by PHP; null defaults to application/octet-stream
+ * @param int|null $size The file size provided by the uploader
+ * @param int|null $error The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants); null defaults to UPLOAD_ERR_OK
+ * @param bool $test Whether the test mode is active
+ * Local files are used in test mode hence the code should not enforce HTTP uploads
+ *
+ * @throws FileException If file_uploads is disabled
+ * @throws FileNotFoundException If the file does not exist
+ */
+ public function __construct($path, $originalName, $mimeType = null, $size = null, $error = null, $test = false)
+ {
+ $this->originalName = $this->getName($originalName);
+ $this->mimeType = $mimeType ?: 'application/octet-stream';
+ $this->size = $size;
+ $this->error = $error ?: UPLOAD_ERR_OK;
+ $this->test = (bool) $test;
+
+ parent::__construct($path, UPLOAD_ERR_OK === $this->error);
+ }
+
+ /**
+ * Returns the original file name.
+ *
+ * It is extracted from the request from which the file has been uploaded.
+ * Then it should not be considered as a safe value.
+ *
+ * @return string|null The original name
+ */
+ public function getClientOriginalName()
+ {
+ return $this->originalName;
+ }
+
+ /**
+ * Returns the original file extension.
+ *
+ * It is extracted from the original file name that was uploaded.
+ * Then it should not be considered as a safe value.
+ *
+ * @return string The extension
+ */
+ public function getClientOriginalExtension()
+ {
+ return pathinfo($this->originalName, PATHINFO_EXTENSION);
+ }
+
+ /**
+ * Returns the file mime type.
+ *
+ * The client mime type is extracted from the request from which the file
+ * was uploaded, so it should not be considered as a safe value.
+ *
+ * For a trusted mime type, use getMimeType() instead (which guesses the mime
+ * type based on the file content).
+ *
+ * @return string|null The mime type
+ *
+ * @see getMimeType()
+ */
+ public function getClientMimeType()
+ {
+ return $this->mimeType;
+ }
+
+ /**
+ * Returns the extension based on the client mime type.
+ *
+ * If the mime type is unknown, returns null.
+ *
+ * This method uses the mime type as guessed by getClientMimeType()
+ * to guess the file extension. As such, the extension returned
+ * by this method cannot be trusted.
+ *
+ * For a trusted extension, use guessExtension() instead (which guesses
+ * the extension based on the guessed mime type for the file).
+ *
+ * @return string|null The guessed extension or null if it cannot be guessed
+ *
+ * @see guessExtension()
+ * @see getClientMimeType()
+ */
+ public function guessClientExtension()
+ {
+ $type = $this->getClientMimeType();
+ $guesser = ExtensionGuesser::getInstance();
+
+ return $guesser->guess($type);
+ }
+
+ /**
+ * Returns the file size.
+ *
+ * It is extracted from the request from which the file has been uploaded.
+ * Then it should not be considered as a safe value.
+ *
+ * @return int|null The file size
+ */
+ public function getClientSize()
+ {
+ return $this->size;
+ }
+
+ /**
+ * Returns the upload error.
+ *
+ * If the upload was successful, the constant UPLOAD_ERR_OK is returned.
+ * Otherwise one of the other UPLOAD_ERR_XXX constants is returned.
+ *
+ * @return int The upload error
+ */
+ public function getError()
+ {
+ return $this->error;
+ }
+
+ /**
+ * Returns whether the file was uploaded successfully.
+ *
+ * @return bool True if the file has been uploaded with HTTP and no error occurred
+ */
+ public function isValid()
+ {
+ $isOk = UPLOAD_ERR_OK === $this->error;
+
+ return $this->test ? $isOk : $isOk && is_uploaded_file($this->getPathname());
+ }
+
+ /**
+ * Moves the file to a new location.
+ *
+ * @param string $directory The destination folder
+ * @param string $name The new file name
+ *
+ * @return File A File object representing the new file
+ *
+ * @throws FileException if, for any reason, the file could not have been moved
+ */
+ public function move($directory, $name = null)
+ {
+ if ($this->isValid()) {
+ if ($this->test) {
+ return parent::move($directory, $name);
+ }
+
+ $target = $this->getTargetFile($directory, $name);
+
+ if (!@move_uploaded_file($this->getPathname(), $target)) {
+ $error = error_get_last();
+ throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error['message'])));
+ }
+
+ @chmod($target, 0666 & ~umask());
+
+ return $target;
+ }
+
+ throw new FileException($this->getErrorMessage());
+ }
+
+ /**
+ * Returns the maximum size of an uploaded file as configured in php.ini.
+ *
+ * @return int The maximum size of an uploaded file in bytes
+ */
+ public static function getMaxFilesize()
+ {
+ $iniMax = strtolower(ini_get('upload_max_filesize'));
+
+ if ('' === $iniMax) {
+ return PHP_INT_MAX;
+ }
+
+ $max = ltrim($iniMax, '+');
+ if (0 === strpos($max, '0x')) {
+ $max = intval($max, 16);
+ } elseif (0 === strpos($max, '0')) {
+ $max = intval($max, 8);
+ } else {
+ $max = (int) $max;
+ }
+
+ switch (substr($iniMax, -1)) {
+ case 't': $max *= 1024;
+ // no break
+ case 'g': $max *= 1024;
+ // no break
+ case 'm': $max *= 1024;
+ // no break
+ case 'k': $max *= 1024;
+ }
+
+ return $max;
+ }
+
+ /**
+ * Returns an informative upload error message.
+ *
+ * @return string The error message regarding the specified error code
+ */
+ public function getErrorMessage()
+ {
+ static $errors = array(
+ UPLOAD_ERR_INI_SIZE => 'The file "%s" exceeds your upload_max_filesize ini directive (limit is %d KiB).',
+ UPLOAD_ERR_FORM_SIZE => 'The file "%s" exceeds the upload limit defined in your form.',
+ UPLOAD_ERR_PARTIAL => 'The file "%s" was only partially uploaded.',
+ UPLOAD_ERR_NO_FILE => 'No file was uploaded.',
+ UPLOAD_ERR_CANT_WRITE => 'The file "%s" could not be written on disk.',
+ UPLOAD_ERR_NO_TMP_DIR => 'File could not be uploaded: missing temporary directory.',
+ UPLOAD_ERR_EXTENSION => 'File upload was stopped by a PHP extension.',
+ );
+
+ $errorCode = $this->error;
+ $maxFilesize = UPLOAD_ERR_INI_SIZE === $errorCode ? self::getMaxFilesize() / 1024 : 0;
+ $message = isset($errors[$errorCode]) ? $errors[$errorCode] : 'The file "%s" was not uploaded due to an unknown error.';
+
+ return sprintf($message, $this->getClientOriginalName(), $maxFilesize);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/FileBag.php b/assets/php/vendor/symfony/http-foundation/FileBag.php
new file mode 100644
index 0000000..5edd0e6
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/FileBag.php
@@ -0,0 +1,144 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+
+/**
+ * FileBag is a container for uploaded files.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
+ */
+class FileBag extends ParameterBag
+{
+ private static $fileKeys = array('error', 'name', 'size', 'tmp_name', 'type');
+
+ /**
+ * @param array $parameters An array of HTTP files
+ */
+ public function __construct(array $parameters = array())
+ {
+ $this->replace($parameters);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function replace(array $files = array())
+ {
+ $this->parameters = array();
+ $this->add($files);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($key, $value)
+ {
+ if (!is_array($value) && !$value instanceof UploadedFile) {
+ throw new \InvalidArgumentException('An uploaded file must be an array or an instance of UploadedFile.');
+ }
+
+ parent::set($key, $this->convertFileInformation($value));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add(array $files = array())
+ {
+ foreach ($files as $key => $file) {
+ $this->set($key, $file);
+ }
+ }
+
+ /**
+ * Converts uploaded files to UploadedFile instances.
+ *
+ * @param array|UploadedFile $file A (multi-dimensional) array of uploaded file information
+ *
+ * @return UploadedFile[]|UploadedFile|null A (multi-dimensional) array of UploadedFile instances
+ */
+ protected function convertFileInformation($file)
+ {
+ if ($file instanceof UploadedFile) {
+ return $file;
+ }
+
+ $file = $this->fixPhpFilesArray($file);
+ if (is_array($file)) {
+ $keys = array_keys($file);
+ sort($keys);
+
+ if ($keys == self::$fileKeys) {
+ if (UPLOAD_ERR_NO_FILE == $file['error']) {
+ $file = null;
+ } else {
+ $file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['size'], $file['error']);
+ }
+ } else {
+ $file = array_map(array($this, 'convertFileInformation'), $file);
+ if (array_keys($keys) === $keys) {
+ $file = array_filter($file);
+ }
+ }
+ }
+
+ return $file;
+ }
+
+ /**
+ * Fixes a malformed PHP $_FILES array.
+ *
+ * PHP has a bug that the format of the $_FILES array differs, depending on
+ * whether the uploaded file fields had normal field names or array-like
+ * field names ("normal" vs. "parent[child]").
+ *
+ * This method fixes the array to look like the "normal" $_FILES array.
+ *
+ * It's safe to pass an already converted array, in which case this method
+ * just returns the original array unmodified.
+ *
+ * @return array
+ */
+ protected function fixPhpFilesArray($data)
+ {
+ if (!is_array($data)) {
+ return $data;
+ }
+
+ $keys = array_keys($data);
+ sort($keys);
+
+ if (self::$fileKeys != $keys || !isset($data['name']) || !is_array($data['name'])) {
+ return $data;
+ }
+
+ $files = $data;
+ foreach (self::$fileKeys as $k) {
+ unset($files[$k]);
+ }
+
+ foreach ($data['name'] as $key => $name) {
+ $files[$key] = $this->fixPhpFilesArray(array(
+ 'error' => $data['error'][$key],
+ 'name' => $name,
+ 'type' => $data['type'][$key],
+ 'tmp_name' => $data['tmp_name'][$key],
+ 'size' => $data['size'][$key],
+ ));
+ }
+
+ return $files;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/HeaderBag.php b/assets/php/vendor/symfony/http-foundation/HeaderBag.php
new file mode 100644
index 0000000..7aaa52a
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/HeaderBag.php
@@ -0,0 +1,331 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * HeaderBag is a container for HTTP headers.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class HeaderBag implements \IteratorAggregate, \Countable
+{
+ protected $headers = array();
+ protected $cacheControl = array();
+
+ /**
+ * @param array $headers An array of HTTP headers
+ */
+ public function __construct(array $headers = array())
+ {
+ foreach ($headers as $key => $values) {
+ $this->set($key, $values);
+ }
+ }
+
+ /**
+ * Returns the headers as a string.
+ *
+ * @return string The headers
+ */
+ public function __toString()
+ {
+ if (!$headers = $this->all()) {
+ return '';
+ }
+
+ ksort($headers);
+ $max = max(array_map('strlen', array_keys($headers))) + 1;
+ $content = '';
+ foreach ($headers as $name => $values) {
+ $name = implode('-', array_map('ucfirst', explode('-', $name)));
+ foreach ($values as $value) {
+ $content .= sprintf("%-{$max}s %s\r\n", $name.':', $value);
+ }
+ }
+
+ return $content;
+ }
+
+ /**
+ * Returns the headers.
+ *
+ * @return array An array of headers
+ */
+ public function all()
+ {
+ return $this->headers;
+ }
+
+ /**
+ * Returns the parameter keys.
+ *
+ * @return array An array of parameter keys
+ */
+ public function keys()
+ {
+ return array_keys($this->all());
+ }
+
+ /**
+ * Replaces the current HTTP headers by a new set.
+ *
+ * @param array $headers An array of HTTP headers
+ */
+ public function replace(array $headers = array())
+ {
+ $this->headers = array();
+ $this->add($headers);
+ }
+
+ /**
+ * Adds new headers the current HTTP headers set.
+ *
+ * @param array $headers An array of HTTP headers
+ */
+ public function add(array $headers)
+ {
+ foreach ($headers as $key => $values) {
+ $this->set($key, $values);
+ }
+ }
+
+ /**
+ * Returns a header value by name.
+ *
+ * @param string $key The header name
+ * @param string|string[] $default The default value
+ * @param bool $first Whether to return the first value or all header values
+ *
+ * @return string|string[] The first header value or default value if $first is true, an array of values otherwise
+ */
+ public function get($key, $default = null, $first = true)
+ {
+ $key = str_replace('_', '-', strtolower($key));
+ $headers = $this->all();
+
+ if (!array_key_exists($key, $headers)) {
+ if (null === $default) {
+ return $first ? null : array();
+ }
+
+ return $first ? $default : array($default);
+ }
+
+ if ($first) {
+ return \count($headers[$key]) ? $headers[$key][0] : $default;
+ }
+
+ return $headers[$key];
+ }
+
+ /**
+ * Sets a header by name.
+ *
+ * @param string $key The key
+ * @param string|string[] $values The value or an array of values
+ * @param bool $replace Whether to replace the actual value or not (true by default)
+ */
+ public function set($key, $values, $replace = true)
+ {
+ $key = str_replace('_', '-', strtolower($key));
+
+ if (\is_array($values)) {
+ $values = array_values($values);
+
+ if (true === $replace || !isset($this->headers[$key])) {
+ $this->headers[$key] = $values;
+ } else {
+ $this->headers[$key] = array_merge($this->headers[$key], $values);
+ }
+ } else {
+ if (true === $replace || !isset($this->headers[$key])) {
+ $this->headers[$key] = array($values);
+ } else {
+ $this->headers[$key][] = $values;
+ }
+ }
+
+ if ('cache-control' === $key) {
+ $this->cacheControl = $this->parseCacheControl(implode(', ', $this->headers[$key]));
+ }
+ }
+
+ /**
+ * Returns true if the HTTP header is defined.
+ *
+ * @param string $key The HTTP header
+ *
+ * @return bool true if the parameter exists, false otherwise
+ */
+ public function has($key)
+ {
+ return array_key_exists(str_replace('_', '-', strtolower($key)), $this->all());
+ }
+
+ /**
+ * Returns true if the given HTTP header contains the given value.
+ *
+ * @param string $key The HTTP header name
+ * @param string $value The HTTP value
+ *
+ * @return bool true if the value is contained in the header, false otherwise
+ */
+ public function contains($key, $value)
+ {
+ return in_array($value, $this->get($key, null, false));
+ }
+
+ /**
+ * Removes a header.
+ *
+ * @param string $key The HTTP header name
+ */
+ public function remove($key)
+ {
+ $key = str_replace('_', '-', strtolower($key));
+
+ unset($this->headers[$key]);
+
+ if ('cache-control' === $key) {
+ $this->cacheControl = array();
+ }
+ }
+
+ /**
+ * Returns the HTTP header value converted to a date.
+ *
+ * @param string $key The parameter key
+ * @param \DateTime $default The default value
+ *
+ * @return null|\DateTime The parsed DateTime or the default value if the header does not exist
+ *
+ * @throws \RuntimeException When the HTTP header is not parseable
+ */
+ public function getDate($key, \DateTime $default = null)
+ {
+ if (null === $value = $this->get($key)) {
+ return $default;
+ }
+
+ if (false === $date = \DateTime::createFromFormat(DATE_RFC2822, $value)) {
+ throw new \RuntimeException(sprintf('The %s HTTP header is not parseable (%s).', $key, $value));
+ }
+
+ return $date;
+ }
+
+ /**
+ * Adds a custom Cache-Control directive.
+ *
+ * @param string $key The Cache-Control directive name
+ * @param mixed $value The Cache-Control directive value
+ */
+ public function addCacheControlDirective($key, $value = true)
+ {
+ $this->cacheControl[$key] = $value;
+
+ $this->set('Cache-Control', $this->getCacheControlHeader());
+ }
+
+ /**
+ * Returns true if the Cache-Control directive is defined.
+ *
+ * @param string $key The Cache-Control directive
+ *
+ * @return bool true if the directive exists, false otherwise
+ */
+ public function hasCacheControlDirective($key)
+ {
+ return array_key_exists($key, $this->cacheControl);
+ }
+
+ /**
+ * Returns a Cache-Control directive value by name.
+ *
+ * @param string $key The directive name
+ *
+ * @return mixed|null The directive value if defined, null otherwise
+ */
+ public function getCacheControlDirective($key)
+ {
+ return array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null;
+ }
+
+ /**
+ * Removes a Cache-Control directive.
+ *
+ * @param string $key The Cache-Control directive
+ */
+ public function removeCacheControlDirective($key)
+ {
+ unset($this->cacheControl[$key]);
+
+ $this->set('Cache-Control', $this->getCacheControlHeader());
+ }
+
+ /**
+ * Returns an iterator for headers.
+ *
+ * @return \ArrayIterator An \ArrayIterator instance
+ */
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->headers);
+ }
+
+ /**
+ * Returns the number of headers.
+ *
+ * @return int The number of headers
+ */
+ public function count()
+ {
+ return count($this->headers);
+ }
+
+ protected function getCacheControlHeader()
+ {
+ $parts = array();
+ ksort($this->cacheControl);
+ foreach ($this->cacheControl as $key => $value) {
+ if (true === $value) {
+ $parts[] = $key;
+ } else {
+ if (preg_match('#[^a-zA-Z0-9._-]#', $value)) {
+ $value = '"'.$value.'"';
+ }
+
+ $parts[] = "$key=$value";
+ }
+ }
+
+ return implode(', ', $parts);
+ }
+
+ /**
+ * Parses a Cache-Control HTTP header.
+ *
+ * @param string $header The value of the Cache-Control HTTP header
+ *
+ * @return array An array representing the attribute values
+ */
+ protected function parseCacheControl($header)
+ {
+ $cacheControl = array();
+ preg_match_all('#([a-zA-Z][a-zA-Z_-]*)\s*(?:=(?:"([^"]*)"|([^ \t",;]*)))?#', $header, $matches, PREG_SET_ORDER);
+ foreach ($matches as $match) {
+ $cacheControl[strtolower($match[1])] = isset($match[3]) ? $match[3] : (isset($match[2]) ? $match[2] : true);
+ }
+
+ return $cacheControl;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/IpUtils.php b/assets/php/vendor/symfony/http-foundation/IpUtils.php
new file mode 100644
index 0000000..86d135b
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/IpUtils.php
@@ -0,0 +1,156 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Http utility functions.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class IpUtils
+{
+ private static $checkedIps = array();
+
+ /**
+ * This class should not be instantiated.
+ */
+ private function __construct()
+ {
+ }
+
+ /**
+ * Checks if an IPv4 or IPv6 address is contained in the list of given IPs or subnets.
+ *
+ * @param string $requestIp IP to check
+ * @param string|array $ips List of IPs or subnets (can be a string if only a single one)
+ *
+ * @return bool Whether the IP is valid
+ */
+ public static function checkIp($requestIp, $ips)
+ {
+ if (!is_array($ips)) {
+ $ips = array($ips);
+ }
+
+ $method = substr_count($requestIp, ':') > 1 ? 'checkIp6' : 'checkIp4';
+
+ foreach ($ips as $ip) {
+ if (self::$method($requestIp, $ip)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Compares two IPv4 addresses.
+ * In case a subnet is given, it checks if it contains the request IP.
+ *
+ * @param string $requestIp IPv4 address to check
+ * @param string $ip IPv4 address or subnet in CIDR notation
+ *
+ * @return bool Whether the request IP matches the IP, or whether the request IP is within the CIDR subnet
+ */
+ public static function checkIp4($requestIp, $ip)
+ {
+ $cacheKey = $requestIp.'-'.$ip;
+ if (isset(self::$checkedIps[$cacheKey])) {
+ return self::$checkedIps[$cacheKey];
+ }
+
+ if (!filter_var($requestIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+
+ if (false !== strpos($ip, '/')) {
+ list($address, $netmask) = explode('/', $ip, 2);
+
+ if ('0' === $netmask) {
+ return self::$checkedIps[$cacheKey] = filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
+ }
+
+ if ($netmask < 0 || $netmask > 32) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+ } else {
+ $address = $ip;
+ $netmask = 32;
+ }
+
+ if (false === ip2long($address)) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+
+ return self::$checkedIps[$cacheKey] = 0 === substr_compare(sprintf('%032b', ip2long($requestIp)), sprintf('%032b', ip2long($address)), 0, $netmask);
+ }
+
+ /**
+ * Compares two IPv6 addresses.
+ * In case a subnet is given, it checks if it contains the request IP.
+ *
+ * @author David Soria Parra <dsp at php dot net>
+ *
+ * @see https://github.com/dsp/v6tools
+ *
+ * @param string $requestIp IPv6 address to check
+ * @param string $ip IPv6 address or subnet in CIDR notation
+ *
+ * @return bool Whether the IP is valid
+ *
+ * @throws \RuntimeException When IPV6 support is not enabled
+ */
+ public static function checkIp6($requestIp, $ip)
+ {
+ $cacheKey = $requestIp.'-'.$ip;
+ if (isset(self::$checkedIps[$cacheKey])) {
+ return self::$checkedIps[$cacheKey];
+ }
+
+ if (!((extension_loaded('sockets') && defined('AF_INET6')) || @inet_pton('::1'))) {
+ throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".');
+ }
+
+ if (false !== strpos($ip, '/')) {
+ list($address, $netmask) = explode('/', $ip, 2);
+
+ if ('0' === $netmask) {
+ return (bool) unpack('n*', @inet_pton($address));
+ }
+
+ if ($netmask < 1 || $netmask > 128) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+ } else {
+ $address = $ip;
+ $netmask = 128;
+ }
+
+ $bytesAddr = unpack('n*', @inet_pton($address));
+ $bytesTest = unpack('n*', @inet_pton($requestIp));
+
+ if (!$bytesAddr || !$bytesTest) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+
+ for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; ++$i) {
+ $left = $netmask - 16 * ($i - 1);
+ $left = ($left <= 16) ? $left : 16;
+ $mask = ~(0xffff >> $left) & 0xffff;
+ if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+ }
+
+ return self::$checkedIps[$cacheKey] = true;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/JsonResponse.php b/assets/php/vendor/symfony/http-foundation/JsonResponse.php
new file mode 100644
index 0000000..137ac33
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/JsonResponse.php
@@ -0,0 +1,220 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Response represents an HTTP response in JSON format.
+ *
+ * Note that this class does not force the returned JSON content to be an
+ * object. It is however recommended that you do return an object as it
+ * protects yourself against XSSI and JSON-JavaScript Hijacking.
+ *
+ * @see https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside
+ *
+ * @author Igor Wiedler <igor@wiedler.ch>
+ */
+class JsonResponse extends Response
+{
+ protected $data;
+ protected $callback;
+
+ // Encode <, >, ', &, and " characters in the JSON, making it also safe to be embedded into HTML.
+ // 15 === JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT
+ const DEFAULT_ENCODING_OPTIONS = 15;
+
+ protected $encodingOptions = self::DEFAULT_ENCODING_OPTIONS;
+
+ /**
+ * @param mixed $data The response data
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ * @param bool $json If the data is already a JSON string
+ */
+ public function __construct($data = null, $status = 200, $headers = array(), $json = false)
+ {
+ parent::__construct('', $status, $headers);
+
+ if (null === $data) {
+ $data = new \ArrayObject();
+ }
+
+ $json ? $this->setJson($data) : $this->setData($data);
+ }
+
+ /**
+ * Factory method for chainability.
+ *
+ * Example:
+ *
+ * return JsonResponse::create($data, 200)
+ * ->setSharedMaxAge(300);
+ *
+ * @param mixed $data The json response data
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ *
+ * @return static
+ */
+ public static function create($data = null, $status = 200, $headers = array())
+ {
+ return new static($data, $status, $headers);
+ }
+
+ /**
+ * Make easier the creation of JsonResponse from raw json.
+ */
+ public static function fromJsonString($data = null, $status = 200, $headers = array())
+ {
+ return new static($data, $status, $headers, true);
+ }
+
+ /**
+ * Sets the JSONP callback.
+ *
+ * @param string|null $callback The JSONP callback or null to use none
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException When the callback name is not valid
+ */
+ public function setCallback($callback = null)
+ {
+ if (null !== $callback) {
+ // partially taken from http://www.geekality.net/2011/08/03/valid-javascript-identifier/
+ // partially taken from https://github.com/willdurand/JsonpCallbackValidator
+ // JsonpCallbackValidator is released under the MIT License. See https://github.com/willdurand/JsonpCallbackValidator/blob/v1.1.0/LICENSE for details.
+ // (c) William Durand <william.durand1@gmail.com>
+ $pattern = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*(?:\[(?:"(?:\\\.|[^"\\\])*"|\'(?:\\\.|[^\'\\\])*\'|\d+)\])*?$/u';
+ $reserved = array(
+ 'break', 'do', 'instanceof', 'typeof', 'case', 'else', 'new', 'var', 'catch', 'finally', 'return', 'void', 'continue', 'for', 'switch', 'while',
+ 'debugger', 'function', 'this', 'with', 'default', 'if', 'throw', 'delete', 'in', 'try', 'class', 'enum', 'extends', 'super', 'const', 'export',
+ 'import', 'implements', 'let', 'private', 'public', 'yield', 'interface', 'package', 'protected', 'static', 'null', 'true', 'false',
+ );
+ $parts = explode('.', $callback);
+ foreach ($parts as $part) {
+ if (!preg_match($pattern, $part) || in_array($part, $reserved, true)) {
+ throw new \InvalidArgumentException('The callback name is not valid.');
+ }
+ }
+ }
+
+ $this->callback = $callback;
+
+ return $this->update();
+ }
+
+ /**
+ * Sets a raw string containing a JSON document to be sent.
+ *
+ * @param string $json
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setJson($json)
+ {
+ $this->data = $json;
+
+ return $this->update();
+ }
+
+ /**
+ * Sets the data to be sent as JSON.
+ *
+ * @param mixed $data
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setData($data = array())
+ {
+ if (defined('HHVM_VERSION')) {
+ // HHVM does not trigger any warnings and let exceptions
+ // thrown from a JsonSerializable object pass through.
+ // If only PHP did the same...
+ $data = json_encode($data, $this->encodingOptions);
+ } else {
+ if (!interface_exists('JsonSerializable', false)) {
+ set_error_handler(function () { return false; });
+ try {
+ $data = @json_encode($data, $this->encodingOptions);
+ } finally {
+ restore_error_handler();
+ }
+ } else {
+ try {
+ $data = json_encode($data, $this->encodingOptions);
+ } catch (\Exception $e) {
+ if ('Exception' === get_class($e) && 0 === strpos($e->getMessage(), 'Failed calling ')) {
+ throw $e->getPrevious() ?: $e;
+ }
+ throw $e;
+ }
+ }
+ }
+
+ if (JSON_ERROR_NONE !== json_last_error()) {
+ throw new \InvalidArgumentException(json_last_error_msg());
+ }
+
+ return $this->setJson($data);
+ }
+
+ /**
+ * Returns options used while encoding data to JSON.
+ *
+ * @return int
+ */
+ public function getEncodingOptions()
+ {
+ return $this->encodingOptions;
+ }
+
+ /**
+ * Sets options used while encoding data to JSON.
+ *
+ * @param int $encodingOptions
+ *
+ * @return $this
+ */
+ public function setEncodingOptions($encodingOptions)
+ {
+ $this->encodingOptions = (int) $encodingOptions;
+
+ return $this->setData(json_decode($this->data));
+ }
+
+ /**
+ * Updates the content and headers according to the JSON data and callback.
+ *
+ * @return $this
+ */
+ protected function update()
+ {
+ if (null !== $this->callback) {
+ // Not using application/javascript for compatibility reasons with older browsers.
+ $this->headers->set('Content-Type', 'text/javascript');
+
+ return $this->setContent(sprintf('/**/%s(%s);', $this->callback, $this->data));
+ }
+
+ // Only set the header when there is none or when it equals 'text/javascript' (from a previous update with callback)
+ // in order to not overwrite a custom definition.
+ if (!$this->headers->has('Content-Type') || 'text/javascript' === $this->headers->get('Content-Type')) {
+ $this->headers->set('Content-Type', 'application/json');
+ }
+
+ return $this->setContent($this->data);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/LICENSE b/assets/php/vendor/symfony/http-foundation/LICENSE
new file mode 100644
index 0000000..21d7fb9
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2018 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/assets/php/vendor/symfony/http-foundation/ParameterBag.php b/assets/php/vendor/symfony/http-foundation/ParameterBag.php
new file mode 100644
index 0000000..257ef8b
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/ParameterBag.php
@@ -0,0 +1,234 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * ParameterBag is a container for key/value pairs.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ParameterBag implements \IteratorAggregate, \Countable
+{
+ /**
+ * Parameter storage.
+ */
+ protected $parameters;
+
+ /**
+ * @param array $parameters An array of parameters
+ */
+ public function __construct(array $parameters = array())
+ {
+ $this->parameters = $parameters;
+ }
+
+ /**
+ * Returns the parameters.
+ *
+ * @return array An array of parameters
+ */
+ public function all()
+ {
+ return $this->parameters;
+ }
+
+ /**
+ * Returns the parameter keys.
+ *
+ * @return array An array of parameter keys
+ */
+ public function keys()
+ {
+ return array_keys($this->parameters);
+ }
+
+ /**
+ * Replaces the current parameters by a new set.
+ *
+ * @param array $parameters An array of parameters
+ */
+ public function replace(array $parameters = array())
+ {
+ $this->parameters = $parameters;
+ }
+
+ /**
+ * Adds parameters.
+ *
+ * @param array $parameters An array of parameters
+ */
+ public function add(array $parameters = array())
+ {
+ $this->parameters = array_replace($this->parameters, $parameters);
+ }
+
+ /**
+ * Returns a parameter by name.
+ *
+ * @param string $key The key
+ * @param mixed $default The default value if the parameter key does not exist
+ *
+ * @return mixed
+ */
+ public function get($key, $default = null)
+ {
+ return array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
+ }
+
+ /**
+ * Sets a parameter by name.
+ *
+ * @param string $key The key
+ * @param mixed $value The value
+ */
+ public function set($key, $value)
+ {
+ $this->parameters[$key] = $value;
+ }
+
+ /**
+ * Returns true if the parameter is defined.
+ *
+ * @param string $key The key
+ *
+ * @return bool true if the parameter exists, false otherwise
+ */
+ public function has($key)
+ {
+ return array_key_exists($key, $this->parameters);
+ }
+
+ /**
+ * Removes a parameter.
+ *
+ * @param string $key The key
+ */
+ public function remove($key)
+ {
+ unset($this->parameters[$key]);
+ }
+
+ /**
+ * Returns the alphabetic characters of the parameter value.
+ *
+ * @param string $key The parameter key
+ * @param string $default The default value if the parameter key does not exist
+ *
+ * @return string The filtered value
+ */
+ public function getAlpha($key, $default = '')
+ {
+ return preg_replace('/[^[:alpha:]]/', '', $this->get($key, $default));
+ }
+
+ /**
+ * Returns the alphabetic characters and digits of the parameter value.
+ *
+ * @param string $key The parameter key
+ * @param string $default The default value if the parameter key does not exist
+ *
+ * @return string The filtered value
+ */
+ public function getAlnum($key, $default = '')
+ {
+ return preg_replace('/[^[:alnum:]]/', '', $this->get($key, $default));
+ }
+
+ /**
+ * Returns the digits of the parameter value.
+ *
+ * @param string $key The parameter key
+ * @param string $default The default value if the parameter key does not exist
+ *
+ * @return string The filtered value
+ */
+ public function getDigits($key, $default = '')
+ {
+ // we need to remove - and + because they're allowed in the filter
+ return str_replace(array('-', '+'), '', $this->filter($key, $default, FILTER_SANITIZE_NUMBER_INT));
+ }
+
+ /**
+ * Returns the parameter value converted to integer.
+ *
+ * @param string $key The parameter key
+ * @param int $default The default value if the parameter key does not exist
+ *
+ * @return int The filtered value
+ */
+ public function getInt($key, $default = 0)
+ {
+ return (int) $this->get($key, $default);
+ }
+
+ /**
+ * Returns the parameter value converted to boolean.
+ *
+ * @param string $key The parameter key
+ * @param mixed $default The default value if the parameter key does not exist
+ *
+ * @return bool The filtered value
+ */
+ public function getBoolean($key, $default = false)
+ {
+ return $this->filter($key, $default, FILTER_VALIDATE_BOOLEAN);
+ }
+
+ /**
+ * Filter key.
+ *
+ * @param string $key Key
+ * @param mixed $default Default = null
+ * @param int $filter FILTER_* constant
+ * @param mixed $options Filter options
+ *
+ * @see http://php.net/manual/en/function.filter-var.php
+ *
+ * @return mixed
+ */
+ public function filter($key, $default = null, $filter = FILTER_DEFAULT, $options = array())
+ {
+ $value = $this->get($key, $default);
+
+ // Always turn $options into an array - this allows filter_var option shortcuts.
+ if (!is_array($options) && $options) {
+ $options = array('flags' => $options);
+ }
+
+ // Add a convenience check for arrays.
+ if (is_array($value) && !isset($options['flags'])) {
+ $options['flags'] = FILTER_REQUIRE_ARRAY;
+ }
+
+ return filter_var($value, $filter, $options);
+ }
+
+ /**
+ * Returns an iterator for parameters.
+ *
+ * @return \ArrayIterator An \ArrayIterator instance
+ */
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->parameters);
+ }
+
+ /**
+ * Returns the number of parameters.
+ *
+ * @return int The number of parameters
+ */
+ public function count()
+ {
+ return count($this->parameters);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/README.md b/assets/php/vendor/symfony/http-foundation/README.md
new file mode 100644
index 0000000..8907f0b
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/README.md
@@ -0,0 +1,14 @@
+HttpFoundation Component
+========================
+
+The HttpFoundation component defines an object-oriented layer for the HTTP
+specification.
+
+Resources
+---------
+
+ * [Documentation](https://symfony.com/doc/current/components/http_foundation/index.html)
+ * [Contributing](https://symfony.com/doc/current/contributing/index.html)
+ * [Report issues](https://github.com/symfony/symfony/issues) and
+ [send Pull Requests](https://github.com/symfony/symfony/pulls)
+ in the [main Symfony repository](https://github.com/symfony/symfony)
diff --git a/assets/php/vendor/symfony/http-foundation/RedirectResponse.php b/assets/php/vendor/symfony/http-foundation/RedirectResponse.php
new file mode 100644
index 0000000..01681dc
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/RedirectResponse.php
@@ -0,0 +1,109 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * RedirectResponse represents an HTTP response doing a redirect.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class RedirectResponse extends Response
+{
+ protected $targetUrl;
+
+ /**
+ * Creates a redirect response so that it conforms to the rules defined for a redirect status code.
+ *
+ * @param string $url The URL to redirect to. The URL should be a full URL, with schema etc.,
+ * but practically every browser redirects on paths only as well
+ * @param int $status The status code (302 by default)
+ * @param array $headers The headers (Location is always set to the given URL)
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @see http://tools.ietf.org/html/rfc2616#section-10.3
+ */
+ public function __construct($url, $status = 302, $headers = array())
+ {
+ parent::__construct('', $status, $headers);
+
+ $this->setTargetUrl($url);
+
+ if (!$this->isRedirect()) {
+ throw new \InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $status));
+ }
+
+ if (301 == $status && !array_key_exists('cache-control', $headers)) {
+ $this->headers->remove('cache-control');
+ }
+ }
+
+ /**
+ * Factory method for chainability.
+ *
+ * @param string $url The url to redirect to
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ *
+ * @return static
+ */
+ public static function create($url = '', $status = 302, $headers = array())
+ {
+ return new static($url, $status, $headers);
+ }
+
+ /**
+ * Returns the target URL.
+ *
+ * @return string target URL
+ */
+ public function getTargetUrl()
+ {
+ return $this->targetUrl;
+ }
+
+ /**
+ * Sets the redirect target of this response.
+ *
+ * @param string $url The URL to redirect to
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setTargetUrl($url)
+ {
+ if (empty($url)) {
+ throw new \InvalidArgumentException('Cannot redirect to an empty URL.');
+ }
+
+ $this->targetUrl = $url;
+
+ $this->setContent(
+ sprintf('<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="UTF-8" />
+ <meta http-equiv="refresh" content="0;url=%1$s" />
+
+ <title>Redirecting to %1$s</title>
+ </head>
+ <body>
+ Redirecting to <a href="%1$s">%1$s</a>.
+ </body>
+</html>', htmlspecialchars($url, ENT_QUOTES, 'UTF-8')));
+
+ $this->headers->set('Location', $url);
+
+ return $this;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Request.php b/assets/php/vendor/symfony/http-foundation/Request.php
new file mode 100644
index 0000000..164fb4e
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Request.php
@@ -0,0 +1,2154 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException;
+use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
+use Symfony\Component\HttpFoundation\Session\SessionInterface;
+
+/**
+ * Request represents an HTTP request.
+ *
+ * The methods dealing with URL accept / return a raw path (% encoded):
+ * * getBasePath
+ * * getBaseUrl
+ * * getPathInfo
+ * * getRequestUri
+ * * getUri
+ * * getUriForPath
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class Request
+{
+ const HEADER_FORWARDED = 0b00001; // When using RFC 7239
+ const HEADER_X_FORWARDED_FOR = 0b00010;
+ const HEADER_X_FORWARDED_HOST = 0b00100;
+ const HEADER_X_FORWARDED_PROTO = 0b01000;
+ const HEADER_X_FORWARDED_PORT = 0b10000;
+ const HEADER_X_FORWARDED_ALL = 0b11110; // All "X-Forwarded-*" headers
+ const HEADER_X_FORWARDED_AWS_ELB = 0b11010; // AWS ELB doesn't send X-Forwarded-Host
+
+ /** @deprecated since version 3.3, to be removed in 4.0 */
+ const HEADER_CLIENT_IP = self::HEADER_X_FORWARDED_FOR;
+ /** @deprecated since version 3.3, to be removed in 4.0 */
+ const HEADER_CLIENT_HOST = self::HEADER_X_FORWARDED_HOST;
+ /** @deprecated since version 3.3, to be removed in 4.0 */
+ const HEADER_CLIENT_PROTO = self::HEADER_X_FORWARDED_PROTO;
+ /** @deprecated since version 3.3, to be removed in 4.0 */
+ const HEADER_CLIENT_PORT = self::HEADER_X_FORWARDED_PORT;
+
+ const METHOD_HEAD = 'HEAD';
+ const METHOD_GET = 'GET';
+ const METHOD_POST = 'POST';
+ const METHOD_PUT = 'PUT';
+ const METHOD_PATCH = 'PATCH';
+ const METHOD_DELETE = 'DELETE';
+ const METHOD_PURGE = 'PURGE';
+ const METHOD_OPTIONS = 'OPTIONS';
+ const METHOD_TRACE = 'TRACE';
+ const METHOD_CONNECT = 'CONNECT';
+
+ /**
+ * @var string[]
+ */
+ protected static $trustedProxies = array();
+
+ /**
+ * @var string[]
+ */
+ protected static $trustedHostPatterns = array();
+
+ /**
+ * @var string[]
+ */
+ protected static $trustedHosts = array();
+
+ /**
+ * Names for headers that can be trusted when
+ * using trusted proxies.
+ *
+ * The FORWARDED header is the standard as of rfc7239.
+ *
+ * The other headers are non-standard, but widely used
+ * by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
+ *
+ * @deprecated since version 3.3, to be removed in 4.0
+ */
+ protected static $trustedHeaders = array(
+ self::HEADER_FORWARDED => 'FORWARDED',
+ self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
+ self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
+ self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
+ self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
+ );
+
+ protected static $httpMethodParameterOverride = false;
+
+ /**
+ * Custom parameters.
+ *
+ * @var \Symfony\Component\HttpFoundation\ParameterBag
+ */
+ public $attributes;
+
+ /**
+ * Request body parameters ($_POST).
+ *
+ * @var \Symfony\Component\HttpFoundation\ParameterBag
+ */
+ public $request;
+
+ /**
+ * Query string parameters ($_GET).
+ *
+ * @var \Symfony\Component\HttpFoundation\ParameterBag
+ */
+ public $query;
+
+ /**
+ * Server and execution environment parameters ($_SERVER).
+ *
+ * @var \Symfony\Component\HttpFoundation\ServerBag
+ */
+ public $server;
+
+ /**
+ * Uploaded files ($_FILES).
+ *
+ * @var \Symfony\Component\HttpFoundation\FileBag
+ */
+ public $files;
+
+ /**
+ * Cookies ($_COOKIE).
+ *
+ * @var \Symfony\Component\HttpFoundation\ParameterBag
+ */
+ public $cookies;
+
+ /**
+ * Headers (taken from the $_SERVER).
+ *
+ * @var \Symfony\Component\HttpFoundation\HeaderBag
+ */
+ public $headers;
+
+ /**
+ * @var string|resource|false|null
+ */
+ protected $content;
+
+ /**
+ * @var array
+ */
+ protected $languages;
+
+ /**
+ * @var array
+ */
+ protected $charsets;
+
+ /**
+ * @var array
+ */
+ protected $encodings;
+
+ /**
+ * @var array
+ */
+ protected $acceptableContentTypes;
+
+ /**
+ * @var string
+ */
+ protected $pathInfo;
+
+ /**
+ * @var string
+ */
+ protected $requestUri;
+
+ /**
+ * @var string
+ */
+ protected $baseUrl;
+
+ /**
+ * @var string
+ */
+ protected $basePath;
+
+ /**
+ * @var string
+ */
+ protected $method;
+
+ /**
+ * @var string
+ */
+ protected $format;
+
+ /**
+ * @var \Symfony\Component\HttpFoundation\Session\SessionInterface
+ */
+ protected $session;
+
+ /**
+ * @var string
+ */
+ protected $locale;
+
+ /**
+ * @var string
+ */
+ protected $defaultLocale = 'en';
+
+ /**
+ * @var array
+ */
+ protected static $formats;
+
+ protected static $requestFactory;
+
+ private $isHostValid = true;
+ private $isForwardedValid = true;
+
+ private static $trustedHeaderSet = -1;
+
+ /** @deprecated since version 3.3, to be removed in 4.0 */
+ private static $trustedHeaderNames = array(
+ self::HEADER_FORWARDED => 'FORWARDED',
+ self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
+ self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
+ self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
+ self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
+ );
+
+ private static $forwardedParams = array(
+ self::HEADER_X_FORWARDED_FOR => 'for',
+ self::HEADER_X_FORWARDED_HOST => 'host',
+ self::HEADER_X_FORWARDED_PROTO => 'proto',
+ self::HEADER_X_FORWARDED_PORT => 'host',
+ );
+
+ /**
+ * @param array $query The GET parameters
+ * @param array $request The POST parameters
+ * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
+ * @param array $cookies The COOKIE parameters
+ * @param array $files The FILES parameters
+ * @param array $server The SERVER parameters
+ * @param string|resource|null $content The raw body data
+ */
+ public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
+ {
+ $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content);
+ }
+
+ /**
+ * Sets the parameters for this request.
+ *
+ * This method also re-initializes all properties.
+ *
+ * @param array $query The GET parameters
+ * @param array $request The POST parameters
+ * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
+ * @param array $cookies The COOKIE parameters
+ * @param array $files The FILES parameters
+ * @param array $server The SERVER parameters
+ * @param string|resource|null $content The raw body data
+ */
+ public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
+ {
+ $this->request = new ParameterBag($request);
+ $this->query = new ParameterBag($query);
+ $this->attributes = new ParameterBag($attributes);
+ $this->cookies = new ParameterBag($cookies);
+ $this->files = new FileBag($files);
+ $this->server = new ServerBag($server);
+ $this->headers = new HeaderBag($this->server->getHeaders());
+
+ $this->content = $content;
+ $this->languages = null;
+ $this->charsets = null;
+ $this->encodings = null;
+ $this->acceptableContentTypes = null;
+ $this->pathInfo = null;
+ $this->requestUri = null;
+ $this->baseUrl = null;
+ $this->basePath = null;
+ $this->method = null;
+ $this->format = null;
+ }
+
+ /**
+ * Creates a new request with values from PHP's super globals.
+ *
+ * @return static
+ */
+ public static function createFromGlobals()
+ {
+ // With the php's bug #66606, the php's built-in web server
+ // stores the Content-Type and Content-Length header values in
+ // HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH fields.
+ $server = $_SERVER;
+ if ('cli-server' === PHP_SAPI) {
+ if (array_key_exists('HTTP_CONTENT_LENGTH', $_SERVER)) {
+ $server['CONTENT_LENGTH'] = $_SERVER['HTTP_CONTENT_LENGTH'];
+ }
+ if (array_key_exists('HTTP_CONTENT_TYPE', $_SERVER)) {
+ $server['CONTENT_TYPE'] = $_SERVER['HTTP_CONTENT_TYPE'];
+ }
+ }
+
+ $request = self::createRequestFromFactory($_GET, $_POST, array(), $_COOKIE, $_FILES, $server);
+
+ if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
+ && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH'))
+ ) {
+ parse_str($request->getContent(), $data);
+ $request->request = new ParameterBag($data);
+ }
+
+ return $request;
+ }
+
+ /**
+ * Creates a Request based on a given URI and configuration.
+ *
+ * The information contained in the URI always take precedence
+ * over the other information (server and parameters).
+ *
+ * @param string $uri The URI
+ * @param string $method The HTTP method
+ * @param array $parameters The query (GET) or request (POST) parameters
+ * @param array $cookies The request cookies ($_COOKIE)
+ * @param array $files The request files ($_FILES)
+ * @param array $server The server parameters ($_SERVER)
+ * @param string|resource|null $content The raw body data
+ *
+ * @return static
+ */
+ public static function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null)
+ {
+ $server = array_replace(array(
+ 'SERVER_NAME' => 'localhost',
+ 'SERVER_PORT' => 80,
+ 'HTTP_HOST' => 'localhost',
+ 'HTTP_USER_AGENT' => 'Symfony/3.X',
+ 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
+ 'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5',
+ 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
+ 'REMOTE_ADDR' => '127.0.0.1',
+ 'SCRIPT_NAME' => '',
+ 'SCRIPT_FILENAME' => '',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ 'REQUEST_TIME' => time(),
+ ), $server);
+
+ $server['PATH_INFO'] = '';
+ $server['REQUEST_METHOD'] = strtoupper($method);
+
+ $components = parse_url($uri);
+ if (isset($components['host'])) {
+ $server['SERVER_NAME'] = $components['host'];
+ $server['HTTP_HOST'] = $components['host'];
+ }
+
+ if (isset($components['scheme'])) {
+ if ('https' === $components['scheme']) {
+ $server['HTTPS'] = 'on';
+ $server['SERVER_PORT'] = 443;
+ } else {
+ unset($server['HTTPS']);
+ $server['SERVER_PORT'] = 80;
+ }
+ }
+
+ if (isset($components['port'])) {
+ $server['SERVER_PORT'] = $components['port'];
+ $server['HTTP_HOST'] = $server['HTTP_HOST'].':'.$components['port'];
+ }
+
+ if (isset($components['user'])) {
+ $server['PHP_AUTH_USER'] = $components['user'];
+ }
+
+ if (isset($components['pass'])) {
+ $server['PHP_AUTH_PW'] = $components['pass'];
+ }
+
+ if (!isset($components['path'])) {
+ $components['path'] = '/';
+ }
+
+ switch (strtoupper($method)) {
+ case 'POST':
+ case 'PUT':
+ case 'DELETE':
+ if (!isset($server['CONTENT_TYPE'])) {
+ $server['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
+ }
+ // no break
+ case 'PATCH':
+ $request = $parameters;
+ $query = array();
+ break;
+ default:
+ $request = array();
+ $query = $parameters;
+ break;
+ }
+
+ $queryString = '';
+ if (isset($components['query'])) {
+ parse_str(html_entity_decode($components['query']), $qs);
+
+ if ($query) {
+ $query = array_replace($qs, $query);
+ $queryString = http_build_query($query, '', '&');
+ } else {
+ $query = $qs;
+ $queryString = $components['query'];
+ }
+ } elseif ($query) {
+ $queryString = http_build_query($query, '', '&');
+ }
+
+ $server['REQUEST_URI'] = $components['path'].('' !== $queryString ? '?'.$queryString : '');
+ $server['QUERY_STRING'] = $queryString;
+
+ return self::createRequestFromFactory($query, $request, array(), $cookies, $files, $server, $content);
+ }
+
+ /**
+ * Sets a callable able to create a Request instance.
+ *
+ * This is mainly useful when you need to override the Request class
+ * to keep BC with an existing system. It should not be used for any
+ * other purpose.
+ *
+ * @param callable|null $callable A PHP callable
+ */
+ public static function setFactory($callable)
+ {
+ self::$requestFactory = $callable;
+ }
+
+ /**
+ * Clones a request and overrides some of its parameters.
+ *
+ * @param array $query The GET parameters
+ * @param array $request The POST parameters
+ * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
+ * @param array $cookies The COOKIE parameters
+ * @param array $files The FILES parameters
+ * @param array $server The SERVER parameters
+ *
+ * @return static
+ */
+ public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null)
+ {
+ $dup = clone $this;
+ if (null !== $query) {
+ $dup->query = new ParameterBag($query);
+ }
+ if (null !== $request) {
+ $dup->request = new ParameterBag($request);
+ }
+ if (null !== $attributes) {
+ $dup->attributes = new ParameterBag($attributes);
+ }
+ if (null !== $cookies) {
+ $dup->cookies = new ParameterBag($cookies);
+ }
+ if (null !== $files) {
+ $dup->files = new FileBag($files);
+ }
+ if (null !== $server) {
+ $dup->server = new ServerBag($server);
+ $dup->headers = new HeaderBag($dup->server->getHeaders());
+ }
+ $dup->languages = null;
+ $dup->charsets = null;
+ $dup->encodings = null;
+ $dup->acceptableContentTypes = null;
+ $dup->pathInfo = null;
+ $dup->requestUri = null;
+ $dup->baseUrl = null;
+ $dup->basePath = null;
+ $dup->method = null;
+ $dup->format = null;
+
+ if (!$dup->get('_format') && $this->get('_format')) {
+ $dup->attributes->set('_format', $this->get('_format'));
+ }
+
+ if (!$dup->getRequestFormat(null)) {
+ $dup->setRequestFormat($this->getRequestFormat(null));
+ }
+
+ return $dup;
+ }
+
+ /**
+ * Clones the current request.
+ *
+ * Note that the session is not cloned as duplicated requests
+ * are most of the time sub-requests of the main one.
+ */
+ public function __clone()
+ {
+ $this->query = clone $this->query;
+ $this->request = clone $this->request;
+ $this->attributes = clone $this->attributes;
+ $this->cookies = clone $this->cookies;
+ $this->files = clone $this->files;
+ $this->server = clone $this->server;
+ $this->headers = clone $this->headers;
+ }
+
+ /**
+ * Returns the request as a string.
+ *
+ * @return string The request
+ */
+ public function __toString()
+ {
+ try {
+ $content = $this->getContent();
+ } catch (\LogicException $e) {
+ return trigger_error($e, E_USER_ERROR);
+ }
+
+ $cookieHeader = '';
+ $cookies = array();
+
+ foreach ($this->cookies as $k => $v) {
+ $cookies[] = $k.'='.$v;
+ }
+
+ if (!empty($cookies)) {
+ $cookieHeader = 'Cookie: '.implode('; ', $cookies)."\r\n";
+ }
+
+ return
+ sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n".
+ $this->headers.
+ $cookieHeader."\r\n".
+ $content;
+ }
+
+ /**
+ * Overrides the PHP global variables according to this request instance.
+ *
+ * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE.
+ * $_FILES is never overridden, see rfc1867
+ */
+ public function overrideGlobals()
+ {
+ $this->server->set('QUERY_STRING', static::normalizeQueryString(http_build_query($this->query->all(), '', '&')));
+
+ $_GET = $this->query->all();
+ $_POST = $this->request->all();
+ $_SERVER = $this->server->all();
+ $_COOKIE = $this->cookies->all();
+
+ foreach ($this->headers->all() as $key => $value) {
+ $key = strtoupper(str_replace('-', '_', $key));
+ if (in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH'))) {
+ $_SERVER[$key] = implode(', ', $value);
+ } else {
+ $_SERVER['HTTP_'.$key] = implode(', ', $value);
+ }
+ }
+
+ $request = array('g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE);
+
+ $requestOrder = ini_get('request_order') ?: ini_get('variables_order');
+ $requestOrder = preg_replace('#[^cgp]#', '', strtolower($requestOrder)) ?: 'gp';
+
+ $_REQUEST = array();
+ foreach (str_split($requestOrder) as $order) {
+ $_REQUEST = array_merge($_REQUEST, $request[$order]);
+ }
+ }
+
+ /**
+ * Sets a list of trusted proxies.
+ *
+ * You should only list the reverse proxies that you manage directly.
+ *
+ * @param array $proxies A list of trusted proxies
+ * @param int $trustedHeaderSet A bit field of Request::HEADER_*, to set which headers to trust from your proxies
+ *
+ * @throws \InvalidArgumentException When $trustedHeaderSet is invalid
+ */
+ public static function setTrustedProxies(array $proxies/*, int $trustedHeaderSet*/)
+ {
+ self::$trustedProxies = $proxies;
+
+ if (2 > func_num_args()) {
+ @trigger_error(sprintf('The %s() method expects a bit field of Request::HEADER_* as second argument since Symfony 3.3. Defining it will be required in 4.0. ', __METHOD__), E_USER_DEPRECATED);
+
+ return;
+ }
+ $trustedHeaderSet = (int) func_get_arg(1);
+
+ foreach (self::$trustedHeaderNames as $header => $name) {
+ self::$trustedHeaders[$header] = $header & $trustedHeaderSet ? $name : null;
+ }
+ self::$trustedHeaderSet = $trustedHeaderSet;
+ }
+
+ /**
+ * Gets the list of trusted proxies.
+ *
+ * @return array An array of trusted proxies
+ */
+ public static function getTrustedProxies()
+ {
+ return self::$trustedProxies;
+ }
+
+ /**
+ * Gets the set of trusted headers from trusted proxies.
+ *
+ * @return int A bit field of Request::HEADER_* that defines which headers are trusted from your proxies
+ */
+ public static function getTrustedHeaderSet()
+ {
+ return self::$trustedHeaderSet;
+ }
+
+ /**
+ * Sets a list of trusted host patterns.
+ *
+ * You should only list the hosts you manage using regexs.
+ *
+ * @param array $hostPatterns A list of trusted host patterns
+ */
+ public static function setTrustedHosts(array $hostPatterns)
+ {
+ self::$trustedHostPatterns = array_map(function ($hostPattern) {
+ return sprintf('#%s#i', $hostPattern);
+ }, $hostPatterns);
+ // we need to reset trusted hosts on trusted host patterns change
+ self::$trustedHosts = array();
+ }
+
+ /**
+ * Gets the list of trusted host patterns.
+ *
+ * @return array An array of trusted host patterns
+ */
+ public static function getTrustedHosts()
+ {
+ return self::$trustedHostPatterns;
+ }
+
+ /**
+ * Sets the name for trusted headers.
+ *
+ * The following header keys are supported:
+ *
+ * * Request::HEADER_CLIENT_IP: defaults to X-Forwarded-For (see getClientIp())
+ * * Request::HEADER_CLIENT_HOST: defaults to X-Forwarded-Host (see getHost())
+ * * Request::HEADER_CLIENT_PORT: defaults to X-Forwarded-Port (see getPort())
+ * * Request::HEADER_CLIENT_PROTO: defaults to X-Forwarded-Proto (see getScheme() and isSecure())
+ * * Request::HEADER_FORWARDED: defaults to Forwarded (see RFC 7239)
+ *
+ * Setting an empty value allows to disable the trusted header for the given key.
+ *
+ * @param string $key The header key
+ * @param string $value The header name
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @deprecated since version 3.3, to be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead.
+ */
+ public static function setTrustedHeaderName($key, $value)
+ {
+ @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead.', __METHOD__), E_USER_DEPRECATED);
+
+ if ('forwarded' === $key) {
+ $key = self::HEADER_FORWARDED;
+ } elseif ('client_ip' === $key) {
+ $key = self::HEADER_CLIENT_IP;
+ } elseif ('client_host' === $key) {
+ $key = self::HEADER_CLIENT_HOST;
+ } elseif ('client_proto' === $key) {
+ $key = self::HEADER_CLIENT_PROTO;
+ } elseif ('client_port' === $key) {
+ $key = self::HEADER_CLIENT_PORT;
+ } elseif (!array_key_exists($key, self::$trustedHeaders)) {
+ throw new \InvalidArgumentException(sprintf('Unable to set the trusted header name for key "%s".', $key));
+ }
+
+ self::$trustedHeaders[$key] = $value;
+
+ if (null !== $value) {
+ self::$trustedHeaderNames[$key] = $value;
+ self::$trustedHeaderSet |= $key;
+ } else {
+ self::$trustedHeaderSet &= ~$key;
+ }
+ }
+
+ /**
+ * Gets the trusted proxy header name.
+ *
+ * @param string $key The header key
+ *
+ * @return string The header name
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @deprecated since version 3.3, to be removed in 4.0. Use the Request::getTrustedHeaderSet() method instead.
+ */
+ public static function getTrustedHeaderName($key)
+ {
+ if (2 > func_num_args() || func_get_arg(1)) {
+ @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the Request::getTrustedHeaderSet() method instead.', __METHOD__), E_USER_DEPRECATED);
+ }
+
+ if (!array_key_exists($key, self::$trustedHeaders)) {
+ throw new \InvalidArgumentException(sprintf('Unable to get the trusted header name for key "%s".', $key));
+ }
+
+ return self::$trustedHeaders[$key];
+ }
+
+ /**
+ * Normalizes a query string.
+ *
+ * It builds a normalized query string, where keys/value pairs are alphabetized,
+ * have consistent escaping and unneeded delimiters are removed.
+ *
+ * @param string $qs Query string
+ *
+ * @return string A normalized query string for the Request
+ */
+ public static function normalizeQueryString($qs)
+ {
+ if ('' == $qs) {
+ return '';
+ }
+
+ $parts = array();
+ $order = array();
+
+ foreach (explode('&', $qs) as $param) {
+ if ('' === $param || '=' === $param[0]) {
+ // Ignore useless delimiters, e.g. "x=y&".
+ // Also ignore pairs with empty key, even if there was a value, e.g. "=value", as such nameless values cannot be retrieved anyway.
+ // PHP also does not include them when building _GET.
+ continue;
+ }
+
+ $keyValuePair = explode('=', $param, 2);
+
+ // GET parameters, that are submitted from a HTML form, encode spaces as "+" by default (as defined in enctype application/x-www-form-urlencoded).
+ // PHP also converts "+" to spaces when filling the global _GET or when using the function parse_str. This is why we use urldecode and then normalize to
+ // RFC 3986 with rawurlencode.
+ $parts[] = isset($keyValuePair[1]) ?
+ rawurlencode(urldecode($keyValuePair[0])).'='.rawurlencode(urldecode($keyValuePair[1])) :
+ rawurlencode(urldecode($keyValuePair[0]));
+ $order[] = urldecode($keyValuePair[0]);
+ }
+
+ array_multisort($order, SORT_ASC, $parts);
+
+ return implode('&', $parts);
+ }
+
+ /**
+ * Enables support for the _method request parameter to determine the intended HTTP method.
+ *
+ * Be warned that enabling this feature might lead to CSRF issues in your code.
+ * Check that you are using CSRF tokens when required.
+ * If the HTTP method parameter override is enabled, an html-form with method "POST" can be altered
+ * and used to send a "PUT" or "DELETE" request via the _method request parameter.
+ * If these methods are not protected against CSRF, this presents a possible vulnerability.
+ *
+ * The HTTP method can only be overridden when the real HTTP method is POST.
+ */
+ public static function enableHttpMethodParameterOverride()
+ {
+ self::$httpMethodParameterOverride = true;
+ }
+
+ /**
+ * Checks whether support for the _method request parameter is enabled.
+ *
+ * @return bool True when the _method request parameter is enabled, false otherwise
+ */
+ public static function getHttpMethodParameterOverride()
+ {
+ return self::$httpMethodParameterOverride;
+ }
+
+ /**
+ * Gets a "parameter" value from any bag.
+ *
+ * This method is mainly useful for libraries that want to provide some flexibility. If you don't need the
+ * flexibility in controllers, it is better to explicitly get request parameters from the appropriate
+ * public property instead (attributes, query, request).
+ *
+ * Order of precedence: PATH (routing placeholders or custom attributes), GET, BODY
+ *
+ * @param string $key The key
+ * @param mixed $default The default value if the parameter key does not exist
+ *
+ * @return mixed
+ */
+ public function get($key, $default = null)
+ {
+ if ($this !== $result = $this->attributes->get($key, $this)) {
+ return $result;
+ }
+
+ if ($this !== $result = $this->query->get($key, $this)) {
+ return $result;
+ }
+
+ if ($this !== $result = $this->request->get($key, $this)) {
+ return $result;
+ }
+
+ return $default;
+ }
+
+ /**
+ * Gets the Session.
+ *
+ * @return SessionInterface|null The session
+ */
+ public function getSession()
+ {
+ return $this->session;
+ }
+
+ /**
+ * Whether the request contains a Session which was started in one of the
+ * previous requests.
+ *
+ * @return bool
+ */
+ public function hasPreviousSession()
+ {
+ // the check for $this->session avoids malicious users trying to fake a session cookie with proper name
+ return $this->hasSession() && $this->cookies->has($this->session->getName());
+ }
+
+ /**
+ * Whether the request contains a Session object.
+ *
+ * This method does not give any information about the state of the session object,
+ * like whether the session is started or not. It is just a way to check if this Request
+ * is associated with a Session instance.
+ *
+ * @return bool true when the Request contains a Session object, false otherwise
+ */
+ public function hasSession()
+ {
+ return null !== $this->session;
+ }
+
+ /**
+ * Sets the Session.
+ *
+ * @param SessionInterface $session The Session
+ */
+ public function setSession(SessionInterface $session)
+ {
+ $this->session = $session;
+ }
+
+ /**
+ * Returns the client IP addresses.
+ *
+ * In the returned array the most trusted IP address is first, and the
+ * least trusted one last. The "real" client IP address is the last one,
+ * but this is also the least trusted one. Trusted proxies are stripped.
+ *
+ * Use this method carefully; you should use getClientIp() instead.
+ *
+ * @return array The client IP addresses
+ *
+ * @see getClientIp()
+ */
+ public function getClientIps()
+ {
+ $ip = $this->server->get('REMOTE_ADDR');
+
+ if (!$this->isFromTrustedProxy()) {
+ return array($ip);
+ }
+
+ return $this->getTrustedValues(self::HEADER_CLIENT_IP, $ip) ?: array($ip);
+ }
+
+ /**
+ * Returns the client IP address.
+ *
+ * This method can read the client IP address from the "X-Forwarded-For" header
+ * when trusted proxies were set via "setTrustedProxies()". The "X-Forwarded-For"
+ * header value is a comma+space separated list of IP addresses, the left-most
+ * being the original client, and each successive proxy that passed the request
+ * adding the IP address where it received the request from.
+ *
+ * If your reverse proxy uses a different header name than "X-Forwarded-For",
+ * ("Client-Ip" for instance), configure it via the $trustedHeaderSet
+ * argument of the Request::setTrustedProxies() method instead.
+ *
+ * @return string|null The client IP address
+ *
+ * @see getClientIps()
+ * @see http://en.wikipedia.org/wiki/X-Forwarded-For
+ */
+ public function getClientIp()
+ {
+ $ipAddresses = $this->getClientIps();
+
+ return $ipAddresses[0];
+ }
+
+ /**
+ * Returns current script name.
+ *
+ * @return string
+ */
+ public function getScriptName()
+ {
+ return $this->server->get('SCRIPT_NAME', $this->server->get('ORIG_SCRIPT_NAME', ''));
+ }
+
+ /**
+ * Returns the path being requested relative to the executed script.
+ *
+ * The path info always starts with a /.
+ *
+ * Suppose this request is instantiated from /mysite on localhost:
+ *
+ * * http://localhost/mysite returns an empty string
+ * * http://localhost/mysite/about returns '/about'
+ * * http://localhost/mysite/enco%20ded returns '/enco%20ded'
+ * * http://localhost/mysite/about?var=1 returns '/about'
+ *
+ * @return string The raw path (i.e. not urldecoded)
+ */
+ public function getPathInfo()
+ {
+ if (null === $this->pathInfo) {
+ $this->pathInfo = $this->preparePathInfo();
+ }
+
+ return $this->pathInfo;
+ }
+
+ /**
+ * Returns the root path from which this request is executed.
+ *
+ * Suppose that an index.php file instantiates this request object:
+ *
+ * * http://localhost/index.php returns an empty string
+ * * http://localhost/index.php/page returns an empty string
+ * * http://localhost/web/index.php returns '/web'
+ * * http://localhost/we%20b/index.php returns '/we%20b'
+ *
+ * @return string The raw path (i.e. not urldecoded)
+ */
+ public function getBasePath()
+ {
+ if (null === $this->basePath) {
+ $this->basePath = $this->prepareBasePath();
+ }
+
+ return $this->basePath;
+ }
+
+ /**
+ * Returns the root URL from which this request is executed.
+ *
+ * The base URL never ends with a /.
+ *
+ * This is similar to getBasePath(), except that it also includes the
+ * script filename (e.g. index.php) if one exists.
+ *
+ * @return string The raw URL (i.e. not urldecoded)
+ */
+ public function getBaseUrl()
+ {
+ if (null === $this->baseUrl) {
+ $this->baseUrl = $this->prepareBaseUrl();
+ }
+
+ return $this->baseUrl;
+ }
+
+ /**
+ * Gets the request's scheme.
+ *
+ * @return string
+ */
+ public function getScheme()
+ {
+ return $this->isSecure() ? 'https' : 'http';
+ }
+
+ /**
+ * Returns the port on which the request is made.
+ *
+ * This method can read the client port from the "X-Forwarded-Port" header
+ * when trusted proxies were set via "setTrustedProxies()".
+ *
+ * The "X-Forwarded-Port" header must contain the client port.
+ *
+ * If your reverse proxy uses a different header name than "X-Forwarded-Port",
+ * configure it via via the $trustedHeaderSet argument of the
+ * Request::setTrustedProxies() method instead.
+ *
+ * @return int|string can be a string if fetched from the server bag
+ */
+ public function getPort()
+ {
+ if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_PORT)) {
+ $host = $host[0];
+ } elseif ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_HOST)) {
+ $host = $host[0];
+ } elseif (!$host = $this->headers->get('HOST')) {
+ return $this->server->get('SERVER_PORT');
+ }
+
+ if ('[' === $host[0]) {
+ $pos = strpos($host, ':', strrpos($host, ']'));
+ } else {
+ $pos = strrpos($host, ':');
+ }
+
+ if (false !== $pos) {
+ return (int) substr($host, $pos + 1);
+ }
+
+ return 'https' === $this->getScheme() ? 443 : 80;
+ }
+
+ /**
+ * Returns the user.
+ *
+ * @return string|null
+ */
+ public function getUser()
+ {
+ return $this->headers->get('PHP_AUTH_USER');
+ }
+
+ /**
+ * Returns the password.
+ *
+ * @return string|null
+ */
+ public function getPassword()
+ {
+ return $this->headers->get('PHP_AUTH_PW');
+ }
+
+ /**
+ * Gets the user info.
+ *
+ * @return string A user name and, optionally, scheme-specific information about how to gain authorization to access the server
+ */
+ public function getUserInfo()
+ {
+ $userinfo = $this->getUser();
+
+ $pass = $this->getPassword();
+ if ('' != $pass) {
+ $userinfo .= ":$pass";
+ }
+
+ return $userinfo;
+ }
+
+ /**
+ * Returns the HTTP host being requested.
+ *
+ * The port name will be appended to the host if it's non-standard.
+ *
+ * @return string
+ */
+ public function getHttpHost()
+ {
+ $scheme = $this->getScheme();
+ $port = $this->getPort();
+
+ if (('http' == $scheme && 80 == $port) || ('https' == $scheme && 443 == $port)) {
+ return $this->getHost();
+ }
+
+ return $this->getHost().':'.$port;
+ }
+
+ /**
+ * Returns the requested URI (path and query string).
+ *
+ * @return string The raw URI (i.e. not URI decoded)
+ */
+ public function getRequestUri()
+ {
+ if (null === $this->requestUri) {
+ $this->requestUri = $this->prepareRequestUri();
+ }
+
+ return $this->requestUri;
+ }
+
+ /**
+ * Gets the scheme and HTTP host.
+ *
+ * If the URL was called with basic authentication, the user
+ * and the password are not added to the generated string.
+ *
+ * @return string The scheme and HTTP host
+ */
+ public function getSchemeAndHttpHost()
+ {
+ return $this->getScheme().'://'.$this->getHttpHost();
+ }
+
+ /**
+ * Generates a normalized URI (URL) for the Request.
+ *
+ * @return string A normalized URI (URL) for the Request
+ *
+ * @see getQueryString()
+ */
+ public function getUri()
+ {
+ if (null !== $qs = $this->getQueryString()) {
+ $qs = '?'.$qs;
+ }
+
+ return $this->getSchemeAndHttpHost().$this->getBaseUrl().$this->getPathInfo().$qs;
+ }
+
+ /**
+ * Generates a normalized URI for the given path.
+ *
+ * @param string $path A path to use instead of the current one
+ *
+ * @return string The normalized URI for the path
+ */
+ public function getUriForPath($path)
+ {
+ return $this->getSchemeAndHttpHost().$this->getBaseUrl().$path;
+ }
+
+ /**
+ * Returns the path as relative reference from the current Request path.
+ *
+ * Only the URIs path component (no schema, host etc.) is relevant and must be given.
+ * Both paths must be absolute and not contain relative parts.
+ * Relative URLs from one resource to another are useful when generating self-contained downloadable document archives.
+ * Furthermore, they can be used to reduce the link size in documents.
+ *
+ * Example target paths, given a base path of "/a/b/c/d":
+ * - "/a/b/c/d" -> ""
+ * - "/a/b/c/" -> "./"
+ * - "/a/b/" -> "../"
+ * - "/a/b/c/other" -> "other"
+ * - "/a/x/y" -> "../../x/y"
+ *
+ * @param string $path The target path
+ *
+ * @return string The relative target path
+ */
+ public function getRelativeUriForPath($path)
+ {
+ // be sure that we are dealing with an absolute path
+ if (!isset($path[0]) || '/' !== $path[0]) {
+ return $path;
+ }
+
+ if ($path === $basePath = $this->getPathInfo()) {
+ return '';
+ }
+
+ $sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath);
+ $targetDirs = explode('/', isset($path[0]) && '/' === $path[0] ? substr($path, 1) : $path);
+ array_pop($sourceDirs);
+ $targetFile = array_pop($targetDirs);
+
+ foreach ($sourceDirs as $i => $dir) {
+ if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) {
+ unset($sourceDirs[$i], $targetDirs[$i]);
+ } else {
+ break;
+ }
+ }
+
+ $targetDirs[] = $targetFile;
+ $path = str_repeat('../', count($sourceDirs)).implode('/', $targetDirs);
+
+ // A reference to the same base directory or an empty subdirectory must be prefixed with "./".
+ // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
+ // as the first segment of a relative-path reference, as it would be mistaken for a scheme name
+ // (see http://tools.ietf.org/html/rfc3986#section-4.2).
+ return !isset($path[0]) || '/' === $path[0]
+ || false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos)
+ ? "./$path" : $path;
+ }
+
+ /**
+ * Generates the normalized query string for the Request.
+ *
+ * It builds a normalized query string, where keys/value pairs are alphabetized
+ * and have consistent escaping.
+ *
+ * @return string|null A normalized query string for the Request
+ */
+ public function getQueryString()
+ {
+ $qs = static::normalizeQueryString($this->server->get('QUERY_STRING'));
+
+ return '' === $qs ? null : $qs;
+ }
+
+ /**
+ * Checks whether the request is secure or not.
+ *
+ * This method can read the client protocol from the "X-Forwarded-Proto" header
+ * when trusted proxies were set via "setTrustedProxies()".
+ *
+ * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http".
+ *
+ * If your reverse proxy uses a different header name than "X-Forwarded-Proto"
+ * ("SSL_HTTPS" for instance), configure it via the $trustedHeaderSet
+ * argument of the Request::setTrustedProxies() method instead.
+ *
+ * @return bool
+ */
+ public function isSecure()
+ {
+ if ($this->isFromTrustedProxy() && $proto = $this->getTrustedValues(self::HEADER_CLIENT_PROTO)) {
+ return in_array(strtolower($proto[0]), array('https', 'on', 'ssl', '1'), true);
+ }
+
+ $https = $this->server->get('HTTPS');
+
+ return !empty($https) && 'off' !== strtolower($https);
+ }
+
+ /**
+ * Returns the host name.
+ *
+ * This method can read the client host name from the "X-Forwarded-Host" header
+ * when trusted proxies were set via "setTrustedProxies()".
+ *
+ * The "X-Forwarded-Host" header must contain the client host name.
+ *
+ * If your reverse proxy uses a different header name than "X-Forwarded-Host",
+ * configure it via the $trustedHeaderSet argument of the
+ * Request::setTrustedProxies() method instead.
+ *
+ * @return string
+ *
+ * @throws SuspiciousOperationException when the host name is invalid or not trusted
+ */
+ public function getHost()
+ {
+ if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_HOST)) {
+ $host = $host[0];
+ } elseif (!$host = $this->headers->get('HOST')) {
+ if (!$host = $this->server->get('SERVER_NAME')) {
+ $host = $this->server->get('SERVER_ADDR', '');
+ }
+ }
+
+ // trim and remove port number from host
+ // host is lowercase as per RFC 952/2181
+ $host = strtolower(preg_replace('/:\d+$/', '', trim($host)));
+
+ // as the host can come from the user (HTTP_HOST and depending on the configuration, SERVER_NAME too can come from the user)
+ // check that it does not contain forbidden characters (see RFC 952 and RFC 2181)
+ // use preg_replace() instead of preg_match() to prevent DoS attacks with long host names
+ if ($host && '' !== preg_replace('/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/', '', $host)) {
+ if (!$this->isHostValid) {
+ return '';
+ }
+ $this->isHostValid = false;
+
+ throw new SuspiciousOperationException(sprintf('Invalid Host "%s".', $host));
+ }
+
+ if (count(self::$trustedHostPatterns) > 0) {
+ // to avoid host header injection attacks, you should provide a list of trusted host patterns
+
+ if (in_array($host, self::$trustedHosts)) {
+ return $host;
+ }
+
+ foreach (self::$trustedHostPatterns as $pattern) {
+ if (preg_match($pattern, $host)) {
+ self::$trustedHosts[] = $host;
+
+ return $host;
+ }
+ }
+
+ if (!$this->isHostValid) {
+ return '';
+ }
+ $this->isHostValid = false;
+
+ throw new SuspiciousOperationException(sprintf('Untrusted Host "%s".', $host));
+ }
+
+ return $host;
+ }
+
+ /**
+ * Sets the request method.
+ *
+ * @param string $method
+ */
+ public function setMethod($method)
+ {
+ $this->method = null;
+ $this->server->set('REQUEST_METHOD', $method);
+ }
+
+ /**
+ * Gets the request "intended" method.
+ *
+ * If the X-HTTP-Method-Override header is set, and if the method is a POST,
+ * then it is used to determine the "real" intended HTTP method.
+ *
+ * The _method request parameter can also be used to determine the HTTP method,
+ * but only if enableHttpMethodParameterOverride() has been called.
+ *
+ * The method is always an uppercased string.
+ *
+ * @return string The request method
+ *
+ * @see getRealMethod()
+ */
+ public function getMethod()
+ {
+ if (null === $this->method) {
+ $this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
+
+ if ('POST' === $this->method) {
+ if ($method = $this->headers->get('X-HTTP-METHOD-OVERRIDE')) {
+ $this->method = strtoupper($method);
+ } elseif (self::$httpMethodParameterOverride) {
+ $this->method = strtoupper($this->request->get('_method', $this->query->get('_method', 'POST')));
+ }
+ }
+ }
+
+ return $this->method;
+ }
+
+ /**
+ * Gets the "real" request method.
+ *
+ * @return string The request method
+ *
+ * @see getMethod()
+ */
+ public function getRealMethod()
+ {
+ return strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
+ }
+
+ /**
+ * Gets the mime type associated with the format.
+ *
+ * @param string $format The format
+ *
+ * @return string The associated mime type (null if not found)
+ */
+ public function getMimeType($format)
+ {
+ if (null === static::$formats) {
+ static::initializeFormats();
+ }
+
+ return isset(static::$formats[$format]) ? static::$formats[$format][0] : null;
+ }
+
+ /**
+ * Gets the mime types associated with the format.
+ *
+ * @param string $format The format
+ *
+ * @return array The associated mime types
+ */
+ public static function getMimeTypes($format)
+ {
+ if (null === static::$formats) {
+ static::initializeFormats();
+ }
+
+ return isset(static::$formats[$format]) ? static::$formats[$format] : array();
+ }
+
+ /**
+ * Gets the format associated with the mime type.
+ *
+ * @param string $mimeType The associated mime type
+ *
+ * @return string|null The format (null if not found)
+ */
+ public function getFormat($mimeType)
+ {
+ $canonicalMimeType = null;
+ if (false !== $pos = strpos($mimeType, ';')) {
+ $canonicalMimeType = substr($mimeType, 0, $pos);
+ }
+
+ if (null === static::$formats) {
+ static::initializeFormats();
+ }
+
+ foreach (static::$formats as $format => $mimeTypes) {
+ if (in_array($mimeType, (array) $mimeTypes)) {
+ return $format;
+ }
+ if (null !== $canonicalMimeType && in_array($canonicalMimeType, (array) $mimeTypes)) {
+ return $format;
+ }
+ }
+ }
+
+ /**
+ * Associates a format with mime types.
+ *
+ * @param string $format The format
+ * @param string|array $mimeTypes The associated mime types (the preferred one must be the first as it will be used as the content type)
+ */
+ public function setFormat($format, $mimeTypes)
+ {
+ if (null === static::$formats) {
+ static::initializeFormats();
+ }
+
+ static::$formats[$format] = is_array($mimeTypes) ? $mimeTypes : array($mimeTypes);
+ }
+
+ /**
+ * Gets the request format.
+ *
+ * Here is the process to determine the format:
+ *
+ * * format defined by the user (with setRequestFormat())
+ * * _format request attribute
+ * * $default
+ *
+ * @param string $default The default format
+ *
+ * @return string The request format
+ */
+ public function getRequestFormat($default = 'html')
+ {
+ if (null === $this->format) {
+ $this->format = $this->attributes->get('_format');
+ }
+
+ return null === $this->format ? $default : $this->format;
+ }
+
+ /**
+ * Sets the request format.
+ *
+ * @param string $format The request format
+ */
+ public function setRequestFormat($format)
+ {
+ $this->format = $format;
+ }
+
+ /**
+ * Gets the format associated with the request.
+ *
+ * @return string|null The format (null if no content type is present)
+ */
+ public function getContentType()
+ {
+ return $this->getFormat($this->headers->get('CONTENT_TYPE'));
+ }
+
+ /**
+ * Sets the default locale.
+ *
+ * @param string $locale
+ */
+ public function setDefaultLocale($locale)
+ {
+ $this->defaultLocale = $locale;
+
+ if (null === $this->locale) {
+ $this->setPhpDefaultLocale($locale);
+ }
+ }
+
+ /**
+ * Get the default locale.
+ *
+ * @return string
+ */
+ public function getDefaultLocale()
+ {
+ return $this->defaultLocale;
+ }
+
+ /**
+ * Sets the locale.
+ *
+ * @param string $locale
+ */
+ public function setLocale($locale)
+ {
+ $this->setPhpDefaultLocale($this->locale = $locale);
+ }
+
+ /**
+ * Get the locale.
+ *
+ * @return string
+ */
+ public function getLocale()
+ {
+ return null === $this->locale ? $this->defaultLocale : $this->locale;
+ }
+
+ /**
+ * Checks if the request method is of specified type.
+ *
+ * @param string $method Uppercase request method (GET, POST etc)
+ *
+ * @return bool
+ */
+ public function isMethod($method)
+ {
+ return $this->getMethod() === strtoupper($method);
+ }
+
+ /**
+ * Checks whether or not the method is safe.
+ *
+ * @see https://tools.ietf.org/html/rfc7231#section-4.2.1
+ *
+ * @param bool $andCacheable Adds the additional condition that the method should be cacheable. True by default.
+ *
+ * @return bool
+ */
+ public function isMethodSafe(/* $andCacheable = true */)
+ {
+ if (!func_num_args() || func_get_arg(0)) {
+ // This deprecation should be turned into a BadMethodCallException in 4.0 (without adding the argument in the signature)
+ // then setting $andCacheable to false should be deprecated in 4.1
+ @trigger_error('Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is deprecated since Symfony 3.2 and will throw an exception in 4.0. Disable checking only for cacheable methods by calling the method with `false` as first argument or use the Request::isMethodCacheable() instead.', E_USER_DEPRECATED);
+
+ return in_array($this->getMethod(), array('GET', 'HEAD'));
+ }
+
+ return in_array($this->getMethod(), array('GET', 'HEAD', 'OPTIONS', 'TRACE'));
+ }
+
+ /**
+ * Checks whether or not the method is idempotent.
+ *
+ * @return bool
+ */
+ public function isMethodIdempotent()
+ {
+ return in_array($this->getMethod(), array('HEAD', 'GET', 'PUT', 'DELETE', 'TRACE', 'OPTIONS', 'PURGE'));
+ }
+
+ /**
+ * Checks whether the method is cacheable or not.
+ *
+ * @see https://tools.ietf.org/html/rfc7231#section-4.2.3
+ *
+ * @return bool
+ */
+ public function isMethodCacheable()
+ {
+ return in_array($this->getMethod(), array('GET', 'HEAD'));
+ }
+
+ /**
+ * Returns the protocol version.
+ *
+ * If the application is behind a proxy, the protocol version used in the
+ * requests between the client and the proxy and between the proxy and the
+ * server might be different. This returns the former (from the "Via" header)
+ * if the proxy is trusted (see "setTrustedProxies()"), otherwise it returns
+ * the latter (from the "SERVER_PROTOCOL" server parameter).
+ *
+ * @return string
+ */
+ public function getProtocolVersion()
+ {
+ if ($this->isFromTrustedProxy()) {
+ preg_match('~^(HTTP/)?([1-9]\.[0-9]) ~', $this->headers->get('Via'), $matches);
+
+ if ($matches) {
+ return 'HTTP/'.$matches[2];
+ }
+ }
+
+ return $this->server->get('SERVER_PROTOCOL');
+ }
+
+ /**
+ * Returns the request body content.
+ *
+ * @param bool $asResource If true, a resource will be returned
+ *
+ * @return string|resource The request body content or a resource to read the body stream
+ *
+ * @throws \LogicException
+ */
+ public function getContent($asResource = false)
+ {
+ $currentContentIsResource = is_resource($this->content);
+ if (\PHP_VERSION_ID < 50600 && false === $this->content) {
+ throw new \LogicException('getContent() can only be called once when using the resource return type and PHP below 5.6.');
+ }
+
+ if (true === $asResource) {
+ if ($currentContentIsResource) {
+ rewind($this->content);
+
+ return $this->content;
+ }
+
+ // Content passed in parameter (test)
+ if (is_string($this->content)) {
+ $resource = fopen('php://temp', 'r+');
+ fwrite($resource, $this->content);
+ rewind($resource);
+
+ return $resource;
+ }
+
+ $this->content = false;
+
+ return fopen('php://input', 'rb');
+ }
+
+ if ($currentContentIsResource) {
+ rewind($this->content);
+
+ return stream_get_contents($this->content);
+ }
+
+ if (null === $this->content || false === $this->content) {
+ $this->content = file_get_contents('php://input');
+ }
+
+ return $this->content;
+ }
+
+ /**
+ * Gets the Etags.
+ *
+ * @return array The entity tags
+ */
+ public function getETags()
+ {
+ return preg_split('/\s*,\s*/', $this->headers->get('if_none_match'), null, PREG_SPLIT_NO_EMPTY);
+ }
+
+ /**
+ * @return bool
+ */
+ public function isNoCache()
+ {
+ return $this->headers->hasCacheControlDirective('no-cache') || 'no-cache' == $this->headers->get('Pragma');
+ }
+
+ /**
+ * Returns the preferred language.
+ *
+ * @param array $locales An array of ordered available locales
+ *
+ * @return string|null The preferred locale
+ */
+ public function getPreferredLanguage(array $locales = null)
+ {
+ $preferredLanguages = $this->getLanguages();
+
+ if (empty($locales)) {
+ return isset($preferredLanguages[0]) ? $preferredLanguages[0] : null;
+ }
+
+ if (!$preferredLanguages) {
+ return $locales[0];
+ }
+
+ $extendedPreferredLanguages = array();
+ foreach ($preferredLanguages as $language) {
+ $extendedPreferredLanguages[] = $language;
+ if (false !== $position = strpos($language, '_')) {
+ $superLanguage = substr($language, 0, $position);
+ if (!in_array($superLanguage, $preferredLanguages)) {
+ $extendedPreferredLanguages[] = $superLanguage;
+ }
+ }
+ }
+
+ $preferredLanguages = array_values(array_intersect($extendedPreferredLanguages, $locales));
+
+ return isset($preferredLanguages[0]) ? $preferredLanguages[0] : $locales[0];
+ }
+
+ /**
+ * Gets a list of languages acceptable by the client browser.
+ *
+ * @return array Languages ordered in the user browser preferences
+ */
+ public function getLanguages()
+ {
+ if (null !== $this->languages) {
+ return $this->languages;
+ }
+
+ $languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all();
+ $this->languages = array();
+ foreach ($languages as $lang => $acceptHeaderItem) {
+ if (false !== strpos($lang, '-')) {
+ $codes = explode('-', $lang);
+ if ('i' === $codes[0]) {
+ // Language not listed in ISO 639 that are not variants
+ // of any listed language, which can be registered with the
+ // i-prefix, such as i-cherokee
+ if (count($codes) > 1) {
+ $lang = $codes[1];
+ }
+ } else {
+ for ($i = 0, $max = count($codes); $i < $max; ++$i) {
+ if (0 === $i) {
+ $lang = strtolower($codes[0]);
+ } else {
+ $lang .= '_'.strtoupper($codes[$i]);
+ }
+ }
+ }
+ }
+
+ $this->languages[] = $lang;
+ }
+
+ return $this->languages;
+ }
+
+ /**
+ * Gets a list of charsets acceptable by the client browser.
+ *
+ * @return array List of charsets in preferable order
+ */
+ public function getCharsets()
+ {
+ if (null !== $this->charsets) {
+ return $this->charsets;
+ }
+
+ return $this->charsets = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all());
+ }
+
+ /**
+ * Gets a list of encodings acceptable by the client browser.
+ *
+ * @return array List of encodings in preferable order
+ */
+ public function getEncodings()
+ {
+ if (null !== $this->encodings) {
+ return $this->encodings;
+ }
+
+ return $this->encodings = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Encoding'))->all());
+ }
+
+ /**
+ * Gets a list of content types acceptable by the client browser.
+ *
+ * @return array List of content types in preferable order
+ */
+ public function getAcceptableContentTypes()
+ {
+ if (null !== $this->acceptableContentTypes) {
+ return $this->acceptableContentTypes;
+ }
+
+ return $this->acceptableContentTypes = array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all());
+ }
+
+ /**
+ * Returns true if the request is a XMLHttpRequest.
+ *
+ * It works if your JavaScript library sets an X-Requested-With HTTP header.
+ * It is known to work with common JavaScript frameworks:
+ *
+ * @see http://en.wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript
+ *
+ * @return bool true if the request is an XMLHttpRequest, false otherwise
+ */
+ public function isXmlHttpRequest()
+ {
+ return 'XMLHttpRequest' == $this->headers->get('X-Requested-With');
+ }
+
+ /*
+ * The following methods are derived from code of the Zend Framework (1.10dev - 2010-01-24)
+ *
+ * Code subject to the new BSD license (http://framework.zend.com/license/new-bsd).
+ *
+ * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ */
+
+ protected function prepareRequestUri()
+ {
+ $requestUri = '';
+
+ if ($this->headers->has('X_ORIGINAL_URL')) {
+ // IIS with Microsoft Rewrite Module
+ $requestUri = $this->headers->get('X_ORIGINAL_URL');
+ $this->headers->remove('X_ORIGINAL_URL');
+ $this->server->remove('HTTP_X_ORIGINAL_URL');
+ $this->server->remove('UNENCODED_URL');
+ $this->server->remove('IIS_WasUrlRewritten');
+ } elseif ($this->headers->has('X_REWRITE_URL')) {
+ // IIS with ISAPI_Rewrite
+ $requestUri = $this->headers->get('X_REWRITE_URL');
+ $this->headers->remove('X_REWRITE_URL');
+ } elseif ('1' == $this->server->get('IIS_WasUrlRewritten') && '' != $this->server->get('UNENCODED_URL')) {
+ // IIS7 with URL Rewrite: make sure we get the unencoded URL (double slash problem)
+ $requestUri = $this->server->get('UNENCODED_URL');
+ $this->server->remove('UNENCODED_URL');
+ $this->server->remove('IIS_WasUrlRewritten');
+ } elseif ($this->server->has('REQUEST_URI')) {
+ $requestUri = $this->server->get('REQUEST_URI');
+ // HTTP proxy reqs setup request URI with scheme and host [and port] + the URL path, only use URL path
+ $schemeAndHttpHost = $this->getSchemeAndHttpHost();
+ if (0 === strpos($requestUri, $schemeAndHttpHost)) {
+ $requestUri = substr($requestUri, strlen($schemeAndHttpHost));
+ }
+ } elseif ($this->server->has('ORIG_PATH_INFO')) {
+ // IIS 5.0, PHP as CGI
+ $requestUri = $this->server->get('ORIG_PATH_INFO');
+ if ('' != $this->server->get('QUERY_STRING')) {
+ $requestUri .= '?'.$this->server->get('QUERY_STRING');
+ }
+ $this->server->remove('ORIG_PATH_INFO');
+ }
+
+ // normalize the request URI to ease creating sub-requests from this request
+ $this->server->set('REQUEST_URI', $requestUri);
+
+ return $requestUri;
+ }
+
+ /**
+ * Prepares the base URL.
+ *
+ * @return string
+ */
+ protected function prepareBaseUrl()
+ {
+ $filename = basename($this->server->get('SCRIPT_FILENAME'));
+
+ if (basename($this->server->get('SCRIPT_NAME')) === $filename) {
+ $baseUrl = $this->server->get('SCRIPT_NAME');
+ } elseif (basename($this->server->get('PHP_SELF')) === $filename) {
+ $baseUrl = $this->server->get('PHP_SELF');
+ } elseif (basename($this->server->get('ORIG_SCRIPT_NAME')) === $filename) {
+ $baseUrl = $this->server->get('ORIG_SCRIPT_NAME'); // 1and1 shared hosting compatibility
+ } else {
+ // Backtrack up the script_filename to find the portion matching
+ // php_self
+ $path = $this->server->get('PHP_SELF', '');
+ $file = $this->server->get('SCRIPT_FILENAME', '');
+ $segs = explode('/', trim($file, '/'));
+ $segs = array_reverse($segs);
+ $index = 0;
+ $last = count($segs);
+ $baseUrl = '';
+ do {
+ $seg = $segs[$index];
+ $baseUrl = '/'.$seg.$baseUrl;
+ ++$index;
+ } while ($last > $index && (false !== $pos = strpos($path, $baseUrl)) && 0 != $pos);
+ }
+
+ // Does the baseUrl have anything in common with the request_uri?
+ $requestUri = $this->getRequestUri();
+ if ('' !== $requestUri && '/' !== $requestUri[0]) {
+ $requestUri = '/'.$requestUri;
+ }
+
+ if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) {
+ // full $baseUrl matches
+ return $prefix;
+ }
+
+ if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, rtrim(dirname($baseUrl), '/'.DIRECTORY_SEPARATOR).'/')) {
+ // directory portion of $baseUrl matches
+ return rtrim($prefix, '/'.DIRECTORY_SEPARATOR);
+ }
+
+ $truncatedRequestUri = $requestUri;
+ if (false !== $pos = strpos($requestUri, '?')) {
+ $truncatedRequestUri = substr($requestUri, 0, $pos);
+ }
+
+ $basename = basename($baseUrl);
+ if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri), $basename)) {
+ // no match whatsoever; set it blank
+ return '';
+ }
+
+ // If using mod_rewrite or ISAPI_Rewrite strip the script filename
+ // out of baseUrl. $pos !== 0 makes sure it is not matching a value
+ // from PATH_INFO or QUERY_STRING
+ if (strlen($requestUri) >= strlen($baseUrl) && (false !== $pos = strpos($requestUri, $baseUrl)) && 0 !== $pos) {
+ $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl));
+ }
+
+ return rtrim($baseUrl, '/'.DIRECTORY_SEPARATOR);
+ }
+
+ /**
+ * Prepares the base path.
+ *
+ * @return string base path
+ */
+ protected function prepareBasePath()
+ {
+ $baseUrl = $this->getBaseUrl();
+ if (empty($baseUrl)) {
+ return '';
+ }
+
+ $filename = basename($this->server->get('SCRIPT_FILENAME'));
+ if (basename($baseUrl) === $filename) {
+ $basePath = dirname($baseUrl);
+ } else {
+ $basePath = $baseUrl;
+ }
+
+ if ('\\' === DIRECTORY_SEPARATOR) {
+ $basePath = str_replace('\\', '/', $basePath);
+ }
+
+ return rtrim($basePath, '/');
+ }
+
+ /**
+ * Prepares the path info.
+ *
+ * @return string path info
+ */
+ protected function preparePathInfo()
+ {
+ if (null === ($requestUri = $this->getRequestUri())) {
+ return '/';
+ }
+
+ // Remove the query string from REQUEST_URI
+ if (false !== $pos = strpos($requestUri, '?')) {
+ $requestUri = substr($requestUri, 0, $pos);
+ }
+ if ('' !== $requestUri && '/' !== $requestUri[0]) {
+ $requestUri = '/'.$requestUri;
+ }
+
+ if (null === ($baseUrl = $this->getBaseUrl())) {
+ return $requestUri;
+ }
+
+ $pathInfo = substr($requestUri, strlen($baseUrl));
+ if (false === $pathInfo || '' === $pathInfo) {
+ // If substr() returns false then PATH_INFO is set to an empty string
+ return '/';
+ }
+
+ return (string) $pathInfo;
+ }
+
+ /**
+ * Initializes HTTP request formats.
+ */
+ protected static function initializeFormats()
+ {
+ static::$formats = array(
+ 'html' => array('text/html', 'application/xhtml+xml'),
+ 'txt' => array('text/plain'),
+ 'js' => array('application/javascript', 'application/x-javascript', 'text/javascript'),
+ 'css' => array('text/css'),
+ 'json' => array('application/json', 'application/x-json'),
+ 'jsonld' => array('application/ld+json'),
+ 'xml' => array('text/xml', 'application/xml', 'application/x-xml'),
+ 'rdf' => array('application/rdf+xml'),
+ 'atom' => array('application/atom+xml'),
+ 'rss' => array('application/rss+xml'),
+ 'form' => array('application/x-www-form-urlencoded'),
+ );
+ }
+
+ /**
+ * Sets the default PHP locale.
+ *
+ * @param string $locale
+ */
+ private function setPhpDefaultLocale($locale)
+ {
+ // if either the class Locale doesn't exist, or an exception is thrown when
+ // setting the default locale, the intl module is not installed, and
+ // the call can be ignored:
+ try {
+ if (class_exists('Locale', false)) {
+ \Locale::setDefault($locale);
+ }
+ } catch (\Exception $e) {
+ }
+ }
+
+ /*
+ * Returns the prefix as encoded in the string when the string starts with
+ * the given prefix, false otherwise.
+ *
+ * @param string $string The urlencoded string
+ * @param string $prefix The prefix not encoded
+ *
+ * @return string|false The prefix as it is encoded in $string, or false
+ */
+ private function getUrlencodedPrefix($string, $prefix)
+ {
+ if (0 !== strpos(rawurldecode($string), $prefix)) {
+ return false;
+ }
+
+ $len = strlen($prefix);
+
+ if (preg_match(sprintf('#^(%%[[:xdigit:]]{2}|.){%d}#', $len), $string, $match)) {
+ return $match[0];
+ }
+
+ return false;
+ }
+
+ private static function createRequestFromFactory(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
+ {
+ if (self::$requestFactory) {
+ $request = call_user_func(self::$requestFactory, $query, $request, $attributes, $cookies, $files, $server, $content);
+
+ if (!$request instanceof self) {
+ throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.');
+ }
+
+ return $request;
+ }
+
+ return new static($query, $request, $attributes, $cookies, $files, $server, $content);
+ }
+
+ /**
+ * Indicates whether this request originated from a trusted proxy.
+ *
+ * This can be useful to determine whether or not to trust the
+ * contents of a proxy-specific header.
+ *
+ * @return bool true if the request came from a trusted proxy, false otherwise
+ */
+ public function isFromTrustedProxy()
+ {
+ return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR'), self::$trustedProxies);
+ }
+
+ private function getTrustedValues($type, $ip = null)
+ {
+ $clientValues = array();
+ $forwardedValues = array();
+
+ if (self::$trustedHeaders[$type] && $this->headers->has(self::$trustedHeaders[$type])) {
+ foreach (explode(',', $this->headers->get(self::$trustedHeaders[$type])) as $v) {
+ $clientValues[] = (self::HEADER_CLIENT_PORT === $type ? '0.0.0.0:' : '').trim($v);
+ }
+ }
+
+ if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) {
+ $forwardedValues = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
+ $forwardedValues = preg_match_all(sprintf('{(?:%s)=(?:"?\[?)([a-zA-Z0-9\.:_\-/]*+)}', self::$forwardedParams[$type]), $forwardedValues, $matches) ? $matches[1] : array();
+ }
+
+ if (null !== $ip) {
+ $clientValues = $this->normalizeAndFilterClientIps($clientValues, $ip);
+ $forwardedValues = $this->normalizeAndFilterClientIps($forwardedValues, $ip);
+ }
+
+ if ($forwardedValues === $clientValues || !$clientValues) {
+ return $forwardedValues;
+ }
+
+ if (!$forwardedValues) {
+ return $clientValues;
+ }
+
+ if (!$this->isForwardedValid) {
+ return null !== $ip ? array('0.0.0.0', $ip) : array();
+ }
+ $this->isForwardedValid = false;
+
+ throw new ConflictingHeadersException(sprintf('The request has both a trusted "%s" header and a trusted "%s" header, conflicting with each other. You should either configure your proxy to remove one of them, or configure your project to distrust the offending one.', self::$trustedHeaders[self::HEADER_FORWARDED], self::$trustedHeaders[$type]));
+ }
+
+ private function normalizeAndFilterClientIps(array $clientIps, $ip)
+ {
+ if (!$clientIps) {
+ return array();
+ }
+ $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
+ $firstTrustedIp = null;
+
+ foreach ($clientIps as $key => $clientIp) {
+ // Remove port (unfortunately, it does happen)
+ if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) {
+ $clientIps[$key] = $clientIp = $match[1];
+ }
+
+ if (!filter_var($clientIp, FILTER_VALIDATE_IP)) {
+ unset($clientIps[$key]);
+
+ continue;
+ }
+
+ if (IpUtils::checkIp($clientIp, self::$trustedProxies)) {
+ unset($clientIps[$key]);
+
+ // Fallback to this when the client IP falls into the range of trusted proxies
+ if (null === $firstTrustedIp) {
+ $firstTrustedIp = $clientIp;
+ }
+ }
+ }
+
+ // Now the IP chain contains only untrusted proxies and the client IP
+ return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/RequestMatcher.php b/assets/php/vendor/symfony/http-foundation/RequestMatcher.php
new file mode 100644
index 0000000..076d077
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/RequestMatcher.php
@@ -0,0 +1,178 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * RequestMatcher compares a pre-defined set of checks against a Request instance.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class RequestMatcher implements RequestMatcherInterface
+{
+ /**
+ * @var string|null
+ */
+ private $path;
+
+ /**
+ * @var string|null
+ */
+ private $host;
+
+ /**
+ * @var string[]
+ */
+ private $methods = array();
+
+ /**
+ * @var string[]
+ */
+ private $ips = array();
+
+ /**
+ * @var array
+ */
+ private $attributes = array();
+
+ /**
+ * @var string[]
+ */
+ private $schemes = array();
+
+ /**
+ * @param string|null $path
+ * @param string|null $host
+ * @param string|string[]|null $methods
+ * @param string|string[]|null $ips
+ * @param array $attributes
+ * @param string|string[]|null $schemes
+ */
+ public function __construct($path = null, $host = null, $methods = null, $ips = null, array $attributes = array(), $schemes = null)
+ {
+ $this->matchPath($path);
+ $this->matchHost($host);
+ $this->matchMethod($methods);
+ $this->matchIps($ips);
+ $this->matchScheme($schemes);
+
+ foreach ($attributes as $k => $v) {
+ $this->matchAttribute($k, $v);
+ }
+ }
+
+ /**
+ * Adds a check for the HTTP scheme.
+ *
+ * @param string|string[]|null $scheme An HTTP scheme or an array of HTTP schemes
+ */
+ public function matchScheme($scheme)
+ {
+ $this->schemes = null !== $scheme ? array_map('strtolower', (array) $scheme) : array();
+ }
+
+ /**
+ * Adds a check for the URL host name.
+ *
+ * @param string|null $regexp A Regexp
+ */
+ public function matchHost($regexp)
+ {
+ $this->host = $regexp;
+ }
+
+ /**
+ * Adds a check for the URL path info.
+ *
+ * @param string|null $regexp A Regexp
+ */
+ public function matchPath($regexp)
+ {
+ $this->path = $regexp;
+ }
+
+ /**
+ * Adds a check for the client IP.
+ *
+ * @param string $ip A specific IP address or a range specified using IP/netmask like 192.168.1.0/24
+ */
+ public function matchIp($ip)
+ {
+ $this->matchIps($ip);
+ }
+
+ /**
+ * Adds a check for the client IP.
+ *
+ * @param string|string[]|null $ips A specific IP address or a range specified using IP/netmask like 192.168.1.0/24
+ */
+ public function matchIps($ips)
+ {
+ $this->ips = null !== $ips ? (array) $ips : array();
+ }
+
+ /**
+ * Adds a check for the HTTP method.
+ *
+ * @param string|string[]|null $method An HTTP method or an array of HTTP methods
+ */
+ public function matchMethod($method)
+ {
+ $this->methods = null !== $method ? array_map('strtoupper', (array) $method) : array();
+ }
+
+ /**
+ * Adds a check for request attribute.
+ *
+ * @param string $key The request attribute name
+ * @param string $regexp A Regexp
+ */
+ public function matchAttribute($key, $regexp)
+ {
+ $this->attributes[$key] = $regexp;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function matches(Request $request)
+ {
+ if ($this->schemes && !in_array($request->getScheme(), $this->schemes, true)) {
+ return false;
+ }
+
+ if ($this->methods && !in_array($request->getMethod(), $this->methods, true)) {
+ return false;
+ }
+
+ foreach ($this->attributes as $key => $pattern) {
+ if (!preg_match('{'.$pattern.'}', $request->attributes->get($key))) {
+ return false;
+ }
+ }
+
+ if (null !== $this->path && !preg_match('{'.$this->path.'}', rawurldecode($request->getPathInfo()))) {
+ return false;
+ }
+
+ if (null !== $this->host && !preg_match('{'.$this->host.'}i', $request->getHost())) {
+ return false;
+ }
+
+ if (IpUtils::checkIp($request->getClientIp(), $this->ips)) {
+ return true;
+ }
+
+ // Note to future implementors: add additional checks above the
+ // foreach above or else your check might not be run!
+ return 0 === count($this->ips);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/RequestMatcherInterface.php b/assets/php/vendor/symfony/http-foundation/RequestMatcherInterface.php
new file mode 100644
index 0000000..c26db3e
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/RequestMatcherInterface.php
@@ -0,0 +1,27 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * RequestMatcherInterface is an interface for strategies to match a Request.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface RequestMatcherInterface
+{
+ /**
+ * Decides whether the rule(s) implemented by the strategy matches the supplied request.
+ *
+ * @return bool true if the request matches, false otherwise
+ */
+ public function matches(Request $request);
+}
diff --git a/assets/php/vendor/symfony/http-foundation/RequestStack.php b/assets/php/vendor/symfony/http-foundation/RequestStack.php
new file mode 100644
index 0000000..3d9cfd0
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/RequestStack.php
@@ -0,0 +1,103 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Request stack that controls the lifecycle of requests.
+ *
+ * @author Benjamin Eberlei <kontakt@beberlei.de>
+ */
+class RequestStack
+{
+ /**
+ * @var Request[]
+ */
+ private $requests = array();
+
+ /**
+ * Pushes a Request on the stack.
+ *
+ * This method should generally not be called directly as the stack
+ * management should be taken care of by the application itself.
+ */
+ public function push(Request $request)
+ {
+ $this->requests[] = $request;
+ }
+
+ /**
+ * Pops the current request from the stack.
+ *
+ * This operation lets the current request go out of scope.
+ *
+ * This method should generally not be called directly as the stack
+ * management should be taken care of by the application itself.
+ *
+ * @return Request|null
+ */
+ public function pop()
+ {
+ if (!$this->requests) {
+ return;
+ }
+
+ return array_pop($this->requests);
+ }
+
+ /**
+ * @return Request|null
+ */
+ public function getCurrentRequest()
+ {
+ return end($this->requests) ?: null;
+ }
+
+ /**
+ * Gets the master Request.
+ *
+ * Be warned that making your code aware of the master request
+ * might make it un-compatible with other features of your framework
+ * like ESI support.
+ *
+ * @return Request|null
+ */
+ public function getMasterRequest()
+ {
+ if (!$this->requests) {
+ return;
+ }
+
+ return $this->requests[0];
+ }
+
+ /**
+ * Returns the parent request of the current.
+ *
+ * Be warned that making your code aware of the parent request
+ * might make it un-compatible with other features of your framework
+ * like ESI support.
+ *
+ * If current Request is the master request, it returns null.
+ *
+ * @return Request|null
+ */
+ public function getParentRequest()
+ {
+ $pos = count($this->requests) - 2;
+
+ if (!isset($this->requests[$pos])) {
+ return;
+ }
+
+ return $this->requests[$pos];
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Response.php b/assets/php/vendor/symfony/http-foundation/Response.php
new file mode 100644
index 0000000..6f8a623
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Response.php
@@ -0,0 +1,1298 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Response represents an HTTP response.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class Response
+{
+ const HTTP_CONTINUE = 100;
+ const HTTP_SWITCHING_PROTOCOLS = 101;
+ const HTTP_PROCESSING = 102; // RFC2518
+ const HTTP_OK = 200;
+ const HTTP_CREATED = 201;
+ const HTTP_ACCEPTED = 202;
+ const HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
+ const HTTP_NO_CONTENT = 204;
+ const HTTP_RESET_CONTENT = 205;
+ const HTTP_PARTIAL_CONTENT = 206;
+ const HTTP_MULTI_STATUS = 207; // RFC4918
+ const HTTP_ALREADY_REPORTED = 208; // RFC5842
+ const HTTP_IM_USED = 226; // RFC3229
+ const HTTP_MULTIPLE_CHOICES = 300;
+ const HTTP_MOVED_PERMANENTLY = 301;
+ const HTTP_FOUND = 302;
+ const HTTP_SEE_OTHER = 303;
+ const HTTP_NOT_MODIFIED = 304;
+ const HTTP_USE_PROXY = 305;
+ const HTTP_RESERVED = 306;
+ const HTTP_TEMPORARY_REDIRECT = 307;
+ const HTTP_PERMANENTLY_REDIRECT = 308; // RFC7238
+ const HTTP_BAD_REQUEST = 400;
+ const HTTP_UNAUTHORIZED = 401;
+ const HTTP_PAYMENT_REQUIRED = 402;
+ const HTTP_FORBIDDEN = 403;
+ const HTTP_NOT_FOUND = 404;
+ const HTTP_METHOD_NOT_ALLOWED = 405;
+ const HTTP_NOT_ACCEPTABLE = 406;
+ const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
+ const HTTP_REQUEST_TIMEOUT = 408;
+ const HTTP_CONFLICT = 409;
+ const HTTP_GONE = 410;
+ const HTTP_LENGTH_REQUIRED = 411;
+ const HTTP_PRECONDITION_FAILED = 412;
+ const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
+ const HTTP_REQUEST_URI_TOO_LONG = 414;
+ const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
+ const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+ const HTTP_EXPECTATION_FAILED = 417;
+ const HTTP_I_AM_A_TEAPOT = 418; // RFC2324
+ const HTTP_MISDIRECTED_REQUEST = 421; // RFC7540
+ const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918
+ const HTTP_LOCKED = 423; // RFC4918
+ const HTTP_FAILED_DEPENDENCY = 424; // RFC4918
+ const HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425; // RFC2817
+ const HTTP_UPGRADE_REQUIRED = 426; // RFC2817
+ const HTTP_PRECONDITION_REQUIRED = 428; // RFC6585
+ const HTTP_TOO_MANY_REQUESTS = 429; // RFC6585
+ const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; // RFC6585
+ const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451;
+ const HTTP_INTERNAL_SERVER_ERROR = 500;
+ const HTTP_NOT_IMPLEMENTED = 501;
+ const HTTP_BAD_GATEWAY = 502;
+ const HTTP_SERVICE_UNAVAILABLE = 503;
+ const HTTP_GATEWAY_TIMEOUT = 504;
+ const HTTP_VERSION_NOT_SUPPORTED = 505;
+ const HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506; // RFC2295
+ const HTTP_INSUFFICIENT_STORAGE = 507; // RFC4918
+ const HTTP_LOOP_DETECTED = 508; // RFC5842
+ const HTTP_NOT_EXTENDED = 510; // RFC2774
+ const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; // RFC6585
+
+ /**
+ * @var \Symfony\Component\HttpFoundation\ResponseHeaderBag
+ */
+ public $headers;
+
+ /**
+ * @var string
+ */
+ protected $content;
+
+ /**
+ * @var string
+ */
+ protected $version;
+
+ /**
+ * @var int
+ */
+ protected $statusCode;
+
+ /**
+ * @var string
+ */
+ protected $statusText;
+
+ /**
+ * @var string
+ */
+ protected $charset;
+
+ /**
+ * Status codes translation table.
+ *
+ * The list of codes is complete according to the
+ * {@link http://www.iana.org/assignments/http-status-codes/ Hypertext Transfer Protocol (HTTP) Status Code Registry}
+ * (last updated 2016-03-01).
+ *
+ * Unless otherwise noted, the status code is defined in RFC2616.
+ *
+ * @var array
+ */
+ public static $statusTexts = array(
+ 100 => 'Continue',
+ 101 => 'Switching Protocols',
+ 102 => 'Processing', // RFC2518
+ 103 => 'Early Hints',
+ 200 => 'OK',
+ 201 => 'Created',
+ 202 => 'Accepted',
+ 203 => 'Non-Authoritative Information',
+ 204 => 'No Content',
+ 205 => 'Reset Content',
+ 206 => 'Partial Content',
+ 207 => 'Multi-Status', // RFC4918
+ 208 => 'Already Reported', // RFC5842
+ 226 => 'IM Used', // RFC3229
+ 300 => 'Multiple Choices',
+ 301 => 'Moved Permanently',
+ 302 => 'Found',
+ 303 => 'See Other',
+ 304 => 'Not Modified',
+ 305 => 'Use Proxy',
+ 307 => 'Temporary Redirect',
+ 308 => 'Permanent Redirect', // RFC7238
+ 400 => 'Bad Request',
+ 401 => 'Unauthorized',
+ 402 => 'Payment Required',
+ 403 => 'Forbidden',
+ 404 => 'Not Found',
+ 405 => 'Method Not Allowed',
+ 406 => 'Not Acceptable',
+ 407 => 'Proxy Authentication Required',
+ 408 => 'Request Timeout',
+ 409 => 'Conflict',
+ 410 => 'Gone',
+ 411 => 'Length Required',
+ 412 => 'Precondition Failed',
+ 413 => 'Payload Too Large',
+ 414 => 'URI Too Long',
+ 415 => 'Unsupported Media Type',
+ 416 => 'Range Not Satisfiable',
+ 417 => 'Expectation Failed',
+ 418 => 'I\'m a teapot', // RFC2324
+ 421 => 'Misdirected Request', // RFC7540
+ 422 => 'Unprocessable Entity', // RFC4918
+ 423 => 'Locked', // RFC4918
+ 424 => 'Failed Dependency', // RFC4918
+ 425 => 'Reserved for WebDAV advanced collections expired proposal', // RFC2817
+ 426 => 'Upgrade Required', // RFC2817
+ 428 => 'Precondition Required', // RFC6585
+ 429 => 'Too Many Requests', // RFC6585
+ 431 => 'Request Header Fields Too Large', // RFC6585
+ 451 => 'Unavailable For Legal Reasons', // RFC7725
+ 500 => 'Internal Server Error',
+ 501 => 'Not Implemented',
+ 502 => 'Bad Gateway',
+ 503 => 'Service Unavailable',
+ 504 => 'Gateway Timeout',
+ 505 => 'HTTP Version Not Supported',
+ 506 => 'Variant Also Negotiates', // RFC2295
+ 507 => 'Insufficient Storage', // RFC4918
+ 508 => 'Loop Detected', // RFC5842
+ 510 => 'Not Extended', // RFC2774
+ 511 => 'Network Authentication Required', // RFC6585
+ );
+
+ /**
+ * @param mixed $content The response content, see setContent()
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ *
+ * @throws \InvalidArgumentException When the HTTP status code is not valid
+ */
+ public function __construct($content = '', $status = 200, $headers = array())
+ {
+ $this->headers = new ResponseHeaderBag($headers);
+ $this->setContent($content);
+ $this->setStatusCode($status);
+ $this->setProtocolVersion('1.0');
+ }
+
+ /**
+ * Factory method for chainability.
+ *
+ * Example:
+ *
+ * return Response::create($body, 200)
+ * ->setSharedMaxAge(300);
+ *
+ * @param mixed $content The response content, see setContent()
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ *
+ * @return static
+ */
+ public static function create($content = '', $status = 200, $headers = array())
+ {
+ return new static($content, $status, $headers);
+ }
+
+ /**
+ * Returns the Response as an HTTP string.
+ *
+ * The string representation of the Response is the same as the
+ * one that will be sent to the client only if the prepare() method
+ * has been called before.
+ *
+ * @return string The Response as an HTTP string
+ *
+ * @see prepare()
+ */
+ public function __toString()
+ {
+ return
+ sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)."\r\n".
+ $this->headers."\r\n".
+ $this->getContent();
+ }
+
+ /**
+ * Clones the current Response instance.
+ */
+ public function __clone()
+ {
+ $this->headers = clone $this->headers;
+ }
+
+ /**
+ * Prepares the Response before it is sent to the client.
+ *
+ * This method tweaks the Response to ensure that it is
+ * compliant with RFC 2616. Most of the changes are based on
+ * the Request that is "associated" with this Response.
+ *
+ * @return $this
+ */
+ public function prepare(Request $request)
+ {
+ $headers = $this->headers;
+
+ if ($this->isInformational() || $this->isEmpty()) {
+ $this->setContent(null);
+ $headers->remove('Content-Type');
+ $headers->remove('Content-Length');
+ } else {
+ // Content-type based on the Request
+ if (!$headers->has('Content-Type')) {
+ $format = $request->getRequestFormat();
+ if (null !== $format && $mimeType = $request->getMimeType($format)) {
+ $headers->set('Content-Type', $mimeType);
+ }
+ }
+
+ // Fix Content-Type
+ $charset = $this->charset ?: 'UTF-8';
+ if (!$headers->has('Content-Type')) {
+ $headers->set('Content-Type', 'text/html; charset='.$charset);
+ } elseif (0 === stripos($headers->get('Content-Type'), 'text/') && false === stripos($headers->get('Content-Type'), 'charset')) {
+ // add the charset
+ $headers->set('Content-Type', $headers->get('Content-Type').'; charset='.$charset);
+ }
+
+ // Fix Content-Length
+ if ($headers->has('Transfer-Encoding')) {
+ $headers->remove('Content-Length');
+ }
+
+ if ($request->isMethod('HEAD')) {
+ // cf. RFC2616 14.13
+ $length = $headers->get('Content-Length');
+ $this->setContent(null);
+ if ($length) {
+ $headers->set('Content-Length', $length);
+ }
+ }
+ }
+
+ // Fix protocol
+ if ('HTTP/1.0' != $request->server->get('SERVER_PROTOCOL')) {
+ $this->setProtocolVersion('1.1');
+ }
+
+ // Check if we need to send extra expire info headers
+ if ('1.0' == $this->getProtocolVersion() && false !== strpos($this->headers->get('Cache-Control'), 'no-cache')) {
+ $this->headers->set('pragma', 'no-cache');
+ $this->headers->set('expires', -1);
+ }
+
+ $this->ensureIEOverSSLCompatibility($request);
+
+ return $this;
+ }
+
+ /**
+ * Sends HTTP headers.
+ *
+ * @return $this
+ */
+ public function sendHeaders()
+ {
+ // headers have already been sent by the developer
+ if (headers_sent()) {
+ return $this;
+ }
+
+ // headers
+ foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) {
+ foreach ($values as $value) {
+ header($name.': '.$value, false, $this->statusCode);
+ }
+ }
+
+ // status
+ header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode);
+
+ // cookies
+ foreach ($this->headers->getCookies() as $cookie) {
+ if ($cookie->isRaw()) {
+ setrawcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly());
+ } else {
+ setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly());
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sends content for the current web response.
+ *
+ * @return $this
+ */
+ public function sendContent()
+ {
+ echo $this->content;
+
+ return $this;
+ }
+
+ /**
+ * Sends HTTP headers and content.
+ *
+ * @return $this
+ */
+ public function send()
+ {
+ $this->sendHeaders();
+ $this->sendContent();
+
+ if (function_exists('fastcgi_finish_request')) {
+ fastcgi_finish_request();
+ } elseif (!\in_array(PHP_SAPI, array('cli', 'phpdbg'), true)) {
+ static::closeOutputBuffers(0, true);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sets the response content.
+ *
+ * Valid types are strings, numbers, null, and objects that implement a __toString() method.
+ *
+ * @param mixed $content Content that can be cast to string
+ *
+ * @return $this
+ *
+ * @throws \UnexpectedValueException
+ */
+ public function setContent($content)
+ {
+ if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable(array($content, '__toString'))) {
+ throw new \UnexpectedValueException(sprintf('The Response content must be a string or object implementing __toString(), "%s" given.', gettype($content)));
+ }
+
+ $this->content = (string) $content;
+
+ return $this;
+ }
+
+ /**
+ * Gets the current response content.
+ *
+ * @return string Content
+ */
+ public function getContent()
+ {
+ return $this->content;
+ }
+
+ /**
+ * Sets the HTTP protocol version (1.0 or 1.1).
+ *
+ * @param string $version The HTTP protocol version
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setProtocolVersion($version)
+ {
+ $this->version = $version;
+
+ return $this;
+ }
+
+ /**
+ * Gets the HTTP protocol version.
+ *
+ * @return string The HTTP protocol version
+ *
+ * @final since version 3.2
+ */
+ public function getProtocolVersion()
+ {
+ return $this->version;
+ }
+
+ /**
+ * Sets the response status code.
+ *
+ * If the status text is null it will be automatically populated for the known
+ * status codes and left empty otherwise.
+ *
+ * @param int $code HTTP status code
+ * @param mixed $text HTTP status text
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException When the HTTP status code is not valid
+ *
+ * @final since version 3.2
+ */
+ public function setStatusCode($code, $text = null)
+ {
+ $this->statusCode = $code = (int) $code;
+ if ($this->isInvalid()) {
+ throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code));
+ }
+
+ if (null === $text) {
+ $this->statusText = isset(self::$statusTexts[$code]) ? self::$statusTexts[$code] : 'unknown status';
+
+ return $this;
+ }
+
+ if (false === $text) {
+ $this->statusText = '';
+
+ return $this;
+ }
+
+ $this->statusText = $text;
+
+ return $this;
+ }
+
+ /**
+ * Retrieves the status code for the current web response.
+ *
+ * @return int Status code
+ *
+ * @final since version 3.2
+ */
+ public function getStatusCode()
+ {
+ return $this->statusCode;
+ }
+
+ /**
+ * Sets the response charset.
+ *
+ * @param string $charset Character set
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setCharset($charset)
+ {
+ $this->charset = $charset;
+
+ return $this;
+ }
+
+ /**
+ * Retrieves the response charset.
+ *
+ * @return string Character set
+ *
+ * @final since version 3.2
+ */
+ public function getCharset()
+ {
+ return $this->charset;
+ }
+
+ /**
+ * Returns true if the response is worth caching under any circumstance.
+ *
+ * Responses marked "private" with an explicit Cache-Control directive are
+ * considered uncacheable.
+ *
+ * Responses with neither a freshness lifetime (Expires, max-age) nor cache
+ * validator (Last-Modified, ETag) are considered uncacheable.
+ *
+ * @return bool true if the response is worth caching, false otherwise
+ *
+ * @final since version 3.3
+ */
+ public function isCacheable()
+ {
+ if (!in_array($this->statusCode, array(200, 203, 300, 301, 302, 404, 410))) {
+ return false;
+ }
+
+ if ($this->headers->hasCacheControlDirective('no-store') || $this->headers->getCacheControlDirective('private')) {
+ return false;
+ }
+
+ return $this->isValidateable() || $this->isFresh();
+ }
+
+ /**
+ * Returns true if the response is "fresh".
+ *
+ * Fresh responses may be served from cache without any interaction with the
+ * origin. A response is considered fresh when it includes a Cache-Control/max-age
+ * indicator or Expires header and the calculated age is less than the freshness lifetime.
+ *
+ * @return bool true if the response is fresh, false otherwise
+ *
+ * @final since version 3.3
+ */
+ public function isFresh()
+ {
+ return $this->getTtl() > 0;
+ }
+
+ /**
+ * Returns true if the response includes headers that can be used to validate
+ * the response with the origin server using a conditional GET request.
+ *
+ * @return bool true if the response is validateable, false otherwise
+ *
+ * @final since version 3.3
+ */
+ public function isValidateable()
+ {
+ return $this->headers->has('Last-Modified') || $this->headers->has('ETag');
+ }
+
+ /**
+ * Marks the response as "private".
+ *
+ * It makes the response ineligible for serving other clients.
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setPrivate()
+ {
+ $this->headers->removeCacheControlDirective('public');
+ $this->headers->addCacheControlDirective('private');
+
+ return $this;
+ }
+
+ /**
+ * Marks the response as "public".
+ *
+ * It makes the response eligible for serving other clients.
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setPublic()
+ {
+ $this->headers->addCacheControlDirective('public');
+ $this->headers->removeCacheControlDirective('private');
+
+ return $this;
+ }
+
+ /**
+ * Marks the response as "immutable".
+ *
+ * @param bool $immutable enables or disables the immutable directive
+ *
+ * @return $this
+ *
+ * @final
+ */
+ public function setImmutable($immutable = true)
+ {
+ if ($immutable) {
+ $this->headers->addCacheControlDirective('immutable');
+ } else {
+ $this->headers->removeCacheControlDirective('immutable');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns true if the response is marked as "immutable".
+ *
+ * @return bool returns true if the response is marked as "immutable"; otherwise false
+ *
+ * @final
+ */
+ public function isImmutable()
+ {
+ return $this->headers->hasCacheControlDirective('immutable');
+ }
+
+ /**
+ * Returns true if the response must be revalidated by caches.
+ *
+ * This method indicates that the response must not be served stale by a
+ * cache in any circumstance without first revalidating with the origin.
+ * When present, the TTL of the response should not be overridden to be
+ * greater than the value provided by the origin.
+ *
+ * @return bool true if the response must be revalidated by a cache, false otherwise
+ *
+ * @final since version 3.3
+ */
+ public function mustRevalidate()
+ {
+ return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->hasCacheControlDirective('proxy-revalidate');
+ }
+
+ /**
+ * Returns the Date header as a DateTime instance.
+ *
+ * @return \DateTime A \DateTime instance
+ *
+ * @throws \RuntimeException When the header is not parseable
+ *
+ * @final since version 3.2
+ */
+ public function getDate()
+ {
+ return $this->headers->getDate('Date');
+ }
+
+ /**
+ * Sets the Date header.
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setDate(\DateTime $date)
+ {
+ $date->setTimezone(new \DateTimeZone('UTC'));
+ $this->headers->set('Date', $date->format('D, d M Y H:i:s').' GMT');
+
+ return $this;
+ }
+
+ /**
+ * Returns the age of the response.
+ *
+ * @return int The age of the response in seconds
+ *
+ * @final since version 3.2
+ */
+ public function getAge()
+ {
+ if (null !== $age = $this->headers->get('Age')) {
+ return (int) $age;
+ }
+
+ return max(time() - $this->getDate()->format('U'), 0);
+ }
+
+ /**
+ * Marks the response stale by setting the Age header to be equal to the maximum age of the response.
+ *
+ * @return $this
+ */
+ public function expire()
+ {
+ if ($this->isFresh()) {
+ $this->headers->set('Age', $this->getMaxAge());
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the value of the Expires header as a DateTime instance.
+ *
+ * @return \DateTime|null A DateTime instance or null if the header does not exist
+ *
+ * @final since version 3.2
+ */
+ public function getExpires()
+ {
+ try {
+ return $this->headers->getDate('Expires');
+ } catch (\RuntimeException $e) {
+ // according to RFC 2616 invalid date formats (e.g. "0" and "-1") must be treated as in the past
+ return \DateTime::createFromFormat(DATE_RFC2822, 'Sat, 01 Jan 00 00:00:00 +0000');
+ }
+ }
+
+ /**
+ * Sets the Expires HTTP header with a DateTime instance.
+ *
+ * Passing null as value will remove the header.
+ *
+ * @param \DateTime|null $date A \DateTime instance or null to remove the header
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setExpires(\DateTime $date = null)
+ {
+ if (null === $date) {
+ $this->headers->remove('Expires');
+ } else {
+ $date = clone $date;
+ $date->setTimezone(new \DateTimeZone('UTC'));
+ $this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the number of seconds after the time specified in the response's Date
+ * header when the response should no longer be considered fresh.
+ *
+ * First, it checks for a s-maxage directive, then a max-age directive, and then it falls
+ * back on an expires header. It returns null when no maximum age can be established.
+ *
+ * @return int|null Number of seconds
+ *
+ * @final since version 3.2
+ */
+ public function getMaxAge()
+ {
+ if ($this->headers->hasCacheControlDirective('s-maxage')) {
+ return (int) $this->headers->getCacheControlDirective('s-maxage');
+ }
+
+ if ($this->headers->hasCacheControlDirective('max-age')) {
+ return (int) $this->headers->getCacheControlDirective('max-age');
+ }
+
+ if (null !== $this->getExpires()) {
+ return $this->getExpires()->format('U') - $this->getDate()->format('U');
+ }
+ }
+
+ /**
+ * Sets the number of seconds after which the response should no longer be considered fresh.
+ *
+ * This methods sets the Cache-Control max-age directive.
+ *
+ * @param int $value Number of seconds
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setMaxAge($value)
+ {
+ $this->headers->addCacheControlDirective('max-age', $value);
+
+ return $this;
+ }
+
+ /**
+ * Sets the number of seconds after which the response should no longer be considered fresh by shared caches.
+ *
+ * This methods sets the Cache-Control s-maxage directive.
+ *
+ * @param int $value Number of seconds
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setSharedMaxAge($value)
+ {
+ $this->setPublic();
+ $this->headers->addCacheControlDirective('s-maxage', $value);
+
+ return $this;
+ }
+
+ /**
+ * Returns the response's time-to-live in seconds.
+ *
+ * It returns null when no freshness information is present in the response.
+ *
+ * When the responses TTL is <= 0, the response may not be served from cache without first
+ * revalidating with the origin.
+ *
+ * @return int|null The TTL in seconds
+ *
+ * @final since version 3.2
+ */
+ public function getTtl()
+ {
+ if (null !== $maxAge = $this->getMaxAge()) {
+ return $maxAge - $this->getAge();
+ }
+ }
+
+ /**
+ * Sets the response's time-to-live for shared caches.
+ *
+ * This method adjusts the Cache-Control/s-maxage directive.
+ *
+ * @param int $seconds Number of seconds
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setTtl($seconds)
+ {
+ $this->setSharedMaxAge($this->getAge() + $seconds);
+
+ return $this;
+ }
+
+ /**
+ * Sets the response's time-to-live for private/client caches.
+ *
+ * This method adjusts the Cache-Control/max-age directive.
+ *
+ * @param int $seconds Number of seconds
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setClientTtl($seconds)
+ {
+ $this->setMaxAge($this->getAge() + $seconds);
+
+ return $this;
+ }
+
+ /**
+ * Returns the Last-Modified HTTP header as a DateTime instance.
+ *
+ * @return \DateTime|null A DateTime instance or null if the header does not exist
+ *
+ * @throws \RuntimeException When the HTTP header is not parseable
+ *
+ * @final since version 3.2
+ */
+ public function getLastModified()
+ {
+ return $this->headers->getDate('Last-Modified');
+ }
+
+ /**
+ * Sets the Last-Modified HTTP header with a DateTime instance.
+ *
+ * Passing null as value will remove the header.
+ *
+ * @param \DateTime|null $date A \DateTime instance or null to remove the header
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setLastModified(\DateTime $date = null)
+ {
+ if (null === $date) {
+ $this->headers->remove('Last-Modified');
+ } else {
+ $date = clone $date;
+ $date->setTimezone(new \DateTimeZone('UTC'));
+ $this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the literal value of the ETag HTTP header.
+ *
+ * @return string|null The ETag HTTP header or null if it does not exist
+ *
+ * @final since version 3.2
+ */
+ public function getEtag()
+ {
+ return $this->headers->get('ETag');
+ }
+
+ /**
+ * Sets the ETag value.
+ *
+ * @param string|null $etag The ETag unique identifier or null to remove the header
+ * @param bool $weak Whether you want a weak ETag or not
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setEtag($etag = null, $weak = false)
+ {
+ if (null === $etag) {
+ $this->headers->remove('Etag');
+ } else {
+ if (0 !== strpos($etag, '"')) {
+ $etag = '"'.$etag.'"';
+ }
+
+ $this->headers->set('ETag', (true === $weak ? 'W/' : '').$etag);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sets the response's cache headers (validation and/or expiration).
+ *
+ * Available options are: etag, last_modified, max_age, s_maxage, private, public and immutable.
+ *
+ * @param array $options An array of cache options
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @final since version 3.3
+ */
+ public function setCache(array $options)
+ {
+ if ($diff = array_diff(array_keys($options), array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public', 'immutable'))) {
+ throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', array_values($diff))));
+ }
+
+ if (isset($options['etag'])) {
+ $this->setEtag($options['etag']);
+ }
+
+ if (isset($options['last_modified'])) {
+ $this->setLastModified($options['last_modified']);
+ }
+
+ if (isset($options['max_age'])) {
+ $this->setMaxAge($options['max_age']);
+ }
+
+ if (isset($options['s_maxage'])) {
+ $this->setSharedMaxAge($options['s_maxage']);
+ }
+
+ if (isset($options['public'])) {
+ if ($options['public']) {
+ $this->setPublic();
+ } else {
+ $this->setPrivate();
+ }
+ }
+
+ if (isset($options['private'])) {
+ if ($options['private']) {
+ $this->setPrivate();
+ } else {
+ $this->setPublic();
+ }
+ }
+
+ if (isset($options['immutable'])) {
+ $this->setImmutable((bool) $options['immutable']);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Modifies the response so that it conforms to the rules defined for a 304 status code.
+ *
+ * This sets the status, removes the body, and discards any headers
+ * that MUST NOT be included in 304 responses.
+ *
+ * @return $this
+ *
+ * @see http://tools.ietf.org/html/rfc2616#section-10.3.5
+ *
+ * @final since version 3.3
+ */
+ public function setNotModified()
+ {
+ $this->setStatusCode(304);
+ $this->setContent(null);
+
+ // remove headers that MUST NOT be included with 304 Not Modified responses
+ foreach (array('Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified') as $header) {
+ $this->headers->remove($header);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns true if the response includes a Vary header.
+ *
+ * @return bool true if the response includes a Vary header, false otherwise
+ *
+ * @final since version 3.2
+ */
+ public function hasVary()
+ {
+ return null !== $this->headers->get('Vary');
+ }
+
+ /**
+ * Returns an array of header names given in the Vary header.
+ *
+ * @return array An array of Vary names
+ *
+ * @final since version 3.2
+ */
+ public function getVary()
+ {
+ if (!$vary = $this->headers->get('Vary', null, false)) {
+ return array();
+ }
+
+ $ret = array();
+ foreach ($vary as $item) {
+ $ret = array_merge($ret, preg_split('/[\s,]+/', $item));
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Sets the Vary header.
+ *
+ * @param string|array $headers
+ * @param bool $replace Whether to replace the actual value or not (true by default)
+ *
+ * @return $this
+ *
+ * @final since version 3.2
+ */
+ public function setVary($headers, $replace = true)
+ {
+ $this->headers->set('Vary', $headers, $replace);
+
+ return $this;
+ }
+
+ /**
+ * Determines if the Response validators (ETag, Last-Modified) match
+ * a conditional value specified in the Request.
+ *
+ * If the Response is not modified, it sets the status code to 304 and
+ * removes the actual content by calling the setNotModified() method.
+ *
+ * @return bool true if the Response validators match the Request, false otherwise
+ *
+ * @final since version 3.3
+ */
+ public function isNotModified(Request $request)
+ {
+ if (!$request->isMethodCacheable()) {
+ return false;
+ }
+
+ $notModified = false;
+ $lastModified = $this->headers->get('Last-Modified');
+ $modifiedSince = $request->headers->get('If-Modified-Since');
+
+ if ($etags = $request->getETags()) {
+ $notModified = in_array($this->getEtag(), $etags) || in_array('*', $etags);
+ }
+
+ if ($modifiedSince && $lastModified) {
+ $notModified = strtotime($modifiedSince) >= strtotime($lastModified) && (!$etags || $notModified);
+ }
+
+ if ($notModified) {
+ $this->setNotModified();
+ }
+
+ return $notModified;
+ }
+
+ /**
+ * Is response invalid?
+ *
+ * @return bool
+ *
+ * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+ *
+ * @final since version 3.2
+ */
+ public function isInvalid()
+ {
+ return $this->statusCode < 100 || $this->statusCode >= 600;
+ }
+
+ /**
+ * Is response informative?
+ *
+ * @return bool
+ *
+ * @final since version 3.3
+ */
+ public function isInformational()
+ {
+ return $this->statusCode >= 100 && $this->statusCode < 200;
+ }
+
+ /**
+ * Is response successful?
+ *
+ * @return bool
+ *
+ * @final since version 3.2
+ */
+ public function isSuccessful()
+ {
+ return $this->statusCode >= 200 && $this->statusCode < 300;
+ }
+
+ /**
+ * Is the response a redirect?
+ *
+ * @return bool
+ *
+ * @final since version 3.2
+ */
+ public function isRedirection()
+ {
+ return $this->statusCode >= 300 && $this->statusCode < 400;
+ }
+
+ /**
+ * Is there a client error?
+ *
+ * @return bool
+ *
+ * @final since version 3.2
+ */
+ public function isClientError()
+ {
+ return $this->statusCode >= 400 && $this->statusCode < 500;
+ }
+
+ /**
+ * Was there a server side error?
+ *
+ * @return bool
+ *
+ * @final since version 3.3
+ */
+ public function isServerError()
+ {
+ return $this->statusCode >= 500 && $this->statusCode < 600;
+ }
+
+ /**
+ * Is the response OK?
+ *
+ * @return bool
+ *
+ * @final since version 3.2
+ */
+ public function isOk()
+ {
+ return 200 === $this->statusCode;
+ }
+
+ /**
+ * Is the response forbidden?
+ *
+ * @return bool
+ *
+ * @final since version 3.2
+ */
+ public function isForbidden()
+ {
+ return 403 === $this->statusCode;
+ }
+
+ /**
+ * Is the response a not found error?
+ *
+ * @return bool
+ *
+ * @final since version 3.2
+ */
+ public function isNotFound()
+ {
+ return 404 === $this->statusCode;
+ }
+
+ /**
+ * Is the response a redirect of some form?
+ *
+ * @param string $location
+ *
+ * @return bool
+ *
+ * @final since version 3.2
+ */
+ public function isRedirect($location = null)
+ {
+ return in_array($this->statusCode, array(201, 301, 302, 303, 307, 308)) && (null === $location ?: $location == $this->headers->get('Location'));
+ }
+
+ /**
+ * Is the response empty?
+ *
+ * @return bool
+ *
+ * @final since version 3.2
+ */
+ public function isEmpty()
+ {
+ return in_array($this->statusCode, array(204, 304));
+ }
+
+ /**
+ * Cleans or flushes output buffers up to target level.
+ *
+ * Resulting level can be greater than target level if a non-removable buffer has been encountered.
+ *
+ * @param int $targetLevel The target output buffering level
+ * @param bool $flush Whether to flush or clean the buffers
+ *
+ * @final since version 3.3
+ */
+ public static function closeOutputBuffers($targetLevel, $flush)
+ {
+ $status = ob_get_status(true);
+ $level = count($status);
+ // PHP_OUTPUT_HANDLER_* are not defined on HHVM 3.3
+ $flags = defined('PHP_OUTPUT_HANDLER_REMOVABLE') ? PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE) : -1;
+
+ while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s['del']) ? !isset($s['flags']) || ($s['flags'] & $flags) === $flags : $s['del'])) {
+ if ($flush) {
+ ob_end_flush();
+ } else {
+ ob_end_clean();
+ }
+ }
+ }
+
+ /**
+ * Checks if we need to remove Cache-Control for SSL encrypted downloads when using IE < 9.
+ *
+ * @see http://support.microsoft.com/kb/323308
+ *
+ * @final since version 3.3
+ */
+ protected function ensureIEOverSSLCompatibility(Request $request)
+ {
+ if (false !== stripos($this->headers->get('Content-Disposition'), 'attachment') && 1 == preg_match('/MSIE (.*?);/i', $request->server->get('HTTP_USER_AGENT'), $match) && true === $request->isSecure()) {
+ if ((int) preg_replace('/(MSIE )(.*?);/', '$2', $match[0]) < 9) {
+ $this->headers->remove('Cache-Control');
+ }
+ }
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/ResponseHeaderBag.php b/assets/php/vendor/symfony/http-foundation/ResponseHeaderBag.php
new file mode 100644
index 0000000..11a8593
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/ResponseHeaderBag.php
@@ -0,0 +1,340 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * ResponseHeaderBag is a container for Response HTTP headers.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ResponseHeaderBag extends HeaderBag
+{
+ const COOKIES_FLAT = 'flat';
+ const COOKIES_ARRAY = 'array';
+
+ const DISPOSITION_ATTACHMENT = 'attachment';
+ const DISPOSITION_INLINE = 'inline';
+
+ protected $computedCacheControl = array();
+ protected $cookies = array();
+ protected $headerNames = array();
+
+ public function __construct(array $headers = array())
+ {
+ parent::__construct($headers);
+
+ if (!isset($this->headers['cache-control'])) {
+ $this->set('Cache-Control', '');
+ }
+
+ /* RFC2616 - 14.18 says all Responses need to have a Date */
+ if (!isset($this->headers['date'])) {
+ $this->initDate();
+ }
+ }
+
+ /**
+ * Returns the headers, with original capitalizations.
+ *
+ * @return array An array of headers
+ */
+ public function allPreserveCase()
+ {
+ $headers = array();
+ foreach ($this->all() as $name => $value) {
+ $headers[isset($this->headerNames[$name]) ? $this->headerNames[$name] : $name] = $value;
+ }
+
+ return $headers;
+ }
+
+ public function allPreserveCaseWithoutCookies()
+ {
+ $headers = $this->allPreserveCase();
+ if (isset($this->headerNames['set-cookie'])) {
+ unset($headers[$this->headerNames['set-cookie']]);
+ }
+
+ return $headers;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function replace(array $headers = array())
+ {
+ $this->headerNames = array();
+
+ parent::replace($headers);
+
+ if (!isset($this->headers['cache-control'])) {
+ $this->set('Cache-Control', '');
+ }
+
+ if (!isset($this->headers['date'])) {
+ $this->initDate();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all()
+ {
+ $headers = parent::all();
+ foreach ($this->getCookies() as $cookie) {
+ $headers['set-cookie'][] = (string) $cookie;
+ }
+
+ return $headers;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($key, $values, $replace = true)
+ {
+ $uniqueKey = str_replace('_', '-', strtolower($key));
+
+ if ('set-cookie' === $uniqueKey) {
+ if ($replace) {
+ $this->cookies = array();
+ }
+ foreach ((array) $values as $cookie) {
+ $this->setCookie(Cookie::fromString($cookie));
+ }
+ $this->headerNames[$uniqueKey] = $key;
+
+ return;
+ }
+
+ $this->headerNames[$uniqueKey] = $key;
+
+ parent::set($key, $values, $replace);
+
+ // ensure the cache-control header has sensible defaults
+ if (\in_array($uniqueKey, array('cache-control', 'etag', 'last-modified', 'expires'), true)) {
+ $computed = $this->computeCacheControlValue();
+ $this->headers['cache-control'] = array($computed);
+ $this->headerNames['cache-control'] = 'Cache-Control';
+ $this->computedCacheControl = $this->parseCacheControl($computed);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function remove($key)
+ {
+ $uniqueKey = str_replace('_', '-', strtolower($key));
+ unset($this->headerNames[$uniqueKey]);
+
+ if ('set-cookie' === $uniqueKey) {
+ $this->cookies = array();
+
+ return;
+ }
+
+ parent::remove($key);
+
+ if ('cache-control' === $uniqueKey) {
+ $this->computedCacheControl = array();
+ }
+
+ if ('date' === $uniqueKey) {
+ $this->initDate();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hasCacheControlDirective($key)
+ {
+ return array_key_exists($key, $this->computedCacheControl);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCacheControlDirective($key)
+ {
+ return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
+ }
+
+ public function setCookie(Cookie $cookie)
+ {
+ $this->cookies[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie;
+ $this->headerNames['set-cookie'] = 'Set-Cookie';
+ }
+
+ /**
+ * Removes a cookie from the array, but does not unset it in the browser.
+ *
+ * @param string $name
+ * @param string $path
+ * @param string $domain
+ */
+ public function removeCookie($name, $path = '/', $domain = null)
+ {
+ if (null === $path) {
+ $path = '/';
+ }
+
+ unset($this->cookies[$domain][$path][$name]);
+
+ if (empty($this->cookies[$domain][$path])) {
+ unset($this->cookies[$domain][$path]);
+
+ if (empty($this->cookies[$domain])) {
+ unset($this->cookies[$domain]);
+ }
+ }
+
+ if (empty($this->cookies)) {
+ unset($this->headerNames['set-cookie']);
+ }
+ }
+
+ /**
+ * Returns an array with all cookies.
+ *
+ * @param string $format
+ *
+ * @return array
+ *
+ * @throws \InvalidArgumentException When the $format is invalid
+ */
+ public function getCookies($format = self::COOKIES_FLAT)
+ {
+ if (!in_array($format, array(self::COOKIES_FLAT, self::COOKIES_ARRAY))) {
+ throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', array(self::COOKIES_FLAT, self::COOKIES_ARRAY))));
+ }
+
+ if (self::COOKIES_ARRAY === $format) {
+ return $this->cookies;
+ }
+
+ $flattenedCookies = array();
+ foreach ($this->cookies as $path) {
+ foreach ($path as $cookies) {
+ foreach ($cookies as $cookie) {
+ $flattenedCookies[] = $cookie;
+ }
+ }
+ }
+
+ return $flattenedCookies;
+ }
+
+ /**
+ * Clears a cookie in the browser.
+ *
+ * @param string $name
+ * @param string $path
+ * @param string $domain
+ * @param bool $secure
+ * @param bool $httpOnly
+ */
+ public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true)
+ {
+ $this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly));
+ }
+
+ /**
+ * Generates a HTTP Content-Disposition field-value.
+ *
+ * @param string $disposition One of "inline" or "attachment"
+ * @param string $filename A unicode string
+ * @param string $filenameFallback A string containing only ASCII characters that
+ * is semantically equivalent to $filename. If the filename is already ASCII,
+ * it can be omitted, or just copied from $filename
+ *
+ * @return string A string suitable for use as a Content-Disposition field-value
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @see RFC 6266
+ */
+ public function makeDisposition($disposition, $filename, $filenameFallback = '')
+ {
+ if (!in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) {
+ throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));
+ }
+
+ if ('' == $filenameFallback) {
+ $filenameFallback = $filename;
+ }
+
+ // filenameFallback is not ASCII.
+ if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) {
+ throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.');
+ }
+
+ // percent characters aren't safe in fallback.
+ if (false !== strpos($filenameFallback, '%')) {
+ throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.');
+ }
+
+ // path separators aren't allowed in either.
+ if (false !== strpos($filename, '/') || false !== strpos($filename, '\\') || false !== strpos($filenameFallback, '/') || false !== strpos($filenameFallback, '\\')) {
+ throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
+ }
+
+ $output = sprintf('%s; filename="%s"', $disposition, str_replace('"', '\\"', $filenameFallback));
+
+ if ($filename !== $filenameFallback) {
+ $output .= sprintf("; filename*=utf-8''%s", rawurlencode($filename));
+ }
+
+ return $output;
+ }
+
+ /**
+ * Returns the calculated value of the cache-control header.
+ *
+ * This considers several other headers and calculates or modifies the
+ * cache-control header to a sensible, conservative value.
+ *
+ * @return string
+ */
+ protected function computeCacheControlValue()
+ {
+ if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) {
+ return 'no-cache, private';
+ }
+
+ if (!$this->cacheControl) {
+ // conservative by default
+ return 'private, must-revalidate';
+ }
+
+ $header = $this->getCacheControlHeader();
+ if (isset($this->cacheControl['public']) || isset($this->cacheControl['private'])) {
+ return $header;
+ }
+
+ // public if s-maxage is defined, private otherwise
+ if (!isset($this->cacheControl['s-maxage'])) {
+ return $header.', private';
+ }
+
+ return $header;
+ }
+
+ private function initDate()
+ {
+ $now = \DateTime::createFromFormat('U', time());
+ $now->setTimezone(new \DateTimeZone('UTC'));
+ $this->set('Date', $now->format('D, d M Y H:i:s').' GMT');
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/ServerBag.php b/assets/php/vendor/symfony/http-foundation/ServerBag.php
new file mode 100644
index 0000000..19d2022
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/ServerBag.php
@@ -0,0 +1,102 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * ServerBag is a container for HTTP headers from the $_SERVER variable.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
+ * @author Robert Kiss <kepten@gmail.com>
+ */
+class ServerBag extends ParameterBag
+{
+ /**
+ * Gets the HTTP headers.
+ *
+ * @return array
+ */
+ public function getHeaders()
+ {
+ $headers = array();
+ $contentHeaders = array('CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true);
+ foreach ($this->parameters as $key => $value) {
+ if (0 === strpos($key, 'HTTP_')) {
+ $headers[substr($key, 5)] = $value;
+ }
+ // CONTENT_* are not prefixed with HTTP_
+ elseif (isset($contentHeaders[$key])) {
+ $headers[$key] = $value;
+ }
+ }
+
+ if (isset($this->parameters['PHP_AUTH_USER'])) {
+ $headers['PHP_AUTH_USER'] = $this->parameters['PHP_AUTH_USER'];
+ $headers['PHP_AUTH_PW'] = isset($this->parameters['PHP_AUTH_PW']) ? $this->parameters['PHP_AUTH_PW'] : '';
+ } else {
+ /*
+ * php-cgi under Apache does not pass HTTP Basic user/pass to PHP by default
+ * For this workaround to work, add these lines to your .htaccess file:
+ * RewriteCond %{HTTP:Authorization} ^(.+)$
+ * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+ *
+ * A sample .htaccess file:
+ * RewriteEngine On
+ * RewriteCond %{HTTP:Authorization} ^(.+)$
+ * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+ * RewriteCond %{REQUEST_FILENAME} !-f
+ * RewriteRule ^(.*)$ app.php [QSA,L]
+ */
+
+ $authorizationHeader = null;
+ if (isset($this->parameters['HTTP_AUTHORIZATION'])) {
+ $authorizationHeader = $this->parameters['HTTP_AUTHORIZATION'];
+ } elseif (isset($this->parameters['REDIRECT_HTTP_AUTHORIZATION'])) {
+ $authorizationHeader = $this->parameters['REDIRECT_HTTP_AUTHORIZATION'];
+ }
+
+ if (null !== $authorizationHeader) {
+ if (0 === stripos($authorizationHeader, 'basic ')) {
+ // Decode AUTHORIZATION header into PHP_AUTH_USER and PHP_AUTH_PW when authorization header is basic
+ $exploded = explode(':', base64_decode(substr($authorizationHeader, 6)), 2);
+ if (2 == count($exploded)) {
+ list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded;
+ }
+ } elseif (empty($this->parameters['PHP_AUTH_DIGEST']) && (0 === stripos($authorizationHeader, 'digest '))) {
+ // In some circumstances PHP_AUTH_DIGEST needs to be set
+ $headers['PHP_AUTH_DIGEST'] = $authorizationHeader;
+ $this->parameters['PHP_AUTH_DIGEST'] = $authorizationHeader;
+ } elseif (0 === stripos($authorizationHeader, 'bearer ')) {
+ /*
+ * XXX: Since there is no PHP_AUTH_BEARER in PHP predefined variables,
+ * I'll just set $headers['AUTHORIZATION'] here.
+ * http://php.net/manual/en/reserved.variables.server.php
+ */
+ $headers['AUTHORIZATION'] = $authorizationHeader;
+ }
+ }
+ }
+
+ if (isset($headers['AUTHORIZATION'])) {
+ return $headers;
+ }
+
+ // PHP_AUTH_USER/PHP_AUTH_PW
+ if (isset($headers['PHP_AUTH_USER'])) {
+ $headers['AUTHORIZATION'] = 'Basic '.base64_encode($headers['PHP_AUTH_USER'].':'.$headers['PHP_AUTH_PW']);
+ } elseif (isset($headers['PHP_AUTH_DIGEST'])) {
+ $headers['AUTHORIZATION'] = $headers['PHP_AUTH_DIGEST'];
+ }
+
+ return $headers;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php b/assets/php/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php
new file mode 100644
index 0000000..ea1fda2
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php
@@ -0,0 +1,148 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Attribute;
+
+/**
+ * This class relates to session attribute storage.
+ */
+class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Countable
+{
+ private $name = 'attributes';
+ private $storageKey;
+
+ protected $attributes = array();
+
+ /**
+ * @param string $storageKey The key used to store attributes in the session
+ */
+ public function __construct($storageKey = '_sf2_attributes')
+ {
+ $this->storageKey = $storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(array &$attributes)
+ {
+ $this->attributes = &$attributes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStorageKey()
+ {
+ return $this->storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has($name)
+ {
+ return array_key_exists($name, $this->attributes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get($name, $default = null)
+ {
+ return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($name, $value)
+ {
+ $this->attributes[$name] = $value;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function replace(array $attributes)
+ {
+ $this->attributes = array();
+ foreach ($attributes as $key => $value) {
+ $this->set($key, $value);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function remove($name)
+ {
+ $retval = null;
+ if (array_key_exists($name, $this->attributes)) {
+ $retval = $this->attributes[$name];
+ unset($this->attributes[$name]);
+ }
+
+ return $retval;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ $return = $this->attributes;
+ $this->attributes = array();
+
+ return $return;
+ }
+
+ /**
+ * Returns an iterator for attributes.
+ *
+ * @return \ArrayIterator An \ArrayIterator instance
+ */
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->attributes);
+ }
+
+ /**
+ * Returns the number of attributes.
+ *
+ * @return int The number of attributes
+ */
+ public function count()
+ {
+ return count($this->attributes);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php b/assets/php/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php
new file mode 100644
index 0000000..0d8d179
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php
@@ -0,0 +1,72 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Attribute;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * Attributes store.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+interface AttributeBagInterface extends SessionBagInterface
+{
+ /**
+ * Checks if an attribute is defined.
+ *
+ * @param string $name The attribute name
+ *
+ * @return bool true if the attribute is defined, false otherwise
+ */
+ public function has($name);
+
+ /**
+ * Returns an attribute.
+ *
+ * @param string $name The attribute name
+ * @param mixed $default The default value if not found
+ *
+ * @return mixed
+ */
+ public function get($name, $default = null);
+
+ /**
+ * Sets an attribute.
+ *
+ * @param string $name
+ * @param mixed $value
+ */
+ public function set($name, $value);
+
+ /**
+ * Returns attributes.
+ *
+ * @return array Attributes
+ */
+ public function all();
+
+ /**
+ * Sets attributes.
+ *
+ * @param array $attributes Attributes
+ */
+ public function replace(array $attributes);
+
+ /**
+ * Removes an attribute.
+ *
+ * @param string $name
+ *
+ * @return mixed The removed value or null when it does not exist
+ */
+ public function remove($name);
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php b/assets/php/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php
new file mode 100644
index 0000000..abbf37e
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php
@@ -0,0 +1,153 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Attribute;
+
+/**
+ * This class provides structured storage of session attributes using
+ * a name spacing character in the key.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class NamespacedAttributeBag extends AttributeBag
+{
+ private $namespaceCharacter;
+
+ /**
+ * @param string $storageKey Session storage key
+ * @param string $namespaceCharacter Namespace character to use in keys
+ */
+ public function __construct($storageKey = '_sf2_attributes', $namespaceCharacter = '/')
+ {
+ $this->namespaceCharacter = $namespaceCharacter;
+ parent::__construct($storageKey);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has($name)
+ {
+ // reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is
+ $attributes = $this->resolveAttributePath($name);
+ $name = $this->resolveKey($name);
+
+ if (null === $attributes) {
+ return false;
+ }
+
+ return array_key_exists($name, $attributes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get($name, $default = null)
+ {
+ // reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is
+ $attributes = $this->resolveAttributePath($name);
+ $name = $this->resolveKey($name);
+
+ if (null === $attributes) {
+ return $default;
+ }
+
+ return array_key_exists($name, $attributes) ? $attributes[$name] : $default;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($name, $value)
+ {
+ $attributes = &$this->resolveAttributePath($name, true);
+ $name = $this->resolveKey($name);
+ $attributes[$name] = $value;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function remove($name)
+ {
+ $retval = null;
+ $attributes = &$this->resolveAttributePath($name);
+ $name = $this->resolveKey($name);
+ if (null !== $attributes && array_key_exists($name, $attributes)) {
+ $retval = $attributes[$name];
+ unset($attributes[$name]);
+ }
+
+ return $retval;
+ }
+
+ /**
+ * Resolves a path in attributes property and returns it as a reference.
+ *
+ * This method allows structured namespacing of session attributes.
+ *
+ * @param string $name Key name
+ * @param bool $writeContext Write context, default false
+ *
+ * @return array
+ */
+ protected function &resolveAttributePath($name, $writeContext = false)
+ {
+ $array = &$this->attributes;
+ $name = (0 === strpos($name, $this->namespaceCharacter)) ? substr($name, 1) : $name;
+
+ // Check if there is anything to do, else return
+ if (!$name) {
+ return $array;
+ }
+
+ $parts = explode($this->namespaceCharacter, $name);
+ if (count($parts) < 2) {
+ if (!$writeContext) {
+ return $array;
+ }
+
+ $array[$parts[0]] = array();
+
+ return $array;
+ }
+
+ unset($parts[count($parts) - 1]);
+
+ foreach ($parts as $part) {
+ if (null !== $array && !array_key_exists($part, $array)) {
+ $array[$part] = $writeContext ? array() : null;
+ }
+
+ $array = &$array[$part];
+ }
+
+ return $array;
+ }
+
+ /**
+ * Resolves the key from the name.
+ *
+ * This is the last part in a dot separated string.
+ *
+ * @param string $name
+ *
+ * @return string
+ */
+ protected function resolveKey($name)
+ {
+ if (false !== $pos = strrpos($name, $this->namespaceCharacter)) {
+ $name = substr($name, $pos + 1);
+ }
+
+ return $name;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php b/assets/php/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php
new file mode 100644
index 0000000..77521c2
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php
@@ -0,0 +1,161 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Flash;
+
+/**
+ * AutoExpireFlashBag flash message container.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class AutoExpireFlashBag implements FlashBagInterface
+{
+ private $name = 'flashes';
+ private $flashes = array('display' => array(), 'new' => array());
+ private $storageKey;
+
+ /**
+ * @param string $storageKey The key used to store flashes in the session
+ */
+ public function __construct($storageKey = '_symfony_flashes')
+ {
+ $this->storageKey = $storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(array &$flashes)
+ {
+ $this->flashes = &$flashes;
+
+ // The logic: messages from the last request will be stored in new, so we move them to previous
+ // This request we will show what is in 'display'. What is placed into 'new' this time round will
+ // be moved to display next time round.
+ $this->flashes['display'] = array_key_exists('new', $this->flashes) ? $this->flashes['new'] : array();
+ $this->flashes['new'] = array();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add($type, $message)
+ {
+ $this->flashes['new'][$type][] = $message;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function peek($type, array $default = array())
+ {
+ return $this->has($type) ? $this->flashes['display'][$type] : $default;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function peekAll()
+ {
+ return array_key_exists('display', $this->flashes) ? (array) $this->flashes['display'] : array();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get($type, array $default = array())
+ {
+ $return = $default;
+
+ if (!$this->has($type)) {
+ return $return;
+ }
+
+ if (isset($this->flashes['display'][$type])) {
+ $return = $this->flashes['display'][$type];
+ unset($this->flashes['display'][$type]);
+ }
+
+ return $return;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all()
+ {
+ $return = $this->flashes['display'];
+ $this->flashes['display'] = array();
+
+ return $return;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setAll(array $messages)
+ {
+ $this->flashes['new'] = $messages;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($type, $messages)
+ {
+ $this->flashes['new'][$type] = (array) $messages;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has($type)
+ {
+ return array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function keys()
+ {
+ return array_keys($this->flashes['display']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStorageKey()
+ {
+ return $this->storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ return $this->all();
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Flash/FlashBag.php b/assets/php/vendor/symfony/http-foundation/Session/Flash/FlashBag.php
new file mode 100644
index 0000000..12fb740
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Flash/FlashBag.php
@@ -0,0 +1,152 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Flash;
+
+/**
+ * FlashBag flash message container.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class FlashBag implements FlashBagInterface
+{
+ private $name = 'flashes';
+ private $flashes = array();
+ private $storageKey;
+
+ /**
+ * @param string $storageKey The key used to store flashes in the session
+ */
+ public function __construct($storageKey = '_symfony_flashes')
+ {
+ $this->storageKey = $storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(array &$flashes)
+ {
+ $this->flashes = &$flashes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add($type, $message)
+ {
+ $this->flashes[$type][] = $message;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function peek($type, array $default = array())
+ {
+ return $this->has($type) ? $this->flashes[$type] : $default;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function peekAll()
+ {
+ return $this->flashes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get($type, array $default = array())
+ {
+ if (!$this->has($type)) {
+ return $default;
+ }
+
+ $return = $this->flashes[$type];
+
+ unset($this->flashes[$type]);
+
+ return $return;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all()
+ {
+ $return = $this->peekAll();
+ $this->flashes = array();
+
+ return $return;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($type, $messages)
+ {
+ $this->flashes[$type] = (array) $messages;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setAll(array $messages)
+ {
+ $this->flashes = $messages;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has($type)
+ {
+ return array_key_exists($type, $this->flashes) && $this->flashes[$type];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function keys()
+ {
+ return array_keys($this->flashes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStorageKey()
+ {
+ return $this->storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ return $this->all();
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php b/assets/php/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php
new file mode 100644
index 0000000..80e97f1
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php
@@ -0,0 +1,93 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Flash;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * FlashBagInterface.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+interface FlashBagInterface extends SessionBagInterface
+{
+ /**
+ * Adds a flash message for type.
+ *
+ * @param string $type
+ * @param string $message
+ */
+ public function add($type, $message);
+
+ /**
+ * Registers a message for a given type.
+ *
+ * @param string $type
+ * @param string|array $message
+ */
+ public function set($type, $message);
+
+ /**
+ * Gets flash messages for a given type.
+ *
+ * @param string $type Message category type
+ * @param array $default Default value if $type does not exist
+ *
+ * @return array
+ */
+ public function peek($type, array $default = array());
+
+ /**
+ * Gets all flash messages.
+ *
+ * @return array
+ */
+ public function peekAll();
+
+ /**
+ * Gets and clears flash from the stack.
+ *
+ * @param string $type
+ * @param array $default Default value if $type does not exist
+ *
+ * @return array
+ */
+ public function get($type, array $default = array());
+
+ /**
+ * Gets and clears flashes from the stack.
+ *
+ * @return array
+ */
+ public function all();
+
+ /**
+ * Sets all flash messages.
+ */
+ public function setAll(array $messages);
+
+ /**
+ * Has flash messages for a given type?
+ *
+ * @param string $type
+ *
+ * @return bool
+ */
+ public function has($type);
+
+ /**
+ * Returns a list of all defined types.
+ *
+ * @return array
+ */
+ public function keys();
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Session.php b/assets/php/vendor/symfony/http-foundation/Session/Session.php
new file mode 100644
index 0000000..a46cffb
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Session.php
@@ -0,0 +1,273 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session;
+
+use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
+use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Drak <drak@zikula.org>
+ */
+class Session implements SessionInterface, \IteratorAggregate, \Countable
+{
+ protected $storage;
+
+ private $flashName;
+ private $attributeName;
+ private $data = array();
+ private $hasBeenStarted;
+
+ /**
+ * @param SessionStorageInterface $storage A SessionStorageInterface instance
+ * @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag)
+ * @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag)
+ */
+ public function __construct(SessionStorageInterface $storage = null, AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null)
+ {
+ $this->storage = $storage ?: new NativeSessionStorage();
+
+ $attributes = $attributes ?: new AttributeBag();
+ $this->attributeName = $attributes->getName();
+ $this->registerBag($attributes);
+
+ $flashes = $flashes ?: new FlashBag();
+ $this->flashName = $flashes->getName();
+ $this->registerBag($flashes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function start()
+ {
+ return $this->storage->start();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has($name)
+ {
+ return $this->getAttributeBag()->has($name);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get($name, $default = null)
+ {
+ return $this->getAttributeBag()->get($name, $default);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set($name, $value)
+ {
+ $this->getAttributeBag()->set($name, $value);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all()
+ {
+ return $this->getAttributeBag()->all();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function replace(array $attributes)
+ {
+ $this->getAttributeBag()->replace($attributes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function remove($name)
+ {
+ return $this->getAttributeBag()->remove($name);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ $this->getAttributeBag()->clear();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isStarted()
+ {
+ return $this->storage->isStarted();
+ }
+
+ /**
+ * Returns an iterator for attributes.
+ *
+ * @return \ArrayIterator An \ArrayIterator instance
+ */
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->getAttributeBag()->all());
+ }
+
+ /**
+ * Returns the number of attributes.
+ *
+ * @return int The number of attributes
+ */
+ public function count()
+ {
+ return count($this->getAttributeBag()->all());
+ }
+
+ /**
+ * @return bool
+ *
+ * @internal
+ */
+ public function hasBeenStarted()
+ {
+ return $this->hasBeenStarted;
+ }
+
+ /**
+ * @return bool
+ *
+ * @internal
+ */
+ public function isEmpty()
+ {
+ foreach ($this->data as &$data) {
+ if (!empty($data)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function invalidate($lifetime = null)
+ {
+ $this->storage->clear();
+
+ return $this->migrate(true, $lifetime);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function migrate($destroy = false, $lifetime = null)
+ {
+ return $this->storage->regenerate($destroy, $lifetime);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save()
+ {
+ $this->storage->save();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return $this->storage->getId();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setId($id)
+ {
+ $this->storage->setId($id);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->storage->getName();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setName($name)
+ {
+ $this->storage->setName($name);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getMetadataBag()
+ {
+ return $this->storage->getMetadataBag();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function registerBag(SessionBagInterface $bag)
+ {
+ $this->storage->registerBag(new SessionBagProxy($bag, $this->data, $this->hasBeenStarted));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getBag($name)
+ {
+ return $this->storage->getBag($name)->getBag();
+ }
+
+ /**
+ * Gets the flashbag interface.
+ *
+ * @return FlashBagInterface
+ */
+ public function getFlashBag()
+ {
+ return $this->getBag($this->flashName);
+ }
+
+ /**
+ * Gets the attributebag interface.
+ *
+ * Note that this method was added to help with IDE autocompletion.
+ *
+ * @return AttributeBagInterface
+ */
+ private function getAttributeBag()
+ {
+ return $this->getBag($this->attributeName);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/SessionBagInterface.php b/assets/php/vendor/symfony/http-foundation/Session/SessionBagInterface.php
new file mode 100644
index 0000000..8e37d06
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/SessionBagInterface.php
@@ -0,0 +1,46 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session;
+
+/**
+ * Session Bag store.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+interface SessionBagInterface
+{
+ /**
+ * Gets this bag's name.
+ *
+ * @return string
+ */
+ public function getName();
+
+ /**
+ * Initializes the Bag.
+ */
+ public function initialize(array &$array);
+
+ /**
+ * Gets the storage key for this bag.
+ *
+ * @return string
+ */
+ public function getStorageKey();
+
+ /**
+ * Clears out data from bag.
+ *
+ * @return mixed Whatever data was contained
+ */
+ public function clear();
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/SessionBagProxy.php b/assets/php/vendor/symfony/http-foundation/Session/SessionBagProxy.php
new file mode 100644
index 0000000..307836d
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/SessionBagProxy.php
@@ -0,0 +1,82 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session;
+
+/**
+ * @author Nicolas Grekas <p@tchwork.com>
+ *
+ * @internal
+ */
+final class SessionBagProxy implements SessionBagInterface
+{
+ private $bag;
+ private $data;
+ private $hasBeenStarted;
+
+ public function __construct(SessionBagInterface $bag, array &$data, &$hasBeenStarted)
+ {
+ $this->bag = $bag;
+ $this->data = &$data;
+ $this->hasBeenStarted = &$hasBeenStarted;
+ }
+
+ /**
+ * @return SessionBagInterface
+ */
+ public function getBag()
+ {
+ return $this->bag;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isEmpty()
+ {
+ return empty($this->data[$this->bag->getStorageKey()]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->bag->getName();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(array &$array)
+ {
+ $this->hasBeenStarted = true;
+ $this->data[$this->bag->getStorageKey()] = &$array;
+
+ $this->bag->initialize($array);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStorageKey()
+ {
+ return $this->bag->getStorageKey();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ return $this->bag->clear();
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/SessionInterface.php b/assets/php/vendor/symfony/http-foundation/Session/SessionInterface.php
new file mode 100644
index 0000000..95fca85
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/SessionInterface.php
@@ -0,0 +1,180 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session;
+
+use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
+
+/**
+ * Interface for the session.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+interface SessionInterface
+{
+ /**
+ * Starts the session storage.
+ *
+ * @return bool True if session started
+ *
+ * @throws \RuntimeException if session fails to start
+ */
+ public function start();
+
+ /**
+ * Returns the session ID.
+ *
+ * @return string The session ID
+ */
+ public function getId();
+
+ /**
+ * Sets the session ID.
+ *
+ * @param string $id
+ */
+ public function setId($id);
+
+ /**
+ * Returns the session name.
+ *
+ * @return mixed The session name
+ */
+ public function getName();
+
+ /**
+ * Sets the session name.
+ *
+ * @param string $name
+ */
+ public function setName($name);
+
+ /**
+ * Invalidates the current session.
+ *
+ * Clears all session attributes and flashes and regenerates the
+ * session and deletes the old session from persistence.
+ *
+ * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value
+ * will leave the system settings unchanged, 0 sets the cookie
+ * to expire with browser session. Time is in seconds, and is
+ * not a Unix timestamp.
+ *
+ * @return bool True if session invalidated, false if error
+ */
+ public function invalidate($lifetime = null);
+
+ /**
+ * Migrates the current session to a new session id while maintaining all
+ * session attributes.
+ *
+ * @param bool $destroy Whether to delete the old session or leave it to garbage collection
+ * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value
+ * will leave the system settings unchanged, 0 sets the cookie
+ * to expire with browser session. Time is in seconds, and is
+ * not a Unix timestamp.
+ *
+ * @return bool True if session migrated, false if error
+ */
+ public function migrate($destroy = false, $lifetime = null);
+
+ /**
+ * Force the session to be saved and closed.
+ *
+ * This method is generally not required for real sessions as
+ * the session will be automatically saved at the end of
+ * code execution.
+ */
+ public function save();
+
+ /**
+ * Checks if an attribute is defined.
+ *
+ * @param string $name The attribute name
+ *
+ * @return bool true if the attribute is defined, false otherwise
+ */
+ public function has($name);
+
+ /**
+ * Returns an attribute.
+ *
+ * @param string $name The attribute name
+ * @param mixed $default The default value if not found
+ *
+ * @return mixed
+ */
+ public function get($name, $default = null);
+
+ /**
+ * Sets an attribute.
+ *
+ * @param string $name
+ * @param mixed $value
+ */
+ public function set($name, $value);
+
+ /**
+ * Returns attributes.
+ *
+ * @return array Attributes
+ */
+ public function all();
+
+ /**
+ * Sets attributes.
+ *
+ * @param array $attributes Attributes
+ */
+ public function replace(array $attributes);
+
+ /**
+ * Removes an attribute.
+ *
+ * @param string $name
+ *
+ * @return mixed The removed value or null when it does not exist
+ */
+ public function remove($name);
+
+ /**
+ * Clears all attributes.
+ */
+ public function clear();
+
+ /**
+ * Checks if the session was started.
+ *
+ * @return bool
+ */
+ public function isStarted();
+
+ /**
+ * Registers a SessionBagInterface with the session.
+ */
+ public function registerBag(SessionBagInterface $bag);
+
+ /**
+ * Gets a bag instance by name.
+ *
+ * @param string $name
+ *
+ * @return SessionBagInterface
+ */
+ public function getBag($name);
+
+ /**
+ * Gets session meta.
+ *
+ * @return MetadataBag
+ */
+ public function getMetadataBag();
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php
new file mode 100644
index 0000000..6ae1355
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php
@@ -0,0 +1,168 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * This abstract session handler provides a generic implementation
+ * of the PHP 7.0 SessionUpdateTimestampHandlerInterface,
+ * enabling strict and lazy session handling.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+abstract class AbstractSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
+{
+ private $sessionName;
+ private $prefetchId;
+ private $prefetchData;
+ private $newSessionId;
+ private $igbinaryEmptyData;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function open($savePath, $sessionName)
+ {
+ $this->sessionName = $sessionName;
+ if (!headers_sent() && !ini_get('session.cache_limiter') && '0' !== ini_get('session.cache_limiter')) {
+ header(sprintf('Cache-Control: max-age=%d, private, must-revalidate', 60 * (int) ini_get('session.cache_expire')));
+ }
+
+ return true;
+ }
+
+ /**
+ * @param string $sessionId
+ *
+ * @return string
+ */
+ abstract protected function doRead($sessionId);
+
+ /**
+ * @param string $sessionId
+ * @param string $data
+ *
+ * @return bool
+ */
+ abstract protected function doWrite($sessionId, $data);
+
+ /**
+ * @param string $sessionId
+ *
+ * @return bool
+ */
+ abstract protected function doDestroy($sessionId);
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validateId($sessionId)
+ {
+ $this->prefetchData = $this->read($sessionId);
+ $this->prefetchId = $sessionId;
+
+ return '' !== $this->prefetchData;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function read($sessionId)
+ {
+ if (null !== $this->prefetchId) {
+ $prefetchId = $this->prefetchId;
+ $prefetchData = $this->prefetchData;
+ $this->prefetchId = $this->prefetchData = null;
+
+ if ($prefetchId === $sessionId || '' === $prefetchData) {
+ $this->newSessionId = '' === $prefetchData ? $sessionId : null;
+
+ return $prefetchData;
+ }
+ }
+
+ $data = $this->doRead($sessionId);
+ $this->newSessionId = '' === $data ? $sessionId : null;
+ if (\PHP_VERSION_ID < 70000) {
+ $this->prefetchData = $data;
+ }
+
+ return $data;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write($sessionId, $data)
+ {
+ if (\PHP_VERSION_ID < 70000 && $this->prefetchData) {
+ $readData = $this->prefetchData;
+ $this->prefetchData = null;
+
+ if ($readData === $data) {
+ return $this->updateTimestamp($sessionId, $data);
+ }
+ }
+ if (null === $this->igbinaryEmptyData) {
+ // see https://github.com/igbinary/igbinary/issues/146
+ $this->igbinaryEmptyData = \function_exists('igbinary_serialize') ? igbinary_serialize(array()) : '';
+ }
+ if ('' === $data || $this->igbinaryEmptyData === $data) {
+ return $this->destroy($sessionId);
+ }
+ $this->newSessionId = null;
+
+ return $this->doWrite($sessionId, $data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function destroy($sessionId)
+ {
+ if (\PHP_VERSION_ID < 70000) {
+ $this->prefetchData = null;
+ }
+ if (!headers_sent() && ini_get('session.use_cookies')) {
+ if (!$this->sessionName) {
+ throw new \LogicException(sprintf('Session name cannot be empty, did you forget to call "parent::open()" in "%s"?.', get_class($this)));
+ }
+ $sessionCookie = sprintf(' %s=', urlencode($this->sessionName));
+ $sessionCookieWithId = sprintf('%s%s;', $sessionCookie, urlencode($sessionId));
+ $sessionCookieFound = false;
+ $otherCookies = array();
+ foreach (headers_list() as $h) {
+ if (0 !== stripos($h, 'Set-Cookie:')) {
+ continue;
+ }
+ if (11 === strpos($h, $sessionCookie, 11)) {
+ $sessionCookieFound = true;
+
+ if (11 !== strpos($h, $sessionCookieWithId, 11)) {
+ $otherCookies[] = $h;
+ }
+ } else {
+ $otherCookies[] = $h;
+ }
+ }
+ if ($sessionCookieFound) {
+ header_remove('Set-Cookie');
+ foreach ($otherCookies as $h) {
+ header($h, false);
+ }
+ } else {
+ setcookie($this->sessionName, '', 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure'), ini_get('session.cookie_httponly'));
+ }
+ }
+
+ return $this->newSessionId === $sessionId || $this->doDestroy($sessionId);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php
new file mode 100644
index 0000000..90726be
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php
@@ -0,0 +1,120 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+@trigger_error(sprintf('The class %s is deprecated since Symfony 3.4 and will be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler instead.', MemcacheSessionHandler::class), E_USER_DEPRECATED);
+
+/**
+ * @author Drak <drak@zikula.org>
+ *
+ * @deprecated since version 3.4, to be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler instead.
+ */
+class MemcacheSessionHandler implements \SessionHandlerInterface
+{
+ private $memcache;
+
+ /**
+ * @var int Time to live in seconds
+ */
+ private $ttl;
+
+ /**
+ * @var string Key prefix for shared environments
+ */
+ private $prefix;
+
+ /**
+ * Constructor.
+ *
+ * List of available options:
+ * * prefix: The prefix to use for the memcache keys in order to avoid collision
+ * * expiretime: The time to live in seconds
+ *
+ * @param \Memcache $memcache A \Memcache instance
+ * @param array $options An associative array of Memcache options
+ *
+ * @throws \InvalidArgumentException When unsupported options are passed
+ */
+ public function __construct(\Memcache $memcache, array $options = array())
+ {
+ if ($diff = array_diff(array_keys($options), array('prefix', 'expiretime'))) {
+ throw new \InvalidArgumentException(sprintf(
+ 'The following options are not supported "%s"', implode(', ', $diff)
+ ));
+ }
+
+ $this->memcache = $memcache;
+ $this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400;
+ $this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function open($savePath, $sessionName)
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function close()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function read($sessionId)
+ {
+ return $this->memcache->get($this->prefix.$sessionId) ?: '';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write($sessionId, $data)
+ {
+ return $this->memcache->set($this->prefix.$sessionId, $data, 0, time() + $this->ttl);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function destroy($sessionId)
+ {
+ $this->memcache->delete($this->prefix.$sessionId);
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function gc($maxlifetime)
+ {
+ // not required here because memcache will auto expire the records anyhow.
+ return true;
+ }
+
+ /**
+ * Return a Memcache instance.
+ *
+ * @return \Memcache
+ */
+ protected function getMemcache()
+ {
+ return $this->memcache;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php
new file mode 100644
index 0000000..dd37eae
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php
@@ -0,0 +1,124 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Memcached based session storage handler based on the Memcached class
+ * provided by the PHP memcached extension.
+ *
+ * @see http://php.net/memcached
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class MemcachedSessionHandler extends AbstractSessionHandler
+{
+ private $memcached;
+
+ /**
+ * @var int Time to live in seconds
+ */
+ private $ttl;
+
+ /**
+ * @var string Key prefix for shared environments
+ */
+ private $prefix;
+
+ /**
+ * Constructor.
+ *
+ * List of available options:
+ * * prefix: The prefix to use for the memcached keys in order to avoid collision
+ * * expiretime: The time to live in seconds.
+ *
+ * @param \Memcached $memcached A \Memcached instance
+ * @param array $options An associative array of Memcached options
+ *
+ * @throws \InvalidArgumentException When unsupported options are passed
+ */
+ public function __construct(\Memcached $memcached, array $options = array())
+ {
+ $this->memcached = $memcached;
+
+ if ($diff = array_diff(array_keys($options), array('prefix', 'expiretime'))) {
+ throw new \InvalidArgumentException(sprintf(
+ 'The following options are not supported "%s"', implode(', ', $diff)
+ ));
+ }
+
+ $this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400;
+ $this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function close()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doRead($sessionId)
+ {
+ return $this->memcached->get($this->prefix.$sessionId) ?: '';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function updateTimestamp($sessionId, $data)
+ {
+ $this->memcached->touch($this->prefix.$sessionId, time() + $this->ttl);
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doWrite($sessionId, $data)
+ {
+ return $this->memcached->set($this->prefix.$sessionId, $data, time() + $this->ttl);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDestroy($sessionId)
+ {
+ $result = $this->memcached->delete($this->prefix.$sessionId);
+
+ return $result || \Memcached::RES_NOTFOUND == $this->memcached->getResultCode();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function gc($maxlifetime)
+ {
+ // not required here because memcached will auto expire the records anyhow.
+ return true;
+ }
+
+ /**
+ * Return a Memcached instance.
+ *
+ * @return \Memcached
+ */
+ protected function getMemcached()
+ {
+ return $this->memcached;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php
new file mode 100644
index 0000000..7d3fa21
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php
@@ -0,0 +1,255 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Session handler using the mongodb/mongodb package and MongoDB driver extension.
+ *
+ * @author Markus Bachmann <markus.bachmann@bachi.biz>
+ *
+ * @see https://packagist.org/packages/mongodb/mongodb
+ * @see http://php.net/manual/en/set.mongodb.php
+ */
+class MongoDbSessionHandler extends AbstractSessionHandler
+{
+ private $mongo;
+
+ /**
+ * @var \MongoCollection
+ */
+ private $collection;
+
+ /**
+ * @var array
+ */
+ private $options;
+
+ /**
+ * Constructor.
+ *
+ * List of available options:
+ * * database: The name of the database [required]
+ * * collection: The name of the collection [required]
+ * * id_field: The field name for storing the session id [default: _id]
+ * * data_field: The field name for storing the session data [default: data]
+ * * time_field: The field name for storing the timestamp [default: time]
+ * * expiry_field: The field name for storing the expiry-timestamp [default: expires_at].
+ *
+ * It is strongly recommended to put an index on the `expiry_field` for
+ * garbage-collection. Alternatively it's possible to automatically expire
+ * the sessions in the database as described below:
+ *
+ * A TTL collections can be used on MongoDB 2.2+ to cleanup expired sessions
+ * automatically. Such an index can for example look like this:
+ *
+ * db.<session-collection>.ensureIndex(
+ * { "<expiry-field>": 1 },
+ * { "expireAfterSeconds": 0 }
+ * )
+ *
+ * More details on: http://docs.mongodb.org/manual/tutorial/expire-data/
+ *
+ * If you use such an index, you can drop `gc_probability` to 0 since
+ * no garbage-collection is required.
+ *
+ * @param \MongoDB\Client $mongo A MongoDB\Client instance
+ * @param array $options An associative array of field options
+ *
+ * @throws \InvalidArgumentException When MongoClient or Mongo instance not provided
+ * @throws \InvalidArgumentException When "database" or "collection" not provided
+ */
+ public function __construct($mongo, array $options)
+ {
+ if ($mongo instanceof \MongoClient || $mongo instanceof \Mongo) {
+ @trigger_error(sprintf('Using %s with the legacy mongo extension is deprecated as of 3.4 and will be removed in 4.0. Use it with the mongodb/mongodb package and ext-mongodb instead.', __CLASS__), E_USER_DEPRECATED);
+ }
+
+ if (!($mongo instanceof \MongoDB\Client || $mongo instanceof \MongoClient || $mongo instanceof \Mongo)) {
+ throw new \InvalidArgumentException('MongoClient or Mongo instance required');
+ }
+
+ if (!isset($options['database']) || !isset($options['collection'])) {
+ throw new \InvalidArgumentException('You must provide the "database" and "collection" option for MongoDBSessionHandler');
+ }
+
+ $this->mongo = $mongo;
+
+ $this->options = array_merge(array(
+ 'id_field' => '_id',
+ 'data_field' => 'data',
+ 'time_field' => 'time',
+ 'expiry_field' => 'expires_at',
+ ), $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function close()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDestroy($sessionId)
+ {
+ $methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteOne' : 'remove';
+
+ $this->getCollection()->$methodName(array(
+ $this->options['id_field'] => $sessionId,
+ ));
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function gc($maxlifetime)
+ {
+ $methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteMany' : 'remove';
+
+ $this->getCollection()->$methodName(array(
+ $this->options['expiry_field'] => array('$lt' => $this->createDateTime()),
+ ));
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doWrite($sessionId, $data)
+ {
+ $expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
+
+ $fields = array(
+ $this->options['time_field'] => $this->createDateTime(),
+ $this->options['expiry_field'] => $expiry,
+ );
+
+ $options = array('upsert' => true);
+
+ if ($this->mongo instanceof \MongoDB\Client) {
+ $fields[$this->options['data_field']] = new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
+ } else {
+ $fields[$this->options['data_field']] = new \MongoBinData($data, \MongoBinData::BYTE_ARRAY);
+ $options['multiple'] = false;
+ }
+
+ $methodName = $this->mongo instanceof \MongoDB\Client ? 'updateOne' : 'update';
+
+ $this->getCollection()->$methodName(
+ array($this->options['id_field'] => $sessionId),
+ array('$set' => $fields),
+ $options
+ );
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function updateTimestamp($sessionId, $data)
+ {
+ $expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
+
+ if ($this->mongo instanceof \MongoDB\Client) {
+ $methodName = 'updateOne';
+ $options = array();
+ } else {
+ $methodName = 'update';
+ $options = array('multiple' => false);
+ }
+
+ $this->getCollection()->$methodName(
+ array($this->options['id_field'] => $sessionId),
+ array('$set' => array(
+ $this->options['time_field'] => $this->createDateTime(),
+ $this->options['expiry_field'] => $expiry,
+ )),
+ $options
+ );
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doRead($sessionId)
+ {
+ $dbData = $this->getCollection()->findOne(array(
+ $this->options['id_field'] => $sessionId,
+ $this->options['expiry_field'] => array('$gte' => $this->createDateTime()),
+ ));
+
+ if (null === $dbData) {
+ return '';
+ }
+
+ if ($dbData[$this->options['data_field']] instanceof \MongoDB\BSON\Binary) {
+ return $dbData[$this->options['data_field']]->getData();
+ }
+
+ return $dbData[$this->options['data_field']]->bin;
+ }
+
+ /**
+ * Return a "MongoCollection" instance.
+ *
+ * @return \MongoCollection
+ */
+ private function getCollection()
+ {
+ if (null === $this->collection) {
+ $this->collection = $this->mongo->selectCollection($this->options['database'], $this->options['collection']);
+ }
+
+ return $this->collection;
+ }
+
+ /**
+ * Return a Mongo instance.
+ *
+ * @return \Mongo|\MongoClient|\MongoDB\Client
+ */
+ protected function getMongo()
+ {
+ return $this->mongo;
+ }
+
+ /**
+ * Create a date object using the class appropriate for the current mongo connection.
+ *
+ * Return an instance of a MongoDate or \MongoDB\BSON\UTCDateTime
+ *
+ * @param int $seconds An integer representing UTC seconds since Jan 1 1970. Defaults to now.
+ *
+ * @return \MongoDate|\MongoDB\BSON\UTCDateTime
+ */
+ private function createDateTime($seconds = null)
+ {
+ if (null === $seconds) {
+ $seconds = time();
+ }
+
+ if ($this->mongo instanceof \MongoDB\Client) {
+ return new \MongoDB\BSON\UTCDateTime($seconds * 1000);
+ }
+
+ return new \MongoDate($seconds);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php
new file mode 100644
index 0000000..4e9704b
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php
@@ -0,0 +1,55 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Native session handler using PHP's built in file storage.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class NativeFileSessionHandler extends NativeSessionHandler
+{
+ /**
+ * @param string $savePath Path of directory to save session files
+ * Default null will leave setting as defined by PHP.
+ * '/path', 'N;/path', or 'N;octal-mode;/path
+ *
+ * @see http://php.net/session.configuration.php#ini.session.save-path for further details.
+ *
+ * @throws \InvalidArgumentException On invalid $savePath
+ * @throws \RuntimeException When failing to create the save directory
+ */
+ public function __construct($savePath = null)
+ {
+ if (null === $savePath) {
+ $savePath = ini_get('session.save_path');
+ }
+
+ $baseDir = $savePath;
+
+ if ($count = substr_count($savePath, ';')) {
+ if ($count > 2) {
+ throw new \InvalidArgumentException(sprintf('Invalid argument $savePath \'%s\'', $savePath));
+ }
+
+ // characters after last ';' are the path
+ $baseDir = ltrim(strrchr($savePath, ';'), ';');
+ }
+
+ if ($baseDir && !is_dir($baseDir) && !@mkdir($baseDir, 0777, true) && !is_dir($baseDir)) {
+ throw new \RuntimeException(sprintf('Session Storage was not able to create directory "%s"', $baseDir));
+ }
+
+ ini_set('session.save_path', $savePath);
+ ini_set('session.save_handler', 'files');
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php
new file mode 100644
index 0000000..9be4528
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php
@@ -0,0 +1,24 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * @deprecated since version 3.4, to be removed in 4.0. Use \SessionHandler instead.
+ * @see http://php.net/sessionhandler
+ */
+class NativeSessionHandler extends \SessionHandler
+{
+ public function __construct()
+ {
+ @trigger_error('The '.__NAMESPACE__.'\NativeSessionHandler class is deprecated since Symfony 3.4 and will be removed in 4.0. Use the \SessionHandler class instead.', E_USER_DEPRECATED);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php
new file mode 100644
index 0000000..8d19315
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php
@@ -0,0 +1,76 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Can be used in unit testing or in a situations where persisted sessions are not desired.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class NullSessionHandler extends AbstractSessionHandler
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function close()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validateId($sessionId)
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doRead($sessionId)
+ {
+ return '';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function updateTimestamp($sessionId, $data)
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doWrite($sessionId, $data)
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDestroy($sessionId)
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function gc($maxlifetime)
+ {
+ return true;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php
new file mode 100644
index 0000000..c8737be
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php
@@ -0,0 +1,910 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Session handler using a PDO connection to read and write data.
+ *
+ * It works with MySQL, PostgreSQL, Oracle, SQL Server and SQLite and implements
+ * different locking strategies to handle concurrent access to the same session.
+ * Locking is necessary to prevent loss of data due to race conditions and to keep
+ * the session data consistent between read() and write(). With locking, requests
+ * for the same session will wait until the other one finished writing. For this
+ * reason it's best practice to close a session as early as possible to improve
+ * concurrency. PHPs internal files session handler also implements locking.
+ *
+ * Attention: Since SQLite does not support row level locks but locks the whole database,
+ * it means only one session can be accessed at a time. Even different sessions would wait
+ * for another to finish. So saving session in SQLite should only be considered for
+ * development or prototypes.
+ *
+ * Session data is a binary string that can contain non-printable characters like the null byte.
+ * For this reason it must be saved in a binary column in the database like BLOB in MySQL.
+ * Saving it in a character column could corrupt the data. You can use createTable()
+ * to initialize a correctly defined table.
+ *
+ * @see http://php.net/sessionhandlerinterface
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Michael Williams <michael.williams@funsational.com>
+ * @author Tobias Schultze <http://tobion.de>
+ */
+class PdoSessionHandler extends AbstractSessionHandler
+{
+ /**
+ * No locking is done. This means sessions are prone to loss of data due to
+ * race conditions of concurrent requests to the same session. The last session
+ * write will win in this case. It might be useful when you implement your own
+ * logic to deal with this like an optimistic approach.
+ */
+ const LOCK_NONE = 0;
+
+ /**
+ * Creates an application-level lock on a session. The disadvantage is that the
+ * lock is not enforced by the database and thus other, unaware parts of the
+ * application could still concurrently modify the session. The advantage is it
+ * does not require a transaction.
+ * This mode is not available for SQLite and not yet implemented for oci and sqlsrv.
+ */
+ const LOCK_ADVISORY = 1;
+
+ /**
+ * Issues a real row lock. Since it uses a transaction between opening and
+ * closing a session, you have to be careful when you use same database connection
+ * that you also use for your application logic. This mode is the default because
+ * it's the only reliable solution across DBMSs.
+ */
+ const LOCK_TRANSACTIONAL = 2;
+
+ /**
+ * @var \PDO|null PDO instance or null when not connected yet
+ */
+ private $pdo;
+
+ /**
+ * @var string|null|false DSN string or null for session.save_path or false when lazy connection disabled
+ */
+ private $dsn = false;
+
+ /**
+ * @var string Database driver
+ */
+ private $driver;
+
+ /**
+ * @var string Table name
+ */
+ private $table = 'sessions';
+
+ /**
+ * @var string Column for session id
+ */
+ private $idCol = 'sess_id';
+
+ /**
+ * @var string Column for session data
+ */
+ private $dataCol = 'sess_data';
+
+ /**
+ * @var string Column for lifetime
+ */
+ private $lifetimeCol = 'sess_lifetime';
+
+ /**
+ * @var string Column for timestamp
+ */
+ private $timeCol = 'sess_time';
+
+ /**
+ * @var string Username when lazy-connect
+ */
+ private $username = '';
+
+ /**
+ * @var string Password when lazy-connect
+ */
+ private $password = '';
+
+ /**
+ * @var array Connection options when lazy-connect
+ */
+ private $connectionOptions = array();
+
+ /**
+ * @var int The strategy for locking, see constants
+ */
+ private $lockMode = self::LOCK_TRANSACTIONAL;
+
+ /**
+ * It's an array to support multiple reads before closing which is manual, non-standard usage.
+ *
+ * @var \PDOStatement[] An array of statements to release advisory locks
+ */
+ private $unlockStatements = array();
+
+ /**
+ * @var bool True when the current session exists but expired according to session.gc_maxlifetime
+ */
+ private $sessionExpired = false;
+
+ /**
+ * @var bool Whether a transaction is active
+ */
+ private $inTransaction = false;
+
+ /**
+ * @var bool Whether gc() has been called
+ */
+ private $gcCalled = false;
+
+ /**
+ * You can either pass an existing database connection as PDO instance or
+ * pass a DSN string that will be used to lazy-connect to the database
+ * when the session is actually used. Furthermore it's possible to pass null
+ * which will then use the session.save_path ini setting as PDO DSN parameter.
+ *
+ * List of available options:
+ * * db_table: The name of the table [default: sessions]
+ * * db_id_col: The column where to store the session id [default: sess_id]
+ * * db_data_col: The column where to store the session data [default: sess_data]
+ * * db_lifetime_col: The column where to store the lifetime [default: sess_lifetime]
+ * * db_time_col: The column where to store the timestamp [default: sess_time]
+ * * db_username: The username when lazy-connect [default: '']
+ * * db_password: The password when lazy-connect [default: '']
+ * * db_connection_options: An array of driver-specific connection options [default: array()]
+ * * lock_mode: The strategy for locking, see constants [default: LOCK_TRANSACTIONAL]
+ *
+ * @param \PDO|string|null $pdoOrDsn A \PDO instance or DSN string or URL string or null
+ * @param array $options An associative array of options
+ *
+ * @throws \InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION
+ */
+ public function __construct($pdoOrDsn = null, array $options = array())
+ {
+ if ($pdoOrDsn instanceof \PDO) {
+ if (\PDO::ERRMODE_EXCEPTION !== $pdoOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) {
+ throw new \InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))', __CLASS__));
+ }
+
+ $this->pdo = $pdoOrDsn;
+ $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
+ } elseif (is_string($pdoOrDsn) && false !== strpos($pdoOrDsn, '://')) {
+ $this->dsn = $this->buildDsnFromUrl($pdoOrDsn);
+ } else {
+ $this->dsn = $pdoOrDsn;
+ }
+
+ $this->table = isset($options['db_table']) ? $options['db_table'] : $this->table;
+ $this->idCol = isset($options['db_id_col']) ? $options['db_id_col'] : $this->idCol;
+ $this->dataCol = isset($options['db_data_col']) ? $options['db_data_col'] : $this->dataCol;
+ $this->lifetimeCol = isset($options['db_lifetime_col']) ? $options['db_lifetime_col'] : $this->lifetimeCol;
+ $this->timeCol = isset($options['db_time_col']) ? $options['db_time_col'] : $this->timeCol;
+ $this->username = isset($options['db_username']) ? $options['db_username'] : $this->username;
+ $this->password = isset($options['db_password']) ? $options['db_password'] : $this->password;
+ $this->connectionOptions = isset($options['db_connection_options']) ? $options['db_connection_options'] : $this->connectionOptions;
+ $this->lockMode = isset($options['lock_mode']) ? $options['lock_mode'] : $this->lockMode;
+ }
+
+ /**
+ * Creates the table to store sessions which can be called once for setup.
+ *
+ * Session ID is saved in a column of maximum length 128 because that is enough even
+ * for a 512 bit configured session.hash_function like Whirlpool. Session data is
+ * saved in a BLOB. One could also use a shorter inlined varbinary column
+ * if one was sure the data fits into it.
+ *
+ * @throws \PDOException When the table already exists
+ * @throws \DomainException When an unsupported PDO driver is used
+ */
+ public function createTable()
+ {
+ // connect if we are not yet
+ $this->getConnection();
+
+ switch ($this->driver) {
+ case 'mysql':
+ // We use varbinary for the ID column because it prevents unwanted conversions:
+ // - character set conversions between server and client
+ // - trailing space removal
+ // - case-insensitivity
+ // - language processing like é == e
+ $sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol MEDIUMINT NOT NULL, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8_bin, ENGINE = InnoDB";
+ break;
+ case 'sqlite':
+ $sql = "CREATE TABLE $this->table ($this->idCol TEXT NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)";
+ break;
+ case 'pgsql':
+ $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol BYTEA NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)";
+ break;
+ case 'oci':
+ $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR2(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)";
+ break;
+ case 'sqlsrv':
+ $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol VARBINARY(MAX) NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)";
+ break;
+ default:
+ throw new \DomainException(sprintf('Creating the session table is currently not implemented for PDO driver "%s".', $this->driver));
+ }
+
+ try {
+ $this->pdo->exec($sql);
+ } catch (\PDOException $e) {
+ $this->rollback();
+
+ throw $e;
+ }
+ }
+
+ /**
+ * Returns true when the current session exists but expired according to session.gc_maxlifetime.
+ *
+ * Can be used to distinguish between a new session and one that expired due to inactivity.
+ *
+ * @return bool Whether current session expired
+ */
+ public function isSessionExpired()
+ {
+ return $this->sessionExpired;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function open($savePath, $sessionName)
+ {
+ $this->sessionExpired = false;
+
+ if (null === $this->pdo) {
+ $this->connect($this->dsn ?: $savePath);
+ }
+
+ return parent::open($savePath, $sessionName);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function read($sessionId)
+ {
+ try {
+ return parent::read($sessionId);
+ } catch (\PDOException $e) {
+ $this->rollback();
+
+ throw $e;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function gc($maxlifetime)
+ {
+ // We delay gc() to close() so that it is executed outside the transactional and blocking read-write process.
+ // This way, pruning expired sessions does not block them from being started while the current session is used.
+ $this->gcCalled = true;
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDestroy($sessionId)
+ {
+ // delete the record associated with this id
+ $sql = "DELETE FROM $this->table WHERE $this->idCol = :id";
+
+ try {
+ $stmt = $this->pdo->prepare($sql);
+ $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+ $stmt->execute();
+ } catch (\PDOException $e) {
+ $this->rollback();
+
+ throw $e;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doWrite($sessionId, $data)
+ {
+ $maxlifetime = (int) ini_get('session.gc_maxlifetime');
+
+ try {
+ // We use a single MERGE SQL query when supported by the database.
+ $mergeStmt = $this->getMergeStatement($sessionId, $data, $maxlifetime);
+ if (null !== $mergeStmt) {
+ $mergeStmt->execute();
+
+ return true;
+ }
+
+ $updateStmt = $this->getUpdateStatement($sessionId, $data, $maxlifetime);
+ $updateStmt->execute();
+
+ // When MERGE is not supported, like in Postgres < 9.5, we have to use this approach that can result in
+ // duplicate key errors when the same session is written simultaneously (given the LOCK_NONE behavior).
+ // We can just catch such an error and re-execute the update. This is similar to a serializable
+ // transaction with retry logic on serialization failures but without the overhead and without possible
+ // false positives due to longer gap locking.
+ if (!$updateStmt->rowCount()) {
+ try {
+ $insertStmt = $this->getInsertStatement($sessionId, $data, $maxlifetime);
+ $insertStmt->execute();
+ } catch (\PDOException $e) {
+ // Handle integrity violation SQLSTATE 23000 (or a subclass like 23505 in Postgres) for duplicate keys
+ if (0 === strpos($e->getCode(), '23')) {
+ $updateStmt->execute();
+ } else {
+ throw $e;
+ }
+ }
+ }
+ } catch (\PDOException $e) {
+ $this->rollback();
+
+ throw $e;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function updateTimestamp($sessionId, $data)
+ {
+ $maxlifetime = (int) ini_get('session.gc_maxlifetime');
+
+ try {
+ $updateStmt = $this->pdo->prepare(
+ "UPDATE $this->table SET $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id"
+ );
+ $updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+ $updateStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
+ $updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
+ $updateStmt->execute();
+ } catch (\PDOException $e) {
+ $this->rollback();
+
+ throw $e;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function close()
+ {
+ $this->commit();
+
+ while ($unlockStmt = array_shift($this->unlockStatements)) {
+ $unlockStmt->execute();
+ }
+
+ if ($this->gcCalled) {
+ $this->gcCalled = false;
+
+ // delete the session records that have expired
+ if ('mysql' === $this->driver) {
+ $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol < :time";
+ } else {
+ $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol < :time - $this->timeCol";
+ }
+
+ $stmt = $this->pdo->prepare($sql);
+ $stmt->bindValue(':time', time(), \PDO::PARAM_INT);
+ $stmt->execute();
+ }
+
+ if (false !== $this->dsn) {
+ $this->pdo = null; // only close lazy-connection
+ }
+
+ return true;
+ }
+
+ /**
+ * Lazy-connects to the database.
+ *
+ * @param string $dsn DSN string
+ */
+ private function connect($dsn)
+ {
+ $this->pdo = new \PDO($dsn, $this->username, $this->password, $this->connectionOptions);
+ $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+ $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
+ }
+
+ /**
+ * Builds a PDO DSN from a URL-like connection string.
+ *
+ * @param string $dsnOrUrl
+ *
+ * @return string
+ *
+ * @todo implement missing support for oci DSN (which look totally different from other PDO ones)
+ */
+ private function buildDsnFromUrl($dsnOrUrl)
+ {
+ // (pdo_)?sqlite3?:///... => (pdo_)?sqlite3?://localhost/... or else the URL will be invalid
+ $url = preg_replace('#^((?:pdo_)?sqlite3?):///#', '$1://localhost/', $dsnOrUrl);
+
+ $params = parse_url($url);
+
+ if (false === $params) {
+ return $dsnOrUrl; // If the URL is not valid, let's assume it might be a DSN already.
+ }
+
+ $params = array_map('rawurldecode', $params);
+
+ // Override the default username and password. Values passed through options will still win over these in the constructor.
+ if (isset($params['user'])) {
+ $this->username = $params['user'];
+ }
+
+ if (isset($params['pass'])) {
+ $this->password = $params['pass'];
+ }
+
+ if (!isset($params['scheme'])) {
+ throw new \InvalidArgumentException('URLs without scheme are not supported to configure the PdoSessionHandler');
+ }
+
+ $driverAliasMap = array(
+ 'mssql' => 'sqlsrv',
+ 'mysql2' => 'mysql', // Amazon RDS, for some weird reason
+ 'postgres' => 'pgsql',
+ 'postgresql' => 'pgsql',
+ 'sqlite3' => 'sqlite',
+ );
+
+ $driver = isset($driverAliasMap[$params['scheme']]) ? $driverAliasMap[$params['scheme']] : $params['scheme'];
+
+ // Doctrine DBAL supports passing its internal pdo_* driver names directly too (allowing both dashes and underscores). This allows supporting the same here.
+ if (0 === strpos($driver, 'pdo_') || 0 === strpos($driver, 'pdo-')) {
+ $driver = substr($driver, 4);
+ }
+
+ switch ($driver) {
+ case 'mysql':
+ case 'pgsql':
+ $dsn = $driver.':';
+
+ if (isset($params['host']) && '' !== $params['host']) {
+ $dsn .= 'host='.$params['host'].';';
+ }
+
+ if (isset($params['port']) && '' !== $params['port']) {
+ $dsn .= 'port='.$params['port'].';';
+ }
+
+ if (isset($params['path'])) {
+ $dbName = substr($params['path'], 1); // Remove the leading slash
+ $dsn .= 'dbname='.$dbName.';';
+ }
+
+ return $dsn;
+
+ case 'sqlite':
+ return 'sqlite:'.substr($params['path'], 1);
+
+ case 'sqlsrv':
+ $dsn = 'sqlsrv:server=';
+
+ if (isset($params['host'])) {
+ $dsn .= $params['host'];
+ }
+
+ if (isset($params['port']) && '' !== $params['port']) {
+ $dsn .= ','.$params['port'];
+ }
+
+ if (isset($params['path'])) {
+ $dbName = substr($params['path'], 1); // Remove the leading slash
+ $dsn .= ';Database='.$dbName;
+ }
+
+ return $dsn;
+
+ default:
+ throw new \InvalidArgumentException(sprintf('The scheme "%s" is not supported by the PdoSessionHandler URL configuration. Pass a PDO DSN directly.', $params['scheme']));
+ }
+ }
+
+ /**
+ * Helper method to begin a transaction.
+ *
+ * Since SQLite does not support row level locks, we have to acquire a reserved lock
+ * on the database immediately. Because of https://bugs.php.net/42766 we have to create
+ * such a transaction manually which also means we cannot use PDO::commit or
+ * PDO::rollback or PDO::inTransaction for SQLite.
+ *
+ * Also MySQLs default isolation, REPEATABLE READ, causes deadlock for different sessions
+ * due to http://www.mysqlperformanceblog.com/2013/12/12/one-more-innodb-gap-lock-to-avoid/ .
+ * So we change it to READ COMMITTED.
+ */
+ private function beginTransaction()
+ {
+ if (!$this->inTransaction) {
+ if ('sqlite' === $this->driver) {
+ $this->pdo->exec('BEGIN IMMEDIATE TRANSACTION');
+ } else {
+ if ('mysql' === $this->driver) {
+ $this->pdo->exec('SET TRANSACTION ISOLATION LEVEL READ COMMITTED');
+ }
+ $this->pdo->beginTransaction();
+ }
+ $this->inTransaction = true;
+ }
+ }
+
+ /**
+ * Helper method to commit a transaction.
+ */
+ private function commit()
+ {
+ if ($this->inTransaction) {
+ try {
+ // commit read-write transaction which also releases the lock
+ if ('sqlite' === $this->driver) {
+ $this->pdo->exec('COMMIT');
+ } else {
+ $this->pdo->commit();
+ }
+ $this->inTransaction = false;
+ } catch (\PDOException $e) {
+ $this->rollback();
+
+ throw $e;
+ }
+ }
+ }
+
+ /**
+ * Helper method to rollback a transaction.
+ */
+ private function rollback()
+ {
+ // We only need to rollback if we are in a transaction. Otherwise the resulting
+ // error would hide the real problem why rollback was called. We might not be
+ // in a transaction when not using the transactional locking behavior or when
+ // two callbacks (e.g. destroy and write) are invoked that both fail.
+ if ($this->inTransaction) {
+ if ('sqlite' === $this->driver) {
+ $this->pdo->exec('ROLLBACK');
+ } else {
+ $this->pdo->rollBack();
+ }
+ $this->inTransaction = false;
+ }
+ }
+
+ /**
+ * Reads the session data in respect to the different locking strategies.
+ *
+ * We need to make sure we do not return session data that is already considered garbage according
+ * to the session.gc_maxlifetime setting because gc() is called after read() and only sometimes.
+ *
+ * @param string $sessionId Session ID
+ *
+ * @return string The session data
+ */
+ protected function doRead($sessionId)
+ {
+ if (self::LOCK_ADVISORY === $this->lockMode) {
+ $this->unlockStatements[] = $this->doAdvisoryLock($sessionId);
+ }
+
+ $selectSql = $this->getSelectSql();
+ $selectStmt = $this->pdo->prepare($selectSql);
+ $selectStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+
+ do {
+ $selectStmt->execute();
+ $sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM);
+
+ if ($sessionRows) {
+ if ($sessionRows[0][1] + $sessionRows[0][2] < time()) {
+ $this->sessionExpired = true;
+
+ return '';
+ }
+
+ return is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0];
+ }
+
+ if (!ini_get('session.use_strict_mode') && self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
+ // In strict mode, session fixation is not possible: new sessions always start with a unique
+ // random id, so that concurrency is not possible and this code path can be skipped.
+ // Exclusive-reading of non-existent rows does not block, so we need to do an insert to block
+ // until other connections to the session are committed.
+ try {
+ $insertStmt = $this->getInsertStatement($sessionId, '', 0);
+ $insertStmt->execute();
+ } catch (\PDOException $e) {
+ // Catch duplicate key error because other connection created the session already.
+ // It would only not be the case when the other connection destroyed the session.
+ if (0 === strpos($e->getCode(), '23')) {
+ // Retrieve finished session data written by concurrent connection by restarting the loop.
+ // We have to start a new transaction as a failed query will mark the current transaction as
+ // aborted in PostgreSQL and disallow further queries within it.
+ $this->rollback();
+ $this->beginTransaction();
+ continue;
+ }
+
+ throw $e;
+ }
+ }
+
+ return '';
+ } while (true);
+ }
+
+ /**
+ * Executes an application-level lock on the database.
+ *
+ * @param string $sessionId Session ID
+ *
+ * @return \PDOStatement The statement that needs to be executed later to release the lock
+ *
+ * @throws \DomainException When an unsupported PDO driver is used
+ *
+ * @todo implement missing advisory locks
+ * - for oci using DBMS_LOCK.REQUEST
+ * - for sqlsrv using sp_getapplock with LockOwner = Session
+ */
+ private function doAdvisoryLock($sessionId)
+ {
+ switch ($this->driver) {
+ case 'mysql':
+ // should we handle the return value? 0 on timeout, null on error
+ // we use a timeout of 50 seconds which is also the default for innodb_lock_wait_timeout
+ $stmt = $this->pdo->prepare('SELECT GET_LOCK(:key, 50)');
+ $stmt->bindValue(':key', $sessionId, \PDO::PARAM_STR);
+ $stmt->execute();
+
+ $releaseStmt = $this->pdo->prepare('DO RELEASE_LOCK(:key)');
+ $releaseStmt->bindValue(':key', $sessionId, \PDO::PARAM_STR);
+
+ return $releaseStmt;
+ case 'pgsql':
+ // Obtaining an exclusive session level advisory lock requires an integer key.
+ // When session.sid_bits_per_character > 4, the session id can contain non-hex-characters.
+ // So we cannot just use hexdec().
+ if (4 === \PHP_INT_SIZE) {
+ $sessionInt1 = $this->convertStringToInt($sessionId);
+ $sessionInt2 = $this->convertStringToInt(substr($sessionId, 4, 4));
+
+ $stmt = $this->pdo->prepare('SELECT pg_advisory_lock(:key1, :key2)');
+ $stmt->bindValue(':key1', $sessionInt1, \PDO::PARAM_INT);
+ $stmt->bindValue(':key2', $sessionInt2, \PDO::PARAM_INT);
+ $stmt->execute();
+
+ $releaseStmt = $this->pdo->prepare('SELECT pg_advisory_unlock(:key1, :key2)');
+ $releaseStmt->bindValue(':key1', $sessionInt1, \PDO::PARAM_INT);
+ $releaseStmt->bindValue(':key2', $sessionInt2, \PDO::PARAM_INT);
+ } else {
+ $sessionBigInt = $this->convertStringToInt($sessionId);
+
+ $stmt = $this->pdo->prepare('SELECT pg_advisory_lock(:key)');
+ $stmt->bindValue(':key', $sessionBigInt, \PDO::PARAM_INT);
+ $stmt->execute();
+
+ $releaseStmt = $this->pdo->prepare('SELECT pg_advisory_unlock(:key)');
+ $releaseStmt->bindValue(':key', $sessionBigInt, \PDO::PARAM_INT);
+ }
+
+ return $releaseStmt;
+ case 'sqlite':
+ throw new \DomainException('SQLite does not support advisory locks.');
+ default:
+ throw new \DomainException(sprintf('Advisory locks are currently not implemented for PDO driver "%s".', $this->driver));
+ }
+ }
+
+ /**
+ * Encodes the first 4 (when PHP_INT_SIZE == 4) or 8 characters of the string as an integer.
+ *
+ * Keep in mind, PHP integers are signed.
+ *
+ * @param string $string
+ *
+ * @return int
+ */
+ private function convertStringToInt($string)
+ {
+ if (4 === \PHP_INT_SIZE) {
+ return (ord($string[3]) << 24) + (ord($string[2]) << 16) + (ord($string[1]) << 8) + ord($string[0]);
+ }
+
+ $int1 = (ord($string[7]) << 24) + (ord($string[6]) << 16) + (ord($string[5]) << 8) + ord($string[4]);
+ $int2 = (ord($string[3]) << 24) + (ord($string[2]) << 16) + (ord($string[1]) << 8) + ord($string[0]);
+
+ return $int2 + ($int1 << 32);
+ }
+
+ /**
+ * Return a locking or nonlocking SQL query to read session information.
+ *
+ * @return string The SQL string
+ *
+ * @throws \DomainException When an unsupported PDO driver is used
+ */
+ private function getSelectSql()
+ {
+ if (self::LOCK_TRANSACTIONAL === $this->lockMode) {
+ $this->beginTransaction();
+
+ switch ($this->driver) {
+ case 'mysql':
+ case 'oci':
+ case 'pgsql':
+ return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WHERE $this->idCol = :id FOR UPDATE";
+ case 'sqlsrv':
+ return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WITH (UPDLOCK, ROWLOCK) WHERE $this->idCol = :id";
+ case 'sqlite':
+ // we already locked when starting transaction
+ break;
+ default:
+ throw new \DomainException(sprintf('Transactional locks are currently not implemented for PDO driver "%s".', $this->driver));
+ }
+ }
+
+ return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WHERE $this->idCol = :id";
+ }
+
+ /**
+ * Returns an insert statement supported by the database for writing session data.
+ *
+ * @param string $sessionId Session ID
+ * @param string $sessionData Encoded session data
+ * @param int $maxlifetime session.gc_maxlifetime
+ *
+ * @return \PDOStatement The insert statement
+ */
+ private function getInsertStatement($sessionId, $sessionData, $maxlifetime)
+ {
+ switch ($this->driver) {
+ case 'oci':
+ $data = fopen('php://memory', 'r+');
+ fwrite($data, $sessionData);
+ rewind($data);
+ $sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, EMPTY_BLOB(), :lifetime, :time) RETURNING $this->dataCol into :data";
+ break;
+ default:
+ $data = $sessionData;
+ $sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
+ break;
+ }
+
+ $stmt = $this->pdo->prepare($sql);
+ $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+ $stmt->bindParam(':data', $data, \PDO::PARAM_LOB);
+ $stmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
+ $stmt->bindValue(':time', time(), \PDO::PARAM_INT);
+
+ return $stmt;
+ }
+
+ /**
+ * Returns an update statement supported by the database for writing session data.
+ *
+ * @param string $sessionId Session ID
+ * @param string $sessionData Encoded session data
+ * @param int $maxlifetime session.gc_maxlifetime
+ *
+ * @return \PDOStatement The update statement
+ */
+ private function getUpdateStatement($sessionId, $sessionData, $maxlifetime)
+ {
+ switch ($this->driver) {
+ case 'oci':
+ $data = fopen('php://memory', 'r+');
+ fwrite($data, $sessionData);
+ rewind($data);
+ $sql = "UPDATE $this->table SET $this->dataCol = EMPTY_BLOB(), $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id RETURNING $this->dataCol into :data";
+ break;
+ default:
+ $data = $sessionData;
+ $sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id";
+ break;
+ }
+
+ $stmt = $this->pdo->prepare($sql);
+ $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+ $stmt->bindParam(':data', $data, \PDO::PARAM_LOB);
+ $stmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
+ $stmt->bindValue(':time', time(), \PDO::PARAM_INT);
+
+ return $stmt;
+ }
+
+ /**
+ * Returns a merge/upsert (i.e. insert or update) statement when supported by the database for writing session data.
+ *
+ * @param string $sessionId Session ID
+ * @param string $data Encoded session data
+ * @param int $maxlifetime session.gc_maxlifetime
+ *
+ * @return \PDOStatement|null The merge statement or null when not supported
+ */
+ private function getMergeStatement($sessionId, $data, $maxlifetime)
+ {
+ switch (true) {
+ case 'mysql' === $this->driver:
+ $mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
+ "ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)";
+ break;
+ case 'sqlsrv' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '10', '>='):
+ // MERGE is only available since SQL Server 2008 and must be terminated by semicolon
+ // It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
+ $mergeSql = "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = ?) ".
+ "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ".
+ "WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?;";
+ break;
+ case 'sqlite' === $this->driver:
+ $mergeSql = "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
+ break;
+ case 'pgsql' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '9.5', '>='):
+ $mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
+ "ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->lifetimeCol, EXCLUDED.$this->timeCol)";
+ break;
+ default:
+ // MERGE is not supported with LOBs: http://www.oracle.com/technetwork/articles/fuecks-lobs-095315.html
+ return null;
+ }
+
+ $mergeStmt = $this->pdo->prepare($mergeSql);
+
+ if ('sqlsrv' === $this->driver) {
+ $mergeStmt->bindParam(1, $sessionId, \PDO::PARAM_STR);
+ $mergeStmt->bindParam(2, $sessionId, \PDO::PARAM_STR);
+ $mergeStmt->bindParam(3, $data, \PDO::PARAM_LOB);
+ $mergeStmt->bindParam(4, $maxlifetime, \PDO::PARAM_INT);
+ $mergeStmt->bindValue(5, time(), \PDO::PARAM_INT);
+ $mergeStmt->bindParam(6, $data, \PDO::PARAM_LOB);
+ $mergeStmt->bindParam(7, $maxlifetime, \PDO::PARAM_INT);
+ $mergeStmt->bindValue(8, time(), \PDO::PARAM_INT);
+ } else {
+ $mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+ $mergeStmt->bindParam(':data', $data, \PDO::PARAM_LOB);
+ $mergeStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
+ $mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT);
+ }
+
+ return $mergeStmt;
+ }
+
+ /**
+ * Return a PDO instance.
+ *
+ * @return \PDO
+ */
+ protected function getConnection()
+ {
+ if (null === $this->pdo) {
+ $this->connect($this->dsn ?: ini_get('session.save_path'));
+ }
+
+ return $this->pdo;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php
new file mode 100644
index 0000000..2281192
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php
@@ -0,0 +1,103 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Adds basic `SessionUpdateTimestampHandlerInterface` behaviors to another `SessionHandlerInterface`.
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class StrictSessionHandler extends AbstractSessionHandler
+{
+ private $handler;
+ private $doDestroy;
+
+ public function __construct(\SessionHandlerInterface $handler)
+ {
+ if ($handler instanceof \SessionUpdateTimestampHandlerInterface) {
+ throw new \LogicException(sprintf('"%s" is already an instance of "SessionUpdateTimestampHandlerInterface", you cannot wrap it with "%s".', get_class($handler), self::class));
+ }
+
+ $this->handler = $handler;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function open($savePath, $sessionName)
+ {
+ parent::open($savePath, $sessionName);
+
+ return $this->handler->open($savePath, $sessionName);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doRead($sessionId)
+ {
+ return $this->handler->read($sessionId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function updateTimestamp($sessionId, $data)
+ {
+ return $this->write($sessionId, $data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doWrite($sessionId, $data)
+ {
+ return $this->handler->write($sessionId, $data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function destroy($sessionId)
+ {
+ $this->doDestroy = true;
+ $destroyed = parent::destroy($sessionId);
+
+ return $this->doDestroy ? $this->doDestroy($sessionId) : $destroyed;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDestroy($sessionId)
+ {
+ $this->doDestroy = false;
+
+ return $this->handler->destroy($sessionId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function close()
+ {
+ return $this->handler->close();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function gc($maxlifetime)
+ {
+ return $this->handler->gc($maxlifetime);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php
new file mode 100644
index 0000000..1541ec4
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php
@@ -0,0 +1,92 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead.', WriteCheckSessionHandler::class), E_USER_DEPRECATED);
+
+/**
+ * Wraps another SessionHandlerInterface to only write the session when it has been modified.
+ *
+ * @author Adrien Brault <adrien.brault@gmail.com>
+ *
+ * @deprecated since version 3.4, to be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead.
+ */
+class WriteCheckSessionHandler implements \SessionHandlerInterface
+{
+ private $wrappedSessionHandler;
+
+ /**
+ * @var array sessionId => session
+ */
+ private $readSessions;
+
+ public function __construct(\SessionHandlerInterface $wrappedSessionHandler)
+ {
+ $this->wrappedSessionHandler = $wrappedSessionHandler;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function close()
+ {
+ return $this->wrappedSessionHandler->close();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function destroy($sessionId)
+ {
+ return $this->wrappedSessionHandler->destroy($sessionId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function gc($maxlifetime)
+ {
+ return $this->wrappedSessionHandler->gc($maxlifetime);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function open($savePath, $sessionName)
+ {
+ return $this->wrappedSessionHandler->open($savePath, $sessionName);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function read($sessionId)
+ {
+ $session = $this->wrappedSessionHandler->read($sessionId);
+
+ $this->readSessions[$sessionId] = $session;
+
+ return $session;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write($sessionId, $data)
+ {
+ if (isset($this->readSessions[$sessionId]) && $data === $this->readSessions[$sessionId]) {
+ return true;
+ }
+
+ return $this->wrappedSessionHandler->write($sessionId, $data);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php
new file mode 100644
index 0000000..6f59af4
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php
@@ -0,0 +1,168 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * Metadata container.
+ *
+ * Adds metadata to the session.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class MetadataBag implements SessionBagInterface
+{
+ const CREATED = 'c';
+ const UPDATED = 'u';
+ const LIFETIME = 'l';
+
+ /**
+ * @var string
+ */
+ private $name = '__metadata';
+
+ /**
+ * @var string
+ */
+ private $storageKey;
+
+ /**
+ * @var array
+ */
+ protected $meta = array(self::CREATED => 0, self::UPDATED => 0, self::LIFETIME => 0);
+
+ /**
+ * Unix timestamp.
+ *
+ * @var int
+ */
+ private $lastUsed;
+
+ /**
+ * @var int
+ */
+ private $updateThreshold;
+
+ /**
+ * @param string $storageKey The key used to store bag in the session
+ * @param int $updateThreshold The time to wait between two UPDATED updates
+ */
+ public function __construct($storageKey = '_sf2_meta', $updateThreshold = 0)
+ {
+ $this->storageKey = $storageKey;
+ $this->updateThreshold = $updateThreshold;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(array &$array)
+ {
+ $this->meta = &$array;
+
+ if (isset($array[self::CREATED])) {
+ $this->lastUsed = $this->meta[self::UPDATED];
+
+ $timeStamp = time();
+ if ($timeStamp - $array[self::UPDATED] >= $this->updateThreshold) {
+ $this->meta[self::UPDATED] = $timeStamp;
+ }
+ } else {
+ $this->stampCreated();
+ }
+ }
+
+ /**
+ * Gets the lifetime that the session cookie was set with.
+ *
+ * @return int
+ */
+ public function getLifetime()
+ {
+ return $this->meta[self::LIFETIME];
+ }
+
+ /**
+ * Stamps a new session's metadata.
+ *
+ * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value
+ * will leave the system settings unchanged, 0 sets the cookie
+ * to expire with browser session. Time is in seconds, and is
+ * not a Unix timestamp.
+ */
+ public function stampNew($lifetime = null)
+ {
+ $this->stampCreated($lifetime);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStorageKey()
+ {
+ return $this->storageKey;
+ }
+
+ /**
+ * Gets the created timestamp metadata.
+ *
+ * @return int Unix timestamp
+ */
+ public function getCreated()
+ {
+ return $this->meta[self::CREATED];
+ }
+
+ /**
+ * Gets the last used metadata.
+ *
+ * @return int Unix timestamp
+ */
+ public function getLastUsed()
+ {
+ return $this->lastUsed;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ // nothing to do
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Sets name.
+ *
+ * @param string $name
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ private function stampCreated($lifetime = null)
+ {
+ $timeStamp = time();
+ $this->meta[self::CREATED] = $this->meta[self::UPDATED] = $this->lastUsed = $timeStamp;
+ $this->meta[self::LIFETIME] = (null === $lifetime) ? ini_get('session.cookie_lifetime') : $lifetime;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php
new file mode 100644
index 0000000..027f4ef
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php
@@ -0,0 +1,256 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * MockArraySessionStorage mocks the session for unit tests.
+ *
+ * No PHP session is actually started since a session can be initialized
+ * and shutdown only once per PHP execution cycle.
+ *
+ * When doing functional testing, you should use MockFileSessionStorage instead.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
+ * @author Drak <drak@zikula.org>
+ */
+class MockArraySessionStorage implements SessionStorageInterface
+{
+ /**
+ * @var string
+ */
+ protected $id = '';
+
+ /**
+ * @var string
+ */
+ protected $name;
+
+ /**
+ * @var bool
+ */
+ protected $started = false;
+
+ /**
+ * @var bool
+ */
+ protected $closed = false;
+
+ /**
+ * @var array
+ */
+ protected $data = array();
+
+ /**
+ * @var MetadataBag
+ */
+ protected $metadataBag;
+
+ /**
+ * @var array|SessionBagInterface[]
+ */
+ protected $bags = array();
+
+ /**
+ * @param string $name Session name
+ * @param MetadataBag $metaBag MetadataBag instance
+ */
+ public function __construct($name = 'MOCKSESSID', MetadataBag $metaBag = null)
+ {
+ $this->name = $name;
+ $this->setMetadataBag($metaBag);
+ }
+
+ public function setSessionData(array $array)
+ {
+ $this->data = $array;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function start()
+ {
+ if ($this->started) {
+ return true;
+ }
+
+ if (empty($this->id)) {
+ $this->id = $this->generateId();
+ }
+
+ $this->loadSession();
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function regenerate($destroy = false, $lifetime = null)
+ {
+ if (!$this->started) {
+ $this->start();
+ }
+
+ $this->metadataBag->stampNew($lifetime);
+ $this->id = $this->generateId();
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setId($id)
+ {
+ if ($this->started) {
+ throw new \LogicException('Cannot set session ID after the session has started.');
+ }
+
+ $this->id = $id;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save()
+ {
+ if (!$this->started || $this->closed) {
+ throw new \RuntimeException('Trying to save a session that was not started yet or was already closed');
+ }
+ // nothing to do since we don't persist the session data
+ $this->closed = false;
+ $this->started = false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ // clear out the bags
+ foreach ($this->bags as $bag) {
+ $bag->clear();
+ }
+
+ // clear out the session
+ $this->data = array();
+
+ // reconnect the bags to the session
+ $this->loadSession();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function registerBag(SessionBagInterface $bag)
+ {
+ $this->bags[$bag->getName()] = $bag;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getBag($name)
+ {
+ if (!isset($this->bags[$name])) {
+ throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
+ }
+
+ if (!$this->started) {
+ $this->start();
+ }
+
+ return $this->bags[$name];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isStarted()
+ {
+ return $this->started;
+ }
+
+ public function setMetadataBag(MetadataBag $bag = null)
+ {
+ if (null === $bag) {
+ $bag = new MetadataBag();
+ }
+
+ $this->metadataBag = $bag;
+ }
+
+ /**
+ * Gets the MetadataBag.
+ *
+ * @return MetadataBag
+ */
+ public function getMetadataBag()
+ {
+ return $this->metadataBag;
+ }
+
+ /**
+ * Generates a session ID.
+ *
+ * This doesn't need to be particularly cryptographically secure since this is just
+ * a mock.
+ *
+ * @return string
+ */
+ protected function generateId()
+ {
+ return hash('sha256', uniqid('ss_mock_', true));
+ }
+
+ protected function loadSession()
+ {
+ $bags = array_merge($this->bags, array($this->metadataBag));
+
+ foreach ($bags as $bag) {
+ $key = $bag->getStorageKey();
+ $this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : array();
+ $bag->initialize($this->data[$key]);
+ }
+
+ $this->started = true;
+ $this->closed = false;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php
new file mode 100644
index 0000000..14f4270
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php
@@ -0,0 +1,152 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+/**
+ * MockFileSessionStorage is used to mock sessions for
+ * functional testing when done in a single PHP process.
+ *
+ * No PHP session is actually started since a session can be initialized
+ * and shutdown only once per PHP execution cycle and this class does
+ * not pollute any session related globals, including session_*() functions
+ * or session.* PHP ini directives.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class MockFileSessionStorage extends MockArraySessionStorage
+{
+ private $savePath;
+
+ /**
+ * @param string $savePath Path of directory to save session files
+ * @param string $name Session name
+ * @param MetadataBag $metaBag MetadataBag instance
+ */
+ public function __construct($savePath = null, $name = 'MOCKSESSID', MetadataBag $metaBag = null)
+ {
+ if (null === $savePath) {
+ $savePath = sys_get_temp_dir();
+ }
+
+ if (!is_dir($savePath) && !@mkdir($savePath, 0777, true) && !is_dir($savePath)) {
+ throw new \RuntimeException(sprintf('Session Storage was not able to create directory "%s"', $savePath));
+ }
+
+ $this->savePath = $savePath;
+
+ parent::__construct($name, $metaBag);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function start()
+ {
+ if ($this->started) {
+ return true;
+ }
+
+ if (!$this->id) {
+ $this->id = $this->generateId();
+ }
+
+ $this->read();
+
+ $this->started = true;
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function regenerate($destroy = false, $lifetime = null)
+ {
+ if (!$this->started) {
+ $this->start();
+ }
+
+ if ($destroy) {
+ $this->destroy();
+ }
+
+ return parent::regenerate($destroy, $lifetime);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save()
+ {
+ if (!$this->started) {
+ throw new \RuntimeException('Trying to save a session that was not started yet or was already closed');
+ }
+
+ $data = $this->data;
+
+ foreach ($this->bags as $bag) {
+ if (empty($data[$key = $bag->getStorageKey()])) {
+ unset($data[$key]);
+ }
+ }
+ if (array($key = $this->metadataBag->getStorageKey()) === array_keys($data)) {
+ unset($data[$key]);
+ }
+
+ try {
+ if ($data) {
+ file_put_contents($this->getFilePath(), serialize($data));
+ } else {
+ $this->destroy();
+ }
+ } finally {
+ $this->data = $data;
+ }
+
+ // this is needed for Silex, where the session object is re-used across requests
+ // in functional tests. In Symfony, the container is rebooted, so we don't have
+ // this issue
+ $this->started = false;
+ }
+
+ /**
+ * Deletes a session from persistent storage.
+ * Deliberately leaves session data in memory intact.
+ */
+ private function destroy()
+ {
+ if (is_file($this->getFilePath())) {
+ unlink($this->getFilePath());
+ }
+ }
+
+ /**
+ * Calculate path to file.
+ *
+ * @return string File path
+ */
+ private function getFilePath()
+ {
+ return $this->savePath.'/'.$this->id.'.mocksess';
+ }
+
+ /**
+ * Reads session from storage and loads session.
+ */
+ private function read()
+ {
+ $filePath = $this->getFilePath();
+ $this->data = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : array();
+
+ $this->loadSession();
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php
new file mode 100644
index 0000000..0dfad9a
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php
@@ -0,0 +1,445 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
+
+/**
+ * This provides a base class for session attribute storage.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class NativeSessionStorage implements SessionStorageInterface
+{
+ /**
+ * @var SessionBagInterface[]
+ */
+ protected $bags = array();
+
+ /**
+ * @var bool
+ */
+ protected $started = false;
+
+ /**
+ * @var bool
+ */
+ protected $closed = false;
+
+ /**
+ * @var AbstractProxy|\SessionHandlerInterface
+ */
+ protected $saveHandler;
+
+ /**
+ * @var MetadataBag
+ */
+ protected $metadataBag;
+
+ /**
+ * Depending on how you want the storage driver to behave you probably
+ * want to override this constructor entirely.
+ *
+ * List of options for $options array with their defaults.
+ *
+ * @see http://php.net/session.configuration for options
+ * but we omit 'session.' from the beginning of the keys for convenience.
+ *
+ * ("auto_start", is not supported as it tells PHP to start a session before
+ * PHP starts to execute user-land code. Setting during runtime has no effect).
+ *
+ * cache_limiter, "" (use "0" to prevent headers from being sent entirely).
+ * cache_expire, "0"
+ * cookie_domain, ""
+ * cookie_httponly, ""
+ * cookie_lifetime, "0"
+ * cookie_path, "/"
+ * cookie_secure, ""
+ * entropy_file, ""
+ * entropy_length, "0"
+ * gc_divisor, "100"
+ * gc_maxlifetime, "1440"
+ * gc_probability, "1"
+ * hash_bits_per_character, "4"
+ * hash_function, "0"
+ * lazy_write, "1"
+ * name, "PHPSESSID"
+ * referer_check, ""
+ * serialize_handler, "php"
+ * use_strict_mode, "0"
+ * use_cookies, "1"
+ * use_only_cookies, "1"
+ * use_trans_sid, "0"
+ * upload_progress.enabled, "1"
+ * upload_progress.cleanup, "1"
+ * upload_progress.prefix, "upload_progress_"
+ * upload_progress.name, "PHP_SESSION_UPLOAD_PROGRESS"
+ * upload_progress.freq, "1%"
+ * upload_progress.min-freq, "1"
+ * url_rewriter.tags, "a=href,area=href,frame=src,form=,fieldset="
+ * sid_length, "32"
+ * sid_bits_per_character, "5"
+ * trans_sid_hosts, $_SERVER['HTTP_HOST']
+ * trans_sid_tags, "a=href,area=href,frame=src,form="
+ *
+ * @param array $options Session configuration options
+ * @param \SessionHandlerInterface|null $handler
+ * @param MetadataBag $metaBag MetadataBag
+ */
+ public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null)
+ {
+ $options += array(
+ 'cache_limiter' => '',
+ 'cache_expire' => 0,
+ 'use_cookies' => 1,
+ 'lazy_write' => 1,
+ );
+
+ session_register_shutdown();
+
+ $this->setMetadataBag($metaBag);
+ $this->setOptions($options);
+ $this->setSaveHandler($handler);
+ }
+
+ /**
+ * Gets the save handler instance.
+ *
+ * @return AbstractProxy|\SessionHandlerInterface
+ */
+ public function getSaveHandler()
+ {
+ return $this->saveHandler;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function start()
+ {
+ if ($this->started) {
+ return true;
+ }
+
+ if (\PHP_SESSION_ACTIVE === session_status()) {
+ throw new \RuntimeException('Failed to start the session: already started by PHP.');
+ }
+
+ if (ini_get('session.use_cookies') && headers_sent($file, $line)) {
+ throw new \RuntimeException(sprintf('Failed to start the session because headers have already been sent by "%s" at line %d.', $file, $line));
+ }
+
+ // ok to try and start the session
+ if (!session_start()) {
+ throw new \RuntimeException('Failed to start the session');
+ }
+
+ $this->loadSession();
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return $this->saveHandler->getId();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setId($id)
+ {
+ $this->saveHandler->setId($id);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->saveHandler->getName();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setName($name)
+ {
+ $this->saveHandler->setName($name);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function regenerate($destroy = false, $lifetime = null)
+ {
+ // Cannot regenerate the session ID for non-active sessions.
+ if (\PHP_SESSION_ACTIVE !== session_status()) {
+ return false;
+ }
+
+ if (headers_sent()) {
+ return false;
+ }
+
+ if (null !== $lifetime) {
+ ini_set('session.cookie_lifetime', $lifetime);
+ }
+
+ if ($destroy) {
+ $this->metadataBag->stampNew();
+ }
+
+ $isRegenerated = session_regenerate_id($destroy);
+
+ // The reference to $_SESSION in session bags is lost in PHP7 and we need to re-create it.
+ // @see https://bugs.php.net/bug.php?id=70013
+ $this->loadSession();
+
+ return $isRegenerated;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save()
+ {
+ $session = $_SESSION;
+
+ foreach ($this->bags as $bag) {
+ if (empty($_SESSION[$key = $bag->getStorageKey()])) {
+ unset($_SESSION[$key]);
+ }
+ }
+ if (array($key = $this->metadataBag->getStorageKey()) === array_keys($_SESSION)) {
+ unset($_SESSION[$key]);
+ }
+
+ // Register custom error handler to catch a possible failure warning during session write
+ set_error_handler(function ($errno, $errstr, $errfile, $errline) {
+ throw new \ErrorException($errstr, $errno, E_WARNING, $errfile, $errline);
+ }, E_WARNING);
+
+ try {
+ $e = null;
+ session_write_close();
+ } catch (\ErrorException $e) {
+ } finally {
+ restore_error_handler();
+ $_SESSION = $session;
+ }
+ if (null !== $e) {
+ // The default PHP error message is not very helpful, as it does not give any information on the current save handler.
+ // Therefore, we catch this error and trigger a warning with a better error message
+ $handler = $this->getSaveHandler();
+ if ($handler instanceof SessionHandlerProxy) {
+ $handler = $handler->getHandler();
+ }
+
+ trigger_error(sprintf('session_write_close(): Failed to write session data with %s handler', get_class($handler)), E_USER_WARNING);
+ }
+
+ $this->closed = true;
+ $this->started = false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ // clear out the bags
+ foreach ($this->bags as $bag) {
+ $bag->clear();
+ }
+
+ // clear out the session
+ $_SESSION = array();
+
+ // reconnect the bags to the session
+ $this->loadSession();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function registerBag(SessionBagInterface $bag)
+ {
+ if ($this->started) {
+ throw new \LogicException('Cannot register a bag when the session is already started.');
+ }
+
+ $this->bags[$bag->getName()] = $bag;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getBag($name)
+ {
+ if (!isset($this->bags[$name])) {
+ throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
+ }
+
+ if (!$this->started && $this->saveHandler->isActive()) {
+ $this->loadSession();
+ } elseif (!$this->started) {
+ $this->start();
+ }
+
+ return $this->bags[$name];
+ }
+
+ public function setMetadataBag(MetadataBag $metaBag = null)
+ {
+ if (null === $metaBag) {
+ $metaBag = new MetadataBag();
+ }
+
+ $this->metadataBag = $metaBag;
+ }
+
+ /**
+ * Gets the MetadataBag.
+ *
+ * @return MetadataBag
+ */
+ public function getMetadataBag()
+ {
+ return $this->metadataBag;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isStarted()
+ {
+ return $this->started;
+ }
+
+ /**
+ * Sets session.* ini variables.
+ *
+ * For convenience we omit 'session.' from the beginning of the keys.
+ * Explicitly ignores other ini keys.
+ *
+ * @param array $options Session ini directives array(key => value)
+ *
+ * @see http://php.net/session.configuration
+ */
+ public function setOptions(array $options)
+ {
+ if (headers_sent() || \PHP_SESSION_ACTIVE === session_status()) {
+ return;
+ }
+
+ $validOptions = array_flip(array(
+ 'cache_limiter', 'cache_expire', 'cookie_domain', 'cookie_httponly',
+ 'cookie_lifetime', 'cookie_path', 'cookie_secure',
+ 'entropy_file', 'entropy_length', 'gc_divisor',
+ 'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character',
+ 'hash_function', 'lazy_write', 'name', 'referer_check',
+ 'serialize_handler', 'use_strict_mode', 'use_cookies',
+ 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
+ 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
+ 'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags',
+ 'sid_length', 'sid_bits_per_character', 'trans_sid_hosts', 'trans_sid_tags',
+ ));
+
+ foreach ($options as $key => $value) {
+ if (isset($validOptions[$key])) {
+ ini_set('session.'.$key, $value);
+ }
+ }
+ }
+
+ /**
+ * Registers session save handler as a PHP session handler.
+ *
+ * To use internal PHP session save handlers, override this method using ini_set with
+ * session.save_handler and session.save_path e.g.
+ *
+ * ini_set('session.save_handler', 'files');
+ * ini_set('session.save_path', '/tmp');
+ *
+ * or pass in a \SessionHandler instance which configures session.save_handler in the
+ * constructor, for a template see NativeFileSessionHandler or use handlers in
+ * composer package drak/native-session
+ *
+ * @see http://php.net/session-set-save-handler
+ * @see http://php.net/sessionhandlerinterface
+ * @see http://php.net/sessionhandler
+ * @see http://github.com/drak/NativeSession
+ *
+ * @param \SessionHandlerInterface|null $saveHandler
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setSaveHandler($saveHandler = null)
+ {
+ if (!$saveHandler instanceof AbstractProxy &&
+ !$saveHandler instanceof \SessionHandlerInterface &&
+ null !== $saveHandler) {
+ throw new \InvalidArgumentException('Must be instance of AbstractProxy; implement \SessionHandlerInterface; or be null.');
+ }
+
+ // Wrap $saveHandler in proxy and prevent double wrapping of proxy
+ if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) {
+ $saveHandler = new SessionHandlerProxy($saveHandler);
+ } elseif (!$saveHandler instanceof AbstractProxy) {
+ $saveHandler = new SessionHandlerProxy(new StrictSessionHandler(new \SessionHandler()));
+ }
+ $this->saveHandler = $saveHandler;
+
+ if (headers_sent() || \PHP_SESSION_ACTIVE === session_status()) {
+ return;
+ }
+
+ if ($this->saveHandler instanceof SessionHandlerProxy) {
+ session_set_save_handler($this->saveHandler->getHandler(), false);
+ } elseif ($this->saveHandler instanceof \SessionHandlerInterface) {
+ session_set_save_handler($this->saveHandler, false);
+ }
+ }
+
+ /**
+ * Load the session with attributes.
+ *
+ * After starting the session, PHP retrieves the session from whatever handlers
+ * are set to (either PHP's internal, or a custom save handler set with session_set_save_handler()).
+ * PHP takes the return value from the read() handler, unserializes it
+ * and populates $_SESSION with the result automatically.
+ */
+ protected function loadSession(array &$session = null)
+ {
+ if (null === $session) {
+ $session = &$_SESSION;
+ }
+
+ $bags = array_merge($this->bags, array($this->metadataBag));
+
+ foreach ($bags as $bag) {
+ $key = $bag->getStorageKey();
+ $session[$key] = isset($session[$key]) ? $session[$key] : array();
+ $bag->initialize($session[$key]);
+ }
+
+ $this->started = true;
+ $this->closed = false;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php
new file mode 100644
index 0000000..662ed50
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php
@@ -0,0 +1,59 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+/**
+ * Allows session to be started by PHP and managed by Symfony.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class PhpBridgeSessionStorage extends NativeSessionStorage
+{
+ /**
+ * @param \SessionHandlerInterface|null $handler
+ * @param MetadataBag $metaBag MetadataBag
+ */
+ public function __construct($handler = null, MetadataBag $metaBag = null)
+ {
+ $this->setMetadataBag($metaBag);
+ $this->setSaveHandler($handler);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function start()
+ {
+ if ($this->started) {
+ return true;
+ }
+
+ $this->loadSession();
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ // clear out the bags and nothing else that may be set
+ // since the purpose of this driver is to share a handler
+ foreach ($this->bags as $bag) {
+ $bag->clear();
+ }
+
+ // reconnect the bags to the session
+ $this->loadSession();
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php
new file mode 100644
index 0000000..09c9248
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php
@@ -0,0 +1,122 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy;
+
+/**
+ * @author Drak <drak@zikula.org>
+ */
+abstract class AbstractProxy
+{
+ /**
+ * Flag if handler wraps an internal PHP session handler (using \SessionHandler).
+ *
+ * @var bool
+ */
+ protected $wrapper = false;
+
+ /**
+ * @var string
+ */
+ protected $saveHandlerName;
+
+ /**
+ * Gets the session.save_handler name.
+ *
+ * @return string
+ */
+ public function getSaveHandlerName()
+ {
+ return $this->saveHandlerName;
+ }
+
+ /**
+ * Is this proxy handler and instance of \SessionHandlerInterface.
+ *
+ * @return bool
+ */
+ public function isSessionHandlerInterface()
+ {
+ return $this instanceof \SessionHandlerInterface;
+ }
+
+ /**
+ * Returns true if this handler wraps an internal PHP session save handler using \SessionHandler.
+ *
+ * @return bool
+ */
+ public function isWrapper()
+ {
+ return $this->wrapper;
+ }
+
+ /**
+ * Has a session started?
+ *
+ * @return bool
+ */
+ public function isActive()
+ {
+ return \PHP_SESSION_ACTIVE === session_status();
+ }
+
+ /**
+ * Gets the session ID.
+ *
+ * @return string
+ */
+ public function getId()
+ {
+ return session_id();
+ }
+
+ /**
+ * Sets the session ID.
+ *
+ * @param string $id
+ *
+ * @throws \LogicException
+ */
+ public function setId($id)
+ {
+ if ($this->isActive()) {
+ throw new \LogicException('Cannot change the ID of an active session');
+ }
+
+ session_id($id);
+ }
+
+ /**
+ * Gets the session name.
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return session_name();
+ }
+
+ /**
+ * Sets the session name.
+ *
+ * @param string $name
+ *
+ * @throws \LogicException
+ */
+ public function setName($name)
+ {
+ if ($this->isActive()) {
+ throw new \LogicException('Cannot change the name of an active session');
+ }
+
+ session_name($name);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.php
new file mode 100644
index 0000000..082eed1
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.php
@@ -0,0 +1,40 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy;
+
+@trigger_error('The '.__NAMESPACE__.'\NativeProxy class is deprecated since Symfony 3.4 and will be removed in 4.0. Use your session handler implementation directly.', E_USER_DEPRECATED);
+
+/**
+ * This proxy is built-in session handlers in PHP 5.3.x.
+ *
+ * @deprecated since version 3.4, to be removed in 4.0. Use your session handler implementation directly.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class NativeProxy extends AbstractProxy
+{
+ public function __construct()
+ {
+ // this makes an educated guess as to what the handler is since it should already be set.
+ $this->saveHandlerName = ini_get('session.save_handler');
+ }
+
+ /**
+ * Returns true if this handler wraps an internal PHP session save handler using \SessionHandler.
+ *
+ * @return bool False
+ */
+ public function isWrapper()
+ {
+ return false;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php
new file mode 100644
index 0000000..53c1209
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php
@@ -0,0 +1,85 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy;
+
+/**
+ * @author Drak <drak@zikula.org>
+ */
+class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface
+{
+ protected $handler;
+
+ public function __construct(\SessionHandlerInterface $handler)
+ {
+ $this->handler = $handler;
+ $this->wrapper = ($handler instanceof \SessionHandler);
+ $this->saveHandlerName = $this->wrapper ? ini_get('session.save_handler') : 'user';
+ }
+
+ /**
+ * @return \SessionHandlerInterface
+ */
+ public function getHandler()
+ {
+ return $this->handler;
+ }
+
+ // \SessionHandlerInterface
+
+ /**
+ * {@inheritdoc}
+ */
+ public function open($savePath, $sessionName)
+ {
+ return (bool) $this->handler->open($savePath, $sessionName);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function close()
+ {
+ return (bool) $this->handler->close();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function read($sessionId)
+ {
+ return (string) $this->handler->read($sessionId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write($sessionId, $data)
+ {
+ return (bool) $this->handler->write($sessionId, $data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function destroy($sessionId)
+ {
+ return (bool) $this->handler->destroy($sessionId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function gc($maxlifetime)
+ {
+ return (bool) $this->handler->gc($maxlifetime);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php b/assets/php/vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php
new file mode 100644
index 0000000..66e8b33
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php
@@ -0,0 +1,137 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * StorageInterface.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Drak <drak@zikula.org>
+ */
+interface SessionStorageInterface
+{
+ /**
+ * Starts the session.
+ *
+ * @return bool True if started
+ *
+ * @throws \RuntimeException if something goes wrong starting the session
+ */
+ public function start();
+
+ /**
+ * Checks if the session is started.
+ *
+ * @return bool True if started, false otherwise
+ */
+ public function isStarted();
+
+ /**
+ * Returns the session ID.
+ *
+ * @return string The session ID or empty
+ */
+ public function getId();
+
+ /**
+ * Sets the session ID.
+ *
+ * @param string $id
+ */
+ public function setId($id);
+
+ /**
+ * Returns the session name.
+ *
+ * @return mixed The session name
+ */
+ public function getName();
+
+ /**
+ * Sets the session name.
+ *
+ * @param string $name
+ */
+ public function setName($name);
+
+ /**
+ * Regenerates id that represents this storage.
+ *
+ * This method must invoke session_regenerate_id($destroy) unless
+ * this interface is used for a storage object designed for unit
+ * or functional testing where a real PHP session would interfere
+ * with testing.
+ *
+ * Note regenerate+destroy should not clear the session data in memory
+ * only delete the session data from persistent storage.
+ *
+ * Care: When regenerating the session ID no locking is involved in PHP's
+ * session design. See https://bugs.php.net/bug.php?id=61470 for a discussion.
+ * So you must make sure the regenerated session is saved BEFORE sending the
+ * headers with the new ID. Symfony's HttpKernel offers a listener for this.
+ * See Symfony\Component\HttpKernel\EventListener\SaveSessionListener.
+ * Otherwise session data could get lost again for concurrent requests with the
+ * new ID. One result could be that you get logged out after just logging in.
+ *
+ * @param bool $destroy Destroy session when regenerating?
+ * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value
+ * will leave the system settings unchanged, 0 sets the cookie
+ * to expire with browser session. Time is in seconds, and is
+ * not a Unix timestamp.
+ *
+ * @return bool True if session regenerated, false if error
+ *
+ * @throws \RuntimeException If an error occurs while regenerating this storage
+ */
+ public function regenerate($destroy = false, $lifetime = null);
+
+ /**
+ * Force the session to be saved and closed.
+ *
+ * This method must invoke session_write_close() unless this interface is
+ * used for a storage object design for unit or functional testing where
+ * a real PHP session would interfere with testing, in which case
+ * it should actually persist the session data if required.
+ *
+ * @throws \RuntimeException if the session is saved without being started, or if the session
+ * is already closed
+ */
+ public function save();
+
+ /**
+ * Clear all session data in memory.
+ */
+ public function clear();
+
+ /**
+ * Gets a SessionBagInterface by name.
+ *
+ * @param string $name
+ *
+ * @return SessionBagInterface
+ *
+ * @throws \InvalidArgumentException If the bag does not exist
+ */
+ public function getBag($name);
+
+ /**
+ * Registers a SessionBagInterface for use.
+ */
+ public function registerBag(SessionBagInterface $bag);
+
+ /**
+ * @return MetadataBag
+ */
+ public function getMetadataBag();
+}
diff --git a/assets/php/vendor/symfony/http-foundation/StreamedResponse.php b/assets/php/vendor/symfony/http-foundation/StreamedResponse.php
new file mode 100644
index 0000000..92868d3
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/StreamedResponse.php
@@ -0,0 +1,144 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * StreamedResponse represents a streamed HTTP response.
+ *
+ * A StreamedResponse uses a callback for its content.
+ *
+ * The callback should use the standard PHP functions like echo
+ * to stream the response back to the client. The flush() method
+ * can also be used if needed.
+ *
+ * @see flush()
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class StreamedResponse extends Response
+{
+ protected $callback;
+ protected $streamed;
+ private $headersSent;
+
+ /**
+ * @param callable|null $callback A valid PHP callback or null to set it later
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ */
+ public function __construct(callable $callback = null, $status = 200, $headers = array())
+ {
+ parent::__construct(null, $status, $headers);
+
+ if (null !== $callback) {
+ $this->setCallback($callback);
+ }
+ $this->streamed = false;
+ $this->headersSent = false;
+ }
+
+ /**
+ * Factory method for chainability.
+ *
+ * @param callable|null $callback A valid PHP callback or null to set it later
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ *
+ * @return static
+ */
+ public static function create($callback = null, $status = 200, $headers = array())
+ {
+ return new static($callback, $status, $headers);
+ }
+
+ /**
+ * Sets the PHP callback associated with this Response.
+ *
+ * @param callable $callback A valid PHP callback
+ *
+ * @return $this
+ */
+ public function setCallback(callable $callback)
+ {
+ $this->callback = $callback;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * This method only sends the headers once.
+ *
+ * @return $this
+ */
+ public function sendHeaders()
+ {
+ if ($this->headersSent) {
+ return $this;
+ }
+
+ $this->headersSent = true;
+
+ return parent::sendHeaders();
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * This method only sends the content once.
+ *
+ * @return $this
+ */
+ public function sendContent()
+ {
+ if ($this->streamed) {
+ return $this;
+ }
+
+ $this->streamed = true;
+
+ if (null === $this->callback) {
+ throw new \LogicException('The Response callback must not be null.');
+ }
+
+ call_user_func($this->callback);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @throws \LogicException when the content is not null
+ *
+ * @return $this
+ */
+ public function setContent($content)
+ {
+ if (null !== $content) {
+ throw new \LogicException('The content cannot be set on a StreamedResponse instance.');
+ }
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return false
+ */
+ public function getContent()
+ {
+ return false;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php b/assets/php/vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php
new file mode 100644
index 0000000..cb43bb3
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php
@@ -0,0 +1,113 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\AcceptHeaderItem;
+
+class AcceptHeaderItemTest extends TestCase
+{
+ /**
+ * @dataProvider provideFromStringData
+ */
+ public function testFromString($string, $value, array $attributes)
+ {
+ $item = AcceptHeaderItem::fromString($string);
+ $this->assertEquals($value, $item->getValue());
+ $this->assertEquals($attributes, $item->getAttributes());
+ }
+
+ public function provideFromStringData()
+ {
+ return array(
+ array(
+ 'text/html',
+ 'text/html', array(),
+ ),
+ array(
+ '"this;should,not=matter"',
+ 'this;should,not=matter', array(),
+ ),
+ array(
+ "text/plain; charset=utf-8;param=\"this;should,not=matter\";\tfootnotes=true",
+ 'text/plain', array('charset' => 'utf-8', 'param' => 'this;should,not=matter', 'footnotes' => 'true'),
+ ),
+ array(
+ '"this;should,not=matter";charset=utf-8',
+ 'this;should,not=matter', array('charset' => 'utf-8'),
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider provideToStringData
+ */
+ public function testToString($value, array $attributes, $string)
+ {
+ $item = new AcceptHeaderItem($value, $attributes);
+ $this->assertEquals($string, (string) $item);
+ }
+
+ public function provideToStringData()
+ {
+ return array(
+ array(
+ 'text/html', array(),
+ 'text/html',
+ ),
+ array(
+ 'text/plain', array('charset' => 'utf-8', 'param' => 'this;should,not=matter', 'footnotes' => 'true'),
+ 'text/plain;charset=utf-8;param="this;should,not=matter";footnotes=true',
+ ),
+ );
+ }
+
+ public function testValue()
+ {
+ $item = new AcceptHeaderItem('value', array());
+ $this->assertEquals('value', $item->getValue());
+
+ $item->setValue('new value');
+ $this->assertEquals('new value', $item->getValue());
+
+ $item->setValue(1);
+ $this->assertEquals('1', $item->getValue());
+ }
+
+ public function testQuality()
+ {
+ $item = new AcceptHeaderItem('value', array());
+ $this->assertEquals(1.0, $item->getQuality());
+
+ $item->setQuality(0.5);
+ $this->assertEquals(0.5, $item->getQuality());
+
+ $item->setAttribute('q', 0.75);
+ $this->assertEquals(0.75, $item->getQuality());
+ $this->assertFalse($item->hasAttribute('q'));
+ }
+
+ public function testAttribute()
+ {
+ $item = new AcceptHeaderItem('value', array());
+ $this->assertEquals(array(), $item->getAttributes());
+ $this->assertFalse($item->hasAttribute('test'));
+ $this->assertNull($item->getAttribute('test'));
+ $this->assertEquals('default', $item->getAttribute('test', 'default'));
+
+ $item->setAttribute('test', 'value');
+ $this->assertEquals(array('test' => 'value'), $item->getAttributes());
+ $this->assertTrue($item->hasAttribute('test'));
+ $this->assertEquals('value', $item->getAttribute('test'));
+ $this->assertEquals('value', $item->getAttribute('test', 'default'));
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php b/assets/php/vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php
new file mode 100644
index 0000000..9929eac
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php
@@ -0,0 +1,103 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\AcceptHeader;
+use Symfony\Component\HttpFoundation\AcceptHeaderItem;
+
+class AcceptHeaderTest extends TestCase
+{
+ public function testFirst()
+ {
+ $header = AcceptHeader::fromString('text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c');
+ $this->assertSame('text/html', $header->first()->getValue());
+ }
+
+ /**
+ * @dataProvider provideFromStringData
+ */
+ public function testFromString($string, array $items)
+ {
+ $header = AcceptHeader::fromString($string);
+ $parsed = array_values($header->all());
+ // reset index since the fixtures don't have them set
+ foreach ($parsed as $item) {
+ $item->setIndex(0);
+ }
+ $this->assertEquals($items, $parsed);
+ }
+
+ public function provideFromStringData()
+ {
+ return array(
+ array('', array()),
+ array('gzip', array(new AcceptHeaderItem('gzip'))),
+ array('gzip,deflate,sdch', array(new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch'))),
+ array("gzip, deflate\t,sdch", array(new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch'))),
+ array('"this;should,not=matter"', array(new AcceptHeaderItem('this;should,not=matter'))),
+ );
+ }
+
+ /**
+ * @dataProvider provideToStringData
+ */
+ public function testToString(array $items, $string)
+ {
+ $header = new AcceptHeader($items);
+ $this->assertEquals($string, (string) $header);
+ }
+
+ public function provideToStringData()
+ {
+ return array(
+ array(array(), ''),
+ array(array(new AcceptHeaderItem('gzip')), 'gzip'),
+ array(array(new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch')), 'gzip,deflate,sdch'),
+ array(array(new AcceptHeaderItem('this;should,not=matter')), 'this;should,not=matter'),
+ );
+ }
+
+ /**
+ * @dataProvider provideFilterData
+ */
+ public function testFilter($string, $filter, array $values)
+ {
+ $header = AcceptHeader::fromString($string)->filter($filter);
+ $this->assertEquals($values, array_keys($header->all()));
+ }
+
+ public function provideFilterData()
+ {
+ return array(
+ array('fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4', '/fr.*/', array('fr-FR', 'fr')),
+ );
+ }
+
+ /**
+ * @dataProvider provideSortingData
+ */
+ public function testSorting($string, array $values)
+ {
+ $header = AcceptHeader::fromString($string);
+ $this->assertEquals($values, array_keys($header->all()));
+ }
+
+ public function provideSortingData()
+ {
+ return array(
+ 'quality has priority' => array('*;q=0.3,ISO-8859-1,utf-8;q=0.7', array('ISO-8859-1', 'utf-8', '*')),
+ 'order matters when q is equal' => array('*;q=0.3,ISO-8859-1;q=0.7,utf-8;q=0.7', array('ISO-8859-1', 'utf-8', '*')),
+ 'order matters when q is equal2' => array('*;q=0.3,utf-8;q=0.7,ISO-8859-1;q=0.7', array('utf-8', 'ISO-8859-1', '*')),
+ );
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/ApacheRequestTest.php b/assets/php/vendor/symfony/http-foundation/Tests/ApacheRequestTest.php
new file mode 100644
index 0000000..157ab90
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/ApacheRequestTest.php
@@ -0,0 +1,93 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\ApacheRequest;
+
+class ApacheRequestTest extends TestCase
+{
+ /**
+ * @dataProvider provideServerVars
+ */
+ public function testUriMethods($server, $expectedRequestUri, $expectedBaseUrl, $expectedPathInfo)
+ {
+ $request = new ApacheRequest();
+ $request->server->replace($server);
+
+ $this->assertEquals($expectedRequestUri, $request->getRequestUri(), '->getRequestUri() is correct');
+ $this->assertEquals($expectedBaseUrl, $request->getBaseUrl(), '->getBaseUrl() is correct');
+ $this->assertEquals($expectedPathInfo, $request->getPathInfo(), '->getPathInfo() is correct');
+ }
+
+ public function provideServerVars()
+ {
+ return array(
+ array(
+ array(
+ 'REQUEST_URI' => '/foo/app_dev.php/bar',
+ 'SCRIPT_NAME' => '/foo/app_dev.php',
+ 'PATH_INFO' => '/bar',
+ ),
+ '/foo/app_dev.php/bar',
+ '/foo/app_dev.php',
+ '/bar',
+ ),
+ array(
+ array(
+ 'REQUEST_URI' => '/foo/bar',
+ 'SCRIPT_NAME' => '/foo/app_dev.php',
+ ),
+ '/foo/bar',
+ '/foo',
+ '/bar',
+ ),
+ array(
+ array(
+ 'REQUEST_URI' => '/app_dev.php/foo/bar',
+ 'SCRIPT_NAME' => '/app_dev.php',
+ 'PATH_INFO' => '/foo/bar',
+ ),
+ '/app_dev.php/foo/bar',
+ '/app_dev.php',
+ '/foo/bar',
+ ),
+ array(
+ array(
+ 'REQUEST_URI' => '/foo/bar',
+ 'SCRIPT_NAME' => '/app_dev.php',
+ ),
+ '/foo/bar',
+ '',
+ '/foo/bar',
+ ),
+ array(
+ array(
+ 'REQUEST_URI' => '/app_dev.php',
+ 'SCRIPT_NAME' => '/app_dev.php',
+ ),
+ '/app_dev.php',
+ '/app_dev.php',
+ '/',
+ ),
+ array(
+ array(
+ 'REQUEST_URI' => '/',
+ 'SCRIPT_NAME' => '/app_dev.php',
+ ),
+ '/',
+ '',
+ '/',
+ ),
+ );
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php b/assets/php/vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php
new file mode 100644
index 0000000..1b9e589
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php
@@ -0,0 +1,352 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use Symfony\Component\HttpFoundation\BinaryFileResponse;
+use Symfony\Component\HttpFoundation\File\Stream;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\ResponseHeaderBag;
+use Symfony\Component\HttpFoundation\Tests\File\FakeFile;
+
+class BinaryFileResponseTest extends ResponseTestCase
+{
+ public function testConstruction()
+ {
+ $file = __DIR__.'/../README.md';
+ $response = new BinaryFileResponse($file, 404, array('X-Header' => 'Foo'), true, null, true, true);
+ $this->assertEquals(404, $response->getStatusCode());
+ $this->assertEquals('Foo', $response->headers->get('X-Header'));
+ $this->assertTrue($response->headers->has('ETag'));
+ $this->assertTrue($response->headers->has('Last-Modified'));
+ $this->assertFalse($response->headers->has('Content-Disposition'));
+
+ $response = BinaryFileResponse::create($file, 404, array(), true, ResponseHeaderBag::DISPOSITION_INLINE);
+ $this->assertEquals(404, $response->getStatusCode());
+ $this->assertFalse($response->headers->has('ETag'));
+ $this->assertEquals('inline; filename="README.md"', $response->headers->get('Content-Disposition'));
+ }
+
+ public function testConstructWithNonAsciiFilename()
+ {
+ touch(sys_get_temp_dir().'/fööö.html');
+
+ $response = new BinaryFileResponse(sys_get_temp_dir().'/fööö.html', 200, array(), true, 'attachment');
+
+ @unlink(sys_get_temp_dir().'/fööö.html');
+
+ $this->assertSame('fööö.html', $response->getFile()->getFilename());
+ }
+
+ /**
+ * @expectedException \LogicException
+ */
+ public function testSetContent()
+ {
+ $response = new BinaryFileResponse(__FILE__);
+ $response->setContent('foo');
+ }
+
+ public function testGetContent()
+ {
+ $response = new BinaryFileResponse(__FILE__);
+ $this->assertFalse($response->getContent());
+ }
+
+ public function testSetContentDispositionGeneratesSafeFallbackFilename()
+ {
+ $response = new BinaryFileResponse(__FILE__);
+ $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'föö.html');
+
+ $this->assertSame('attachment; filename="f__.html"; filename*=utf-8\'\'f%C3%B6%C3%B6.html', $response->headers->get('Content-Disposition'));
+ }
+
+ public function testSetContentDispositionGeneratesSafeFallbackFilenameForWronglyEncodedFilename()
+ {
+ $response = new BinaryFileResponse(__FILE__);
+
+ $iso88591EncodedFilename = utf8_decode('föö.html');
+ $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $iso88591EncodedFilename);
+
+ // the parameter filename* is invalid in this case (rawurldecode('f%F6%F6') does not provide a UTF-8 string but an ISO-8859-1 encoded one)
+ $this->assertSame('attachment; filename="f__.html"; filename*=utf-8\'\'f%F6%F6.html', $response->headers->get('Content-Disposition'));
+ }
+
+ /**
+ * @dataProvider provideRanges
+ */
+ public function testRequests($requestRange, $offset, $length, $responseRange)
+ {
+ $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag();
+
+ // do a request to get the ETag
+ $request = Request::create('/');
+ $response->prepare($request);
+ $etag = $response->headers->get('ETag');
+
+ // prepare a request for a range of the testing file
+ $request = Request::create('/');
+ $request->headers->set('If-Range', $etag);
+ $request->headers->set('Range', $requestRange);
+
+ $file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
+ fseek($file, $offset);
+ $data = fread($file, $length);
+ fclose($file);
+
+ $this->expectOutputString($data);
+ $response = clone $response;
+ $response->prepare($request);
+ $response->sendContent();
+
+ $this->assertEquals(206, $response->getStatusCode());
+ $this->assertEquals($responseRange, $response->headers->get('Content-Range'));
+ $this->assertSame($length, $response->headers->get('Content-Length'));
+ }
+
+ /**
+ * @dataProvider provideRanges
+ */
+ public function testRequestsWithoutEtag($requestRange, $offset, $length, $responseRange)
+ {
+ $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
+
+ // do a request to get the LastModified
+ $request = Request::create('/');
+ $response->prepare($request);
+ $lastModified = $response->headers->get('Last-Modified');
+
+ // prepare a request for a range of the testing file
+ $request = Request::create('/');
+ $request->headers->set('If-Range', $lastModified);
+ $request->headers->set('Range', $requestRange);
+
+ $file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
+ fseek($file, $offset);
+ $data = fread($file, $length);
+ fclose($file);
+
+ $this->expectOutputString($data);
+ $response = clone $response;
+ $response->prepare($request);
+ $response->sendContent();
+
+ $this->assertEquals(206, $response->getStatusCode());
+ $this->assertEquals($responseRange, $response->headers->get('Content-Range'));
+ }
+
+ public function provideRanges()
+ {
+ return array(
+ array('bytes=1-4', 1, 4, 'bytes 1-4/35'),
+ array('bytes=-5', 30, 5, 'bytes 30-34/35'),
+ array('bytes=30-', 30, 5, 'bytes 30-34/35'),
+ array('bytes=30-30', 30, 1, 'bytes 30-30/35'),
+ array('bytes=30-34', 30, 5, 'bytes 30-34/35'),
+ );
+ }
+
+ public function testRangeRequestsWithoutLastModifiedDate()
+ {
+ // prevent auto last modified
+ $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'), true, null, false, false);
+
+ // prepare a request for a range of the testing file
+ $request = Request::create('/');
+ $request->headers->set('If-Range', date('D, d M Y H:i:s').' GMT');
+ $request->headers->set('Range', 'bytes=1-4');
+
+ $this->expectOutputString(file_get_contents(__DIR__.'/File/Fixtures/test.gif'));
+ $response = clone $response;
+ $response->prepare($request);
+ $response->sendContent();
+
+ $this->assertEquals(200, $response->getStatusCode());
+ $this->assertNull($response->headers->get('Content-Range'));
+ }
+
+ /**
+ * @dataProvider provideFullFileRanges
+ */
+ public function testFullFileRequests($requestRange)
+ {
+ $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag();
+
+ // prepare a request for a range of the testing file
+ $request = Request::create('/');
+ $request->headers->set('Range', $requestRange);
+
+ $file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
+ $data = fread($file, 35);
+ fclose($file);
+
+ $this->expectOutputString($data);
+ $response = clone $response;
+ $response->prepare($request);
+ $response->sendContent();
+
+ $this->assertEquals(200, $response->getStatusCode());
+ }
+
+ public function provideFullFileRanges()
+ {
+ return array(
+ array('bytes=0-'),
+ array('bytes=0-34'),
+ array('bytes=-35'),
+ // Syntactical invalid range-request should also return the full resource
+ array('bytes=20-10'),
+ array('bytes=50-40'),
+ );
+ }
+
+ /**
+ * @dataProvider provideInvalidRanges
+ */
+ public function testInvalidRequests($requestRange)
+ {
+ $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag();
+
+ // prepare a request for a range of the testing file
+ $request = Request::create('/');
+ $request->headers->set('Range', $requestRange);
+
+ $response = clone $response;
+ $response->prepare($request);
+ $response->sendContent();
+
+ $this->assertEquals(416, $response->getStatusCode());
+ $this->assertEquals('bytes */35', $response->headers->get('Content-Range'));
+ }
+
+ public function provideInvalidRanges()
+ {
+ return array(
+ array('bytes=-40'),
+ array('bytes=30-40'),
+ );
+ }
+
+ /**
+ * @dataProvider provideXSendfileFiles
+ */
+ public function testXSendfile($file)
+ {
+ $request = Request::create('/');
+ $request->headers->set('X-Sendfile-Type', 'X-Sendfile');
+
+ BinaryFileResponse::trustXSendfileTypeHeader();
+ $response = BinaryFileResponse::create($file, 200, array('Content-Type' => 'application/octet-stream'));
+ $response->prepare($request);
+
+ $this->expectOutputString('');
+ $response->sendContent();
+
+ $this->assertContains('README.md', $response->headers->get('X-Sendfile'));
+ }
+
+ public function provideXSendfileFiles()
+ {
+ return array(
+ array(__DIR__.'/../README.md'),
+ array('file://'.__DIR__.'/../README.md'),
+ );
+ }
+
+ /**
+ * @dataProvider getSampleXAccelMappings
+ */
+ public function testXAccelMapping($realpath, $mapping, $virtual)
+ {
+ $request = Request::create('/');
+ $request->headers->set('X-Sendfile-Type', 'X-Accel-Redirect');
+ $request->headers->set('X-Accel-Mapping', $mapping);
+
+ $file = new FakeFile($realpath, __DIR__.'/File/Fixtures/test');
+
+ BinaryFileResponse::trustXSendfileTypeHeader();
+ $response = new BinaryFileResponse($file, 200, array('Content-Type' => 'application/octet-stream'));
+ $reflection = new \ReflectionObject($response);
+ $property = $reflection->getProperty('file');
+ $property->setAccessible(true);
+ $property->setValue($response, $file);
+
+ $response->prepare($request);
+ $this->assertEquals($virtual, $response->headers->get('X-Accel-Redirect'));
+ }
+
+ public function testDeleteFileAfterSend()
+ {
+ $request = Request::create('/');
+
+ $path = __DIR__.'/File/Fixtures/to_delete';
+ touch($path);
+ $realPath = realpath($path);
+ $this->assertFileExists($realPath);
+
+ $response = new BinaryFileResponse($realPath, 200, array('Content-Type' => 'application/octet-stream'));
+ $response->deleteFileAfterSend(true);
+
+ $response->prepare($request);
+ $response->sendContent();
+
+ $this->assertFileNotExists($path);
+ }
+
+ public function testAcceptRangeOnUnsafeMethods()
+ {
+ $request = Request::create('/', 'POST');
+ $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
+ $response->prepare($request);
+
+ $this->assertEquals('none', $response->headers->get('Accept-Ranges'));
+ }
+
+ public function testAcceptRangeNotOverriden()
+ {
+ $request = Request::create('/', 'POST');
+ $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
+ $response->headers->set('Accept-Ranges', 'foo');
+ $response->prepare($request);
+
+ $this->assertEquals('foo', $response->headers->get('Accept-Ranges'));
+ }
+
+ public function getSampleXAccelMappings()
+ {
+ return array(
+ array('/var/www/var/www/files/foo.txt', '/var/www/=/files/', '/files/var/www/files/foo.txt'),
+ array('/home/foo/bar.txt', '/var/www/=/files/,/home/foo/=/baz/', '/baz/bar.txt'),
+ );
+ }
+
+ public function testStream()
+ {
+ $request = Request::create('/');
+ $response = new BinaryFileResponse(new Stream(__DIR__.'/../README.md'), 200, array('Content-Type' => 'text/plain'));
+ $response->prepare($request);
+
+ $this->assertNull($response->headers->get('Content-Length'));
+ }
+
+ protected function provideResponse()
+ {
+ return new BinaryFileResponse(__DIR__.'/../README.md', 200, array('Content-Type' => 'application/octet-stream'));
+ }
+
+ public static function tearDownAfterClass()
+ {
+ $path = __DIR__.'/../Fixtures/to_delete';
+ if (file_exists($path)) {
+ @unlink($path);
+ }
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/CookieTest.php b/assets/php/vendor/symfony/http-foundation/Tests/CookieTest.php
new file mode 100644
index 0000000..070b7dd
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/CookieTest.php
@@ -0,0 +1,223 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Cookie;
+
+/**
+ * CookieTest.
+ *
+ * @author John Kary <john@johnkary.net>
+ * @author Hugo Hamon <hugo.hamon@sensio.com>
+ *
+ * @group time-sensitive
+ */
+class CookieTest extends TestCase
+{
+ public function invalidNames()
+ {
+ return array(
+ array(''),
+ array(',MyName'),
+ array(';MyName'),
+ array(' MyName'),
+ array("\tMyName"),
+ array("\rMyName"),
+ array("\nMyName"),
+ array("\013MyName"),
+ array("\014MyName"),
+ );
+ }
+
+ /**
+ * @dataProvider invalidNames
+ * @expectedException \InvalidArgumentException
+ */
+ public function testInstantiationThrowsExceptionIfCookieNameContainsInvalidCharacters($name)
+ {
+ new Cookie($name);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testInvalidExpiration()
+ {
+ new Cookie('MyCookie', 'foo', 'bar');
+ }
+
+ public function testNegativeExpirationIsNotPossible()
+ {
+ $cookie = new Cookie('foo', 'bar', -100);
+
+ $this->assertSame(0, $cookie->getExpiresTime());
+ }
+
+ public function testGetValue()
+ {
+ $value = 'MyValue';
+ $cookie = new Cookie('MyCookie', $value);
+
+ $this->assertSame($value, $cookie->getValue(), '->getValue() returns the proper value');
+ }
+
+ public function testGetPath()
+ {
+ $cookie = new Cookie('foo', 'bar');
+
+ $this->assertSame('/', $cookie->getPath(), '->getPath() returns / as the default path');
+ }
+
+ public function testGetExpiresTime()
+ {
+ $cookie = new Cookie('foo', 'bar');
+
+ $this->assertEquals(0, $cookie->getExpiresTime(), '->getExpiresTime() returns the default expire date');
+
+ $cookie = new Cookie('foo', 'bar', $expire = time() + 3600);
+
+ $this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
+ }
+
+ public function testGetExpiresTimeIsCastToInt()
+ {
+ $cookie = new Cookie('foo', 'bar', 3600.9);
+
+ $this->assertSame(3600, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date as an integer');
+ }
+
+ public function testConstructorWithDateTime()
+ {
+ $expire = new \DateTime();
+ $cookie = new Cookie('foo', 'bar', $expire);
+
+ $this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
+ }
+
+ /**
+ * @requires PHP 5.5
+ */
+ public function testConstructorWithDateTimeImmutable()
+ {
+ $expire = new \DateTimeImmutable();
+ $cookie = new Cookie('foo', 'bar', $expire);
+
+ $this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
+ }
+
+ public function testGetExpiresTimeWithStringValue()
+ {
+ $value = '+1 day';
+ $cookie = new Cookie('foo', 'bar', $value);
+ $expire = strtotime($value);
+
+ $this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date', 1);
+ }
+
+ public function testGetDomain()
+ {
+ $cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com');
+
+ $this->assertEquals('.myfoodomain.com', $cookie->getDomain(), '->getDomain() returns the domain name on which the cookie is valid');
+ }
+
+ public function testIsSecure()
+ {
+ $cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com', true);
+
+ $this->assertTrue($cookie->isSecure(), '->isSecure() returns whether the cookie is transmitted over HTTPS');
+ }
+
+ public function testIsHttpOnly()
+ {
+ $cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com', false, true);
+
+ $this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns whether the cookie is only transmitted over HTTP');
+ }
+
+ public function testCookieIsNotCleared()
+ {
+ $cookie = new Cookie('foo', 'bar', time() + 3600 * 24);
+
+ $this->assertFalse($cookie->isCleared(), '->isCleared() returns false if the cookie did not expire yet');
+ }
+
+ public function testCookieIsCleared()
+ {
+ $cookie = new Cookie('foo', 'bar', time() - 20);
+
+ $this->assertTrue($cookie->isCleared(), '->isCleared() returns true if the cookie has expired');
+ }
+
+ public function testToString()
+ {
+ $cookie = new Cookie('foo', 'bar', $expire = strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true);
+ $this->assertEquals('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; max-age='.($expire - time()).'; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() returns string representation of the cookie');
+
+ $cookie = new Cookie('foo', 'bar with white spaces', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true);
+ $this->assertEquals('foo=bar%20with%20white%20spaces; expires=Fri, 20-May-2011 15:25:52 GMT; max-age='.($expire - time()).'; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)');
+
+ $cookie = new Cookie('foo', null, 1, '/admin/', '.myfoodomain.com');
+ $this->assertEquals('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', $expire = time() - 31536001).'; max-age='.($expire - time()).'; path=/admin/; domain=.myfoodomain.com; httponly', (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL');
+
+ $cookie = new Cookie('foo', 'bar', 0, '/', '');
+ $this->assertEquals('foo=bar; path=/; httponly', (string) $cookie);
+ }
+
+ public function testRawCookie()
+ {
+ $cookie = new Cookie('foo', 'b a r', 0, '/', null, false, false);
+ $this->assertFalse($cookie->isRaw());
+ $this->assertEquals('foo=b%20a%20r; path=/', (string) $cookie);
+
+ $cookie = new Cookie('foo', 'b+a+r', 0, '/', null, false, false, true);
+ $this->assertTrue($cookie->isRaw());
+ $this->assertEquals('foo=b+a+r; path=/', (string) $cookie);
+ }
+
+ public function testGetMaxAge()
+ {
+ $cookie = new Cookie('foo', 'bar');
+ $this->assertEquals(0, $cookie->getMaxAge());
+
+ $cookie = new Cookie('foo', 'bar', $expire = time() + 100);
+ $this->assertEquals($expire - time(), $cookie->getMaxAge());
+
+ $cookie = new Cookie('foo', 'bar', $expire = time() - 100);
+ $this->assertEquals($expire - time(), $cookie->getMaxAge());
+ }
+
+ public function testFromString()
+ {
+ $cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly');
+ $this->assertEquals(new Cookie('foo', 'bar', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true, true, true), $cookie);
+
+ $cookie = Cookie::fromString('foo=bar', true);
+ $this->assertEquals(new Cookie('foo', 'bar', 0, '/', null, false, false), $cookie);
+ }
+
+ public function testFromStringWithHttpOnly()
+ {
+ $cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly');
+ $this->assertTrue($cookie->isHttpOnly());
+
+ $cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure');
+ $this->assertFalse($cookie->isHttpOnly());
+ }
+
+ public function testSameSiteAttributeIsCaseInsensitive()
+ {
+ $cookie = new Cookie('foo', 'bar', 0, '/', null, false, true, false, 'Lax');
+ $this->assertEquals('lax', $cookie->getSameSite());
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php b/assets/php/vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php
new file mode 100644
index 0000000..1152e46
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php
@@ -0,0 +1,69 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
+use Symfony\Component\HttpFoundation\ExpressionRequestMatcher;
+use Symfony\Component\HttpFoundation\Request;
+
+class ExpressionRequestMatcherTest extends TestCase
+{
+ /**
+ * @expectedException \LogicException
+ */
+ public function testWhenNoExpressionIsSet()
+ {
+ $expressionRequestMatcher = new ExpressionRequestMatcher();
+ $expressionRequestMatcher->matches(new Request());
+ }
+
+ /**
+ * @dataProvider provideExpressions
+ */
+ public function testMatchesWhenParentMatchesIsTrue($expression, $expected)
+ {
+ $request = Request::create('/foo');
+ $expressionRequestMatcher = new ExpressionRequestMatcher();
+
+ $expressionRequestMatcher->setExpression(new ExpressionLanguage(), $expression);
+ $this->assertSame($expected, $expressionRequestMatcher->matches($request));
+ }
+
+ /**
+ * @dataProvider provideExpressions
+ */
+ public function testMatchesWhenParentMatchesIsFalse($expression)
+ {
+ $request = Request::create('/foo');
+ $request->attributes->set('foo', 'foo');
+ $expressionRequestMatcher = new ExpressionRequestMatcher();
+ $expressionRequestMatcher->matchAttribute('foo', 'bar');
+
+ $expressionRequestMatcher->setExpression(new ExpressionLanguage(), $expression);
+ $this->assertFalse($expressionRequestMatcher->matches($request));
+ }
+
+ public function provideExpressions()
+ {
+ return array(
+ array('request.getMethod() == method', true),
+ array('request.getPathInfo() == path', true),
+ array('request.getHost() == host', true),
+ array('request.getClientIp() == ip', true),
+ array('request.attributes.all() == attributes', true),
+ array('request.getMethod() == method && request.getPathInfo() == path && request.getHost() == host && request.getClientIp() == ip && request.attributes.all() == attributes', true),
+ array('request.getMethod() != method', false),
+ array('request.getMethod() != method && request.getPathInfo() == path && request.getHost() == host && request.getClientIp() == ip && request.attributes.all() == attributes', false),
+ );
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/File/FakeFile.php b/assets/php/vendor/symfony/http-foundation/Tests/File/FakeFile.php
new file mode 100644
index 0000000..c415989
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/File/FakeFile.php
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\File;
+
+use Symfony\Component\HttpFoundation\File\File as OrigFile;
+
+class FakeFile extends OrigFile
+{
+ private $realpath;
+
+ public function __construct($realpath, $path)
+ {
+ $this->realpath = $realpath;
+ parent::__construct($path, false);
+ }
+
+ public function isReadable()
+ {
+ return true;
+ }
+
+ public function getRealpath()
+ {
+ return $this->realpath;
+ }
+
+ public function getSize()
+ {
+ return 42;
+ }
+
+ public function getMTime()
+ {
+ return time();
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/File/FileTest.php b/assets/php/vendor/symfony/http-foundation/Tests/File/FileTest.php
new file mode 100644
index 0000000..dbd9c44
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/File/FileTest.php
@@ -0,0 +1,180 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\File;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\File\File;
+use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
+
+class FileTest extends TestCase
+{
+ protected $file;
+
+ public function testGetMimeTypeUsesMimeTypeGuessers()
+ {
+ $file = new File(__DIR__.'/Fixtures/test.gif');
+ $guesser = $this->createMockGuesser($file->getPathname(), 'image/gif');
+
+ MimeTypeGuesser::getInstance()->register($guesser);
+
+ $this->assertEquals('image/gif', $file->getMimeType());
+ }
+
+ public function testGuessExtensionWithoutGuesser()
+ {
+ $file = new File(__DIR__.'/Fixtures/directory/.empty');
+
+ $this->assertNull($file->guessExtension());
+ }
+
+ public function testGuessExtensionIsBasedOnMimeType()
+ {
+ $file = new File(__DIR__.'/Fixtures/test');
+ $guesser = $this->createMockGuesser($file->getPathname(), 'image/gif');
+
+ MimeTypeGuesser::getInstance()->register($guesser);
+
+ $this->assertEquals('gif', $file->guessExtension());
+ }
+
+ /**
+ * @requires extension fileinfo
+ */
+ public function testGuessExtensionWithReset()
+ {
+ $file = new File(__DIR__.'/Fixtures/other-file.example');
+ $guesser = $this->createMockGuesser($file->getPathname(), 'image/gif');
+ MimeTypeGuesser::getInstance()->register($guesser);
+
+ $this->assertEquals('gif', $file->guessExtension());
+
+ MimeTypeGuesser::reset();
+
+ $this->assertNull($file->guessExtension());
+ }
+
+ public function testConstructWhenFileNotExists()
+ {
+ $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
+
+ new File(__DIR__.'/Fixtures/not_here');
+ }
+
+ public function testMove()
+ {
+ $path = __DIR__.'/Fixtures/test.copy.gif';
+ $targetDir = __DIR__.'/Fixtures/directory';
+ $targetPath = $targetDir.'/test.copy.gif';
+ @unlink($path);
+ @unlink($targetPath);
+ copy(__DIR__.'/Fixtures/test.gif', $path);
+
+ $file = new File($path);
+ $movedFile = $file->move($targetDir);
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\File\File', $movedFile);
+
+ $this->assertFileExists($targetPath);
+ $this->assertFileNotExists($path);
+ $this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
+
+ @unlink($targetPath);
+ }
+
+ public function testMoveWithNewName()
+ {
+ $path = __DIR__.'/Fixtures/test.copy.gif';
+ $targetDir = __DIR__.'/Fixtures/directory';
+ $targetPath = $targetDir.'/test.newname.gif';
+ @unlink($path);
+ @unlink($targetPath);
+ copy(__DIR__.'/Fixtures/test.gif', $path);
+
+ $file = new File($path);
+ $movedFile = $file->move($targetDir, 'test.newname.gif');
+
+ $this->assertFileExists($targetPath);
+ $this->assertFileNotExists($path);
+ $this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
+
+ @unlink($targetPath);
+ }
+
+ public function getFilenameFixtures()
+ {
+ return array(
+ array('original.gif', 'original.gif'),
+ array('..\\..\\original.gif', 'original.gif'),
+ array('../../original.gif', 'original.gif'),
+ array('файлfile.gif', 'файлfile.gif'),
+ array('..\\..\\файлfile.gif', 'файлfile.gif'),
+ array('../../файлfile.gif', 'файлfile.gif'),
+ );
+ }
+
+ /**
+ * @dataProvider getFilenameFixtures
+ */
+ public function testMoveWithNonLatinName($filename, $sanitizedFilename)
+ {
+ $path = __DIR__.'/Fixtures/'.$sanitizedFilename;
+ $targetDir = __DIR__.'/Fixtures/directory/';
+ $targetPath = $targetDir.$sanitizedFilename;
+ @unlink($path);
+ @unlink($targetPath);
+ copy(__DIR__.'/Fixtures/test.gif', $path);
+
+ $file = new File($path);
+ $movedFile = $file->move($targetDir, $filename);
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\File\File', $movedFile);
+
+ $this->assertFileExists($targetPath);
+ $this->assertFileNotExists($path);
+ $this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
+
+ @unlink($targetPath);
+ }
+
+ public function testMoveToAnUnexistentDirectory()
+ {
+ $sourcePath = __DIR__.'/Fixtures/test.copy.gif';
+ $targetDir = __DIR__.'/Fixtures/directory/sub';
+ $targetPath = $targetDir.'/test.copy.gif';
+ @unlink($sourcePath);
+ @unlink($targetPath);
+ @rmdir($targetDir);
+ copy(__DIR__.'/Fixtures/test.gif', $sourcePath);
+
+ $file = new File($sourcePath);
+ $movedFile = $file->move($targetDir);
+
+ $this->assertFileExists($targetPath);
+ $this->assertFileNotExists($sourcePath);
+ $this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
+
+ @unlink($sourcePath);
+ @unlink($targetPath);
+ @rmdir($targetDir);
+ }
+
+ protected function createMockGuesser($path, $mimeType)
+ {
+ $guesser = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface')->getMock();
+ $guesser
+ ->expects($this->once())
+ ->method('guess')
+ ->with($this->equalTo($path))
+ ->will($this->returnValue($mimeType))
+ ;
+
+ return $guesser;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension b/assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension
new file mode 100644
index 0000000..4d1ae35
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension
@@ -0,0 +1 @@
+f \ No newline at end of file
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/directory/.empty b/assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/directory/.empty
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/directory/.empty
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/other-file.example b/assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/other-file.example
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/other-file.example
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/test b/assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/test
new file mode 100644
index 0000000..b636f4b
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/test
Binary files differ
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/test.gif b/assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/test.gif
new file mode 100644
index 0000000..b636f4b
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/File/Fixtures/test.gif
Binary files differ
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php b/assets/php/vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php
new file mode 100644
index 0000000..b3f1f02
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php
@@ -0,0 +1,90 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\File\MimeType;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
+use Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser;
+
+/**
+ * @requires extension fileinfo
+ */
+class MimeTypeTest extends TestCase
+{
+ protected $path;
+
+ public function testGuessImageWithoutExtension()
+ {
+ $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test'));
+ }
+
+ public function testGuessImageWithDirectory()
+ {
+ $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
+
+ MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/directory');
+ }
+
+ public function testGuessImageWithFileBinaryMimeTypeGuesser()
+ {
+ $guesser = MimeTypeGuesser::getInstance();
+ $guesser->register(new FileBinaryMimeTypeGuesser());
+ $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test'));
+ }
+
+ public function testGuessImageWithKnownExtension()
+ {
+ $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test.gif'));
+ }
+
+ public function testGuessFileWithUnknownExtension()
+ {
+ $this->assertEquals('application/octet-stream', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/.unknownextension'));
+ }
+
+ public function testGuessWithIncorrectPath()
+ {
+ $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
+ MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/not_here');
+ }
+
+ public function testGuessWithNonReadablePath()
+ {
+ if ('\\' === DIRECTORY_SEPARATOR) {
+ $this->markTestSkipped('Can not verify chmod operations on Windows');
+ }
+
+ if (!getenv('USER') || 'root' === getenv('USER')) {
+ $this->markTestSkipped('This test will fail if run under superuser');
+ }
+
+ $path = __DIR__.'/../Fixtures/to_delete';
+ touch($path);
+ @chmod($path, 0333);
+
+ if ('0333' == substr(sprintf('%o', fileperms($path)), -4)) {
+ $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException');
+ MimeTypeGuesser::getInstance()->guess($path);
+ } else {
+ $this->markTestSkipped('Can not verify chmod operations, change of file permissions failed');
+ }
+ }
+
+ public static function tearDownAfterClass()
+ {
+ $path = __DIR__.'/../Fixtures/to_delete';
+ if (file_exists($path)) {
+ @chmod($path, 0666);
+ @unlink($path);
+ }
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php b/assets/php/vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php
new file mode 100644
index 0000000..36f122f
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php
@@ -0,0 +1,273 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\File;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+
+class UploadedFileTest extends TestCase
+{
+ protected function setUp()
+ {
+ if (!ini_get('file_uploads')) {
+ $this->markTestSkipped('file_uploads is disabled in php.ini');
+ }
+ }
+
+ public function testConstructWhenFileNotExists()
+ {
+ $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
+
+ new UploadedFile(
+ __DIR__.'/Fixtures/not_here',
+ 'original.gif',
+ null
+ );
+ }
+
+ public function testFileUploadsWithNoMimeType()
+ {
+ $file = new UploadedFile(
+ __DIR__.'/Fixtures/test.gif',
+ 'original.gif',
+ null,
+ filesize(__DIR__.'/Fixtures/test.gif'),
+ UPLOAD_ERR_OK
+ );
+
+ $this->assertEquals('application/octet-stream', $file->getClientMimeType());
+
+ if (extension_loaded('fileinfo')) {
+ $this->assertEquals('image/gif', $file->getMimeType());
+ }
+ }
+
+ public function testFileUploadsWithUnknownMimeType()
+ {
+ $file = new UploadedFile(
+ __DIR__.'/Fixtures/.unknownextension',
+ 'original.gif',
+ null,
+ filesize(__DIR__.'/Fixtures/.unknownextension'),
+ UPLOAD_ERR_OK
+ );
+
+ $this->assertEquals('application/octet-stream', $file->getClientMimeType());
+ }
+
+ public function testGuessClientExtension()
+ {
+ $file = new UploadedFile(
+ __DIR__.'/Fixtures/test.gif',
+ 'original.gif',
+ 'image/gif',
+ filesize(__DIR__.'/Fixtures/test.gif'),
+ null
+ );
+
+ $this->assertEquals('gif', $file->guessClientExtension());
+ }
+
+ public function testGuessClientExtensionWithIncorrectMimeType()
+ {
+ $file = new UploadedFile(
+ __DIR__.'/Fixtures/test.gif',
+ 'original.gif',
+ 'image/jpeg',
+ filesize(__DIR__.'/Fixtures/test.gif'),
+ null
+ );
+
+ $this->assertEquals('jpeg', $file->guessClientExtension());
+ }
+
+ public function testErrorIsOkByDefault()
+ {
+ $file = new UploadedFile(
+ __DIR__.'/Fixtures/test.gif',
+ 'original.gif',
+ 'image/gif',
+ filesize(__DIR__.'/Fixtures/test.gif'),
+ null
+ );
+
+ $this->assertEquals(UPLOAD_ERR_OK, $file->getError());
+ }
+
+ public function testGetClientOriginalName()
+ {
+ $file = new UploadedFile(
+ __DIR__.'/Fixtures/test.gif',
+ 'original.gif',
+ 'image/gif',
+ filesize(__DIR__.'/Fixtures/test.gif'),
+ null
+ );
+
+ $this->assertEquals('original.gif', $file->getClientOriginalName());
+ }
+
+ public function testGetClientOriginalExtension()
+ {
+ $file = new UploadedFile(
+ __DIR__.'/Fixtures/test.gif',
+ 'original.gif',
+ 'image/gif',
+ filesize(__DIR__.'/Fixtures/test.gif'),
+ null
+ );
+
+ $this->assertEquals('gif', $file->getClientOriginalExtension());
+ }
+
+ /**
+ * @expectedException \Symfony\Component\HttpFoundation\File\Exception\FileException
+ */
+ public function testMoveLocalFileIsNotAllowed()
+ {
+ $file = new UploadedFile(
+ __DIR__.'/Fixtures/test.gif',
+ 'original.gif',
+ 'image/gif',
+ filesize(__DIR__.'/Fixtures/test.gif'),
+ UPLOAD_ERR_OK
+ );
+
+ $movedFile = $file->move(__DIR__.'/Fixtures/directory');
+ }
+
+ public function testMoveLocalFileIsAllowedInTestMode()
+ {
+ $path = __DIR__.'/Fixtures/test.copy.gif';
+ $targetDir = __DIR__.'/Fixtures/directory';
+ $targetPath = $targetDir.'/test.copy.gif';
+ @unlink($path);
+ @unlink($targetPath);
+ copy(__DIR__.'/Fixtures/test.gif', $path);
+
+ $file = new UploadedFile(
+ $path,
+ 'original.gif',
+ 'image/gif',
+ filesize($path),
+ UPLOAD_ERR_OK,
+ true
+ );
+
+ $movedFile = $file->move(__DIR__.'/Fixtures/directory');
+
+ $this->assertFileExists($targetPath);
+ $this->assertFileNotExists($path);
+ $this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
+
+ @unlink($targetPath);
+ }
+
+ public function testGetClientOriginalNameSanitizeFilename()
+ {
+ $file = new UploadedFile(
+ __DIR__.'/Fixtures/test.gif',
+ '../../original.gif',
+ 'image/gif',
+ filesize(__DIR__.'/Fixtures/test.gif'),
+ null
+ );
+
+ $this->assertEquals('original.gif', $file->getClientOriginalName());
+ }
+
+ public function testGetSize()
+ {
+ $file = new UploadedFile(
+ __DIR__.'/Fixtures/test.gif',
+ 'original.gif',
+ 'image/gif',
+ filesize(__DIR__.'/Fixtures/test.gif'),
+ null
+ );
+
+ $this->assertEquals(filesize(__DIR__.'/Fixtures/test.gif'), $file->getSize());
+
+ $file = new UploadedFile(
+ __DIR__.'/Fixtures/test',
+ 'original.gif',
+ 'image/gif'
+ );
+
+ $this->assertEquals(filesize(__DIR__.'/Fixtures/test'), $file->getSize());
+ }
+
+ public function testGetExtension()
+ {
+ $file = new UploadedFile(
+ __DIR__.'/Fixtures/test.gif',
+ 'original.gif',
+ null
+ );
+
+ $this->assertEquals('gif', $file->getExtension());
+ }
+
+ public function testIsValid()
+ {
+ $file = new UploadedFile(
+ __DIR__.'/Fixtures/test.gif',
+ 'original.gif',
+ null,
+ filesize(__DIR__.'/Fixtures/test.gif'),
+ UPLOAD_ERR_OK,
+ true
+ );
+
+ $this->assertTrue($file->isValid());
+ }
+
+ /**
+ * @dataProvider uploadedFileErrorProvider
+ */
+ public function testIsInvalidOnUploadError($error)
+ {
+ $file = new UploadedFile(
+ __DIR__.'/Fixtures/test.gif',
+ 'original.gif',
+ null,
+ filesize(__DIR__.'/Fixtures/test.gif'),
+ $error
+ );
+
+ $this->assertFalse($file->isValid());
+ }
+
+ public function uploadedFileErrorProvider()
+ {
+ return array(
+ array(UPLOAD_ERR_INI_SIZE),
+ array(UPLOAD_ERR_FORM_SIZE),
+ array(UPLOAD_ERR_PARTIAL),
+ array(UPLOAD_ERR_NO_TMP_DIR),
+ array(UPLOAD_ERR_EXTENSION),
+ );
+ }
+
+ public function testIsInvalidIfNotHttpUpload()
+ {
+ $file = new UploadedFile(
+ __DIR__.'/Fixtures/test.gif',
+ 'original.gif',
+ null,
+ filesize(__DIR__.'/Fixtures/test.gif'),
+ UPLOAD_ERR_OK
+ );
+
+ $this->assertFalse($file->isValid());
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/FileBagTest.php b/assets/php/vendor/symfony/http-foundation/Tests/FileBagTest.php
new file mode 100644
index 0000000..b1bbba0
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/FileBagTest.php
@@ -0,0 +1,175 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+use Symfony\Component\HttpFoundation\FileBag;
+
+/**
+ * FileBagTest.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
+ */
+class FileBagTest extends TestCase
+{
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testFileMustBeAnArrayOrUploadedFile()
+ {
+ new FileBag(array('file' => 'foo'));
+ }
+
+ public function testShouldConvertsUploadedFiles()
+ {
+ $tmpFile = $this->createTempFile();
+ $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
+
+ $bag = new FileBag(array('file' => array(
+ 'name' => basename($tmpFile),
+ 'type' => 'text/plain',
+ 'tmp_name' => $tmpFile,
+ 'error' => 0,
+ 'size' => 100,
+ )));
+
+ $this->assertEquals($file, $bag->get('file'));
+ }
+
+ public function testShouldSetEmptyUploadedFilesToNull()
+ {
+ $bag = new FileBag(array('file' => array(
+ 'name' => '',
+ 'type' => '',
+ 'tmp_name' => '',
+ 'error' => UPLOAD_ERR_NO_FILE,
+ 'size' => 0,
+ )));
+
+ $this->assertNull($bag->get('file'));
+ }
+
+ public function testShouldRemoveEmptyUploadedFilesForMultiUpload()
+ {
+ $bag = new FileBag(array('files' => array(
+ 'name' => array(''),
+ 'type' => array(''),
+ 'tmp_name' => array(''),
+ 'error' => array(UPLOAD_ERR_NO_FILE),
+ 'size' => array(0),
+ )));
+
+ $this->assertSame(array(), $bag->get('files'));
+ }
+
+ public function testShouldNotRemoveEmptyUploadedFilesForAssociativeArray()
+ {
+ $bag = new FileBag(array('files' => array(
+ 'name' => array('file1' => ''),
+ 'type' => array('file1' => ''),
+ 'tmp_name' => array('file1' => ''),
+ 'error' => array('file1' => UPLOAD_ERR_NO_FILE),
+ 'size' => array('file1' => 0),
+ )));
+
+ $this->assertSame(array('file1' => null), $bag->get('files'));
+ }
+
+ public function testShouldConvertUploadedFilesWithPhpBug()
+ {
+ $tmpFile = $this->createTempFile();
+ $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
+
+ $bag = new FileBag(array(
+ 'child' => array(
+ 'name' => array(
+ 'file' => basename($tmpFile),
+ ),
+ 'type' => array(
+ 'file' => 'text/plain',
+ ),
+ 'tmp_name' => array(
+ 'file' => $tmpFile,
+ ),
+ 'error' => array(
+ 'file' => 0,
+ ),
+ 'size' => array(
+ 'file' => 100,
+ ),
+ ),
+ ));
+
+ $files = $bag->all();
+ $this->assertEquals($file, $files['child']['file']);
+ }
+
+ public function testShouldConvertNestedUploadedFilesWithPhpBug()
+ {
+ $tmpFile = $this->createTempFile();
+ $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
+
+ $bag = new FileBag(array(
+ 'child' => array(
+ 'name' => array(
+ 'sub' => array('file' => basename($tmpFile)),
+ ),
+ 'type' => array(
+ 'sub' => array('file' => 'text/plain'),
+ ),
+ 'tmp_name' => array(
+ 'sub' => array('file' => $tmpFile),
+ ),
+ 'error' => array(
+ 'sub' => array('file' => 0),
+ ),
+ 'size' => array(
+ 'sub' => array('file' => 100),
+ ),
+ ),
+ ));
+
+ $files = $bag->all();
+ $this->assertEquals($file, $files['child']['sub']['file']);
+ }
+
+ public function testShouldNotConvertNestedUploadedFiles()
+ {
+ $tmpFile = $this->createTempFile();
+ $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
+ $bag = new FileBag(array('image' => array('file' => $file)));
+
+ $files = $bag->all();
+ $this->assertEquals($file, $files['image']['file']);
+ }
+
+ protected function createTempFile()
+ {
+ return tempnam(sys_get_temp_dir().'/form_test', 'FormTest');
+ }
+
+ protected function setUp()
+ {
+ mkdir(sys_get_temp_dir().'/form_test', 0777, true);
+ }
+
+ protected function tearDown()
+ {
+ foreach (glob(sys_get_temp_dir().'/form_test/*') as $file) {
+ unlink($file);
+ }
+
+ rmdir(sys_get_temp_dir().'/form_test');
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/HeaderBagTest.php b/assets/php/vendor/symfony/http-foundation/Tests/HeaderBagTest.php
new file mode 100644
index 0000000..6d19ceb
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/HeaderBagTest.php
@@ -0,0 +1,205 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\HeaderBag;
+
+class HeaderBagTest extends TestCase
+{
+ public function testConstructor()
+ {
+ $bag = new HeaderBag(array('foo' => 'bar'));
+ $this->assertTrue($bag->has('foo'));
+ }
+
+ public function testToStringNull()
+ {
+ $bag = new HeaderBag();
+ $this->assertEquals('', $bag->__toString());
+ }
+
+ public function testToStringNotNull()
+ {
+ $bag = new HeaderBag(array('foo' => 'bar'));
+ $this->assertEquals("Foo: bar\r\n", $bag->__toString());
+ }
+
+ public function testKeys()
+ {
+ $bag = new HeaderBag(array('foo' => 'bar'));
+ $keys = $bag->keys();
+ $this->assertEquals('foo', $keys[0]);
+ }
+
+ public function testGetDate()
+ {
+ $bag = new HeaderBag(array('foo' => 'Tue, 4 Sep 2012 20:00:00 +0200'));
+ $headerDate = $bag->getDate('foo');
+ $this->assertInstanceOf('DateTime', $headerDate);
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function testGetDateException()
+ {
+ $bag = new HeaderBag(array('foo' => 'Tue'));
+ $headerDate = $bag->getDate('foo');
+ }
+
+ public function testGetCacheControlHeader()
+ {
+ $bag = new HeaderBag();
+ $bag->addCacheControlDirective('public', '#a');
+ $this->assertTrue($bag->hasCacheControlDirective('public'));
+ $this->assertEquals('#a', $bag->getCacheControlDirective('public'));
+ }
+
+ public function testAll()
+ {
+ $bag = new HeaderBag(array('foo' => 'bar'));
+ $this->assertEquals(array('foo' => array('bar')), $bag->all(), '->all() gets all the input');
+
+ $bag = new HeaderBag(array('FOO' => 'BAR'));
+ $this->assertEquals(array('foo' => array('BAR')), $bag->all(), '->all() gets all the input key are lower case');
+ }
+
+ public function testReplace()
+ {
+ $bag = new HeaderBag(array('foo' => 'bar'));
+
+ $bag->replace(array('NOPE' => 'BAR'));
+ $this->assertEquals(array('nope' => array('BAR')), $bag->all(), '->replace() replaces the input with the argument');
+ $this->assertFalse($bag->has('foo'), '->replace() overrides previously set the input');
+ }
+
+ public function testGet()
+ {
+ $bag = new HeaderBag(array('foo' => 'bar', 'fuzz' => 'bizz'));
+ $this->assertEquals('bar', $bag->get('foo'), '->get return current value');
+ $this->assertEquals('bar', $bag->get('FoO'), '->get key in case insensitive');
+ $this->assertEquals(array('bar'), $bag->get('foo', 'nope', false), '->get return the value as array');
+
+ // defaults
+ $this->assertNull($bag->get('none'), '->get unknown values returns null');
+ $this->assertEquals('default', $bag->get('none', 'default'), '->get unknown values returns default');
+ $this->assertEquals(array('default'), $bag->get('none', 'default', false), '->get unknown values returns default as array');
+
+ $bag->set('foo', 'bor', false);
+ $this->assertEquals('bar', $bag->get('foo'), '->get return first value');
+ $this->assertEquals(array('bar', 'bor'), $bag->get('foo', 'nope', false), '->get return all values as array');
+ }
+
+ public function testSetAssociativeArray()
+ {
+ $bag = new HeaderBag();
+ $bag->set('foo', array('bad-assoc-index' => 'value'));
+ $this->assertSame('value', $bag->get('foo'));
+ $this->assertEquals(array('value'), $bag->get('foo', 'nope', false), 'assoc indices of multi-valued headers are ignored');
+ }
+
+ public function testContains()
+ {
+ $bag = new HeaderBag(array('foo' => 'bar', 'fuzz' => 'bizz'));
+ $this->assertTrue($bag->contains('foo', 'bar'), '->contains first value');
+ $this->assertTrue($bag->contains('fuzz', 'bizz'), '->contains second value');
+ $this->assertFalse($bag->contains('nope', 'nope'), '->contains unknown value');
+ $this->assertFalse($bag->contains('foo', 'nope'), '->contains unknown value');
+
+ // Multiple values
+ $bag->set('foo', 'bor', false);
+ $this->assertTrue($bag->contains('foo', 'bar'), '->contains first value');
+ $this->assertTrue($bag->contains('foo', 'bor'), '->contains second value');
+ $this->assertFalse($bag->contains('foo', 'nope'), '->contains unknown value');
+ }
+
+ public function testCacheControlDirectiveAccessors()
+ {
+ $bag = new HeaderBag();
+ $bag->addCacheControlDirective('public');
+
+ $this->assertTrue($bag->hasCacheControlDirective('public'));
+ $this->assertTrue($bag->getCacheControlDirective('public'));
+ $this->assertEquals('public', $bag->get('cache-control'));
+
+ $bag->addCacheControlDirective('max-age', 10);
+ $this->assertTrue($bag->hasCacheControlDirective('max-age'));
+ $this->assertEquals(10, $bag->getCacheControlDirective('max-age'));
+ $this->assertEquals('max-age=10, public', $bag->get('cache-control'));
+
+ $bag->removeCacheControlDirective('max-age');
+ $this->assertFalse($bag->hasCacheControlDirective('max-age'));
+ }
+
+ public function testCacheControlDirectiveParsing()
+ {
+ $bag = new HeaderBag(array('cache-control' => 'public, max-age=10'));
+ $this->assertTrue($bag->hasCacheControlDirective('public'));
+ $this->assertTrue($bag->getCacheControlDirective('public'));
+
+ $this->assertTrue($bag->hasCacheControlDirective('max-age'));
+ $this->assertEquals(10, $bag->getCacheControlDirective('max-age'));
+
+ $bag->addCacheControlDirective('s-maxage', 100);
+ $this->assertEquals('max-age=10, public, s-maxage=100', $bag->get('cache-control'));
+ }
+
+ public function testCacheControlDirectiveParsingQuotedZero()
+ {
+ $bag = new HeaderBag(array('cache-control' => 'max-age="0"'));
+ $this->assertTrue($bag->hasCacheControlDirective('max-age'));
+ $this->assertEquals(0, $bag->getCacheControlDirective('max-age'));
+ }
+
+ public function testCacheControlDirectiveOverrideWithReplace()
+ {
+ $bag = new HeaderBag(array('cache-control' => 'private, max-age=100'));
+ $bag->replace(array('cache-control' => 'public, max-age=10'));
+ $this->assertTrue($bag->hasCacheControlDirective('public'));
+ $this->assertTrue($bag->getCacheControlDirective('public'));
+
+ $this->assertTrue($bag->hasCacheControlDirective('max-age'));
+ $this->assertEquals(10, $bag->getCacheControlDirective('max-age'));
+ }
+
+ public function testCacheControlClone()
+ {
+ $headers = array('foo' => 'bar');
+ $bag1 = new HeaderBag($headers);
+ $bag2 = new HeaderBag($bag1->all());
+
+ $this->assertEquals($bag1->all(), $bag2->all());
+ }
+
+ public function testGetIterator()
+ {
+ $headers = array('foo' => 'bar', 'hello' => 'world', 'third' => 'charm');
+ $headerBag = new HeaderBag($headers);
+
+ $i = 0;
+ foreach ($headerBag as $key => $val) {
+ ++$i;
+ $this->assertEquals(array($headers[$key]), $val);
+ }
+
+ $this->assertEquals(count($headers), $i);
+ }
+
+ public function testCount()
+ {
+ $headers = array('foo' => 'bar', 'HELLO' => 'WORLD');
+ $headerBag = new HeaderBag($headers);
+
+ $this->assertCount(count($headers), $headerBag);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/IpUtilsTest.php b/assets/php/vendor/symfony/http-foundation/Tests/IpUtilsTest.php
new file mode 100644
index 0000000..7a93f99
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/IpUtilsTest.php
@@ -0,0 +1,104 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\IpUtils;
+
+class IpUtilsTest extends TestCase
+{
+ /**
+ * @dataProvider getIpv4Data
+ */
+ public function testIpv4($matches, $remoteAddr, $cidr)
+ {
+ $this->assertSame($matches, IpUtils::checkIp($remoteAddr, $cidr));
+ }
+
+ public function getIpv4Data()
+ {
+ return array(
+ array(true, '192.168.1.1', '192.168.1.1'),
+ array(true, '192.168.1.1', '192.168.1.1/1'),
+ array(true, '192.168.1.1', '192.168.1.0/24'),
+ array(false, '192.168.1.1', '1.2.3.4/1'),
+ array(false, '192.168.1.1', '192.168.1.1/33'), // invalid subnet
+ array(true, '192.168.1.1', array('1.2.3.4/1', '192.168.1.0/24')),
+ array(true, '192.168.1.1', array('192.168.1.0/24', '1.2.3.4/1')),
+ array(false, '192.168.1.1', array('1.2.3.4/1', '4.3.2.1/1')),
+ array(true, '1.2.3.4', '0.0.0.0/0'),
+ array(true, '1.2.3.4', '192.168.1.0/0'),
+ array(false, '1.2.3.4', '256.256.256/0'), // invalid CIDR notation
+ array(false, 'an_invalid_ip', '192.168.1.0/24'),
+ );
+ }
+
+ /**
+ * @dataProvider getIpv6Data
+ */
+ public function testIpv6($matches, $remoteAddr, $cidr)
+ {
+ if (!defined('AF_INET6')) {
+ $this->markTestSkipped('Only works when PHP is compiled without the option "disable-ipv6".');
+ }
+
+ $this->assertSame($matches, IpUtils::checkIp($remoteAddr, $cidr));
+ }
+
+ public function getIpv6Data()
+ {
+ return array(
+ array(true, '2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'),
+ array(false, '2a00:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'),
+ array(false, '2a01:198:603:0:396e:4789:8e99:890f', '::1'),
+ array(true, '0:0:0:0:0:0:0:1', '::1'),
+ array(false, '0:0:603:0:396e:4789:8e99:0001', '::1'),
+ array(true, '0:0:603:0:396e:4789:8e99:0001', '::/0'),
+ array(true, '0:0:603:0:396e:4789:8e99:0001', '2a01:198:603:0::/0'),
+ array(true, '2a01:198:603:0:396e:4789:8e99:890f', array('::1', '2a01:198:603:0::/65')),
+ array(true, '2a01:198:603:0:396e:4789:8e99:890f', array('2a01:198:603:0::/65', '::1')),
+ array(false, '2a01:198:603:0:396e:4789:8e99:890f', array('::1', '1a01:198:603:0::/65')),
+ array(false, '}__test|O:21:&quot;JDatabaseDriverMysqli&quot;:3:{s:2', '::1'),
+ array(false, '2a01:198:603:0:396e:4789:8e99:890f', 'unknown'),
+ );
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ * @requires extension sockets
+ */
+ public function testAnIpv6WithOptionDisabledIpv6()
+ {
+ if (defined('AF_INET6')) {
+ $this->markTestSkipped('Only works when PHP is compiled with the option "disable-ipv6".');
+ }
+
+ IpUtils::checkIp('2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65');
+ }
+
+ /**
+ * @dataProvider invalidIpAddressData
+ */
+ public function testInvalidIpAddressesDoNotMatch($requestIp, $proxyIp)
+ {
+ $this->assertFalse(IpUtils::checkIp4($requestIp, $proxyIp));
+ }
+
+ public function invalidIpAddressData()
+ {
+ return array(
+ 'invalid proxy wildcard' => array('192.168.20.13', '*'),
+ 'invalid proxy missing netmask' => array('192.168.20.13', '0.0.0.0'),
+ 'invalid request IP with invalid proxy wildcard' => array('0.0.0.0', '*'),
+ );
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/JsonResponseTest.php b/assets/php/vendor/symfony/http-foundation/Tests/JsonResponseTest.php
new file mode 100644
index 0000000..201839f
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/JsonResponseTest.php
@@ -0,0 +1,257 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\JsonResponse;
+
+class JsonResponseTest extends TestCase
+{
+ public function testConstructorEmptyCreatesJsonObject()
+ {
+ $response = new JsonResponse();
+ $this->assertSame('{}', $response->getContent());
+ }
+
+ public function testConstructorWithArrayCreatesJsonArray()
+ {
+ $response = new JsonResponse(array(0, 1, 2, 3));
+ $this->assertSame('[0,1,2,3]', $response->getContent());
+ }
+
+ public function testConstructorWithAssocArrayCreatesJsonObject()
+ {
+ $response = new JsonResponse(array('foo' => 'bar'));
+ $this->assertSame('{"foo":"bar"}', $response->getContent());
+ }
+
+ public function testConstructorWithSimpleTypes()
+ {
+ $response = new JsonResponse('foo');
+ $this->assertSame('"foo"', $response->getContent());
+
+ $response = new JsonResponse(0);
+ $this->assertSame('0', $response->getContent());
+
+ $response = new JsonResponse(0.1);
+ $this->assertSame('0.1', $response->getContent());
+
+ $response = new JsonResponse(true);
+ $this->assertSame('true', $response->getContent());
+ }
+
+ public function testConstructorWithCustomStatus()
+ {
+ $response = new JsonResponse(array(), 202);
+ $this->assertSame(202, $response->getStatusCode());
+ }
+
+ public function testConstructorAddsContentTypeHeader()
+ {
+ $response = new JsonResponse();
+ $this->assertSame('application/json', $response->headers->get('Content-Type'));
+ }
+
+ public function testConstructorWithCustomHeaders()
+ {
+ $response = new JsonResponse(array(), 200, array('ETag' => 'foo'));
+ $this->assertSame('application/json', $response->headers->get('Content-Type'));
+ $this->assertSame('foo', $response->headers->get('ETag'));
+ }
+
+ public function testConstructorWithCustomContentType()
+ {
+ $headers = array('Content-Type' => 'application/vnd.acme.blog-v1+json');
+
+ $response = new JsonResponse(array(), 200, $headers);
+ $this->assertSame('application/vnd.acme.blog-v1+json', $response->headers->get('Content-Type'));
+ }
+
+ public function testSetJson()
+ {
+ $response = new JsonResponse('1', 200, array(), true);
+ $this->assertEquals('1', $response->getContent());
+
+ $response = new JsonResponse('[1]', 200, array(), true);
+ $this->assertEquals('[1]', $response->getContent());
+
+ $response = new JsonResponse(null, 200, array());
+ $response->setJson('true');
+ $this->assertEquals('true', $response->getContent());
+ }
+
+ public function testCreate()
+ {
+ $response = JsonResponse::create(array('foo' => 'bar'), 204);
+
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
+ $this->assertEquals('{"foo":"bar"}', $response->getContent());
+ $this->assertEquals(204, $response->getStatusCode());
+ }
+
+ public function testStaticCreateEmptyJsonObject()
+ {
+ $response = JsonResponse::create();
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
+ $this->assertSame('{}', $response->getContent());
+ }
+
+ public function testStaticCreateJsonArray()
+ {
+ $response = JsonResponse::create(array(0, 1, 2, 3));
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
+ $this->assertSame('[0,1,2,3]', $response->getContent());
+ }
+
+ public function testStaticCreateJsonObject()
+ {
+ $response = JsonResponse::create(array('foo' => 'bar'));
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
+ $this->assertSame('{"foo":"bar"}', $response->getContent());
+ }
+
+ public function testStaticCreateWithSimpleTypes()
+ {
+ $response = JsonResponse::create('foo');
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
+ $this->assertSame('"foo"', $response->getContent());
+
+ $response = JsonResponse::create(0);
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
+ $this->assertSame('0', $response->getContent());
+
+ $response = JsonResponse::create(0.1);
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
+ $this->assertSame('0.1', $response->getContent());
+
+ $response = JsonResponse::create(true);
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
+ $this->assertSame('true', $response->getContent());
+ }
+
+ public function testStaticCreateWithCustomStatus()
+ {
+ $response = JsonResponse::create(array(), 202);
+ $this->assertSame(202, $response->getStatusCode());
+ }
+
+ public function testStaticCreateAddsContentTypeHeader()
+ {
+ $response = JsonResponse::create();
+ $this->assertSame('application/json', $response->headers->get('Content-Type'));
+ }
+
+ public function testStaticCreateWithCustomHeaders()
+ {
+ $response = JsonResponse::create(array(), 200, array('ETag' => 'foo'));
+ $this->assertSame('application/json', $response->headers->get('Content-Type'));
+ $this->assertSame('foo', $response->headers->get('ETag'));
+ }
+
+ public function testStaticCreateWithCustomContentType()
+ {
+ $headers = array('Content-Type' => 'application/vnd.acme.blog-v1+json');
+
+ $response = JsonResponse::create(array(), 200, $headers);
+ $this->assertSame('application/vnd.acme.blog-v1+json', $response->headers->get('Content-Type'));
+ }
+
+ public function testSetCallback()
+ {
+ $response = JsonResponse::create(array('foo' => 'bar'))->setCallback('callback');
+
+ $this->assertEquals('/**/callback({"foo":"bar"});', $response->getContent());
+ $this->assertEquals('text/javascript', $response->headers->get('Content-Type'));
+ }
+
+ public function testJsonEncodeFlags()
+ {
+ $response = new JsonResponse('<>\'&"');
+
+ $this->assertEquals('"\u003C\u003E\u0027\u0026\u0022"', $response->getContent());
+ }
+
+ public function testGetEncodingOptions()
+ {
+ $response = new JsonResponse();
+
+ $this->assertEquals(JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT, $response->getEncodingOptions());
+ }
+
+ public function testSetEncodingOptions()
+ {
+ $response = new JsonResponse();
+ $response->setData(array(array(1, 2, 3)));
+
+ $this->assertEquals('[[1,2,3]]', $response->getContent());
+
+ $response->setEncodingOptions(JSON_FORCE_OBJECT);
+
+ $this->assertEquals('{"0":{"0":1,"1":2,"2":3}}', $response->getContent());
+ }
+
+ public function testItAcceptsJsonAsString()
+ {
+ $response = JsonResponse::fromJsonString('{"foo":"bar"}');
+ $this->assertSame('{"foo":"bar"}', $response->getContent());
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testSetCallbackInvalidIdentifier()
+ {
+ $response = new JsonResponse('foo');
+ $response->setCallback('+invalid');
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testSetContent()
+ {
+ JsonResponse::create("\xB1\x31");
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage This error is expected
+ */
+ public function testSetContentJsonSerializeError()
+ {
+ if (!interface_exists('JsonSerializable', false)) {
+ $this->markTestSkipped('JsonSerializable is required.');
+ }
+
+ $serializable = new JsonSerializableObject();
+
+ JsonResponse::create($serializable);
+ }
+
+ public function testSetComplexCallback()
+ {
+ $response = JsonResponse::create(array('foo' => 'bar'));
+ $response->setCallback('ಠ_ಠ["foo"].bar[0]');
+
+ $this->assertEquals('/**/ಠ_ಠ["foo"].bar[0]({"foo":"bar"});', $response->getContent());
+ }
+}
+
+if (interface_exists('JsonSerializable', false)) {
+ class JsonSerializableObject implements \JsonSerializable
+ {
+ public function jsonSerialize()
+ {
+ throw new \Exception('This error is expected');
+ }
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/ParameterBagTest.php b/assets/php/vendor/symfony/http-foundation/Tests/ParameterBagTest.php
new file mode 100644
index 0000000..ab908d8
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/ParameterBagTest.php
@@ -0,0 +1,194 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\ParameterBag;
+
+class ParameterBagTest extends TestCase
+{
+ public function testConstructor()
+ {
+ $this->testAll();
+ }
+
+ public function testAll()
+ {
+ $bag = new ParameterBag(array('foo' => 'bar'));
+ $this->assertEquals(array('foo' => 'bar'), $bag->all(), '->all() gets all the input');
+ }
+
+ public function testKeys()
+ {
+ $bag = new ParameterBag(array('foo' => 'bar'));
+ $this->assertEquals(array('foo'), $bag->keys());
+ }
+
+ public function testAdd()
+ {
+ $bag = new ParameterBag(array('foo' => 'bar'));
+ $bag->add(array('bar' => 'bas'));
+ $this->assertEquals(array('foo' => 'bar', 'bar' => 'bas'), $bag->all());
+ }
+
+ public function testRemove()
+ {
+ $bag = new ParameterBag(array('foo' => 'bar'));
+ $bag->add(array('bar' => 'bas'));
+ $this->assertEquals(array('foo' => 'bar', 'bar' => 'bas'), $bag->all());
+ $bag->remove('bar');
+ $this->assertEquals(array('foo' => 'bar'), $bag->all());
+ }
+
+ public function testReplace()
+ {
+ $bag = new ParameterBag(array('foo' => 'bar'));
+
+ $bag->replace(array('FOO' => 'BAR'));
+ $this->assertEquals(array('FOO' => 'BAR'), $bag->all(), '->replace() replaces the input with the argument');
+ $this->assertFalse($bag->has('foo'), '->replace() overrides previously set the input');
+ }
+
+ public function testGet()
+ {
+ $bag = new ParameterBag(array('foo' => 'bar', 'null' => null));
+
+ $this->assertEquals('bar', $bag->get('foo'), '->get() gets the value of a parameter');
+ $this->assertEquals('default', $bag->get('unknown', 'default'), '->get() returns second argument as default if a parameter is not defined');
+ $this->assertNull($bag->get('null', 'default'), '->get() returns null if null is set');
+ }
+
+ public function testGetDoesNotUseDeepByDefault()
+ {
+ $bag = new ParameterBag(array('foo' => array('bar' => 'moo')));
+
+ $this->assertNull($bag->get('foo[bar]'));
+ }
+
+ public function testSet()
+ {
+ $bag = new ParameterBag(array());
+
+ $bag->set('foo', 'bar');
+ $this->assertEquals('bar', $bag->get('foo'), '->set() sets the value of parameter');
+
+ $bag->set('foo', 'baz');
+ $this->assertEquals('baz', $bag->get('foo'), '->set() overrides previously set parameter');
+ }
+
+ public function testHas()
+ {
+ $bag = new ParameterBag(array('foo' => 'bar'));
+
+ $this->assertTrue($bag->has('foo'), '->has() returns true if a parameter is defined');
+ $this->assertFalse($bag->has('unknown'), '->has() return false if a parameter is not defined');
+ }
+
+ public function testGetAlpha()
+ {
+ $bag = new ParameterBag(array('word' => 'foo_BAR_012'));
+
+ $this->assertEquals('fooBAR', $bag->getAlpha('word'), '->getAlpha() gets only alphabetic characters');
+ $this->assertEquals('', $bag->getAlpha('unknown'), '->getAlpha() returns empty string if a parameter is not defined');
+ }
+
+ public function testGetAlnum()
+ {
+ $bag = new ParameterBag(array('word' => 'foo_BAR_012'));
+
+ $this->assertEquals('fooBAR012', $bag->getAlnum('word'), '->getAlnum() gets only alphanumeric characters');
+ $this->assertEquals('', $bag->getAlnum('unknown'), '->getAlnum() returns empty string if a parameter is not defined');
+ }
+
+ public function testGetDigits()
+ {
+ $bag = new ParameterBag(array('word' => 'foo_BAR_012'));
+
+ $this->assertEquals('012', $bag->getDigits('word'), '->getDigits() gets only digits as string');
+ $this->assertEquals('', $bag->getDigits('unknown'), '->getDigits() returns empty string if a parameter is not defined');
+ }
+
+ public function testGetInt()
+ {
+ $bag = new ParameterBag(array('digits' => '0123'));
+
+ $this->assertEquals(123, $bag->getInt('digits'), '->getInt() gets a value of parameter as integer');
+ $this->assertEquals(0, $bag->getInt('unknown'), '->getInt() returns zero if a parameter is not defined');
+ }
+
+ public function testFilter()
+ {
+ $bag = new ParameterBag(array(
+ 'digits' => '0123ab',
+ 'email' => 'example@example.com',
+ 'url' => 'http://example.com/foo',
+ 'dec' => '256',
+ 'hex' => '0x100',
+ 'array' => array('bang'),
+ ));
+
+ $this->assertEmpty($bag->filter('nokey'), '->filter() should return empty by default if no key is found');
+
+ $this->assertEquals('0123', $bag->filter('digits', '', FILTER_SANITIZE_NUMBER_INT), '->filter() gets a value of parameter as integer filtering out invalid characters');
+
+ $this->assertEquals('example@example.com', $bag->filter('email', '', FILTER_VALIDATE_EMAIL), '->filter() gets a value of parameter as email');
+
+ $this->assertEquals('http://example.com/foo', $bag->filter('url', '', FILTER_VALIDATE_URL, array('flags' => FILTER_FLAG_PATH_REQUIRED)), '->filter() gets a value of parameter as URL with a path');
+
+ // This test is repeated for code-coverage
+ $this->assertEquals('http://example.com/foo', $bag->filter('url', '', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED), '->filter() gets a value of parameter as URL with a path');
+
+ $this->assertFalse($bag->filter('dec', '', FILTER_VALIDATE_INT, array(
+ 'flags' => FILTER_FLAG_ALLOW_HEX,
+ 'options' => array('min_range' => 1, 'max_range' => 0xff),
+ )), '->filter() gets a value of parameter as integer between boundaries');
+
+ $this->assertFalse($bag->filter('hex', '', FILTER_VALIDATE_INT, array(
+ 'flags' => FILTER_FLAG_ALLOW_HEX,
+ 'options' => array('min_range' => 1, 'max_range' => 0xff),
+ )), '->filter() gets a value of parameter as integer between boundaries');
+
+ $this->assertEquals(array('bang'), $bag->filter('array', ''), '->filter() gets a value of parameter as an array');
+ }
+
+ public function testGetIterator()
+ {
+ $parameters = array('foo' => 'bar', 'hello' => 'world');
+ $bag = new ParameterBag($parameters);
+
+ $i = 0;
+ foreach ($bag as $key => $val) {
+ ++$i;
+ $this->assertEquals($parameters[$key], $val);
+ }
+
+ $this->assertEquals(count($parameters), $i);
+ }
+
+ public function testCount()
+ {
+ $parameters = array('foo' => 'bar', 'hello' => 'world');
+ $bag = new ParameterBag($parameters);
+
+ $this->assertCount(count($parameters), $bag);
+ }
+
+ public function testGetBoolean()
+ {
+ $parameters = array('string_true' => 'true', 'string_false' => 'false');
+ $bag = new ParameterBag($parameters);
+
+ $this->assertTrue($bag->getBoolean('string_true'), '->getBoolean() gets the string true as boolean true');
+ $this->assertFalse($bag->getBoolean('string_false'), '->getBoolean() gets the string false as boolean false');
+ $this->assertFalse($bag->getBoolean('unknown'), '->getBoolean() returns false if a parameter is not defined');
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/RedirectResponseTest.php b/assets/php/vendor/symfony/http-foundation/Tests/RedirectResponseTest.php
new file mode 100644
index 0000000..d389e83
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/RedirectResponseTest.php
@@ -0,0 +1,97 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+
+class RedirectResponseTest extends TestCase
+{
+ public function testGenerateMetaRedirect()
+ {
+ $response = new RedirectResponse('foo.bar');
+
+ $this->assertEquals(1, preg_match(
+ '#<meta http-equiv="refresh" content="\d+;url=foo\.bar" />#',
+ preg_replace(array('/\s+/', '/\'/'), array(' ', '"'), $response->getContent())
+ ));
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testRedirectResponseConstructorNullUrl()
+ {
+ $response = new RedirectResponse(null);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testRedirectResponseConstructorWrongStatusCode()
+ {
+ $response = new RedirectResponse('foo.bar', 404);
+ }
+
+ public function testGenerateLocationHeader()
+ {
+ $response = new RedirectResponse('foo.bar');
+
+ $this->assertTrue($response->headers->has('Location'));
+ $this->assertEquals('foo.bar', $response->headers->get('Location'));
+ }
+
+ public function testGetTargetUrl()
+ {
+ $response = new RedirectResponse('foo.bar');
+
+ $this->assertEquals('foo.bar', $response->getTargetUrl());
+ }
+
+ public function testSetTargetUrl()
+ {
+ $response = new RedirectResponse('foo.bar');
+ $response->setTargetUrl('baz.beep');
+
+ $this->assertEquals('baz.beep', $response->getTargetUrl());
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testSetTargetUrlNull()
+ {
+ $response = new RedirectResponse('foo.bar');
+ $response->setTargetUrl(null);
+ }
+
+ public function testCreate()
+ {
+ $response = RedirectResponse::create('foo', 301);
+
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response);
+ $this->assertEquals(301, $response->getStatusCode());
+ }
+
+ public function testCacheHeaders()
+ {
+ $response = new RedirectResponse('foo.bar', 301);
+ $this->assertFalse($response->headers->hasCacheControlDirective('no-cache'));
+
+ $response = new RedirectResponse('foo.bar', 301, array('cache-control' => 'max-age=86400'));
+ $this->assertFalse($response->headers->hasCacheControlDirective('no-cache'));
+ $this->assertTrue($response->headers->hasCacheControlDirective('max-age'));
+
+ $response = new RedirectResponse('foo.bar', 302);
+ $this->assertTrue($response->headers->hasCacheControlDirective('no-cache'));
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/RequestMatcherTest.php b/assets/php/vendor/symfony/http-foundation/Tests/RequestMatcherTest.php
new file mode 100644
index 0000000..b5d8004
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/RequestMatcherTest.php
@@ -0,0 +1,151 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\RequestMatcher;
+use Symfony\Component\HttpFoundation\Request;
+
+class RequestMatcherTest extends TestCase
+{
+ /**
+ * @dataProvider getMethodData
+ */
+ public function testMethod($requestMethod, $matcherMethod, $isMatch)
+ {
+ $matcher = new RequestMatcher();
+ $matcher->matchMethod($matcherMethod);
+ $request = Request::create('', $requestMethod);
+ $this->assertSame($isMatch, $matcher->matches($request));
+
+ $matcher = new RequestMatcher(null, null, $matcherMethod);
+ $request = Request::create('', $requestMethod);
+ $this->assertSame($isMatch, $matcher->matches($request));
+ }
+
+ public function getMethodData()
+ {
+ return array(
+ array('get', 'get', true),
+ array('get', array('get', 'post'), true),
+ array('get', 'post', false),
+ array('get', 'GET', true),
+ array('get', array('GET', 'POST'), true),
+ array('get', 'POST', false),
+ );
+ }
+
+ public function testScheme()
+ {
+ $httpRequest = $request = $request = Request::create('');
+ $httpsRequest = $request = $request = Request::create('', 'get', array(), array(), array(), array('HTTPS' => 'on'));
+
+ $matcher = new RequestMatcher();
+ $matcher->matchScheme('https');
+ $this->assertFalse($matcher->matches($httpRequest));
+ $this->assertTrue($matcher->matches($httpsRequest));
+
+ $matcher->matchScheme('http');
+ $this->assertFalse($matcher->matches($httpsRequest));
+ $this->assertTrue($matcher->matches($httpRequest));
+
+ $matcher = new RequestMatcher();
+ $this->assertTrue($matcher->matches($httpsRequest));
+ $this->assertTrue($matcher->matches($httpRequest));
+ }
+
+ /**
+ * @dataProvider getHostData
+ */
+ public function testHost($pattern, $isMatch)
+ {
+ $matcher = new RequestMatcher();
+ $request = Request::create('', 'get', array(), array(), array(), array('HTTP_HOST' => 'foo.example.com'));
+
+ $matcher->matchHost($pattern);
+ $this->assertSame($isMatch, $matcher->matches($request));
+
+ $matcher = new RequestMatcher(null, $pattern);
+ $this->assertSame($isMatch, $matcher->matches($request));
+ }
+
+ public function getHostData()
+ {
+ return array(
+ array('.*\.example\.com', true),
+ array('\.example\.com$', true),
+ array('^.*\.example\.com$', true),
+ array('.*\.sensio\.com', false),
+ array('.*\.example\.COM', true),
+ array('\.example\.COM$', true),
+ array('^.*\.example\.COM$', true),
+ array('.*\.sensio\.COM', false),
+ );
+ }
+
+ public function testPath()
+ {
+ $matcher = new RequestMatcher();
+
+ $request = Request::create('/admin/foo');
+
+ $matcher->matchPath('/admin/.*');
+ $this->assertTrue($matcher->matches($request));
+
+ $matcher->matchPath('/admin');
+ $this->assertTrue($matcher->matches($request));
+
+ $matcher->matchPath('^/admin/.*$');
+ $this->assertTrue($matcher->matches($request));
+
+ $matcher->matchMethod('/blog/.*');
+ $this->assertFalse($matcher->matches($request));
+ }
+
+ public function testPathWithLocaleIsNotSupported()
+ {
+ $matcher = new RequestMatcher();
+ $request = Request::create('/en/login');
+ $request->setLocale('en');
+
+ $matcher->matchPath('^/{_locale}/login$');
+ $this->assertFalse($matcher->matches($request));
+ }
+
+ public function testPathWithEncodedCharacters()
+ {
+ $matcher = new RequestMatcher();
+ $request = Request::create('/admin/fo%20o');
+ $matcher->matchPath('^/admin/fo o*$');
+ $this->assertTrue($matcher->matches($request));
+ }
+
+ public function testAttributes()
+ {
+ $matcher = new RequestMatcher();
+
+ $request = Request::create('/admin/foo');
+ $request->attributes->set('foo', 'foo_bar');
+
+ $matcher->matchAttribute('foo', 'foo_.*');
+ $this->assertTrue($matcher->matches($request));
+
+ $matcher->matchAttribute('foo', 'foo');
+ $this->assertTrue($matcher->matches($request));
+
+ $matcher->matchAttribute('foo', '^foo_bar$');
+ $this->assertTrue($matcher->matches($request));
+
+ $matcher->matchAttribute('foo', 'babar');
+ $this->assertFalse($matcher->matches($request));
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/RequestStackTest.php b/assets/php/vendor/symfony/http-foundation/Tests/RequestStackTest.php
new file mode 100644
index 0000000..a84fb26
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/RequestStackTest.php
@@ -0,0 +1,70 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RequestStack;
+
+class RequestStackTest extends TestCase
+{
+ public function testGetCurrentRequest()
+ {
+ $requestStack = new RequestStack();
+ $this->assertNull($requestStack->getCurrentRequest());
+
+ $request = Request::create('/foo');
+
+ $requestStack->push($request);
+ $this->assertSame($request, $requestStack->getCurrentRequest());
+
+ $this->assertSame($request, $requestStack->pop());
+ $this->assertNull($requestStack->getCurrentRequest());
+
+ $this->assertNull($requestStack->pop());
+ }
+
+ public function testGetMasterRequest()
+ {
+ $requestStack = new RequestStack();
+ $this->assertNull($requestStack->getMasterRequest());
+
+ $masterRequest = Request::create('/foo');
+ $subRequest = Request::create('/bar');
+
+ $requestStack->push($masterRequest);
+ $requestStack->push($subRequest);
+
+ $this->assertSame($masterRequest, $requestStack->getMasterRequest());
+ }
+
+ public function testGetParentRequest()
+ {
+ $requestStack = new RequestStack();
+ $this->assertNull($requestStack->getParentRequest());
+
+ $masterRequest = Request::create('/foo');
+
+ $requestStack->push($masterRequest);
+ $this->assertNull($requestStack->getParentRequest());
+
+ $firstSubRequest = Request::create('/bar');
+
+ $requestStack->push($firstSubRequest);
+ $this->assertSame($masterRequest, $requestStack->getParentRequest());
+
+ $secondSubRequest = Request::create('/baz');
+
+ $requestStack->push($secondSubRequest);
+ $this->assertSame($firstSubRequest, $requestStack->getParentRequest());
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/RequestTest.php b/assets/php/vendor/symfony/http-foundation/Tests/RequestTest.php
new file mode 100644
index 0000000..230ad15
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/RequestTest.php
@@ -0,0 +1,2329 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
+use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Request;
+
+class RequestTest extends TestCase
+{
+ protected function tearDown()
+ {
+ // reset
+ Request::setTrustedProxies(array(), -1);
+ }
+
+ public function testInitialize()
+ {
+ $request = new Request();
+
+ $request->initialize(array('foo' => 'bar'));
+ $this->assertEquals('bar', $request->query->get('foo'), '->initialize() takes an array of query parameters as its first argument');
+
+ $request->initialize(array(), array('foo' => 'bar'));
+ $this->assertEquals('bar', $request->request->get('foo'), '->initialize() takes an array of request parameters as its second argument');
+
+ $request->initialize(array(), array(), array('foo' => 'bar'));
+ $this->assertEquals('bar', $request->attributes->get('foo'), '->initialize() takes an array of attributes as its third argument');
+
+ $request->initialize(array(), array(), array(), array(), array(), array('HTTP_FOO' => 'bar'));
+ $this->assertEquals('bar', $request->headers->get('FOO'), '->initialize() takes an array of HTTP headers as its sixth argument');
+ }
+
+ public function testGetLocale()
+ {
+ $request = new Request();
+ $request->setLocale('pl');
+ $locale = $request->getLocale();
+ $this->assertEquals('pl', $locale);
+ }
+
+ public function testGetUser()
+ {
+ $request = Request::create('http://user:password@test.com');
+ $user = $request->getUser();
+
+ $this->assertEquals('user', $user);
+ }
+
+ public function testGetPassword()
+ {
+ $request = Request::create('http://user:password@test.com');
+ $password = $request->getPassword();
+
+ $this->assertEquals('password', $password);
+ }
+
+ public function testIsNoCache()
+ {
+ $request = new Request();
+ $isNoCache = $request->isNoCache();
+
+ $this->assertFalse($isNoCache);
+ }
+
+ public function testGetContentType()
+ {
+ $request = new Request();
+ $contentType = $request->getContentType();
+
+ $this->assertNull($contentType);
+ }
+
+ public function testSetDefaultLocale()
+ {
+ $request = new Request();
+ $request->setDefaultLocale('pl');
+ $locale = $request->getLocale();
+
+ $this->assertEquals('pl', $locale);
+ }
+
+ public function testCreate()
+ {
+ $request = Request::create('http://test.com/foo?bar=baz');
+ $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri());
+ $this->assertEquals('/foo', $request->getPathInfo());
+ $this->assertEquals('bar=baz', $request->getQueryString());
+ $this->assertEquals(80, $request->getPort());
+ $this->assertEquals('test.com', $request->getHttpHost());
+ $this->assertFalse($request->isSecure());
+
+ $request = Request::create('http://test.com/foo', 'GET', array('bar' => 'baz'));
+ $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri());
+ $this->assertEquals('/foo', $request->getPathInfo());
+ $this->assertEquals('bar=baz', $request->getQueryString());
+ $this->assertEquals(80, $request->getPort());
+ $this->assertEquals('test.com', $request->getHttpHost());
+ $this->assertFalse($request->isSecure());
+
+ $request = Request::create('http://test.com/foo?bar=foo', 'GET', array('bar' => 'baz'));
+ $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri());
+ $this->assertEquals('/foo', $request->getPathInfo());
+ $this->assertEquals('bar=baz', $request->getQueryString());
+ $this->assertEquals(80, $request->getPort());
+ $this->assertEquals('test.com', $request->getHttpHost());
+ $this->assertFalse($request->isSecure());
+
+ $request = Request::create('https://test.com/foo?bar=baz');
+ $this->assertEquals('https://test.com/foo?bar=baz', $request->getUri());
+ $this->assertEquals('/foo', $request->getPathInfo());
+ $this->assertEquals('bar=baz', $request->getQueryString());
+ $this->assertEquals(443, $request->getPort());
+ $this->assertEquals('test.com', $request->getHttpHost());
+ $this->assertTrue($request->isSecure());
+
+ $request = Request::create('test.com:90/foo');
+ $this->assertEquals('http://test.com:90/foo', $request->getUri());
+ $this->assertEquals('/foo', $request->getPathInfo());
+ $this->assertEquals('test.com', $request->getHost());
+ $this->assertEquals('test.com:90', $request->getHttpHost());
+ $this->assertEquals(90, $request->getPort());
+ $this->assertFalse($request->isSecure());
+
+ $request = Request::create('https://test.com:90/foo');
+ $this->assertEquals('https://test.com:90/foo', $request->getUri());
+ $this->assertEquals('/foo', $request->getPathInfo());
+ $this->assertEquals('test.com', $request->getHost());
+ $this->assertEquals('test.com:90', $request->getHttpHost());
+ $this->assertEquals(90, $request->getPort());
+ $this->assertTrue($request->isSecure());
+
+ $request = Request::create('https://127.0.0.1:90/foo');
+ $this->assertEquals('https://127.0.0.1:90/foo', $request->getUri());
+ $this->assertEquals('/foo', $request->getPathInfo());
+ $this->assertEquals('127.0.0.1', $request->getHost());
+ $this->assertEquals('127.0.0.1:90', $request->getHttpHost());
+ $this->assertEquals(90, $request->getPort());
+ $this->assertTrue($request->isSecure());
+
+ $request = Request::create('https://[::1]:90/foo');
+ $this->assertEquals('https://[::1]:90/foo', $request->getUri());
+ $this->assertEquals('/foo', $request->getPathInfo());
+ $this->assertEquals('[::1]', $request->getHost());
+ $this->assertEquals('[::1]:90', $request->getHttpHost());
+ $this->assertEquals(90, $request->getPort());
+ $this->assertTrue($request->isSecure());
+
+ $request = Request::create('https://[::1]/foo');
+ $this->assertEquals('https://[::1]/foo', $request->getUri());
+ $this->assertEquals('/foo', $request->getPathInfo());
+ $this->assertEquals('[::1]', $request->getHost());
+ $this->assertEquals('[::1]', $request->getHttpHost());
+ $this->assertEquals(443, $request->getPort());
+ $this->assertTrue($request->isSecure());
+
+ $json = '{"jsonrpc":"2.0","method":"echo","id":7,"params":["Hello World"]}';
+ $request = Request::create('http://example.com/jsonrpc', 'POST', array(), array(), array(), array(), $json);
+ $this->assertEquals($json, $request->getContent());
+ $this->assertFalse($request->isSecure());
+
+ $request = Request::create('http://test.com');
+ $this->assertEquals('http://test.com/', $request->getUri());
+ $this->assertEquals('/', $request->getPathInfo());
+ $this->assertEquals('', $request->getQueryString());
+ $this->assertEquals(80, $request->getPort());
+ $this->assertEquals('test.com', $request->getHttpHost());
+ $this->assertFalse($request->isSecure());
+
+ $request = Request::create('http://test.com?test=1');
+ $this->assertEquals('http://test.com/?test=1', $request->getUri());
+ $this->assertEquals('/', $request->getPathInfo());
+ $this->assertEquals('test=1', $request->getQueryString());
+ $this->assertEquals(80, $request->getPort());
+ $this->assertEquals('test.com', $request->getHttpHost());
+ $this->assertFalse($request->isSecure());
+
+ $request = Request::create('http://test.com:90/?test=1');
+ $this->assertEquals('http://test.com:90/?test=1', $request->getUri());
+ $this->assertEquals('/', $request->getPathInfo());
+ $this->assertEquals('test=1', $request->getQueryString());
+ $this->assertEquals(90, $request->getPort());
+ $this->assertEquals('test.com:90', $request->getHttpHost());
+ $this->assertFalse($request->isSecure());
+
+ $request = Request::create('http://username:password@test.com');
+ $this->assertEquals('http://test.com/', $request->getUri());
+ $this->assertEquals('/', $request->getPathInfo());
+ $this->assertEquals('', $request->getQueryString());
+ $this->assertEquals(80, $request->getPort());
+ $this->assertEquals('test.com', $request->getHttpHost());
+ $this->assertEquals('username', $request->getUser());
+ $this->assertEquals('password', $request->getPassword());
+ $this->assertFalse($request->isSecure());
+
+ $request = Request::create('http://username@test.com');
+ $this->assertEquals('http://test.com/', $request->getUri());
+ $this->assertEquals('/', $request->getPathInfo());
+ $this->assertEquals('', $request->getQueryString());
+ $this->assertEquals(80, $request->getPort());
+ $this->assertEquals('test.com', $request->getHttpHost());
+ $this->assertEquals('username', $request->getUser());
+ $this->assertSame('', $request->getPassword());
+ $this->assertFalse($request->isSecure());
+
+ $request = Request::create('http://test.com/?foo');
+ $this->assertEquals('/?foo', $request->getRequestUri());
+ $this->assertEquals(array('foo' => ''), $request->query->all());
+
+ // assume rewrite rule: (.*) --> app/app.php; app/ is a symlink to a symfony web/ directory
+ $request = Request::create('http://test.com/apparthotel-1234', 'GET', array(), array(), array(),
+ array(
+ 'DOCUMENT_ROOT' => '/var/www/www.test.com',
+ 'SCRIPT_FILENAME' => '/var/www/www.test.com/app/app.php',
+ 'SCRIPT_NAME' => '/app/app.php',
+ 'PHP_SELF' => '/app/app.php/apparthotel-1234',
+ ));
+ $this->assertEquals('http://test.com/apparthotel-1234', $request->getUri());
+ $this->assertEquals('/apparthotel-1234', $request->getPathInfo());
+ $this->assertEquals('', $request->getQueryString());
+ $this->assertEquals(80, $request->getPort());
+ $this->assertEquals('test.com', $request->getHttpHost());
+ $this->assertFalse($request->isSecure());
+ }
+
+ public function testCreateCheckPrecedence()
+ {
+ // server is used by default
+ $request = Request::create('/', 'DELETE', array(), array(), array(), array(
+ 'HTTP_HOST' => 'example.com',
+ 'HTTPS' => 'on',
+ 'SERVER_PORT' => 443,
+ 'PHP_AUTH_USER' => 'fabien',
+ 'PHP_AUTH_PW' => 'pa$$',
+ 'QUERY_STRING' => 'foo=bar',
+ 'CONTENT_TYPE' => 'application/json',
+ ));
+ $this->assertEquals('example.com', $request->getHost());
+ $this->assertEquals(443, $request->getPort());
+ $this->assertTrue($request->isSecure());
+ $this->assertEquals('fabien', $request->getUser());
+ $this->assertEquals('pa$$', $request->getPassword());
+ $this->assertEquals('', $request->getQueryString());
+ $this->assertEquals('application/json', $request->headers->get('CONTENT_TYPE'));
+
+ // URI has precedence over server
+ $request = Request::create('http://thomas:pokemon@example.net:8080/?foo=bar', 'GET', array(), array(), array(), array(
+ 'HTTP_HOST' => 'example.com',
+ 'HTTPS' => 'on',
+ 'SERVER_PORT' => 443,
+ ));
+ $this->assertEquals('example.net', $request->getHost());
+ $this->assertEquals(8080, $request->getPort());
+ $this->assertFalse($request->isSecure());
+ $this->assertEquals('thomas', $request->getUser());
+ $this->assertEquals('pokemon', $request->getPassword());
+ $this->assertEquals('foo=bar', $request->getQueryString());
+ }
+
+ public function testDuplicate()
+ {
+ $request = new Request(array('foo' => 'bar'), array('foo' => 'bar'), array('foo' => 'bar'), array(), array(), array('HTTP_FOO' => 'bar'));
+ $dup = $request->duplicate();
+
+ $this->assertEquals($request->query->all(), $dup->query->all(), '->duplicate() duplicates a request an copy the current query parameters');
+ $this->assertEquals($request->request->all(), $dup->request->all(), '->duplicate() duplicates a request an copy the current request parameters');
+ $this->assertEquals($request->attributes->all(), $dup->attributes->all(), '->duplicate() duplicates a request an copy the current attributes');
+ $this->assertEquals($request->headers->all(), $dup->headers->all(), '->duplicate() duplicates a request an copy the current HTTP headers');
+
+ $dup = $request->duplicate(array('foo' => 'foobar'), array('foo' => 'foobar'), array('foo' => 'foobar'), array(), array(), array('HTTP_FOO' => 'foobar'));
+
+ $this->assertEquals(array('foo' => 'foobar'), $dup->query->all(), '->duplicate() overrides the query parameters if provided');
+ $this->assertEquals(array('foo' => 'foobar'), $dup->request->all(), '->duplicate() overrides the request parameters if provided');
+ $this->assertEquals(array('foo' => 'foobar'), $dup->attributes->all(), '->duplicate() overrides the attributes if provided');
+ $this->assertEquals(array('foo' => array('foobar')), $dup->headers->all(), '->duplicate() overrides the HTTP header if provided');
+ }
+
+ public function testDuplicateWithFormat()
+ {
+ $request = new Request(array(), array(), array('_format' => 'json'));
+ $dup = $request->duplicate();
+
+ $this->assertEquals('json', $dup->getRequestFormat());
+ $this->assertEquals('json', $dup->attributes->get('_format'));
+
+ $request = new Request();
+ $request->setRequestFormat('xml');
+ $dup = $request->duplicate();
+
+ $this->assertEquals('xml', $dup->getRequestFormat());
+ }
+
+ /**
+ * @dataProvider getFormatToMimeTypeMapProviderWithAdditionalNullFormat
+ */
+ public function testGetFormatFromMimeType($format, $mimeTypes)
+ {
+ $request = new Request();
+ foreach ($mimeTypes as $mime) {
+ $this->assertEquals($format, $request->getFormat($mime));
+ }
+ $request->setFormat($format, $mimeTypes);
+ foreach ($mimeTypes as $mime) {
+ $this->assertEquals($format, $request->getFormat($mime));
+
+ if (null !== $format) {
+ $this->assertEquals($mimeTypes[0], $request->getMimeType($format));
+ }
+ }
+ }
+
+ public function getFormatToMimeTypeMapProviderWithAdditionalNullFormat()
+ {
+ return array_merge(
+ array(array(null, array(null, 'unexistent-mime-type'))),
+ $this->getFormatToMimeTypeMapProvider()
+ );
+ }
+
+ public function testGetFormatFromMimeTypeWithParameters()
+ {
+ $request = new Request();
+ $this->assertEquals('json', $request->getFormat('application/json; charset=utf-8'));
+ }
+
+ /**
+ * @dataProvider getFormatToMimeTypeMapProvider
+ */
+ public function testGetMimeTypeFromFormat($format, $mimeTypes)
+ {
+ $request = new Request();
+ $this->assertEquals($mimeTypes[0], $request->getMimeType($format));
+ }
+
+ /**
+ * @dataProvider getFormatToMimeTypeMapProvider
+ */
+ public function testGetMimeTypesFromFormat($format, $mimeTypes)
+ {
+ $this->assertEquals($mimeTypes, Request::getMimeTypes($format));
+ }
+
+ public function testGetMimeTypesFromInexistentFormat()
+ {
+ $request = new Request();
+ $this->assertNull($request->getMimeType('foo'));
+ $this->assertEquals(array(), Request::getMimeTypes('foo'));
+ }
+
+ public function testGetFormatWithCustomMimeType()
+ {
+ $request = new Request();
+ $request->setFormat('custom', 'application/vnd.foo.api;myversion=2.3');
+ $this->assertEquals('custom', $request->getFormat('application/vnd.foo.api;myversion=2.3'));
+ }
+
+ public function getFormatToMimeTypeMapProvider()
+ {
+ return array(
+ array('txt', array('text/plain')),
+ array('js', array('application/javascript', 'application/x-javascript', 'text/javascript')),
+ array('css', array('text/css')),
+ array('json', array('application/json', 'application/x-json')),
+ array('jsonld', array('application/ld+json')),
+ array('xml', array('text/xml', 'application/xml', 'application/x-xml')),
+ array('rdf', array('application/rdf+xml')),
+ array('atom', array('application/atom+xml')),
+ );
+ }
+
+ public function testGetUri()
+ {
+ $server = array();
+
+ // Standard Request on non default PORT
+ // http://host:8080/index.php/path/info?query=string
+
+ $server['HTTP_HOST'] = 'host:8080';
+ $server['SERVER_NAME'] = 'servername';
+ $server['SERVER_PORT'] = '8080';
+
+ $server['QUERY_STRING'] = 'query=string';
+ $server['REQUEST_URI'] = '/index.php/path/info?query=string';
+ $server['SCRIPT_NAME'] = '/index.php';
+ $server['PATH_INFO'] = '/path/info';
+ $server['PATH_TRANSLATED'] = 'redirect:/index.php/path/info';
+ $server['PHP_SELF'] = '/index_dev.php/path/info';
+ $server['SCRIPT_FILENAME'] = '/some/where/index.php';
+
+ $request = new Request();
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('http://host:8080/index.php/path/info?query=string', $request->getUri(), '->getUri() with non default port');
+
+ // Use std port number
+ $server['HTTP_HOST'] = 'host';
+ $server['SERVER_NAME'] = 'servername';
+ $server['SERVER_PORT'] = '80';
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('http://host/index.php/path/info?query=string', $request->getUri(), '->getUri() with default port');
+
+ // Without HOST HEADER
+ unset($server['HTTP_HOST']);
+ $server['SERVER_NAME'] = 'servername';
+ $server['SERVER_PORT'] = '80';
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('http://servername/index.php/path/info?query=string', $request->getUri(), '->getUri() with default port without HOST_HEADER');
+
+ // Request with URL REWRITING (hide index.php)
+ // RewriteCond %{REQUEST_FILENAME} !-f
+ // RewriteRule ^(.*)$ index.php [QSA,L]
+ // http://host:8080/path/info?query=string
+ $server = array();
+ $server['HTTP_HOST'] = 'host:8080';
+ $server['SERVER_NAME'] = 'servername';
+ $server['SERVER_PORT'] = '8080';
+
+ $server['REDIRECT_QUERY_STRING'] = 'query=string';
+ $server['REDIRECT_URL'] = '/path/info';
+ $server['SCRIPT_NAME'] = '/index.php';
+ $server['QUERY_STRING'] = 'query=string';
+ $server['REQUEST_URI'] = '/path/info?toto=test&1=1';
+ $server['SCRIPT_NAME'] = '/index.php';
+ $server['PHP_SELF'] = '/index.php';
+ $server['SCRIPT_FILENAME'] = '/some/where/index.php';
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+ $this->assertEquals('http://host:8080/path/info?query=string', $request->getUri(), '->getUri() with rewrite');
+
+ // Use std port number
+ // http://host/path/info?query=string
+ $server['HTTP_HOST'] = 'host';
+ $server['SERVER_NAME'] = 'servername';
+ $server['SERVER_PORT'] = '80';
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('http://host/path/info?query=string', $request->getUri(), '->getUri() with rewrite and default port');
+
+ // Without HOST HEADER
+ unset($server['HTTP_HOST']);
+ $server['SERVER_NAME'] = 'servername';
+ $server['SERVER_PORT'] = '80';
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('http://servername/path/info?query=string', $request->getUri(), '->getUri() with rewrite, default port without HOST_HEADER');
+
+ // With encoded characters
+
+ $server = array(
+ 'HTTP_HOST' => 'host:8080',
+ 'SERVER_NAME' => 'servername',
+ 'SERVER_PORT' => '8080',
+ 'QUERY_STRING' => 'query=string',
+ 'REQUEST_URI' => '/ba%20se/index_dev.php/foo%20bar/in+fo?query=string',
+ 'SCRIPT_NAME' => '/ba se/index_dev.php',
+ 'PATH_TRANSLATED' => 'redirect:/index.php/foo bar/in+fo',
+ 'PHP_SELF' => '/ba se/index_dev.php/path/info',
+ 'SCRIPT_FILENAME' => '/some/where/ba se/index_dev.php',
+ );
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals(
+ 'http://host:8080/ba%20se/index_dev.php/foo%20bar/in+fo?query=string',
+ $request->getUri()
+ );
+
+ // with user info
+
+ $server['PHP_AUTH_USER'] = 'fabien';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+ $this->assertEquals('http://host:8080/ba%20se/index_dev.php/foo%20bar/in+fo?query=string', $request->getUri());
+
+ $server['PHP_AUTH_PW'] = 'symfony';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+ $this->assertEquals('http://host:8080/ba%20se/index_dev.php/foo%20bar/in+fo?query=string', $request->getUri());
+ }
+
+ public function testGetUriForPath()
+ {
+ $request = Request::create('http://test.com/foo?bar=baz');
+ $this->assertEquals('http://test.com/some/path', $request->getUriForPath('/some/path'));
+
+ $request = Request::create('http://test.com:90/foo?bar=baz');
+ $this->assertEquals('http://test.com:90/some/path', $request->getUriForPath('/some/path'));
+
+ $request = Request::create('https://test.com/foo?bar=baz');
+ $this->assertEquals('https://test.com/some/path', $request->getUriForPath('/some/path'));
+
+ $request = Request::create('https://test.com:90/foo?bar=baz');
+ $this->assertEquals('https://test.com:90/some/path', $request->getUriForPath('/some/path'));
+
+ $server = array();
+
+ // Standard Request on non default PORT
+ // http://host:8080/index.php/path/info?query=string
+
+ $server['HTTP_HOST'] = 'host:8080';
+ $server['SERVER_NAME'] = 'servername';
+ $server['SERVER_PORT'] = '8080';
+
+ $server['QUERY_STRING'] = 'query=string';
+ $server['REQUEST_URI'] = '/index.php/path/info?query=string';
+ $server['SCRIPT_NAME'] = '/index.php';
+ $server['PATH_INFO'] = '/path/info';
+ $server['PATH_TRANSLATED'] = 'redirect:/index.php/path/info';
+ $server['PHP_SELF'] = '/index_dev.php/path/info';
+ $server['SCRIPT_FILENAME'] = '/some/where/index.php';
+
+ $request = new Request();
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('http://host:8080/index.php/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with non default port');
+
+ // Use std port number
+ $server['HTTP_HOST'] = 'host';
+ $server['SERVER_NAME'] = 'servername';
+ $server['SERVER_PORT'] = '80';
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('http://host/index.php/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with default port');
+
+ // Without HOST HEADER
+ unset($server['HTTP_HOST']);
+ $server['SERVER_NAME'] = 'servername';
+ $server['SERVER_PORT'] = '80';
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('http://servername/index.php/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with default port without HOST_HEADER');
+
+ // Request with URL REWRITING (hide index.php)
+ // RewriteCond %{REQUEST_FILENAME} !-f
+ // RewriteRule ^(.*)$ index.php [QSA,L]
+ // http://host:8080/path/info?query=string
+ $server = array();
+ $server['HTTP_HOST'] = 'host:8080';
+ $server['SERVER_NAME'] = 'servername';
+ $server['SERVER_PORT'] = '8080';
+
+ $server['REDIRECT_QUERY_STRING'] = 'query=string';
+ $server['REDIRECT_URL'] = '/path/info';
+ $server['SCRIPT_NAME'] = '/index.php';
+ $server['QUERY_STRING'] = 'query=string';
+ $server['REQUEST_URI'] = '/path/info?toto=test&1=1';
+ $server['SCRIPT_NAME'] = '/index.php';
+ $server['PHP_SELF'] = '/index.php';
+ $server['SCRIPT_FILENAME'] = '/some/where/index.php';
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+ $this->assertEquals('http://host:8080/some/path', $request->getUriForPath('/some/path'), '->getUri() with rewrite');
+
+ // Use std port number
+ // http://host/path/info?query=string
+ $server['HTTP_HOST'] = 'host';
+ $server['SERVER_NAME'] = 'servername';
+ $server['SERVER_PORT'] = '80';
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('http://host/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with rewrite and default port');
+
+ // Without HOST HEADER
+ unset($server['HTTP_HOST']);
+ $server['SERVER_NAME'] = 'servername';
+ $server['SERVER_PORT'] = '80';
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('http://servername/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with rewrite, default port without HOST_HEADER');
+ $this->assertEquals('servername', $request->getHttpHost());
+
+ // with user info
+
+ $server['PHP_AUTH_USER'] = 'fabien';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+ $this->assertEquals('http://servername/some/path', $request->getUriForPath('/some/path'));
+
+ $server['PHP_AUTH_PW'] = 'symfony';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+ $this->assertEquals('http://servername/some/path', $request->getUriForPath('/some/path'));
+ }
+
+ /**
+ * @dataProvider getRelativeUriForPathData()
+ */
+ public function testGetRelativeUriForPath($expected, $pathinfo, $path)
+ {
+ $this->assertEquals($expected, Request::create($pathinfo)->getRelativeUriForPath($path));
+ }
+
+ public function getRelativeUriForPathData()
+ {
+ return array(
+ array('me.png', '/foo', '/me.png'),
+ array('../me.png', '/foo/bar', '/me.png'),
+ array('me.png', '/foo/bar', '/foo/me.png'),
+ array('../baz/me.png', '/foo/bar/b', '/foo/baz/me.png'),
+ array('../../fooz/baz/me.png', '/foo/bar/b', '/fooz/baz/me.png'),
+ array('baz/me.png', '/foo/bar/b', 'baz/me.png'),
+ );
+ }
+
+ public function testGetUserInfo()
+ {
+ $request = new Request();
+
+ $server = array('PHP_AUTH_USER' => 'fabien');
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+ $this->assertEquals('fabien', $request->getUserInfo());
+
+ $server['PHP_AUTH_USER'] = '0';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+ $this->assertEquals('0', $request->getUserInfo());
+
+ $server['PHP_AUTH_PW'] = '0';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+ $this->assertEquals('0:0', $request->getUserInfo());
+ }
+
+ public function testGetSchemeAndHttpHost()
+ {
+ $request = new Request();
+
+ $server = array();
+ $server['SERVER_NAME'] = 'servername';
+ $server['SERVER_PORT'] = '90';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+ $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost());
+
+ $server['PHP_AUTH_USER'] = 'fabien';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+ $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost());
+
+ $server['PHP_AUTH_USER'] = '0';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+ $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost());
+
+ $server['PHP_AUTH_PW'] = '0';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+ $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost());
+ }
+
+ /**
+ * @dataProvider getQueryStringNormalizationData
+ */
+ public function testGetQueryString($query, $expectedQuery, $msg)
+ {
+ $request = new Request();
+
+ $request->server->set('QUERY_STRING', $query);
+ $this->assertSame($expectedQuery, $request->getQueryString(), $msg);
+ }
+
+ public function getQueryStringNormalizationData()
+ {
+ return array(
+ array('foo', 'foo', 'works with valueless parameters'),
+ array('foo=', 'foo=', 'includes a dangling equal sign'),
+ array('bar=&foo=bar', 'bar=&foo=bar', '->works with empty parameters'),
+ array('foo=bar&bar=', 'bar=&foo=bar', 'sorts keys alphabetically'),
+
+ // GET parameters, that are submitted from a HTML form, encode spaces as "+" by default (as defined in enctype application/x-www-form-urlencoded).
+ // PHP also converts "+" to spaces when filling the global _GET or when using the function parse_str.
+ array('him=John%20Doe&her=Jane+Doe', 'her=Jane%20Doe&him=John%20Doe', 'normalizes spaces in both encodings "%20" and "+"'),
+
+ array('foo[]=1&foo[]=2', 'foo%5B%5D=1&foo%5B%5D=2', 'allows array notation'),
+ array('foo=1&foo=2', 'foo=1&foo=2', 'allows repeated parameters'),
+ array('pa%3Dram=foo%26bar%3Dbaz&test=test', 'pa%3Dram=foo%26bar%3Dbaz&test=test', 'works with encoded delimiters'),
+ array('0', '0', 'allows "0"'),
+ array('Jane Doe&John%20Doe', 'Jane%20Doe&John%20Doe', 'normalizes encoding in keys'),
+ array('her=Jane Doe&him=John%20Doe', 'her=Jane%20Doe&him=John%20Doe', 'normalizes encoding in values'),
+ array('foo=bar&&&test&&', 'foo=bar&test', 'removes unneeded delimiters'),
+ array('formula=e=m*c^2', 'formula=e%3Dm%2Ac%5E2', 'correctly treats only the first "=" as delimiter and the next as value'),
+
+ // Ignore pairs with empty key, even if there was a value, e.g. "=value", as such nameless values cannot be retrieved anyway.
+ // PHP also does not include them when building _GET.
+ array('foo=bar&=a=b&=x=y', 'foo=bar', 'removes params with empty key'),
+ );
+ }
+
+ public function testGetQueryStringReturnsNull()
+ {
+ $request = new Request();
+
+ $this->assertNull($request->getQueryString(), '->getQueryString() returns null for non-existent query string');
+
+ $request->server->set('QUERY_STRING', '');
+ $this->assertNull($request->getQueryString(), '->getQueryString() returns null for empty query string');
+ }
+
+ public function testGetHost()
+ {
+ $request = new Request();
+
+ $request->initialize(array('foo' => 'bar'));
+ $this->assertEquals('', $request->getHost(), '->getHost() return empty string if not initialized');
+
+ $request->initialize(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'www.example.com'));
+ $this->assertEquals('www.example.com', $request->getHost(), '->getHost() from Host Header');
+
+ // Host header with port number
+ $request->initialize(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'www.example.com:8080'));
+ $this->assertEquals('www.example.com', $request->getHost(), '->getHost() from Host Header with port number');
+
+ // Server values
+ $request->initialize(array(), array(), array(), array(), array(), array('SERVER_NAME' => 'www.example.com'));
+ $this->assertEquals('www.example.com', $request->getHost(), '->getHost() from server name');
+
+ $request->initialize(array(), array(), array(), array(), array(), array('SERVER_NAME' => 'www.example.com', 'HTTP_HOST' => 'www.host.com'));
+ $this->assertEquals('www.host.com', $request->getHost(), '->getHost() value from Host header has priority over SERVER_NAME ');
+ }
+
+ public function testGetPort()
+ {
+ $request = Request::create('http://example.com', 'GET', array(), array(), array(), array(
+ 'HTTP_X_FORWARDED_PROTO' => 'https',
+ 'HTTP_X_FORWARDED_PORT' => '443',
+ ));
+ $port = $request->getPort();
+
+ $this->assertEquals(80, $port, 'Without trusted proxies FORWARDED_PROTO and FORWARDED_PORT are ignored.');
+
+ Request::setTrustedProxies(array('1.1.1.1'), Request::HEADER_X_FORWARDED_ALL);
+ $request = Request::create('http://example.com', 'GET', array(), array(), array(), array(
+ 'HTTP_X_FORWARDED_PROTO' => 'https',
+ 'HTTP_X_FORWARDED_PORT' => '8443',
+ ));
+ $this->assertEquals(80, $request->getPort(), 'With PROTO and PORT on untrusted connection server value takes precedence.');
+ $request->server->set('REMOTE_ADDR', '1.1.1.1');
+ $this->assertEquals(8443, $request->getPort(), 'With PROTO and PORT set PORT takes precedence.');
+
+ $request = Request::create('http://example.com', 'GET', array(), array(), array(), array(
+ 'HTTP_X_FORWARDED_PROTO' => 'https',
+ ));
+ $this->assertEquals(80, $request->getPort(), 'With only PROTO set getPort() ignores trusted headers on untrusted connection.');
+ $request->server->set('REMOTE_ADDR', '1.1.1.1');
+ $this->assertEquals(443, $request->getPort(), 'With only PROTO set getPort() defaults to 443.');
+
+ $request = Request::create('http://example.com', 'GET', array(), array(), array(), array(
+ 'HTTP_X_FORWARDED_PROTO' => 'http',
+ ));
+ $this->assertEquals(80, $request->getPort(), 'If X_FORWARDED_PROTO is set to HTTP getPort() ignores trusted headers on untrusted connection.');
+ $request->server->set('REMOTE_ADDR', '1.1.1.1');
+ $this->assertEquals(80, $request->getPort(), 'If X_FORWARDED_PROTO is set to HTTP getPort() returns port of the original request.');
+
+ $request = Request::create('http://example.com', 'GET', array(), array(), array(), array(
+ 'HTTP_X_FORWARDED_PROTO' => 'On',
+ ));
+ $this->assertEquals(80, $request->getPort(), 'With only PROTO set and value is On, getPort() ignores trusted headers on untrusted connection.');
+ $request->server->set('REMOTE_ADDR', '1.1.1.1');
+ $this->assertEquals(443, $request->getPort(), 'With only PROTO set and value is On, getPort() defaults to 443.');
+
+ $request = Request::create('http://example.com', 'GET', array(), array(), array(), array(
+ 'HTTP_X_FORWARDED_PROTO' => '1',
+ ));
+ $this->assertEquals(80, $request->getPort(), 'With only PROTO set and value is 1, getPort() ignores trusted headers on untrusted connection.');
+ $request->server->set('REMOTE_ADDR', '1.1.1.1');
+ $this->assertEquals(443, $request->getPort(), 'With only PROTO set and value is 1, getPort() defaults to 443.');
+
+ $request = Request::create('http://example.com', 'GET', array(), array(), array(), array(
+ 'HTTP_X_FORWARDED_PROTO' => 'something-else',
+ ));
+ $port = $request->getPort();
+ $this->assertEquals(80, $port, 'With only PROTO set and value is not recognized, getPort() defaults to 80.');
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function testGetHostWithFakeHttpHostValue()
+ {
+ $request = new Request();
+ $request->initialize(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'www.host.com?query=string'));
+ $request->getHost();
+ }
+
+ public function testGetSetMethod()
+ {
+ $request = new Request();
+
+ $this->assertEquals('GET', $request->getMethod(), '->getMethod() returns GET if no method is defined');
+
+ $request->setMethod('get');
+ $this->assertEquals('GET', $request->getMethod(), '->getMethod() returns an uppercased string');
+
+ $request->setMethod('PURGE');
+ $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method even if it is not a standard one');
+
+ $request->setMethod('POST');
+ $this->assertEquals('POST', $request->getMethod(), '->getMethod() returns the method POST if no _method is defined');
+
+ $request->setMethod('POST');
+ $request->request->set('_method', 'purge');
+ $this->assertEquals('POST', $request->getMethod(), '->getMethod() does not return the method from _method if defined and POST but support not enabled');
+
+ $request = new Request();
+ $request->setMethod('POST');
+ $request->request->set('_method', 'purge');
+
+ $this->assertFalse(Request::getHttpMethodParameterOverride(), 'httpMethodParameterOverride should be disabled by default');
+
+ Request::enableHttpMethodParameterOverride();
+
+ $this->assertTrue(Request::getHttpMethodParameterOverride(), 'httpMethodParameterOverride should be enabled now but it is not');
+
+ $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method from _method if defined and POST');
+ $this->disableHttpMethodParameterOverride();
+
+ $request = new Request();
+ $request->setMethod('POST');
+ $request->query->set('_method', 'purge');
+ $this->assertEquals('POST', $request->getMethod(), '->getMethod() does not return the method from _method if defined and POST but support not enabled');
+
+ $request = new Request();
+ $request->setMethod('POST');
+ $request->query->set('_method', 'purge');
+ Request::enableHttpMethodParameterOverride();
+ $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method from _method if defined and POST');
+ $this->disableHttpMethodParameterOverride();
+
+ $request = new Request();
+ $request->setMethod('POST');
+ $request->headers->set('X-HTTP-METHOD-OVERRIDE', 'delete');
+ $this->assertEquals('DELETE', $request->getMethod(), '->getMethod() returns the method from X-HTTP-Method-Override even though _method is set if defined and POST');
+
+ $request = new Request();
+ $request->setMethod('POST');
+ $request->headers->set('X-HTTP-METHOD-OVERRIDE', 'delete');
+ $this->assertEquals('DELETE', $request->getMethod(), '->getMethod() returns the method from X-HTTP-Method-Override if defined and POST');
+ }
+
+ /**
+ * @dataProvider getClientIpsProvider
+ */
+ public function testGetClientIp($expected, $remoteAddr, $httpForwardedFor, $trustedProxies)
+ {
+ $request = $this->getRequestInstanceForClientIpTests($remoteAddr, $httpForwardedFor, $trustedProxies);
+
+ $this->assertEquals($expected[0], $request->getClientIp());
+ }
+
+ /**
+ * @dataProvider getClientIpsProvider
+ */
+ public function testGetClientIps($expected, $remoteAddr, $httpForwardedFor, $trustedProxies)
+ {
+ $request = $this->getRequestInstanceForClientIpTests($remoteAddr, $httpForwardedFor, $trustedProxies);
+
+ $this->assertEquals($expected, $request->getClientIps());
+ }
+
+ /**
+ * @dataProvider getClientIpsForwardedProvider
+ */
+ public function testGetClientIpsForwarded($expected, $remoteAddr, $httpForwarded, $trustedProxies)
+ {
+ $request = $this->getRequestInstanceForClientIpsForwardedTests($remoteAddr, $httpForwarded, $trustedProxies);
+
+ $this->assertEquals($expected, $request->getClientIps());
+ }
+
+ public function getClientIpsForwardedProvider()
+ {
+ // $expected $remoteAddr $httpForwarded $trustedProxies
+ return array(
+ array(array('127.0.0.1'), '127.0.0.1', 'for="_gazonk"', null),
+ array(array('127.0.0.1'), '127.0.0.1', 'for="_gazonk"', array('127.0.0.1')),
+ array(array('88.88.88.88'), '127.0.0.1', 'for="88.88.88.88:80"', array('127.0.0.1')),
+ array(array('192.0.2.60'), '::1', 'for=192.0.2.60;proto=http;by=203.0.113.43', array('::1')),
+ array(array('2620:0:1cfe:face:b00c::3', '192.0.2.43'), '::1', 'for=192.0.2.43, for=2620:0:1cfe:face:b00c::3', array('::1')),
+ array(array('2001:db8:cafe::17'), '::1', 'for="[2001:db8:cafe::17]:4711', array('::1')),
+ );
+ }
+
+ public function getClientIpsProvider()
+ {
+ // $expected $remoteAddr $httpForwardedFor $trustedProxies
+ return array(
+ // simple IPv4
+ array(array('88.88.88.88'), '88.88.88.88', null, null),
+ // trust the IPv4 remote addr
+ array(array('88.88.88.88'), '88.88.88.88', null, array('88.88.88.88')),
+
+ // simple IPv6
+ array(array('::1'), '::1', null, null),
+ // trust the IPv6 remote addr
+ array(array('::1'), '::1', null, array('::1')),
+
+ // forwarded for with remote IPv4 addr not trusted
+ array(array('127.0.0.1'), '127.0.0.1', '88.88.88.88', null),
+ // forwarded for with remote IPv4 addr trusted
+ array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88', array('127.0.0.1')),
+ // forwarded for with remote IPv4 and all FF addrs trusted
+ array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88', array('127.0.0.1', '88.88.88.88')),
+ // forwarded for with remote IPv4 range trusted
+ array(array('88.88.88.88'), '123.45.67.89', '88.88.88.88', array('123.45.67.0/24')),
+
+ // forwarded for with remote IPv6 addr not trusted
+ array(array('1620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3', null),
+ // forwarded for with remote IPv6 addr trusted
+ array(array('2620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3')),
+ // forwarded for with remote IPv6 range trusted
+ array(array('88.88.88.88'), '2a01:198:603:0:396e:4789:8e99:890f', '88.88.88.88', array('2a01:198:603:0::/65')),
+
+ // multiple forwarded for with remote IPv4 addr trusted
+ array(array('88.88.88.88', '87.65.43.21', '127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89')),
+ // multiple forwarded for with remote IPv4 addr and some reverse proxies trusted
+ array(array('87.65.43.21', '127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89', '88.88.88.88')),
+ // multiple forwarded for with remote IPv4 addr and some reverse proxies trusted but in the middle
+ array(array('88.88.88.88', '127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89', '87.65.43.21')),
+ // multiple forwarded for with remote IPv4 addr and all reverse proxies trusted
+ array(array('127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89', '87.65.43.21', '88.88.88.88', '127.0.0.1')),
+
+ // multiple forwarded for with remote IPv6 addr trusted
+ array(array('2620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3,2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3')),
+ // multiple forwarded for with remote IPv6 addr and some reverse proxies trusted
+ array(array('3620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3,2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3')),
+ // multiple forwarded for with remote IPv4 addr and some reverse proxies trusted but in the middle
+ array(array('2620:0:1cfe:face:b00c::3', '4620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '4620:0:1cfe:face:b00c::3,3620:0:1cfe:face:b00c::3,2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3')),
+
+ // client IP with port
+ array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88:12345, 127.0.0.1', array('127.0.0.1')),
+
+ // invalid forwarded IP is ignored
+ array(array('88.88.88.88'), '127.0.0.1', 'unknown,88.88.88.88', array('127.0.0.1')),
+ array(array('88.88.88.88'), '127.0.0.1', '}__test|O:21:&quot;JDatabaseDriverMysqli&quot;:3:{s:2,88.88.88.88', array('127.0.0.1')),
+ );
+ }
+
+ /**
+ * @expectedException \Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException
+ * @dataProvider getClientIpsWithConflictingHeadersProvider
+ */
+ public function testGetClientIpsWithConflictingHeaders($httpForwarded, $httpXForwardedFor)
+ {
+ $request = new Request();
+
+ $server = array(
+ 'REMOTE_ADDR' => '88.88.88.88',
+ 'HTTP_FORWARDED' => $httpForwarded,
+ 'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor,
+ );
+
+ Request::setTrustedProxies(array('88.88.88.88'), Request::HEADER_X_FORWARDED_ALL | Request::HEADER_FORWARDED);
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $request->getClientIps();
+ }
+
+ /**
+ * @dataProvider getClientIpsWithConflictingHeadersProvider
+ */
+ public function testGetClientIpsOnlyXHttpForwardedForTrusted($httpForwarded, $httpXForwardedFor)
+ {
+ $request = new Request();
+
+ $server = array(
+ 'REMOTE_ADDR' => '88.88.88.88',
+ 'HTTP_FORWARDED' => $httpForwarded,
+ 'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor,
+ );
+
+ Request::setTrustedProxies(array('88.88.88.88'), Request::HEADER_X_FORWARDED_FOR);
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertSame(array_reverse(explode(',', $httpXForwardedFor)), $request->getClientIps());
+ }
+
+ public function getClientIpsWithConflictingHeadersProvider()
+ {
+ // $httpForwarded $httpXForwardedFor
+ return array(
+ array('for=87.65.43.21', '192.0.2.60'),
+ array('for=87.65.43.21, for=192.0.2.60', '192.0.2.60'),
+ array('for=192.0.2.60', '192.0.2.60,87.65.43.21'),
+ array('for="::face", for=192.0.2.60', '192.0.2.60,192.0.2.43'),
+ array('for=87.65.43.21, for=192.0.2.60', '192.0.2.60,87.65.43.21'),
+ );
+ }
+
+ /**
+ * @dataProvider getClientIpsWithAgreeingHeadersProvider
+ */
+ public function testGetClientIpsWithAgreeingHeaders($httpForwarded, $httpXForwardedFor, $expectedIps)
+ {
+ $request = new Request();
+
+ $server = array(
+ 'REMOTE_ADDR' => '88.88.88.88',
+ 'HTTP_FORWARDED' => $httpForwarded,
+ 'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor,
+ );
+
+ Request::setTrustedProxies(array('88.88.88.88'), Request::HEADER_X_FORWARDED_ALL);
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $clientIps = $request->getClientIps();
+
+ $this->assertSame($expectedIps, $clientIps);
+ }
+
+ public function getClientIpsWithAgreeingHeadersProvider()
+ {
+ // $httpForwarded $httpXForwardedFor
+ return array(
+ array('for="192.0.2.60"', '192.0.2.60', array('192.0.2.60')),
+ array('for=192.0.2.60, for=87.65.43.21', '192.0.2.60,87.65.43.21', array('87.65.43.21', '192.0.2.60')),
+ array('for="[::face]", for=192.0.2.60', '::face,192.0.2.60', array('192.0.2.60', '::face')),
+ array('for="192.0.2.60:80"', '192.0.2.60', array('192.0.2.60')),
+ array('for=192.0.2.60;proto=http;by=203.0.113.43', '192.0.2.60', array('192.0.2.60')),
+ array('for="[2001:db8:cafe::17]:4711"', '2001:db8:cafe::17', array('2001:db8:cafe::17')),
+ );
+ }
+
+ public function testGetContentWorksTwiceInDefaultMode()
+ {
+ $req = new Request();
+ $this->assertEquals('', $req->getContent());
+ $this->assertEquals('', $req->getContent());
+ }
+
+ public function testGetContentReturnsResource()
+ {
+ $req = new Request();
+ $retval = $req->getContent(true);
+ $this->assertInternalType('resource', $retval);
+ $this->assertEquals('', fread($retval, 1));
+ $this->assertTrue(feof($retval));
+ }
+
+ public function testGetContentReturnsResourceWhenContentSetInConstructor()
+ {
+ $req = new Request(array(), array(), array(), array(), array(), array(), 'MyContent');
+ $resource = $req->getContent(true);
+
+ $this->assertInternalType('resource', $resource);
+ $this->assertEquals('MyContent', stream_get_contents($resource));
+ }
+
+ public function testContentAsResource()
+ {
+ $resource = fopen('php://memory', 'r+');
+ fwrite($resource, 'My other content');
+ rewind($resource);
+
+ $req = new Request(array(), array(), array(), array(), array(), array(), $resource);
+ $this->assertEquals('My other content', stream_get_contents($req->getContent(true)));
+ $this->assertEquals('My other content', $req->getContent());
+ }
+
+ /**
+ * @expectedException \LogicException
+ * @dataProvider getContentCantBeCalledTwiceWithResourcesProvider
+ */
+ public function testGetContentCantBeCalledTwiceWithResources($first, $second)
+ {
+ if (\PHP_VERSION_ID >= 50600) {
+ $this->markTestSkipped('PHP >= 5.6 allows to open php://input several times.');
+ }
+
+ $req = new Request();
+ $req->getContent($first);
+ $req->getContent($second);
+ }
+
+ public function getContentCantBeCalledTwiceWithResourcesProvider()
+ {
+ return array(
+ 'Resource then fetch' => array(true, false),
+ 'Resource then resource' => array(true, true),
+ );
+ }
+
+ /**
+ * @dataProvider getContentCanBeCalledTwiceWithResourcesProvider
+ * @requires PHP 5.6
+ */
+ public function testGetContentCanBeCalledTwiceWithResources($first, $second)
+ {
+ $req = new Request();
+ $a = $req->getContent($first);
+ $b = $req->getContent($second);
+
+ if ($first) {
+ $a = stream_get_contents($a);
+ }
+
+ if ($second) {
+ $b = stream_get_contents($b);
+ }
+
+ $this->assertSame($a, $b);
+ }
+
+ public function getContentCanBeCalledTwiceWithResourcesProvider()
+ {
+ return array(
+ 'Fetch then fetch' => array(false, false),
+ 'Fetch then resource' => array(false, true),
+ 'Resource then fetch' => array(true, false),
+ 'Resource then resource' => array(true, true),
+ );
+ }
+
+ public function provideOverloadedMethods()
+ {
+ return array(
+ array('PUT'),
+ array('DELETE'),
+ array('PATCH'),
+ array('put'),
+ array('delete'),
+ array('patch'),
+ );
+ }
+
+ /**
+ * @dataProvider provideOverloadedMethods
+ */
+ public function testCreateFromGlobals($method)
+ {
+ $normalizedMethod = strtoupper($method);
+
+ $_GET['foo1'] = 'bar1';
+ $_POST['foo2'] = 'bar2';
+ $_COOKIE['foo3'] = 'bar3';
+ $_FILES['foo4'] = array('bar4');
+ $_SERVER['foo5'] = 'bar5';
+
+ $request = Request::createFromGlobals();
+ $this->assertEquals('bar1', $request->query->get('foo1'), '::fromGlobals() uses values from $_GET');
+ $this->assertEquals('bar2', $request->request->get('foo2'), '::fromGlobals() uses values from $_POST');
+ $this->assertEquals('bar3', $request->cookies->get('foo3'), '::fromGlobals() uses values from $_COOKIE');
+ $this->assertEquals(array('bar4'), $request->files->get('foo4'), '::fromGlobals() uses values from $_FILES');
+ $this->assertEquals('bar5', $request->server->get('foo5'), '::fromGlobals() uses values from $_SERVER');
+
+ unset($_GET['foo1'], $_POST['foo2'], $_COOKIE['foo3'], $_FILES['foo4'], $_SERVER['foo5']);
+
+ $_SERVER['REQUEST_METHOD'] = $method;
+ $_SERVER['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
+ $request = RequestContentProxy::createFromGlobals();
+ $this->assertEquals($normalizedMethod, $request->getMethod());
+ $this->assertEquals('mycontent', $request->request->get('content'));
+
+ unset($_SERVER['REQUEST_METHOD'], $_SERVER['CONTENT_TYPE']);
+
+ Request::createFromGlobals();
+ Request::enableHttpMethodParameterOverride();
+ $_POST['_method'] = $method;
+ $_POST['foo6'] = 'bar6';
+ $_SERVER['REQUEST_METHOD'] = 'PoSt';
+ $request = Request::createFromGlobals();
+ $this->assertEquals($normalizedMethod, $request->getMethod());
+ $this->assertEquals('POST', $request->getRealMethod());
+ $this->assertEquals('bar6', $request->request->get('foo6'));
+
+ unset($_POST['_method'], $_POST['foo6'], $_SERVER['REQUEST_METHOD']);
+ $this->disableHttpMethodParameterOverride();
+ }
+
+ public function testOverrideGlobals()
+ {
+ $request = new Request();
+ $request->initialize(array('foo' => 'bar'));
+
+ // as the Request::overrideGlobals really work, it erase $_SERVER, so we must backup it
+ $server = $_SERVER;
+
+ $request->overrideGlobals();
+
+ $this->assertEquals(array('foo' => 'bar'), $_GET);
+
+ $request->initialize(array(), array('foo' => 'bar'));
+ $request->overrideGlobals();
+
+ $this->assertEquals(array('foo' => 'bar'), $_POST);
+
+ $this->assertArrayNotHasKey('HTTP_X_FORWARDED_PROTO', $_SERVER);
+
+ $request->headers->set('X_FORWARDED_PROTO', 'https');
+
+ Request::setTrustedProxies(array('1.1.1.1'), Request::HEADER_X_FORWARDED_ALL);
+ $this->assertFalse($request->isSecure());
+ $request->server->set('REMOTE_ADDR', '1.1.1.1');
+ $this->assertTrue($request->isSecure());
+
+ $request->overrideGlobals();
+
+ $this->assertArrayHasKey('HTTP_X_FORWARDED_PROTO', $_SERVER);
+
+ $request->headers->set('CONTENT_TYPE', 'multipart/form-data');
+ $request->headers->set('CONTENT_LENGTH', 12345);
+
+ $request->overrideGlobals();
+
+ $this->assertArrayHasKey('CONTENT_TYPE', $_SERVER);
+ $this->assertArrayHasKey('CONTENT_LENGTH', $_SERVER);
+
+ $request->initialize(array('foo' => 'bar', 'baz' => 'foo'));
+ $request->query->remove('baz');
+
+ $request->overrideGlobals();
+
+ $this->assertEquals(array('foo' => 'bar'), $_GET);
+ $this->assertEquals('foo=bar', $_SERVER['QUERY_STRING']);
+ $this->assertEquals('foo=bar', $request->server->get('QUERY_STRING'));
+
+ // restore initial $_SERVER array
+ $_SERVER = $server;
+ }
+
+ public function testGetScriptName()
+ {
+ $request = new Request();
+ $this->assertEquals('', $request->getScriptName());
+
+ $server = array();
+ $server['SCRIPT_NAME'] = '/index.php';
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('/index.php', $request->getScriptName());
+
+ $server = array();
+ $server['ORIG_SCRIPT_NAME'] = '/frontend.php';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('/frontend.php', $request->getScriptName());
+
+ $server = array();
+ $server['SCRIPT_NAME'] = '/index.php';
+ $server['ORIG_SCRIPT_NAME'] = '/frontend.php';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('/index.php', $request->getScriptName());
+ }
+
+ public function testGetBasePath()
+ {
+ $request = new Request();
+ $this->assertEquals('', $request->getBasePath());
+
+ $server = array();
+ $server['SCRIPT_FILENAME'] = '/some/where/index.php';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+ $this->assertEquals('', $request->getBasePath());
+
+ $server = array();
+ $server['SCRIPT_FILENAME'] = '/some/where/index.php';
+ $server['SCRIPT_NAME'] = '/index.php';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('', $request->getBasePath());
+
+ $server = array();
+ $server['SCRIPT_FILENAME'] = '/some/where/index.php';
+ $server['PHP_SELF'] = '/index.php';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('', $request->getBasePath());
+
+ $server = array();
+ $server['SCRIPT_FILENAME'] = '/some/where/index.php';
+ $server['ORIG_SCRIPT_NAME'] = '/index.php';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('', $request->getBasePath());
+ }
+
+ public function testGetPathInfo()
+ {
+ $request = new Request();
+ $this->assertEquals('/', $request->getPathInfo());
+
+ $server = array();
+ $server['REQUEST_URI'] = '/path/info';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('/path/info', $request->getPathInfo());
+
+ $server = array();
+ $server['REQUEST_URI'] = '/path%20test/info';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('/path%20test/info', $request->getPathInfo());
+
+ $server = array();
+ $server['REQUEST_URI'] = '?a=b';
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals('/', $request->getPathInfo());
+ }
+
+ public function testGetParameterPrecedence()
+ {
+ $request = new Request();
+ $request->attributes->set('foo', 'attr');
+ $request->query->set('foo', 'query');
+ $request->request->set('foo', 'body');
+
+ $this->assertSame('attr', $request->get('foo'));
+
+ $request->attributes->remove('foo');
+ $this->assertSame('query', $request->get('foo'));
+
+ $request->query->remove('foo');
+ $this->assertSame('body', $request->get('foo'));
+
+ $request->request->remove('foo');
+ $this->assertNull($request->get('foo'));
+ }
+
+ public function testGetPreferredLanguage()
+ {
+ $request = new Request();
+ $this->assertNull($request->getPreferredLanguage());
+ $this->assertNull($request->getPreferredLanguage(array()));
+ $this->assertEquals('fr', $request->getPreferredLanguage(array('fr')));
+ $this->assertEquals('fr', $request->getPreferredLanguage(array('fr', 'en')));
+ $this->assertEquals('en', $request->getPreferredLanguage(array('en', 'fr')));
+ $this->assertEquals('fr-ch', $request->getPreferredLanguage(array('fr-ch', 'fr-fr')));
+
+ $request = new Request();
+ $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6');
+ $this->assertEquals('en', $request->getPreferredLanguage(array('en', 'en-us')));
+
+ $request = new Request();
+ $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6');
+ $this->assertEquals('en', $request->getPreferredLanguage(array('fr', 'en')));
+
+ $request = new Request();
+ $request->headers->set('Accept-language', 'zh, en-us; q=0.8');
+ $this->assertEquals('en', $request->getPreferredLanguage(array('fr', 'en')));
+
+ $request = new Request();
+ $request->headers->set('Accept-language', 'zh, en-us; q=0.8, fr-fr; q=0.6, fr; q=0.5');
+ $this->assertEquals('en', $request->getPreferredLanguage(array('fr', 'en')));
+ }
+
+ public function testIsXmlHttpRequest()
+ {
+ $request = new Request();
+ $this->assertFalse($request->isXmlHttpRequest());
+
+ $request->headers->set('X-Requested-With', 'XMLHttpRequest');
+ $this->assertTrue($request->isXmlHttpRequest());
+
+ $request->headers->remove('X-Requested-With');
+ $this->assertFalse($request->isXmlHttpRequest());
+ }
+
+ /**
+ * @requires extension intl
+ */
+ public function testIntlLocale()
+ {
+ $request = new Request();
+
+ $request->setDefaultLocale('fr');
+ $this->assertEquals('fr', $request->getLocale());
+ $this->assertEquals('fr', \Locale::getDefault());
+
+ $request->setLocale('en');
+ $this->assertEquals('en', $request->getLocale());
+ $this->assertEquals('en', \Locale::getDefault());
+
+ $request->setDefaultLocale('de');
+ $this->assertEquals('en', $request->getLocale());
+ $this->assertEquals('en', \Locale::getDefault());
+ }
+
+ public function testGetCharsets()
+ {
+ $request = new Request();
+ $this->assertEquals(array(), $request->getCharsets());
+ $request->headers->set('Accept-Charset', 'ISO-8859-1, US-ASCII, UTF-8; q=0.8, ISO-10646-UCS-2; q=0.6');
+ $this->assertEquals(array(), $request->getCharsets()); // testing caching
+
+ $request = new Request();
+ $request->headers->set('Accept-Charset', 'ISO-8859-1, US-ASCII, UTF-8; q=0.8, ISO-10646-UCS-2; q=0.6');
+ $this->assertEquals(array('ISO-8859-1', 'US-ASCII', 'UTF-8', 'ISO-10646-UCS-2'), $request->getCharsets());
+
+ $request = new Request();
+ $request->headers->set('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7');
+ $this->assertEquals(array('ISO-8859-1', 'utf-8', '*'), $request->getCharsets());
+ }
+
+ public function testGetEncodings()
+ {
+ $request = new Request();
+ $this->assertEquals(array(), $request->getEncodings());
+ $request->headers->set('Accept-Encoding', 'gzip,deflate,sdch');
+ $this->assertEquals(array(), $request->getEncodings()); // testing caching
+
+ $request = new Request();
+ $request->headers->set('Accept-Encoding', 'gzip,deflate,sdch');
+ $this->assertEquals(array('gzip', 'deflate', 'sdch'), $request->getEncodings());
+
+ $request = new Request();
+ $request->headers->set('Accept-Encoding', 'gzip;q=0.4,deflate;q=0.9,compress;q=0.7');
+ $this->assertEquals(array('deflate', 'compress', 'gzip'), $request->getEncodings());
+ }
+
+ public function testGetAcceptableContentTypes()
+ {
+ $request = new Request();
+ $this->assertEquals(array(), $request->getAcceptableContentTypes());
+ $request->headers->set('Accept', 'application/vnd.wap.wmlscriptc, text/vnd.wap.wml, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/html, multipart/mixed, */*');
+ $this->assertEquals(array(), $request->getAcceptableContentTypes()); // testing caching
+
+ $request = new Request();
+ $request->headers->set('Accept', 'application/vnd.wap.wmlscriptc, text/vnd.wap.wml, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/html, multipart/mixed, */*');
+ $this->assertEquals(array('application/vnd.wap.wmlscriptc', 'text/vnd.wap.wml', 'application/vnd.wap.xhtml+xml', 'application/xhtml+xml', 'text/html', 'multipart/mixed', '*/*'), $request->getAcceptableContentTypes());
+ }
+
+ public function testGetLanguages()
+ {
+ $request = new Request();
+ $this->assertEquals(array(), $request->getLanguages());
+
+ $request = new Request();
+ $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6');
+ $this->assertEquals(array('zh', 'en_US', 'en'), $request->getLanguages());
+ $this->assertEquals(array('zh', 'en_US', 'en'), $request->getLanguages());
+
+ $request = new Request();
+ $request->headers->set('Accept-language', 'zh, en-us; q=0.6, en; q=0.8');
+ $this->assertEquals(array('zh', 'en', 'en_US'), $request->getLanguages()); // Test out of order qvalues
+
+ $request = new Request();
+ $request->headers->set('Accept-language', 'zh, en, en-us');
+ $this->assertEquals(array('zh', 'en', 'en_US'), $request->getLanguages()); // Test equal weighting without qvalues
+
+ $request = new Request();
+ $request->headers->set('Accept-language', 'zh; q=0.6, en, en-us; q=0.6');
+ $this->assertEquals(array('en', 'zh', 'en_US'), $request->getLanguages()); // Test equal weighting with qvalues
+
+ $request = new Request();
+ $request->headers->set('Accept-language', 'zh, i-cherokee; q=0.6');
+ $this->assertEquals(array('zh', 'cherokee'), $request->getLanguages());
+ }
+
+ public function testGetRequestFormat()
+ {
+ $request = new Request();
+ $this->assertEquals('html', $request->getRequestFormat());
+
+ // Ensure that setting different default values over time is possible,
+ // aka. setRequestFormat determines the state.
+ $this->assertEquals('json', $request->getRequestFormat('json'));
+ $this->assertEquals('html', $request->getRequestFormat('html'));
+
+ $request = new Request();
+ $this->assertNull($request->getRequestFormat(null));
+
+ $request = new Request();
+ $this->assertNull($request->setRequestFormat('foo'));
+ $this->assertEquals('foo', $request->getRequestFormat(null));
+
+ $request = new Request(array('_format' => 'foo'));
+ $this->assertEquals('html', $request->getRequestFormat());
+ }
+
+ public function testHasSession()
+ {
+ $request = new Request();
+
+ $this->assertFalse($request->hasSession());
+ $request->setSession(new Session(new MockArraySessionStorage()));
+ $this->assertTrue($request->hasSession());
+ }
+
+ public function testGetSession()
+ {
+ $request = new Request();
+
+ $request->setSession(new Session(new MockArraySessionStorage()));
+ $this->assertTrue($request->hasSession());
+
+ $session = $request->getSession();
+ $this->assertObjectHasAttribute('storage', $session);
+ $this->assertObjectHasAttribute('flashName', $session);
+ $this->assertObjectHasAttribute('attributeName', $session);
+ }
+
+ public function testHasPreviousSession()
+ {
+ $request = new Request();
+
+ $this->assertFalse($request->hasPreviousSession());
+ $request->cookies->set('MOCKSESSID', 'foo');
+ $this->assertFalse($request->hasPreviousSession());
+ $request->setSession(new Session(new MockArraySessionStorage()));
+ $this->assertTrue($request->hasPreviousSession());
+ }
+
+ public function testToString()
+ {
+ $request = new Request();
+
+ $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6');
+ $request->cookies->set('Foo', 'Bar');
+
+ $asString = (string) $request;
+
+ $this->assertContains('Accept-Language: zh, en-us; q=0.8, en; q=0.6', $asString);
+ $this->assertContains('Cookie: Foo=Bar', $asString);
+
+ $request->cookies->set('Another', 'Cookie');
+
+ $asString = (string) $request;
+
+ $this->assertContains('Cookie: Foo=Bar; Another=Cookie', $asString);
+ }
+
+ public function testIsMethod()
+ {
+ $request = new Request();
+ $request->setMethod('POST');
+ $this->assertTrue($request->isMethod('POST'));
+ $this->assertTrue($request->isMethod('post'));
+ $this->assertFalse($request->isMethod('GET'));
+ $this->assertFalse($request->isMethod('get'));
+
+ $request->setMethod('GET');
+ $this->assertTrue($request->isMethod('GET'));
+ $this->assertTrue($request->isMethod('get'));
+ $this->assertFalse($request->isMethod('POST'));
+ $this->assertFalse($request->isMethod('post'));
+ }
+
+ /**
+ * @dataProvider getBaseUrlData
+ */
+ public function testGetBaseUrl($uri, $server, $expectedBaseUrl, $expectedPathInfo)
+ {
+ $request = Request::create($uri, 'GET', array(), array(), array(), $server);
+
+ $this->assertSame($expectedBaseUrl, $request->getBaseUrl(), 'baseUrl');
+ $this->assertSame($expectedPathInfo, $request->getPathInfo(), 'pathInfo');
+ }
+
+ public function getBaseUrlData()
+ {
+ return array(
+ array(
+ '/fruit/strawberry/1234index.php/blah',
+ array(
+ 'SCRIPT_FILENAME' => 'E:/Sites/cc-new/public_html/fruit/index.php',
+ 'SCRIPT_NAME' => '/fruit/index.php',
+ 'PHP_SELF' => '/fruit/index.php',
+ ),
+ '/fruit',
+ '/strawberry/1234index.php/blah',
+ ),
+ array(
+ '/fruit/strawberry/1234index.php/blah',
+ array(
+ 'SCRIPT_FILENAME' => 'E:/Sites/cc-new/public_html/index.php',
+ 'SCRIPT_NAME' => '/index.php',
+ 'PHP_SELF' => '/index.php',
+ ),
+ '',
+ '/fruit/strawberry/1234index.php/blah',
+ ),
+ array(
+ '/foo%20bar/',
+ array(
+ 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php',
+ 'SCRIPT_NAME' => '/foo bar/app.php',
+ 'PHP_SELF' => '/foo bar/app.php',
+ ),
+ '/foo%20bar',
+ '/',
+ ),
+ array(
+ '/foo%20bar/home',
+ array(
+ 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php',
+ 'SCRIPT_NAME' => '/foo bar/app.php',
+ 'PHP_SELF' => '/foo bar/app.php',
+ ),
+ '/foo%20bar',
+ '/home',
+ ),
+ array(
+ '/foo%20bar/app.php/home',
+ array(
+ 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php',
+ 'SCRIPT_NAME' => '/foo bar/app.php',
+ 'PHP_SELF' => '/foo bar/app.php',
+ ),
+ '/foo%20bar/app.php',
+ '/home',
+ ),
+ array(
+ '/foo%20bar/app.php/home%3Dbaz',
+ array(
+ 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php',
+ 'SCRIPT_NAME' => '/foo bar/app.php',
+ 'PHP_SELF' => '/foo bar/app.php',
+ ),
+ '/foo%20bar/app.php',
+ '/home%3Dbaz',
+ ),
+ array(
+ '/foo/bar+baz',
+ array(
+ 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo/app.php',
+ 'SCRIPT_NAME' => '/foo/app.php',
+ 'PHP_SELF' => '/foo/app.php',
+ ),
+ '/foo',
+ '/bar+baz',
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider urlencodedStringPrefixData
+ */
+ public function testUrlencodedStringPrefix($string, $prefix, $expect)
+ {
+ $request = new Request();
+
+ $me = new \ReflectionMethod($request, 'getUrlencodedPrefix');
+ $me->setAccessible(true);
+
+ $this->assertSame($expect, $me->invoke($request, $string, $prefix));
+ }
+
+ public function urlencodedStringPrefixData()
+ {
+ return array(
+ array('foo', 'foo', 'foo'),
+ array('fo%6f', 'foo', 'fo%6f'),
+ array('foo/bar', 'foo', 'foo'),
+ array('fo%6f/bar', 'foo', 'fo%6f'),
+ array('f%6f%6f/bar', 'foo', 'f%6f%6f'),
+ array('%66%6F%6F/bar', 'foo', '%66%6F%6F'),
+ array('fo+o/bar', 'fo+o', 'fo+o'),
+ array('fo%2Bo/bar', 'fo+o', 'fo%2Bo'),
+ );
+ }
+
+ private function disableHttpMethodParameterOverride()
+ {
+ $class = new \ReflectionClass('Symfony\\Component\\HttpFoundation\\Request');
+ $property = $class->getProperty('httpMethodParameterOverride');
+ $property->setAccessible(true);
+ $property->setValue(false);
+ }
+
+ private function getRequestInstanceForClientIpTests($remoteAddr, $httpForwardedFor, $trustedProxies)
+ {
+ $request = new Request();
+
+ $server = array('REMOTE_ADDR' => $remoteAddr);
+ if (null !== $httpForwardedFor) {
+ $server['HTTP_X_FORWARDED_FOR'] = $httpForwardedFor;
+ }
+
+ if ($trustedProxies) {
+ Request::setTrustedProxies($trustedProxies, Request::HEADER_X_FORWARDED_ALL);
+ }
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ return $request;
+ }
+
+ private function getRequestInstanceForClientIpsForwardedTests($remoteAddr, $httpForwarded, $trustedProxies)
+ {
+ $request = new Request();
+
+ $server = array('REMOTE_ADDR' => $remoteAddr);
+
+ if (null !== $httpForwarded) {
+ $server['HTTP_FORWARDED'] = $httpForwarded;
+ }
+
+ if ($trustedProxies) {
+ Request::setTrustedProxies($trustedProxies, Request::HEADER_FORWARDED);
+ }
+
+ $request->initialize(array(), array(), array(), array(), array(), $server);
+
+ return $request;
+ }
+
+ public function testTrustedProxiesXForwardedFor()
+ {
+ $request = Request::create('http://example.com/');
+ $request->server->set('REMOTE_ADDR', '3.3.3.3');
+ $request->headers->set('X_FORWARDED_FOR', '1.1.1.1, 2.2.2.2');
+ $request->headers->set('X_FORWARDED_HOST', 'foo.example.com:1234, real.example.com:8080');
+ $request->headers->set('X_FORWARDED_PROTO', 'https');
+ $request->headers->set('X_FORWARDED_PORT', 443);
+
+ // no trusted proxies
+ $this->assertEquals('3.3.3.3', $request->getClientIp());
+ $this->assertEquals('example.com', $request->getHost());
+ $this->assertEquals(80, $request->getPort());
+ $this->assertFalse($request->isSecure());
+
+ // disabling proxy trusting
+ Request::setTrustedProxies(array(), Request::HEADER_X_FORWARDED_ALL);
+ $this->assertEquals('3.3.3.3', $request->getClientIp());
+ $this->assertEquals('example.com', $request->getHost());
+ $this->assertEquals(80, $request->getPort());
+ $this->assertFalse($request->isSecure());
+
+ // request is forwarded by a non-trusted proxy
+ Request::setTrustedProxies(array('2.2.2.2'), Request::HEADER_X_FORWARDED_ALL);
+ $this->assertEquals('3.3.3.3', $request->getClientIp());
+ $this->assertEquals('example.com', $request->getHost());
+ $this->assertEquals(80, $request->getPort());
+ $this->assertFalse($request->isSecure());
+
+ // trusted proxy via setTrustedProxies()
+ Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2'), Request::HEADER_X_FORWARDED_ALL);
+ $this->assertEquals('1.1.1.1', $request->getClientIp());
+ $this->assertEquals('foo.example.com', $request->getHost());
+ $this->assertEquals(443, $request->getPort());
+ $this->assertTrue($request->isSecure());
+
+ // trusted proxy via setTrustedProxies()
+ Request::setTrustedProxies(array('3.3.3.4', '2.2.2.2'), Request::HEADER_X_FORWARDED_ALL);
+ $this->assertEquals('3.3.3.3', $request->getClientIp());
+ $this->assertEquals('example.com', $request->getHost());
+ $this->assertEquals(80, $request->getPort());
+ $this->assertFalse($request->isSecure());
+
+ // check various X_FORWARDED_PROTO header values
+ Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2'), Request::HEADER_X_FORWARDED_ALL);
+ $request->headers->set('X_FORWARDED_PROTO', 'ssl');
+ $this->assertTrue($request->isSecure());
+
+ $request->headers->set('X_FORWARDED_PROTO', 'https, http');
+ $this->assertTrue($request->isSecure());
+ }
+
+ /**
+ * @group legacy
+ * @expectedDeprecation The "Symfony\Component\HttpFoundation\Request::setTrustedHeaderName()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead.
+ */
+ public function testLegacyTrustedProxies()
+ {
+ $request = Request::create('http://example.com/');
+ $request->server->set('REMOTE_ADDR', '3.3.3.3');
+ $request->headers->set('X_FORWARDED_FOR', '1.1.1.1, 2.2.2.2');
+ $request->headers->set('X_FORWARDED_HOST', 'foo.example.com, real.example.com:8080');
+ $request->headers->set('X_FORWARDED_PROTO', 'https');
+ $request->headers->set('X_FORWARDED_PORT', 443);
+ $request->headers->set('X_MY_FOR', '3.3.3.3, 4.4.4.4');
+ $request->headers->set('X_MY_HOST', 'my.example.com');
+ $request->headers->set('X_MY_PROTO', 'http');
+ $request->headers->set('X_MY_PORT', 81);
+
+ Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2'), Request::HEADER_X_FORWARDED_ALL);
+
+ // custom header names
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X_MY_FOR');
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'X_MY_HOST');
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'X_MY_PORT');
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'X_MY_PROTO');
+ $this->assertEquals('4.4.4.4', $request->getClientIp());
+ $this->assertEquals('my.example.com', $request->getHost());
+ $this->assertEquals(81, $request->getPort());
+ $this->assertFalse($request->isSecure());
+
+ // disabling via empty header names
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, null);
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, null);
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, null);
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, null);
+ $this->assertEquals('3.3.3.3', $request->getClientIp());
+ $this->assertEquals('example.com', $request->getHost());
+ $this->assertEquals(80, $request->getPort());
+ $this->assertFalse($request->isSecure());
+
+ //reset
+ Request::setTrustedHeaderName(Request::HEADER_FORWARDED, 'FORWARDED');
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X_FORWARDED_FOR');
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'X_FORWARDED_HOST');
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'X_FORWARDED_PORT');
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'X_FORWARDED_PROTO');
+ }
+
+ public function testTrustedProxiesForwarded()
+ {
+ $request = Request::create('http://example.com/');
+ $request->server->set('REMOTE_ADDR', '3.3.3.3');
+ $request->headers->set('FORWARDED', 'for=1.1.1.1, host=foo.example.com:8080, proto=https, for=2.2.2.2, host=real.example.com:8080');
+
+ // no trusted proxies
+ $this->assertEquals('3.3.3.3', $request->getClientIp());
+ $this->assertEquals('example.com', $request->getHost());
+ $this->assertEquals(80, $request->getPort());
+ $this->assertFalse($request->isSecure());
+
+ // disabling proxy trusting
+ Request::setTrustedProxies(array(), Request::HEADER_FORWARDED);
+ $this->assertEquals('3.3.3.3', $request->getClientIp());
+ $this->assertEquals('example.com', $request->getHost());
+ $this->assertEquals(80, $request->getPort());
+ $this->assertFalse($request->isSecure());
+
+ // request is forwarded by a non-trusted proxy
+ Request::setTrustedProxies(array('2.2.2.2'), Request::HEADER_FORWARDED);
+ $this->assertEquals('3.3.3.3', $request->getClientIp());
+ $this->assertEquals('example.com', $request->getHost());
+ $this->assertEquals(80, $request->getPort());
+ $this->assertFalse($request->isSecure());
+
+ // trusted proxy via setTrustedProxies()
+ Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2'), Request::HEADER_FORWARDED);
+ $this->assertEquals('1.1.1.1', $request->getClientIp());
+ $this->assertEquals('foo.example.com', $request->getHost());
+ $this->assertEquals(8080, $request->getPort());
+ $this->assertTrue($request->isSecure());
+
+ // trusted proxy via setTrustedProxies()
+ Request::setTrustedProxies(array('3.3.3.4', '2.2.2.2'), Request::HEADER_FORWARDED);
+ $this->assertEquals('3.3.3.3', $request->getClientIp());
+ $this->assertEquals('example.com', $request->getHost());
+ $this->assertEquals(80, $request->getPort());
+ $this->assertFalse($request->isSecure());
+
+ // check various X_FORWARDED_PROTO header values
+ Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2'), Request::HEADER_FORWARDED);
+ $request->headers->set('FORWARDED', 'proto=ssl');
+ $this->assertTrue($request->isSecure());
+
+ $request->headers->set('FORWARDED', 'proto=https, proto=http');
+ $this->assertTrue($request->isSecure());
+ }
+
+ /**
+ * @group legacy
+ * @expectedException \InvalidArgumentException
+ */
+ public function testSetTrustedProxiesInvalidHeaderName()
+ {
+ Request::create('http://example.com/');
+ Request::setTrustedHeaderName('bogus name', 'X_MY_FOR');
+ }
+
+ /**
+ * @group legacy
+ * @expectedException \InvalidArgumentException
+ */
+ public function testGetTrustedProxiesInvalidHeaderName()
+ {
+ Request::create('http://example.com/');
+ Request::getTrustedHeaderName('bogus name');
+ }
+
+ /**
+ * @dataProvider iisRequestUriProvider
+ */
+ public function testIISRequestUri($headers, $server, $expectedRequestUri)
+ {
+ $request = new Request();
+ $request->headers->replace($headers);
+ $request->server->replace($server);
+
+ $this->assertEquals($expectedRequestUri, $request->getRequestUri(), '->getRequestUri() is correct');
+
+ $subRequestUri = '/bar/foo';
+ $subRequest = Request::create($subRequestUri, 'get', array(), array(), array(), $request->server->all());
+ $this->assertEquals($subRequestUri, $subRequest->getRequestUri(), '->getRequestUri() is correct in sub request');
+ }
+
+ public function iisRequestUriProvider()
+ {
+ return array(
+ array(
+ array(
+ 'X_ORIGINAL_URL' => '/foo/bar',
+ ),
+ array(),
+ '/foo/bar',
+ ),
+ array(
+ array(
+ 'X_REWRITE_URL' => '/foo/bar',
+ ),
+ array(),
+ '/foo/bar',
+ ),
+ array(
+ array(),
+ array(
+ 'IIS_WasUrlRewritten' => '1',
+ 'UNENCODED_URL' => '/foo/bar',
+ ),
+ '/foo/bar',
+ ),
+ array(
+ array(
+ 'X_ORIGINAL_URL' => '/foo/bar',
+ ),
+ array(
+ 'HTTP_X_ORIGINAL_URL' => '/foo/bar',
+ ),
+ '/foo/bar',
+ ),
+ array(
+ array(
+ 'X_ORIGINAL_URL' => '/foo/bar',
+ ),
+ array(
+ 'IIS_WasUrlRewritten' => '1',
+ 'UNENCODED_URL' => '/foo/bar',
+ ),
+ '/foo/bar',
+ ),
+ array(
+ array(
+ 'X_ORIGINAL_URL' => '/foo/bar',
+ ),
+ array(
+ 'HTTP_X_ORIGINAL_URL' => '/foo/bar',
+ 'IIS_WasUrlRewritten' => '1',
+ 'UNENCODED_URL' => '/foo/bar',
+ ),
+ '/foo/bar',
+ ),
+ array(
+ array(),
+ array(
+ 'ORIG_PATH_INFO' => '/foo/bar',
+ ),
+ '/foo/bar',
+ ),
+ array(
+ array(),
+ array(
+ 'ORIG_PATH_INFO' => '/foo/bar',
+ 'QUERY_STRING' => 'foo=bar',
+ ),
+ '/foo/bar?foo=bar',
+ ),
+ );
+ }
+
+ public function testTrustedHosts()
+ {
+ // create a request
+ $request = Request::create('/');
+
+ // no trusted host set -> no host check
+ $request->headers->set('host', 'evil.com');
+ $this->assertEquals('evil.com', $request->getHost());
+
+ // add a trusted domain and all its subdomains
+ Request::setTrustedHosts(array('^([a-z]{9}\.)?trusted\.com$'));
+
+ // untrusted host
+ $request->headers->set('host', 'evil.com');
+ try {
+ $request->getHost();
+ $this->fail('Request::getHost() should throw an exception when host is not trusted.');
+ } catch (SuspiciousOperationException $e) {
+ $this->assertEquals('Untrusted Host "evil.com".', $e->getMessage());
+ }
+
+ // trusted hosts
+ $request->headers->set('host', 'trusted.com');
+ $this->assertEquals('trusted.com', $request->getHost());
+ $this->assertEquals(80, $request->getPort());
+
+ $request->server->set('HTTPS', true);
+ $request->headers->set('host', 'trusted.com');
+ $this->assertEquals('trusted.com', $request->getHost());
+ $this->assertEquals(443, $request->getPort());
+ $request->server->set('HTTPS', false);
+
+ $request->headers->set('host', 'trusted.com:8000');
+ $this->assertEquals('trusted.com', $request->getHost());
+ $this->assertEquals(8000, $request->getPort());
+
+ $request->headers->set('host', 'subdomain.trusted.com');
+ $this->assertEquals('subdomain.trusted.com', $request->getHost());
+
+ // reset request for following tests
+ Request::setTrustedHosts(array());
+ }
+
+ public function testFactory()
+ {
+ Request::setFactory(function (array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) {
+ return new NewRequest();
+ });
+
+ $this->assertEquals('foo', Request::create('/')->getFoo());
+
+ Request::setFactory(null);
+ }
+
+ /**
+ * @dataProvider getLongHostNames
+ */
+ public function testVeryLongHosts($host)
+ {
+ $start = microtime(true);
+
+ $request = Request::create('/');
+ $request->headers->set('host', $host);
+ $this->assertEquals($host, $request->getHost());
+ $this->assertLessThan(5, microtime(true) - $start);
+ }
+
+ /**
+ * @dataProvider getHostValidities
+ */
+ public function testHostValidity($host, $isValid, $expectedHost = null, $expectedPort = null)
+ {
+ $request = Request::create('/');
+ $request->headers->set('host', $host);
+
+ if ($isValid) {
+ $this->assertSame($expectedHost ?: $host, $request->getHost());
+ if ($expectedPort) {
+ $this->assertSame($expectedPort, $request->getPort());
+ }
+ } else {
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(SuspiciousOperationException::class);
+ $this->expectExceptionMessage('Invalid Host');
+ } else {
+ $this->setExpectedException(SuspiciousOperationException::class, 'Invalid Host');
+ }
+
+ $request->getHost();
+ }
+ }
+
+ public function getHostValidities()
+ {
+ return array(
+ array('.a', false),
+ array('a..', false),
+ array('a.', true),
+ array("\xE9", false),
+ array('[::1]', true),
+ array('[::1]:80', true, '[::1]', 80),
+ array(str_repeat('.', 101), false),
+ );
+ }
+
+ public function getLongHostNames()
+ {
+ return array(
+ array('a'.str_repeat('.a', 40000)),
+ array(str_repeat(':', 101)),
+ );
+ }
+
+ /**
+ * @dataProvider methodIdempotentProvider
+ */
+ public function testMethodIdempotent($method, $idempotent)
+ {
+ $request = new Request();
+ $request->setMethod($method);
+ $this->assertEquals($idempotent, $request->isMethodIdempotent());
+ }
+
+ public function methodIdempotentProvider()
+ {
+ return array(
+ array('HEAD', true),
+ array('GET', true),
+ array('POST', false),
+ array('PUT', true),
+ array('PATCH', false),
+ array('DELETE', true),
+ array('PURGE', true),
+ array('OPTIONS', true),
+ array('TRACE', true),
+ array('CONNECT', false),
+ );
+ }
+
+ /**
+ * @dataProvider methodSafeProvider
+ */
+ public function testMethodSafe($method, $safe)
+ {
+ $request = new Request();
+ $request->setMethod($method);
+ $this->assertEquals($safe, $request->isMethodSafe(false));
+ }
+
+ public function methodSafeProvider()
+ {
+ return array(
+ array('HEAD', true),
+ array('GET', true),
+ array('POST', false),
+ array('PUT', false),
+ array('PATCH', false),
+ array('DELETE', false),
+ array('PURGE', false),
+ array('OPTIONS', true),
+ array('TRACE', true),
+ array('CONNECT', false),
+ );
+ }
+
+ /**
+ * @group legacy
+ * @expectedDeprecation Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is deprecated since Symfony 3.2 and will throw an exception in 4.0. Disable checking only for cacheable methods by calling the method with `false` as first argument or use the Request::isMethodCacheable() instead.
+ */
+ public function testMethodSafeChecksCacheable()
+ {
+ $request = new Request();
+ $request->setMethod('OPTIONS');
+ $this->assertFalse($request->isMethodSafe());
+ }
+
+ /**
+ * @dataProvider methodCacheableProvider
+ */
+ public function testMethodCacheable($method, $cacheable)
+ {
+ $request = new Request();
+ $request->setMethod($method);
+ $this->assertEquals($cacheable, $request->isMethodCacheable());
+ }
+
+ public function methodCacheableProvider()
+ {
+ return array(
+ array('HEAD', true),
+ array('GET', true),
+ array('POST', false),
+ array('PUT', false),
+ array('PATCH', false),
+ array('DELETE', false),
+ array('PURGE', false),
+ array('OPTIONS', false),
+ array('TRACE', false),
+ array('CONNECT', false),
+ );
+ }
+
+ /**
+ * @group legacy
+ */
+ public function testGetTrustedHeaderName()
+ {
+ Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_X_FORWARDED_ALL);
+
+ $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_FORWARDED));
+ $this->assertSame('X_FORWARDED_FOR', Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP));
+ $this->assertSame('X_FORWARDED_HOST', Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST));
+ $this->assertSame('X_FORWARDED_PORT', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT));
+ $this->assertSame('X_FORWARDED_PROTO', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO));
+
+ Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_FORWARDED);
+
+ $this->assertSame('FORWARDED', Request::getTrustedHeaderName(Request::HEADER_FORWARDED));
+ $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP));
+ $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST));
+ $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT));
+ $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO));
+
+ Request::setTrustedHeaderName(Request::HEADER_FORWARDED, 'A');
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'B');
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'C');
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'D');
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'E');
+
+ Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_FORWARDED);
+
+ $this->assertSame('A', Request::getTrustedHeaderName(Request::HEADER_FORWARDED));
+ $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP));
+ $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST));
+ $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT));
+ $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO));
+
+ Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_X_FORWARDED_ALL);
+
+ $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_FORWARDED));
+ $this->assertSame('B', Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP));
+ $this->assertSame('C', Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST));
+ $this->assertSame('D', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT));
+ $this->assertSame('E', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO));
+
+ Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_FORWARDED);
+
+ $this->assertSame('A', Request::getTrustedHeaderName(Request::HEADER_FORWARDED));
+
+ //reset
+ Request::setTrustedHeaderName(Request::HEADER_FORWARDED, 'FORWARDED');
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X_FORWARDED_FOR');
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'X_FORWARDED_HOST');
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'X_FORWARDED_PORT');
+ Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'X_FORWARDED_PROTO');
+ }
+
+ /**
+ * @dataProvider protocolVersionProvider
+ */
+ public function testProtocolVersion($serverProtocol, $trustedProxy, $via, $expected)
+ {
+ if ($trustedProxy) {
+ Request::setTrustedProxies(array('1.1.1.1'), -1);
+ }
+
+ $request = new Request();
+ $request->server->set('SERVER_PROTOCOL', $serverProtocol);
+ $request->server->set('REMOTE_ADDR', '1.1.1.1');
+ $request->headers->set('Via', $via);
+
+ $this->assertSame($expected, $request->getProtocolVersion());
+ }
+
+ public function protocolVersionProvider()
+ {
+ return array(
+ 'untrusted without via' => array('HTTP/2.0', false, '', 'HTTP/2.0'),
+ 'untrusted with via' => array('HTTP/2.0', false, '1.0 fred, 1.1 nowhere.com (Apache/1.1)', 'HTTP/2.0'),
+ 'trusted without via' => array('HTTP/2.0', true, '', 'HTTP/2.0'),
+ 'trusted with via' => array('HTTP/2.0', true, '1.0 fred, 1.1 nowhere.com (Apache/1.1)', 'HTTP/1.0'),
+ 'trusted with via and protocol name' => array('HTTP/2.0', true, 'HTTP/1.0 fred, HTTP/1.1 nowhere.com (Apache/1.1)', 'HTTP/1.0'),
+ 'trusted with broken via' => array('HTTP/2.0', true, 'HTTP/1^0 foo', 'HTTP/2.0'),
+ 'trusted with partially-broken via' => array('HTTP/2.0', true, '1.0 fred, foo', 'HTTP/1.0'),
+ );
+ }
+
+ public function nonstandardRequestsData()
+ {
+ return array(
+ array('', '', '/', 'http://host:8080/', ''),
+ array('/', '', '/', 'http://host:8080/', ''),
+
+ array('hello/app.php/x', '', '/x', 'http://host:8080/hello/app.php/x', '/hello', '/hello/app.php'),
+ array('/hello/app.php/x', '', '/x', 'http://host:8080/hello/app.php/x', '/hello', '/hello/app.php'),
+
+ array('', 'a=b', '/', 'http://host:8080/?a=b'),
+ array('?a=b', 'a=b', '/', 'http://host:8080/?a=b'),
+ array('/?a=b', 'a=b', '/', 'http://host:8080/?a=b'),
+
+ array('x', 'a=b', '/x', 'http://host:8080/x?a=b'),
+ array('x?a=b', 'a=b', '/x', 'http://host:8080/x?a=b'),
+ array('/x?a=b', 'a=b', '/x', 'http://host:8080/x?a=b'),
+
+ array('hello/x', '', '/x', 'http://host:8080/hello/x', '/hello'),
+ array('/hello/x', '', '/x', 'http://host:8080/hello/x', '/hello'),
+
+ array('hello/app.php/x', 'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'),
+ array('hello/app.php/x?a=b', 'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'),
+ array('/hello/app.php/x?a=b', 'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'),
+ );
+ }
+
+ /**
+ * @dataProvider nonstandardRequestsData
+ */
+ public function testNonstandardRequests($requestUri, $queryString, $expectedPathInfo, $expectedUri, $expectedBasePath = '', $expectedBaseUrl = null)
+ {
+ if (null === $expectedBaseUrl) {
+ $expectedBaseUrl = $expectedBasePath;
+ }
+
+ $server = array(
+ 'HTTP_HOST' => 'host:8080',
+ 'SERVER_PORT' => '8080',
+ 'QUERY_STRING' => $queryString,
+ 'PHP_SELF' => '/hello/app.php',
+ 'SCRIPT_FILENAME' => '/some/path/app.php',
+ 'REQUEST_URI' => $requestUri,
+ );
+
+ $request = new Request(array(), array(), array(), array(), array(), $server);
+
+ $this->assertEquals($expectedPathInfo, $request->getPathInfo());
+ $this->assertEquals($expectedUri, $request->getUri());
+ $this->assertEquals($queryString, $request->getQueryString());
+ $this->assertEquals(8080, $request->getPort());
+ $this->assertEquals('host:8080', $request->getHttpHost());
+ $this->assertEquals($expectedBaseUrl, $request->getBaseUrl());
+ $this->assertEquals($expectedBasePath, $request->getBasePath());
+ }
+}
+
+class RequestContentProxy extends Request
+{
+ public function getContent($asResource = false)
+ {
+ return http_build_query(array('_method' => 'PUT', 'content' => 'mycontent'), '', '&');
+ }
+}
+
+class NewRequest extends Request
+{
+ public function getFoo()
+ {
+ return 'foo';
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php b/assets/php/vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php
new file mode 100644
index 0000000..ce85535
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php
@@ -0,0 +1,363 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\ResponseHeaderBag;
+use Symfony\Component\HttpFoundation\Cookie;
+
+/**
+ * @group time-sensitive
+ */
+class ResponseHeaderBagTest extends TestCase
+{
+ public function testAllPreserveCase()
+ {
+ $headers = array(
+ 'fOo' => 'BAR',
+ 'ETag' => 'xyzzy',
+ 'Content-MD5' => 'Q2hlY2sgSW50ZWdyaXR5IQ==',
+ 'P3P' => 'CP="CAO PSA OUR"',
+ 'WWW-Authenticate' => 'Basic realm="WallyWorld"',
+ 'X-UA-Compatible' => 'IE=edge,chrome=1',
+ 'X-XSS-Protection' => '1; mode=block',
+ );
+
+ $bag = new ResponseHeaderBag($headers);
+ $allPreservedCase = $bag->allPreserveCase();
+
+ foreach (array_keys($headers) as $headerName) {
+ $this->assertArrayHasKey($headerName, $allPreservedCase, '->allPreserveCase() gets all input keys in original case');
+ }
+ }
+
+ public function testCacheControlHeader()
+ {
+ $bag = new ResponseHeaderBag(array());
+ $this->assertEquals('no-cache, private', $bag->get('Cache-Control'));
+ $this->assertTrue($bag->hasCacheControlDirective('no-cache'));
+
+ $bag = new ResponseHeaderBag(array('Cache-Control' => 'public'));
+ $this->assertEquals('public', $bag->get('Cache-Control'));
+ $this->assertTrue($bag->hasCacheControlDirective('public'));
+
+ $bag = new ResponseHeaderBag(array('ETag' => 'abcde'));
+ $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
+ $this->assertTrue($bag->hasCacheControlDirective('private'));
+ $this->assertTrue($bag->hasCacheControlDirective('must-revalidate'));
+ $this->assertFalse($bag->hasCacheControlDirective('max-age'));
+
+ $bag = new ResponseHeaderBag(array('Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT'));
+ $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
+
+ $bag = new ResponseHeaderBag(array(
+ 'Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT',
+ 'Cache-Control' => 'max-age=3600',
+ ));
+ $this->assertEquals('max-age=3600, private', $bag->get('Cache-Control'));
+
+ $bag = new ResponseHeaderBag(array('Last-Modified' => 'abcde'));
+ $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
+
+ $bag = new ResponseHeaderBag(array('Etag' => 'abcde', 'Last-Modified' => 'abcde'));
+ $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
+
+ $bag = new ResponseHeaderBag(array('cache-control' => 'max-age=100'));
+ $this->assertEquals('max-age=100, private', $bag->get('Cache-Control'));
+
+ $bag = new ResponseHeaderBag(array('cache-control' => 's-maxage=100'));
+ $this->assertEquals('s-maxage=100', $bag->get('Cache-Control'));
+
+ $bag = new ResponseHeaderBag(array('cache-control' => 'private, max-age=100'));
+ $this->assertEquals('max-age=100, private', $bag->get('Cache-Control'));
+
+ $bag = new ResponseHeaderBag(array('cache-control' => 'public, max-age=100'));
+ $this->assertEquals('max-age=100, public', $bag->get('Cache-Control'));
+
+ $bag = new ResponseHeaderBag();
+ $bag->set('Last-Modified', 'abcde');
+ $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
+
+ $bag = new ResponseHeaderBag();
+ $bag->set('Cache-Control', array('public', 'must-revalidate'));
+ $this->assertCount(1, $bag->get('Cache-Control', null, false));
+ $this->assertEquals('must-revalidate, public', $bag->get('Cache-Control'));
+
+ $bag = new ResponseHeaderBag();
+ $bag->set('Cache-Control', 'public');
+ $bag->set('Cache-Control', 'must-revalidate', false);
+ $this->assertCount(1, $bag->get('Cache-Control', null, false));
+ $this->assertEquals('must-revalidate, public', $bag->get('Cache-Control'));
+ }
+
+ public function testCacheControlClone()
+ {
+ $headers = array('foo' => 'bar');
+ $bag1 = new ResponseHeaderBag($headers);
+ $bag2 = new ResponseHeaderBag($bag1->allPreserveCase());
+ $this->assertEquals($bag1->allPreserveCase(), $bag2->allPreserveCase());
+ }
+
+ public function testToStringIncludesCookieHeaders()
+ {
+ $bag = new ResponseHeaderBag(array());
+ $bag->setCookie(new Cookie('foo', 'bar'));
+
+ $this->assertSetCookieHeader('foo=bar; path=/; httponly', $bag);
+
+ $bag->clearCookie('foo');
+
+ $this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; max-age=-31536001; path=/; httponly', $bag);
+ }
+
+ public function testClearCookieSecureNotHttpOnly()
+ {
+ $bag = new ResponseHeaderBag(array());
+
+ $bag->clearCookie('foo', '/', null, true, false);
+
+ $this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; max-age=-31536001; path=/; secure', $bag);
+ }
+
+ public function testReplace()
+ {
+ $bag = new ResponseHeaderBag(array());
+ $this->assertEquals('no-cache, private', $bag->get('Cache-Control'));
+ $this->assertTrue($bag->hasCacheControlDirective('no-cache'));
+
+ $bag->replace(array('Cache-Control' => 'public'));
+ $this->assertEquals('public', $bag->get('Cache-Control'));
+ $this->assertTrue($bag->hasCacheControlDirective('public'));
+ }
+
+ public function testReplaceWithRemove()
+ {
+ $bag = new ResponseHeaderBag(array());
+ $this->assertEquals('no-cache, private', $bag->get('Cache-Control'));
+ $this->assertTrue($bag->hasCacheControlDirective('no-cache'));
+
+ $bag->remove('Cache-Control');
+ $bag->replace(array());
+ $this->assertEquals('no-cache, private', $bag->get('Cache-Control'));
+ $this->assertTrue($bag->hasCacheControlDirective('no-cache'));
+ }
+
+ public function testCookiesWithSameNames()
+ {
+ $bag = new ResponseHeaderBag();
+ $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar'));
+ $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'foo.bar'));
+ $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'bar.foo'));
+ $bag->setCookie(new Cookie('foo', 'bar'));
+
+ $this->assertCount(4, $bag->getCookies());
+ $this->assertEquals('foo=bar; path=/path/foo; domain=foo.bar; httponly', $bag->get('set-cookie'));
+ $this->assertEquals(array(
+ 'foo=bar; path=/path/foo; domain=foo.bar; httponly',
+ 'foo=bar; path=/path/bar; domain=foo.bar; httponly',
+ 'foo=bar; path=/path/bar; domain=bar.foo; httponly',
+ 'foo=bar; path=/; httponly',
+ ), $bag->get('set-cookie', null, false));
+
+ $this->assertSetCookieHeader('foo=bar; path=/path/foo; domain=foo.bar; httponly', $bag);
+ $this->assertSetCookieHeader('foo=bar; path=/path/bar; domain=foo.bar; httponly', $bag);
+ $this->assertSetCookieHeader('foo=bar; path=/path/bar; domain=bar.foo; httponly', $bag);
+ $this->assertSetCookieHeader('foo=bar; path=/; httponly', $bag);
+
+ $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
+
+ $this->assertArrayHasKey('foo', $cookies['foo.bar']['/path/foo']);
+ $this->assertArrayHasKey('foo', $cookies['foo.bar']['/path/bar']);
+ $this->assertArrayHasKey('foo', $cookies['bar.foo']['/path/bar']);
+ $this->assertArrayHasKey('foo', $cookies['']['/']);
+ }
+
+ public function testRemoveCookie()
+ {
+ $bag = new ResponseHeaderBag();
+ $this->assertFalse($bag->has('set-cookie'));
+
+ $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar'));
+ $bag->setCookie(new Cookie('bar', 'foo', 0, '/path/bar', 'foo.bar'));
+ $this->assertTrue($bag->has('set-cookie'));
+
+ $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
+ $this->assertArrayHasKey('/path/foo', $cookies['foo.bar']);
+
+ $bag->removeCookie('foo', '/path/foo', 'foo.bar');
+ $this->assertTrue($bag->has('set-cookie'));
+
+ $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
+ $this->assertArrayNotHasKey('/path/foo', $cookies['foo.bar']);
+
+ $bag->removeCookie('bar', '/path/bar', 'foo.bar');
+ $this->assertFalse($bag->has('set-cookie'));
+
+ $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
+ $this->assertArrayNotHasKey('foo.bar', $cookies);
+ }
+
+ public function testRemoveCookieWithNullRemove()
+ {
+ $bag = new ResponseHeaderBag();
+ $bag->setCookie(new Cookie('foo', 'bar', 0));
+ $bag->setCookie(new Cookie('bar', 'foo', 0));
+
+ $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
+ $this->assertArrayHasKey('/', $cookies['']);
+
+ $bag->removeCookie('foo', null);
+ $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
+ $this->assertArrayNotHasKey('foo', $cookies['']['/']);
+
+ $bag->removeCookie('bar', null);
+ $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
+ $this->assertFalse(isset($cookies['']['/']['bar']));
+ }
+
+ public function testSetCookieHeader()
+ {
+ $bag = new ResponseHeaderBag();
+ $bag->set('set-cookie', 'foo=bar');
+ $this->assertEquals(array(new Cookie('foo', 'bar', 0, '/', null, false, false, true)), $bag->getCookies());
+
+ $bag->set('set-cookie', 'foo2=bar2', false);
+ $this->assertEquals(array(
+ new Cookie('foo', 'bar', 0, '/', null, false, false, true),
+ new Cookie('foo2', 'bar2', 0, '/', null, false, false, true),
+ ), $bag->getCookies());
+
+ $bag->remove('set-cookie');
+ $this->assertEquals(array(), $bag->getCookies());
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testGetCookiesWithInvalidArgument()
+ {
+ $bag = new ResponseHeaderBag();
+
+ $bag->getCookies('invalid_argument');
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testMakeDispositionInvalidDisposition()
+ {
+ $headers = new ResponseHeaderBag();
+
+ $headers->makeDisposition('invalid', 'foo.html');
+ }
+
+ /**
+ * @dataProvider provideMakeDisposition
+ */
+ public function testMakeDisposition($disposition, $filename, $filenameFallback, $expected)
+ {
+ $headers = new ResponseHeaderBag();
+
+ $this->assertEquals($expected, $headers->makeDisposition($disposition, $filename, $filenameFallback));
+ }
+
+ public function testToStringDoesntMessUpHeaders()
+ {
+ $headers = new ResponseHeaderBag();
+
+ $headers->set('Location', 'http://www.symfony.com');
+ $headers->set('Content-type', 'text/html');
+
+ (string) $headers;
+
+ $allHeaders = $headers->allPreserveCase();
+ $this->assertEquals(array('http://www.symfony.com'), $allHeaders['Location']);
+ $this->assertEquals(array('text/html'), $allHeaders['Content-type']);
+ }
+
+ public function provideMakeDisposition()
+ {
+ return array(
+ array('attachment', 'foo.html', 'foo.html', 'attachment; filename="foo.html"'),
+ array('attachment', 'foo.html', '', 'attachment; filename="foo.html"'),
+ array('attachment', 'foo bar.html', '', 'attachment; filename="foo bar.html"'),
+ array('attachment', 'foo "bar".html', '', 'attachment; filename="foo \\"bar\\".html"'),
+ array('attachment', 'foo%20bar.html', 'foo bar.html', 'attachment; filename="foo bar.html"; filename*=utf-8\'\'foo%2520bar.html'),
+ array('attachment', 'föö.html', 'foo.html', 'attachment; filename="foo.html"; filename*=utf-8\'\'f%C3%B6%C3%B6.html'),
+ );
+ }
+
+ /**
+ * @dataProvider provideMakeDispositionFail
+ * @expectedException \InvalidArgumentException
+ */
+ public function testMakeDispositionFail($disposition, $filename)
+ {
+ $headers = new ResponseHeaderBag();
+
+ $headers->makeDisposition($disposition, $filename);
+ }
+
+ public function provideMakeDispositionFail()
+ {
+ return array(
+ array('attachment', 'foo%20bar.html'),
+ array('attachment', 'foo/bar.html'),
+ array('attachment', '/foo.html'),
+ array('attachment', 'foo\bar.html'),
+ array('attachment', '\foo.html'),
+ array('attachment', 'föö.html'),
+ );
+ }
+
+ public function testDateHeaderAddedOnCreation()
+ {
+ $now = time();
+
+ $bag = new ResponseHeaderBag();
+ $this->assertTrue($bag->has('Date'));
+
+ $this->assertEquals($now, $bag->getDate('Date')->getTimestamp());
+ }
+
+ public function testDateHeaderCanBeSetOnCreation()
+ {
+ $someDate = 'Thu, 23 Mar 2017 09:15:12 GMT';
+ $bag = new ResponseHeaderBag(array('Date' => $someDate));
+
+ $this->assertEquals($someDate, $bag->get('Date'));
+ }
+
+ public function testDateHeaderWillBeRecreatedWhenRemoved()
+ {
+ $someDate = 'Thu, 23 Mar 2017 09:15:12 GMT';
+ $bag = new ResponseHeaderBag(array('Date' => $someDate));
+ $bag->remove('Date');
+
+ // a (new) Date header is still present
+ $this->assertTrue($bag->has('Date'));
+ $this->assertNotEquals($someDate, $bag->get('Date'));
+ }
+
+ public function testDateHeaderWillBeRecreatedWhenHeadersAreReplaced()
+ {
+ $bag = new ResponseHeaderBag();
+ $bag->replace(array());
+
+ $this->assertTrue($bag->has('Date'));
+ }
+
+ private function assertSetCookieHeader($expected, ResponseHeaderBag $actual)
+ {
+ $this->assertRegExp('#^Set-Cookie:\s+'.preg_quote($expected, '#').'$#m', str_replace("\r\n", "\n", (string) $actual));
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/ResponseTest.php b/assets/php/vendor/symfony/http-foundation/Tests/ResponseTest.php
new file mode 100644
index 0000000..350d972
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/ResponseTest.php
@@ -0,0 +1,1003 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * @group time-sensitive
+ */
+class ResponseTest extends ResponseTestCase
+{
+ public function testCreate()
+ {
+ $response = Response::create('foo', 301, array('Foo' => 'bar'));
+
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
+ $this->assertEquals(301, $response->getStatusCode());
+ $this->assertEquals('bar', $response->headers->get('foo'));
+ }
+
+ public function testToString()
+ {
+ $response = new Response();
+ $response = explode("\r\n", $response);
+ $this->assertEquals('HTTP/1.0 200 OK', $response[0]);
+ $this->assertEquals('Cache-Control: no-cache, private', $response[1]);
+ }
+
+ public function testClone()
+ {
+ $response = new Response();
+ $responseClone = clone $response;
+ $this->assertEquals($response, $responseClone);
+ }
+
+ public function testSendHeaders()
+ {
+ $response = new Response();
+ $headers = $response->sendHeaders();
+ $this->assertObjectHasAttribute('headers', $headers);
+ $this->assertObjectHasAttribute('content', $headers);
+ $this->assertObjectHasAttribute('version', $headers);
+ $this->assertObjectHasAttribute('statusCode', $headers);
+ $this->assertObjectHasAttribute('statusText', $headers);
+ $this->assertObjectHasAttribute('charset', $headers);
+ }
+
+ public function testSend()
+ {
+ $response = new Response();
+ $responseSend = $response->send();
+ $this->assertObjectHasAttribute('headers', $responseSend);
+ $this->assertObjectHasAttribute('content', $responseSend);
+ $this->assertObjectHasAttribute('version', $responseSend);
+ $this->assertObjectHasAttribute('statusCode', $responseSend);
+ $this->assertObjectHasAttribute('statusText', $responseSend);
+ $this->assertObjectHasAttribute('charset', $responseSend);
+ }
+
+ public function testGetCharset()
+ {
+ $response = new Response();
+ $charsetOrigin = 'UTF-8';
+ $response->setCharset($charsetOrigin);
+ $charset = $response->getCharset();
+ $this->assertEquals($charsetOrigin, $charset);
+ }
+
+ public function testIsCacheable()
+ {
+ $response = new Response();
+ $this->assertFalse($response->isCacheable());
+ }
+
+ public function testIsCacheableWithErrorCode()
+ {
+ $response = new Response('', 500);
+ $this->assertFalse($response->isCacheable());
+ }
+
+ public function testIsCacheableWithNoStoreDirective()
+ {
+ $response = new Response();
+ $response->headers->set('cache-control', 'private');
+ $this->assertFalse($response->isCacheable());
+ }
+
+ public function testIsCacheableWithSetTtl()
+ {
+ $response = new Response();
+ $response->setTtl(10);
+ $this->assertTrue($response->isCacheable());
+ }
+
+ public function testMustRevalidate()
+ {
+ $response = new Response();
+ $this->assertFalse($response->mustRevalidate());
+ }
+
+ public function testMustRevalidateWithMustRevalidateCacheControlHeader()
+ {
+ $response = new Response();
+ $response->headers->set('cache-control', 'must-revalidate');
+
+ $this->assertTrue($response->mustRevalidate());
+ }
+
+ public function testMustRevalidateWithProxyRevalidateCacheControlHeader()
+ {
+ $response = new Response();
+ $response->headers->set('cache-control', 'proxy-revalidate');
+
+ $this->assertTrue($response->mustRevalidate());
+ }
+
+ public function testSetNotModified()
+ {
+ $response = new Response();
+ $modified = $response->setNotModified();
+ $this->assertObjectHasAttribute('headers', $modified);
+ $this->assertObjectHasAttribute('content', $modified);
+ $this->assertObjectHasAttribute('version', $modified);
+ $this->assertObjectHasAttribute('statusCode', $modified);
+ $this->assertObjectHasAttribute('statusText', $modified);
+ $this->assertObjectHasAttribute('charset', $modified);
+ $this->assertEquals(304, $modified->getStatusCode());
+ }
+
+ public function testIsSuccessful()
+ {
+ $response = new Response();
+ $this->assertTrue($response->isSuccessful());
+ }
+
+ public function testIsNotModified()
+ {
+ $response = new Response();
+ $modified = $response->isNotModified(new Request());
+ $this->assertFalse($modified);
+ }
+
+ public function testIsNotModifiedNotSafe()
+ {
+ $request = Request::create('/homepage', 'POST');
+
+ $response = new Response();
+ $this->assertFalse($response->isNotModified($request));
+ }
+
+ public function testIsNotModifiedLastModified()
+ {
+ $before = 'Sun, 25 Aug 2013 18:32:31 GMT';
+ $modified = 'Sun, 25 Aug 2013 18:33:31 GMT';
+ $after = 'Sun, 25 Aug 2013 19:33:31 GMT';
+
+ $request = new Request();
+ $request->headers->set('If-Modified-Since', $modified);
+
+ $response = new Response();
+
+ $response->headers->set('Last-Modified', $modified);
+ $this->assertTrue($response->isNotModified($request));
+
+ $response->headers->set('Last-Modified', $before);
+ $this->assertTrue($response->isNotModified($request));
+
+ $response->headers->set('Last-Modified', $after);
+ $this->assertFalse($response->isNotModified($request));
+
+ $response->headers->set('Last-Modified', '');
+ $this->assertFalse($response->isNotModified($request));
+ }
+
+ public function testIsNotModifiedEtag()
+ {
+ $etagOne = 'randomly_generated_etag';
+ $etagTwo = 'randomly_generated_etag_2';
+
+ $request = new Request();
+ $request->headers->set('if_none_match', sprintf('%s, %s, %s', $etagOne, $etagTwo, 'etagThree'));
+
+ $response = new Response();
+
+ $response->headers->set('ETag', $etagOne);
+ $this->assertTrue($response->isNotModified($request));
+
+ $response->headers->set('ETag', $etagTwo);
+ $this->assertTrue($response->isNotModified($request));
+
+ $response->headers->set('ETag', '');
+ $this->assertFalse($response->isNotModified($request));
+ }
+
+ public function testIsNotModifiedLastModifiedAndEtag()
+ {
+ $before = 'Sun, 25 Aug 2013 18:32:31 GMT';
+ $modified = 'Sun, 25 Aug 2013 18:33:31 GMT';
+ $after = 'Sun, 25 Aug 2013 19:33:31 GMT';
+ $etag = 'randomly_generated_etag';
+
+ $request = new Request();
+ $request->headers->set('if_none_match', sprintf('%s, %s', $etag, 'etagThree'));
+ $request->headers->set('If-Modified-Since', $modified);
+
+ $response = new Response();
+
+ $response->headers->set('ETag', $etag);
+ $response->headers->set('Last-Modified', $after);
+ $this->assertFalse($response->isNotModified($request));
+
+ $response->headers->set('ETag', 'non-existent-etag');
+ $response->headers->set('Last-Modified', $before);
+ $this->assertFalse($response->isNotModified($request));
+
+ $response->headers->set('ETag', $etag);
+ $response->headers->set('Last-Modified', $modified);
+ $this->assertTrue($response->isNotModified($request));
+ }
+
+ public function testIsNotModifiedIfModifiedSinceAndEtagWithoutLastModified()
+ {
+ $modified = 'Sun, 25 Aug 2013 18:33:31 GMT';
+ $etag = 'randomly_generated_etag';
+
+ $request = new Request();
+ $request->headers->set('if_none_match', sprintf('%s, %s', $etag, 'etagThree'));
+ $request->headers->set('If-Modified-Since', $modified);
+
+ $response = new Response();
+
+ $response->headers->set('ETag', $etag);
+ $this->assertTrue($response->isNotModified($request));
+
+ $response->headers->set('ETag', 'non-existent-etag');
+ $this->assertFalse($response->isNotModified($request));
+ }
+
+ public function testIsValidateable()
+ {
+ $response = new Response('', 200, array('Last-Modified' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822)));
+ $this->assertTrue($response->isValidateable(), '->isValidateable() returns true if Last-Modified is present');
+
+ $response = new Response('', 200, array('ETag' => '"12345"'));
+ $this->assertTrue($response->isValidateable(), '->isValidateable() returns true if ETag is present');
+
+ $response = new Response();
+ $this->assertFalse($response->isValidateable(), '->isValidateable() returns false when no validator is present');
+ }
+
+ public function testGetDate()
+ {
+ $oneHourAgo = $this->createDateTimeOneHourAgo();
+ $response = new Response('', 200, array('Date' => $oneHourAgo->format(DATE_RFC2822)));
+ $date = $response->getDate();
+ $this->assertEquals($oneHourAgo->getTimestamp(), $date->getTimestamp(), '->getDate() returns the Date header if present');
+
+ $response = new Response();
+ $date = $response->getDate();
+ $this->assertEquals(time(), $date->getTimestamp(), '->getDate() returns the current Date if no Date header present');
+
+ $response = new Response('', 200, array('Date' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822)));
+ $now = $this->createDateTimeNow();
+ $response->headers->set('Date', $now->format(DATE_RFC2822));
+ $date = $response->getDate();
+ $this->assertEquals($now->getTimestamp(), $date->getTimestamp(), '->getDate() returns the date when the header has been modified');
+
+ $response = new Response('', 200);
+ $now = $this->createDateTimeNow();
+ $response->headers->remove('Date');
+ $date = $response->getDate();
+ $this->assertEquals($now->getTimestamp(), $date->getTimestamp(), '->getDate() returns the current Date when the header has previously been removed');
+ }
+
+ public function testGetMaxAge()
+ {
+ $response = new Response();
+ $response->headers->set('Cache-Control', 's-maxage=600, max-age=0');
+ $this->assertEquals(600, $response->getMaxAge(), '->getMaxAge() uses s-maxage cache control directive when present');
+
+ $response = new Response();
+ $response->headers->set('Cache-Control', 'max-age=600');
+ $this->assertEquals(600, $response->getMaxAge(), '->getMaxAge() falls back to max-age when no s-maxage directive present');
+
+ $response = new Response();
+ $response->headers->set('Cache-Control', 'must-revalidate');
+ $response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(DATE_RFC2822));
+ $this->assertEquals(3600, $response->getMaxAge(), '->getMaxAge() falls back to Expires when no max-age or s-maxage directive present');
+
+ $response = new Response();
+ $response->headers->set('Cache-Control', 'must-revalidate');
+ $response->headers->set('Expires', -1);
+ $this->assertEquals('Sat, 01 Jan 00 00:00:00 +0000', $response->getExpires()->format(DATE_RFC822));
+
+ $response = new Response();
+ $this->assertNull($response->getMaxAge(), '->getMaxAge() returns null if no freshness information available');
+ }
+
+ public function testSetSharedMaxAge()
+ {
+ $response = new Response();
+ $response->setSharedMaxAge(20);
+
+ $cacheControl = $response->headers->get('Cache-Control');
+ $this->assertEquals('public, s-maxage=20', $cacheControl);
+ }
+
+ public function testIsPrivate()
+ {
+ $response = new Response();
+ $response->headers->set('Cache-Control', 'max-age=100');
+ $response->setPrivate();
+ $this->assertEquals(100, $response->headers->getCacheControlDirective('max-age'), '->isPrivate() adds the private Cache-Control directive when set to true');
+ $this->assertTrue($response->headers->getCacheControlDirective('private'), '->isPrivate() adds the private Cache-Control directive when set to true');
+
+ $response = new Response();
+ $response->headers->set('Cache-Control', 'public, max-age=100');
+ $response->setPrivate();
+ $this->assertEquals(100, $response->headers->getCacheControlDirective('max-age'), '->isPrivate() adds the private Cache-Control directive when set to true');
+ $this->assertTrue($response->headers->getCacheControlDirective('private'), '->isPrivate() adds the private Cache-Control directive when set to true');
+ $this->assertFalse($response->headers->hasCacheControlDirective('public'), '->isPrivate() removes the public Cache-Control directive');
+ }
+
+ public function testExpire()
+ {
+ $response = new Response();
+ $response->headers->set('Cache-Control', 'max-age=100');
+ $response->expire();
+ $this->assertEquals(100, $response->headers->get('Age'), '->expire() sets the Age to max-age when present');
+
+ $response = new Response();
+ $response->headers->set('Cache-Control', 'max-age=100, s-maxage=500');
+ $response->expire();
+ $this->assertEquals(500, $response->headers->get('Age'), '->expire() sets the Age to s-maxage when both max-age and s-maxage are present');
+
+ $response = new Response();
+ $response->headers->set('Cache-Control', 'max-age=5, s-maxage=500');
+ $response->headers->set('Age', '1000');
+ $response->expire();
+ $this->assertEquals(1000, $response->headers->get('Age'), '->expire() does nothing when the response is already stale/expired');
+
+ $response = new Response();
+ $response->expire();
+ $this->assertFalse($response->headers->has('Age'), '->expire() does nothing when the response does not include freshness information');
+
+ $response = new Response();
+ $response->headers->set('Expires', -1);
+ $response->expire();
+ $this->assertNull($response->headers->get('Age'), '->expire() does not set the Age when the response is expired');
+ }
+
+ public function testGetTtl()
+ {
+ $response = new Response();
+ $this->assertNull($response->getTtl(), '->getTtl() returns null when no Expires or Cache-Control headers are present');
+
+ $response = new Response();
+ $response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(DATE_RFC2822));
+ $this->assertEquals(3600, $response->getTtl(), '->getTtl() uses the Expires header when no max-age is present');
+
+ $response = new Response();
+ $response->headers->set('Expires', $this->createDateTimeOneHourAgo()->format(DATE_RFC2822));
+ $this->assertLessThan(0, $response->getTtl(), '->getTtl() returns negative values when Expires is in past');
+
+ $response = new Response();
+ $response->headers->set('Expires', $response->getDate()->format(DATE_RFC2822));
+ $response->headers->set('Age', 0);
+ $this->assertSame(0, $response->getTtl(), '->getTtl() correctly handles zero');
+
+ $response = new Response();
+ $response->headers->set('Cache-Control', 'max-age=60');
+ $this->assertEquals(60, $response->getTtl(), '->getTtl() uses Cache-Control max-age when present');
+ }
+
+ public function testSetClientTtl()
+ {
+ $response = new Response();
+ $response->setClientTtl(10);
+
+ $this->assertEquals($response->getMaxAge(), $response->getAge() + 10);
+ }
+
+ public function testGetSetProtocolVersion()
+ {
+ $response = new Response();
+
+ $this->assertEquals('1.0', $response->getProtocolVersion());
+
+ $response->setProtocolVersion('1.1');
+
+ $this->assertEquals('1.1', $response->getProtocolVersion());
+ }
+
+ public function testGetVary()
+ {
+ $response = new Response();
+ $this->assertEquals(array(), $response->getVary(), '->getVary() returns an empty array if no Vary header is present');
+
+ $response = new Response();
+ $response->headers->set('Vary', 'Accept-Language');
+ $this->assertEquals(array('Accept-Language'), $response->getVary(), '->getVary() parses a single header name value');
+
+ $response = new Response();
+ $response->headers->set('Vary', 'Accept-Language User-Agent X-Foo');
+ $this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->getVary() parses multiple header name values separated by spaces');
+
+ $response = new Response();
+ $response->headers->set('Vary', 'Accept-Language,User-Agent, X-Foo');
+ $this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->getVary() parses multiple header name values separated by commas');
+
+ $vary = array('Accept-Language', 'User-Agent', 'X-foo');
+
+ $response = new Response();
+ $response->headers->set('Vary', $vary);
+ $this->assertEquals($vary, $response->getVary(), '->getVary() parses multiple header name values in arrays');
+
+ $response = new Response();
+ $response->headers->set('Vary', 'Accept-Language, User-Agent, X-foo');
+ $this->assertEquals($vary, $response->getVary(), '->getVary() parses multiple header name values in arrays');
+ }
+
+ public function testSetVary()
+ {
+ $response = new Response();
+ $response->setVary('Accept-Language');
+ $this->assertEquals(array('Accept-Language'), $response->getVary());
+
+ $response->setVary('Accept-Language, User-Agent');
+ $this->assertEquals(array('Accept-Language', 'User-Agent'), $response->getVary(), '->setVary() replace the vary header by default');
+
+ $response->setVary('X-Foo', false);
+ $this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->setVary() doesn\'t wipe out earlier Vary headers if replace is set to false');
+ }
+
+ public function testDefaultContentType()
+ {
+ $headerMock = $this->getMockBuilder('Symfony\Component\HttpFoundation\ResponseHeaderBag')->setMethods(array('set'))->getMock();
+ $headerMock->expects($this->at(0))
+ ->method('set')
+ ->with('Content-Type', 'text/html');
+ $headerMock->expects($this->at(1))
+ ->method('set')
+ ->with('Content-Type', 'text/html; charset=UTF-8');
+
+ $response = new Response('foo');
+ $response->headers = $headerMock;
+
+ $response->prepare(new Request());
+ }
+
+ public function testContentTypeCharset()
+ {
+ $response = new Response();
+ $response->headers->set('Content-Type', 'text/css');
+
+ // force fixContentType() to be called
+ $response->prepare(new Request());
+
+ $this->assertEquals('text/css; charset=UTF-8', $response->headers->get('Content-Type'));
+ }
+
+ public function testPrepareDoesNothingIfContentTypeIsSet()
+ {
+ $response = new Response('foo');
+ $response->headers->set('Content-Type', 'text/plain');
+
+ $response->prepare(new Request());
+
+ $this->assertEquals('text/plain; charset=UTF-8', $response->headers->get('content-type'));
+ }
+
+ public function testPrepareDoesNothingIfRequestFormatIsNotDefined()
+ {
+ $response = new Response('foo');
+
+ $response->prepare(new Request());
+
+ $this->assertEquals('text/html; charset=UTF-8', $response->headers->get('content-type'));
+ }
+
+ public function testPrepareSetContentType()
+ {
+ $response = new Response('foo');
+ $request = Request::create('/');
+ $request->setRequestFormat('json');
+
+ $response->prepare($request);
+
+ $this->assertEquals('application/json', $response->headers->get('content-type'));
+ }
+
+ public function testPrepareRemovesContentForHeadRequests()
+ {
+ $response = new Response('foo');
+ $request = Request::create('/', 'HEAD');
+
+ $length = 12345;
+ $response->headers->set('Content-Length', $length);
+ $response->prepare($request);
+
+ $this->assertEquals('', $response->getContent());
+ $this->assertEquals($length, $response->headers->get('Content-Length'), 'Content-Length should be as if it was GET; see RFC2616 14.13');
+ }
+
+ public function testPrepareRemovesContentForInformationalResponse()
+ {
+ $response = new Response('foo');
+ $request = Request::create('/');
+
+ $response->setContent('content');
+ $response->setStatusCode(101);
+ $response->prepare($request);
+ $this->assertEquals('', $response->getContent());
+ $this->assertFalse($response->headers->has('Content-Type'));
+ $this->assertFalse($response->headers->has('Content-Type'));
+
+ $response->setContent('content');
+ $response->setStatusCode(304);
+ $response->prepare($request);
+ $this->assertEquals('', $response->getContent());
+ $this->assertFalse($response->headers->has('Content-Type'));
+ $this->assertFalse($response->headers->has('Content-Length'));
+ }
+
+ public function testPrepareRemovesContentLength()
+ {
+ $response = new Response('foo');
+ $request = Request::create('/');
+
+ $response->headers->set('Content-Length', 12345);
+ $response->prepare($request);
+ $this->assertEquals(12345, $response->headers->get('Content-Length'));
+
+ $response->headers->set('Transfer-Encoding', 'chunked');
+ $response->prepare($request);
+ $this->assertFalse($response->headers->has('Content-Length'));
+ }
+
+ public function testPrepareSetsPragmaOnHttp10Only()
+ {
+ $request = Request::create('/', 'GET');
+ $request->server->set('SERVER_PROTOCOL', 'HTTP/1.0');
+
+ $response = new Response('foo');
+ $response->prepare($request);
+ $this->assertEquals('no-cache', $response->headers->get('pragma'));
+ $this->assertEquals('-1', $response->headers->get('expires'));
+
+ $request->server->set('SERVER_PROTOCOL', 'HTTP/1.1');
+ $response = new Response('foo');
+ $response->prepare($request);
+ $this->assertFalse($response->headers->has('pragma'));
+ $this->assertFalse($response->headers->has('expires'));
+ }
+
+ public function testSetCache()
+ {
+ $response = new Response();
+ //array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public')
+ try {
+ $response->setCache(array('wrong option' => 'value'));
+ $this->fail('->setCache() throws an InvalidArgumentException if an option is not supported');
+ } catch (\Exception $e) {
+ $this->assertInstanceOf('InvalidArgumentException', $e, '->setCache() throws an InvalidArgumentException if an option is not supported');
+ $this->assertContains('"wrong option"', $e->getMessage());
+ }
+
+ $options = array('etag' => '"whatever"');
+ $response->setCache($options);
+ $this->assertEquals($response->getEtag(), '"whatever"');
+
+ $now = $this->createDateTimeNow();
+ $options = array('last_modified' => $now);
+ $response->setCache($options);
+ $this->assertEquals($response->getLastModified()->getTimestamp(), $now->getTimestamp());
+
+ $options = array('max_age' => 100);
+ $response->setCache($options);
+ $this->assertEquals($response->getMaxAge(), 100);
+
+ $options = array('s_maxage' => 200);
+ $response->setCache($options);
+ $this->assertEquals($response->getMaxAge(), 200);
+
+ $this->assertTrue($response->headers->hasCacheControlDirective('public'));
+ $this->assertFalse($response->headers->hasCacheControlDirective('private'));
+
+ $response->setCache(array('public' => true));
+ $this->assertTrue($response->headers->hasCacheControlDirective('public'));
+ $this->assertFalse($response->headers->hasCacheControlDirective('private'));
+
+ $response->setCache(array('public' => false));
+ $this->assertFalse($response->headers->hasCacheControlDirective('public'));
+ $this->assertTrue($response->headers->hasCacheControlDirective('private'));
+
+ $response->setCache(array('private' => true));
+ $this->assertFalse($response->headers->hasCacheControlDirective('public'));
+ $this->assertTrue($response->headers->hasCacheControlDirective('private'));
+
+ $response->setCache(array('private' => false));
+ $this->assertTrue($response->headers->hasCacheControlDirective('public'));
+ $this->assertFalse($response->headers->hasCacheControlDirective('private'));
+
+ $response->setCache(array('immutable' => true));
+ $this->assertTrue($response->headers->hasCacheControlDirective('immutable'));
+
+ $response->setCache(array('immutable' => false));
+ $this->assertFalse($response->headers->hasCacheControlDirective('immutable'));
+ }
+
+ public function testSendContent()
+ {
+ $response = new Response('test response rendering', 200);
+
+ ob_start();
+ $response->sendContent();
+ $string = ob_get_clean();
+ $this->assertContains('test response rendering', $string);
+ }
+
+ public function testSetPublic()
+ {
+ $response = new Response();
+ $response->setPublic();
+
+ $this->assertTrue($response->headers->hasCacheControlDirective('public'));
+ $this->assertFalse($response->headers->hasCacheControlDirective('private'));
+ }
+
+ public function testSetImmutable()
+ {
+ $response = new Response();
+ $response->setImmutable();
+
+ $this->assertTrue($response->headers->hasCacheControlDirective('immutable'));
+ }
+
+ public function testIsImmutable()
+ {
+ $response = new Response();
+ $response->setImmutable();
+
+ $this->assertTrue($response->isImmutable());
+ }
+
+ public function testSetExpires()
+ {
+ $response = new Response();
+ $response->setExpires(null);
+
+ $this->assertNull($response->getExpires(), '->setExpires() remove the header when passed null');
+
+ $now = $this->createDateTimeNow();
+ $response->setExpires($now);
+
+ $this->assertEquals($response->getExpires()->getTimestamp(), $now->getTimestamp());
+ }
+
+ public function testSetLastModified()
+ {
+ $response = new Response();
+ $response->setLastModified($this->createDateTimeNow());
+ $this->assertNotNull($response->getLastModified());
+
+ $response->setLastModified(null);
+ $this->assertNull($response->getLastModified());
+ }
+
+ public function testIsInvalid()
+ {
+ $response = new Response();
+
+ try {
+ $response->setStatusCode(99);
+ $this->fail();
+ } catch (\InvalidArgumentException $e) {
+ $this->assertTrue($response->isInvalid());
+ }
+
+ try {
+ $response->setStatusCode(650);
+ $this->fail();
+ } catch (\InvalidArgumentException $e) {
+ $this->assertTrue($response->isInvalid());
+ }
+
+ $response = new Response('', 200);
+ $this->assertFalse($response->isInvalid());
+ }
+
+ /**
+ * @dataProvider getStatusCodeFixtures
+ */
+ public function testSetStatusCode($code, $text, $expectedText)
+ {
+ $response = new Response();
+
+ $response->setStatusCode($code, $text);
+
+ $statusText = new \ReflectionProperty($response, 'statusText');
+ $statusText->setAccessible(true);
+
+ $this->assertEquals($expectedText, $statusText->getValue($response));
+ }
+
+ public function getStatusCodeFixtures()
+ {
+ return array(
+ array('200', null, 'OK'),
+ array('200', false, ''),
+ array('200', 'foo', 'foo'),
+ array('199', null, 'unknown status'),
+ array('199', false, ''),
+ array('199', 'foo', 'foo'),
+ );
+ }
+
+ public function testIsInformational()
+ {
+ $response = new Response('', 100);
+ $this->assertTrue($response->isInformational());
+
+ $response = new Response('', 200);
+ $this->assertFalse($response->isInformational());
+ }
+
+ public function testIsRedirectRedirection()
+ {
+ foreach (array(301, 302, 303, 307) as $code) {
+ $response = new Response('', $code);
+ $this->assertTrue($response->isRedirection());
+ $this->assertTrue($response->isRedirect());
+ }
+
+ $response = new Response('', 304);
+ $this->assertTrue($response->isRedirection());
+ $this->assertFalse($response->isRedirect());
+
+ $response = new Response('', 200);
+ $this->assertFalse($response->isRedirection());
+ $this->assertFalse($response->isRedirect());
+
+ $response = new Response('', 404);
+ $this->assertFalse($response->isRedirection());
+ $this->assertFalse($response->isRedirect());
+
+ $response = new Response('', 301, array('Location' => '/good-uri'));
+ $this->assertFalse($response->isRedirect('/bad-uri'));
+ $this->assertTrue($response->isRedirect('/good-uri'));
+ }
+
+ public function testIsNotFound()
+ {
+ $response = new Response('', 404);
+ $this->assertTrue($response->isNotFound());
+
+ $response = new Response('', 200);
+ $this->assertFalse($response->isNotFound());
+ }
+
+ public function testIsEmpty()
+ {
+ foreach (array(204, 304) as $code) {
+ $response = new Response('', $code);
+ $this->assertTrue($response->isEmpty());
+ }
+
+ $response = new Response('', 200);
+ $this->assertFalse($response->isEmpty());
+ }
+
+ public function testIsForbidden()
+ {
+ $response = new Response('', 403);
+ $this->assertTrue($response->isForbidden());
+
+ $response = new Response('', 200);
+ $this->assertFalse($response->isForbidden());
+ }
+
+ public function testIsOk()
+ {
+ $response = new Response('', 200);
+ $this->assertTrue($response->isOk());
+
+ $response = new Response('', 404);
+ $this->assertFalse($response->isOk());
+ }
+
+ public function testIsServerOrClientError()
+ {
+ $response = new Response('', 404);
+ $this->assertTrue($response->isClientError());
+ $this->assertFalse($response->isServerError());
+
+ $response = new Response('', 500);
+ $this->assertFalse($response->isClientError());
+ $this->assertTrue($response->isServerError());
+ }
+
+ public function testHasVary()
+ {
+ $response = new Response();
+ $this->assertFalse($response->hasVary());
+
+ $response->setVary('User-Agent');
+ $this->assertTrue($response->hasVary());
+ }
+
+ public function testSetEtag()
+ {
+ $response = new Response('', 200, array('ETag' => '"12345"'));
+ $response->setEtag();
+
+ $this->assertNull($response->headers->get('Etag'), '->setEtag() removes Etags when call with null');
+ }
+
+ /**
+ * @dataProvider validContentProvider
+ */
+ public function testSetContent($content)
+ {
+ $response = new Response();
+ $response->setContent($content);
+ $this->assertEquals((string) $content, $response->getContent());
+ }
+
+ /**
+ * @expectedException \UnexpectedValueException
+ * @dataProvider invalidContentProvider
+ */
+ public function testSetContentInvalid($content)
+ {
+ $response = new Response();
+ $response->setContent($content);
+ }
+
+ public function testSettersAreChainable()
+ {
+ $response = new Response();
+
+ $setters = array(
+ 'setProtocolVersion' => '1.0',
+ 'setCharset' => 'UTF-8',
+ 'setPublic' => null,
+ 'setPrivate' => null,
+ 'setDate' => $this->createDateTimeNow(),
+ 'expire' => null,
+ 'setMaxAge' => 1,
+ 'setSharedMaxAge' => 1,
+ 'setTtl' => 1,
+ 'setClientTtl' => 1,
+ );
+
+ foreach ($setters as $setter => $arg) {
+ $this->assertEquals($response, $response->{$setter}($arg));
+ }
+ }
+
+ public function testNoDeprecationsAreTriggered()
+ {
+ new DefaultResponse();
+ $this->getMockBuilder(Response::class)->getMock();
+
+ // we just need to ensure that subclasses of Response can be created without any deprecations
+ // being triggered if the subclass does not override any final methods
+ $this->addToAssertionCount(1);
+ }
+
+ public function validContentProvider()
+ {
+ return array(
+ 'obj' => array(new StringableObject()),
+ 'string' => array('Foo'),
+ 'int' => array(2),
+ );
+ }
+
+ public function invalidContentProvider()
+ {
+ return array(
+ 'obj' => array(new \stdClass()),
+ 'array' => array(array()),
+ 'bool' => array(true, '1'),
+ );
+ }
+
+ protected function createDateTimeOneHourAgo()
+ {
+ return $this->createDateTimeNow()->sub(new \DateInterval('PT1H'));
+ }
+
+ protected function createDateTimeOneHourLater()
+ {
+ return $this->createDateTimeNow()->add(new \DateInterval('PT1H'));
+ }
+
+ protected function createDateTimeNow()
+ {
+ $date = new \DateTime();
+
+ return $date->setTimestamp(time());
+ }
+
+ protected function provideResponse()
+ {
+ return new Response();
+ }
+
+ /**
+ * @see http://github.com/zendframework/zend-diactoros for the canonical source repository
+ *
+ * @author Fábio Pacheco
+ * @copyright Copyright (c) 2015-2016 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
+ */
+ public function ianaCodesReasonPhrasesProvider()
+ {
+ if (!in_array('https', stream_get_wrappers(), true)) {
+ $this->markTestSkipped('The "https" wrapper is not available');
+ }
+
+ $ianaHttpStatusCodes = new \DOMDocument();
+
+ libxml_set_streams_context(stream_context_create(array(
+ 'http' => array(
+ 'method' => 'GET',
+ 'timeout' => 30,
+ ),
+ )));
+
+ $ianaHttpStatusCodes->load('https://www.iana.org/assignments/http-status-codes/http-status-codes.xml');
+ if (!$ianaHttpStatusCodes->relaxNGValidate(__DIR__.'/schema/http-status-codes.rng')) {
+ self::fail('Invalid IANA\'s HTTP status code list.');
+ }
+
+ $ianaCodesReasonPhrases = array();
+
+ $xpath = new \DOMXPath($ianaHttpStatusCodes);
+ $xpath->registerNamespace('ns', 'http://www.iana.org/assignments');
+
+ $records = $xpath->query('//ns:record');
+ foreach ($records as $record) {
+ $value = $xpath->query('.//ns:value', $record)->item(0)->nodeValue;
+ $description = $xpath->query('.//ns:description', $record)->item(0)->nodeValue;
+
+ if (in_array($description, array('Unassigned', '(Unused)'), true)) {
+ continue;
+ }
+
+ if (preg_match('/^([0-9]+)\s*\-\s*([0-9]+)$/', $value, $matches)) {
+ for ($value = $matches[1]; $value <= $matches[2]; ++$value) {
+ $ianaCodesReasonPhrases[] = array($value, $description);
+ }
+ } else {
+ $ianaCodesReasonPhrases[] = array($value, $description);
+ }
+ }
+
+ return $ianaCodesReasonPhrases;
+ }
+
+ /**
+ * @dataProvider ianaCodesReasonPhrasesProvider
+ */
+ public function testReasonPhraseDefaultsAgainstIana($code, $reasonPhrase)
+ {
+ $this->assertEquals($reasonPhrase, Response::$statusTexts[$code]);
+ }
+}
+
+class StringableObject
+{
+ public function __toString()
+ {
+ return 'Foo';
+ }
+}
+
+class DefaultResponse extends Response
+{
+}
+
+class ExtendedResponse extends Response
+{
+ public function setLastModified(\DateTime $date = null)
+ {
+ }
+
+ public function getDate()
+ {
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/ResponseTestCase.php b/assets/php/vendor/symfony/http-foundation/Tests/ResponseTestCase.php
new file mode 100644
index 0000000..4ead34c
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/ResponseTestCase.php
@@ -0,0 +1,89 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Request;
+
+abstract class ResponseTestCase extends TestCase
+{
+ public function testNoCacheControlHeaderOnAttachmentUsingHTTPSAndMSIE()
+ {
+ // Check for HTTPS and IE 8
+ $request = new Request();
+ $request->server->set('HTTPS', true);
+ $request->server->set('HTTP_USER_AGENT', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)');
+
+ $response = $this->provideResponse();
+ $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
+ $response->prepare($request);
+
+ $this->assertFalse($response->headers->has('Cache-Control'));
+
+ // Check for IE 10 and HTTPS
+ $request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)');
+
+ $response = $this->provideResponse();
+ $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
+ $response->prepare($request);
+
+ $this->assertTrue($response->headers->has('Cache-Control'));
+
+ // Check for IE 9 and HTTPS
+ $request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.1; Trident/5.0)');
+
+ $response = $this->provideResponse();
+ $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
+ $response->prepare($request);
+
+ $this->assertTrue($response->headers->has('Cache-Control'));
+
+ // Check for IE 9 and HTTP
+ $request->server->set('HTTPS', false);
+
+ $response = $this->provideResponse();
+ $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
+ $response->prepare($request);
+
+ $this->assertTrue($response->headers->has('Cache-Control'));
+
+ // Check for IE 8 and HTTP
+ $request->server->set('HTTP_USER_AGENT', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)');
+
+ $response = $this->provideResponse();
+ $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
+ $response->prepare($request);
+
+ $this->assertTrue($response->headers->has('Cache-Control'));
+
+ // Check for non-IE and HTTPS
+ $request->server->set('HTTPS', true);
+ $request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.60 Safari/537.17');
+
+ $response = $this->provideResponse();
+ $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
+ $response->prepare($request);
+
+ $this->assertTrue($response->headers->has('Cache-Control'));
+
+ // Check for non-IE and HTTP
+ $request->server->set('HTTPS', false);
+
+ $response = $this->provideResponse();
+ $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
+ $response->prepare($request);
+
+ $this->assertTrue($response->headers->has('Cache-Control'));
+ }
+
+ abstract protected function provideResponse();
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/ServerBagTest.php b/assets/php/vendor/symfony/http-foundation/Tests/ServerBagTest.php
new file mode 100644
index 0000000..f8becec
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/ServerBagTest.php
@@ -0,0 +1,170 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\ServerBag;
+
+/**
+ * ServerBagTest.
+ *
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
+ */
+class ServerBagTest extends TestCase
+{
+ public function testShouldExtractHeadersFromServerArray()
+ {
+ $server = array(
+ 'SOME_SERVER_VARIABLE' => 'value',
+ 'SOME_SERVER_VARIABLE2' => 'value',
+ 'ROOT' => 'value',
+ 'HTTP_CONTENT_TYPE' => 'text/html',
+ 'HTTP_CONTENT_LENGTH' => '0',
+ 'HTTP_ETAG' => 'asdf',
+ 'PHP_AUTH_USER' => 'foo',
+ 'PHP_AUTH_PW' => 'bar',
+ );
+
+ $bag = new ServerBag($server);
+
+ $this->assertEquals(array(
+ 'CONTENT_TYPE' => 'text/html',
+ 'CONTENT_LENGTH' => '0',
+ 'ETAG' => 'asdf',
+ 'AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'),
+ 'PHP_AUTH_USER' => 'foo',
+ 'PHP_AUTH_PW' => 'bar',
+ ), $bag->getHeaders());
+ }
+
+ public function testHttpPasswordIsOptional()
+ {
+ $bag = new ServerBag(array('PHP_AUTH_USER' => 'foo'));
+
+ $this->assertEquals(array(
+ 'AUTHORIZATION' => 'Basic '.base64_encode('foo:'),
+ 'PHP_AUTH_USER' => 'foo',
+ 'PHP_AUTH_PW' => '',
+ ), $bag->getHeaders());
+ }
+
+ public function testHttpBasicAuthWithPhpCgi()
+ {
+ $bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:bar')));
+
+ $this->assertEquals(array(
+ 'AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'),
+ 'PHP_AUTH_USER' => 'foo',
+ 'PHP_AUTH_PW' => 'bar',
+ ), $bag->getHeaders());
+ }
+
+ public function testHttpBasicAuthWithPhpCgiBogus()
+ {
+ $bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic_'.base64_encode('foo:bar')));
+
+ // Username and passwords should not be set as the header is bogus
+ $headers = $bag->getHeaders();
+ $this->assertArrayNotHasKey('PHP_AUTH_USER', $headers);
+ $this->assertArrayNotHasKey('PHP_AUTH_PW', $headers);
+ }
+
+ public function testHttpBasicAuthWithPhpCgiRedirect()
+ {
+ $bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => 'Basic '.base64_encode('username:pass:word')));
+
+ $this->assertEquals(array(
+ 'AUTHORIZATION' => 'Basic '.base64_encode('username:pass:word'),
+ 'PHP_AUTH_USER' => 'username',
+ 'PHP_AUTH_PW' => 'pass:word',
+ ), $bag->getHeaders());
+ }
+
+ public function testHttpBasicAuthWithPhpCgiEmptyPassword()
+ {
+ $bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:')));
+
+ $this->assertEquals(array(
+ 'AUTHORIZATION' => 'Basic '.base64_encode('foo:'),
+ 'PHP_AUTH_USER' => 'foo',
+ 'PHP_AUTH_PW' => '',
+ ), $bag->getHeaders());
+ }
+
+ public function testHttpDigestAuthWithPhpCgi()
+ {
+ $digest = 'Digest username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"';
+ $bag = new ServerBag(array('HTTP_AUTHORIZATION' => $digest));
+
+ $this->assertEquals(array(
+ 'AUTHORIZATION' => $digest,
+ 'PHP_AUTH_DIGEST' => $digest,
+ ), $bag->getHeaders());
+ }
+
+ public function testHttpDigestAuthWithPhpCgiBogus()
+ {
+ $digest = 'Digest_username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"';
+ $bag = new ServerBag(array('HTTP_AUTHORIZATION' => $digest));
+
+ // Username and passwords should not be set as the header is bogus
+ $headers = $bag->getHeaders();
+ $this->assertArrayNotHasKey('PHP_AUTH_USER', $headers);
+ $this->assertArrayNotHasKey('PHP_AUTH_PW', $headers);
+ }
+
+ public function testHttpDigestAuthWithPhpCgiRedirect()
+ {
+ $digest = 'Digest username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"';
+ $bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => $digest));
+
+ $this->assertEquals(array(
+ 'AUTHORIZATION' => $digest,
+ 'PHP_AUTH_DIGEST' => $digest,
+ ), $bag->getHeaders());
+ }
+
+ public function testOAuthBearerAuth()
+ {
+ $headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo';
+ $bag = new ServerBag(array('HTTP_AUTHORIZATION' => $headerContent));
+
+ $this->assertEquals(array(
+ 'AUTHORIZATION' => $headerContent,
+ ), $bag->getHeaders());
+ }
+
+ public function testOAuthBearerAuthWithRedirect()
+ {
+ $headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo';
+ $bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => $headerContent));
+
+ $this->assertEquals(array(
+ 'AUTHORIZATION' => $headerContent,
+ ), $bag->getHeaders());
+ }
+
+ /**
+ * @see https://github.com/symfony/symfony/issues/17345
+ */
+ public function testItDoesNotOverwriteTheAuthorizationHeaderIfItIsAlreadySet()
+ {
+ $headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo';
+ $bag = new ServerBag(array('PHP_AUTH_USER' => 'foo', 'HTTP_AUTHORIZATION' => $headerContent));
+
+ $this->assertEquals(array(
+ 'AUTHORIZATION' => $headerContent,
+ 'PHP_AUTH_USER' => 'foo',
+ 'PHP_AUTH_PW' => '',
+ ), $bag->getHeaders());
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php
new file mode 100644
index 0000000..724a0b9
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php
@@ -0,0 +1,186 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Attribute;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+
+/**
+ * Tests AttributeBag.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class AttributeBagTest extends TestCase
+{
+ private $array = array();
+
+ /**
+ * @var AttributeBag
+ */
+ private $bag;
+
+ protected function setUp()
+ {
+ $this->array = array(
+ 'hello' => 'world',
+ 'always' => 'be happy',
+ 'user.login' => 'drak',
+ 'csrf.token' => array(
+ 'a' => '1234',
+ 'b' => '4321',
+ ),
+ 'category' => array(
+ 'fishing' => array(
+ 'first' => 'cod',
+ 'second' => 'sole',
+ ),
+ ),
+ );
+ $this->bag = new AttributeBag('_sf2');
+ $this->bag->initialize($this->array);
+ }
+
+ protected function tearDown()
+ {
+ $this->bag = null;
+ $this->array = array();
+ }
+
+ public function testInitialize()
+ {
+ $bag = new AttributeBag();
+ $bag->initialize($this->array);
+ $this->assertEquals($this->array, $bag->all());
+ $array = array('should' => 'change');
+ $bag->initialize($array);
+ $this->assertEquals($array, $bag->all());
+ }
+
+ public function testGetStorageKey()
+ {
+ $this->assertEquals('_sf2', $this->bag->getStorageKey());
+ $attributeBag = new AttributeBag('test');
+ $this->assertEquals('test', $attributeBag->getStorageKey());
+ }
+
+ public function testGetSetName()
+ {
+ $this->assertEquals('attributes', $this->bag->getName());
+ $this->bag->setName('foo');
+ $this->assertEquals('foo', $this->bag->getName());
+ }
+
+ /**
+ * @dataProvider attributesProvider
+ */
+ public function testHas($key, $value, $exists)
+ {
+ $this->assertEquals($exists, $this->bag->has($key));
+ }
+
+ /**
+ * @dataProvider attributesProvider
+ */
+ public function testGet($key, $value, $expected)
+ {
+ $this->assertEquals($value, $this->bag->get($key));
+ }
+
+ public function testGetDefaults()
+ {
+ $this->assertNull($this->bag->get('user2.login'));
+ $this->assertEquals('default', $this->bag->get('user2.login', 'default'));
+ }
+
+ /**
+ * @dataProvider attributesProvider
+ */
+ public function testSet($key, $value, $expected)
+ {
+ $this->bag->set($key, $value);
+ $this->assertEquals($value, $this->bag->get($key));
+ }
+
+ public function testAll()
+ {
+ $this->assertEquals($this->array, $this->bag->all());
+
+ $this->bag->set('hello', 'fabien');
+ $array = $this->array;
+ $array['hello'] = 'fabien';
+ $this->assertEquals($array, $this->bag->all());
+ }
+
+ public function testReplace()
+ {
+ $array = array();
+ $array['name'] = 'jack';
+ $array['foo.bar'] = 'beep';
+ $this->bag->replace($array);
+ $this->assertEquals($array, $this->bag->all());
+ $this->assertNull($this->bag->get('hello'));
+ $this->assertNull($this->bag->get('always'));
+ $this->assertNull($this->bag->get('user.login'));
+ }
+
+ public function testRemove()
+ {
+ $this->assertEquals('world', $this->bag->get('hello'));
+ $this->bag->remove('hello');
+ $this->assertNull($this->bag->get('hello'));
+
+ $this->assertEquals('be happy', $this->bag->get('always'));
+ $this->bag->remove('always');
+ $this->assertNull($this->bag->get('always'));
+
+ $this->assertEquals('drak', $this->bag->get('user.login'));
+ $this->bag->remove('user.login');
+ $this->assertNull($this->bag->get('user.login'));
+ }
+
+ public function testClear()
+ {
+ $this->bag->clear();
+ $this->assertEquals(array(), $this->bag->all());
+ }
+
+ public function attributesProvider()
+ {
+ return array(
+ array('hello', 'world', true),
+ array('always', 'be happy', true),
+ array('user.login', 'drak', true),
+ array('csrf.token', array('a' => '1234', 'b' => '4321'), true),
+ array('category', array('fishing' => array('first' => 'cod', 'second' => 'sole')), true),
+ array('user2.login', null, false),
+ array('never', null, false),
+ array('bye', null, false),
+ array('bye/for/now', null, false),
+ );
+ }
+
+ public function testGetIterator()
+ {
+ $i = 0;
+ foreach ($this->bag as $key => $val) {
+ $this->assertEquals($this->array[$key], $val);
+ ++$i;
+ }
+
+ $this->assertEquals(count($this->array), $i);
+ }
+
+ public function testCount()
+ {
+ $this->assertCount(count($this->array), $this->bag);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php
new file mode 100644
index 0000000..f074ce1
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php
@@ -0,0 +1,182 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Attribute;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag;
+
+/**
+ * Tests NamespacedAttributeBag.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class NamespacedAttributeBagTest extends TestCase
+{
+ private $array = array();
+
+ /**
+ * @var NamespacedAttributeBag
+ */
+ private $bag;
+
+ protected function setUp()
+ {
+ $this->array = array(
+ 'hello' => 'world',
+ 'always' => 'be happy',
+ 'user.login' => 'drak',
+ 'csrf.token' => array(
+ 'a' => '1234',
+ 'b' => '4321',
+ ),
+ 'category' => array(
+ 'fishing' => array(
+ 'first' => 'cod',
+ 'second' => 'sole',
+ ),
+ ),
+ );
+ $this->bag = new NamespacedAttributeBag('_sf2', '/');
+ $this->bag->initialize($this->array);
+ }
+
+ protected function tearDown()
+ {
+ $this->bag = null;
+ $this->array = array();
+ }
+
+ public function testInitialize()
+ {
+ $bag = new NamespacedAttributeBag();
+ $bag->initialize($this->array);
+ $this->assertEquals($this->array, $this->bag->all());
+ $array = array('should' => 'not stick');
+ $bag->initialize($array);
+
+ // should have remained the same
+ $this->assertEquals($this->array, $this->bag->all());
+ }
+
+ public function testGetStorageKey()
+ {
+ $this->assertEquals('_sf2', $this->bag->getStorageKey());
+ $attributeBag = new NamespacedAttributeBag('test');
+ $this->assertEquals('test', $attributeBag->getStorageKey());
+ }
+
+ /**
+ * @dataProvider attributesProvider
+ */
+ public function testHas($key, $value, $exists)
+ {
+ $this->assertEquals($exists, $this->bag->has($key));
+ }
+
+ /**
+ * @dataProvider attributesProvider
+ */
+ public function testGet($key, $value, $expected)
+ {
+ $this->assertEquals($value, $this->bag->get($key));
+ }
+
+ public function testGetDefaults()
+ {
+ $this->assertNull($this->bag->get('user2.login'));
+ $this->assertEquals('default', $this->bag->get('user2.login', 'default'));
+ }
+
+ /**
+ * @dataProvider attributesProvider
+ */
+ public function testSet($key, $value, $expected)
+ {
+ $this->bag->set($key, $value);
+ $this->assertEquals($value, $this->bag->get($key));
+ }
+
+ public function testAll()
+ {
+ $this->assertEquals($this->array, $this->bag->all());
+
+ $this->bag->set('hello', 'fabien');
+ $array = $this->array;
+ $array['hello'] = 'fabien';
+ $this->assertEquals($array, $this->bag->all());
+ }
+
+ public function testReplace()
+ {
+ $array = array();
+ $array['name'] = 'jack';
+ $array['foo.bar'] = 'beep';
+ $this->bag->replace($array);
+ $this->assertEquals($array, $this->bag->all());
+ $this->assertNull($this->bag->get('hello'));
+ $this->assertNull($this->bag->get('always'));
+ $this->assertNull($this->bag->get('user.login'));
+ }
+
+ public function testRemove()
+ {
+ $this->assertEquals('world', $this->bag->get('hello'));
+ $this->bag->remove('hello');
+ $this->assertNull($this->bag->get('hello'));
+
+ $this->assertEquals('be happy', $this->bag->get('always'));
+ $this->bag->remove('always');
+ $this->assertNull($this->bag->get('always'));
+
+ $this->assertEquals('drak', $this->bag->get('user.login'));
+ $this->bag->remove('user.login');
+ $this->assertNull($this->bag->get('user.login'));
+ }
+
+ public function testRemoveExistingNamespacedAttribute()
+ {
+ $this->assertSame('cod', $this->bag->remove('category/fishing/first'));
+ }
+
+ public function testRemoveNonexistingNamespacedAttribute()
+ {
+ $this->assertNull($this->bag->remove('foo/bar/baz'));
+ }
+
+ public function testClear()
+ {
+ $this->bag->clear();
+ $this->assertEquals(array(), $this->bag->all());
+ }
+
+ public function attributesProvider()
+ {
+ return array(
+ array('hello', 'world', true),
+ array('always', 'be happy', true),
+ array('user.login', 'drak', true),
+ array('csrf.token', array('a' => '1234', 'b' => '4321'), true),
+ array('csrf.token/a', '1234', true),
+ array('csrf.token/b', '4321', true),
+ array('category', array('fishing' => array('first' => 'cod', 'second' => 'sole')), true),
+ array('category/fishing', array('first' => 'cod', 'second' => 'sole'), true),
+ array('category/fishing/missing/first', null, false),
+ array('category/fishing/first', 'cod', true),
+ array('category/fishing/second', 'sole', true),
+ array('category/fishing/missing/second', null, false),
+ array('user2.login', null, false),
+ array('never', null, false),
+ array('bye', null, false),
+ array('bye/for/now', null, false),
+ );
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php
new file mode 100644
index 0000000..fa8626a
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php
@@ -0,0 +1,161 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Flash;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag as FlashBag;
+
+/**
+ * AutoExpireFlashBagTest.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class AutoExpireFlashBagTest extends TestCase
+{
+ /**
+ * @var \Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag
+ */
+ private $bag;
+
+ protected $array = array();
+
+ protected function setUp()
+ {
+ parent::setUp();
+ $this->bag = new FlashBag();
+ $this->array = array('new' => array('notice' => array('A previous flash message')));
+ $this->bag->initialize($this->array);
+ }
+
+ protected function tearDown()
+ {
+ $this->bag = null;
+ parent::tearDown();
+ }
+
+ public function testInitialize()
+ {
+ $bag = new FlashBag();
+ $array = array('new' => array('notice' => array('A previous flash message')));
+ $bag->initialize($array);
+ $this->assertEquals(array('A previous flash message'), $bag->peek('notice'));
+ $array = array('new' => array(
+ 'notice' => array('Something else'),
+ 'error' => array('a'),
+ ));
+ $bag->initialize($array);
+ $this->assertEquals(array('Something else'), $bag->peek('notice'));
+ $this->assertEquals(array('a'), $bag->peek('error'));
+ }
+
+ public function testGetStorageKey()
+ {
+ $this->assertEquals('_symfony_flashes', $this->bag->getStorageKey());
+ $attributeBag = new FlashBag('test');
+ $this->assertEquals('test', $attributeBag->getStorageKey());
+ }
+
+ public function testGetSetName()
+ {
+ $this->assertEquals('flashes', $this->bag->getName());
+ $this->bag->setName('foo');
+ $this->assertEquals('foo', $this->bag->getName());
+ }
+
+ public function testPeek()
+ {
+ $this->assertEquals(array(), $this->bag->peek('non_existing'));
+ $this->assertEquals(array('default'), $this->bag->peek('non_existing', array('default')));
+ $this->assertEquals(array('A previous flash message'), $this->bag->peek('notice'));
+ $this->assertEquals(array('A previous flash message'), $this->bag->peek('notice'));
+ }
+
+ public function testSet()
+ {
+ $this->bag->set('notice', 'Foo');
+ $this->assertEquals(array('A previous flash message'), $this->bag->peek('notice'));
+ }
+
+ public function testHas()
+ {
+ $this->assertFalse($this->bag->has('nothing'));
+ $this->assertTrue($this->bag->has('notice'));
+ }
+
+ public function testKeys()
+ {
+ $this->assertEquals(array('notice'), $this->bag->keys());
+ }
+
+ public function testPeekAll()
+ {
+ $array = array(
+ 'new' => array(
+ 'notice' => 'Foo',
+ 'error' => 'Bar',
+ ),
+ );
+
+ $this->bag->initialize($array);
+ $this->assertEquals(array(
+ 'notice' => 'Foo',
+ 'error' => 'Bar',
+ ), $this->bag->peekAll()
+ );
+
+ $this->assertEquals(array(
+ 'notice' => 'Foo',
+ 'error' => 'Bar',
+ ), $this->bag->peekAll()
+ );
+ }
+
+ public function testGet()
+ {
+ $this->assertEquals(array(), $this->bag->get('non_existing'));
+ $this->assertEquals(array('default'), $this->bag->get('non_existing', array('default')));
+ $this->assertEquals(array('A previous flash message'), $this->bag->get('notice'));
+ $this->assertEquals(array(), $this->bag->get('notice'));
+ }
+
+ public function testSetAll()
+ {
+ $this->bag->setAll(array('a' => 'first', 'b' => 'second'));
+ $this->assertFalse($this->bag->has('a'));
+ $this->assertFalse($this->bag->has('b'));
+ }
+
+ public function testAll()
+ {
+ $this->bag->set('notice', 'Foo');
+ $this->bag->set('error', 'Bar');
+ $this->assertEquals(array(
+ 'notice' => array('A previous flash message'),
+ ), $this->bag->all()
+ );
+
+ $this->assertEquals(array(), $this->bag->all());
+ }
+
+ public function testClear()
+ {
+ $this->assertEquals(array('notice' => array('A previous flash message')), $this->bag->clear());
+ }
+
+ public function testDoNotRemoveTheNewFlashesWhenDisplayingTheExistingOnes()
+ {
+ $this->bag->add('success', 'Something');
+ $this->bag->all();
+
+ $this->assertEquals(array('new' => array('success' => array('Something')), 'display' => array()), $this->array);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php
new file mode 100644
index 0000000..c4e75b1
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php
@@ -0,0 +1,132 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Flash;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+
+/**
+ * FlashBagTest.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class FlashBagTest extends TestCase
+{
+ /**
+ * @var \Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface
+ */
+ private $bag;
+
+ protected $array = array();
+
+ protected function setUp()
+ {
+ parent::setUp();
+ $this->bag = new FlashBag();
+ $this->array = array('notice' => array('A previous flash message'));
+ $this->bag->initialize($this->array);
+ }
+
+ protected function tearDown()
+ {
+ $this->bag = null;
+ parent::tearDown();
+ }
+
+ public function testInitialize()
+ {
+ $bag = new FlashBag();
+ $bag->initialize($this->array);
+ $this->assertEquals($this->array, $bag->peekAll());
+ $array = array('should' => array('change'));
+ $bag->initialize($array);
+ $this->assertEquals($array, $bag->peekAll());
+ }
+
+ public function testGetStorageKey()
+ {
+ $this->assertEquals('_symfony_flashes', $this->bag->getStorageKey());
+ $attributeBag = new FlashBag('test');
+ $this->assertEquals('test', $attributeBag->getStorageKey());
+ }
+
+ public function testGetSetName()
+ {
+ $this->assertEquals('flashes', $this->bag->getName());
+ $this->bag->setName('foo');
+ $this->assertEquals('foo', $this->bag->getName());
+ }
+
+ public function testPeek()
+ {
+ $this->assertEquals(array(), $this->bag->peek('non_existing'));
+ $this->assertEquals(array('default'), $this->bag->peek('not_existing', array('default')));
+ $this->assertEquals(array('A previous flash message'), $this->bag->peek('notice'));
+ $this->assertEquals(array('A previous flash message'), $this->bag->peek('notice'));
+ }
+
+ public function testGet()
+ {
+ $this->assertEquals(array(), $this->bag->get('non_existing'));
+ $this->assertEquals(array('default'), $this->bag->get('not_existing', array('default')));
+ $this->assertEquals(array('A previous flash message'), $this->bag->get('notice'));
+ $this->assertEquals(array(), $this->bag->get('notice'));
+ }
+
+ public function testAll()
+ {
+ $this->bag->set('notice', 'Foo');
+ $this->bag->set('error', 'Bar');
+ $this->assertEquals(array(
+ 'notice' => array('Foo'),
+ 'error' => array('Bar'), ), $this->bag->all()
+ );
+
+ $this->assertEquals(array(), $this->bag->all());
+ }
+
+ public function testSet()
+ {
+ $this->bag->set('notice', 'Foo');
+ $this->bag->set('notice', 'Bar');
+ $this->assertEquals(array('Bar'), $this->bag->peek('notice'));
+ }
+
+ public function testHas()
+ {
+ $this->assertFalse($this->bag->has('nothing'));
+ $this->assertTrue($this->bag->has('notice'));
+ }
+
+ public function testKeys()
+ {
+ $this->assertEquals(array('notice'), $this->bag->keys());
+ }
+
+ public function testPeekAll()
+ {
+ $this->bag->set('notice', 'Foo');
+ $this->bag->set('error', 'Bar');
+ $this->assertEquals(array(
+ 'notice' => array('Foo'),
+ 'error' => array('Bar'),
+ ), $this->bag->peekAll()
+ );
+ $this->assertTrue($this->bag->has('notice'));
+ $this->assertTrue($this->bag->has('error'));
+ $this->assertEquals(array(
+ 'notice' => array('Foo'),
+ 'error' => array('Bar'),
+ ), $this->bag->peekAll()
+ );
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/SessionTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/SessionTest.php
new file mode 100644
index 0000000..41720e4
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/SessionTest.php
@@ -0,0 +1,242 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
+
+/**
+ * SessionTest.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Robert Schönthal <seroscho@googlemail.com>
+ * @author Drak <drak@zikula.org>
+ */
+class SessionTest extends TestCase
+{
+ /**
+ * @var \Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface
+ */
+ protected $storage;
+
+ /**
+ * @var \Symfony\Component\HttpFoundation\Session\SessionInterface
+ */
+ protected $session;
+
+ protected function setUp()
+ {
+ $this->storage = new MockArraySessionStorage();
+ $this->session = new Session($this->storage, new AttributeBag(), new FlashBag());
+ }
+
+ protected function tearDown()
+ {
+ $this->storage = null;
+ $this->session = null;
+ }
+
+ public function testStart()
+ {
+ $this->assertEquals('', $this->session->getId());
+ $this->assertTrue($this->session->start());
+ $this->assertNotEquals('', $this->session->getId());
+ }
+
+ public function testIsStarted()
+ {
+ $this->assertFalse($this->session->isStarted());
+ $this->session->start();
+ $this->assertTrue($this->session->isStarted());
+ }
+
+ public function testSetId()
+ {
+ $this->assertEquals('', $this->session->getId());
+ $this->session->setId('0123456789abcdef');
+ $this->session->start();
+ $this->assertEquals('0123456789abcdef', $this->session->getId());
+ }
+
+ public function testSetName()
+ {
+ $this->assertEquals('MOCKSESSID', $this->session->getName());
+ $this->session->setName('session.test.com');
+ $this->session->start();
+ $this->assertEquals('session.test.com', $this->session->getName());
+ }
+
+ public function testGet()
+ {
+ // tests defaults
+ $this->assertNull($this->session->get('foo'));
+ $this->assertEquals(1, $this->session->get('foo', 1));
+ }
+
+ /**
+ * @dataProvider setProvider
+ */
+ public function testSet($key, $value)
+ {
+ $this->session->set($key, $value);
+ $this->assertEquals($value, $this->session->get($key));
+ }
+
+ /**
+ * @dataProvider setProvider
+ */
+ public function testHas($key, $value)
+ {
+ $this->session->set($key, $value);
+ $this->assertTrue($this->session->has($key));
+ $this->assertFalse($this->session->has($key.'non_value'));
+ }
+
+ public function testReplace()
+ {
+ $this->session->replace(array('happiness' => 'be good', 'symfony' => 'awesome'));
+ $this->assertEquals(array('happiness' => 'be good', 'symfony' => 'awesome'), $this->session->all());
+ $this->session->replace(array());
+ $this->assertEquals(array(), $this->session->all());
+ }
+
+ /**
+ * @dataProvider setProvider
+ */
+ public function testAll($key, $value, $result)
+ {
+ $this->session->set($key, $value);
+ $this->assertEquals($result, $this->session->all());
+ }
+
+ /**
+ * @dataProvider setProvider
+ */
+ public function testClear($key, $value)
+ {
+ $this->session->set('hi', 'fabien');
+ $this->session->set($key, $value);
+ $this->session->clear();
+ $this->assertEquals(array(), $this->session->all());
+ }
+
+ public function setProvider()
+ {
+ return array(
+ array('foo', 'bar', array('foo' => 'bar')),
+ array('foo.bar', 'too much beer', array('foo.bar' => 'too much beer')),
+ array('great', 'symfony is great', array('great' => 'symfony is great')),
+ );
+ }
+
+ /**
+ * @dataProvider setProvider
+ */
+ public function testRemove($key, $value)
+ {
+ $this->session->set('hi.world', 'have a nice day');
+ $this->session->set($key, $value);
+ $this->session->remove($key);
+ $this->assertEquals(array('hi.world' => 'have a nice day'), $this->session->all());
+ }
+
+ public function testInvalidate()
+ {
+ $this->session->set('invalidate', 123);
+ $this->session->invalidate();
+ $this->assertEquals(array(), $this->session->all());
+ }
+
+ public function testMigrate()
+ {
+ $this->session->set('migrate', 321);
+ $this->session->migrate();
+ $this->assertEquals(321, $this->session->get('migrate'));
+ }
+
+ public function testMigrateDestroy()
+ {
+ $this->session->set('migrate', 333);
+ $this->session->migrate(true);
+ $this->assertEquals(333, $this->session->get('migrate'));
+ }
+
+ public function testSave()
+ {
+ $this->session->start();
+ $this->session->save();
+
+ $this->assertFalse($this->session->isStarted());
+ }
+
+ public function testGetId()
+ {
+ $this->assertEquals('', $this->session->getId());
+ $this->session->start();
+ $this->assertNotEquals('', $this->session->getId());
+ }
+
+ public function testGetFlashBag()
+ {
+ $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface', $this->session->getFlashBag());
+ }
+
+ public function testGetIterator()
+ {
+ $attributes = array('hello' => 'world', 'symfony' => 'rocks');
+ foreach ($attributes as $key => $val) {
+ $this->session->set($key, $val);
+ }
+
+ $i = 0;
+ foreach ($this->session as $key => $val) {
+ $this->assertEquals($attributes[$key], $val);
+ ++$i;
+ }
+
+ $this->assertEquals(count($attributes), $i);
+ }
+
+ public function testGetCount()
+ {
+ $this->session->set('hello', 'world');
+ $this->session->set('symfony', 'rocks');
+
+ $this->assertCount(2, $this->session);
+ }
+
+ public function testGetMeta()
+ {
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\MetadataBag', $this->session->getMetadataBag());
+ }
+
+ public function testIsEmpty()
+ {
+ $this->assertTrue($this->session->isEmpty());
+
+ $this->session->set('hello', 'world');
+ $this->assertFalse($this->session->isEmpty());
+
+ $this->session->remove('hello');
+ $this->assertTrue($this->session->isEmpty());
+
+ $flash = $this->session->getFlashBag();
+ $flash->set('hello', 'world');
+ $this->assertFalse($this->session->isEmpty());
+
+ $flash->get('hello');
+ $this->assertTrue($this->session->isEmpty());
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php
new file mode 100644
index 0000000..3ac081e
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php
@@ -0,0 +1,61 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @requires PHP 7.0
+ */
+class AbstractSessionHandlerTest extends TestCase
+{
+ private static $server;
+
+ public static function setUpBeforeClass()
+ {
+ $spec = array(
+ 1 => array('file', '/dev/null', 'w'),
+ 2 => array('file', '/dev/null', 'w'),
+ );
+ if (!self::$server = @proc_open('exec php -S localhost:8053', $spec, $pipes, __DIR__.'/Fixtures')) {
+ self::markTestSkipped('PHP server unable to start.');
+ }
+ sleep(1);
+ }
+
+ public static function tearDownAfterClass()
+ {
+ if (self::$server) {
+ proc_terminate(self::$server);
+ proc_close(self::$server);
+ }
+ }
+
+ /**
+ * @dataProvider provideSession
+ */
+ public function testSession($fixture)
+ {
+ $context = array('http' => array('header' => "Cookie: sid=123abc\r\n"));
+ $context = stream_context_create($context);
+ $result = file_get_contents(sprintf('http://localhost:8053/%s.php', $fixture), false, $context);
+
+ $this->assertStringEqualsFile(__DIR__.sprintf('/Fixtures/%s.expected', $fixture), $result);
+ }
+
+ public function provideSession()
+ {
+ foreach (glob(__DIR__.'/Fixtures/*.php') as $file) {
+ yield array(pathinfo($file, PATHINFO_FILENAME));
+ }
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc
new file mode 100644
index 0000000..7a064c7
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc
@@ -0,0 +1,151 @@
+<?php
+
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler;
+
+$parent = __DIR__;
+while (!@file_exists($parent.'/vendor/autoload.php')) {
+ if (!@file_exists($parent)) {
+ // open_basedir restriction in effect
+ break;
+ }
+ if ($parent === dirname($parent)) {
+ echo "vendor/autoload.php not found\n";
+ exit(1);
+ }
+
+ $parent = dirname($parent);
+}
+
+require $parent.'/vendor/autoload.php';
+
+error_reporting(-1);
+ini_set('html_errors', 0);
+ini_set('display_errors', 1);
+ini_set('session.gc_probability', 0);
+ini_set('session.serialize_handler', 'php');
+ini_set('session.cookie_lifetime', 0);
+ini_set('session.cookie_domain', '');
+ini_set('session.cookie_secure', '');
+ini_set('session.cookie_httponly', '');
+ini_set('session.use_cookies', 1);
+ini_set('session.use_only_cookies', 1);
+ini_set('session.cache_expire', 180);
+ini_set('session.cookie_path', '/');
+ini_set('session.cookie_domain', '');
+ini_set('session.cookie_secure', 1);
+ini_set('session.cookie_httponly', 1);
+ini_set('session.use_strict_mode', 1);
+ini_set('session.lazy_write', 1);
+ini_set('session.name', 'sid');
+ini_set('session.save_path', __DIR__);
+ini_set('session.cache_limiter', '');
+
+header_remove('X-Powered-By');
+header('Content-Type: text/plain; charset=utf-8');
+
+register_shutdown_function(function () {
+ echo "\n";
+ session_write_close();
+ print_r(headers_list());
+ echo "shutdown\n";
+});
+ob_start();
+
+class TestSessionHandler extends AbstractSessionHandler
+{
+ private $data;
+
+ public function __construct($data = '')
+ {
+ $this->data = $data;
+ }
+
+ public function open($path, $name)
+ {
+ echo __FUNCTION__, "\n";
+
+ return parent::open($path, $name);
+ }
+
+ public function validateId($sessionId)
+ {
+ echo __FUNCTION__, "\n";
+
+ return parent::validateId($sessionId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function read($sessionId)
+ {
+ echo __FUNCTION__, "\n";
+
+ return parent::read($sessionId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function updateTimestamp($sessionId, $data)
+ {
+ echo __FUNCTION__, "\n";
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write($sessionId, $data)
+ {
+ echo __FUNCTION__, "\n";
+
+ return parent::write($sessionId, $data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function destroy($sessionId)
+ {
+ echo __FUNCTION__, "\n";
+
+ return parent::destroy($sessionId);
+ }
+
+ public function close()
+ {
+ echo __FUNCTION__, "\n";
+
+ return true;
+ }
+
+ public function gc($maxLifetime)
+ {
+ echo __FUNCTION__, "\n";
+
+ return true;
+ }
+
+ protected function doRead($sessionId)
+ {
+ echo __FUNCTION__.': ', $this->data, "\n";
+
+ return $this->data;
+ }
+
+ protected function doWrite($sessionId, $data)
+ {
+ echo __FUNCTION__.': ', $data, "\n";
+
+ return true;
+ }
+
+ protected function doDestroy($sessionId)
+ {
+ echo __FUNCTION__, "\n";
+
+ return true;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected
new file mode 100644
index 0000000..8203714
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected
@@ -0,0 +1,17 @@
+open
+validateId
+read
+doRead: abc|i:123;
+read
+
+write
+destroy
+doDestroy
+close
+Array
+(
+ [0] => Content-Type: text/plain; charset=utf-8
+ [1] => Cache-Control: max-age=10800, private, must-revalidate
+ [2] => Set-Cookie: sid=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; secure; HttpOnly
+)
+shutdown
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php
new file mode 100644
index 0000000..3cfc125
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php
@@ -0,0 +1,8 @@
+<?php
+
+require __DIR__.'/common.inc';
+
+session_set_save_handler(new TestSessionHandler('abc|i:123;'), false);
+session_start();
+
+unset($_SESSION['abc']);
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.expected b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.expected
new file mode 100644
index 0000000..587adaf
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.expected
@@ -0,0 +1,14 @@
+open
+validateId
+read
+doRead: abc|i:123;
+read
+123
+updateTimestamp
+close
+Array
+(
+ [0] => Content-Type: text/plain; charset=utf-8
+ [1] => Cache-Control: max-age=10800, private, must-revalidate
+)
+shutdown
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php
new file mode 100644
index 0000000..3e62fb9
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php
@@ -0,0 +1,8 @@
+<?php
+
+require __DIR__.'/common.inc';
+
+session_set_save_handler(new TestSessionHandler('abc|i:123;'), false);
+session_start();
+
+echo $_SESSION['abc'];
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected
new file mode 100644
index 0000000..baa5f2f
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected
@@ -0,0 +1,24 @@
+open
+validateId
+read
+doRead: abc|i:123;
+read
+destroy
+doDestroy
+close
+open
+validateId
+read
+doRead: abc|i:123;
+read
+
+write
+doWrite: abc|i:123;
+close
+Array
+(
+ [0] => Content-Type: text/plain; charset=utf-8
+ [1] => Cache-Control: max-age=10800, private, must-revalidate
+ [2] => Set-Cookie: sid=random_session_id; path=/; secure; HttpOnly
+)
+shutdown
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php
new file mode 100644
index 0000000..a0f635c
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php
@@ -0,0 +1,10 @@
+<?php
+
+require __DIR__.'/common.inc';
+
+session_set_save_handler(new TestSessionHandler('abc|i:123;'), false);
+session_start();
+
+session_regenerate_id(true);
+
+ob_start(function ($buffer) { return str_replace(session_id(), 'random_session_id', $buffer); });
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.expected b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.expected
new file mode 100644
index 0000000..4533a10
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.expected
@@ -0,0 +1,20 @@
+open
+validateId
+read
+doRead:
+read
+Array
+(
+ [0] => bar
+)
+$_SESSION is not empty
+write
+destroy
+close
+$_SESSION is not empty
+Array
+(
+ [0] => Content-Type: text/plain; charset=utf-8
+ [1] => Cache-Control: max-age=0, private, must-revalidate
+)
+shutdown
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php
new file mode 100644
index 0000000..96dca3c
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php
@@ -0,0 +1,24 @@
+<?php
+
+require __DIR__.'/common.inc';
+
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
+
+$storage = new NativeSessionStorage();
+$storage->setSaveHandler(new TestSessionHandler());
+$flash = new FlashBag();
+$storage->registerBag($flash);
+$storage->start();
+
+$flash->add('foo', 'bar');
+
+print_r($flash->get('foo'));
+echo empty($_SESSION) ? '$_SESSION is empty' : '$_SESSION is not empty';
+echo "\n";
+
+$storage->save();
+
+echo empty($_SESSION) ? '$_SESSION is empty' : '$_SESSION is not empty';
+
+ob_start(function ($buffer) { return str_replace(session_id(), 'random_session_id', $buffer); });
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected
new file mode 100644
index 0000000..33da0a5
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected
@@ -0,0 +1,15 @@
+open
+validateId
+read
+doRead: abc|i:123;
+read
+
+updateTimestamp
+close
+Array
+(
+ [0] => Content-Type: text/plain; charset=utf-8
+ [1] => Cache-Control: max-age=10800, private, must-revalidate
+ [2] => Set-Cookie: abc=def
+)
+shutdown
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php
new file mode 100644
index 0000000..ffb5b20
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php
@@ -0,0 +1,8 @@
+<?php
+
+require __DIR__.'/common.inc';
+
+session_set_save_handler(new TestSessionHandler('abc|i:123;'), false);
+session_start();
+
+setcookie('abc', 'def');
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected
new file mode 100644
index 0000000..5de2d9e
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected
@@ -0,0 +1,24 @@
+open
+validateId
+read
+doRead: abc|i:123;
+read
+updateTimestamp
+close
+open
+validateId
+read
+doRead: abc|i:123;
+read
+
+write
+destroy
+doDestroy
+close
+Array
+(
+ [0] => Content-Type: text/plain; charset=utf-8
+ [1] => Cache-Control: max-age=10800, private, must-revalidate
+ [2] => Set-Cookie: abc=def
+)
+shutdown
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php
new file mode 100644
index 0000000..ec51193
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php
@@ -0,0 +1,13 @@
+<?php
+
+require __DIR__.'/common.inc';
+
+setcookie('abc', 'def');
+
+session_set_save_handler(new TestSessionHandler('abc|i:123;'), false);
+session_start();
+session_write_close();
+session_start();
+
+$_SESSION['abc'] = 234;
+unset($_SESSION['abc']);
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php
new file mode 100644
index 0000000..dda43c8
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php
@@ -0,0 +1,135 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcacheSessionHandler;
+
+/**
+ * @requires extension memcache
+ * @group time-sensitive
+ * @group legacy
+ */
+class MemcacheSessionHandlerTest extends TestCase
+{
+ const PREFIX = 'prefix_';
+ const TTL = 1000;
+
+ /**
+ * @var MemcacheSessionHandler
+ */
+ protected $storage;
+
+ protected $memcache;
+
+ protected function setUp()
+ {
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('PHPUnit_MockObject cannot mock the Memcache class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
+ }
+
+ parent::setUp();
+ $this->memcache = $this->getMockBuilder('Memcache')->getMock();
+ $this->storage = new MemcacheSessionHandler(
+ $this->memcache,
+ array('prefix' => self::PREFIX, 'expiretime' => self::TTL)
+ );
+ }
+
+ protected function tearDown()
+ {
+ $this->memcache = null;
+ $this->storage = null;
+ parent::tearDown();
+ }
+
+ public function testOpenSession()
+ {
+ $this->assertTrue($this->storage->open('', ''));
+ }
+
+ public function testCloseSession()
+ {
+ $this->assertTrue($this->storage->close());
+ }
+
+ public function testReadSession()
+ {
+ $this->memcache
+ ->expects($this->once())
+ ->method('get')
+ ->with(self::PREFIX.'id')
+ ;
+
+ $this->assertEquals('', $this->storage->read('id'));
+ }
+
+ public function testWriteSession()
+ {
+ $this->memcache
+ ->expects($this->once())
+ ->method('set')
+ ->with(self::PREFIX.'id', 'data', 0, $this->equalTo(time() + self::TTL, 2))
+ ->will($this->returnValue(true))
+ ;
+
+ $this->assertTrue($this->storage->write('id', 'data'));
+ }
+
+ public function testDestroySession()
+ {
+ $this->memcache
+ ->expects($this->once())
+ ->method('delete')
+ ->with(self::PREFIX.'id')
+ ->will($this->returnValue(true))
+ ;
+
+ $this->assertTrue($this->storage->destroy('id'));
+ }
+
+ public function testGcSession()
+ {
+ $this->assertTrue($this->storage->gc(123));
+ }
+
+ /**
+ * @dataProvider getOptionFixtures
+ */
+ public function testSupportedOptions($options, $supported)
+ {
+ try {
+ new MemcacheSessionHandler($this->memcache, $options);
+ $this->assertTrue($supported);
+ } catch (\InvalidArgumentException $e) {
+ $this->assertFalse($supported);
+ }
+ }
+
+ public function getOptionFixtures()
+ {
+ return array(
+ array(array('prefix' => 'session'), true),
+ array(array('expiretime' => 100), true),
+ array(array('prefix' => 'session', 'expiretime' => 200), true),
+ array(array('expiretime' => 100, 'foo' => 'bar'), false),
+ );
+ }
+
+ public function testGetConnection()
+ {
+ $method = new \ReflectionMethod($this->storage, 'getMemcache');
+ $method->setAccessible(true);
+
+ $this->assertInstanceOf('\Memcache', $method->invoke($this->storage));
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php
new file mode 100644
index 0000000..2e7be35
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php
@@ -0,0 +1,139 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler;
+
+/**
+ * @requires extension memcached
+ * @group time-sensitive
+ */
+class MemcachedSessionHandlerTest extends TestCase
+{
+ const PREFIX = 'prefix_';
+ const TTL = 1000;
+
+ /**
+ * @var MemcachedSessionHandler
+ */
+ protected $storage;
+
+ protected $memcached;
+
+ protected function setUp()
+ {
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('PHPUnit_MockObject cannot mock the Memcached class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
+ }
+
+ parent::setUp();
+
+ if (version_compare(phpversion('memcached'), '2.2.0', '>=') && version_compare(phpversion('memcached'), '3.0.0b1', '<')) {
+ $this->markTestSkipped('Tests can only be run with memcached extension 2.1.0 or lower, or 3.0.0b1 or higher');
+ }
+
+ $this->memcached = $this->getMockBuilder('Memcached')->getMock();
+ $this->storage = new MemcachedSessionHandler(
+ $this->memcached,
+ array('prefix' => self::PREFIX, 'expiretime' => self::TTL)
+ );
+ }
+
+ protected function tearDown()
+ {
+ $this->memcached = null;
+ $this->storage = null;
+ parent::tearDown();
+ }
+
+ public function testOpenSession()
+ {
+ $this->assertTrue($this->storage->open('', ''));
+ }
+
+ public function testCloseSession()
+ {
+ $this->assertTrue($this->storage->close());
+ }
+
+ public function testReadSession()
+ {
+ $this->memcached
+ ->expects($this->once())
+ ->method('get')
+ ->with(self::PREFIX.'id')
+ ;
+
+ $this->assertEquals('', $this->storage->read('id'));
+ }
+
+ public function testWriteSession()
+ {
+ $this->memcached
+ ->expects($this->once())
+ ->method('set')
+ ->with(self::PREFIX.'id', 'data', $this->equalTo(time() + self::TTL, 2))
+ ->will($this->returnValue(true))
+ ;
+
+ $this->assertTrue($this->storage->write('id', 'data'));
+ }
+
+ public function testDestroySession()
+ {
+ $this->memcached
+ ->expects($this->once())
+ ->method('delete')
+ ->with(self::PREFIX.'id')
+ ->will($this->returnValue(true))
+ ;
+
+ $this->assertTrue($this->storage->destroy('id'));
+ }
+
+ public function testGcSession()
+ {
+ $this->assertTrue($this->storage->gc(123));
+ }
+
+ /**
+ * @dataProvider getOptionFixtures
+ */
+ public function testSupportedOptions($options, $supported)
+ {
+ try {
+ new MemcachedSessionHandler($this->memcached, $options);
+ $this->assertTrue($supported);
+ } catch (\InvalidArgumentException $e) {
+ $this->assertFalse($supported);
+ }
+ }
+
+ public function getOptionFixtures()
+ {
+ return array(
+ array(array('prefix' => 'session'), true),
+ array(array('expiretime' => 100), true),
+ array(array('prefix' => 'session', 'expiretime' => 200), true),
+ array(array('expiretime' => 100, 'foo' => 'bar'), false),
+ );
+ }
+
+ public function testGetConnection()
+ {
+ $method = new \ReflectionMethod($this->storage, 'getMemcached');
+ $method->setAccessible(true);
+
+ $this->assertInstanceOf('\Memcached', $method->invoke($this->storage));
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php
new file mode 100644
index 0000000..da05109
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php
@@ -0,0 +1,333 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler;
+
+/**
+ * @author Markus Bachmann <markus.bachmann@bachi.biz>
+ * @group time-sensitive
+ * @group legacy
+ */
+class MongoDbSessionHandlerTest extends TestCase
+{
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $mongo;
+ private $storage;
+ public $options;
+
+ protected function setUp()
+ {
+ parent::setUp();
+
+ if (extension_loaded('mongodb')) {
+ if (!class_exists('MongoDB\Client')) {
+ $this->markTestSkipped('The mongodb/mongodb package is required.');
+ }
+ } elseif (!extension_loaded('mongo')) {
+ $this->markTestSkipped('The Mongo or MongoDB extension is required.');
+ }
+
+ if (phpversion('mongodb')) {
+ $mongoClass = 'MongoDB\Client';
+ } else {
+ $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
+ }
+
+ $this->mongo = $this->getMockBuilder($mongoClass)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->options = array(
+ 'id_field' => '_id',
+ 'data_field' => 'data',
+ 'time_field' => 'time',
+ 'expiry_field' => 'expires_at',
+ 'database' => 'sf2-test',
+ 'collection' => 'session-test',
+ );
+
+ $this->storage = new MongoDbSessionHandler($this->mongo, $this->options);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testConstructorShouldThrowExceptionForInvalidMongo()
+ {
+ new MongoDbSessionHandler(new \stdClass(), $this->options);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testConstructorShouldThrowExceptionForMissingOptions()
+ {
+ new MongoDbSessionHandler($this->mongo, array());
+ }
+
+ public function testOpenMethodAlwaysReturnTrue()
+ {
+ $this->assertTrue($this->storage->open('test', 'test'), 'The "open" method should always return true');
+ }
+
+ public function testCloseMethodAlwaysReturnTrue()
+ {
+ $this->assertTrue($this->storage->close(), 'The "close" method should always return true');
+ }
+
+ public function testRead()
+ {
+ $collection = $this->createMongoCollectionMock();
+
+ $this->mongo->expects($this->once())
+ ->method('selectCollection')
+ ->with($this->options['database'], $this->options['collection'])
+ ->will($this->returnValue($collection));
+
+ // defining the timeout before the actual method call
+ // allows to test for "greater than" values in the $criteria
+ $testTimeout = time() + 1;
+
+ $collection->expects($this->once())
+ ->method('findOne')
+ ->will($this->returnCallback(function ($criteria) use ($testTimeout) {
+ $this->assertArrayHasKey($this->options['id_field'], $criteria);
+ $this->assertEquals($criteria[$this->options['id_field']], 'foo');
+
+ $this->assertArrayHasKey($this->options['expiry_field'], $criteria);
+ $this->assertArrayHasKey('$gte', $criteria[$this->options['expiry_field']]);
+
+ if (phpversion('mongodb')) {
+ $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$gte']);
+ $this->assertGreaterThanOrEqual(round((string) $criteria[$this->options['expiry_field']]['$gte'] / 1000), $testTimeout);
+ } else {
+ $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']);
+ $this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout);
+ }
+
+ $fields = array(
+ $this->options['id_field'] => 'foo',
+ );
+
+ if (phpversion('mongodb')) {
+ $fields[$this->options['data_field']] = new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
+ $fields[$this->options['id_field']] = new \MongoDB\BSON\UTCDateTime(time() * 1000);
+ } else {
+ $fields[$this->options['data_field']] = new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY);
+ $fields[$this->options['id_field']] = new \MongoDate();
+ }
+
+ return $fields;
+ }));
+
+ $this->assertEquals('bar', $this->storage->read('foo'));
+ }
+
+ public function testWrite()
+ {
+ $collection = $this->createMongoCollectionMock();
+
+ $this->mongo->expects($this->once())
+ ->method('selectCollection')
+ ->with($this->options['database'], $this->options['collection'])
+ ->will($this->returnValue($collection));
+
+ $data = array();
+
+ $methodName = phpversion('mongodb') ? 'updateOne' : 'update';
+
+ $collection->expects($this->once())
+ ->method($methodName)
+ ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
+ $this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria);
+
+ if (phpversion('mongodb')) {
+ $this->assertEquals(array('upsert' => true), $options);
+ } else {
+ $this->assertEquals(array('upsert' => true, 'multiple' => false), $options);
+ }
+
+ $data = $updateData['$set'];
+ }));
+
+ $expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime');
+ $this->assertTrue($this->storage->write('foo', 'bar'));
+
+ if (phpversion('mongodb')) {
+ $this->assertEquals('bar', $data[$this->options['data_field']]->getData());
+ $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]);
+ $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]);
+ $this->assertGreaterThanOrEqual($expectedExpiry, round((string) $data[$this->options['expiry_field']] / 1000));
+ } else {
+ $this->assertEquals('bar', $data[$this->options['data_field']]->bin);
+ $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
+ $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
+ $this->assertGreaterThanOrEqual($expectedExpiry, $data[$this->options['expiry_field']]->sec);
+ }
+ }
+
+ public function testWriteWhenUsingExpiresField()
+ {
+ $this->options = array(
+ 'id_field' => '_id',
+ 'data_field' => 'data',
+ 'time_field' => 'time',
+ 'database' => 'sf2-test',
+ 'collection' => 'session-test',
+ 'expiry_field' => 'expiresAt',
+ );
+
+ $this->storage = new MongoDbSessionHandler($this->mongo, $this->options);
+
+ $collection = $this->createMongoCollectionMock();
+
+ $this->mongo->expects($this->once())
+ ->method('selectCollection')
+ ->with($this->options['database'], $this->options['collection'])
+ ->will($this->returnValue($collection));
+
+ $data = array();
+
+ $methodName = phpversion('mongodb') ? 'updateOne' : 'update';
+
+ $collection->expects($this->once())
+ ->method($methodName)
+ ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
+ $this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria);
+
+ if (phpversion('mongodb')) {
+ $this->assertEquals(array('upsert' => true), $options);
+ } else {
+ $this->assertEquals(array('upsert' => true, 'multiple' => false), $options);
+ }
+
+ $data = $updateData['$set'];
+ }));
+
+ $this->assertTrue($this->storage->write('foo', 'bar'));
+
+ if (phpversion('mongodb')) {
+ $this->assertEquals('bar', $data[$this->options['data_field']]->getData());
+ $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]);
+ $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]);
+ } else {
+ $this->assertEquals('bar', $data[$this->options['data_field']]->bin);
+ $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
+ $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
+ }
+ }
+
+ public function testReplaceSessionData()
+ {
+ $collection = $this->createMongoCollectionMock();
+
+ $this->mongo->expects($this->once())
+ ->method('selectCollection')
+ ->with($this->options['database'], $this->options['collection'])
+ ->will($this->returnValue($collection));
+
+ $data = array();
+
+ $methodName = phpversion('mongodb') ? 'updateOne' : 'update';
+
+ $collection->expects($this->exactly(2))
+ ->method($methodName)
+ ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
+ $data = $updateData;
+ }));
+
+ $this->storage->write('foo', 'bar');
+ $this->storage->write('foo', 'foobar');
+
+ if (phpversion('mongodb')) {
+ $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->getData());
+ } else {
+ $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin);
+ }
+ }
+
+ public function testDestroy()
+ {
+ $collection = $this->createMongoCollectionMock();
+
+ $this->mongo->expects($this->once())
+ ->method('selectCollection')
+ ->with($this->options['database'], $this->options['collection'])
+ ->will($this->returnValue($collection));
+
+ $methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
+
+ $collection->expects($this->once())
+ ->method($methodName)
+ ->with(array($this->options['id_field'] => 'foo'));
+
+ $this->assertTrue($this->storage->destroy('foo'));
+ }
+
+ public function testGc()
+ {
+ $collection = $this->createMongoCollectionMock();
+
+ $this->mongo->expects($this->once())
+ ->method('selectCollection')
+ ->with($this->options['database'], $this->options['collection'])
+ ->will($this->returnValue($collection));
+
+ $methodName = phpversion('mongodb') ? 'deleteMany' : 'remove';
+
+ $collection->expects($this->once())
+ ->method($methodName)
+ ->will($this->returnCallback(function ($criteria) {
+ if (phpversion('mongodb')) {
+ $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$lt']);
+ $this->assertGreaterThanOrEqual(time() - 1, round((string) $criteria[$this->options['expiry_field']]['$lt'] / 1000));
+ } else {
+ $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$lt']);
+ $this->assertGreaterThanOrEqual(time() - 1, $criteria[$this->options['expiry_field']]['$lt']->sec);
+ }
+ }));
+
+ $this->assertTrue($this->storage->gc(1));
+ }
+
+ public function testGetConnection()
+ {
+ $method = new \ReflectionMethod($this->storage, 'getMongo');
+ $method->setAccessible(true);
+
+ if (phpversion('mongodb')) {
+ $mongoClass = 'MongoDB\Client';
+ } else {
+ $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
+ }
+
+ $this->assertInstanceOf($mongoClass, $method->invoke($this->storage));
+ }
+
+ private function createMongoCollectionMock()
+ {
+ $collectionClass = 'MongoCollection';
+ if (phpversion('mongodb')) {
+ $collectionClass = 'MongoDB\Collection';
+ }
+
+ $collection = $this->getMockBuilder($collectionClass)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ return $collection;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php
new file mode 100644
index 0000000..a6264e5
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php
@@ -0,0 +1,77 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler;
+use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
+
+/**
+ * Test class for NativeFileSessionHandler.
+ *
+ * @author Drak <drak@zikula.org>
+ *
+ * @runTestsInSeparateProcesses
+ * @preserveGlobalState disabled
+ */
+class NativeFileSessionHandlerTest extends TestCase
+{
+ public function testConstruct()
+ {
+ $storage = new NativeSessionStorage(array('name' => 'TESTING'), new NativeFileSessionHandler(sys_get_temp_dir()));
+
+ $this->assertEquals('files', $storage->getSaveHandler()->getSaveHandlerName());
+ $this->assertEquals('user', ini_get('session.save_handler'));
+
+ $this->assertEquals(sys_get_temp_dir(), ini_get('session.save_path'));
+ $this->assertEquals('TESTING', ini_get('session.name'));
+ }
+
+ /**
+ * @dataProvider savePathDataProvider
+ */
+ public function testConstructSavePath($savePath, $expectedSavePath, $path)
+ {
+ $handler = new NativeFileSessionHandler($savePath);
+ $this->assertEquals($expectedSavePath, ini_get('session.save_path'));
+ $this->assertTrue(is_dir(realpath($path)));
+
+ rmdir($path);
+ }
+
+ public function savePathDataProvider()
+ {
+ $base = sys_get_temp_dir();
+
+ return array(
+ array("$base/foo", "$base/foo", "$base/foo"),
+ array("5;$base/foo", "5;$base/foo", "$base/foo"),
+ array("5;0600;$base/foo", "5;0600;$base/foo", "$base/foo"),
+ );
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testConstructException()
+ {
+ $handler = new NativeFileSessionHandler('something;invalid;with;too-many-args');
+ }
+
+ public function testConstructDefault()
+ {
+ $path = ini_get('session.save_path');
+ $storage = new NativeSessionStorage(array('name' => 'TESTING'), new NativeFileSessionHandler());
+
+ $this->assertEquals($path, ini_get('session.save_path'));
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php
new file mode 100644
index 0000000..4a9fb60
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
+
+/**
+ * Test class for NativeSessionHandler.
+ *
+ * @author Drak <drak@zikula.org>
+ *
+ * @runTestsInSeparateProcesses
+ * @preserveGlobalState disabled
+ * @group legacy
+ */
+class NativeSessionHandlerTest extends TestCase
+{
+ /**
+ * @expectedDeprecation The Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler class is deprecated since Symfony 3.4 and will be removed in 4.0. Use the \SessionHandler class instead.
+ */
+ public function testConstruct()
+ {
+ $handler = new NativeSessionHandler();
+
+ $this->assertInstanceOf('SessionHandler', $handler);
+ $this->assertTrue($handler instanceof NativeSessionHandler);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php
new file mode 100644
index 0000000..718fd0f
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php
@@ -0,0 +1,59 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
+use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
+use Symfony\Component\HttpFoundation\Session\Session;
+
+/**
+ * Test class for NullSessionHandler.
+ *
+ * @author Drak <drak@zikula.org>
+ *
+ * @runTestsInSeparateProcesses
+ * @preserveGlobalState disabled
+ */
+class NullSessionHandlerTest extends TestCase
+{
+ public function testSaveHandlers()
+ {
+ $storage = $this->getStorage();
+ $this->assertEquals('user', ini_get('session.save_handler'));
+ }
+
+ public function testSession()
+ {
+ session_id('nullsessionstorage');
+ $storage = $this->getStorage();
+ $session = new Session($storage);
+ $this->assertNull($session->get('something'));
+ $session->set('something', 'unique');
+ $this->assertEquals('unique', $session->get('something'));
+ }
+
+ public function testNothingIsPersisted()
+ {
+ session_id('nullsessionstorage');
+ $storage = $this->getStorage();
+ $session = new Session($storage);
+ $session->start();
+ $this->assertEquals('nullsessionstorage', $session->getId());
+ $this->assertNull($session->get('something'));
+ }
+
+ public function getStorage()
+ {
+ return new NativeSessionStorage(array(), new NullSessionHandler());
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php
new file mode 100644
index 0000000..0a0e449
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php
@@ -0,0 +1,411 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
+
+/**
+ * @requires extension pdo_sqlite
+ * @group time-sensitive
+ */
+class PdoSessionHandlerTest extends TestCase
+{
+ private $dbFile;
+
+ protected function tearDown()
+ {
+ // make sure the temporary database file is deleted when it has been created (even when a test fails)
+ if ($this->dbFile) {
+ @unlink($this->dbFile);
+ }
+ parent::tearDown();
+ }
+
+ protected function getPersistentSqliteDsn()
+ {
+ $this->dbFile = tempnam(sys_get_temp_dir(), 'sf2_sqlite_sessions');
+
+ return 'sqlite:'.$this->dbFile;
+ }
+
+ protected function getMemorySqlitePdo()
+ {
+ $pdo = new \PDO('sqlite::memory:');
+ $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+ $storage = new PdoSessionHandler($pdo);
+ $storage->createTable();
+
+ return $pdo;
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testWrongPdoErrMode()
+ {
+ $pdo = $this->getMemorySqlitePdo();
+ $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT);
+
+ $storage = new PdoSessionHandler($pdo);
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function testInexistentTable()
+ {
+ $storage = new PdoSessionHandler($this->getMemorySqlitePdo(), array('db_table' => 'inexistent_table'));
+ $storage->open('', 'sid');
+ $storage->read('id');
+ $storage->write('id', 'data');
+ $storage->close();
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function testCreateTableTwice()
+ {
+ $storage = new PdoSessionHandler($this->getMemorySqlitePdo());
+ $storage->createTable();
+ }
+
+ public function testWithLazyDsnConnection()
+ {
+ $dsn = $this->getPersistentSqliteDsn();
+
+ $storage = new PdoSessionHandler($dsn);
+ $storage->createTable();
+ $storage->open('', 'sid');
+ $data = $storage->read('id');
+ $storage->write('id', 'data');
+ $storage->close();
+ $this->assertSame('', $data, 'New session returns empty string data');
+
+ $storage->open('', 'sid');
+ $data = $storage->read('id');
+ $storage->close();
+ $this->assertSame('data', $data, 'Written value can be read back correctly');
+ }
+
+ public function testWithLazySavePathConnection()
+ {
+ $dsn = $this->getPersistentSqliteDsn();
+
+ // Open is called with what ini_set('session.save_path', $dsn) would mean
+ $storage = new PdoSessionHandler(null);
+ $storage->open($dsn, 'sid');
+ $storage->createTable();
+ $data = $storage->read('id');
+ $storage->write('id', 'data');
+ $storage->close();
+ $this->assertSame('', $data, 'New session returns empty string data');
+
+ $storage->open($dsn, 'sid');
+ $data = $storage->read('id');
+ $storage->close();
+ $this->assertSame('data', $data, 'Written value can be read back correctly');
+ }
+
+ public function testReadWriteReadWithNullByte()
+ {
+ $sessionData = 'da'."\0".'ta';
+
+ $storage = new PdoSessionHandler($this->getMemorySqlitePdo());
+ $storage->open('', 'sid');
+ $readData = $storage->read('id');
+ $storage->write('id', $sessionData);
+ $storage->close();
+ $this->assertSame('', $readData, 'New session returns empty string data');
+
+ $storage->open('', 'sid');
+ $readData = $storage->read('id');
+ $storage->close();
+ $this->assertSame($sessionData, $readData, 'Written value can be read back correctly');
+ }
+
+ public function testReadConvertsStreamToString()
+ {
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
+ }
+
+ $pdo = new MockPdo('pgsql');
+ $pdo->prepareResult = $this->getMockBuilder('PDOStatement')->getMock();
+
+ $content = 'foobar';
+ $stream = $this->createStream($content);
+
+ $pdo->prepareResult->expects($this->once())->method('fetchAll')
+ ->will($this->returnValue(array(array($stream, 42, time()))));
+
+ $storage = new PdoSessionHandler($pdo);
+ $result = $storage->read('foo');
+
+ $this->assertSame($content, $result);
+ }
+
+ public function testReadLockedConvertsStreamToString()
+ {
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
+ }
+ if (ini_get('session.use_strict_mode')) {
+ $this->markTestSkipped('Strict mode needs no locking for new sessions.');
+ }
+
+ $pdo = new MockPdo('pgsql');
+ $selectStmt = $this->getMockBuilder('PDOStatement')->getMock();
+ $insertStmt = $this->getMockBuilder('PDOStatement')->getMock();
+
+ $pdo->prepareResult = function ($statement) use ($selectStmt, $insertStmt) {
+ return 0 === strpos($statement, 'INSERT') ? $insertStmt : $selectStmt;
+ };
+
+ $content = 'foobar';
+ $stream = $this->createStream($content);
+ $exception = null;
+
+ $selectStmt->expects($this->atLeast(2))->method('fetchAll')
+ ->will($this->returnCallback(function () use (&$exception, $stream) {
+ return $exception ? array(array($stream, 42, time())) : array();
+ }));
+
+ $insertStmt->expects($this->once())->method('execute')
+ ->will($this->returnCallback(function () use (&$exception) {
+ throw $exception = new \PDOException('', '23');
+ }));
+
+ $storage = new PdoSessionHandler($pdo);
+ $result = $storage->read('foo');
+
+ $this->assertSame($content, $result);
+ }
+
+ public function testReadingRequiresExactlySameId()
+ {
+ $storage = new PdoSessionHandler($this->getMemorySqlitePdo());
+ $storage->open('', 'sid');
+ $storage->write('id', 'data');
+ $storage->write('test', 'data');
+ $storage->write('space ', 'data');
+ $storage->close();
+
+ $storage->open('', 'sid');
+ $readDataCaseSensitive = $storage->read('ID');
+ $readDataNoCharFolding = $storage->read('tést');
+ $readDataKeepSpace = $storage->read('space ');
+ $readDataExtraSpace = $storage->read('space ');
+ $storage->close();
+
+ $this->assertSame('', $readDataCaseSensitive, 'Retrieval by ID should be case-sensitive (collation setting)');
+ $this->assertSame('', $readDataNoCharFolding, 'Retrieval by ID should not do character folding (collation setting)');
+ $this->assertSame('data', $readDataKeepSpace, 'Retrieval by ID requires spaces as-is');
+ $this->assertSame('', $readDataExtraSpace, 'Retrieval by ID requires spaces as-is');
+ }
+
+ /**
+ * Simulates session_regenerate_id(true) which will require an INSERT or UPDATE (replace).
+ */
+ public function testWriteDifferentSessionIdThanRead()
+ {
+ $storage = new PdoSessionHandler($this->getMemorySqlitePdo());
+ $storage->open('', 'sid');
+ $storage->read('id');
+ $storage->destroy('id');
+ $storage->write('new_id', 'data_of_new_session_id');
+ $storage->close();
+
+ $storage->open('', 'sid');
+ $data = $storage->read('new_id');
+ $storage->close();
+
+ $this->assertSame('data_of_new_session_id', $data, 'Data of regenerated session id is available');
+ }
+
+ public function testWrongUsageStillWorks()
+ {
+ // wrong method sequence that should no happen, but still works
+ $storage = new PdoSessionHandler($this->getMemorySqlitePdo());
+ $storage->write('id', 'data');
+ $storage->write('other_id', 'other_data');
+ $storage->destroy('inexistent');
+ $storage->open('', 'sid');
+ $data = $storage->read('id');
+ $otherData = $storage->read('other_id');
+ $storage->close();
+
+ $this->assertSame('data', $data);
+ $this->assertSame('other_data', $otherData);
+ }
+
+ public function testSessionDestroy()
+ {
+ $pdo = $this->getMemorySqlitePdo();
+ $storage = new PdoSessionHandler($pdo);
+
+ $storage->open('', 'sid');
+ $storage->read('id');
+ $storage->write('id', 'data');
+ $storage->close();
+ $this->assertEquals(1, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn());
+
+ $storage->open('', 'sid');
+ $storage->read('id');
+ $storage->destroy('id');
+ $storage->close();
+ $this->assertEquals(0, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn());
+
+ $storage->open('', 'sid');
+ $data = $storage->read('id');
+ $storage->close();
+ $this->assertSame('', $data, 'Destroyed session returns empty string');
+ }
+
+ /**
+ * @runInSeparateProcess
+ */
+ public function testSessionGC()
+ {
+ $previousLifeTime = ini_set('session.gc_maxlifetime', 1000);
+ $pdo = $this->getMemorySqlitePdo();
+ $storage = new PdoSessionHandler($pdo);
+
+ $storage->open('', 'sid');
+ $storage->read('id');
+ $storage->write('id', 'data');
+ $storage->close();
+
+ $storage->open('', 'sid');
+ $storage->read('gc_id');
+ ini_set('session.gc_maxlifetime', -1); // test that you can set lifetime of a session after it has been read
+ $storage->write('gc_id', 'data');
+ $storage->close();
+ $this->assertEquals(2, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'No session pruned because gc not called');
+
+ $storage->open('', 'sid');
+ $data = $storage->read('gc_id');
+ $storage->gc(-1);
+ $storage->close();
+
+ ini_set('session.gc_maxlifetime', $previousLifeTime);
+
+ $this->assertSame('', $data, 'Session already considered garbage, so not returning data even if it is not pruned yet');
+ $this->assertEquals(1, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'Expired session is pruned');
+ }
+
+ public function testGetConnection()
+ {
+ $storage = new PdoSessionHandler($this->getMemorySqlitePdo());
+
+ $method = new \ReflectionMethod($storage, 'getConnection');
+ $method->setAccessible(true);
+
+ $this->assertInstanceOf('\PDO', $method->invoke($storage));
+ }
+
+ public function testGetConnectionConnectsIfNeeded()
+ {
+ $storage = new PdoSessionHandler('sqlite::memory:');
+
+ $method = new \ReflectionMethod($storage, 'getConnection');
+ $method->setAccessible(true);
+
+ $this->assertInstanceOf('\PDO', $method->invoke($storage));
+ }
+
+ /**
+ * @dataProvider provideUrlDsnPairs
+ */
+ public function testUrlDsn($url, $expectedDsn, $expectedUser = null, $expectedPassword = null)
+ {
+ $storage = new PdoSessionHandler($url);
+
+ $this->assertAttributeEquals($expectedDsn, 'dsn', $storage);
+
+ if (null !== $expectedUser) {
+ $this->assertAttributeEquals($expectedUser, 'username', $storage);
+ }
+
+ if (null !== $expectedPassword) {
+ $this->assertAttributeEquals($expectedPassword, 'password', $storage);
+ }
+ }
+
+ public function provideUrlDsnPairs()
+ {
+ yield array('mysql://localhost/test', 'mysql:host=localhost;dbname=test;');
+ yield array('mysql://localhost:56/test', 'mysql:host=localhost;port=56;dbname=test;');
+ yield array('mysql2://root:pwd@localhost/test', 'mysql:host=localhost;dbname=test;', 'root', 'pwd');
+ yield array('postgres://localhost/test', 'pgsql:host=localhost;dbname=test;');
+ yield array('postgresql://localhost:5634/test', 'pgsql:host=localhost;port=5634;dbname=test;');
+ yield array('postgres://root:pwd@localhost/test', 'pgsql:host=localhost;dbname=test;', 'root', 'pwd');
+ yield 'sqlite relative path' => array('sqlite://localhost/tmp/test', 'sqlite:tmp/test');
+ yield 'sqlite absolute path' => array('sqlite://localhost//tmp/test', 'sqlite:/tmp/test');
+ yield 'sqlite relative path without host' => array('sqlite:///tmp/test', 'sqlite:tmp/test');
+ yield 'sqlite absolute path without host' => array('sqlite3:////tmp/test', 'sqlite:/tmp/test');
+ yield array('sqlite://localhost/:memory:', 'sqlite::memory:');
+ yield array('mssql://localhost/test', 'sqlsrv:server=localhost;Database=test');
+ yield array('mssql://localhost:56/test', 'sqlsrv:server=localhost,56;Database=test');
+ }
+
+ private function createStream($content)
+ {
+ $stream = tmpfile();
+ fwrite($stream, $content);
+ fseek($stream, 0);
+
+ return $stream;
+ }
+}
+
+class MockPdo extends \PDO
+{
+ public $prepareResult;
+ private $driverName;
+ private $errorMode;
+
+ public function __construct($driverName = null, $errorMode = null)
+ {
+ $this->driverName = $driverName;
+ $this->errorMode = null !== $errorMode ?: \PDO::ERRMODE_EXCEPTION;
+ }
+
+ public function getAttribute($attribute)
+ {
+ if (\PDO::ATTR_ERRMODE === $attribute) {
+ return $this->errorMode;
+ }
+
+ if (\PDO::ATTR_DRIVER_NAME === $attribute) {
+ return $this->driverName;
+ }
+
+ return parent::getAttribute($attribute);
+ }
+
+ public function prepare($statement, $driverOptions = array())
+ {
+ return is_callable($this->prepareResult)
+ ? call_user_func($this->prepareResult, $statement, $driverOptions)
+ : $this->prepareResult;
+ }
+
+ public function beginTransaction()
+ {
+ }
+
+ public function rollBack()
+ {
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php
new file mode 100644
index 0000000..b02c41a
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php
@@ -0,0 +1,189 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
+
+class StrictSessionHandlerTest extends TestCase
+{
+ public function testOpen()
+ {
+ $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $handler->expects($this->once())->method('open')
+ ->with('path', 'name')->willReturn(true);
+ $proxy = new StrictSessionHandler($handler);
+
+ $this->assertInstanceOf('SessionUpdateTimestampHandlerInterface', $proxy);
+ $this->assertInstanceOf(AbstractSessionHandler::class, $proxy);
+ $this->assertTrue($proxy->open('path', 'name'));
+ }
+
+ public function testCloseSession()
+ {
+ $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $handler->expects($this->once())->method('close')
+ ->willReturn(true);
+ $proxy = new StrictSessionHandler($handler);
+
+ $this->assertTrue($proxy->close());
+ }
+
+ public function testValidateIdOK()
+ {
+ $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $handler->expects($this->once())->method('read')
+ ->with('id')->willReturn('data');
+ $proxy = new StrictSessionHandler($handler);
+
+ $this->assertTrue($proxy->validateId('id'));
+ }
+
+ public function testValidateIdKO()
+ {
+ $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $handler->expects($this->once())->method('read')
+ ->with('id')->willReturn('');
+ $proxy = new StrictSessionHandler($handler);
+
+ $this->assertFalse($proxy->validateId('id'));
+ }
+
+ public function testRead()
+ {
+ $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $handler->expects($this->once())->method('read')
+ ->with('id')->willReturn('data');
+ $proxy = new StrictSessionHandler($handler);
+
+ $this->assertSame('data', $proxy->read('id'));
+ }
+
+ public function testReadWithValidateIdOK()
+ {
+ $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $handler->expects($this->once())->method('read')
+ ->with('id')->willReturn('data');
+ $proxy = new StrictSessionHandler($handler);
+
+ $this->assertTrue($proxy->validateId('id'));
+ $this->assertSame('data', $proxy->read('id'));
+ }
+
+ public function testReadWithValidateIdMismatch()
+ {
+ $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $handler->expects($this->exactly(2))->method('read')
+ ->withConsecutive(array('id1'), array('id2'))
+ ->will($this->onConsecutiveCalls('data1', 'data2'));
+ $proxy = new StrictSessionHandler($handler);
+
+ $this->assertTrue($proxy->validateId('id1'));
+ $this->assertSame('data2', $proxy->read('id2'));
+ }
+
+ public function testUpdateTimestamp()
+ {
+ $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $handler->expects($this->once())->method('write')
+ ->with('id', 'data')->willReturn(true);
+ $proxy = new StrictSessionHandler($handler);
+
+ $this->assertTrue($proxy->updateTimestamp('id', 'data'));
+ }
+
+ public function testWrite()
+ {
+ $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $handler->expects($this->once())->method('write')
+ ->with('id', 'data')->willReturn(true);
+ $proxy = new StrictSessionHandler($handler);
+
+ $this->assertTrue($proxy->write('id', 'data'));
+ }
+
+ public function testWriteEmptyNewSession()
+ {
+ $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $handler->expects($this->once())->method('read')
+ ->with('id')->willReturn('');
+ $handler->expects($this->never())->method('write');
+ $handler->expects($this->once())->method('destroy')->willReturn(true);
+ $proxy = new StrictSessionHandler($handler);
+
+ $this->assertFalse($proxy->validateId('id'));
+ $this->assertSame('', $proxy->read('id'));
+ $this->assertTrue($proxy->write('id', ''));
+ }
+
+ public function testWriteEmptyExistingSession()
+ {
+ $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $handler->expects($this->once())->method('read')
+ ->with('id')->willReturn('data');
+ $handler->expects($this->never())->method('write');
+ $handler->expects($this->once())->method('destroy')->willReturn(true);
+ $proxy = new StrictSessionHandler($handler);
+
+ $this->assertSame('data', $proxy->read('id'));
+ $this->assertTrue($proxy->write('id', ''));
+ }
+
+ public function testDestroy()
+ {
+ $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $handler->expects($this->once())->method('destroy')
+ ->with('id')->willReturn(true);
+ $proxy = new StrictSessionHandler($handler);
+
+ $this->assertTrue($proxy->destroy('id'));
+ }
+
+ public function testDestroyNewSession()
+ {
+ $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $handler->expects($this->once())->method('read')
+ ->with('id')->willReturn('');
+ $handler->expects($this->once())->method('destroy')->willReturn(true);
+ $proxy = new StrictSessionHandler($handler);
+
+ $this->assertSame('', $proxy->read('id'));
+ $this->assertTrue($proxy->destroy('id'));
+ }
+
+ public function testDestroyNonEmptyNewSession()
+ {
+ $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $handler->expects($this->once())->method('read')
+ ->with('id')->willReturn('');
+ $handler->expects($this->once())->method('write')
+ ->with('id', 'data')->willReturn(true);
+ $handler->expects($this->once())->method('destroy')
+ ->with('id')->willReturn(true);
+ $proxy = new StrictSessionHandler($handler);
+
+ $this->assertSame('', $proxy->read('id'));
+ $this->assertTrue($proxy->write('id', 'data'));
+ $this->assertTrue($proxy->destroy('id'));
+ }
+
+ public function testGc()
+ {
+ $handler = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $handler->expects($this->once())->method('gc')
+ ->with(123)->willReturn(true);
+ $proxy = new StrictSessionHandler($handler);
+
+ $this->assertTrue($proxy->gc(123));
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php
new file mode 100644
index 0000000..898a7d1
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php
@@ -0,0 +1,97 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler;
+
+/**
+ * @author Adrien Brault <adrien.brault@gmail.com>
+ *
+ * @group legacy
+ */
+class WriteCheckSessionHandlerTest extends TestCase
+{
+ public function test()
+ {
+ $wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
+
+ $wrappedSessionHandlerMock
+ ->expects($this->once())
+ ->method('close')
+ ->with()
+ ->will($this->returnValue(true))
+ ;
+
+ $this->assertTrue($writeCheckSessionHandler->close());
+ }
+
+ public function testWrite()
+ {
+ $wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
+
+ $wrappedSessionHandlerMock
+ ->expects($this->once())
+ ->method('write')
+ ->with('foo', 'bar')
+ ->will($this->returnValue(true))
+ ;
+
+ $this->assertTrue($writeCheckSessionHandler->write('foo', 'bar'));
+ }
+
+ public function testSkippedWrite()
+ {
+ $wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
+
+ $wrappedSessionHandlerMock
+ ->expects($this->once())
+ ->method('read')
+ ->with('foo')
+ ->will($this->returnValue('bar'))
+ ;
+
+ $wrappedSessionHandlerMock
+ ->expects($this->never())
+ ->method('write')
+ ;
+
+ $this->assertEquals('bar', $writeCheckSessionHandler->read('foo'));
+ $this->assertTrue($writeCheckSessionHandler->write('foo', 'bar'));
+ }
+
+ public function testNonSkippedWrite()
+ {
+ $wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
+
+ $wrappedSessionHandlerMock
+ ->expects($this->once())
+ ->method('read')
+ ->with('foo')
+ ->will($this->returnValue('bar'))
+ ;
+
+ $wrappedSessionHandlerMock
+ ->expects($this->once())
+ ->method('write')
+ ->with('foo', 'baZZZ')
+ ->will($this->returnValue(true))
+ ;
+
+ $this->assertEquals('bar', $writeCheckSessionHandler->read('foo'));
+ $this->assertTrue($writeCheckSessionHandler->write('foo', 'baZZZ'));
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php
new file mode 100644
index 0000000..69cf616
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php
@@ -0,0 +1,139 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
+
+/**
+ * Test class for MetadataBag.
+ *
+ * @group time-sensitive
+ */
+class MetadataBagTest extends TestCase
+{
+ /**
+ * @var MetadataBag
+ */
+ protected $bag;
+
+ protected $array = array();
+
+ protected function setUp()
+ {
+ parent::setUp();
+ $this->bag = new MetadataBag();
+ $this->array = array(MetadataBag::CREATED => 1234567, MetadataBag::UPDATED => 12345678, MetadataBag::LIFETIME => 0);
+ $this->bag->initialize($this->array);
+ }
+
+ protected function tearDown()
+ {
+ $this->array = array();
+ $this->bag = null;
+ parent::tearDown();
+ }
+
+ public function testInitialize()
+ {
+ $sessionMetadata = array();
+
+ $bag1 = new MetadataBag();
+ $bag1->initialize($sessionMetadata);
+ $this->assertGreaterThanOrEqual(time(), $bag1->getCreated());
+ $this->assertEquals($bag1->getCreated(), $bag1->getLastUsed());
+
+ sleep(1);
+ $bag2 = new MetadataBag();
+ $bag2->initialize($sessionMetadata);
+ $this->assertEquals($bag1->getCreated(), $bag2->getCreated());
+ $this->assertEquals($bag1->getLastUsed(), $bag2->getLastUsed());
+ $this->assertEquals($bag2->getCreated(), $bag2->getLastUsed());
+
+ sleep(1);
+ $bag3 = new MetadataBag();
+ $bag3->initialize($sessionMetadata);
+ $this->assertEquals($bag1->getCreated(), $bag3->getCreated());
+ $this->assertGreaterThan($bag2->getLastUsed(), $bag3->getLastUsed());
+ $this->assertNotEquals($bag3->getCreated(), $bag3->getLastUsed());
+ }
+
+ public function testGetSetName()
+ {
+ $this->assertEquals('__metadata', $this->bag->getName());
+ $this->bag->setName('foo');
+ $this->assertEquals('foo', $this->bag->getName());
+ }
+
+ public function testGetStorageKey()
+ {
+ $this->assertEquals('_sf2_meta', $this->bag->getStorageKey());
+ }
+
+ public function testGetLifetime()
+ {
+ $bag = new MetadataBag();
+ $array = array(MetadataBag::CREATED => 1234567, MetadataBag::UPDATED => 12345678, MetadataBag::LIFETIME => 1000);
+ $bag->initialize($array);
+ $this->assertEquals(1000, $bag->getLifetime());
+ }
+
+ public function testGetCreated()
+ {
+ $this->assertEquals(1234567, $this->bag->getCreated());
+ }
+
+ public function testGetLastUsed()
+ {
+ $this->assertLessThanOrEqual(time(), $this->bag->getLastUsed());
+ }
+
+ public function testClear()
+ {
+ $this->bag->clear();
+
+ // the clear method has no side effects, we just want to ensure it doesn't trigger any exceptions
+ $this->addToAssertionCount(1);
+ }
+
+ public function testSkipLastUsedUpdate()
+ {
+ $bag = new MetadataBag('', 30);
+ $timeStamp = time();
+
+ $created = $timeStamp - 15;
+ $sessionMetadata = array(
+ MetadataBag::CREATED => $created,
+ MetadataBag::UPDATED => $created,
+ MetadataBag::LIFETIME => 1000,
+ );
+ $bag->initialize($sessionMetadata);
+
+ $this->assertEquals($created, $sessionMetadata[MetadataBag::UPDATED]);
+ }
+
+ public function testDoesNotSkipLastUsedUpdate()
+ {
+ $bag = new MetadataBag('', 30);
+ $timeStamp = time();
+
+ $created = $timeStamp - 45;
+ $sessionMetadata = array(
+ MetadataBag::CREATED => $created,
+ MetadataBag::UPDATED => $created,
+ MetadataBag::LIFETIME => 1000,
+ );
+ $bag->initialize($sessionMetadata);
+
+ $this->assertEquals($timeStamp, $sessionMetadata[MetadataBag::UPDATED]);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php
new file mode 100644
index 0000000..82df554
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php
@@ -0,0 +1,131 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+
+/**
+ * Test class for MockArraySessionStorage.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class MockArraySessionStorageTest extends TestCase
+{
+ /**
+ * @var MockArraySessionStorage
+ */
+ private $storage;
+
+ /**
+ * @var AttributeBag
+ */
+ private $attributes;
+
+ /**
+ * @var FlashBag
+ */
+ private $flashes;
+
+ private $data;
+
+ protected function setUp()
+ {
+ $this->attributes = new AttributeBag();
+ $this->flashes = new FlashBag();
+
+ $this->data = array(
+ $this->attributes->getStorageKey() => array('foo' => 'bar'),
+ $this->flashes->getStorageKey() => array('notice' => 'hello'),
+ );
+
+ $this->storage = new MockArraySessionStorage();
+ $this->storage->registerBag($this->flashes);
+ $this->storage->registerBag($this->attributes);
+ $this->storage->setSessionData($this->data);
+ }
+
+ protected function tearDown()
+ {
+ $this->data = null;
+ $this->flashes = null;
+ $this->attributes = null;
+ $this->storage = null;
+ }
+
+ public function testStart()
+ {
+ $this->assertEquals('', $this->storage->getId());
+ $this->storage->start();
+ $id = $this->storage->getId();
+ $this->assertNotEquals('', $id);
+ $this->storage->start();
+ $this->assertEquals($id, $this->storage->getId());
+ }
+
+ public function testRegenerate()
+ {
+ $this->storage->start();
+ $id = $this->storage->getId();
+ $this->storage->regenerate();
+ $this->assertNotEquals($id, $this->storage->getId());
+ $this->assertEquals(array('foo' => 'bar'), $this->storage->getBag('attributes')->all());
+ $this->assertEquals(array('notice' => 'hello'), $this->storage->getBag('flashes')->peekAll());
+
+ $id = $this->storage->getId();
+ $this->storage->regenerate(true);
+ $this->assertNotEquals($id, $this->storage->getId());
+ $this->assertEquals(array('foo' => 'bar'), $this->storage->getBag('attributes')->all());
+ $this->assertEquals(array('notice' => 'hello'), $this->storage->getBag('flashes')->peekAll());
+ }
+
+ public function testGetId()
+ {
+ $this->assertEquals('', $this->storage->getId());
+ $this->storage->start();
+ $this->assertNotEquals('', $this->storage->getId());
+ }
+
+ public function testClearClearsBags()
+ {
+ $this->storage->clear();
+
+ $this->assertSame(array(), $this->storage->getBag('attributes')->all());
+ $this->assertSame(array(), $this->storage->getBag('flashes')->peekAll());
+ }
+
+ public function testClearStartsSession()
+ {
+ $this->storage->clear();
+
+ $this->assertTrue($this->storage->isStarted());
+ }
+
+ public function testClearWithNoBagsStartsSession()
+ {
+ $storage = new MockArraySessionStorage();
+
+ $storage->clear();
+
+ $this->assertTrue($storage->isStarted());
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function testUnstartedSave()
+ {
+ $this->storage->save();
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php
new file mode 100644
index 0000000..53accd3
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php
@@ -0,0 +1,127 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+
+/**
+ * Test class for MockFileSessionStorage.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class MockFileSessionStorageTest extends TestCase
+{
+ /**
+ * @var string
+ */
+ private $sessionDir;
+
+ /**
+ * @var MockFileSessionStorage
+ */
+ protected $storage;
+
+ protected function setUp()
+ {
+ $this->sessionDir = sys_get_temp_dir().'/sf2test';
+ $this->storage = $this->getStorage();
+ }
+
+ protected function tearDown()
+ {
+ $this->sessionDir = null;
+ $this->storage = null;
+ array_map('unlink', glob($this->sessionDir.'/*.session'));
+ if (is_dir($this->sessionDir)) {
+ rmdir($this->sessionDir);
+ }
+ }
+
+ public function testStart()
+ {
+ $this->assertEquals('', $this->storage->getId());
+ $this->assertTrue($this->storage->start());
+ $id = $this->storage->getId();
+ $this->assertNotEquals('', $this->storage->getId());
+ $this->assertTrue($this->storage->start());
+ $this->assertEquals($id, $this->storage->getId());
+ }
+
+ public function testRegenerate()
+ {
+ $this->storage->start();
+ $this->storage->getBag('attributes')->set('regenerate', 1234);
+ $this->storage->regenerate();
+ $this->assertEquals(1234, $this->storage->getBag('attributes')->get('regenerate'));
+ $this->storage->regenerate(true);
+ $this->assertEquals(1234, $this->storage->getBag('attributes')->get('regenerate'));
+ }
+
+ public function testGetId()
+ {
+ $this->assertEquals('', $this->storage->getId());
+ $this->storage->start();
+ $this->assertNotEquals('', $this->storage->getId());
+ }
+
+ public function testSave()
+ {
+ $this->storage->start();
+ $id = $this->storage->getId();
+ $this->assertNotEquals('108', $this->storage->getBag('attributes')->get('new'));
+ $this->assertFalse($this->storage->getBag('flashes')->has('newkey'));
+ $this->storage->getBag('attributes')->set('new', '108');
+ $this->storage->getBag('flashes')->set('newkey', 'test');
+ $this->storage->save();
+
+ $storage = $this->getStorage();
+ $storage->setId($id);
+ $storage->start();
+ $this->assertEquals('108', $storage->getBag('attributes')->get('new'));
+ $this->assertTrue($storage->getBag('flashes')->has('newkey'));
+ $this->assertEquals(array('test'), $storage->getBag('flashes')->peek('newkey'));
+ }
+
+ public function testMultipleInstances()
+ {
+ $storage1 = $this->getStorage();
+ $storage1->start();
+ $storage1->getBag('attributes')->set('foo', 'bar');
+ $storage1->save();
+
+ $storage2 = $this->getStorage();
+ $storage2->setId($storage1->getId());
+ $storage2->start();
+ $this->assertEquals('bar', $storage2->getBag('attributes')->get('foo'), 'values persist between instances');
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function testSaveWithoutStart()
+ {
+ $storage1 = $this->getStorage();
+ $storage1->save();
+ }
+
+ private function getStorage()
+ {
+ $storage = new MockFileSessionStorage($this->sessionDir);
+ $storage->registerBag(new FlashBag());
+ $storage->registerBag(new AttributeBag());
+
+ return $storage;
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php
new file mode 100644
index 0000000..8fb8b42
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php
@@ -0,0 +1,277 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
+use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
+
+/**
+ * Test class for NativeSessionStorage.
+ *
+ * @author Drak <drak@zikula.org>
+ *
+ * These tests require separate processes.
+ *
+ * @runTestsInSeparateProcesses
+ * @preserveGlobalState disabled
+ */
+class NativeSessionStorageTest extends TestCase
+{
+ private $savePath;
+
+ protected function setUp()
+ {
+ $this->iniSet('session.save_handler', 'files');
+ $this->iniSet('session.save_path', $this->savePath = sys_get_temp_dir().'/sf2test');
+ if (!is_dir($this->savePath)) {
+ mkdir($this->savePath);
+ }
+ }
+
+ protected function tearDown()
+ {
+ session_write_close();
+ array_map('unlink', glob($this->savePath.'/*'));
+ if (is_dir($this->savePath)) {
+ rmdir($this->savePath);
+ }
+
+ $this->savePath = null;
+ }
+
+ /**
+ * @return NativeSessionStorage
+ */
+ protected function getStorage(array $options = array())
+ {
+ $storage = new NativeSessionStorage($options);
+ $storage->registerBag(new AttributeBag());
+
+ return $storage;
+ }
+
+ public function testBag()
+ {
+ $storage = $this->getStorage();
+ $bag = new FlashBag();
+ $storage->registerBag($bag);
+ $this->assertSame($bag, $storage->getBag($bag->getName()));
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testRegisterBagException()
+ {
+ $storage = $this->getStorage();
+ $storage->getBag('non_existing');
+ }
+
+ /**
+ * @expectedException \LogicException
+ */
+ public function testRegisterBagForAStartedSessionThrowsException()
+ {
+ $storage = $this->getStorage();
+ $storage->start();
+ $storage->registerBag(new AttributeBag());
+ }
+
+ public function testGetId()
+ {
+ $storage = $this->getStorage();
+ $this->assertSame('', $storage->getId(), 'Empty ID before starting session');
+
+ $storage->start();
+ $id = $storage->getId();
+ $this->assertInternalType('string', $id);
+ $this->assertNotSame('', $id);
+
+ $storage->save();
+ $this->assertSame($id, $storage->getId(), 'ID stays after saving session');
+ }
+
+ public function testRegenerate()
+ {
+ $storage = $this->getStorage();
+ $storage->start();
+ $id = $storage->getId();
+ $storage->getBag('attributes')->set('lucky', 7);
+ $storage->regenerate();
+ $this->assertNotEquals($id, $storage->getId());
+ $this->assertEquals(7, $storage->getBag('attributes')->get('lucky'));
+ }
+
+ public function testRegenerateDestroy()
+ {
+ $storage = $this->getStorage();
+ $storage->start();
+ $id = $storage->getId();
+ $storage->getBag('attributes')->set('legs', 11);
+ $storage->regenerate(true);
+ $this->assertNotEquals($id, $storage->getId());
+ $this->assertEquals(11, $storage->getBag('attributes')->get('legs'));
+ }
+
+ public function testSessionGlobalIsUpToDateAfterIdRegeneration()
+ {
+ $storage = $this->getStorage();
+ $storage->start();
+ $storage->getBag('attributes')->set('lucky', 7);
+ $storage->regenerate();
+ $storage->getBag('attributes')->set('lucky', 42);
+
+ $this->assertEquals(42, $_SESSION['_sf2_attributes']['lucky']);
+ }
+
+ public function testRegenerationFailureDoesNotFlagStorageAsStarted()
+ {
+ $storage = $this->getStorage();
+ $this->assertFalse($storage->regenerate());
+ $this->assertFalse($storage->isStarted());
+ }
+
+ public function testDefaultSessionCacheLimiter()
+ {
+ $this->iniSet('session.cache_limiter', 'nocache');
+
+ $storage = new NativeSessionStorage();
+ $this->assertEquals('', ini_get('session.cache_limiter'));
+ }
+
+ public function testExplicitSessionCacheLimiter()
+ {
+ $this->iniSet('session.cache_limiter', 'nocache');
+
+ $storage = new NativeSessionStorage(array('cache_limiter' => 'public'));
+ $this->assertEquals('public', ini_get('session.cache_limiter'));
+ }
+
+ public function testCookieOptions()
+ {
+ $options = array(
+ 'cookie_lifetime' => 123456,
+ 'cookie_path' => '/my/cookie/path',
+ 'cookie_domain' => 'symfony.example.com',
+ 'cookie_secure' => true,
+ 'cookie_httponly' => false,
+ );
+
+ $this->getStorage($options);
+ $temp = session_get_cookie_params();
+ $gco = array();
+
+ foreach ($temp as $key => $value) {
+ $gco['cookie_'.$key] = $value;
+ }
+
+ $this->assertEquals($options, $gco);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testSetSaveHandlerException()
+ {
+ $storage = $this->getStorage();
+ $storage->setSaveHandler(new \stdClass());
+ }
+
+ public function testSetSaveHandler()
+ {
+ $this->iniSet('session.save_handler', 'files');
+ $storage = $this->getStorage();
+ $storage->setSaveHandler();
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+ $storage->setSaveHandler(null);
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+ $storage->setSaveHandler(new SessionHandlerProxy(new NativeFileSessionHandler()));
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+ $storage->setSaveHandler(new NativeFileSessionHandler());
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+ $storage->setSaveHandler(new SessionHandlerProxy(new NullSessionHandler()));
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+ $storage->setSaveHandler(new NullSessionHandler());
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function testStarted()
+ {
+ $storage = $this->getStorage();
+
+ $this->assertFalse($storage->getSaveHandler()->isActive());
+ $this->assertFalse($storage->isStarted());
+
+ session_start();
+ $this->assertTrue(isset($_SESSION));
+ $this->assertTrue($storage->getSaveHandler()->isActive());
+
+ // PHP session might have started, but the storage driver has not, so false is correct here
+ $this->assertFalse($storage->isStarted());
+
+ $key = $storage->getMetadataBag()->getStorageKey();
+ $this->assertArrayNotHasKey($key, $_SESSION);
+ $storage->start();
+ }
+
+ public function testRestart()
+ {
+ $storage = $this->getStorage();
+ $storage->start();
+ $id = $storage->getId();
+ $storage->getBag('attributes')->set('lucky', 7);
+ $storage->save();
+ $storage->start();
+ $this->assertSame($id, $storage->getId(), 'Same session ID after restarting');
+ $this->assertSame(7, $storage->getBag('attributes')->get('lucky'), 'Data still available');
+ }
+
+ public function testCanCreateNativeSessionStorageWhenSessionAlreadyStarted()
+ {
+ session_start();
+ $this->getStorage();
+
+ // Assert no exception has been thrown by `getStorage()`
+ $this->addToAssertionCount(1);
+ }
+
+ public function testSetSessionOptionsOnceSessionStartedIsIgnored()
+ {
+ session_start();
+ $this->getStorage(array(
+ 'name' => 'something-else',
+ ));
+
+ // Assert no exception has been thrown by `getStorage()`
+ $this->addToAssertionCount(1);
+ }
+
+ public function testGetBagsOnceSessionStartedIsIgnored()
+ {
+ session_start();
+ $bag = new AttributeBag();
+ $bag->setName('flashes');
+
+ $storage = $this->getStorage();
+ $storage->registerBag($bag);
+
+ $this->assertEquals($storage->getBag('flashes'), $bag);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php
new file mode 100644
index 0000000..958dc0b
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php
@@ -0,0 +1,96 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+
+/**
+ * Test class for PhpSessionStorage.
+ *
+ * @author Drak <drak@zikula.org>
+ *
+ * These tests require separate processes.
+ *
+ * @runTestsInSeparateProcesses
+ * @preserveGlobalState disabled
+ */
+class PhpBridgeSessionStorageTest extends TestCase
+{
+ private $savePath;
+
+ protected function setUp()
+ {
+ $this->iniSet('session.save_handler', 'files');
+ $this->iniSet('session.save_path', $this->savePath = sys_get_temp_dir().'/sf2test');
+ if (!is_dir($this->savePath)) {
+ mkdir($this->savePath);
+ }
+ }
+
+ protected function tearDown()
+ {
+ session_write_close();
+ array_map('unlink', glob($this->savePath.'/*'));
+ if (is_dir($this->savePath)) {
+ rmdir($this->savePath);
+ }
+
+ $this->savePath = null;
+ }
+
+ /**
+ * @return PhpBridgeSessionStorage
+ */
+ protected function getStorage()
+ {
+ $storage = new PhpBridgeSessionStorage();
+ $storage->registerBag(new AttributeBag());
+
+ return $storage;
+ }
+
+ public function testPhpSession()
+ {
+ $storage = $this->getStorage();
+
+ $this->assertFalse($storage->getSaveHandler()->isActive());
+ $this->assertFalse($storage->isStarted());
+
+ session_start();
+ $this->assertTrue(isset($_SESSION));
+ // in PHP 5.4 we can reliably detect a session started
+ $this->assertTrue($storage->getSaveHandler()->isActive());
+ // PHP session might have started, but the storage driver has not, so false is correct here
+ $this->assertFalse($storage->isStarted());
+
+ $key = $storage->getMetadataBag()->getStorageKey();
+ $this->assertArrayNotHasKey($key, $_SESSION);
+ $storage->start();
+ $this->assertArrayHasKey($key, $_SESSION);
+ }
+
+ public function testClear()
+ {
+ $storage = $this->getStorage();
+ session_start();
+ $_SESSION['drak'] = 'loves symfony';
+ $storage->getBag('attributes')->set('symfony', 'greatness');
+ $key = $storage->getBag('attributes')->getStorageKey();
+ $this->assertEquals($_SESSION[$key], array('symfony' => 'greatness'));
+ $this->assertEquals($_SESSION['drak'], 'loves symfony');
+ $storage->clear();
+ $this->assertEquals($_SESSION[$key], array());
+ $this->assertEquals($_SESSION['drak'], 'loves symfony');
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php
new file mode 100644
index 0000000..cbb291f
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php
@@ -0,0 +1,113 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Proxy;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
+
+/**
+ * Test class for AbstractProxy.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class AbstractProxyTest extends TestCase
+{
+ /**
+ * @var AbstractProxy
+ */
+ protected $proxy;
+
+ protected function setUp()
+ {
+ $this->proxy = $this->getMockForAbstractClass(AbstractProxy::class);
+ }
+
+ protected function tearDown()
+ {
+ $this->proxy = null;
+ }
+
+ public function testGetSaveHandlerName()
+ {
+ $this->assertNull($this->proxy->getSaveHandlerName());
+ }
+
+ public function testIsSessionHandlerInterface()
+ {
+ $this->assertFalse($this->proxy->isSessionHandlerInterface());
+ $sh = new SessionHandlerProxy(new \SessionHandler());
+ $this->assertTrue($sh->isSessionHandlerInterface());
+ }
+
+ public function testIsWrapper()
+ {
+ $this->assertFalse($this->proxy->isWrapper());
+ }
+
+ /**
+ * @runInSeparateProcess
+ * @preserveGlobalState disabled
+ */
+ public function testIsActive()
+ {
+ $this->assertFalse($this->proxy->isActive());
+ session_start();
+ $this->assertTrue($this->proxy->isActive());
+ }
+
+ /**
+ * @runInSeparateProcess
+ * @preserveGlobalState disabled
+ */
+ public function testName()
+ {
+ $this->assertEquals(session_name(), $this->proxy->getName());
+ $this->proxy->setName('foo');
+ $this->assertEquals('foo', $this->proxy->getName());
+ $this->assertEquals(session_name(), $this->proxy->getName());
+ }
+
+ /**
+ * @runInSeparateProcess
+ * @preserveGlobalState disabled
+ * @expectedException \LogicException
+ */
+ public function testNameException()
+ {
+ session_start();
+ $this->proxy->setName('foo');
+ }
+
+ /**
+ * @runInSeparateProcess
+ * @preserveGlobalState disabled
+ */
+ public function testId()
+ {
+ $this->assertEquals(session_id(), $this->proxy->getId());
+ $this->proxy->setId('foo');
+ $this->assertEquals('foo', $this->proxy->getId());
+ $this->assertEquals(session_id(), $this->proxy->getId());
+ }
+
+ /**
+ * @runInSeparateProcess
+ * @preserveGlobalState disabled
+ * @expectedException \LogicException
+ */
+ public function testIdException()
+ {
+ session_start();
+ $this->proxy->setId('foo');
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/NativeProxyTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/NativeProxyTest.php
new file mode 100644
index 0000000..ed4fee6
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/NativeProxyTest.php
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Proxy;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy;
+
+/**
+ * Test class for NativeProxy.
+ *
+ * @group legacy
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class NativeProxyTest extends TestCase
+{
+ public function testIsWrapper()
+ {
+ $proxy = new NativeProxy();
+ $this->assertFalse($proxy->isWrapper());
+ }
+
+ public function testGetSaveHandlerName()
+ {
+ $name = ini_get('session.save_handler');
+ $proxy = new NativeProxy();
+ $this->assertEquals($name, $proxy->getSaveHandlerName());
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php
new file mode 100644
index 0000000..6828253
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php
@@ -0,0 +1,124 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Proxy;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
+
+/**
+ * Tests for SessionHandlerProxy class.
+ *
+ * @author Drak <drak@zikula.org>
+ *
+ * @runTestsInSeparateProcesses
+ * @preserveGlobalState disabled
+ */
+class SessionHandlerProxyTest extends TestCase
+{
+ /**
+ * @var \PHPUnit_Framework_MockObject_Matcher
+ */
+ private $mock;
+
+ /**
+ * @var SessionHandlerProxy
+ */
+ private $proxy;
+
+ protected function setUp()
+ {
+ $this->mock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
+ $this->proxy = new SessionHandlerProxy($this->mock);
+ }
+
+ protected function tearDown()
+ {
+ $this->mock = null;
+ $this->proxy = null;
+ }
+
+ public function testOpenTrue()
+ {
+ $this->mock->expects($this->once())
+ ->method('open')
+ ->will($this->returnValue(true));
+
+ $this->assertFalse($this->proxy->isActive());
+ $this->proxy->open('name', 'id');
+ $this->assertFalse($this->proxy->isActive());
+ }
+
+ public function testOpenFalse()
+ {
+ $this->mock->expects($this->once())
+ ->method('open')
+ ->will($this->returnValue(false));
+
+ $this->assertFalse($this->proxy->isActive());
+ $this->proxy->open('name', 'id');
+ $this->assertFalse($this->proxy->isActive());
+ }
+
+ public function testClose()
+ {
+ $this->mock->expects($this->once())
+ ->method('close')
+ ->will($this->returnValue(true));
+
+ $this->assertFalse($this->proxy->isActive());
+ $this->proxy->close();
+ $this->assertFalse($this->proxy->isActive());
+ }
+
+ public function testCloseFalse()
+ {
+ $this->mock->expects($this->once())
+ ->method('close')
+ ->will($this->returnValue(false));
+
+ $this->assertFalse($this->proxy->isActive());
+ $this->proxy->close();
+ $this->assertFalse($this->proxy->isActive());
+ }
+
+ public function testRead()
+ {
+ $this->mock->expects($this->once())
+ ->method('read');
+
+ $this->proxy->read('id');
+ }
+
+ public function testWrite()
+ {
+ $this->mock->expects($this->once())
+ ->method('write');
+
+ $this->proxy->write('id', 'data');
+ }
+
+ public function testDestroy()
+ {
+ $this->mock->expects($this->once())
+ ->method('destroy');
+
+ $this->proxy->destroy('id');
+ }
+
+ public function testGc()
+ {
+ $this->mock->expects($this->once())
+ ->method('gc');
+
+ $this->proxy->gc(86400);
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/StreamedResponseTest.php b/assets/php/vendor/symfony/http-foundation/Tests/StreamedResponseTest.php
new file mode 100644
index 0000000..c2ded99
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/StreamedResponseTest.php
@@ -0,0 +1,126 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\StreamedResponse;
+
+class StreamedResponseTest extends TestCase
+{
+ public function testConstructor()
+ {
+ $response = new StreamedResponse(function () { echo 'foo'; }, 404, array('Content-Type' => 'text/plain'));
+
+ $this->assertEquals(404, $response->getStatusCode());
+ $this->assertEquals('text/plain', $response->headers->get('Content-Type'));
+ }
+
+ public function testPrepareWith11Protocol()
+ {
+ $response = new StreamedResponse(function () { echo 'foo'; });
+ $request = Request::create('/');
+ $request->server->set('SERVER_PROTOCOL', 'HTTP/1.1');
+
+ $response->prepare($request);
+
+ $this->assertEquals('1.1', $response->getProtocolVersion());
+ $this->assertNotEquals('chunked', $response->headers->get('Transfer-Encoding'), 'Apache assumes responses with a Transfer-Encoding header set to chunked to already be encoded.');
+ }
+
+ public function testPrepareWith10Protocol()
+ {
+ $response = new StreamedResponse(function () { echo 'foo'; });
+ $request = Request::create('/');
+ $request->server->set('SERVER_PROTOCOL', 'HTTP/1.0');
+
+ $response->prepare($request);
+
+ $this->assertEquals('1.0', $response->getProtocolVersion());
+ $this->assertNull($response->headers->get('Transfer-Encoding'));
+ }
+
+ public function testPrepareWithHeadRequest()
+ {
+ $response = new StreamedResponse(function () { echo 'foo'; }, 200, array('Content-Length' => '123'));
+ $request = Request::create('/', 'HEAD');
+
+ $response->prepare($request);
+
+ $this->assertSame('123', $response->headers->get('Content-Length'));
+ }
+
+ public function testPrepareWithCacheHeaders()
+ {
+ $response = new StreamedResponse(function () { echo 'foo'; }, 200, array('Cache-Control' => 'max-age=600, public'));
+ $request = Request::create('/', 'GET');
+
+ $response->prepare($request);
+ $this->assertEquals('max-age=600, public', $response->headers->get('Cache-Control'));
+ }
+
+ public function testSendContent()
+ {
+ $called = 0;
+
+ $response = new StreamedResponse(function () use (&$called) { ++$called; });
+
+ $response->sendContent();
+ $this->assertEquals(1, $called);
+
+ $response->sendContent();
+ $this->assertEquals(1, $called);
+ }
+
+ /**
+ * @expectedException \LogicException
+ */
+ public function testSendContentWithNonCallable()
+ {
+ $response = new StreamedResponse(null);
+ $response->sendContent();
+ }
+
+ /**
+ * @expectedException \LogicException
+ */
+ public function testSetContent()
+ {
+ $response = new StreamedResponse(function () { echo 'foo'; });
+ $response->setContent('foo');
+ }
+
+ public function testGetContent()
+ {
+ $response = new StreamedResponse(function () { echo 'foo'; });
+ $this->assertFalse($response->getContent());
+ }
+
+ public function testCreate()
+ {
+ $response = StreamedResponse::create(function () {}, 204);
+
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response);
+ $this->assertEquals(204, $response->getStatusCode());
+ }
+
+ public function testReturnThis()
+ {
+ $response = new StreamedResponse(function () {});
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendContent());
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendContent());
+
+ $response = new StreamedResponse(function () {});
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendHeaders());
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendHeaders());
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng b/assets/php/vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng
new file mode 100644
index 0000000..73708ca
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng
@@ -0,0 +1,31 @@
+<?xml version='1.0'?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
+ ns="http://www.iana.org/assignments">
+
+ <include href="iana-registry.rng"/>
+
+ <start>
+ <element name="registry">
+ <ref name="registryMeta"/>
+ <element name="registry">
+ <ref name="registryMeta"/>
+ <zeroOrMore>
+ <element name="record">
+ <optional>
+ <attribute name="date"><ref name="genericDate"/></attribute>
+ </optional>
+ <optional>
+ <attribute name="updated"><ref name="genericDate"/></attribute>
+ </optional>
+ <element name="value"><ref name="genericRange"/></element>
+ <element name="description"><text/></element>
+ <ref name="references"/>
+ </element>
+ </zeroOrMore>
+ </element>
+ <ref name="people"/>
+ </element>
+ </start>
+
+</grammar>
diff --git a/assets/php/vendor/symfony/http-foundation/Tests/schema/iana-registry.rng b/assets/php/vendor/symfony/http-foundation/Tests/schema/iana-registry.rng
new file mode 100644
index 0000000..b9c3ca9
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/Tests/schema/iana-registry.rng
@@ -0,0 +1,198 @@
+<?xml version='1.0'?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
+ ns="http://www.iana.org/assignments">
+
+ <define name="registryMeta">
+ <interleave>
+ <attribute name="id"><data type="ID"/></attribute>
+ <optional><element name="title"><ref name="text_with_references"/></element></optional>
+ <optional><element name="created"><ref name="genericDate"/></element></optional>
+ <optional><element name="updated"><data type="date"/></element></optional>
+ <optional><element name="registration_rule"><ref
+ name="text_with_references"/></element></optional>
+ <optional><element name="expert"><text/></element></optional>
+ <optional><element name="description"><ref name="text_with_references"/></element></optional>
+ <zeroOrMore><element name="note"><ref name="text_with_references"/></element></zeroOrMore>
+ <ref name="references"/>
+ <optional><element name="hide"><empty/></element></optional>
+ <zeroOrMore><element name="category"><text/></element></zeroOrMore>
+ <zeroOrMore><ref name="range"/></zeroOrMore>
+ <optional><ref name="file"/></optional>
+ </interleave>
+ </define>
+
+ <define name="range">
+ <element name="range">
+ <interleave>
+ <element name="value"><text/></element>
+ <optional><element name="hex"><text/></element></optional>
+ <element name="registration_rule"><ref name="text_with_references"/></element>
+ <optional><element name="note"><ref name="text_with_references"/></element></optional>
+ <optional><ref name="xref"/></optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="people">
+ <element name="people">
+ <zeroOrMore>
+ <element name="person">
+ <attribute name="id"><data type="ID"/></attribute>
+ <optional><element name="name"><text/></element></optional>
+ <optional><element name="org"><text/></element></optional>
+ <zeroOrMore><element name="uri"><data type="anyURI"/></element></zeroOrMore>
+ <optional><element name="updated"><ref name="genericDate"/></element></optional>
+ </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="xref">
+ <element name="xref">
+ <optional>
+ <attribute name="lastupdated"><ref name="genericDate"/></attribute>
+ </optional>
+ <choice>
+ <group>
+ <attribute name="type"><value>uri</value></attribute>
+ <attribute name="data"><data type="anyURI"/></attribute>
+ </group>
+ <group>
+ <attribute name="type"><value>rfc</value></attribute>
+ <attribute name="data">
+ <data type="string">
+ <param name="pattern">(rfc|bcp|std)\d+</param>
+ </data>
+ </attribute>
+ </group>
+ <group>
+ <attribute name="type"><value>rfc-errata</value></attribute>
+ <attribute name="data"><data type="positiveInteger"/></attribute>
+ </group>
+ <group>
+ <attribute name="type"><value>draft</value></attribute>
+ <attribute name="data">
+ <data type="string">
+ <param name="pattern">(draft|RFC)(-[a-zA-Z0-9]+)+</param>
+ </data>
+ </attribute>
+ </group>
+ <group>
+ <attribute name="type"><value>registry</value></attribute>
+ <attribute name="data"><data type="NCName"/></attribute>
+ </group>
+ <group>
+ <attribute name="type"><value>person</value></attribute>
+ <attribute name="data"><data type="NCName"/></attribute>
+ </group>
+ <group>
+ <attribute name="type"><value>text</value></attribute>
+ </group>
+ <group>
+ <attribute name="type"><value>note</value></attribute>
+ <attribute name="data"><data type="positiveInteger"/></attribute>
+ </group>
+ <group>
+ <attribute name="type"><value>unicode</value></attribute>
+ <attribute name="data">
+ <data type="string">
+ <param name="pattern">ucd\d+\.\d+\.\d+</param>
+ </data>
+ </attribute>
+ </group>
+ </choice>
+ <text/>
+ </element>
+ </define>
+
+ <define name="references">
+ <zeroOrMore>
+ <ref name="xref"/>
+ </zeroOrMore>
+ </define>
+
+ <define name="text_with_references">
+ <interleave>
+ <zeroOrMore>
+ <text/>
+ <optional><ref name="xref"/></optional>
+ </zeroOrMore>
+ </interleave>
+ </define>
+
+ <define name="richText">
+ <zeroOrMore>
+ <choice>
+ <interleave>
+ <ref name="text_with_references"/>
+ <optional><element name="br"><empty/></element></optional>
+ </interleave>
+ <element name="paragraph">
+ <interleave>
+ <ref name="text_with_references"/>
+ <optional><element name="br"><empty/></element></optional>
+ </interleave>
+ </element>
+ <element name="artwork"><text/></element>
+ </choice>
+ </zeroOrMore>
+ </define>
+
+ <define name="genericRange">
+ <data type="string">
+ <param name="pattern">(\d+|0x[\da-fA-F]+)(\s*-\s*(\d+|0x[\da-fA-F]+))?</param>
+ </data>
+ </define>
+
+ <define name="genericDate">
+ <choice>
+ <data type="date"/>
+ <data type="gYearMonth"/>
+ </choice>
+ </define>
+
+ <define name="hex32">
+ <data type="string">
+ <param name="pattern">0x[0-9]{8}</param>
+ </data>
+ </define>
+
+ <define name="binary">
+ <data type="string">
+ <param name="pattern">[0-1]+</param>
+ </data>
+ </define>
+
+ <define name="footnotes">
+ <zeroOrMore>
+ <element name="footnote">
+ <attribute name="anchor"><data type="positiveInteger"/></attribute>
+ <interleave>
+ <zeroOrMore>
+ <text/>
+ <optional><ref name="xref"/></optional>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </zeroOrMore>
+ </define>
+
+ <define name="file">
+ <element name="file">
+ <attribute name="type">
+ <choice>
+ <value>legacy</value>
+ <value>mib</value>
+ <value>template</value>
+ <value>json</value>
+ </choice>
+ </attribute>
+ <optional>
+ <attribute name="name"/>
+ </optional>
+ <data type="anyURI"/>
+ </element>
+ </define>
+
+</grammar>
diff --git a/assets/php/vendor/symfony/http-foundation/composer.json b/assets/php/vendor/symfony/http-foundation/composer.json
new file mode 100644
index 0000000..f6c6f2e
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/composer.json
@@ -0,0 +1,38 @@
+{
+ "name": "symfony/http-foundation",
+ "type": "library",
+ "description": "Symfony HttpFoundation Component",
+ "keywords": [],
+ "homepage": "https://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": "^5.5.9|>=7.0.8",
+ "symfony/polyfill-mbstring": "~1.1",
+ "symfony/polyfill-php70": "~1.6"
+ },
+ "require-dev": {
+ "symfony/expression-language": "~2.8|~3.0|~4.0"
+ },
+ "autoload": {
+ "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.4-dev"
+ }
+ }
+}
diff --git a/assets/php/vendor/symfony/http-foundation/phpunit.xml.dist b/assets/php/vendor/symfony/http-foundation/phpunit.xml.dist
new file mode 100644
index 0000000..c1d61f8
--- /dev/null
+++ b/assets/php/vendor/symfony/http-foundation/phpunit.xml.dist
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
+ backupGlobals="false"
+ colors="true"
+ bootstrap="vendor/autoload.php"
+ failOnRisky="true"
+ failOnWarning="true"
+>
+ <php>
+ <ini name="error_reporting" value="-1" />
+ </php>
+
+ <testsuites>
+ <testsuite name="Symfony HttpFoundation Component Test Suite">
+ <directory>./Tests/</directory>
+ </testsuite>
+ </testsuites>
+
+ <filter>
+ <whitelist>
+ <directory>./</directory>
+ <exclude>
+ <directory>./Resources</directory>
+ <directory>./Tests</directory>
+ <directory>./vendor</directory>
+ </exclude>
+ </whitelist>
+ </filter>
+</phpunit>