diff --git a/addons/epay/.addonrc b/addons/epay/.addonrc
index 9596243..64dc115 100644
--- a/addons/epay/.addonrc
+++ b/addons/epay/.addonrc
@@ -1 +1 @@
-{"license":"regular","licenseto":"10789","licensekey":"NiwxHIDZr9nmkf65 F8ze5TjmdyiCzy+xGqBJ+A==","files":["public\\assets\\addons\\epay\\css\\common.css","public\\assets\\addons\\epay\\css\\epay.css","public\\assets\\addons\\epay\\css\\wechat.css","public\\assets\\addons\\epay\\images\\alipay.png","public\\assets\\addons\\epay\\images\\expired.png","public\\assets\\addons\\epay\\images\\logo-alipay.png","public\\assets\\addons\\epay\\images\\logo-wechat.png","public\\assets\\addons\\epay\\images\\paid.png","public\\assets\\addons\\epay\\images\\scan.png","public\\assets\\addons\\epay\\images\\tips.png","public\\assets\\addons\\epay\\images\\wechat.png","public\\assets\\addons\\epay\\js\\common.js","public\\assets\\addons\\epay\\less\\common.less","public\\assets\\addons\\epay\\less\\epay.less"]}
\ No newline at end of file
+{"license":"regular","licenseto":"10789","licensekey":"qQPtvf9ZSgGilOuo pqZlObQ0gosFXq480Ek7bg==","files":["public\\assets\\addons\\epay\\css\\common.css","public\\assets\\addons\\epay\\css\\epay.css","public\\assets\\addons\\epay\\css\\wechat.css","public\\assets\\addons\\epay\\images\\alipay.png","public\\assets\\addons\\epay\\images\\expired.png","public\\assets\\addons\\epay\\images\\logo-alipay.png","public\\assets\\addons\\epay\\images\\logo-wechat.png","public\\assets\\addons\\epay\\images\\paid.png","public\\assets\\addons\\epay\\images\\scan.png","public\\assets\\addons\\epay\\images\\tips.png","public\\assets\\addons\\epay\\images\\wechat.png","public\\assets\\addons\\epay\\js\\common.js","public\\assets\\addons\\epay\\less\\common.less","public\\assets\\addons\\epay\\less\\epay.less"]}
\ No newline at end of file
diff --git a/composer.json b/composer.json
index 8b3e476..594279e 100644
--- a/composer.json
+++ b/composer.json
@@ -27,7 +27,8 @@
         "ext-json": "*",
         "ext-curl": "*",
         "ext-pdo": "*",
-        "overtrue/wechat": "~3.1"
+        "overtrue/wechat": "~3.1",
+        "symfony/event-dispatcher": "4.3"
     },
     "config": {
         "preferred-install": "dist"
diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php
index 18e4323..8b8a0e0 100644
--- a/vendor/composer/autoload_files.php
+++ b/vendor/composer/autoload_files.php
@@ -7,8 +7,8 @@ $baseDir = dirname($vendorDir);
 
 return array(
     '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
-    'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
     '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
+    'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
     'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
     '25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
     'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php
index f7525ee..d55a985 100644
--- a/vendor/composer/autoload_psr4.php
+++ b/vendor/composer/autoload_psr4.php
@@ -13,9 +13,11 @@ return array(
     'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
     'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
     'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'),
+    'Symfony\\Contracts\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher-contracts'),
     'Symfony\\Component\\VarExporter\\' => array($vendorDir . '/symfony/var-exporter'),
     'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'),
     'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'),
+    'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
     'Symfony\\Bridge\\PsrHttpMessage\\' => array($vendorDir . '/symfony/psr-http-message-bridge'),
     'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
     'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
index acd2cf2..cef777c 100644
--- a/vendor/composer/autoload_static.php
+++ b/vendor/composer/autoload_static.php
@@ -8,8 +8,8 @@ class ComposerStaticInitf00349b72964a05454b796bb539364c1
 {
     public static $files = array (
         '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
-        'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
         '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
+        'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
         'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
         '25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
         'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
@@ -91,9 +91,11 @@ class ComposerStaticInitf00349b72964a05454b796bb539364c1
             'Symfony\\Polyfill\\Mbstring\\' => 26,
             'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33,
             'Symfony\\Polyfill\\Intl\\Idn\\' => 26,
+            'Symfony\\Contracts\\EventDispatcher\\' => 34,
             'Symfony\\Component\\VarExporter\\' => 30,
             'Symfony\\Component\\HttpFoundation\\' => 33,
             'Symfony\\Component\\Finder\\' => 25,
+            'Symfony\\Component\\EventDispatcher\\' => 34,
             'Symfony\\Bridge\\PsrHttpMessage\\' => 30,
         ),
         'P' => 
@@ -162,6 +164,10 @@ class ComposerStaticInitf00349b72964a05454b796bb539364c1
         array (
             0 => __DIR__ . '/..' . '/symfony/polyfill-intl-idn',
         ),
+        'Symfony\\Contracts\\EventDispatcher\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/event-dispatcher-contracts',
+        ),
         'Symfony\\Component\\VarExporter\\' => 
         array (
             0 => __DIR__ . '/..' . '/symfony/var-exporter',
@@ -174,6 +180,10 @@ class ComposerStaticInitf00349b72964a05454b796bb539364c1
         array (
             0 => __DIR__ . '/..' . '/symfony/finder',
         ),
+        'Symfony\\Component\\EventDispatcher\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/event-dispatcher',
+        ),
         'Symfony\\Bridge\\PsrHttpMessage\\' => 
         array (
             0 => __DIR__ . '/..' . '/symfony/psr-http-message-bridge',
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index f2173a3..549b063 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -1432,6 +1432,168 @@
         "description": "A polyfill for getallheaders."
     },
     {
+        "name": "symfony/event-dispatcher",
+        "version": "v4.3.0",
+        "version_normalized": "4.3.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/event-dispatcher.git",
+            "reference": "c71314cd3b9420b732e1526f33a24eff5430b5b3"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/c71314cd3b9420b732e1526f33a24eff5430b5b3",
+            "reference": "c71314cd3b9420b732e1526f33a24eff5430b5b3",
+            "shasum": "",
+            "mirrors": [
+                {
+                    "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                    "preferred": true
+                }
+            ]
+        },
+        "require": {
+            "php": "^7.1.3",
+            "symfony/event-dispatcher-contracts": "^1.1"
+        },
+        "conflict": {
+            "symfony/dependency-injection": "<3.4"
+        },
+        "provide": {
+            "psr/event-dispatcher-implementation": "1.0",
+            "symfony/event-dispatcher-implementation": "1.1"
+        },
+        "require-dev": {
+            "psr/log": "~1.0",
+            "symfony/config": "~3.4|~4.0",
+            "symfony/dependency-injection": "~3.4|~4.0",
+            "symfony/expression-language": "~3.4|~4.0",
+            "symfony/http-foundation": "^3.4|^4.0",
+            "symfony/service-contracts": "^1.1",
+            "symfony/stopwatch": "~3.4|~4.0"
+        },
+        "suggest": {
+            "symfony/dependency-injection": "",
+            "symfony/http-kernel": ""
+        },
+        "time": "2019-05-28T07:50:59+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "4.3-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Symfony\\Component\\EventDispatcher\\": ""
+            },
+            "exclude-from-classmap": [
+                "/Tests/"
+            ]
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
+            }
+        ],
+        "description": "Symfony EventDispatcher Component",
+        "homepage": "https://symfony.com"
+    },
+    {
+        "name": "symfony/event-dispatcher-contracts",
+        "version": "v1.1.9",
+        "version_normalized": "1.1.9.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/event-dispatcher-contracts.git",
+            "reference": "84e23fdcd2517bf37aecbd16967e83f0caee25a7"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/84e23fdcd2517bf37aecbd16967e83f0caee25a7",
+            "reference": "84e23fdcd2517bf37aecbd16967e83f0caee25a7",
+            "shasum": "",
+            "mirrors": [
+                {
+                    "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                    "preferred": true
+                }
+            ]
+        },
+        "require": {
+            "php": ">=7.1.3"
+        },
+        "suggest": {
+            "psr/event-dispatcher": "",
+            "symfony/event-dispatcher-implementation": ""
+        },
+        "time": "2020-07-06T13:19:58+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.1-dev"
+            },
+            "thanks": {
+                "name": "symfony/contracts",
+                "url": "https://github.com/symfony/contracts"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Symfony\\Contracts\\EventDispatcher\\": ""
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Nicolas Grekas",
+                "email": "p@tchwork.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
+            }
+        ],
+        "description": "Generic abstractions related to dispatching event",
+        "homepage": "https://symfony.com",
+        "keywords": [
+            "abstractions",
+            "contracts",
+            "decoupling",
+            "interfaces",
+            "interoperability",
+            "standards"
+        ],
+        "funding": [
+            {
+                "url": "https://symfony.com/sponsor",
+                "type": "custom"
+            },
+            {
+                "url": "https://github.com/fabpot",
+                "type": "github"
+            },
+            {
+                "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                "type": "tidelift"
+            }
+        ]
+    },
+    {
         "name": "symfony/finder",
         "version": "v4.4.14",
         "version_normalized": "4.4.14.0",
diff --git a/vendor/symfony/event-dispatcher-contracts/.gitignore b/vendor/symfony/event-dispatcher-contracts/.gitignore
new file mode 100644
index 0000000..c49a5d8
--- /dev/null
+++ b/vendor/symfony/event-dispatcher-contracts/.gitignore
@@ -0,0 +1,3 @@
+vendor/
+composer.lock
+phpunit.xml
diff --git a/vendor/symfony/event-dispatcher-contracts/Event.php b/vendor/symfony/event-dispatcher-contracts/Event.php
new file mode 100644
index 0000000..84f60f3
--- /dev/null
+++ b/vendor/symfony/event-dispatcher-contracts/Event.php
@@ -0,0 +1,96 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Contracts\EventDispatcher;
+
+use Psr\EventDispatcher\StoppableEventInterface;
+
+if (interface_exists(StoppableEventInterface::class)) {
+    /**
+     * Event is the base class for classes containing event data.
+     *
+     * This class contains no event data. It is used by events that do not pass
+     * state information to an event handler when an event is raised.
+     *
+     * You can call the method stopPropagation() to abort the execution of
+     * further listeners in your event listener.
+     *
+     * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
+     * @author Jonathan Wage <jonwage@gmail.com>
+     * @author Roman Borschel <roman@code-factory.org>
+     * @author Bernhard Schussek <bschussek@gmail.com>
+     * @author Nicolas Grekas <p@tchwork.com>
+     */
+    class Event implements StoppableEventInterface
+    {
+        private $propagationStopped = false;
+
+        /**
+         * Returns whether further event listeners should be triggered.
+         */
+        public function isPropagationStopped(): bool
+        {
+            return $this->propagationStopped;
+        }
+
+        /**
+         * Stops the propagation of the event to further event listeners.
+         *
+         * If multiple event listeners are connected to the same event, no
+         * further event listener will be triggered once any trigger calls
+         * stopPropagation().
+         */
+        public function stopPropagation(): void
+        {
+            $this->propagationStopped = true;
+        }
+    }
+} else {
+    /**
+     * Event is the base class for classes containing event data.
+     *
+     * This class contains no event data. It is used by events that do not pass
+     * state information to an event handler when an event is raised.
+     *
+     * You can call the method stopPropagation() to abort the execution of
+     * further listeners in your event listener.
+     *
+     * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
+     * @author Jonathan Wage <jonwage@gmail.com>
+     * @author Roman Borschel <roman@code-factory.org>
+     * @author Bernhard Schussek <bschussek@gmail.com>
+     * @author Nicolas Grekas <p@tchwork.com>
+     */
+    class Event
+    {
+        private $propagationStopped = false;
+
+        /**
+         * Returns whether further event listeners should be triggered.
+         */
+        public function isPropagationStopped(): bool
+        {
+            return $this->propagationStopped;
+        }
+
+        /**
+         * Stops the propagation of the event to further event listeners.
+         *
+         * If multiple event listeners are connected to the same event, no
+         * further event listener will be triggered once any trigger calls
+         * stopPropagation().
+         */
+        public function stopPropagation(): void
+        {
+            $this->propagationStopped = true;
+        }
+    }
+}
diff --git a/vendor/symfony/event-dispatcher-contracts/EventDispatcherInterface.php b/vendor/symfony/event-dispatcher-contracts/EventDispatcherInterface.php
new file mode 100644
index 0000000..2d470af
--- /dev/null
+++ b/vendor/symfony/event-dispatcher-contracts/EventDispatcherInterface.php
@@ -0,0 +1,58 @@
+<?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\Contracts\EventDispatcher;
+
+use Psr\EventDispatcher\EventDispatcherInterface as PsrEventDispatcherInterface;
+
+if (interface_exists(PsrEventDispatcherInterface::class)) {
+    /**
+     * Allows providing hooks on domain-specific lifecycles by dispatching events.
+     */
+    interface EventDispatcherInterface extends PsrEventDispatcherInterface
+    {
+        /**
+         * Dispatches an event to all registered listeners.
+         *
+         * For BC with Symfony 4, the $eventName argument is not declared explicitly on the
+         * signature of the method. Implementations that are not bound by this BC constraint
+         * MUST declare it explicitly, as allowed by PHP.
+         *
+         * @param object      $event     The event to pass to the event handlers/listeners
+         * @param string|null $eventName The name of the event to dispatch. If not supplied,
+         *                               the class of $event should be used instead.
+         *
+         * @return object The passed $event MUST be returned
+         */
+        public function dispatch($event/*, string $eventName = null*/);
+    }
+} else {
+    /**
+     * Allows providing hooks on domain-specific lifecycles by dispatching events.
+     */
+    interface EventDispatcherInterface
+    {
+        /**
+         * Dispatches an event to all registered listeners.
+         *
+         * For BC with Symfony 4, the $eventName argument is not declared explicitly on the
+         * signature of the method. Implementations that are not bound by this BC constraint
+         * MUST declare it explicitly, as allowed by PHP.
+         *
+         * @param object      $event     The event to pass to the event handlers/listeners
+         * @param string|null $eventName The name of the event to dispatch. If not supplied,
+         *                               the class of $event should be used instead.
+         *
+         * @return object The passed $event MUST be returned
+         */
+        public function dispatch($event/*, string $eventName = null*/);
+    }
+}
diff --git a/vendor/symfony/event-dispatcher-contracts/LICENSE b/vendor/symfony/event-dispatcher-contracts/LICENSE
new file mode 100644
index 0000000..69d925b
--- /dev/null
+++ b/vendor/symfony/event-dispatcher-contracts/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2018-2020 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/symfony/event-dispatcher-contracts/README.md b/vendor/symfony/event-dispatcher-contracts/README.md
new file mode 100644
index 0000000..fb051c7
--- /dev/null
+++ b/vendor/symfony/event-dispatcher-contracts/README.md
@@ -0,0 +1,9 @@
+Symfony EventDispatcher Contracts
+=================================
+
+A set of abstractions extracted out of the Symfony components.
+
+Can be used to build on semantics that the Symfony components proved useful - and
+that already have battle tested implementations.
+
+See https://github.com/symfony/contracts/blob/master/README.md for more information.
diff --git a/vendor/symfony/event-dispatcher-contracts/composer.json b/vendor/symfony/event-dispatcher-contracts/composer.json
new file mode 100644
index 0000000..862c256
--- /dev/null
+++ b/vendor/symfony/event-dispatcher-contracts/composer.json
@@ -0,0 +1,38 @@
+{
+    "name": "symfony/event-dispatcher-contracts",
+    "type": "library",
+    "description": "Generic abstractions related to dispatching event",
+    "keywords": ["abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards"],
+    "homepage": "https://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Nicolas Grekas",
+            "email": "p@tchwork.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "https://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": ">=7.1.3"
+    },
+    "suggest": {
+        "psr/event-dispatcher": "",
+        "symfony/event-dispatcher-implementation": ""
+    },
+    "autoload": {
+        "psr-4": { "Symfony\\Contracts\\EventDispatcher\\": "" }
+    },
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.1-dev"
+        },
+        "thanks": {
+            "name": "symfony/contracts",
+            "url": "https://github.com/symfony/contracts"
+        }
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/.gitignore b/vendor/symfony/event-dispatcher/.gitignore
new file mode 100644
index 0000000..c49a5d8
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/.gitignore
@@ -0,0 +1,3 @@
+vendor/
+composer.lock
+phpunit.xml
diff --git a/vendor/symfony/event-dispatcher/CHANGELOG.md b/vendor/symfony/event-dispatcher/CHANGELOG.md
new file mode 100644
index 0000000..7653cad
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/CHANGELOG.md
@@ -0,0 +1,61 @@
+CHANGELOG
+=========
+
+4.3.0
+-----
+
+ * The signature of the `EventDispatcherInterface::dispatch()` method should be updated to `dispatch($event, string $eventName = null)`, not doing so is deprecated
+ * deprecated the `Event` class, use `Symfony\Contracts\EventDispatcher\Event` instead
+
+4.1.0
+-----
+
+ * added support for invokable event listeners tagged with `kernel.event_listener` by default 
+ * The `TraceableEventDispatcher::getOrphanedEvents()` method has been added.
+ * The `TraceableEventDispatcherInterface` has been deprecated.
+
+4.0.0
+-----
+
+ * removed the `ContainerAwareEventDispatcher` class
+ * added the `reset()` method to the `TraceableEventDispatcherInterface`
+
+3.4.0
+-----
+
+  * Implementing `TraceableEventDispatcherInterface` without the `reset()` method has been deprecated.
+
+3.3.0
+-----
+
+  * The ContainerAwareEventDispatcher class has been deprecated. Use EventDispatcher with closure factories instead.
+
+3.0.0
+-----
+
+  * The method `getListenerPriority($eventName, $listener)` has been added to the
+    `EventDispatcherInterface`.
+  * The methods `Event::setDispatcher()`, `Event::getDispatcher()`, `Event::setName()`
+    and `Event::getName()` have been removed.
+    The event dispatcher and the event name are passed to the listener call.
+
+2.5.0
+-----
+
+ * added Debug\TraceableEventDispatcher (originally in HttpKernel)
+ * changed Debug\TraceableEventDispatcherInterface to extend EventDispatcherInterface
+ * added RegisterListenersPass (originally in HttpKernel)
+
+2.1.0
+-----
+
+ * added TraceableEventDispatcherInterface
+ * added ContainerAwareEventDispatcher
+ * added a reference to the EventDispatcher on the Event
+ * added a reference to the Event name on the event
+ * added fluid interface to the dispatch() method which now returns the Event
+   object
+ * added GenericEvent event class
+ * added the possibility for subscribers to subscribe several times for the
+   same event
+ * added ImmutableEventDispatcher
diff --git a/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php
new file mode 100644
index 0000000..7716d86
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php
@@ -0,0 +1,407 @@
+<?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\EventDispatcher\Debug;
+
+use Psr\EventDispatcher\StoppableEventInterface;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\BrowserKit\Request;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy;
+use Symfony\Component\EventDispatcher\LegacyEventProxy;
+use Symfony\Component\HttpFoundation\RequestStack;
+use Symfony\Component\Stopwatch\Stopwatch;
+use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
+
+/**
+ * Collects some data about event listeners.
+ *
+ * This event dispatcher delegates the dispatching to another one.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class TraceableEventDispatcher implements TraceableEventDispatcherInterface
+{
+    protected $logger;
+    protected $stopwatch;
+
+    private $callStack;
+    private $dispatcher;
+    private $wrappedListeners;
+    private $orphanedEvents;
+    private $requestStack;
+    private $currentRequestHash = '';
+
+    public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null, RequestStack $requestStack = null)
+    {
+        $this->dispatcher = LegacyEventDispatcherProxy::decorate($dispatcher);
+        $this->stopwatch = $stopwatch;
+        $this->logger = $logger;
+        $this->wrappedListeners = [];
+        $this->orphanedEvents = [];
+        $this->requestStack = $requestStack;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addListener($eventName, $listener, $priority = 0)
+    {
+        $this->dispatcher->addListener($eventName, $listener, $priority);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addSubscriber(EventSubscriberInterface $subscriber)
+    {
+        $this->dispatcher->addSubscriber($subscriber);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function removeListener($eventName, $listener)
+    {
+        if (isset($this->wrappedListeners[$eventName])) {
+            foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) {
+                if ($wrappedListener->getWrappedListener() === $listener) {
+                    $listener = $wrappedListener;
+                    unset($this->wrappedListeners[$eventName][$index]);
+                    break;
+                }
+            }
+        }
+
+        return $this->dispatcher->removeListener($eventName, $listener);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function removeSubscriber(EventSubscriberInterface $subscriber)
+    {
+        return $this->dispatcher->removeSubscriber($subscriber);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getListeners($eventName = null)
+    {
+        return $this->dispatcher->getListeners($eventName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getListenerPriority($eventName, $listener)
+    {
+        // we might have wrapped listeners for the event (if called while dispatching)
+        // in that case get the priority by wrapper
+        if (isset($this->wrappedListeners[$eventName])) {
+            foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) {
+                if ($wrappedListener->getWrappedListener() === $listener) {
+                    return $this->dispatcher->getListenerPriority($eventName, $wrappedListener);
+                }
+            }
+        }
+
+        return $this->dispatcher->getListenerPriority($eventName, $listener);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasListeners($eventName = null)
+    {
+        return $this->dispatcher->hasListeners($eventName);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @param string|null $eventName
+     */
+    public function dispatch($event/*, string $eventName = null*/)
+    {
+        if (null === $this->callStack) {
+            $this->callStack = new \SplObjectStorage();
+        }
+
+        $currentRequestHash = $this->currentRequestHash = $this->requestStack && ($request = $this->requestStack->getCurrentRequest()) ? spl_object_hash($request) : '';
+        $eventName = 1 < \func_num_args() ? \func_get_arg(1) : null;
+
+        if (\is_object($event)) {
+            $eventName = $eventName ?? \get_class($event);
+        } else {
+            @trigger_error(sprintf('Calling the "%s::dispatch()" method with the event name as first argument is deprecated since Symfony 4.3, pass it second and provide the event object first instead.', EventDispatcherInterface::class), E_USER_DEPRECATED);
+            $swap = $event;
+            $event = $eventName ?? new Event();
+            $eventName = $swap;
+
+            if (!$event instanceof Event) {
+                throw new \TypeError(sprintf('Argument 1 passed to "%s::dispatch()" must be an instance of %s, %s given.', EventDispatcherInterface::class, Event::class, \is_object($event) ? \get_class($event) : \gettype($event)));
+            }
+        }
+
+        if (null !== $this->logger && ($event instanceof Event || $event instanceof ContractsEvent || $event instanceof StoppableEventInterface) && $event->isPropagationStopped()) {
+            $this->logger->debug(sprintf('The "%s" event is already stopped. No listeners have been called.', $eventName));
+        }
+
+        $this->preProcess($eventName);
+        try {
+            $this->beforeDispatch($eventName, $event);
+            try {
+                $e = $this->stopwatch->start($eventName, 'section');
+                try {
+                    $this->dispatcher->dispatch($event, $eventName);
+                } finally {
+                    if ($e->isStarted()) {
+                        $e->stop();
+                    }
+                }
+            } finally {
+                $this->afterDispatch($eventName, $event);
+            }
+        } finally {
+            $this->currentRequestHash = $currentRequestHash;
+            $this->postProcess($eventName);
+        }
+
+        return $event;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @param Request|null $request The request to get listeners for
+     */
+    public function getCalledListeners(/* Request $request = null */)
+    {
+        if (null === $this->callStack) {
+            return [];
+        }
+
+        $hash = 1 <= \func_num_args() && null !== ($request = \func_get_arg(0)) ? spl_object_hash($request) : null;
+        $called = [];
+        foreach ($this->callStack as $listener) {
+            list($eventName, $requestHash) = $this->callStack->getInfo();
+            if (null === $hash || $hash === $requestHash) {
+                $called[] = $listener->getInfo($eventName);
+            }
+        }
+
+        return $called;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @param Request|null $request The request to get listeners for
+     */
+    public function getNotCalledListeners(/* Request $request = null */)
+    {
+        try {
+            $allListeners = $this->getListeners();
+        } catch (\Exception $e) {
+            if (null !== $this->logger) {
+                $this->logger->info('An exception was thrown while getting the uncalled listeners.', ['exception' => $e]);
+            }
+
+            // unable to retrieve the uncalled listeners
+            return [];
+        }
+
+        $hash = 1 <= \func_num_args() && null !== ($request = \func_get_arg(0)) ? spl_object_hash($request) : null;
+        $notCalled = [];
+        foreach ($allListeners as $eventName => $listeners) {
+            foreach ($listeners as $listener) {
+                $called = false;
+                if (null !== $this->callStack) {
+                    foreach ($this->callStack as $calledListener) {
+                        list(, $requestHash) = $this->callStack->getInfo();
+                        if ((null === $hash || $hash === $requestHash) && $calledListener->getWrappedListener() === $listener) {
+                            $called = true;
+
+                            break;
+                        }
+                    }
+                }
+
+                if (!$called) {
+                    if (!$listener instanceof WrappedListener) {
+                        $listener = new WrappedListener($listener, null, $this->stopwatch, $this);
+                    }
+                    $notCalled[] = $listener->getInfo($eventName);
+                }
+            }
+        }
+
+        uasort($notCalled, [$this, 'sortNotCalledListeners']);
+
+        return $notCalled;
+    }
+
+    /**
+     * @param Request|null $request The request to get orphaned events for
+     */
+    public function getOrphanedEvents(/* Request $request = null */): array
+    {
+        if (1 <= \func_num_args() && null !== $request = \func_get_arg(0)) {
+            return $this->orphanedEvents[spl_object_hash($request)] ?? [];
+        }
+
+        if (!$this->orphanedEvents) {
+            return [];
+        }
+
+        return array_merge(...array_values($this->orphanedEvents));
+    }
+
+    public function reset()
+    {
+        $this->callStack = null;
+        $this->orphanedEvents = [];
+        $this->currentRequestHash = '';
+    }
+
+    /**
+     * Proxies all method calls to the original event dispatcher.
+     *
+     * @param string $method    The method name
+     * @param array  $arguments The method arguments
+     *
+     * @return mixed
+     */
+    public function __call($method, $arguments)
+    {
+        return $this->dispatcher->{$method}(...$arguments);
+    }
+
+    /**
+     * Called before dispatching the event.
+     *
+     * @param object $event
+     */
+    protected function beforeDispatch(string $eventName, $event)
+    {
+        $this->preDispatch($eventName, $event instanceof Event ? $event : new LegacyEventProxy($event));
+    }
+
+    /**
+     * Called after dispatching the event.
+     *
+     * @param object $event
+     */
+    protected function afterDispatch(string $eventName, $event)
+    {
+        $this->postDispatch($eventName, $event instanceof Event ? $event : new LegacyEventProxy($event));
+    }
+
+    /**
+     * @deprecated since Symfony 4.3, will be removed in 5.0, use beforeDispatch instead
+     */
+    protected function preDispatch($eventName, Event $event)
+    {
+    }
+
+    /**
+     * @deprecated since Symfony 4.3, will be removed in 5.0, use afterDispatch instead
+     */
+    protected function postDispatch($eventName, Event $event)
+    {
+    }
+
+    private function preProcess($eventName)
+    {
+        if (!$this->dispatcher->hasListeners($eventName)) {
+            $this->orphanedEvents[$this->currentRequestHash][] = $eventName;
+
+            return;
+        }
+
+        foreach ($this->dispatcher->getListeners($eventName) as $listener) {
+            $priority = $this->getListenerPriority($eventName, $listener);
+            $wrappedListener = new WrappedListener($listener instanceof WrappedListener ? $listener->getWrappedListener() : $listener, null, $this->stopwatch, $this);
+            $this->wrappedListeners[$eventName][] = $wrappedListener;
+            $this->dispatcher->removeListener($eventName, $listener);
+            $this->dispatcher->addListener($eventName, $wrappedListener, $priority);
+            $this->callStack->attach($wrappedListener, [$eventName, $this->currentRequestHash]);
+        }
+    }
+
+    private function postProcess($eventName)
+    {
+        unset($this->wrappedListeners[$eventName]);
+        $skipped = false;
+        foreach ($this->dispatcher->getListeners($eventName) as $listener) {
+            if (!$listener instanceof WrappedListener) { // #12845: a new listener was added during dispatch.
+                continue;
+            }
+            // Unwrap listener
+            $priority = $this->getListenerPriority($eventName, $listener);
+            $this->dispatcher->removeListener($eventName, $listener);
+            $this->dispatcher->addListener($eventName, $listener->getWrappedListener(), $priority);
+
+            if (null !== $this->logger) {
+                $context = ['event' => $eventName, 'listener' => $listener->getPretty()];
+            }
+
+            if ($listener->wasCalled()) {
+                if (null !== $this->logger) {
+                    $this->logger->debug('Notified event "{event}" to listener "{listener}".', $context);
+                }
+            } else {
+                $this->callStack->detach($listener);
+            }
+
+            if (null !== $this->logger && $skipped) {
+                $this->logger->debug('Listener "{listener}" was not called for event "{event}".', $context);
+            }
+
+            if ($listener->stoppedPropagation()) {
+                if (null !== $this->logger) {
+                    $this->logger->debug('Listener "{listener}" stopped propagation of the event "{event}".', $context);
+                }
+
+                $skipped = true;
+            }
+        }
+    }
+
+    private function sortNotCalledListeners(array $a, array $b)
+    {
+        if (0 !== $cmp = strcmp($a['event'], $b['event'])) {
+            return $cmp;
+        }
+
+        if (\is_int($a['priority']) && !\is_int($b['priority'])) {
+            return 1;
+        }
+
+        if (!\is_int($a['priority']) && \is_int($b['priority'])) {
+            return -1;
+        }
+
+        if ($a['priority'] === $b['priority']) {
+            return 0;
+        }
+
+        if ($a['priority'] > $b['priority']) {
+            return -1;
+        }
+
+        return 1;
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php
new file mode 100644
index 0000000..4fedb9a
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php
@@ -0,0 +1,42 @@
+<?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\EventDispatcher\Debug;
+
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Contracts\Service\ResetInterface;
+
+/**
+ * @deprecated since Symfony 4.1
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface TraceableEventDispatcherInterface extends EventDispatcherInterface, ResetInterface
+{
+    /**
+     * Gets the called listeners.
+     *
+     * @param Request|null $request The request to get listeners for
+     *
+     * @return array An array of called listeners
+     */
+    public function getCalledListeners(/* Request $request = null */);
+
+    /**
+     * Gets the not called listeners.
+     *
+     * @param Request|null $request The request to get listeners for
+     *
+     * @return array An array of not called listeners
+     */
+    public function getNotCalledListeners(/* Request $request = null */);
+}
diff --git a/vendor/symfony/event-dispatcher/Debug/WrappedListener.php b/vendor/symfony/event-dispatcher/Debug/WrappedListener.php
new file mode 100644
index 0000000..e047639
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Debug/WrappedListener.php
@@ -0,0 +1,136 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher\Debug;
+
+use Psr\EventDispatcher\StoppableEventInterface;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\EventDispatcher\LegacyEventProxy;
+use Symfony\Component\Stopwatch\Stopwatch;
+use Symfony\Component\VarDumper\Caster\ClassStub;
+use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @final since Symfony 4.3: the "Event" type-hint on __invoke() will be replaced by "object" in 5.0
+ */
+class WrappedListener
+{
+    private $listener;
+    private $optimizedListener;
+    private $name;
+    private $called;
+    private $stoppedPropagation;
+    private $stopwatch;
+    private $dispatcher;
+    private $pretty;
+    private $stub;
+    private $priority;
+    private static $hasClassStub;
+
+    public function __construct($listener, ?string $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null)
+    {
+        $this->listener = $listener;
+        $this->optimizedListener = $listener instanceof \Closure ? $listener : (\is_callable($listener) ? \Closure::fromCallable($listener) : null);
+        $this->stopwatch = $stopwatch;
+        $this->dispatcher = $dispatcher;
+        $this->called = false;
+        $this->stoppedPropagation = false;
+
+        if (\is_array($listener)) {
+            $this->name = \is_object($listener[0]) ? \get_class($listener[0]) : $listener[0];
+            $this->pretty = $this->name.'::'.$listener[1];
+        } elseif ($listener instanceof \Closure) {
+            $r = new \ReflectionFunction($listener);
+            if (false !== strpos($r->name, '{closure}')) {
+                $this->pretty = $this->name = 'closure';
+            } elseif ($class = $r->getClosureScopeClass()) {
+                $this->name = $class->name;
+                $this->pretty = $this->name.'::'.$r->name;
+            } else {
+                $this->pretty = $this->name = $r->name;
+            }
+        } elseif (\is_string($listener)) {
+            $this->pretty = $this->name = $listener;
+        } else {
+            $this->name = \get_class($listener);
+            $this->pretty = $this->name.'::__invoke';
+        }
+
+        if (null !== $name) {
+            $this->name = $name;
+        }
+
+        if (null === self::$hasClassStub) {
+            self::$hasClassStub = class_exists(ClassStub::class);
+        }
+    }
+
+    public function getWrappedListener()
+    {
+        return $this->listener;
+    }
+
+    public function wasCalled()
+    {
+        return $this->called;
+    }
+
+    public function stoppedPropagation()
+    {
+        return $this->stoppedPropagation;
+    }
+
+    public function getPretty()
+    {
+        return $this->pretty;
+    }
+
+    public function getInfo($eventName)
+    {
+        if (null === $this->stub) {
+            $this->stub = self::$hasClassStub ? new ClassStub($this->pretty.'()', $this->listener) : $this->pretty.'()';
+        }
+
+        return [
+            'event' => $eventName,
+            'priority' => null !== $this->priority ? $this->priority : (null !== $this->dispatcher ? $this->dispatcher->getListenerPriority($eventName, $this->listener) : null),
+            'pretty' => $this->pretty,
+            'stub' => $this->stub,
+        ];
+    }
+
+    public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher)
+    {
+        if ($event instanceof LegacyEventProxy) {
+            $event = $event->getEvent();
+        }
+
+        $dispatcher = $this->dispatcher ?: $dispatcher;
+
+        $this->called = true;
+        $this->priority = $dispatcher->getListenerPriority($eventName, $this->listener);
+
+        $e = $this->stopwatch->start($this->name, 'event_listener');
+
+        ($this->optimizedListener ?? $this->listener)($event, $eventName, $dispatcher);
+
+        if ($e->isStarted()) {
+            $e->stop();
+        }
+
+        if (($event instanceof Event || $event instanceof ContractsEvent || $event instanceof StoppableEventInterface) && $event->isPropagationStopped()) {
+            $this->stoppedPropagation = true;
+        }
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php b/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php
new file mode 100644
index 0000000..3e6493e
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.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\EventDispatcher\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Compiler pass to register tagged services for an event dispatcher.
+ */
+class RegisterListenersPass implements CompilerPassInterface
+{
+    protected $dispatcherService;
+    protected $listenerTag;
+    protected $subscriberTag;
+    protected $eventAliasesParameter;
+
+    private $hotPathEvents = [];
+    private $hotPathTagName;
+
+    public function __construct(string $dispatcherService = 'event_dispatcher', string $listenerTag = 'kernel.event_listener', string $subscriberTag = 'kernel.event_subscriber', string $eventAliasesParameter = 'event_dispatcher.event_aliases')
+    {
+        $this->dispatcherService = $dispatcherService;
+        $this->listenerTag = $listenerTag;
+        $this->subscriberTag = $subscriberTag;
+        $this->eventAliasesParameter = $eventAliasesParameter;
+    }
+
+    public function setHotPathEvents(array $hotPathEvents, $tagName = 'container.hot_path')
+    {
+        $this->hotPathEvents = array_flip($hotPathEvents);
+        $this->hotPathTagName = $tagName;
+
+        return $this;
+    }
+
+    public function process(ContainerBuilder $container)
+    {
+        if (!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService)) {
+            return;
+        }
+
+        if ($container->hasParameter($this->eventAliasesParameter)) {
+            $aliases = $container->getParameter($this->eventAliasesParameter);
+            $container->getParameterBag()->remove($this->eventAliasesParameter);
+        } else {
+            $aliases = [];
+        }
+        $definition = $container->findDefinition($this->dispatcherService);
+
+        foreach ($container->findTaggedServiceIds($this->listenerTag, true) as $id => $events) {
+            foreach ($events as $event) {
+                $priority = isset($event['priority']) ? $event['priority'] : 0;
+
+                if (!isset($event['event'])) {
+                    throw new InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag));
+                }
+                $event['event'] = $aliases[$event['event']] ?? $event['event'];
+
+                if (!isset($event['method'])) {
+                    $event['method'] = 'on'.preg_replace_callback([
+                        '/(?<=\b)[a-z]/i',
+                        '/[^a-z0-9]/i',
+                    ], function ($matches) { return strtoupper($matches[0]); }, $event['event']);
+                    $event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']);
+
+                    if (null !== ($class = $container->getDefinition($id)->getClass()) && ($r = $container->getReflectionClass($class, false)) && !$r->hasMethod($event['method']) && $r->hasMethod('__invoke')) {
+                        $event['method'] = '__invoke';
+                    }
+                }
+
+                $definition->addMethodCall('addListener', [$event['event'], [new ServiceClosureArgument(new Reference($id)), $event['method']], $priority]);
+
+                if (isset($this->hotPathEvents[$event['event']])) {
+                    $container->getDefinition($id)->addTag($this->hotPathTagName);
+                }
+            }
+        }
+
+        $extractingDispatcher = new ExtractingEventDispatcher();
+
+        foreach ($container->findTaggedServiceIds($this->subscriberTag, true) as $id => $attributes) {
+            $def = $container->getDefinition($id);
+
+            // We must assume that the class value has been correctly filled, even if the service is created by a factory
+            $class = $def->getClass();
+
+            if (!$r = $container->getReflectionClass($class)) {
+                throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
+            }
+            if (!$r->isSubclassOf(EventSubscriberInterface::class)) {
+                throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, EventSubscriberInterface::class));
+            }
+            $class = $r->name;
+
+            ExtractingEventDispatcher::$aliases = $aliases;
+            ExtractingEventDispatcher::$subscriber = $class;
+            $extractingDispatcher->addSubscriber($extractingDispatcher);
+            foreach ($extractingDispatcher->listeners as $args) {
+                $args[1] = [new ServiceClosureArgument(new Reference($id)), $args[1]];
+                $definition->addMethodCall('addListener', $args);
+
+                if (isset($this->hotPathEvents[$args[0]])) {
+                    $container->getDefinition($id)->addTag($this->hotPathTagName);
+                }
+            }
+            $extractingDispatcher->listeners = [];
+            ExtractingEventDispatcher::$aliases = [];
+        }
+    }
+}
+
+/**
+ * @internal
+ */
+class ExtractingEventDispatcher extends EventDispatcher implements EventSubscriberInterface
+{
+    public $listeners = [];
+
+    public static $aliases = [];
+    public static $subscriber;
+
+    public function addListener($eventName, $listener, $priority = 0)
+    {
+        $this->listeners[] = [$eventName, $listener[1], $priority];
+    }
+
+    public static function getSubscribedEvents()
+    {
+        $events = [];
+
+        foreach ([self::$subscriber, 'getSubscribedEvents']() as $eventName => $params) {
+            $events[self::$aliases[$eventName] ?? $eventName] = $params;
+        }
+
+        return $events;
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Event.php b/vendor/symfony/event-dispatcher/Event.php
new file mode 100644
index 0000000..307c4be
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Event.php
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher;
+
+/**
+ * @deprecated since Symfony 4.3, use "Symfony\Contracts\EventDispatcher\Event" instead
+ */
+class Event
+{
+    private $propagationStopped = false;
+
+    /**
+     * @return bool Whether propagation was already stopped for this event
+     *
+     * @deprecated since Symfony 4.3, use "Symfony\Contracts\EventDispatcher\Event" instead
+     */
+    public function isPropagationStopped()
+    {
+        return $this->propagationStopped;
+    }
+
+    /**
+     * @deprecated since Symfony 4.3, use "Symfony\Contracts\EventDispatcher\Event" instead
+     */
+    public function stopPropagation()
+    {
+        $this->propagationStopped = true;
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/EventDispatcher.php b/vendor/symfony/event-dispatcher/EventDispatcher.php
new file mode 100644
index 0000000..e68918c
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/EventDispatcher.php
@@ -0,0 +1,308 @@
+<?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\EventDispatcher;
+
+use Psr\EventDispatcher\StoppableEventInterface;
+use Symfony\Component\EventDispatcher\Debug\WrappedListener;
+use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
+
+/**
+ * The EventDispatcherInterface is the central point of Symfony's event listener system.
+ *
+ * Listeners are registered on the manager and events are dispatched through the
+ * manager.
+ *
+ * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
+ * @author Jonathan Wage <jonwage@gmail.com>
+ * @author Roman Borschel <roman@code-factory.org>
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @author Jordan Alliot <jordan.alliot@gmail.com>
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+class EventDispatcher implements EventDispatcherInterface
+{
+    private $listeners = [];
+    private $sorted = [];
+    private $optimized;
+
+    public function __construct()
+    {
+        if (__CLASS__ === \get_class($this)) {
+            $this->optimized = [];
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @param string|null $eventName
+     */
+    public function dispatch($event/*, string $eventName = null*/)
+    {
+        $eventName = 1 < \func_num_args() ? \func_get_arg(1) : null;
+
+        if (\is_object($event)) {
+            $eventName = $eventName ?? \get_class($event);
+        } else {
+            @trigger_error(sprintf('Calling the "%s::dispatch()" method with the event name as first argument is deprecated since Symfony 4.3, pass it second and provide the event object first instead.', EventDispatcherInterface::class), E_USER_DEPRECATED);
+            $swap = $event;
+            $event = $eventName ?? new Event();
+            $eventName = $swap;
+
+            if (!$event instanceof Event) {
+                throw new \TypeError(sprintf('Argument 1 passed to "%s::dispatch()" must be an instance of %s, %s given.', EventDispatcherInterface::class, Event::class, \is_object($event) ? \get_class($event) : \gettype($event)));
+            }
+        }
+
+        if (null !== $this->optimized && null !== $eventName) {
+            $listeners = $this->optimized[$eventName] ?? (empty($this->listeners[$eventName]) ? [] : $this->optimizeListeners($eventName));
+        } else {
+            $listeners = $this->getListeners($eventName);
+        }
+
+        if ($listeners) {
+            $this->callListeners($listeners, $eventName, $event);
+        }
+
+        return $event;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getListeners($eventName = null)
+    {
+        if (null !== $eventName) {
+            if (empty($this->listeners[$eventName])) {
+                return [];
+            }
+
+            if (!isset($this->sorted[$eventName])) {
+                $this->sortListeners($eventName);
+            }
+
+            return $this->sorted[$eventName];
+        }
+
+        foreach ($this->listeners as $eventName => $eventListeners) {
+            if (!isset($this->sorted[$eventName])) {
+                $this->sortListeners($eventName);
+            }
+        }
+
+        return array_filter($this->sorted);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getListenerPriority($eventName, $listener)
+    {
+        if (empty($this->listeners[$eventName])) {
+            return;
+        }
+
+        if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
+            $listener[0] = $listener[0]();
+        }
+
+        foreach ($this->listeners[$eventName] as $priority => &$listeners) {
+            foreach ($listeners as &$v) {
+                if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure) {
+                    $v[0] = $v[0]();
+                }
+                if ($v === $listener) {
+                    return $priority;
+                }
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasListeners($eventName = null)
+    {
+        if (null !== $eventName) {
+            return !empty($this->listeners[$eventName]);
+        }
+
+        foreach ($this->listeners as $eventListeners) {
+            if ($eventListeners) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addListener($eventName, $listener, $priority = 0)
+    {
+        $this->listeners[$eventName][$priority][] = $listener;
+        unset($this->sorted[$eventName], $this->optimized[$eventName]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function removeListener($eventName, $listener)
+    {
+        if (empty($this->listeners[$eventName])) {
+            return;
+        }
+
+        if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
+            $listener[0] = $listener[0]();
+        }
+
+        foreach ($this->listeners[$eventName] as $priority => &$listeners) {
+            foreach ($listeners as $k => &$v) {
+                if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure) {
+                    $v[0] = $v[0]();
+                }
+                if ($v === $listener) {
+                    unset($listeners[$k], $this->sorted[$eventName], $this->optimized[$eventName]);
+                }
+            }
+
+            if (!$listeners) {
+                unset($this->listeners[$eventName][$priority]);
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addSubscriber(EventSubscriberInterface $subscriber)
+    {
+        foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
+            if (\is_string($params)) {
+                $this->addListener($eventName, [$subscriber, $params]);
+            } elseif (\is_string($params[0])) {
+                $this->addListener($eventName, [$subscriber, $params[0]], isset($params[1]) ? $params[1] : 0);
+            } else {
+                foreach ($params as $listener) {
+                    $this->addListener($eventName, [$subscriber, $listener[0]], isset($listener[1]) ? $listener[1] : 0);
+                }
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function removeSubscriber(EventSubscriberInterface $subscriber)
+    {
+        foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
+            if (\is_array($params) && \is_array($params[0])) {
+                foreach ($params as $listener) {
+                    $this->removeListener($eventName, [$subscriber, $listener[0]]);
+                }
+            } else {
+                $this->removeListener($eventName, [$subscriber, \is_string($params) ? $params : $params[0]]);
+            }
+        }
+    }
+
+    /**
+     * Triggers the listeners of an event.
+     *
+     * This method can be overridden to add functionality that is executed
+     * for each listener.
+     *
+     * @param callable[] $listeners The event listeners
+     * @param string     $eventName The name of the event to dispatch
+     * @param object     $event     The event object to pass to the event handlers/listeners
+     */
+    protected function callListeners(iterable $listeners, string $eventName, $event)
+    {
+        if ($event instanceof Event) {
+            $this->doDispatch($listeners, $eventName, $event);
+
+            return;
+        }
+
+        $stoppable = $event instanceof ContractsEvent || $event instanceof StoppableEventInterface;
+
+        foreach ($listeners as $listener) {
+            if ($stoppable && $event->isPropagationStopped()) {
+                break;
+            }
+            // @deprecated: the ternary operator is part of a BC layer and should be removed in 5.0
+            $listener($listener instanceof WrappedListener ? new LegacyEventProxy($event) : $event, $eventName, $this);
+        }
+    }
+
+    /**
+     * @deprecated since Symfony 4.3, use callListeners() instead
+     */
+    protected function doDispatch($listeners, $eventName, Event $event)
+    {
+        foreach ($listeners as $listener) {
+            if ($event->isPropagationStopped()) {
+                break;
+            }
+            $listener($event, $eventName, $this);
+        }
+    }
+
+    /**
+     * Sorts the internal list of listeners for the given event by priority.
+     */
+    private function sortListeners(string $eventName)
+    {
+        krsort($this->listeners[$eventName]);
+        $this->sorted[$eventName] = [];
+
+        foreach ($this->listeners[$eventName] as &$listeners) {
+            foreach ($listeners as $k => $listener) {
+                if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
+                    $listener[0] = $listener[0]();
+                }
+                $this->sorted[$eventName][] = $listener;
+            }
+        }
+    }
+
+    /**
+     * Optimizes the internal list of listeners for the given event by priority.
+     */
+    private function optimizeListeners(string $eventName): array
+    {
+        krsort($this->listeners[$eventName]);
+        $this->optimized[$eventName] = [];
+
+        foreach ($this->listeners[$eventName] as &$listeners) {
+            foreach ($listeners as &$listener) {
+                $closure = &$this->optimized[$eventName][];
+                if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
+                    $closure = static function (...$args) use (&$listener, &$closure) {
+                        if ($listener[0] instanceof \Closure) {
+                            $listener[0] = $listener[0]();
+                        }
+                        ($closure = \Closure::fromCallable($listener))(...$args);
+                    };
+                } else {
+                    $closure = $listener instanceof \Closure || $listener instanceof WrappedListener ? $listener : \Closure::fromCallable($listener);
+                }
+            }
+        }
+
+        return $this->optimized[$eventName];
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/EventDispatcherInterface.php b/vendor/symfony/event-dispatcher/EventDispatcherInterface.php
new file mode 100644
index 0000000..ceaa62a
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/EventDispatcherInterface.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\EventDispatcher;
+
+use Symfony\Contracts\EventDispatcher\EventDispatcherInterface as ContractsEventDispatcherInterface;
+
+/**
+ * The EventDispatcherInterface is the central point of Symfony's event listener system.
+ * Listeners are registered on the manager and events are dispatched through the
+ * manager.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+interface EventDispatcherInterface extends ContractsEventDispatcherInterface
+{
+    /**
+     * Adds an event listener that listens on the specified events.
+     *
+     * @param string   $eventName The event to listen on
+     * @param callable $listener  The listener
+     * @param int      $priority  The higher this value, the earlier an event
+     *                            listener will be triggered in the chain (defaults to 0)
+     */
+    public function addListener($eventName, $listener, $priority = 0);
+
+    /**
+     * Adds an event subscriber.
+     *
+     * The subscriber is asked for all the events it is
+     * interested in and added as a listener for these events.
+     */
+    public function addSubscriber(EventSubscriberInterface $subscriber);
+
+    /**
+     * Removes an event listener from the specified events.
+     *
+     * @param string   $eventName The event to remove a listener from
+     * @param callable $listener  The listener to remove
+     */
+    public function removeListener($eventName, $listener);
+
+    public function removeSubscriber(EventSubscriberInterface $subscriber);
+
+    /**
+     * Gets the listeners of a specific event or all listeners sorted by descending priority.
+     *
+     * @param string|null $eventName The name of the event
+     *
+     * @return array The event listeners for the specified event, or all event listeners by event name
+     */
+    public function getListeners($eventName = null);
+
+    /**
+     * Gets the listener priority for a specific event.
+     *
+     * Returns null if the event or the listener does not exist.
+     *
+     * @param string   $eventName The name of the event
+     * @param callable $listener  The listener
+     *
+     * @return int|null The event listener priority
+     */
+    public function getListenerPriority($eventName, $listener);
+
+    /**
+     * Checks whether an event has any registered listeners.
+     *
+     * @param string|null $eventName The name of the event
+     *
+     * @return bool true if the specified event has any listeners, false otherwise
+     */
+    public function hasListeners($eventName = null);
+}
diff --git a/vendor/symfony/event-dispatcher/EventSubscriberInterface.php b/vendor/symfony/event-dispatcher/EventSubscriberInterface.php
new file mode 100644
index 0000000..824f215
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/EventSubscriberInterface.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\EventDispatcher;
+
+/**
+ * An EventSubscriber knows itself what events it is interested in.
+ * If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes
+ * {@link getSubscribedEvents} and registers the subscriber as a listener for all
+ * returned events.
+ *
+ * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
+ * @author Jonathan Wage <jonwage@gmail.com>
+ * @author Roman Borschel <roman@code-factory.org>
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+interface EventSubscriberInterface
+{
+    /**
+     * Returns an array of event names this subscriber wants to listen to.
+     *
+     * The array keys are event names and the value can be:
+     *
+     *  * The method name to call (priority defaults to 0)
+     *  * An array composed of the method name to call and the priority
+     *  * An array of arrays composed of the method names to call and respective
+     *    priorities, or 0 if unset
+     *
+     * For instance:
+     *
+     *  * ['eventName' => 'methodName']
+     *  * ['eventName' => ['methodName', $priority]]
+     *  * ['eventName' => [['methodName1', $priority], ['methodName2']]]
+     *
+     * @return array The event names to listen to
+     */
+    public static function getSubscribedEvents();
+}
diff --git a/vendor/symfony/event-dispatcher/GenericEvent.php b/vendor/symfony/event-dispatcher/GenericEvent.php
new file mode 100644
index 0000000..f005e3a
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/GenericEvent.php
@@ -0,0 +1,175 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher;
+
+/**
+ * Event encapsulation class.
+ *
+ * Encapsulates events thus decoupling the observer from the subject they encapsulate.
+ *
+ * @author Drak <drak@zikula.org>
+ */
+class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
+{
+    protected $subject;
+    protected $arguments;
+
+    /**
+     * Encapsulate an event with $subject and $args.
+     *
+     * @param mixed $subject   The subject of the event, usually an object or a callable
+     * @param array $arguments Arguments to store in the event
+     */
+    public function __construct($subject = null, array $arguments = [])
+    {
+        $this->subject = $subject;
+        $this->arguments = $arguments;
+    }
+
+    /**
+     * Getter for subject property.
+     *
+     * @return mixed The observer subject
+     */
+    public function getSubject()
+    {
+        return $this->subject;
+    }
+
+    /**
+     * Get argument by key.
+     *
+     * @param string $key Key
+     *
+     * @return mixed Contents of array key
+     *
+     * @throws \InvalidArgumentException if key is not found
+     */
+    public function getArgument($key)
+    {
+        if ($this->hasArgument($key)) {
+            return $this->arguments[$key];
+        }
+
+        throw new \InvalidArgumentException(sprintf('Argument "%s" not found.', $key));
+    }
+
+    /**
+     * Add argument to event.
+     *
+     * @param string $key   Argument name
+     * @param mixed  $value Value
+     *
+     * @return $this
+     */
+    public function setArgument($key, $value)
+    {
+        $this->arguments[$key] = $value;
+
+        return $this;
+    }
+
+    /**
+     * Getter for all arguments.
+     *
+     * @return array
+     */
+    public function getArguments()
+    {
+        return $this->arguments;
+    }
+
+    /**
+     * Set args property.
+     *
+     * @param array $args Arguments
+     *
+     * @return $this
+     */
+    public function setArguments(array $args = [])
+    {
+        $this->arguments = $args;
+
+        return $this;
+    }
+
+    /**
+     * Has argument.
+     *
+     * @param string $key Key of arguments array
+     *
+     * @return bool
+     */
+    public function hasArgument($key)
+    {
+        return \array_key_exists($key, $this->arguments);
+    }
+
+    /**
+     * ArrayAccess for argument getter.
+     *
+     * @param string $key Array key
+     *
+     * @return mixed
+     *
+     * @throws \InvalidArgumentException if key does not exist in $this->args
+     */
+    public function offsetGet($key)
+    {
+        return $this->getArgument($key);
+    }
+
+    /**
+     * ArrayAccess for argument setter.
+     *
+     * @param string $key   Array key to set
+     * @param mixed  $value Value
+     */
+    public function offsetSet($key, $value)
+    {
+        $this->setArgument($key, $value);
+    }
+
+    /**
+     * ArrayAccess for unset argument.
+     *
+     * @param string $key Array key
+     */
+    public function offsetUnset($key)
+    {
+        if ($this->hasArgument($key)) {
+            unset($this->arguments[$key]);
+        }
+    }
+
+    /**
+     * ArrayAccess has argument.
+     *
+     * @param string $key Array key
+     *
+     * @return bool
+     */
+    public function offsetExists($key)
+    {
+        return $this->hasArgument($key);
+    }
+
+    /**
+     * IteratorAggregate for iterating over the object like an array.
+     *
+     * @return \ArrayIterator
+     */
+    public function getIterator()
+    {
+        return new \ArrayIterator($this->arguments);
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php b/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php
new file mode 100644
index 0000000..082e2a0
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php
@@ -0,0 +1,102 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher;
+
+/**
+ * A read-only proxy for an event dispatcher.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class ImmutableEventDispatcher implements EventDispatcherInterface
+{
+    private $dispatcher;
+
+    public function __construct(EventDispatcherInterface $dispatcher)
+    {
+        $this->dispatcher = LegacyEventDispatcherProxy::decorate($dispatcher);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @param string|null $eventName
+     */
+    public function dispatch($event/*, string $eventName = null*/)
+    {
+        $eventName = 1 < \func_num_args() ? \func_get_arg(1) : null;
+
+        if (\is_scalar($event)) {
+            // deprecated
+            $swap = $event;
+            $event = $eventName ?? new Event();
+            $eventName = $swap;
+        }
+
+        return $this->dispatcher->dispatch($event, $eventName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addListener($eventName, $listener, $priority = 0)
+    {
+        throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addSubscriber(EventSubscriberInterface $subscriber)
+    {
+        throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function removeListener($eventName, $listener)
+    {
+        throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function removeSubscriber(EventSubscriberInterface $subscriber)
+    {
+        throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getListeners($eventName = null)
+    {
+        return $this->dispatcher->getListeners($eventName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getListenerPriority($eventName, $listener)
+    {
+        return $this->dispatcher->getListenerPriority($eventName, $listener);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasListeners($eventName = null)
+    {
+        return $this->dispatcher->hasListeners($eventName);
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/LICENSE b/vendor/symfony/event-dispatcher/LICENSE
new file mode 100644
index 0000000..a677f43
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2019 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/symfony/event-dispatcher/LegacyEventDispatcherProxy.php b/vendor/symfony/event-dispatcher/LegacyEventDispatcherProxy.php
new file mode 100644
index 0000000..3ae3735
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/LegacyEventDispatcherProxy.php
@@ -0,0 +1,147 @@
+<?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\EventDispatcher;
+
+use Psr\EventDispatcher\StoppableEventInterface;
+use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
+use Symfony\Contracts\EventDispatcher\EventDispatcherInterface as ContractsEventDispatcherInterface;
+
+/**
+ * An helper class to provide BC/FC with the legacy signature of EventDispatcherInterface::dispatch().
+ *
+ * This class should be deprecated in Symfony 5.1
+ *
+ * @author Nicolas Grekas <p@tchwork.com>
+ */
+final class LegacyEventDispatcherProxy implements EventDispatcherInterface
+{
+    private $dispatcher;
+
+    public static function decorate(?ContractsEventDispatcherInterface $dispatcher): ?ContractsEventDispatcherInterface
+    {
+        if (null === $dispatcher) {
+            return null;
+        }
+        $r = new \ReflectionMethod($dispatcher, 'dispatch');
+        $param2 = $r->getParameters()[1] ?? null;
+
+        if (!$param2 || !$param2->hasType() || $param2->getType()->isBuiltin()) {
+            return $dispatcher;
+        }
+
+        @trigger_error(sprintf('The signature of the "%s::dispatch()" method should be updated to "dispatch($event, string $eventName = null)", not doing so is deprecated since Symfony 4.3.', $r->class), E_USER_DEPRECATED);
+
+        $self = new self();
+        $self->dispatcher = $dispatcher;
+
+        return $self;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @param string|null $eventName
+     */
+    public function dispatch($event/*, string $eventName = null*/)
+    {
+        $eventName = 1 < \func_num_args() ? \func_get_arg(1) : null;
+
+        if (\is_object($event)) {
+            $eventName = $eventName ?? \get_class($event);
+        } else {
+            @trigger_error(sprintf('Calling the "%s::dispatch()" method with the event name as first argument is deprecated since Symfony 4.3, pass it second and provide the event object first instead.', ContractsEventDispatcherInterface::class), E_USER_DEPRECATED);
+            $swap = $event;
+            $event = $eventName ?? new Event();
+            $eventName = $swap;
+
+            if (!$event instanceof Event) {
+                throw new \TypeError(sprintf('Argument 1 passed to "%s::dispatch()" must be an instance of %s, %s given.', ContractsEventDispatcherInterface::class, Event::class, \is_object($event) ? \get_class($event) : \gettype($event)));
+            }
+        }
+
+        $listeners = $this->getListeners($eventName);
+        $stoppable = $event instanceof Event || $event instanceof ContractsEvent || $event instanceof StoppableEventInterface;
+
+        foreach ($listeners as $listener) {
+            if ($stoppable && $event->isPropagationStopped()) {
+                break;
+            }
+            $listener($event, $eventName, $this);
+        }
+
+        return $event;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addListener($eventName, $listener, $priority = 0)
+    {
+        return $this->dispatcher->addListener($eventName, $listener, $priority);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addSubscriber(EventSubscriberInterface $subscriber)
+    {
+        return $this->dispatcher->addSubscriber($subscriber);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function removeListener($eventName, $listener)
+    {
+        return $this->dispatcher->removeListener($eventName, $listener);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function removeSubscriber(EventSubscriberInterface $subscriber)
+    {
+        return $this->dispatcher->removeSubscriber($subscriber);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getListeners($eventName = null)
+    {
+        return $this->dispatcher->getListeners($eventName);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getListenerPriority($eventName, $listener)
+    {
+        return $this->dispatcher->getListenerPriority($eventName, $listener);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasListeners($eventName = null)
+    {
+        return $this->dispatcher->hasListeners($eventName);
+    }
+
+    /**
+     * Proxies all method calls to the original event dispatcher.
+     */
+    public function __call($method, $arguments)
+    {
+        return $this->dispatcher->{$method}(...$arguments);
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/LegacyEventProxy.php b/vendor/symfony/event-dispatcher/LegacyEventProxy.php
new file mode 100644
index 0000000..cad8cfa
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/LegacyEventProxy.php
@@ -0,0 +1,62 @@
+<?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\EventDispatcher;
+
+use Psr\EventDispatcher\StoppableEventInterface;
+use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
+
+/**
+ * @internal to be removed in 5.0.
+ */
+final class LegacyEventProxy extends Event
+{
+    private $event;
+
+    /**
+     * @param object $event
+     */
+    public function __construct($event)
+    {
+        $this->event = $event;
+    }
+
+    /**
+     * @return object $event
+     */
+    public function getEvent()
+    {
+        return $this->event;
+    }
+
+    public function isPropagationStopped()
+    {
+        if (!$this->event instanceof ContractsEvent && !$this->event instanceof StoppableEventInterface) {
+            return false;
+        }
+
+        return $this->event->isPropagationStopped();
+    }
+
+    public function stopPropagation()
+    {
+        if (!$this->event instanceof ContractsEvent) {
+            return;
+        }
+
+        $this->event->stopPropagation();
+    }
+
+    public function __call($name, $args)
+    {
+        return $this->event->{$name}(...$args);
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/README.md b/vendor/symfony/event-dispatcher/README.md
new file mode 100644
index 0000000..185c3fe
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/README.md
@@ -0,0 +1,15 @@
+EventDispatcher Component
+=========================
+
+The EventDispatcher component provides tools that allow your application
+components to communicate with each other by dispatching events and listening to
+them.
+
+Resources
+---------
+
+  * [Documentation](https://symfony.com/doc/current/components/event_dispatcher/index.html)
+  * [Contributing](https://symfony.com/doc/current/contributing/index.html)
+  * [Report issues](https://github.com/symfony/symfony/issues) and
+    [send Pull Requests](https://github.com/symfony/symfony/pulls)
+    in the [main Symfony repository](https://github.com/symfony/symfony)
diff --git a/vendor/symfony/event-dispatcher/Tests/ChildEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/ChildEventDispatcherTest.php
new file mode 100644
index 0000000..442b88f
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Tests/ChildEventDispatcherTest.php
@@ -0,0 +1,26 @@
+<?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\EventDispatcher\Tests;
+
+use Symfony\Component\EventDispatcher\EventDispatcher;
+
+class ChildEventDispatcherTest extends EventDispatcherTest
+{
+    protected function createEventDispatcher()
+    {
+        return new ChildEventDispatcher();
+    }
+}
+
+class ChildEventDispatcher extends EventDispatcher
+{
+}
diff --git a/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php
new file mode 100644
index 0000000..ea476ee
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php
@@ -0,0 +1,319 @@
+<?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\EventDispatcher\Tests\Debug;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\Stopwatch\Stopwatch;
+use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
+
+class TraceableEventDispatcherTest extends TestCase
+{
+    public function testAddRemoveListener()
+    {
+        $dispatcher = new EventDispatcher();
+        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+
+        $tdispatcher->addListener('foo', $listener = function () {});
+        $listeners = $dispatcher->getListeners('foo');
+        $this->assertCount(1, $listeners);
+        $this->assertSame($listener, $listeners[0]);
+
+        $tdispatcher->removeListener('foo', $listener);
+        $this->assertCount(0, $dispatcher->getListeners('foo'));
+    }
+
+    public function testGetListeners()
+    {
+        $dispatcher = new EventDispatcher();
+        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+
+        $tdispatcher->addListener('foo', $listener = function () {});
+        $this->assertSame($dispatcher->getListeners('foo'), $tdispatcher->getListeners('foo'));
+    }
+
+    public function testHasListeners()
+    {
+        $dispatcher = new EventDispatcher();
+        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+
+        $this->assertFalse($dispatcher->hasListeners('foo'));
+        $this->assertFalse($tdispatcher->hasListeners('foo'));
+
+        $tdispatcher->addListener('foo', $listener = function () {});
+        $this->assertTrue($dispatcher->hasListeners('foo'));
+        $this->assertTrue($tdispatcher->hasListeners('foo'));
+    }
+
+    public function testGetListenerPriority()
+    {
+        $dispatcher = new EventDispatcher();
+        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+
+        $tdispatcher->addListener('foo', function () {}, 123);
+
+        $listeners = $dispatcher->getListeners('foo');
+        $this->assertSame(123, $tdispatcher->getListenerPriority('foo', $listeners[0]));
+
+        // Verify that priority is preserved when listener is removed and re-added
+        // in preProcess() and postProcess().
+        $tdispatcher->dispatch(new Event(), 'foo');
+        $listeners = $dispatcher->getListeners('foo');
+        $this->assertSame(123, $tdispatcher->getListenerPriority('foo', $listeners[0]));
+    }
+
+    public function testGetListenerPriorityWhileDispatching()
+    {
+        $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+        $priorityWhileDispatching = null;
+
+        $listener = function () use ($tdispatcher, &$priorityWhileDispatching, &$listener) {
+            $priorityWhileDispatching = $tdispatcher->getListenerPriority('bar', $listener);
+        };
+
+        $tdispatcher->addListener('bar', $listener, 5);
+        $tdispatcher->dispatch(new Event(), 'bar');
+        $this->assertSame(5, $priorityWhileDispatching);
+    }
+
+    public function testAddRemoveSubscriber()
+    {
+        $dispatcher = new EventDispatcher();
+        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+
+        $subscriber = new EventSubscriber();
+
+        $tdispatcher->addSubscriber($subscriber);
+        $listeners = $dispatcher->getListeners('foo');
+        $this->assertCount(1, $listeners);
+        $this->assertSame([$subscriber, 'call'], $listeners[0]);
+
+        $tdispatcher->removeSubscriber($subscriber);
+        $this->assertCount(0, $dispatcher->getListeners('foo'));
+    }
+
+    public function testGetCalledListeners()
+    {
+        $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+        $tdispatcher->addListener('foo', function () {}, 5);
+
+        $listeners = $tdispatcher->getNotCalledListeners();
+        $this->assertArrayHasKey('stub', $listeners[0]);
+        unset($listeners[0]['stub']);
+        $this->assertEquals([], $tdispatcher->getCalledListeners());
+        $this->assertEquals([['event' => 'foo', 'pretty' => 'closure', 'priority' => 5]], $listeners);
+
+        $tdispatcher->dispatch(new Event(), 'foo');
+
+        $listeners = $tdispatcher->getCalledListeners();
+        $this->assertArrayHasKey('stub', $listeners[0]);
+        unset($listeners[0]['stub']);
+        $this->assertEquals([['event' => 'foo', 'pretty' => 'closure', 'priority' => 5]], $listeners);
+        $this->assertEquals([], $tdispatcher->getNotCalledListeners());
+    }
+
+    public function testClearCalledListeners()
+    {
+        $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+        $tdispatcher->addListener('foo', function () {}, 5);
+
+        $tdispatcher->dispatch(new Event(), 'foo');
+        $tdispatcher->reset();
+
+        $listeners = $tdispatcher->getNotCalledListeners();
+        $this->assertArrayHasKey('stub', $listeners[0]);
+        unset($listeners[0]['stub']);
+        $this->assertEquals([], $tdispatcher->getCalledListeners());
+        $this->assertEquals([['event' => 'foo', 'pretty' => 'closure', 'priority' => 5]], $listeners);
+    }
+
+    public function testDispatchContractsEvent()
+    {
+        $expectedEvent = new ContractsEvent();
+        $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+        $tdispatcher->addListener('foo', function ($event) use ($expectedEvent) {
+            $this->assertSame($event, $expectedEvent);
+        }, 5);
+        $tdispatcher->dispatch($expectedEvent, 'foo');
+
+        $listeners = $tdispatcher->getCalledListeners();
+        $this->assertArrayHasKey('stub', $listeners[0]);
+    }
+
+    public function testDispatchAfterReset()
+    {
+        $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+        $tdispatcher->addListener('foo', function () {}, 5);
+
+        $tdispatcher->reset();
+        $tdispatcher->dispatch(new Event(), 'foo');
+
+        $listeners = $tdispatcher->getCalledListeners();
+        $this->assertArrayHasKey('stub', $listeners[0]);
+    }
+
+    public function testGetCalledListenersNested()
+    {
+        $tdispatcher = null;
+        $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+        $dispatcher->addListener('foo', function (Event $event, $eventName, $dispatcher) use (&$tdispatcher) {
+            $tdispatcher = $dispatcher;
+            $dispatcher->dispatch(new Event(), 'bar');
+        });
+        $dispatcher->addListener('bar', function (Event $event) {});
+        $dispatcher->dispatch(new Event(), 'foo');
+        $this->assertSame($dispatcher, $tdispatcher);
+        $this->assertCount(2, $dispatcher->getCalledListeners());
+    }
+
+    public function testItReturnsNoOrphanedEventsWhenCreated()
+    {
+        $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+        $events = $tdispatcher->getOrphanedEvents();
+        $this->assertEmpty($events);
+    }
+
+    public function testItReturnsOrphanedEventsAfterDispatch()
+    {
+        $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+        $tdispatcher->dispatch(new Event(), 'foo');
+        $events = $tdispatcher->getOrphanedEvents();
+        $this->assertCount(1, $events);
+        $this->assertEquals(['foo'], $events);
+    }
+
+    public function testItDoesNotReturnHandledEvents()
+    {
+        $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+        $tdispatcher->addListener('foo', function () {});
+        $tdispatcher->dispatch(new Event(), 'foo');
+        $events = $tdispatcher->getOrphanedEvents();
+        $this->assertEmpty($events);
+    }
+
+    public function testLogger()
+    {
+        $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
+
+        $dispatcher = new EventDispatcher();
+        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
+        $tdispatcher->addListener('foo', $listener1 = function () {});
+        $tdispatcher->addListener('foo', $listener2 = function () {});
+
+        $logger->expects($this->at(0))->method('debug')->with('Notified event "{event}" to listener "{listener}".', ['event' => 'foo', 'listener' => 'closure']);
+        $logger->expects($this->at(1))->method('debug')->with('Notified event "{event}" to listener "{listener}".', ['event' => 'foo', 'listener' => 'closure']);
+
+        $tdispatcher->dispatch(new Event(), 'foo');
+    }
+
+    public function testLoggerWithStoppedEvent()
+    {
+        $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
+
+        $dispatcher = new EventDispatcher();
+        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
+        $tdispatcher->addListener('foo', $listener1 = function (Event $event) { $event->stopPropagation(); });
+        $tdispatcher->addListener('foo', $listener2 = function () {});
+
+        $logger->expects($this->at(0))->method('debug')->with('Notified event "{event}" to listener "{listener}".', ['event' => 'foo', 'listener' => 'closure']);
+        $logger->expects($this->at(1))->method('debug')->with('Listener "{listener}" stopped propagation of the event "{event}".', ['event' => 'foo', 'listener' => 'closure']);
+        $logger->expects($this->at(2))->method('debug')->with('Listener "{listener}" was not called for event "{event}".', ['event' => 'foo', 'listener' => 'closure']);
+
+        $tdispatcher->dispatch(new Event(), 'foo');
+    }
+
+    public function testDispatchCallListeners()
+    {
+        $called = [];
+
+        $dispatcher = new EventDispatcher();
+        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
+        $tdispatcher->addListener('foo', function () use (&$called) { $called[] = 'foo1'; }, 10);
+        $tdispatcher->addListener('foo', function () use (&$called) { $called[] = 'foo2'; }, 20);
+
+        $tdispatcher->dispatch(new Event(), 'foo');
+
+        $this->assertSame(['foo2', 'foo1'], $called);
+    }
+
+    public function testDispatchNested()
+    {
+        $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+        $loop = 1;
+        $dispatchedEvents = 0;
+        $dispatcher->addListener('foo', $listener1 = function () use ($dispatcher, &$loop) {
+            ++$loop;
+            if (2 == $loop) {
+                $dispatcher->dispatch(new Event(), 'foo');
+            }
+        });
+        $dispatcher->addListener('foo', function () use (&$dispatchedEvents) {
+            ++$dispatchedEvents;
+        });
+
+        $dispatcher->dispatch(new Event(), 'foo');
+
+        $this->assertSame(2, $dispatchedEvents);
+    }
+
+    public function testDispatchReusedEventNested()
+    {
+        $nestedCall = false;
+        $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+        $dispatcher->addListener('foo', function (Event $e) use ($dispatcher) {
+            $dispatcher->dispatch(new Event(), 'bar', $e);
+        });
+        $dispatcher->addListener('bar', function (Event $e) use (&$nestedCall) {
+            $nestedCall = true;
+        });
+
+        $this->assertFalse($nestedCall);
+        $dispatcher->dispatch(new Event(), 'foo');
+        $this->assertTrue($nestedCall);
+    }
+
+    public function testListenerCanRemoveItselfWhenExecuted()
+    {
+        $eventDispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+        $listener1 = function ($event, $eventName, EventDispatcherInterface $dispatcher) use (&$listener1) {
+            $dispatcher->removeListener('foo', $listener1);
+        };
+        $eventDispatcher->addListener('foo', $listener1);
+        $eventDispatcher->addListener('foo', function () {});
+        $eventDispatcher->dispatch(new Event(), 'foo');
+
+        $this->assertCount(1, $eventDispatcher->getListeners('foo'), 'expected listener1 to be removed');
+    }
+
+    public function testClearOrphanedEvents()
+    {
+        $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+        $tdispatcher->dispatch(new Event(), 'foo');
+        $events = $tdispatcher->getOrphanedEvents();
+        $this->assertCount(1, $events);
+        $tdispatcher->reset();
+        $events = $tdispatcher->getOrphanedEvents();
+        $this->assertCount(0, $events);
+    }
+}
+
+class EventSubscriber implements EventSubscriberInterface
+{
+    public static function getSubscribedEvents()
+    {
+        return ['foo' => 'call'];
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Tests/Debug/WrappedListenerTest.php b/vendor/symfony/event-dispatcher/Tests/Debug/WrappedListenerTest.php
new file mode 100644
index 0000000..75e9012
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Tests/Debug/WrappedListenerTest.php
@@ -0,0 +1,60 @@
+<?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\EventDispatcher\Tests\Debug;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\Debug\WrappedListener;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\Stopwatch\Stopwatch;
+
+class WrappedListenerTest extends TestCase
+{
+    /**
+     * @dataProvider provideListenersToDescribe
+     */
+    public function testListenerDescription($listener, $expected)
+    {
+        $wrappedListener = new WrappedListener($listener, null, $this->getMockBuilder(Stopwatch::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock());
+
+        $this->assertStringMatchesFormat($expected, $wrappedListener->getPretty());
+    }
+
+    public function provideListenersToDescribe()
+    {
+        return [
+            [new FooListener(), 'Symfony\Component\EventDispatcher\Tests\Debug\FooListener::__invoke'],
+            [[new FooListener(), 'listen'], 'Symfony\Component\EventDispatcher\Tests\Debug\FooListener::listen'],
+            [['Symfony\Component\EventDispatcher\Tests\Debug\FooListener', 'listenStatic'], 'Symfony\Component\EventDispatcher\Tests\Debug\FooListener::listenStatic'],
+            [['Symfony\Component\EventDispatcher\Tests\Debug\FooListener', 'invalidMethod'], 'Symfony\Component\EventDispatcher\Tests\Debug\FooListener::invalidMethod'],
+            ['var_dump', 'var_dump'],
+            [function () {}, 'closure'],
+            [\Closure::fromCallable([new FooListener(), 'listen']), 'Symfony\Component\EventDispatcher\Tests\Debug\FooListener::listen'],
+            [\Closure::fromCallable(['Symfony\Component\EventDispatcher\Tests\Debug\FooListener', 'listenStatic']), 'Symfony\Component\EventDispatcher\Tests\Debug\FooListener::listenStatic'],
+            [\Closure::fromCallable(function () {}), 'closure'],
+        ];
+    }
+}
+
+class FooListener
+{
+    public function listen()
+    {
+    }
+
+    public function __invoke()
+    {
+    }
+
+    public static function listenStatic()
+    {
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php b/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php
new file mode 100644
index 0000000..d665f42
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php
@@ -0,0 +1,206 @@
+<?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\EventDispatcher\Tests\DependencyInjection;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
+
+class RegisterListenersPassTest extends TestCase
+{
+    /**
+     * Tests that event subscribers not implementing EventSubscriberInterface
+     * trigger an exception.
+     *
+     * @expectedException \InvalidArgumentException
+     */
+    public function testEventSubscriberWithoutInterface()
+    {
+        $builder = new ContainerBuilder();
+        $builder->register('event_dispatcher');
+        $builder->register('my_event_subscriber', 'stdClass')
+            ->addTag('kernel.event_subscriber');
+
+        $registerListenersPass = new RegisterListenersPass();
+        $registerListenersPass->process($builder);
+    }
+
+    public function testValidEventSubscriber()
+    {
+        $services = [
+            'my_event_subscriber' => [0 => []],
+        ];
+
+        $builder = new ContainerBuilder();
+        $eventDispatcherDefinition = $builder->register('event_dispatcher');
+        $builder->register('my_event_subscriber', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService')
+            ->addTag('kernel.event_subscriber');
+
+        $registerListenersPass = new RegisterListenersPass();
+        $registerListenersPass->process($builder);
+
+        $expectedCalls = [
+            [
+                'addListener',
+                [
+                    'event',
+                    [new ServiceClosureArgument(new Reference('my_event_subscriber')), 'onEvent'],
+                    0,
+                ],
+            ],
+        ];
+        $this->assertEquals($expectedCalls, $eventDispatcherDefinition->getMethodCalls());
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage The service "foo" tagged "kernel.event_listener" must not be abstract.
+     */
+    public function testAbstractEventListener()
+    {
+        $container = new ContainerBuilder();
+        $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_listener', []);
+        $container->register('event_dispatcher', 'stdClass');
+
+        $registerListenersPass = new RegisterListenersPass();
+        $registerListenersPass->process($container);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage The service "foo" tagged "kernel.event_subscriber" must not be abstract.
+     */
+    public function testAbstractEventSubscriber()
+    {
+        $container = new ContainerBuilder();
+        $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_subscriber', []);
+        $container->register('event_dispatcher', 'stdClass');
+
+        $registerListenersPass = new RegisterListenersPass();
+        $registerListenersPass->process($container);
+    }
+
+    public function testEventSubscriberResolvableClassName()
+    {
+        $container = new ContainerBuilder();
+
+        $container->setParameter('subscriber.class', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService');
+        $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', []);
+        $container->register('event_dispatcher', 'stdClass');
+
+        $registerListenersPass = new RegisterListenersPass();
+        $registerListenersPass->process($container);
+
+        $definition = $container->getDefinition('event_dispatcher');
+        $expectedCalls = [
+            [
+                'addListener',
+                [
+                    'event',
+                    [new ServiceClosureArgument(new Reference('foo')), 'onEvent'],
+                    0,
+                ],
+            ],
+        ];
+        $this->assertEquals($expectedCalls, $definition->getMethodCalls());
+    }
+
+    public function testHotPathEvents()
+    {
+        $container = new ContainerBuilder();
+
+        $container->register('foo', SubscriberService::class)->addTag('kernel.event_subscriber', []);
+        $container->register('event_dispatcher', 'stdClass');
+
+        (new RegisterListenersPass())->setHotPathEvents(['event'])->process($container);
+
+        $this->assertTrue($container->getDefinition('foo')->hasTag('container.hot_path'));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage You have requested a non-existent parameter "subscriber.class"
+     */
+    public function testEventSubscriberUnresolvableClassName()
+    {
+        $container = new ContainerBuilder();
+        $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', []);
+        $container->register('event_dispatcher', 'stdClass');
+
+        $registerListenersPass = new RegisterListenersPass();
+        $registerListenersPass->process($container);
+    }
+
+    public function testInvokableEventListener()
+    {
+        $container = new ContainerBuilder();
+        $container->register('foo', \stdClass::class)->addTag('kernel.event_listener', ['event' => 'foo.bar']);
+        $container->register('bar', InvokableListenerService::class)->addTag('kernel.event_listener', ['event' => 'foo.bar']);
+        $container->register('baz', InvokableListenerService::class)->addTag('kernel.event_listener', ['event' => 'event']);
+        $container->register('event_dispatcher', \stdClass::class);
+
+        $registerListenersPass = new RegisterListenersPass();
+        $registerListenersPass->process($container);
+
+        $definition = $container->getDefinition('event_dispatcher');
+        $expectedCalls = [
+            [
+                'addListener',
+                [
+                    'foo.bar',
+                    [new ServiceClosureArgument(new Reference('foo')), 'onFooBar'],
+                    0,
+                ],
+            ],
+            [
+                'addListener',
+                [
+                    'foo.bar',
+                    [new ServiceClosureArgument(new Reference('bar')), '__invoke'],
+                    0,
+                ],
+            ],
+            [
+                'addListener',
+                [
+                    'event',
+                    [new ServiceClosureArgument(new Reference('baz')), 'onEvent'],
+                    0,
+                ],
+            ],
+        ];
+        $this->assertEquals($expectedCalls, $definition->getMethodCalls());
+    }
+}
+
+class SubscriberService implements \Symfony\Component\EventDispatcher\EventSubscriberInterface
+{
+    public static function getSubscribedEvents()
+    {
+        return [
+            'event' => 'onEvent',
+        ];
+    }
+}
+
+class InvokableListenerService
+{
+    public function __invoke()
+    {
+    }
+
+    public function onEvent()
+    {
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php
new file mode 100644
index 0000000..e89f78c
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php
@@ -0,0 +1,486 @@
+<?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\EventDispatcher\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
+
+class EventDispatcherTest extends TestCase
+{
+    /* Some pseudo events */
+    const preFoo = 'pre.foo';
+    const postFoo = 'post.foo';
+    const preBar = 'pre.bar';
+    const postBar = 'post.bar';
+
+    /**
+     * @var EventDispatcher
+     */
+    private $dispatcher;
+
+    private $listener;
+
+    protected function setUp()
+    {
+        $this->dispatcher = $this->createEventDispatcher();
+        $this->listener = new TestEventListener();
+    }
+
+    protected function tearDown()
+    {
+        $this->dispatcher = null;
+        $this->listener = null;
+    }
+
+    protected function createEventDispatcher()
+    {
+        return new EventDispatcher();
+    }
+
+    public function testInitialState()
+    {
+        $this->assertEquals([], $this->dispatcher->getListeners());
+        $this->assertFalse($this->dispatcher->hasListeners(self::preFoo));
+        $this->assertFalse($this->dispatcher->hasListeners(self::postFoo));
+    }
+
+    public function testAddListener()
+    {
+        $this->dispatcher->addListener('pre.foo', [$this->listener, 'preFoo']);
+        $this->dispatcher->addListener('post.foo', [$this->listener, 'postFoo']);
+        $this->assertTrue($this->dispatcher->hasListeners());
+        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
+        $this->assertTrue($this->dispatcher->hasListeners(self::postFoo));
+        $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo));
+        $this->assertCount(1, $this->dispatcher->getListeners(self::postFoo));
+        $this->assertCount(2, $this->dispatcher->getListeners());
+    }
+
+    public function testGetListenersSortsByPriority()
+    {
+        $listener1 = new TestEventListener();
+        $listener2 = new TestEventListener();
+        $listener3 = new TestEventListener();
+        $listener1->name = '1';
+        $listener2->name = '2';
+        $listener3->name = '3';
+
+        $this->dispatcher->addListener('pre.foo', [$listener1, 'preFoo'], -10);
+        $this->dispatcher->addListener('pre.foo', [$listener2, 'preFoo'], 10);
+        $this->dispatcher->addListener('pre.foo', [$listener3, 'preFoo']);
+
+        $expected = [
+            [$listener2, 'preFoo'],
+            [$listener3, 'preFoo'],
+            [$listener1, 'preFoo'],
+        ];
+
+        $this->assertSame($expected, $this->dispatcher->getListeners('pre.foo'));
+    }
+
+    public function testGetAllListenersSortsByPriority()
+    {
+        $listener1 = new TestEventListener();
+        $listener2 = new TestEventListener();
+        $listener3 = new TestEventListener();
+        $listener4 = new TestEventListener();
+        $listener5 = new TestEventListener();
+        $listener6 = new TestEventListener();
+
+        $this->dispatcher->addListener('pre.foo', $listener1, -10);
+        $this->dispatcher->addListener('pre.foo', $listener2);
+        $this->dispatcher->addListener('pre.foo', $listener3, 10);
+        $this->dispatcher->addListener('post.foo', $listener4, -10);
+        $this->dispatcher->addListener('post.foo', $listener5);
+        $this->dispatcher->addListener('post.foo', $listener6, 10);
+
+        $expected = [
+            'pre.foo' => [$listener3, $listener2, $listener1],
+            'post.foo' => [$listener6, $listener5, $listener4],
+        ];
+
+        $this->assertSame($expected, $this->dispatcher->getListeners());
+    }
+
+    public function testGetListenerPriority()
+    {
+        $listener1 = new TestEventListener();
+        $listener2 = new TestEventListener();
+
+        $this->dispatcher->addListener('pre.foo', $listener1, -10);
+        $this->dispatcher->addListener('pre.foo', $listener2);
+
+        $this->assertSame(-10, $this->dispatcher->getListenerPriority('pre.foo', $listener1));
+        $this->assertSame(0, $this->dispatcher->getListenerPriority('pre.foo', $listener2));
+        $this->assertNull($this->dispatcher->getListenerPriority('pre.bar', $listener2));
+        $this->assertNull($this->dispatcher->getListenerPriority('pre.foo', function () {}));
+    }
+
+    public function testDispatch()
+    {
+        $this->dispatcher->addListener('pre.foo', [$this->listener, 'preFoo']);
+        $this->dispatcher->addListener('post.foo', [$this->listener, 'postFoo']);
+        $this->dispatcher->dispatch(new Event(), self::preFoo);
+        $this->assertTrue($this->listener->preFooInvoked);
+        $this->assertFalse($this->listener->postFooInvoked);
+        $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(new Event(), 'noevent'));
+        $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(new Event(), self::preFoo));
+        $event = new Event();
+        $return = $this->dispatcher->dispatch($event, self::preFoo);
+        $this->assertSame($event, $return);
+    }
+
+    public function testDispatchContractsEvent()
+    {
+        $this->dispatcher->addListener('pre.foo', [$this->listener, 'preFoo']);
+        $this->dispatcher->addListener('post.foo', [$this->listener, 'postFoo']);
+        $this->dispatcher->dispatch(new ContractsEvent(), self::preFoo);
+        $this->assertTrue($this->listener->preFooInvoked);
+        $this->assertFalse($this->listener->postFooInvoked);
+        $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(new Event(), 'noevent'));
+        $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(new Event(), self::preFoo));
+        $event = new Event();
+        $return = $this->dispatcher->dispatch($event, self::preFoo);
+        $this->assertSame($event, $return);
+    }
+
+    public function testDispatchForClosure()
+    {
+        $invoked = 0;
+        $listener = function () use (&$invoked) {
+            ++$invoked;
+        };
+        $this->dispatcher->addListener('pre.foo', $listener);
+        $this->dispatcher->addListener('post.foo', $listener);
+        $this->dispatcher->dispatch(new Event(), self::preFoo);
+        $this->assertEquals(1, $invoked);
+    }
+
+    public function testStopEventPropagation()
+    {
+        $otherListener = new TestEventListener();
+
+        // postFoo() stops the propagation, so only one listener should
+        // be executed
+        // Manually set priority to enforce $this->listener to be called first
+        $this->dispatcher->addListener('post.foo', [$this->listener, 'postFoo'], 10);
+        $this->dispatcher->addListener('post.foo', [$otherListener, 'postFoo']);
+        $this->dispatcher->dispatch(new Event(), self::postFoo);
+        $this->assertTrue($this->listener->postFooInvoked);
+        $this->assertFalse($otherListener->postFooInvoked);
+    }
+
+    public function testDispatchByPriority()
+    {
+        $invoked = [];
+        $listener1 = function () use (&$invoked) {
+            $invoked[] = '1';
+        };
+        $listener2 = function () use (&$invoked) {
+            $invoked[] = '2';
+        };
+        $listener3 = function () use (&$invoked) {
+            $invoked[] = '3';
+        };
+        $this->dispatcher->addListener('pre.foo', $listener1, -10);
+        $this->dispatcher->addListener('pre.foo', $listener2);
+        $this->dispatcher->addListener('pre.foo', $listener3, 10);
+        $this->dispatcher->dispatch(new Event(), self::preFoo);
+        $this->assertEquals(['3', '2', '1'], $invoked);
+    }
+
+    public function testRemoveListener()
+    {
+        $this->dispatcher->addListener('pre.bar', $this->listener);
+        $this->assertTrue($this->dispatcher->hasListeners(self::preBar));
+        $this->dispatcher->removeListener('pre.bar', $this->listener);
+        $this->assertFalse($this->dispatcher->hasListeners(self::preBar));
+        $this->dispatcher->removeListener('notExists', $this->listener);
+    }
+
+    public function testAddSubscriber()
+    {
+        $eventSubscriber = new TestEventSubscriber();
+        $this->dispatcher->addSubscriber($eventSubscriber);
+        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
+        $this->assertTrue($this->dispatcher->hasListeners(self::postFoo));
+    }
+
+    public function testAddSubscriberWithPriorities()
+    {
+        $eventSubscriber = new TestEventSubscriber();
+        $this->dispatcher->addSubscriber($eventSubscriber);
+
+        $eventSubscriber = new TestEventSubscriberWithPriorities();
+        $this->dispatcher->addSubscriber($eventSubscriber);
+
+        $listeners = $this->dispatcher->getListeners('pre.foo');
+        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
+        $this->assertCount(2, $listeners);
+        $this->assertInstanceOf('Symfony\Component\EventDispatcher\Tests\TestEventSubscriberWithPriorities', $listeners[0][0]);
+    }
+
+    public function testAddSubscriberWithMultipleListeners()
+    {
+        $eventSubscriber = new TestEventSubscriberWithMultipleListeners();
+        $this->dispatcher->addSubscriber($eventSubscriber);
+
+        $listeners = $this->dispatcher->getListeners('pre.foo');
+        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
+        $this->assertCount(2, $listeners);
+        $this->assertEquals('preFoo2', $listeners[0][1]);
+    }
+
+    public function testRemoveSubscriber()
+    {
+        $eventSubscriber = new TestEventSubscriber();
+        $this->dispatcher->addSubscriber($eventSubscriber);
+        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
+        $this->assertTrue($this->dispatcher->hasListeners(self::postFoo));
+        $this->dispatcher->removeSubscriber($eventSubscriber);
+        $this->assertFalse($this->dispatcher->hasListeners(self::preFoo));
+        $this->assertFalse($this->dispatcher->hasListeners(self::postFoo));
+    }
+
+    public function testRemoveSubscriberWithPriorities()
+    {
+        $eventSubscriber = new TestEventSubscriberWithPriorities();
+        $this->dispatcher->addSubscriber($eventSubscriber);
+        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
+        $this->dispatcher->removeSubscriber($eventSubscriber);
+        $this->assertFalse($this->dispatcher->hasListeners(self::preFoo));
+    }
+
+    public function testRemoveSubscriberWithMultipleListeners()
+    {
+        $eventSubscriber = new TestEventSubscriberWithMultipleListeners();
+        $this->dispatcher->addSubscriber($eventSubscriber);
+        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
+        $this->assertCount(2, $this->dispatcher->getListeners(self::preFoo));
+        $this->dispatcher->removeSubscriber($eventSubscriber);
+        $this->assertFalse($this->dispatcher->hasListeners(self::preFoo));
+    }
+
+    public function testEventReceivesTheDispatcherInstanceAsArgument()
+    {
+        $listener = new TestWithDispatcher();
+        $this->dispatcher->addListener('test', [$listener, 'foo']);
+        $this->assertNull($listener->name);
+        $this->assertNull($listener->dispatcher);
+        $this->dispatcher->dispatch(new Event(), 'test');
+        $this->assertEquals('test', $listener->name);
+        $this->assertSame($this->dispatcher, $listener->dispatcher);
+    }
+
+    /**
+     * @see https://bugs.php.net/bug.php?id=62976
+     *
+     * This bug affects:
+     *  - The PHP 5.3 branch for versions < 5.3.18
+     *  - The PHP 5.4 branch for versions < 5.4.8
+     *  - The PHP 5.5 branch is not affected
+     */
+    public function testWorkaroundForPhpBug62976()
+    {
+        $dispatcher = $this->createEventDispatcher();
+        $dispatcher->addListener('bug.62976', new CallableClass());
+        $dispatcher->removeListener('bug.62976', function () {});
+        $this->assertTrue($dispatcher->hasListeners('bug.62976'));
+    }
+
+    public function testHasListenersWhenAddedCallbackListenerIsRemoved()
+    {
+        $listener = function () {};
+        $this->dispatcher->addListener('foo', $listener);
+        $this->dispatcher->removeListener('foo', $listener);
+        $this->assertFalse($this->dispatcher->hasListeners());
+    }
+
+    public function testGetListenersWhenAddedCallbackListenerIsRemoved()
+    {
+        $listener = function () {};
+        $this->dispatcher->addListener('foo', $listener);
+        $this->dispatcher->removeListener('foo', $listener);
+        $this->assertSame([], $this->dispatcher->getListeners());
+    }
+
+    public function testHasListenersWithoutEventsReturnsFalseAfterHasListenersWithEventHasBeenCalled()
+    {
+        $this->assertFalse($this->dispatcher->hasListeners('foo'));
+        $this->assertFalse($this->dispatcher->hasListeners());
+    }
+
+    public function testHasListenersIsLazy()
+    {
+        $called = 0;
+        $listener = [function () use (&$called) { ++$called; }, 'onFoo'];
+        $this->dispatcher->addListener('foo', $listener);
+        $this->assertTrue($this->dispatcher->hasListeners());
+        $this->assertTrue($this->dispatcher->hasListeners('foo'));
+        $this->assertSame(0, $called);
+    }
+
+    public function testDispatchLazyListener()
+    {
+        $called = 0;
+        $factory = function () use (&$called) {
+            ++$called;
+
+            return new TestWithDispatcher();
+        };
+        $this->dispatcher->addListener('foo', [$factory, 'foo']);
+        $this->assertSame(0, $called);
+        $this->dispatcher->dispatch(new Event(), 'foo');
+        $this->dispatcher->dispatch(new Event(), 'foo');
+        $this->assertSame(1, $called);
+    }
+
+    public function testRemoveFindsLazyListeners()
+    {
+        $test = new TestWithDispatcher();
+        $factory = function () use ($test) { return $test; };
+
+        $this->dispatcher->addListener('foo', [$factory, 'foo']);
+        $this->assertTrue($this->dispatcher->hasListeners('foo'));
+        $this->dispatcher->removeListener('foo', [$test, 'foo']);
+        $this->assertFalse($this->dispatcher->hasListeners('foo'));
+
+        $this->dispatcher->addListener('foo', [$test, 'foo']);
+        $this->assertTrue($this->dispatcher->hasListeners('foo'));
+        $this->dispatcher->removeListener('foo', [$factory, 'foo']);
+        $this->assertFalse($this->dispatcher->hasListeners('foo'));
+    }
+
+    public function testPriorityFindsLazyListeners()
+    {
+        $test = new TestWithDispatcher();
+        $factory = function () use ($test) { return $test; };
+
+        $this->dispatcher->addListener('foo', [$factory, 'foo'], 3);
+        $this->assertSame(3, $this->dispatcher->getListenerPriority('foo', [$test, 'foo']));
+        $this->dispatcher->removeListener('foo', [$factory, 'foo']);
+
+        $this->dispatcher->addListener('foo', [$test, 'foo'], 5);
+        $this->assertSame(5, $this->dispatcher->getListenerPriority('foo', [$factory, 'foo']));
+    }
+
+    public function testGetLazyListeners()
+    {
+        $test = new TestWithDispatcher();
+        $factory = function () use ($test) { return $test; };
+
+        $this->dispatcher->addListener('foo', [$factory, 'foo'], 3);
+        $this->assertSame([[$test, 'foo']], $this->dispatcher->getListeners('foo'));
+
+        $this->dispatcher->removeListener('foo', [$test, 'foo']);
+        $this->dispatcher->addListener('bar', [$factory, 'foo'], 3);
+        $this->assertSame(['bar' => [[$test, 'foo']]], $this->dispatcher->getListeners());
+    }
+
+    public function testMutatingWhilePropagationIsStopped()
+    {
+        $testLoaded = false;
+        $test = new TestEventListener();
+        $this->dispatcher->addListener('foo', [$test, 'postFoo']);
+        $this->dispatcher->addListener('foo', [function () use ($test, &$testLoaded) {
+            $testLoaded = true;
+
+            return $test;
+        }, 'preFoo']);
+
+        $this->dispatcher->dispatch(new Event(), 'foo');
+
+        $this->assertTrue($test->postFooInvoked);
+        $this->assertFalse($test->preFooInvoked);
+
+        $this->assertsame(0, $this->dispatcher->getListenerPriority('foo', [$test, 'preFoo']));
+
+        $test->preFoo(new Event());
+        $this->dispatcher->dispatch(new Event(), 'foo');
+
+        $this->assertTrue($testLoaded);
+    }
+}
+
+class CallableClass
+{
+    public function __invoke()
+    {
+    }
+}
+
+class TestEventListener
+{
+    public $preFooInvoked = false;
+    public $postFooInvoked = false;
+
+    /* Listener methods */
+
+    public function preFoo($e)
+    {
+        $this->preFooInvoked = true;
+    }
+
+    public function postFoo($e)
+    {
+        $this->postFooInvoked = true;
+
+        if (!$this->preFooInvoked) {
+            $e->stopPropagation();
+        }
+    }
+}
+
+class TestWithDispatcher
+{
+    public $name;
+    public $dispatcher;
+
+    public function foo($e, $name, $dispatcher)
+    {
+        $this->name = $name;
+        $this->dispatcher = $dispatcher;
+    }
+}
+
+class TestEventSubscriber implements EventSubscriberInterface
+{
+    public static function getSubscribedEvents()
+    {
+        return ['pre.foo' => 'preFoo', 'post.foo' => 'postFoo'];
+    }
+}
+
+class TestEventSubscriberWithPriorities implements EventSubscriberInterface
+{
+    public static function getSubscribedEvents()
+    {
+        return [
+            'pre.foo' => ['preFoo', 10],
+            'post.foo' => ['postFoo'],
+        ];
+    }
+}
+
+class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface
+{
+    public static function getSubscribedEvents()
+    {
+        return ['pre.foo' => [
+            ['preFoo1'],
+            ['preFoo2', 10],
+        ]];
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Tests/EventTest.php b/vendor/symfony/event-dispatcher/Tests/EventTest.php
new file mode 100644
index 0000000..ca8e945
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Tests/EventTest.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\EventDispatcher\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\Event;
+
+/**
+ * @group legacy
+ */
+class EventTest extends TestCase
+{
+    /**
+     * @var \Symfony\Component\EventDispatcher\Event
+     */
+    protected $event;
+
+    /**
+     * Sets up the fixture, for example, opens a network connection.
+     * This method is called before a test is executed.
+     */
+    protected function setUp()
+    {
+        $this->event = new Event();
+    }
+
+    /**
+     * Tears down the fixture, for example, closes a network connection.
+     * This method is called after a test is executed.
+     */
+    protected function tearDown()
+    {
+        $this->event = null;
+    }
+
+    public function testIsPropagationStopped()
+    {
+        $this->assertFalse($this->event->isPropagationStopped());
+    }
+
+    public function testStopPropagationAndIsPropagationStopped()
+    {
+        $this->event->stopPropagation();
+        $this->assertTrue($this->event->isPropagationStopped());
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php b/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php
new file mode 100644
index 0000000..e9e011a
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php
@@ -0,0 +1,136 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\GenericEvent;
+
+/**
+ * Test class for Event.
+ */
+class GenericEventTest extends TestCase
+{
+    /**
+     * @var GenericEvent
+     */
+    private $event;
+
+    private $subject;
+
+    /**
+     * Prepares the environment before running a test.
+     */
+    protected function setUp()
+    {
+        $this->subject = new \stdClass();
+        $this->event = new GenericEvent($this->subject, ['name' => 'Event']);
+    }
+
+    /**
+     * Cleans up the environment after running a test.
+     */
+    protected function tearDown()
+    {
+        $this->subject = null;
+        $this->event = null;
+    }
+
+    public function testConstruct()
+    {
+        $this->assertEquals($this->event, new GenericEvent($this->subject, ['name' => 'Event']));
+    }
+
+    /**
+     * Tests Event->getArgs().
+     */
+    public function testGetArguments()
+    {
+        // test getting all
+        $this->assertSame(['name' => 'Event'], $this->event->getArguments());
+    }
+
+    public function testSetArguments()
+    {
+        $result = $this->event->setArguments(['foo' => 'bar']);
+        $this->assertAttributeSame(['foo' => 'bar'], 'arguments', $this->event);
+        $this->assertSame($this->event, $result);
+    }
+
+    public function testSetArgument()
+    {
+        $result = $this->event->setArgument('foo2', 'bar2');
+        $this->assertAttributeSame(['name' => 'Event', 'foo2' => 'bar2'], 'arguments', $this->event);
+        $this->assertEquals($this->event, $result);
+    }
+
+    public function testGetArgument()
+    {
+        // test getting key
+        $this->assertEquals('Event', $this->event->getArgument('name'));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testGetArgException()
+    {
+        $this->event->getArgument('nameNotExist');
+    }
+
+    public function testOffsetGet()
+    {
+        // test getting key
+        $this->assertEquals('Event', $this->event['name']);
+
+        // test getting invalid arg
+        $this->expectException('InvalidArgumentException');
+        $this->assertFalse($this->event['nameNotExist']);
+    }
+
+    public function testOffsetSet()
+    {
+        $this->event['foo2'] = 'bar2';
+        $this->assertAttributeSame(['name' => 'Event', 'foo2' => 'bar2'], 'arguments', $this->event);
+    }
+
+    public function testOffsetUnset()
+    {
+        unset($this->event['name']);
+        $this->assertAttributeSame([], 'arguments', $this->event);
+    }
+
+    public function testOffsetIsset()
+    {
+        $this->assertArrayHasKey('name', $this->event);
+        $this->assertArrayNotHasKey('nameNotExist', $this->event);
+    }
+
+    public function testHasArgument()
+    {
+        $this->assertTrue($this->event->hasArgument('name'));
+        $this->assertFalse($this->event->hasArgument('nameNotExist'));
+    }
+
+    public function testGetSubject()
+    {
+        $this->assertSame($this->subject, $this->event->getSubject());
+    }
+
+    public function testHasIterator()
+    {
+        $data = [];
+        foreach ($this->event as $key => $value) {
+            $data[$key] = $value;
+        }
+        $this->assertEquals(['name' => 'Event'], $data);
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php
new file mode 100644
index 0000000..c896fac
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php
@@ -0,0 +1,106 @@
+<?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\EventDispatcher\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\EventDispatcher\ImmutableEventDispatcher;
+
+/**
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
+class ImmutableEventDispatcherTest extends TestCase
+{
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    private $innerDispatcher;
+
+    /**
+     * @var ImmutableEventDispatcher
+     */
+    private $dispatcher;
+
+    protected function setUp()
+    {
+        $this->innerDispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
+        $this->dispatcher = new ImmutableEventDispatcher($this->innerDispatcher);
+    }
+
+    public function testDispatchDelegates()
+    {
+        $event = new Event();
+
+        $this->innerDispatcher->expects($this->once())
+            ->method('dispatch')
+            ->with($event, 'event')
+            ->will($this->returnValue('result'));
+
+        $this->assertSame('result', $this->dispatcher->dispatch($event, 'event'));
+    }
+
+    public function testGetListenersDelegates()
+    {
+        $this->innerDispatcher->expects($this->once())
+            ->method('getListeners')
+            ->with('event')
+            ->will($this->returnValue('result'));
+
+        $this->assertSame('result', $this->dispatcher->getListeners('event'));
+    }
+
+    public function testHasListenersDelegates()
+    {
+        $this->innerDispatcher->expects($this->once())
+            ->method('hasListeners')
+            ->with('event')
+            ->will($this->returnValue('result'));
+
+        $this->assertSame('result', $this->dispatcher->hasListeners('event'));
+    }
+
+    /**
+     * @expectedException \BadMethodCallException
+     */
+    public function testAddListenerDisallowed()
+    {
+        $this->dispatcher->addListener('event', function () { return 'foo'; });
+    }
+
+    /**
+     * @expectedException \BadMethodCallException
+     */
+    public function testAddSubscriberDisallowed()
+    {
+        $subscriber = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventSubscriberInterface')->getMock();
+
+        $this->dispatcher->addSubscriber($subscriber);
+    }
+
+    /**
+     * @expectedException \BadMethodCallException
+     */
+    public function testRemoveListenerDisallowed()
+    {
+        $this->dispatcher->removeListener('event', function () { return 'foo'; });
+    }
+
+    /**
+     * @expectedException \BadMethodCallException
+     */
+    public function testRemoveSubscriberDisallowed()
+    {
+        $subscriber = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventSubscriberInterface')->getMock();
+
+        $this->dispatcher->removeSubscriber($subscriber);
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/Tests/LegacyEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/LegacyEventDispatcherTest.php
new file mode 100644
index 0000000..49aa2f9
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/Tests/LegacyEventDispatcherTest.php
@@ -0,0 +1,35 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher\Tests;
+
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy;
+
+/**
+ * @group legacy
+ */
+class LegacyEventDispatcherTest extends EventDispatcherTest
+{
+    protected function createEventDispatcher()
+    {
+        return LegacyEventDispatcherProxy::decorate(new TestLegacyEventDispatcher());
+    }
+}
+
+class TestLegacyEventDispatcher extends EventDispatcher
+{
+    public function dispatch($eventName, Event $event = null)
+    {
+        return parent::dispatch($event, $eventName);
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/composer.json b/vendor/symfony/event-dispatcher/composer.json
new file mode 100644
index 0000000..8449c47
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/composer.json
@@ -0,0 +1,54 @@
+{
+    "name": "symfony/event-dispatcher",
+    "type": "library",
+    "description": "Symfony EventDispatcher Component",
+    "keywords": [],
+    "homepage": "https://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Fabien Potencier",
+            "email": "fabien@symfony.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "https://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": "^7.1.3",
+        "symfony/event-dispatcher-contracts": "^1.1"
+    },
+    "require-dev": {
+        "symfony/dependency-injection": "~3.4|~4.0",
+        "symfony/expression-language": "~3.4|~4.0",
+        "symfony/config": "~3.4|~4.0",
+        "symfony/http-foundation": "^3.4|^4.0",
+        "symfony/service-contracts": "^1.1",
+        "symfony/stopwatch": "~3.4|~4.0",
+        "psr/log": "~1.0"
+    },
+    "conflict": {
+        "symfony/dependency-injection": "<3.4"
+    },
+    "provide": {
+        "psr/event-dispatcher-implementation": "1.0",
+        "symfony/event-dispatcher-implementation": "1.1"
+    },
+    "suggest": {
+        "symfony/dependency-injection": "",
+        "symfony/http-kernel": ""
+    },
+    "autoload": {
+        "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" },
+        "exclude-from-classmap": [
+            "/Tests/"
+        ]
+    },
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "4.3-dev"
+        }
+    }
+}
diff --git a/vendor/symfony/event-dispatcher/phpunit.xml.dist b/vendor/symfony/event-dispatcher/phpunit.xml.dist
new file mode 100644
index 0000000..f2eb169
--- /dev/null
+++ b/vendor/symfony/event-dispatcher/phpunit.xml.dist
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/5.2/phpunit.xsd"
+         backupGlobals="false"
+         colors="true"
+         bootstrap="vendor/autoload.php"
+         failOnRisky="true"
+         failOnWarning="true"
+>
+    <php>
+        <ini name="error_reporting" value="-1" />
+    </php>
+
+    <testsuites>
+        <testsuite name="Symfony EventDispatcher Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./Resources</directory>
+                <directory>./Tests</directory>
+                <directory>./vendor</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>