aboutsummaryrefslogtreecommitdiffhomepage
path: root/assets/php/vendor/symfony/http-foundation/Session
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/Session
parent286d643180672f20526f3dc3bd19d7b751e2fa97 (diff)
Initial Commit
Diffstat (limited to 'assets/php/vendor/symfony/http-foundation/Session')
-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
29 files changed, 4751 insertions, 0 deletions
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();
+}