From fc9401f04a3aca5abb22f87ebc210de8afe11d32 Mon Sep 17 00:00:00 2001
From: marvin-borner@live.com
Date: Tue, 10 Apr 2018 21:50:16 +0200
Subject: Initial Commit

---
 assets/php/vendor/evenement/evenement/.gitignore   |   2 +
 assets/php/vendor/evenement/evenement/.travis.yml  |  24 ++
 assets/php/vendor/evenement/evenement/CHANGELOG.md |  35 ++
 assets/php/vendor/evenement/evenement/LICENSE      |  19 +
 assets/php/vendor/evenement/evenement/README.md    |  83 ++++
 .../php/vendor/evenement/evenement/composer.json   |  29 ++
 .../php/vendor/evenement/evenement/doc/00-intro.md |  28 ++
 .../php/vendor/evenement/evenement/doc/01-api.md   |  91 +++++
 .../evenement/evenement/doc/02-plugin-system.md    | 155 ++++++++
 .../examples/benchmark-emit-no-arguments.php       |  28 ++
 .../evenement/examples/benchmark-emit-once.php     |  30 ++
 .../examples/benchmark-emit-one-argument.php       |  28 ++
 .../evenement/examples/benchmark-emit.php          |  28 ++
 .../examples/benchmark-remove-listener-once.php    |  39 ++
 .../vendor/evenement/evenement/phpunit.xml.dist    |  24 ++
 .../evenement/src/Evenement/EventEmitter.php       |  17 +
 .../src/Evenement/EventEmitterInterface.php        |  22 ++
 .../evenement/src/Evenement/EventEmitterTrait.php  | 135 +++++++
 .../tests/Evenement/Tests/EventEmitterTest.php     | 438 +++++++++++++++++++++
 .../evenement/tests/Evenement/Tests/Listener.php   |  51 +++
 .../evenement/tests/Evenement/Tests/functions.php  |  17 +
 21 files changed, 1323 insertions(+)
 create mode 100644 assets/php/vendor/evenement/evenement/.gitignore
 create mode 100644 assets/php/vendor/evenement/evenement/.travis.yml
 create mode 100644 assets/php/vendor/evenement/evenement/CHANGELOG.md
 create mode 100644 assets/php/vendor/evenement/evenement/LICENSE
 create mode 100644 assets/php/vendor/evenement/evenement/README.md
 create mode 100644 assets/php/vendor/evenement/evenement/composer.json
 create mode 100644 assets/php/vendor/evenement/evenement/doc/00-intro.md
 create mode 100644 assets/php/vendor/evenement/evenement/doc/01-api.md
 create mode 100644 assets/php/vendor/evenement/evenement/doc/02-plugin-system.md
 create mode 100644 assets/php/vendor/evenement/evenement/examples/benchmark-emit-no-arguments.php
 create mode 100644 assets/php/vendor/evenement/evenement/examples/benchmark-emit-once.php
 create mode 100644 assets/php/vendor/evenement/evenement/examples/benchmark-emit-one-argument.php
 create mode 100644 assets/php/vendor/evenement/evenement/examples/benchmark-emit.php
 create mode 100644 assets/php/vendor/evenement/evenement/examples/benchmark-remove-listener-once.php
 create mode 100644 assets/php/vendor/evenement/evenement/phpunit.xml.dist
 create mode 100644 assets/php/vendor/evenement/evenement/src/Evenement/EventEmitter.php
 create mode 100644 assets/php/vendor/evenement/evenement/src/Evenement/EventEmitterInterface.php
 create mode 100644 assets/php/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php
 create mode 100644 assets/php/vendor/evenement/evenement/tests/Evenement/Tests/EventEmitterTest.php
 create mode 100644 assets/php/vendor/evenement/evenement/tests/Evenement/Tests/Listener.php
 create mode 100644 assets/php/vendor/evenement/evenement/tests/Evenement/Tests/functions.php

(limited to 'assets/php/vendor/evenement')

diff --git a/assets/php/vendor/evenement/evenement/.gitignore b/assets/php/vendor/evenement/evenement/.gitignore
new file mode 100644
index 0000000..987e2a2
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/.gitignore
@@ -0,0 +1,2 @@
+composer.lock
+vendor
diff --git a/assets/php/vendor/evenement/evenement/.travis.yml b/assets/php/vendor/evenement/evenement/.travis.yml
new file mode 100644
index 0000000..65ba0ce
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/.travis.yml
@@ -0,0 +1,24 @@
+language: php
+
+php:
+  - 7.0
+  - 7.1
+  - hhvm
+  - nightly
+
+matrix:
+    allow_failures:
+        - php: hhvm
+        - php: nightly
+
+before_script:
+  - wget http://getcomposer.org/composer.phar
+  - php composer.phar install
+
+script:
+  - ./vendor/bin/phpunit --coverage-text
+  - php -n examples/benchmark-emit-no-arguments.php
+  - php -n examples/benchmark-emit-one-argument.php
+  - php -n examples/benchmark-emit.php
+  - php -n examples/benchmark-emit-once.php
+  - php -n examples/benchmark-remove-listener-once.php
diff --git a/assets/php/vendor/evenement/evenement/CHANGELOG.md b/assets/php/vendor/evenement/evenement/CHANGELOG.md
new file mode 100644
index 0000000..568f229
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/CHANGELOG.md
@@ -0,0 +1,35 @@
+CHANGELOG
+=========
+
+
+* v3.0.1 (2017-07-23)
+
+  * Resolved regression introduced in once listeners in v3.0.0 [#49](https://github.com/igorw/evenement/pull/49)
+
+* v3.0.0 (2017-07-23)
+
+  * Passing null as event name throw exception [#46](https://github.com/igorw/evenement/pull/46), and [#47](https://github.com/igorw/evenement/pull/47)
+  * Performance improvements [#39](https://github.com/igorw/evenement/pull/39), and [#45](https://github.com/igorw/evenement/pull/45)
+  * Remove once listeners [#44](https://github.com/igorw/evenement/pull/44), [#45](https://github.com/igorw/evenement/pull/45)
+
+* v2.1.0 (2017-07-17)
+
+  * Chaining for "on" method [#30](https://github.com/igorw/evenement/pull/30)
+  * Unit tests (on Travis) improvements [#33](https://github.com/igorw/evenement/pull/33), [#36](https://github.com/igorw/evenement/pull/36), and [#37](https://github.com/igorw/evenement/pull/37)
+  * Benchmarks added [#35](https://github.com/igorw/evenement/pull/35), and [#40](https://github.com/igorw/evenement/pull/40)
+  * Minor performance improvements [#42](https://github.com/igorw/evenement/pull/42), and [#38](https://github.com/igorw/evenement/pull/38)
+
+* v2.0.0 (2012-11-02)
+
+  * Require PHP >=5.4.0
+  * Added EventEmitterTrait
+  * Removed EventEmitter2
+
+* v1.1.0 (2017-07-17)
+
+  * Chaining for "on" method [#29](https://github.com/igorw/evenement/pull/29)
+  * Minor performance improvements [#43](https://github.com/igorw/evenement/pull/43)
+
+* v1.0.0 (2012-05-30)
+
+  * Inital stable release
diff --git a/assets/php/vendor/evenement/evenement/LICENSE b/assets/php/vendor/evenement/evenement/LICENSE
new file mode 100644
index 0000000..d9a37d0
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2011 Igor Wiedler
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/assets/php/vendor/evenement/evenement/README.md b/assets/php/vendor/evenement/evenement/README.md
new file mode 100644
index 0000000..9443011
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/README.md
@@ -0,0 +1,83 @@
+# Événement
+
+Événement is a very simple event dispatching library for PHP.
+
+It has the same design goals as [Silex](http://silex-project.org) and
+[Pimple](http://pimple-project.org), to empower the user while staying concise
+and simple.
+
+It is very strongly inspired by the EventEmitter API found in
+[node.js](http://nodejs.org).
+
+[![Build Status](https://secure.travis-ci.org/igorw/evenement.png?branch=master)](http://travis-ci.org/igorw/evenement)
+
+## Fetch
+
+The recommended way to install Événement is [through composer](http://getcomposer.org).
+
+Just create a composer.json file for your project:
+
+```JSON
+{
+    "require": {
+        "evenement/evenement": "^3.0 || ^2.0"
+    }
+}
+```
+
+**Note:** The `3.x` version of Événement requires PHP 7 and the `2.x` version requires PHP 5.4. If you are
+using PHP 5.3, please use the `1.x` version:
+
+```JSON
+{
+    "require": {
+        "evenement/evenement": "^1.0"
+    }
+}
+```
+
+And run these two commands to install it:
+
+    $ curl -s http://getcomposer.org/installer | php
+    $ php composer.phar install
+
+Now you can add the autoloader, and you will have access to the library:
+
+```php
+<?php
+require 'vendor/autoload.php';
+```
+
+## Usage
+
+### Creating an Emitter
+
+```php
+<?php
+$emitter = new Evenement\EventEmitter();
+```
+
+### Adding Listeners
+
+```php
+<?php
+$emitter->on('user.created', function (User $user) use ($logger) {
+    $logger->log(sprintf("User '%s' was created.", $user->getLogin()));
+});
+```
+
+### Emitting Events
+
+```php
+<?php
+$emitter->emit('user.created', [$user]);
+```
+
+Tests
+-----
+
+    $ ./vendor/bin/phpunit
+
+License
+-------
+MIT, see LICENSE.
diff --git a/assets/php/vendor/evenement/evenement/composer.json b/assets/php/vendor/evenement/evenement/composer.json
new file mode 100644
index 0000000..cbb4827
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/composer.json
@@ -0,0 +1,29 @@
+{
+    "name": "evenement/evenement",
+    "description": "Événement is a very simple event dispatching library for PHP",
+    "keywords": ["event-dispatcher", "event-emitter"],
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Igor Wiedler",
+            "email": "igor@wiedler.ch"
+        }
+    ],
+    "require": {
+        "php": ">=7.0"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "^6.0"
+    },
+    "autoload": {
+        "psr-0": {
+            "Evenement": "src"
+        }
+    },
+    "autoload-dev": {
+        "psr-0": {
+            "Evenement": "tests"
+        },
+        "files": ["tests/Evenement/Tests/functions.php"]
+    }
+}
diff --git a/assets/php/vendor/evenement/evenement/doc/00-intro.md b/assets/php/vendor/evenement/evenement/doc/00-intro.md
new file mode 100644
index 0000000..6c28a2a
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/doc/00-intro.md
@@ -0,0 +1,28 @@
+# Introduction
+
+Événement is is French and means "event". The événement library aims to
+provide a simple way of subscribing to events and notifying those subscribers
+whenever an event occurs.
+
+The API that it exposes is almost a direct port of the EventEmitter API found
+in node.js. It also includes an "EventEmitter". There are some minor
+differences however.
+
+The EventEmitter is an implementation of the publish-subscribe pattern, which
+is a generalized version of the observer pattern. The observer pattern
+specifies an observable subject, which observers can register themselves to.
+Once something interesting happens, the subject notifies its observers.
+
+Pub/sub takes the same idea but encapsulates the observation logic inside a
+separate object which manages all of its subscribers or listeners. Subscribers
+are bound to an event name, and will only receive notifications of the events
+they subscribed to.
+
+**TLDR: What does evenement do, in short? It provides a mapping from event
+names to a list of listener functions and triggers each listener for a given
+event when it is emitted.**
+
+Why do we do this, you ask? To achieve decoupling.
+
+It allows you to design a system where the core will emit events, and modules
+are able to subscribe to these events. And respond to them.
diff --git a/assets/php/vendor/evenement/evenement/doc/01-api.md b/assets/php/vendor/evenement/evenement/doc/01-api.md
new file mode 100644
index 0000000..17ba333
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/doc/01-api.md
@@ -0,0 +1,91 @@
+# API
+
+The API that événement exposes is defined by the
+`Evenement\EventEmitterInterface`. The interface is useful if you want to
+define an interface that extends the emitter and implicitly defines certain
+events to be emitted, or if you want to type hint an `EventEmitter` to be
+passed to a method without coupling to the specific implementation.
+
+## on($event, callable $listener)
+
+Allows you to subscribe to an event.
+
+Example:
+
+```php
+$emitter->on('user.created', function (User $user) use ($logger) {
+    $logger->log(sprintf("User '%s' was created.", $user->getLogin()));
+});
+```
+
+Since the listener can be any callable, you could also use an instance method
+instead of the anonymous function:
+
+```php
+$loggerSubscriber = new LoggerSubscriber($logger);
+$emitter->on('user.created', array($loggerSubscriber, 'onUserCreated'));
+```
+
+This has the benefit that listener does not even need to know that the emitter
+exists.
+
+You can also accept more than one parameter for the listener:
+
+```php
+$emitter->on('numbers_added', function ($result, $a, $b) {});
+```
+
+## once($event, callable $listener)
+
+Convenience method that adds a listener which is guaranteed to only be called
+once.
+
+Example:
+
+```php
+$conn->once('connected', function () use ($conn, $data) {
+    $conn->send($data);
+});
+```
+
+## emit($event, array $arguments = [])
+
+Emit an event, which will call all listeners.
+
+Example:
+
+```php
+$conn->emit('data', [$data]);
+```
+
+The second argument to emit is an array of listener arguments. This is how you
+specify more args:
+
+```php
+$result = $a + $b;
+$emitter->emit('numbers_added', [$result, $a, $b]);
+```
+
+## listeners($event)
+
+Allows you to inspect the listeners attached to an event. Particularly useful
+to check if there are any listeners at all.
+
+Example:
+
+```php
+$e = new \RuntimeException('Everything is broken!');
+if (0 === count($emitter->listeners('error'))) {
+    throw $e;
+}
+```
+
+## removeListener($event, callable $listener)
+
+Remove a specific listener for a specific event.
+
+## removeAllListeners($event = null)
+
+Remove all listeners for a specific event or all listeners all together. This
+is useful for long-running processes, where you want to remove listeners in
+order to allow them to get garbage collected.
diff --git a/assets/php/vendor/evenement/evenement/doc/02-plugin-system.md b/assets/php/vendor/evenement/evenement/doc/02-plugin-system.md
new file mode 100644
index 0000000..6a08371
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/doc/02-plugin-system.md
@@ -0,0 +1,155 @@
+# Example: Plugin system
+
+In this example I will show you how to create a generic plugin system with
+événement where plugins can alter the behaviour of the app. The app is a blog.
+Boring, I know. By using the EventEmitter it will be easy to extend this blog
+with additional functionality without modifying the core system.
+
+The blog is quite basic. Users are able to create blog posts when they log in.
+The users are stored in a static config file, so there is no sign up process.
+Once logged in they get a "new post" link which gives them a form where they
+can create a new blog post with plain HTML. That will store the post in a
+document database. The index lists all blog post titles by date descending.
+Clicking on the post title will take you to the full post.
+
+## Plugin structure
+
+The goal of the plugin system is to allow features to be added to the blog
+without modifying any core files of the blog.
+
+The plugins are managed through a config file, `plugins.json`. This JSON file
+contains a JSON-encoded list of class-names for plugin classes. This allows
+you to enable and disable plugins in a central location. The initial
+`plugins.json` is just an empty array:
+```json
+[]
+```
+
+A plugin class must implement the `PluginInterface`:
+```php
+interface PluginInterface
+{
+    function attachEvents(EventEmitterInterface $emitter);
+}
+```
+
+The `attachEvents` method allows the plugin to attach any events to the
+emitter. For example:
+```php
+class FooPlugin implements PluginInterface
+{
+    public function attachEvents(EventEmitterInterface $emitter)
+    {
+        $emitter->on('foo', function () {
+            echo 'bar!';
+        });
+    }
+}
+```
+
+The blog system creates an emitter instance and loads the plugins:
+```php
+$emitter = new EventEmitter();
+
+$pluginClasses = json_decode(file_get_contents('plugins.json'), true);
+foreach ($pluginClasses as $pluginClass) {
+    $plugin = new $pluginClass();
+    $pluginClass->attachEvents($emitter);
+}
+```
+
+This is the base system. There are no plugins yet, and there are no events yet
+either. That's because I don't know which extension points will be needed. I
+will add them on demand.
+
+## Feature: Markdown
+
+Writing blog posts in HTML sucks! Wouldn't it be great if I could write them
+in a nice format such as markdown, and have that be converted to HTML for me?
+
+This feature will need two extension points. I need to be able to mark posts
+as markdown, and I need to be able to hook into the rendering of the post body
+and convert it from markdown to HTML. So the blog needs two new events:
+`post.create` and `post.render`.
+
+In the code that creates the post, I'll insert the `post.create` event:
+```php
+class PostEvent
+{
+    public $post;
+
+    public function __construct(array $post)
+    {
+        $this->post = $post;
+    }
+}
+
+$post = createPostFromRequest($_POST);
+
+$event = new PostEvent($post);
+$emitter->emit('post.create', [$event]);
+$post = $event->post;
+
+$db->save('post', $post);
+```
+
+This shows that you can wrap a value in an event object to make it mutable,
+allowing listeners to change it.
+
+The same thing for the `post.render` event:
+```php
+public function renderPostBody(array $post)
+{
+    $emitter = $this->emitter;
+
+    $event = new PostEvent($post);
+    $emitter->emit('post.render', [$event]);
+    $post = $event->post;
+
+    return $post['body'];
+}
+
+<h1><?= $post['title'] %></h1>
+<p><?= renderPostBody($post) %></p>
+```
+
+Ok, the events are in place. It's time to create the first plugin, woohoo! I
+will call this the `MarkdownPlugin`, so here's `plugins.json`:
+```json
+[
+    "MarkdownPlugin"
+]
+```
+
+The `MarkdownPlugin` class will be autoloaded, so I don't have to worry about
+including any files. I just have to worry about implementing the plugin class.
+The `markdown` function represents a markdown to HTML converter.
+```php
+class MarkdownPlugin implements PluginInterface
+{
+    public function attachEvents(EventEmitterInterface $emitter)
+    {
+        $emitter->on('post.create', function (PostEvent $event) {
+            $event->post['format'] = 'markdown';
+        });
+
+        $emitter->on('post.render', function (PostEvent $event) {
+            if (isset($event->post['format']) && 'markdown' === $event->post['format']) {
+                $event->post['body'] = markdown($event->post['body']);
+            }
+        });
+    }
+}
+```
+
+There you go, the blog now renders posts as markdown. But all of the previous
+posts before the addition of the markdown plugin are still rendered correctly
+as raw HTML.
+
+## Feature: Comments
+
+TODO
+
+## Feature: Comment spam control
+
+TODO
diff --git a/assets/php/vendor/evenement/evenement/examples/benchmark-emit-no-arguments.php b/assets/php/vendor/evenement/evenement/examples/benchmark-emit-no-arguments.php
new file mode 100644
index 0000000..53d7f4b
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/examples/benchmark-emit-no-arguments.php
@@ -0,0 +1,28 @@
+<?php declare(strict_types=1);
+
+/*
+ * This file is part of Evenement.
+ *
+ * (c) Igor Wiedler <igor@wiedler.ch>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+const ITERATIONS = 10000000;
+
+use Evenement\EventEmitter;
+
+require __DIR__.'/../vendor/autoload.php';
+
+$emitter = new EventEmitter();
+
+$emitter->on('event', function () {});
+
+$start = microtime(true);
+for ($i = 0; $i < ITERATIONS; $i++) {
+    $emitter->emit('event');
+}
+$time = microtime(true) - $start;
+
+echo 'Emitting ', number_format(ITERATIONS), ' events took: ', number_format($time, 2), 's', PHP_EOL;
diff --git a/assets/php/vendor/evenement/evenement/examples/benchmark-emit-once.php b/assets/php/vendor/evenement/evenement/examples/benchmark-emit-once.php
new file mode 100644
index 0000000..74f4d17
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/examples/benchmark-emit-once.php
@@ -0,0 +1,30 @@
+<?php declare(strict_types=1);
+
+/*
+ * This file is part of Evenement.
+ *
+ * (c) Igor Wiedler <igor@wiedler.ch>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+ini_set('memory_limit', '512M');
+
+const ITERATIONS = 100000;
+
+use Evenement\EventEmitter;
+
+require __DIR__.'/../vendor/autoload.php';
+
+$emitter = new EventEmitter();
+
+for ($i = 0; $i < ITERATIONS; $i++) {
+    $emitter->once('event', function ($a, $b, $c) {});
+}
+
+$start = microtime(true);
+$emitter->emit('event', [1, 2, 3]);
+$time = microtime(true) - $start;
+
+echo 'Emitting one event to ', number_format(ITERATIONS), ' once listeners took: ', number_format($time, 2), 's', PHP_EOL;
diff --git a/assets/php/vendor/evenement/evenement/examples/benchmark-emit-one-argument.php b/assets/php/vendor/evenement/evenement/examples/benchmark-emit-one-argument.php
new file mode 100644
index 0000000..39fc4ba
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/examples/benchmark-emit-one-argument.php
@@ -0,0 +1,28 @@
+<?php declare(strict_types=1);
+
+/*
+ * This file is part of Evenement.
+ *
+ * (c) Igor Wiedler <igor@wiedler.ch>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+const ITERATIONS = 10000000;
+
+use Evenement\EventEmitter;
+
+require __DIR__.'/../vendor/autoload.php';
+
+$emitter = new EventEmitter();
+
+$emitter->on('event', function ($a) {});
+
+$start = microtime(true);
+for ($i = 0; $i < ITERATIONS; $i++) {
+    $emitter->emit('event', [1]);
+}
+$time = microtime(true) - $start;
+
+echo 'Emitting ', number_format(ITERATIONS), ' events took: ', number_format($time, 2), 's', PHP_EOL;
diff --git a/assets/php/vendor/evenement/evenement/examples/benchmark-emit.php b/assets/php/vendor/evenement/evenement/examples/benchmark-emit.php
new file mode 100644
index 0000000..3ab639e
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/examples/benchmark-emit.php
@@ -0,0 +1,28 @@
+<?php declare(strict_types=1);
+
+/*
+ * This file is part of Evenement.
+ *
+ * (c) Igor Wiedler <igor@wiedler.ch>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+const ITERATIONS = 10000000;
+
+use Evenement\EventEmitter;
+
+require __DIR__.'/../vendor/autoload.php';
+
+$emitter = new EventEmitter();
+
+$emitter->on('event', function ($a, $b, $c) {});
+
+$start = microtime(true);
+for ($i = 0; $i < ITERATIONS; $i++) {
+    $emitter->emit('event', [1, 2, 3]);
+}
+$time = microtime(true) - $start;
+
+echo 'Emitting ', number_format(ITERATIONS), ' events took: ', number_format($time, 2), 's', PHP_EOL;
diff --git a/assets/php/vendor/evenement/evenement/examples/benchmark-remove-listener-once.php b/assets/php/vendor/evenement/evenement/examples/benchmark-remove-listener-once.php
new file mode 100644
index 0000000..414be3b
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/examples/benchmark-remove-listener-once.php
@@ -0,0 +1,39 @@
+<?php declare(strict_types=1);
+
+/*
+ * This file is part of Evenement.
+ *
+ * (c) Igor Wiedler <igor@wiedler.ch>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+ini_set('memory_limit', '512M');
+
+const ITERATIONS = 100000;
+
+use Evenement\EventEmitter;
+
+require __DIR__.'/../vendor/autoload.php';
+
+$emitter = new EventEmitter();
+
+$listeners = [];
+for ($i = 0; $i < ITERATIONS; $i++) {
+    $listeners[] = function ($a, $b, $c) {};
+}
+
+$start = microtime(true);
+foreach ($listeners as $listener) {
+    $emitter->once('event', $listener);
+}
+$time = microtime(true) - $start;
+echo 'Adding ', number_format(ITERATIONS), ' once listeners took: ', number_format($time, 2), 's', PHP_EOL;
+
+$start = microtime(true);
+foreach ($listeners as $listener) {
+    $emitter->removeListener('event', $listener);
+}
+$time = microtime(true) - $start;
+echo 'Removing ', number_format(ITERATIONS), ' once listeners took: ', number_format($time, 2), 's', PHP_EOL;
diff --git a/assets/php/vendor/evenement/evenement/phpunit.xml.dist b/assets/php/vendor/evenement/evenement/phpunit.xml.dist
new file mode 100644
index 0000000..70bc693
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/phpunit.xml.dist
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false"
+>
+    <testsuites>
+        <testsuite name="Evenement Test Suite">
+            <directory>./tests/Evenement/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./src/</directory>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/assets/php/vendor/evenement/evenement/src/Evenement/EventEmitter.php b/assets/php/vendor/evenement/evenement/src/Evenement/EventEmitter.php
new file mode 100644
index 0000000..db189b9
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/src/Evenement/EventEmitter.php
@@ -0,0 +1,17 @@
+<?php declare(strict_types=1);
+
+/*
+ * This file is part of Evenement.
+ *
+ * (c) Igor Wiedler <igor@wiedler.ch>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Evenement;
+
+class EventEmitter implements EventEmitterInterface
+{
+    use EventEmitterTrait;
+}
diff --git a/assets/php/vendor/evenement/evenement/src/Evenement/EventEmitterInterface.php b/assets/php/vendor/evenement/evenement/src/Evenement/EventEmitterInterface.php
new file mode 100644
index 0000000..310631a
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/src/Evenement/EventEmitterInterface.php
@@ -0,0 +1,22 @@
+<?php declare(strict_types=1);
+
+/*
+ * This file is part of Evenement.
+ *
+ * (c) Igor Wiedler <igor@wiedler.ch>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Evenement;
+
+interface EventEmitterInterface
+{
+    public function on($event, callable $listener);
+    public function once($event, callable $listener);
+    public function removeListener($event, callable $listener);
+    public function removeAllListeners($event = null);
+    public function listeners($event = null);
+    public function emit($event, array $arguments = []);
+}
diff --git a/assets/php/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php b/assets/php/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php
new file mode 100644
index 0000000..a78e65c
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php
@@ -0,0 +1,135 @@
+<?php declare(strict_types=1);
+
+/*
+ * This file is part of Evenement.
+ *
+ * (c) Igor Wiedler <igor@wiedler.ch>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Evenement;
+
+use InvalidArgumentException;
+
+trait EventEmitterTrait
+{
+    protected $listeners = [];
+    protected $onceListeners = [];
+
+    public function on($event, callable $listener)
+    {
+        if ($event === null) {
+            throw new InvalidArgumentException('event name must not be null');
+        }
+
+        if (!isset($this->listeners[$event])) {
+            $this->listeners[$event] = [];
+        }
+
+        $this->listeners[$event][] = $listener;
+
+        return $this;
+    }
+
+    public function once($event, callable $listener)
+    {
+        if ($event === null) {
+            throw new InvalidArgumentException('event name must not be null');
+        }
+
+        if (!isset($this->onceListeners[$event])) {
+            $this->onceListeners[$event] = [];
+        }
+
+        $this->onceListeners[$event][] = $listener;
+
+        return $this;
+    }
+
+    public function removeListener($event, callable $listener)
+    {
+        if ($event === null) {
+            throw new InvalidArgumentException('event name must not be null');
+        }
+
+        if (isset($this->listeners[$event])) {
+            $index = \array_search($listener, $this->listeners[$event], true);
+            if (false !== $index) {
+                unset($this->listeners[$event][$index]);
+                if (\count($this->listeners[$event]) === 0) {
+                    unset($this->listeners[$event]);
+                }
+            }
+        }
+
+        if (isset($this->onceListeners[$event])) {
+            $index = \array_search($listener, $this->onceListeners[$event], true);
+            if (false !== $index) {
+                unset($this->onceListeners[$event][$index]);
+                if (\count($this->onceListeners[$event]) === 0) {
+                    unset($this->onceListeners[$event]);
+                }
+            }
+        }
+    }
+
+    public function removeAllListeners($event = null)
+    {
+        if ($event !== null) {
+            unset($this->listeners[$event]);
+        } else {
+            $this->listeners = [];
+        }
+
+        if ($event !== null) {
+            unset($this->onceListeners[$event]);
+        } else {
+            $this->onceListeners = [];
+        }
+    }
+
+    public function listeners($event = null): array
+    {
+        if ($event === null) {
+            $events = [];
+            $eventNames = \array_unique(
+                \array_merge(\array_keys($this->listeners), \array_keys($this->onceListeners))
+            );
+            foreach ($eventNames as $eventName) {
+                $events[$eventName] = \array_merge(
+                    isset($this->listeners[$eventName]) ? $this->listeners[$eventName] : [],
+                    isset($this->onceListeners[$eventName]) ? $this->onceListeners[$eventName] : []
+                );
+            }
+            return $events;
+        }
+
+        return \array_merge(
+            isset($this->listeners[$event]) ? $this->listeners[$event] : [],
+            isset($this->onceListeners[$event]) ? $this->onceListeners[$event] : []
+        );
+    }
+
+    public function emit($event, array $arguments = [])
+    {
+        if ($event === null) {
+            throw new InvalidArgumentException('event name must not be null');
+        }
+
+        if (isset($this->listeners[$event])) {
+            foreach ($this->listeners[$event] as $listener) {
+                $listener(...$arguments);
+            }
+        }
+
+        if (isset($this->onceListeners[$event])) {
+            $listeners = $this->onceListeners[$event];
+            unset($this->onceListeners[$event]);
+            foreach ($listeners as $listener) {
+                $listener(...$arguments);
+            }
+        }
+    }
+}
diff --git a/assets/php/vendor/evenement/evenement/tests/Evenement/Tests/EventEmitterTest.php b/assets/php/vendor/evenement/evenement/tests/Evenement/Tests/EventEmitterTest.php
new file mode 100644
index 0000000..28f3011
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/tests/Evenement/Tests/EventEmitterTest.php
@@ -0,0 +1,438 @@
+<?php declare(strict_types=1);
+
+/*
+ * This file is part of Evenement.
+ *
+ * (c) Igor Wiedler <igor@wiedler.ch>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Evenement\Tests;
+
+use Evenement\EventEmitter;
+use InvalidArgumentException;
+use PHPUnit\Framework\TestCase;
+
+class EventEmitterTest extends TestCase
+{
+    private $emitter;
+
+    public function setUp()
+    {
+        $this->emitter = new EventEmitter();
+    }
+
+    public function testAddListenerWithLambda()
+    {
+        $this->emitter->on('foo', function () {});
+    }
+
+    public function testAddListenerWithMethod()
+    {
+        $listener = new Listener();
+        $this->emitter->on('foo', [$listener, 'onFoo']);
+    }
+
+    public function testAddListenerWithStaticMethod()
+    {
+        $this->emitter->on('bar', ['Evenement\Tests\Listener', 'onBar']);
+    }
+
+    public function testAddListenerWithInvalidListener()
+    {
+        try {
+            $this->emitter->on('foo', 'not a callable');
+            $this->fail();
+        } catch (\Exception $e) {
+        } catch (\TypeError $e) {
+        }
+    }
+
+    public function testOnce()
+    {
+        $listenerCalled = 0;
+
+        $this->emitter->once('foo', function () use (&$listenerCalled) {
+            $listenerCalled++;
+        });
+
+        $this->assertSame(0, $listenerCalled);
+
+        $this->emitter->emit('foo');
+
+        $this->assertSame(1, $listenerCalled);
+
+        $this->emitter->emit('foo');
+
+        $this->assertSame(1, $listenerCalled);
+    }
+
+    public function testOnceWithArguments()
+    {
+        $capturedArgs = [];
+
+        $this->emitter->once('foo', function ($a, $b) use (&$capturedArgs) {
+            $capturedArgs = array($a, $b);
+        });
+
+        $this->emitter->emit('foo', array('a', 'b'));
+
+        $this->assertSame(array('a', 'b'), $capturedArgs);
+    }
+
+    public function testEmitWithoutArguments()
+    {
+        $listenerCalled = false;
+
+        $this->emitter->on('foo', function () use (&$listenerCalled) {
+            $listenerCalled = true;
+        });
+
+        $this->assertSame(false, $listenerCalled);
+        $this->emitter->emit('foo');
+        $this->assertSame(true, $listenerCalled);
+    }
+
+    public function testEmitWithOneArgument()
+    {
+        $test = $this;
+
+        $listenerCalled = false;
+
+        $this->emitter->on('foo', function ($value) use (&$listenerCalled, $test) {
+            $listenerCalled = true;
+
+            $test->assertSame('bar', $value);
+        });
+
+        $this->assertSame(false, $listenerCalled);
+        $this->emitter->emit('foo', ['bar']);
+        $this->assertSame(true, $listenerCalled);
+    }
+
+    public function testEmitWithTwoArguments()
+    {
+        $test = $this;
+
+        $listenerCalled = false;
+
+        $this->emitter->on('foo', function ($arg1, $arg2) use (&$listenerCalled, $test) {
+            $listenerCalled = true;
+
+            $test->assertSame('bar', $arg1);
+            $test->assertSame('baz', $arg2);
+        });
+
+        $this->assertSame(false, $listenerCalled);
+        $this->emitter->emit('foo', ['bar', 'baz']);
+        $this->assertSame(true, $listenerCalled);
+    }
+
+    public function testEmitWithNoListeners()
+    {
+        $this->emitter->emit('foo');
+        $this->emitter->emit('foo', ['bar']);
+        $this->emitter->emit('foo', ['bar', 'baz']);
+    }
+
+    public function testEmitWithTwoListeners()
+    {
+        $listenersCalled = 0;
+
+        $this->emitter->on('foo', function () use (&$listenersCalled) {
+            $listenersCalled++;
+        });
+
+        $this->emitter->on('foo', function () use (&$listenersCalled) {
+            $listenersCalled++;
+        });
+
+        $this->assertSame(0, $listenersCalled);
+        $this->emitter->emit('foo');
+        $this->assertSame(2, $listenersCalled);
+    }
+
+    public function testRemoveListenerMatching()
+    {
+        $listenersCalled = 0;
+
+        $listener = function () use (&$listenersCalled) {
+            $listenersCalled++;
+        };
+
+        $this->emitter->on('foo', $listener);
+        $this->emitter->removeListener('foo', $listener);
+
+        $this->assertSame(0, $listenersCalled);
+        $this->emitter->emit('foo');
+        $this->assertSame(0, $listenersCalled);
+    }
+
+    public function testRemoveListenerNotMatching()
+    {
+        $listenersCalled = 0;
+
+        $listener = function () use (&$listenersCalled) {
+            $listenersCalled++;
+        };
+
+        $this->emitter->on('foo', $listener);
+        $this->emitter->removeListener('bar', $listener);
+
+        $this->assertSame(0, $listenersCalled);
+        $this->emitter->emit('foo');
+        $this->assertSame(1, $listenersCalled);
+    }
+
+    public function testRemoveAllListenersMatching()
+    {
+        $listenersCalled = 0;
+
+        $this->emitter->on('foo', function () use (&$listenersCalled) {
+            $listenersCalled++;
+        });
+
+        $this->emitter->removeAllListeners('foo');
+
+        $this->assertSame(0, $listenersCalled);
+        $this->emitter->emit('foo');
+        $this->assertSame(0, $listenersCalled);
+    }
+
+    public function testRemoveAllListenersNotMatching()
+    {
+        $listenersCalled = 0;
+
+        $this->emitter->on('foo', function () use (&$listenersCalled) {
+            $listenersCalled++;
+        });
+
+        $this->emitter->removeAllListeners('bar');
+
+        $this->assertSame(0, $listenersCalled);
+        $this->emitter->emit('foo');
+        $this->assertSame(1, $listenersCalled);
+    }
+
+    public function testRemoveAllListenersWithoutArguments()
+    {
+        $listenersCalled = 0;
+
+        $this->emitter->on('foo', function () use (&$listenersCalled) {
+            $listenersCalled++;
+        });
+
+        $this->emitter->on('bar', function () use (&$listenersCalled) {
+            $listenersCalled++;
+        });
+
+        $this->emitter->removeAllListeners();
+
+        $this->assertSame(0, $listenersCalled);
+        $this->emitter->emit('foo');
+        $this->emitter->emit('bar');
+        $this->assertSame(0, $listenersCalled);
+    }
+
+    public function testCallablesClosure()
+    {
+        $calledWith = null;
+
+        $this->emitter->on('foo', function ($data) use (&$calledWith) {
+            $calledWith = $data;
+        });
+
+        $this->emitter->emit('foo', ['bar']);
+
+        self::assertSame('bar', $calledWith);
+    }
+
+    public function testCallablesClass()
+    {
+        $listener = new Listener();
+        $this->emitter->on('foo', [$listener, 'onFoo']);
+
+        $this->emitter->emit('foo', ['bar']);
+
+        self::assertSame(['bar'], $listener->getData());
+    }
+
+
+    public function testCallablesClassInvoke()
+    {
+        $listener = new Listener();
+        $this->emitter->on('foo', $listener);
+
+        $this->emitter->emit('foo', ['bar']);
+
+        self::assertSame(['bar'], $listener->getMagicData());
+    }
+
+    public function testCallablesStaticClass()
+    {
+        $this->emitter->on('foo', '\Evenement\Tests\Listener::onBar');
+
+        $this->emitter->emit('foo', ['bar']);
+
+        self::assertSame(['bar'], Listener::getStaticData());
+    }
+
+    public function testCallablesFunction()
+    {
+        $this->emitter->on('foo', '\Evenement\Tests\setGlobalTestData');
+
+        $this->emitter->emit('foo', ['bar']);
+
+        self::assertSame('bar', $GLOBALS['evenement-evenement-test-data']);
+
+        unset($GLOBALS['evenement-evenement-test-data']);
+    }
+
+    public function testListeners()
+    {
+        $onA = function () {};
+        $onB = function () {};
+        $onC = function () {};
+        $onceA = function () {};
+        $onceB = function () {};
+        $onceC = function () {};
+
+        self::assertCount(0, $this->emitter->listeners('event'));
+        $this->emitter->on('event', $onA);
+        self::assertCount(1, $this->emitter->listeners('event'));
+        self::assertSame([$onA], $this->emitter->listeners('event'));
+        $this->emitter->once('event', $onceA);
+        self::assertCount(2, $this->emitter->listeners('event'));
+        self::assertSame([$onA, $onceA], $this->emitter->listeners('event'));
+        $this->emitter->once('event', $onceB);
+        self::assertCount(3, $this->emitter->listeners('event'));
+        self::assertSame([$onA, $onceA, $onceB], $this->emitter->listeners('event'));
+        $this->emitter->on('event', $onB);
+        self::assertCount(4, $this->emitter->listeners('event'));
+        self::assertSame([$onA, $onB, $onceA, $onceB], $this->emitter->listeners('event'));
+        $this->emitter->removeListener('event', $onceA);
+        self::assertCount(3, $this->emitter->listeners('event'));
+        self::assertSame([$onA, $onB, $onceB], $this->emitter->listeners('event'));
+        $this->emitter->once('event', $onceC);
+        self::assertCount(4, $this->emitter->listeners('event'));
+        self::assertSame([$onA, $onB, $onceB, $onceC], $this->emitter->listeners('event'));
+        $this->emitter->on('event', $onC);
+        self::assertCount(5, $this->emitter->listeners('event'));
+        self::assertSame([$onA, $onB, $onC, $onceB, $onceC], $this->emitter->listeners('event'));
+        $this->emitter->once('event', $onceA);
+        self::assertCount(6, $this->emitter->listeners('event'));
+        self::assertSame([$onA, $onB, $onC, $onceB, $onceC, $onceA], $this->emitter->listeners('event'));
+        $this->emitter->removeListener('event', $onB);
+        self::assertCount(5, $this->emitter->listeners('event'));
+        self::assertSame([$onA, $onC, $onceB, $onceC, $onceA], $this->emitter->listeners('event'));
+        $this->emitter->emit('event');
+        self::assertCount(2, $this->emitter->listeners('event'));
+        self::assertSame([$onA, $onC], $this->emitter->listeners('event'));
+    }
+
+    public function testOnceCallIsNotRemovedWhenWorkingOverOnceListeners()
+    {
+        $aCalled = false;
+        $aCallable = function () use (&$aCalled) {
+            $aCalled = true;
+        };
+        $bCalled = false;
+        $bCallable = function () use (&$bCalled, $aCallable) {
+            $bCalled = true;
+            $this->emitter->once('event', $aCallable);
+        };
+        $this->emitter->once('event', $bCallable);
+
+        self::assertFalse($aCalled);
+        self::assertFalse($bCalled);
+        $this->emitter->emit('event');
+
+        self::assertFalse($aCalled);
+        self::assertTrue($bCalled);
+        $this->emitter->emit('event');
+
+        self::assertTrue($aCalled);
+        self::assertTrue($bCalled);
+    }
+
+    public function testEventNameMustBeStringOn()
+    {
+        self::expectException(InvalidArgumentException::class);
+        self::expectExceptionMessage('event name must not be null');
+
+        $this->emitter->on(null, function () {});
+    }
+
+    public function testEventNameMustBeStringOnce()
+    {
+        self::expectException(InvalidArgumentException::class);
+        self::expectExceptionMessage('event name must not be null');
+
+        $this->emitter->once(null, function () {});
+    }
+
+    public function testEventNameMustBeStringRemoveListener()
+    {
+        self::expectException(InvalidArgumentException::class);
+        self::expectExceptionMessage('event name must not be null');
+
+        $this->emitter->removeListener(null, function () {});
+    }
+
+    public function testEventNameMustBeStringEmit()
+    {
+        self::expectException(InvalidArgumentException::class);
+        self::expectExceptionMessage('event name must not be null');
+
+        $this->emitter->emit(null);
+    }
+
+    public function testListenersGetAll()
+    {
+        $a = function () {};
+        $b = function () {};
+        $c = function () {};
+        $d = function () {};
+
+        $this->emitter->once('event2', $c);
+        $this->emitter->on('event', $a);
+        $this->emitter->once('event', $b);
+        $this->emitter->on('event', $c);
+        $this->emitter->once('event', $d);
+
+        self::assertSame(
+            [
+                'event' => [
+                    $a,
+                    $c,
+                    $b,
+                    $d,
+                ],
+                'event2' => [
+                    $c,
+                ],
+            ],
+            $this->emitter->listeners()
+        );
+    }
+
+    public function testOnceNestedCallRegression()
+    {
+        $first = 0;
+        $second = 0;
+
+        $this->emitter->once('event', function () use (&$first, &$second) {
+            $first++;
+            $this->emitter->once('event', function () use (&$second) {
+                $second++;
+            });
+            $this->emitter->emit('event');
+        });
+        $this->emitter->emit('event');
+
+        self::assertSame(1, $first);
+        self::assertSame(1, $second);
+    }
+}
diff --git a/assets/php/vendor/evenement/evenement/tests/Evenement/Tests/Listener.php b/assets/php/vendor/evenement/evenement/tests/Evenement/Tests/Listener.php
new file mode 100644
index 0000000..df17424
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/tests/Evenement/Tests/Listener.php
@@ -0,0 +1,51 @@
+<?php declare(strict_types=1);
+
+/*
+ * This file is part of Evenement.
+ *
+ * (c) Igor Wiedler <igor@wiedler.ch>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Evenement\Tests;
+
+class Listener
+{
+    private $data = [];
+
+    private $magicData = [];
+
+    private static $staticData = [];
+
+    public function onFoo($data)
+    {
+        $this->data[] = $data;
+    }
+
+    public function __invoke($data)
+    {
+        $this->magicData[] = $data;
+    }
+
+    public static function onBar($data)
+    {
+        self::$staticData[] = $data;
+    }
+
+    public function getData()
+    {
+        return $this->data;
+    }
+
+    public function getMagicData()
+    {
+        return $this->magicData;
+    }
+
+    public static function getStaticData()
+    {
+        return self::$staticData;
+    }
+}
diff --git a/assets/php/vendor/evenement/evenement/tests/Evenement/Tests/functions.php b/assets/php/vendor/evenement/evenement/tests/Evenement/Tests/functions.php
new file mode 100644
index 0000000..7f11f5b
--- /dev/null
+++ b/assets/php/vendor/evenement/evenement/tests/Evenement/Tests/functions.php
@@ -0,0 +1,17 @@
+<?php declare(strict_types=1);
+
+/*
+ * This file is part of Evenement.
+ *
+ * (c) Igor Wiedler <igor@wiedler.ch>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Evenement\Tests;
+
+function setGlobalTestData($data)
+{
+    $GLOBALS['evenement-evenement-test-data'] = $data;
+}
-- 
cgit v1.2.3