diff --git a/composer.json b/composer.json
index 8f4fb1bb1284ef163e772d4fae1ac5a83da9b885..f759bf4e1b94283975d92d71223e16f5f02f7db6 100755
--- a/composer.json
+++ b/composer.json
@@ -32,8 +32,8 @@
     	}
     },
     "require": {
-        "slim/slim": "^3.10",
-        "respect/validation": "^1.1",
+        "slim/slim": "^3.11",
+        "respect/validation": "^1.1.28",
         "php-http/curl-client": "^1.7",
         "m4tthumphrey/php-gitlab-api": "8",
         "jumbojett/openid-connect-php": "^0.3.0",
diff --git a/vendor/kriswallsmith/buzz/.gitattributes b/vendor/kriswallsmith/buzz/.gitattributes
new file mode 100644
index 0000000000000000000000000000000000000000..e993ce46dfaf6ec5ee5a34ec55a9b7fb54daed20
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/.gitattributes
@@ -0,0 +1,9 @@
+* text=auto
+
+/examples export-ignore
+/tests export-ignore
+/.gitignore export-ignore
+/.travis.yml export-ignore
+/CHANGELOG.md export-ignore
+/README.md export-ignore
+/phpunit.xml.dist export-ignore
diff --git a/vendor/kriswallsmith/buzz/.php_cs b/vendor/kriswallsmith/buzz/.php_cs
new file mode 100644
index 0000000000000000000000000000000000000000..82fcf5ae936f4bc4108d52befeceb0a400a7f6d5
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/.php_cs
@@ -0,0 +1,20 @@
+<?php
+
+return PhpCsFixer\Config::create()
+    ->setRules(array(
+        '@Symfony' => true,
+        '@Symfony:risky' => true,
+        'array_syntax' => array('syntax' => 'short'),
+        'protected_to_private' => false,
+        'declare_strict_types' => true,
+    ))
+    ->setRiskyAllowed(true)
+    ->setCacheFile((getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__).'/.php_cs.cache')
+    ->setUsingCache(true)
+    ->setFinder(
+        PhpCsFixer\Finder::create()
+            ->in(__DIR__)
+            ->exclude('vendor')
+            ->name('*.php')
+    )
+;
\ No newline at end of file
diff --git a/vendor/kriswallsmith/buzz/.scrutinizer.yml b/vendor/kriswallsmith/buzz/.scrutinizer.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d92b5c69b960e5d552ba626768b2aebce3ce71d8
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/.scrutinizer.yml
@@ -0,0 +1,9 @@
+filter:
+    excluded_paths: [vendor/*, tests/*, lib/Middleware/DigestAuthMiddleware.php]
+    
+checks:
+    php:
+        code_rating: true
+        duplication: true
+tools:
+    external_code_coverage: true
diff --git a/vendor/kriswallsmith/buzz/.styleci.yml b/vendor/kriswallsmith/buzz/.styleci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f97f9b983d4c9dbbba4650c863f8a904e7819578
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/.styleci.yml
@@ -0,0 +1,11 @@
+preset: symfony
+
+finder:
+    exclude:
+        - "vendor"
+
+enabled:
+    - short_array_syntax
+
+disabled:
+    - phpdoc_annotation_without_dot # This is still buggy: https://github.com/symfony/symfony/pull/19198
\ No newline at end of file
diff --git a/vendor/kriswallsmith/buzz/LICENSE b/vendor/kriswallsmith/buzz/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..e234f8007a80c63ecd95faf6b52fedab2091a7e6
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2010-2011 Kris Wallsmith
+
+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/kriswallsmith/buzz/composer.json b/vendor/kriswallsmith/buzz/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..f4b31232b2ed91d7040eeb7579a9a5313f6e8e37
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/composer.json
@@ -0,0 +1,59 @@
+{
+    "name": "kriswallsmith/buzz",
+    "description": "Lightweight HTTP client",
+    "keywords": ["http client", "curl"],
+    "homepage": "https://github.com/kriswallsmith/Buzz",
+    "type": "library",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Kris Wallsmith",
+            "email": "kris.wallsmith@gmail.com",
+            "homepage": "http://kriswallsmith.net/"
+        },
+        {
+            "name": "Tobias Nyholm",
+            "email": "tobias.nyholm@gmail.com",
+            "homepage": "http://tnyholm.se/"
+        }
+    ],
+    "require": {
+        "php": "^7.1",
+        "psr/http-message": "^1.0",
+        "psr/http-client": "^0.1",
+        "php-http/httplug": "^1.1",
+        "nyholm/psr7": "^1.0",
+        "symfony/options-resolver": "^3.4 || ^4.0",
+        "psr/http-factory": "^1.0"
+    },
+    "require-dev": {
+        "friendsofphp/php-cs-fixer": "^2.11",
+        "php-http/client-integration-tests": "^0.6.2",
+        "phpunit/phpunit": "^6.5.7",
+        "psr/log": "^1.0",
+        "symfony/phpunit-bridge": "^4.0"
+    },
+    "conflict": {
+        "http-interop/http-factory": "<0.4.1"
+    },
+    "provide": {
+        "php-http/client-implementation": "1.0"
+    },
+    "suggest": {
+        "ext-curl": "*"
+    },
+    "autoload": {
+        "psr-4": {
+            "Buzz\\": "lib"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Buzz\\Test\\": "tests"
+        }
+    },
+    "scripts": {
+        "test": "vendor/bin/simple-phpunit",
+        "test-ci": "vendor/bin/simple-phpunit --coverage-text --coverage-clover=build/coverage.xml"
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/doc/client.md b/vendor/kriswallsmith/buzz/doc/client.md
new file mode 100644
index 0000000000000000000000000000000000000000..4a03fcdfc1358664b07fcabac9742eece1cd80dc
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/doc/client.md
@@ -0,0 +1,92 @@
+[<-- Index](/doc/index.md)
+
+# Clients
+
+Clients are the low-level objects to send HTTP requests. All clients are minimalistic both in 
+features and flexibility. You may give the client some default configuration and some additional 
+configuration each time you send a request. 
+
+There are 3 clients: `FileGetContents`, `Curl` and `MultiCurl`. 
+
+```php
+$request = new PSR7Request('GET', 'https://example.com');
+
+$client = new Buzz\Client\FileGetContents(['allow_redirects' => true], new Psr17ResponseFactory());
+$response = $client->send($request, ['timeout' => 4]);
+```
+
+## Configuration
+
+Not all configuration works will all clients. If there is any client specific configuration it 
+will be noted below. 
+
+#### allow_redirects
+
+Type: boolean<br>
+Default: `false`
+
+Should the client follow HTTP redirects or not. 
+
+#### callback
+
+Type: callable<br>
+Default: `function() {}`<br>
+*Only for MultiCurl*
+
+A callback function that is called after a request has been sent. 
+
+```php
+$callback = function(RequestInterface $request, ResponseInterface $response = null, ClientException $exception = null) {
+    // Process the response
+};
+$request = new PSR7Request('GET', 'https://example.com');
+$client->sendAsyncRequest($request, array('callback' => $callback));
+```
+
+#### curl
+
+Type: array<br>
+Default: `[]`<br>
+*Only for Curl and MultiCurl*
+
+An array with Curl options. 
+
+```php
+$request = new PSR7Request('GET', 'https://example.com');
+$client->sendAsyncRequest($request, array('curl' => [
+    CURLOPT_FAILONERROR => false,
+]));
+```
+
+#### max_redirects
+
+Type: integer<br>
+Default: `5`
+
+The maximum number of redirects to follow. Note that this will have no effect unless you set
+`'allow_redirects' => true`.
+
+#### proxy
+
+Type: string<br>
+Default: `null`
+
+A proxy server to use when sending requests. 
+
+#### timeout
+
+Type: integer<br>
+Default: `30`
+
+The time to wait before interrupt the request. 
+
+#### verify
+
+Type: boolean<br>
+Default: `true`
+
+If SSL protocols should verified. 
+
+---
+
+Continue reading about [Middlewares](/doc/middlewares.md).
diff --git a/vendor/kriswallsmith/buzz/doc/index.md b/vendor/kriswallsmith/buzz/doc/index.md
new file mode 100644
index 0000000000000000000000000000000000000000..1750747e484d4629e4ac211b1b8e656bbe368826
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/doc/index.md
@@ -0,0 +1,90 @@
+# Buzz documentation
+
+Buzz is a simple and lightweight HTTP client which is easy to use. This page is 
+the index of the documentation. Please use the table of contents below to start
+reading. 
+
+* [Browser](#browser)
+* [Submit forms](#submit-a-form) 
+* [Client](/doc/client.md)
+* [Middlewares](/doc/middlewares.md) 
+* [Symfony Bundle](/doc/symfony.md) 
+
+
+## Browser
+
+The Browser is the high-level object to send HTTP requests. Main focus is on simplicity. 
+
+When a `Browser` in constructed you have to select a [Client](/doc/client.md) to use. The 
+`FileGetContents` client is used by default. See example of how
+to use the Bowser: 
+
+```php
+$client = new Buzz\Client\FileGetContents([], new Psr17ResponseFactory());
+$browser = new Buzz\Browser($client, new Psr17RequestFactory());
+
+$response = $browser->get('https://example.com');
+$response = $browser->get('https://example.com', ['User-Agent'=>'Buzz']);
+$response = $browser->post('https://example.com', ['User-Agent'=>'Buzz'], 'http-post-body');
+
+$response = $browser->head('https://example.com');
+$response = $browser->patch('https://example.com');
+$response = $browser->put('https://example.com');
+$response = $browser->delete('https://example.com');
+
+
+$response = $browser->request('GET', 'https://example.com');
+```
+
+You do also have a function to send PSR-7 requests. 
+
+```php
+$request = new PSR7Request('GET', 'https://google.com/foo');
+$response = $browser->sendRequest($request)
+```
+
+## Submit a form
+
+With Buzz you have built in support for posing forms. You could of course create your own PSR-7 request and posting it 
+as you normally would. But it might be easier to use the `Browser::submit()` function or the `FormRequestBuilder`. 
+
+Below is an example how to use `Browser::submit()` to upload a file. 
+
+```php
+$browser->submitForm('http://example.com/foo', [
+    'user' => 'Kris Wallsmith',
+    'image' => [
+        'path'=>'/path/to/image.jpg'
+      ],
+]);
+``` 
+
+```php
+$browser->submitForm('http://example.com/foo', [
+    'user[name]' => 'Kris Wallsmith',
+    'user[image]' => [
+        'path'=>'/path/to/image.jpg',
+        'filename' => 'my-image.jpg',
+        'contentType' => 'image/jpg',
+      ],
+]);
+``` 
+
+### Using the FormRequestBuilder
+
+If you have a large from or you want to build your request in a structured way you may use the `FormRequestBuilder`.
+
+```php
+use Buzz\Message\FormRequestBuilder;
+
+$builder = new FormRequestBuilder();
+$builder->addField('user[name]', 'Kris Wallsmith');
+$builder->addFile('user[image]', '/path/to/image.jpg', 'image/jpg', 'my-image.jpg');
+$builder->addFile('cover-image', '/path/to/cover.jpg');
+
+$browser->submitForm('http://example.com/foo', $builder->build());
+``` 
+
+---
+
+Continue reading about [Clients](/doc/client.md).
diff --git a/vendor/kriswallsmith/buzz/doc/middlewares.md b/vendor/kriswallsmith/buzz/doc/middlewares.md
new file mode 100644
index 0000000000000000000000000000000000000000..1b75ea3fd27fc2c350695fea2583b51b0a8bcef5
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/doc/middlewares.md
@@ -0,0 +1,92 @@
+[<-- Index](/doc/index.md)
+
+# Buzz middlewares
+
+If you want to modify the request or response somehow, a middleware is the way to
+go. Every time you send a request with the `Browser` it will run through all the
+middlewares. The order of the middlewares is important. The first middleware added
+to the `Browser` will be the first one that is executed when handling the request and
+the last one to be executed when handling the response. 
+
+```
+Request  ---> Middleware1 ---> Middleware2 ---> HttpClient ----
+                                                               | (processing call)
+Response <--- Middleware1 <--- Middleware2 <--- HttpClient <---
+```
+## Creating a middleware
+
+You are free to create any custom middleware you want. It is super simple to do so. 
+Let's look at the example when we create a middleware for adding the User-Agent 
+request header. 
+
+First we need to create a class that implements `Buzz\Middleware\MiddlewareInterface`
+
+```php
+<?php
+
+use Buzz\Middleware\MiddlewareInterface;
+
+class UserAgentMiddleware implements MiddlewareInterface 
+{
+  // ...
+``` 
+
+The interface has two functions; `handleRequest` and `handleResponse`. The last
+parameter to these functions is a `callable`. That callable is actually the next
+middleware in the chain. It is **very important** that you end your function by
+returning the the result when calling that callable. If you forget about that,
+then no request will be sent.
+
+Let's look at an example implementation of `handleRequest`:
+
+```php
+public function handleRequest(RequestInterface $request, callable $next)
+{
+    $request = $request->withAddedHeader('User-Agent', 'Buzz');
+    
+    return $next($request);
+}
+```
+
+Note that PSR-7 requests and responses are immutable. That is why we do 
+`$request = $request->with...`. 
+
+Sine this middleware does not need to modify the response we just let the 
+`handleResponse` function to be empty like: 
+
+ ```php
+ public function handleResponse(RequestInterface $request, ResponseInterface $response, callable $next)
+ {     
+     return $next($request, $response);
+ }
+ ```
+
+### The full example
+
+```php
+<?php
+
+use Buzz\Middleware\MiddlewareInterface;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+class UserAgentMiddleware implements MiddlewareInterface 
+{
+  public function handleRequest(RequestInterface $request, callable $next)
+  {
+      $request = $request->withAddedHeader('User-Agent', 'Buzz');
+      
+      return $next($request);
+  }
+  
+   public function handleResponse(RequestInterface $request, ResponseInterface $response, callable $next)
+   {     
+       return $next($request, $response);
+   }
+}
+``` 
+
+
+---
+
+Continue reading about [Symfony integration](/doc/symfony.md).
\ No newline at end of file
diff --git a/vendor/kriswallsmith/buzz/doc/symfony.md b/vendor/kriswallsmith/buzz/doc/symfony.md
new file mode 100644
index 0000000000000000000000000000000000000000..5e8988406fc832a540045778fd47cc4ecb9b8529
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/doc/symfony.md
@@ -0,0 +1,48 @@
+[<-- Index](/doc/index.md)
+
+# Symfony integration
+
+Symfony is a great PHP framework and of course should we have some nice integration 
+with it. We have provided a flex recipe in the [contrib repository](https://github.com/symfony/recipes-contrib) 
+which will register the `Browser` and clients as services. But of course, you want
+more! You want a proper bundle. 
+
+Buzz is compatible with HTTPlug which means that we can use all the greatness from 
+the [HTTPlugBundle](https://github.com/php-http/httplugbundle). 
+
+## Install 
+
+```
+composer require php-http/httplug-bundle
+```
+
+## Configure
+
+```
+# config/services.yaml
+# This is done by the flex recipe
+services: 
+    Buzz\Browser: 
+        calls:
+            - ['addMiddleware', ['@buzz.middleware.content_length']]
+    
+    buzz.middleware.content_length:
+        class: Buzz\Middleware\ContentLengthMiddleware
+```
+
+```
+# config/httplug.yaml
+httplug:
+    clients:
+        my_buzz:
+            service: 'Buzz\Browser' 
+```
+
+You will now have a service named `httplug.client.my_buzz`. You can of course add 
+plugins method clients and whatever you want according to the 
+[HTTPlug documentation](http://docs.php-http.org/en/latest/integrations/symfony-bundle.html).
+
+
+---
+
+Go back to [index](/doc/index.md).
\ No newline at end of file
diff --git a/vendor/kriswallsmith/buzz/lib/Browser.php b/vendor/kriswallsmith/buzz/lib/Browser.php
new file mode 100644
index 0000000000000000000000000000000000000000..73a18c858981f1f0d9686fefd4af8b161e47fd2b
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Browser.php
@@ -0,0 +1,278 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz;
+
+use Buzz\Client\BuzzClientInterface;
+use Buzz\Client\FileGetContents;
+use Buzz\Exception\ClientException;
+use Buzz\Exception\InvalidArgumentException;
+use Buzz\Exception\LogicException;
+use Buzz\Middleware\MiddlewareInterface;
+use Http\Message\RequestFactory;
+use Nyholm\Psr7\Factory\Psr17Factory;
+use Psr\Http\Message\RequestFactoryInterface;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+class Browser implements BuzzClientInterface
+{
+    /** @var BuzzClientInterface */
+    private $client;
+
+    /** @var RequestFactoryInterface|RequestFactory */
+    private $requestFactory;
+
+    /**
+     * @var MiddlewareInterface[]
+     */
+    private $middlewares = [];
+
+    /** @var RequestInterface */
+    private $lastRequest;
+
+    /** @var ResponseInterface */
+    private $lastResponse;
+
+    /**
+     * @param BuzzClientInterface|null                    $client
+     * @param RequestFactoryInterface|RequestFactory|null $requestFactory
+     */
+    public function __construct(BuzzClientInterface $client = null, $requestFactory = null)
+    {
+        if (null === $client) {
+            @trigger_error('Not passing a BuzzClientInterface to Browser constructor is deprecated.', E_USER_DEPRECATED);
+            $client = new FileGetContents();
+        }
+
+        if (null === $requestFactory) {
+            @trigger_error('Not passing a RequestFactory to Browser constructor is deprecated.', E_USER_DEPRECATED);
+            $requestFactory = new Psr17Factory();
+        } elseif (!$requestFactory instanceof RequestFactoryInterface && !$requestFactory instanceof RequestFactory) {
+            throw new InvalidArgumentException('$requestFactory not a valid RequestFactory');
+        }
+
+        $this->client = $client;
+        $this->requestFactory = $requestFactory;
+    }
+
+    public function get(string $url, array $headers = []): ResponseInterface
+    {
+        return $this->request('GET', $url, $headers);
+    }
+
+    public function post(string $url, array $headers = [], string $body = ''): ResponseInterface
+    {
+        return $this->request('POST', $url, $headers, $body);
+    }
+
+    public function head(string $url, array  $headers = []): ResponseInterface
+    {
+        return $this->request('HEAD', $url, $headers);
+    }
+
+    public function patch(string $url, array  $headers = [], string $body = ''): ResponseInterface
+    {
+        return $this->request('PATCH', $url, $headers, $body);
+    }
+
+    public function put(string $url, array  $headers = [], string $body = ''): ResponseInterface
+    {
+        return $this->request('PUT', $url, $headers, $body);
+    }
+
+    public function delete(string $url, array  $headers = [], string $body = ''): ResponseInterface
+    {
+        return $this->request('DELETE', $url, $headers, $body);
+    }
+
+    /**
+     * Sends a request.
+     *
+     * @param string $method  The request method to use
+     * @param string $url     The URL to call
+     * @param array  $headers An array of request headers
+     * @param string $body    The request content
+     *
+     * @return ResponseInterface The response object
+     */
+    public function request(string $method, string $url, array $headers = [], string $body = ''): ResponseInterface
+    {
+        $request = $this->createRequest($method, $url, $headers, $body);
+
+        return $this->sendRequest($request);
+    }
+
+    /**
+     * Submit a form.
+     *
+     * @throws ClientException
+     * @throws LogicException
+     * @throws InvalidArgumentException
+     */
+    public function submitForm(string $url, array $fields, string $method = 'POST', array $headers = []): ResponseInterface
+    {
+        $body = [];
+        $files = '';
+        $boundary = uniqid('', true);
+        foreach ($fields as $name => $field) {
+            if (!isset($field['path'])) {
+                $body[$name] = $field;
+            } else {
+                // This is a file
+                $fileContent = file_get_contents($field['path']);
+                $files .= $this->prepareMultipart($name, $fileContent, $boundary, $field);
+            }
+        }
+
+        if (empty($files)) {
+            $headers['Content-Type'] = 'application/x-www-form-urlencoded';
+            $body = http_build_query($body);
+        } else {
+            $headers['Content-Type'] = 'multipart/form-data; boundary="'.$boundary.'"';
+
+            foreach ($body as $name => $value) {
+                $files .= $this->prepareMultipart($name, $value, $boundary);
+            }
+            $body = "$files--{$boundary}--\r\n";
+        }
+
+        $request = $this->createRequest($method, $url, $headers, $body);
+
+        return $this->sendRequest($request);
+    }
+
+    /**
+     * Send a PSR7 request.
+     *
+     * @throws ClientException
+     * @throws LogicException
+     * @throws InvalidArgumentException
+     */
+    public function sendRequest(RequestInterface $request, array $options = []): ResponseInterface
+    {
+        $chain = $this->createMiddlewareChain($this->middlewares, function (RequestInterface $request, callable $responseChain) use ($options) {
+            $response = $this->client->sendRequest($request, $options);
+            $responseChain($request, $response);
+        }, function (RequestInterface $request, ResponseInterface $response) {
+            $this->lastRequest = $request;
+            $this->lastResponse = $response;
+        });
+
+        // Call the chain
+        $chain($request);
+
+        return $this->lastResponse;
+    }
+
+    /**
+     * @param MiddlewareInterface[] $middlewares
+     * @param callable              $requestChainLast
+     * @param callable              $responseChainLast
+     *
+     * @return callable
+     */
+    private function createMiddlewareChain(array $middlewares, callable $requestChainLast, callable $responseChainLast): callable
+    {
+        $responseChainNext = $responseChainLast;
+
+        // Build response chain
+        /** @var MiddlewareInterface $middleware */
+        foreach ($middlewares as $middleware) {
+            $lastCallable = function (RequestInterface $request, ResponseInterface $response) use ($middleware, $responseChainNext) {
+                return $middleware->handleResponse($request, $response, $responseChainNext);
+            };
+
+            $responseChainNext = $lastCallable;
+        }
+
+        $requestChainLast = function (RequestInterface $request) use ($requestChainLast, $responseChainNext) {
+            // Send the actual request and get the response
+            $requestChainLast($request, $responseChainNext);
+        };
+
+        $middlewares = array_reverse($middlewares);
+
+        // Build request chain
+        $requestChainNext = $requestChainLast;
+        /** @var MiddlewareInterface $middleware */
+        foreach ($middlewares as $middleware) {
+            $lastCallable = function (RequestInterface $request) use ($middleware, $requestChainNext) {
+                return $middleware->handleRequest($request, $requestChainNext);
+            };
+
+            $requestChainNext = $lastCallable;
+        }
+
+        return $requestChainNext;
+    }
+
+    public function getLastRequest(): ?RequestInterface
+    {
+        return $this->lastRequest;
+    }
+
+    public function getLastResponse(): ?ResponseInterface
+    {
+        return $this->lastResponse;
+    }
+
+    public function getClient(): BuzzClientInterface
+    {
+        return $this->client;
+    }
+
+    /**
+     * Add a new middleware to the stack.
+     *
+     * @param MiddlewareInterface $middleware
+     */
+    public function addMiddleware(MiddlewareInterface $middleware): void
+    {
+        $this->middlewares[] = $middleware;
+    }
+
+    private function prepareMultipart(string $name, string $content, string $boundary, array $data = []): string
+    {
+        $output = '';
+        $fileHeaders = [];
+
+        // Set a default content-disposition header
+        $fileHeaders['Content-Disposition'] = sprintf('form-data; name="%s"', $name);
+        if (isset($data['filename'])) {
+            $fileHeaders['Content-Disposition'] .= sprintf('; filename="%s"', $data['filename']);
+        }
+
+        // Set a default content-length header
+        if ($length = \strlen($content)) {
+            $fileHeaders['Content-Length'] = (string) $length;
+        }
+
+        if (isset($data['contentType'])) {
+            $fileHeaders['Content-Type'] = $data['contentType'];
+        }
+
+        // Add start
+        $output .= "--$boundary\r\n";
+        foreach ($fileHeaders as $key => $value) {
+            $output .= sprintf("%s: %s\r\n", $key, $value);
+        }
+        $output .= "\r\n";
+        $output .= $content;
+        $output .= "\r\n";
+
+        return $output;
+    }
+
+    protected function createRequest(string $method, string $url, array $headers, $body): RequestInterface
+    {
+        $request = $this->requestFactory->createRequest($method, $url);
+        $request->getBody()->write($body);
+        foreach ($headers as $name => $value) {
+            $request = $request->withAddedHeader($name, $value);
+        }
+
+        return $request;
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Client/AbstractClient.php b/vendor/kriswallsmith/buzz/lib/Client/AbstractClient.php
new file mode 100644
index 0000000000000000000000000000000000000000..ae789f341fe3c91034c93e01bd61ee2cc96c93eb
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Client/AbstractClient.php
@@ -0,0 +1,101 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Client;
+
+use Buzz\Configuration\ParameterBag;
+use Buzz\Exception\InvalidArgumentException;
+use Http\Message\ResponseFactory;
+use Nyholm\Psr7\Factory\Psr17Factory;
+use Psr\Http\Message\ResponseFactoryInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+abstract class AbstractClient
+{
+    /**
+     * @var OptionsResolver
+     */
+    private $optionsResolver;
+
+    /**
+     * @var ParameterBag
+     */
+    private $options;
+
+    /**
+     * @var ResponseFactoryInterface|ResponseFactory
+     */
+    protected $responseFactory;
+
+    public function __construct(array $options = [], $responseFactory = null)
+    {
+        $this->options = new ParameterBag();
+        $this->options = $this->doValidateOptions($options);
+        if (null === $responseFactory) {
+            @trigger_error('Not passing a ResponseFactory to Buzz client constructor is deprecated.', E_USER_DEPRECATED);
+            $responseFactory = new Psr17Factory();
+        } elseif (!$responseFactory instanceof ResponseFactoryInterface && !$responseFactory instanceof ResponseFactory) {
+            throw new InvalidArgumentException('$responseFactory not a valid ResponseFactory');
+        }
+        $this->responseFactory = $responseFactory;
+    }
+
+    protected function getOptionsResolver(): OptionsResolver
+    {
+        if (null !== $this->optionsResolver) {
+            return $this->optionsResolver;
+        }
+
+        $this->optionsResolver = new OptionsResolver();
+        $this->configureOptions($this->optionsResolver);
+
+        return $this->optionsResolver;
+    }
+
+    /**
+     * Validate a set of options and return a new and shiny ParameterBag.
+     */
+    protected function validateOptions(array $options = []): ParameterBag
+    {
+        if (empty($options)) {
+            return $this->options;
+        }
+
+        return $this->doValidateOptions($options);
+    }
+
+    /**
+     * Validate a set of options and return a new and shiny ParameterBag.
+     */
+    private function doValidateOptions(array $options = []): ParameterBag
+    {
+        $parameterBag = $this->options->add($options);
+
+        try {
+            $parameters = $this->getOptionsResolver()->resolve($parameterBag->all());
+        } catch (\Throwable $e) {
+            // Wrap any errors.
+            throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
+        }
+
+        return new ParameterBag($parameters);
+    }
+
+    protected function configureOptions(OptionsResolver $resolver): void
+    {
+        $resolver->setDefaults([
+            'allow_redirects' => false,
+            'max_redirects' => 5,
+            'timeout' => 30,
+            'verify' => true,
+            'proxy' => null,
+        ]);
+
+        $resolver->setAllowedTypes('allow_redirects', 'boolean');
+        $resolver->setAllowedTypes('verify', 'boolean');
+        $resolver->setAllowedTypes('max_redirects', 'integer');
+        $resolver->setAllowedTypes('timeout', ['integer', 'float']);
+        $resolver->setAllowedTypes('proxy', ['null', 'string']);
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Client/AbstractCurl.php b/vendor/kriswallsmith/buzz/lib/Client/AbstractCurl.php
new file mode 100644
index 0000000000000000000000000000000000000000..596b446ee748e9abcae9c3ca9931a3a743cf8caa
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Client/AbstractCurl.php
@@ -0,0 +1,247 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Client;
+
+use Buzz\Configuration\ParameterBag;
+use Buzz\Message\HeaderConverter;
+use Buzz\Exception\ClientException;
+use Buzz\Exception\NetworkException;
+use Buzz\Exception\RequestException;
+use Buzz\Message\ResponseBuilder;
+use Psr\Http\Message\RequestInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+/**
+ * Base client class with helpers for working with cURL.
+ */
+abstract class AbstractCurl extends AbstractClient
+{
+    private $handles = [];
+
+    private $maxHandles = 5;
+
+    protected function configureOptions(OptionsResolver $resolver): void
+    {
+        parent::configureOptions($resolver);
+
+        $resolver->setDefault('curl', []);
+        $resolver->setAllowedTypes('curl', ['array']);
+    }
+
+    /**
+     * Creates a new cURL resource.
+     *
+     * @return resource A new cURL resource
+     *
+     * @throws ClientException If unable to create a cURL resource
+     */
+    protected function createHandle()
+    {
+        $curl = $this->handles ? array_pop($this->handles) : curl_init();
+        if (false === $curl) {
+            throw new ClientException('Unable to create a new cURL handle');
+        }
+
+        return $curl;
+    }
+
+    /**
+     * Release a cUrl resource. This function is from Guzzle.
+     *
+     * @param resource $curl
+     */
+    protected function releaseHandle($curl): void
+    {
+        if (\count($this->handles) >= $this->maxHandles) {
+            curl_close($curl);
+        } else {
+            // Remove all callback functions as they can hold onto references
+            // and are not cleaned up by curl_reset. Using curl_setopt_array
+            // does not work for some reason, so removing each one
+            // individually.
+            curl_setopt($curl, CURLOPT_HEADERFUNCTION, null);
+            curl_setopt($curl, CURLOPT_READFUNCTION, null);
+            curl_setopt($curl, CURLOPT_WRITEFUNCTION, null);
+            curl_setopt($curl, CURLOPT_PROGRESSFUNCTION, null);
+            curl_reset($curl);
+            $this->handles[] = $curl;
+        }
+    }
+
+    /**
+     * Prepares a cURL resource to send a request.
+     *
+     * @param resource         $curl
+     * @param RequestInterface $request
+     * @param ParameterBag     $options
+     *
+     * @return ResponseBuilder
+     */
+    protected function prepare($curl, RequestInterface $request, ParameterBag $options): ResponseBuilder
+    {
+        if (\defined('CURLOPT_PROTOCOLS')) {
+            curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+            curl_setopt($curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+        }
+
+        curl_setopt($curl, CURLOPT_HEADER, false);
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);
+        curl_setopt($curl, CURLOPT_FAILONERROR, false);
+
+        $this->setOptionsFromParameterBag($curl, $options);
+        $this->setOptionsFromRequest($curl, $request);
+
+        $responseBuilder = new ResponseBuilder($this->responseFactory);
+        curl_setopt($curl, CURLOPT_HEADERFUNCTION, function ($ch, $data) use ($responseBuilder) {
+            $str = trim($data);
+            if ('' !== $str) {
+                if (0 === strpos(strtolower($str), 'http/')) {
+                    $responseBuilder->setStatus($str);
+                } else {
+                    $responseBuilder->addHeader($str);
+                }
+            }
+
+            return \strlen($data);
+        });
+
+        curl_setopt($curl, CURLOPT_WRITEFUNCTION, function ($ch, $data) use ($responseBuilder) {
+            return $responseBuilder->writeBody($data);
+        });
+
+        // apply additional options
+        curl_setopt_array($curl, $options->get('curl'));
+
+        return $responseBuilder;
+    }
+
+    /**
+     * Sets options on a cURL resource based on a request.
+     *
+     * @param resource         $curl    A cURL resource
+     * @param RequestInterface $request A request object
+     */
+    private function setOptionsFromRequest($curl, RequestInterface $request): void
+    {
+        $options = [
+            CURLOPT_CUSTOMREQUEST => $request->getMethod(),
+            CURLOPT_URL => $request->getUri()->__toString(),
+            CURLOPT_HTTPHEADER => HeaderConverter::toBuzzHeaders($request->getHeaders()),
+        ];
+
+        if (0 !== $version = $this->getProtocolVersion($request)) {
+            $options[CURLOPT_HTTP_VERSION] = $version;
+        }
+
+        if ($request->getUri()->getUserInfo()) {
+            $options[CURLOPT_USERPWD] = $request->getUri()->getUserInfo();
+        }
+
+        switch (strtoupper($request->getMethod())) {
+            case 'HEAD':
+                $options[CURLOPT_NOBODY] = true;
+
+                break;
+
+            case 'GET':
+                $options[CURLOPT_HTTPGET] = true;
+
+                break;
+
+            case 'POST':
+            case 'PUT':
+            case 'DELETE':
+            case 'PATCH':
+            case 'OPTIONS':
+                $body = $request->getBody();
+                $bodySize = $body->getSize();
+                if (0 !== $bodySize) {
+                    if ($body->isSeekable()) {
+                        $body->rewind();
+                    }
+
+                    // Message has non empty body.
+                    if (null === $bodySize || $bodySize > 1024 * 1024) {
+                        // Avoid full loading large or unknown size body into memory
+                        $options[CURLOPT_UPLOAD] = true;
+                        if (null !== $bodySize) {
+                            $options[CURLOPT_INFILESIZE] = $bodySize;
+                        }
+                        $options[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) {
+                            return $body->read($length);
+                        };
+                    } else {
+                        // Small body can be loaded into memory
+                        $options[CURLOPT_POSTFIELDS] = (string) $body;
+                    }
+                }
+        }
+
+        curl_setopt_array($curl, $options);
+    }
+
+    /**
+     * @param resource     $curl
+     * @param ParameterBag $options
+     */
+    private function setOptionsFromParameterBag($curl, ParameterBag $options): void
+    {
+        $timeout = $options->get('timeout');
+        $proxy = $options->get('proxy');
+        if ($proxy) {
+            curl_setopt($curl, CURLOPT_PROXY, $proxy);
+        }
+
+        $canFollow = !ini_get('safe_mode') && !ini_get('open_basedir') && $options->get('allow_redirects');
+        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, $canFollow);
+        curl_setopt($curl, CURLOPT_MAXREDIRS, $canFollow ? $options->get('max_redirects') : 0);
+        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $options->get('verify') ? 1 : 0);
+        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $options->get('verify') ? 2 : 0);
+        curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
+    }
+
+    /**
+     * @param RequestInterface $request
+     * @param int              $errno
+     * @param resource         $curl
+     *
+     * @throws NetworkException
+     * @throws RequestException
+     */
+    protected function parseError(RequestInterface $request, int $errno, $curl): void
+    {
+        switch ($errno) {
+            case CURLE_OK:
+                // All OK, create a response object
+                break;
+            case CURLE_COULDNT_RESOLVE_PROXY:
+            case CURLE_COULDNT_RESOLVE_HOST:
+            case CURLE_COULDNT_CONNECT:
+            case CURLE_OPERATION_TIMEOUTED:
+            case CURLE_SSL_CONNECT_ERROR:
+                throw new NetworkException($request, curl_error($curl), $errno);
+            default:
+                throw new RequestException($request, curl_error($curl), $errno);
+        }
+    }
+
+    private function getProtocolVersion(RequestInterface $request): int
+    {
+        switch ($request->getProtocolVersion()) {
+            case '1.0':
+                return CURL_HTTP_VERSION_1_0;
+            case '1.1':
+                return CURL_HTTP_VERSION_1_1;
+            case '2.0':
+                if (\defined('CURL_HTTP_VERSION_2_0')) {
+                    return CURL_HTTP_VERSION_2_0;
+                }
+
+                throw new \UnexpectedValueException('libcurl 7.33 needed for HTTP 2.0 support');
+            default:
+                return 0;
+        }
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Client/BatchClientInterface.php b/vendor/kriswallsmith/buzz/lib/Client/BatchClientInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..0cb09a36f46e803fd64b6455a6afd16d091ebc19
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Client/BatchClientInterface.php
@@ -0,0 +1,36 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Client;
+
+use Buzz\Exception\ClientException;
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * A client capable of running batches of requests.
+ *
+ * The Countable implementation should return the number of queued requests.
+ */
+interface BatchClientInterface extends \Countable
+{
+    /**
+     * @param RequestInterface $request
+     * @param array            $options
+     */
+    public function sendAsyncRequest(RequestInterface $request, array $options = []): void;
+
+    /**
+     * Processes all queued requests.
+     *
+     * @throws ClientException If something goes wrong
+     */
+    public function flush(): void;
+
+    /**
+     * Processes zero or more queued requests.
+     *
+     * @throws ClientException If something goes wrong
+     */
+    public function proceed(): void;
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Client/BuzzClientInterface.php b/vendor/kriswallsmith/buzz/lib/Client/BuzzClientInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..21b4a7615a7a1e403e2088be6f23e2593ad9cc08
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Client/BuzzClientInterface.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Client;
+
+use Http\Client\HttpClient;
+use Psr\Http\Client\ClientInterface;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+interface BuzzClientInterface extends ClientInterface, HttpClient
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function sendRequest(RequestInterface $request, array $options = []): ResponseInterface;
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Client/Curl.php b/vendor/kriswallsmith/buzz/lib/Client/Curl.php
new file mode 100644
index 0000000000000000000000000000000000000000..fd246e646f7b6bc9ed9c27cf5a5d5ed3641b0f11
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Client/Curl.php
@@ -0,0 +1,30 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Client;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+class Curl extends AbstractCurl implements BuzzClientInterface
+{
+    public function sendRequest(RequestInterface $request, array $options = []): ResponseInterface
+    {
+        $options = $this->validateOptions($options);
+        $curl = $this->createHandle();
+        $responseBuilder = $this->prepare($curl, $request, $options);
+
+        try {
+            curl_exec($curl);
+            $this->parseError($request, curl_errno($curl), $curl);
+        } finally {
+            $this->releaseHandle($curl);
+        }
+
+        return $responseBuilder->getResponse();
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Client/FileGetContents.php b/vendor/kriswallsmith/buzz/lib/Client/FileGetContents.php
new file mode 100644
index 0000000000000000000000000000000000000000..fb16c2611a480e731ca4c114187a303119a6249f
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Client/FileGetContents.php
@@ -0,0 +1,90 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Client;
+
+use Buzz\Configuration\ParameterBag;
+use Buzz\Message\HeaderConverter;
+use Buzz\Exception\NetworkException;
+use Buzz\Message\ResponseBuilder;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+class FileGetContents extends AbstractClient implements BuzzClientInterface
+{
+    public function sendRequest(RequestInterface $request, array $options = []): ResponseInterface
+    {
+        $options = $this->validateOptions($options);
+        $context = stream_context_create($this->getStreamContextArray($request, $options));
+
+        $level = error_reporting(0);
+        $content = file_get_contents($request->getUri()->__toString(), false, $context);
+        error_reporting($level);
+        if (false === $content) {
+            $error = error_get_last();
+
+            throw new NetworkException($request, $error['message']);
+        }
+
+        $requestBuilder = new ResponseBuilder($this->responseFactory);
+        $requestBuilder->parseHttpHeaders($this->filterHeaders((array) $http_response_header));
+        $requestBuilder->writeBody($content);
+
+        return $requestBuilder->getResponse();
+    }
+
+    /**
+     * Converts a request into an array for stream_context_create().
+     *
+     * @param RequestInterface $request A request object
+     * @param ParameterBag     $options
+     *
+     * @return array An array for stream_context_create()
+     */
+    protected function getStreamContextArray(RequestInterface $request, ParameterBag $options): array
+    {
+        $headers = $request->getHeaders();
+        unset($headers['Host']);
+        $context = [
+            'http' => [
+                // values from the request
+                'method' => $request->getMethod(),
+                'header' => implode("\r\n", HeaderConverter::toBuzzHeaders($headers)),
+                'content' => $request->getBody()->__toString(),
+                'protocol_version' => $request->getProtocolVersion(),
+
+                // values from the current client
+                'ignore_errors' => true,
+                'follow_location' => $options->get('allow_redirects') && $options->get('max_redirects') > 0,
+                'max_redirects' => $options->get('max_redirects') + 1,
+                'timeout' => $options->get('timeout'),
+            ],
+            'ssl' => [
+                'verify_peer' => $options->get('verify'),
+                'verify_host' => $options->get('verify'),
+            ],
+        ];
+
+        if (null !== $options->get('proxy')) {
+            $context['http']['proxy'] = $options->get('proxy');
+            $context['http']['request_fulluri'] = true;
+        }
+
+        return $context;
+    }
+
+    private function filterHeaders(array $headers): array
+    {
+        $filtered = [];
+        foreach ($headers as $header) {
+            if (0 === stripos($header, 'http/')) {
+                $filtered = [];
+            }
+
+            $filtered[] = $header;
+        }
+
+        return $filtered;
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Client/MultiCurl.php b/vendor/kriswallsmith/buzz/lib/Client/MultiCurl.php
new file mode 100644
index 0000000000000000000000000000000000000000..9d3a468a671fdbf100b4fa6c3d49c0dfa5c2b8bf
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Client/MultiCurl.php
@@ -0,0 +1,162 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Client;
+
+use Buzz\Configuration\ParameterBag;
+use Buzz\Exception\ExceptionInterface;
+use Buzz\Exception\ClientException;
+use Buzz\Message\ResponseBuilder;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+class MultiCurl extends AbstractCurl implements BatchClientInterface, BuzzClientInterface
+{
+    private $queue = [];
+
+    private $curlm;
+
+    /**
+     * Populates the supplied response with the response for the supplied request.
+     *
+     * The array of options will be passed to curl_setopt_array().
+     *
+     * If a "callback" option is supplied, its value will be called when the
+     * request completes. The callable should have the following signature:
+     *
+     *     $callback = function($request, $response, $exception) {
+     *         if (!$exception) {
+     *             // success
+     *         } else {
+     *             // error ($error is one of the CURLE_* constants)
+     *         }
+     *     };
+     */
+    public function sendAsyncRequest(RequestInterface $request, array $options = []): void
+    {
+        $options = $this->validateOptions($options);
+
+        $this->queue[] = [$request, $options];
+    }
+
+    public function sendRequest(RequestInterface $request, array $options = []): ResponseInterface
+    {
+        $options = $this->validateOptions($options);
+        $originalCallback = $options->get('callback');
+        $responseToReturn = null;
+        $options = $options->add(['callback' => function (RequestInterface $request, ResponseInterface $response = null, ClientException $e = null) use (&$responseToReturn, $originalCallback) {
+            $responseToReturn = $response;
+            $originalCallback($request, $response, $e);
+        }]);
+
+        $this->queue[] = [$request, $options];
+        $this->flush();
+
+        return $responseToReturn;
+    }
+
+    protected function configureOptions(OptionsResolver $resolver): void
+    {
+        parent::configureOptions($resolver);
+
+        $resolver->setDefault('callback', function (RequestInterface $request, ResponseInterface $response = null, ClientException $e = null) {
+        });
+        $resolver->setAllowedTypes('callback', 'callable');
+    }
+
+    public function count(): int
+    {
+        return \count($this->queue);
+    }
+
+    /**
+     * @throws ClientException
+     */
+    public function flush(): void
+    {
+        while (!empty($this->queue)) {
+            $this->proceed();
+        }
+    }
+
+    /**
+     * @throws ClientException
+     */
+    public function proceed(): void
+    {
+        if (empty($this->queue)) {
+            return;
+        }
+
+        if (!$this->curlm && false === $this->curlm = curl_multi_init()) {
+            throw new ClientException('Unable to create a new cURL multi handle');
+        }
+
+        foreach ($this->queue as $i => $queueItem) {
+            if (2 !== \count($queueItem)) {
+                // We have already prepared this curl
+                continue;
+            }
+            // prepare curl handle
+            /** @var $request RequestInterface */
+            /** @var $options ParameterBag */
+            list($request, $options) = $queueItem;
+            $curl = $this->createHandle();
+            $responseBuilder = $this->prepare($curl, $request, $options);
+            $this->queue[$i][] = $curl;
+            $this->queue[$i][] = $responseBuilder;
+            curl_multi_add_handle($this->curlm, $curl);
+        }
+
+        // process outstanding perform
+        $active = null;
+        do {
+            $mrc = curl_multi_exec($this->curlm, $active);
+        } while ($active && CURLM_CALL_MULTI_PERFORM == $mrc);
+
+        $exception = null;
+        // handle any completed requests
+        while ($done = curl_multi_info_read($this->curlm)) {
+            foreach (array_keys($this->queue) as $i) {
+                /** @var $request RequestInterface */
+                /** @var $options ParameterBag */
+                /** @var $responseBuilder ResponseBuilder */
+                list($request, $options, $curl, $responseBuilder) = $this->queue[$i];
+
+                if ($curl !== $done['handle']) {
+                    continue;
+                }
+
+                try {
+                    $response = null;
+                    $this->parseError($request, $done['result'], $curl);
+                    $response = $responseBuilder->getResponse();
+                } catch (ExceptionInterface $e) {
+                    if (null === $exception) {
+                        $exception = $e;
+                    }
+                }
+
+                // remove from queue
+                curl_multi_remove_handle($this->curlm, $curl);
+                $this->releaseHandle($curl);
+                unset($this->queue[$i]);
+
+                // callback
+                \call_user_func($options->get('callback'), $request, $response, $exception);
+            }
+        }
+
+        // cleanup
+        if (empty($this->queue)) {
+            curl_multi_close($this->curlm);
+            $this->curlm = null;
+
+            if (null !== $exception) {
+                throw $exception;
+            }
+        }
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Configuration/ParameterBag.php b/vendor/kriswallsmith/buzz/lib/Configuration/ParameterBag.php
new file mode 100644
index 0000000000000000000000000000000000000000..90ea31cc0822be1cafd91c001b9e0c4278aea4b1
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Configuration/ParameterBag.php
@@ -0,0 +1,112 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Configuration;
+
+/**
+ * A ParameterBag is a container for key/value pairs. This implementation is immutable.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+final class ParameterBag implements \IteratorAggregate, \Countable
+{
+    /**
+     * Parameter storage.
+     */
+    private $parameters;
+
+    /**
+     * @param array $parameters An array of parameters
+     */
+    public function __construct(array $parameters = [])
+    {
+        $this->parameters = $parameters;
+    }
+
+    /**
+     * Returns the parameters.
+     *
+     * @return array An array of parameters
+     */
+    public function all(): array
+    {
+        return $this->parameters;
+    }
+
+    /**
+     * Returns the parameter keys.
+     *
+     * @return array An array of parameter keys
+     */
+    public function keys(): array
+    {
+        return array_keys($this->parameters);
+    }
+
+    /**
+     * Adds parameters to the existing ones.
+     *
+     * @param array $parameters An array of parameters
+     */
+    public function add(array $parameters = []): self
+    {
+        // Make sure to merge Curl parameters
+        if (isset($this->parameters['curl'])
+            && isset($parameters['curl'])
+            && \is_array($this->parameters['curl'])
+            && \is_array($parameters['curl'])) {
+            $parameters['curl'] = array_replace($this->parameters['curl'], $parameters['curl']);
+        }
+
+        $newParameters = array_replace($this->parameters, $parameters);
+
+        return new self($newParameters);
+    }
+
+    /**
+     * Returns a parameter by name.
+     *
+     * @param string|int $key     The key
+     * @param mixed      $default The default value if the parameter key does not exist
+     *
+     * @return mixed
+     */
+    public function get($key, $default = null)
+    {
+        return array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
+    }
+
+    /**
+     * Returns true if the parameter is defined.
+     *
+     * @param string|int $key The key
+     *
+     * @return bool true if the parameter exists, false otherwise
+     */
+    public function has($key): bool
+    {
+        return array_key_exists($key, $this->parameters);
+    }
+
+    /**
+     * Returns an iterator for parameters.
+     *
+     * @return \ArrayIterator An \ArrayIterator instance
+     */
+    public function getIterator(): \ArrayIterator
+    {
+        return new \ArrayIterator($this->parameters);
+    }
+
+    /**
+     * Returns the number of parameters.
+     *
+     * @return int The number of parameters
+     */
+    public function count(): int
+    {
+        return \count($this->parameters);
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Exception/ClientException.php b/vendor/kriswallsmith/buzz/lib/Exception/ClientException.php
new file mode 100644
index 0000000000000000000000000000000000000000..917488173cb7e80c9e65e779875843d5833ad25f
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Exception/ClientException.php
@@ -0,0 +1,14 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Exception;
+
+use Http\Client\Exception as HTTPlugException;
+
+/**
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+class ClientException extends \RuntimeException implements ExceptionInterface, HTTPlugException
+{
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Exception/ExceptionInterface.php b/vendor/kriswallsmith/buzz/lib/Exception/ExceptionInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..c097bbdc1185b4c83441177b29da34ef7db9dc2f
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Exception/ExceptionInterface.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Exception;
+
+/**
+ * Marker interface to denote exceptions thrown from the Buzz context.
+ */
+interface ExceptionInterface
+{
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Exception/InvalidArgumentException.php b/vendor/kriswallsmith/buzz/lib/Exception/InvalidArgumentException.php
new file mode 100644
index 0000000000000000000000000000000000000000..712cc8b08d208e00834d422ce1c11c734d84dd29
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Exception/InvalidArgumentException.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Exception;
+
+/**
+ * Thrown when an invalid argument is provided.
+ */
+class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Exception/LogicException.php b/vendor/kriswallsmith/buzz/lib/Exception/LogicException.php
new file mode 100644
index 0000000000000000000000000000000000000000..64c84fac2ac2b69b03461953fb4eb442e5b89a85
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Exception/LogicException.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Exception;
+
+/**
+ * Thrown whenever a required call-flow is not respected.
+ */
+class LogicException extends \LogicException implements ExceptionInterface
+{
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Exception/NetworkException.php b/vendor/kriswallsmith/buzz/lib/Exception/NetworkException.php
new file mode 100644
index 0000000000000000000000000000000000000000..1b461d03efd014660ef2f6415e53c47651a73fa4
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Exception/NetworkException.php
@@ -0,0 +1,30 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Exception;
+
+use Psr\Http\Client\Exception\NetworkException as PsrNetworkException;
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+class NetworkException extends ClientException implements PsrNetworkException
+{
+    /**
+     * @var RequestInterface
+     */
+    private $request;
+
+    public function __construct(RequestInterface $request, string $message = '', int $code = 0, \Throwable $previous = null)
+    {
+        $this->request = $request;
+        parent::__construct($message, $code, $previous);
+    }
+
+    public function getRequest(): RequestInterface
+    {
+        return $this->request;
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Exception/RequestException.php b/vendor/kriswallsmith/buzz/lib/Exception/RequestException.php
new file mode 100644
index 0000000000000000000000000000000000000000..d15cff5ca806a27454784be713c85bb9a80c7158
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Exception/RequestException.php
@@ -0,0 +1,30 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Exception;
+
+use Psr\Http\Client\Exception\RequestException as PsrRequestException;
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+class RequestException extends ClientException implements PsrRequestException
+{
+    /**
+     * @var RequestInterface
+     */
+    private $request;
+
+    public function __construct(RequestInterface $request, string $message = '', int $code = 0, \Throwable $previous = null)
+    {
+        $this->request = $request;
+        parent::__construct($message, $code, $previous);
+    }
+
+    public function getRequest(): RequestInterface
+    {
+        return $this->request;
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Message/FormRequestBuilder.php b/vendor/kriswallsmith/buzz/lib/Message/FormRequestBuilder.php
new file mode 100644
index 0000000000000000000000000000000000000000..a9d2d5ed809dbc7851bb6e8b4abec7aa7d745e94
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Message/FormRequestBuilder.php
@@ -0,0 +1,75 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Message;
+
+class FormRequestBuilder
+{
+    /**
+     * @var array
+     */
+    private $data;
+
+    /**
+     * @var array
+     */
+    private $files;
+
+    /**
+     * @param array $data
+     * @param array $files
+     */
+    public function __construct(array $data = [], array $files = [])
+    {
+        $this->data = $data;
+        $this->files = $files;
+    }
+
+    /**
+     * @param string $name
+     * @param string $value
+     */
+    public function addField(string $name, string $value): void
+    {
+        $this->data[$name] = $value;
+    }
+
+    /**
+     * @param string      $name
+     * @param string      $path
+     * @param string|null $contentType
+     * @param string|null $filename
+     */
+    public function addFile(string $name, string $path, string $contentType = null, string $filename = null): void
+    {
+        $this->files[$name] = [
+            'path' => $path,
+            'contentType' => $contentType,
+            'filename' => $filename,
+        ];
+    }
+
+    /**
+     * @return array
+     */
+    public function build(): array
+    {
+        $data = $this->data;
+
+        foreach ($this->files as $name => $file) {
+            $data[$name] = $file;
+        }
+
+        return $data;
+    }
+
+    /**
+     * Reset the builder.
+     */
+    public function reset(): void
+    {
+        $this->data = [];
+        $this->files = [];
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Message/HeaderConverter.php b/vendor/kriswallsmith/buzz/lib/Message/HeaderConverter.php
new file mode 100644
index 0000000000000000000000000000000000000000..9ce2b65853aeafa0c98acd49926428ff2c53eab6
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Message/HeaderConverter.php
@@ -0,0 +1,65 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Message;
+
+/**
+ * Convert between Buzz style:
+ * array(
+ *   'foo: bar',
+ *   'baz: biz',
+ * ).
+ *
+ * and PSR style:
+ * array(
+ *   'foo' => 'bar'
+ *   'baz' => ['biz', 'buz'],
+ * )
+ *
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+class HeaderConverter
+{
+    /**
+     * Convert from Buzz style headers to PSR style.
+     *
+     * @param array $headers
+     *
+     * @return array
+     */
+    public static function toBuzzHeaders(array $headers): array
+    {
+        $buzz = [];
+
+        foreach ($headers as $key => $values) {
+            if (!\is_array($values)) {
+                $buzz[] = sprintf('%s: %s', $key, $values);
+            } else {
+                foreach ($values as $value) {
+                    $buzz[] = sprintf('%s: %s', $key, $value);
+                }
+            }
+        }
+
+        return $buzz;
+    }
+
+    /**
+     * Convert from PSR style headers to Buzz style.
+     *
+     * @param array $headers
+     *
+     * @return array
+     */
+    public static function toPsrHeaders(array $headers): array
+    {
+        $psr = [];
+        foreach ($headers as $header) {
+            list($key, $value) = explode(':', $header, 2);
+            $psr[trim($key)][] = trim($value);
+        }
+
+        return $psr;
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Message/ResponseBuilder.php b/vendor/kriswallsmith/buzz/lib/Message/ResponseBuilder.php
new file mode 100644
index 0000000000000000000000000000000000000000..cbd65e1bef27c362f6b015d690d2ade15f6f94db
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Message/ResponseBuilder.php
@@ -0,0 +1,95 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Message;
+
+use Buzz\Exception\InvalidArgumentException;
+use Http\Message\ResponseFactory as HTTPlugResponseFactory;
+use Psr\Http\Message\ResponseFactoryInterface as PsrResponseFactory;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+class ResponseBuilder
+{
+    /**
+     * @var ResponseInterface
+     */
+    private $response;
+
+    /**
+     * @param HTTPlugResponseFactory|PsrResponseFactory $responseFactory
+     */
+    public function __construct($responseFactory)
+    {
+        if (!$responseFactory instanceof HTTPlugResponseFactory && !$responseFactory instanceof PsrResponseFactory) {
+            throw new InvalidArgumentException('First parameter to ResponseBuilder must be a response factory');
+        }
+
+        $this->response = $responseFactory->createResponse();
+    }
+
+    public function setStatus(string $input): void
+    {
+        $parts = explode(' ', $input, 3);
+        if (\count($parts) < 2 || 0 !== strpos(strtolower($parts[0]), 'http/')) {
+            throw new InvalidArgumentException(sprintf('"%s" is not a valid HTTP status line', $input));
+        }
+
+        $this->response = $this->response->withStatus((int) $parts[1], isset($parts[2]) ? $parts[2] : '');
+        $this->response = $this->response->withProtocolVersion((string) substr($parts[0], 5));
+    }
+
+    /**
+     * Add a single HTTP header line.
+     *
+     * @param string $input
+     */
+    public function addHeader(string $input): void
+    {
+        list($key, $value) = explode(':', $input, 2);
+        $this->response = $this->response->withAddedHeader(trim($key), trim($value));
+    }
+
+    /**
+     * Add HTTP headers. The input array is all the header lines from the HTTP message. Optionally including the
+     * status line.
+     *
+     * @param array $headers
+     */
+    public function parseHttpHeaders(array $headers): void
+    {
+        $statusLine = array_shift($headers);
+
+        try {
+            $this->setStatus($statusLine);
+        } catch (InvalidArgumentException $e) {
+            array_unshift($headers, $statusLine);
+        }
+
+        foreach ($headers as $header) {
+            $this->addHeader($header);
+        }
+    }
+
+    /**
+     * Add some content to the body. This function writes the $input to a stream.
+     *
+     * @param string $input
+     *
+     * @return int returns the number of bytes written
+     */
+    public function writeBody(string $input): int
+    {
+        return $this->response->getBody()->write($input);
+    }
+
+    public function getResponse(): ResponseInterface
+    {
+        $this->response->getBody()->rewind();
+
+        return $this->response;
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Middleware/BasicAuthMiddleware.php b/vendor/kriswallsmith/buzz/lib/Middleware/BasicAuthMiddleware.php
new file mode 100644
index 0000000000000000000000000000000000000000..cf7db608fba1f1b5b87823f8cd2befa87d664e47
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Middleware/BasicAuthMiddleware.php
@@ -0,0 +1,33 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Middleware;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+class BasicAuthMiddleware implements MiddlewareInterface
+{
+    private $username;
+
+    private $password;
+
+    public function __construct($username, $password)
+    {
+        $this->username = $username;
+        $this->password = $password;
+    }
+
+    public function handleRequest(RequestInterface $request, callable $next)
+    {
+        $request = $request->withAddedHeader('Authorization', sprintf('Basic %s', base64_encode($this->username.':'.$this->password)));
+
+        return $next($request);
+    }
+
+    public function handleResponse(RequestInterface $request, ResponseInterface $response, callable $next)
+    {
+        return $next($request, $response);
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Middleware/BearerAuthMiddleware.php b/vendor/kriswallsmith/buzz/lib/Middleware/BearerAuthMiddleware.php
new file mode 100644
index 0000000000000000000000000000000000000000..8dd983da419db00d549ec1daa5c51edc7dda92a6
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Middleware/BearerAuthMiddleware.php
@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Middleware;
+
+use Buzz\Exception\InvalidArgumentException;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+class BearerAuthMiddleware implements MiddlewareInterface
+{
+    private $accessToken;
+
+    public function __construct($accessToken)
+    {
+        if (empty($accessToken)) {
+            throw new InvalidArgumentException('You must supply a non empty accessToken');
+        }
+
+        $this->accessToken = $accessToken;
+    }
+
+    public function handleRequest(RequestInterface $request, callable $next)
+    {
+        $request = $request->withAddedHeader('Authorization', sprintf('Bearer %s', $this->accessToken));
+
+        return $next($request);
+    }
+
+    public function handleResponse(RequestInterface $request, ResponseInterface $response, callable $next)
+    {
+        return $next($request, $response);
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Middleware/CallbackMiddleware.php b/vendor/kriswallsmith/buzz/lib/Middleware/CallbackMiddleware.php
new file mode 100644
index 0000000000000000000000000000000000000000..f56e02153947060d8a548122701bac1be0485d63
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Middleware/CallbackMiddleware.php
@@ -0,0 +1,56 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Middleware;
+
+use Buzz\Exception\InvalidArgumentException;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+class CallbackMiddleware implements MiddlewareInterface
+{
+    private $callable;
+
+    /**
+     * The callback should expect either one or two arguments, depending on
+     * whether it is receiving a pre or post send notification.
+     *
+     *     $middleware = new CallbackMiddleware(function($request, $response = null) {
+     *         if ($response) {
+     *             // postSend
+     *         } else {
+     *             // preSend
+     *         }
+     *     });
+     *
+     * @param mixed $callable A PHP callable
+     *
+     * @throws InvalidArgumentException If the argument is not callable
+     */
+    public function __construct($callable)
+    {
+        if (!\is_callable($callable)) {
+            throw new InvalidArgumentException('The argument is not callable.');
+        }
+
+        $this->callable = $callable;
+    }
+
+    public function handleRequest(RequestInterface $request, callable $next)
+    {
+        $request = \call_user_func($this->callable, $request);
+
+        return $next($request);
+    }
+
+    public function handleResponse(RequestInterface $request, ResponseInterface $response, callable $next)
+    {
+        $response = \call_user_func($this->callable, $request, $response);
+
+        return $next($request, $response);
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Middleware/ContentLengthMiddleware.php b/vendor/kriswallsmith/buzz/lib/Middleware/ContentLengthMiddleware.php
new file mode 100644
index 0000000000000000000000000000000000000000..74177033acc4486608feabb12fc875261dae9a98
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Middleware/ContentLengthMiddleware.php
@@ -0,0 +1,27 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Middleware;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+class ContentLengthMiddleware implements MiddlewareInterface
+{
+    public function handleRequest(RequestInterface $request, callable $next)
+    {
+        $body = $request->getBody();
+
+        if (!$request->hasHeader('Content-Length')) {
+            $request = $request->withAddedHeader('Content-Length', (string) $body->getSize());
+        }
+
+        return $next($request);
+    }
+
+    public function handleResponse(RequestInterface $request, ResponseInterface $response, callable $next)
+    {
+        return $next($request, $response);
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Middleware/CookieMiddleware.php b/vendor/kriswallsmith/buzz/lib/Middleware/CookieMiddleware.php
new file mode 100644
index 0000000000000000000000000000000000000000..059c6742323079fe3e20231bd7cf20d6ab4a4a84
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Middleware/CookieMiddleware.php
@@ -0,0 +1,58 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Middleware;
+
+use Buzz\Util\Cookie;
+use Buzz\Util\CookieJar;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+class CookieMiddleware implements MiddlewareInterface
+{
+    private $cookieJar;
+
+    public function __construct()
+    {
+        $this->cookieJar = new CookieJar();
+    }
+
+    public function setCookies(array $cookies): void
+    {
+        $this->cookieJar->setCookies($cookies);
+    }
+
+    /**
+     * @return Cookie[]
+     */
+    public function getCookies(): array
+    {
+        return $this->cookieJar->getCookies();
+    }
+
+    /**
+     * Adds a cookie to the current cookie jar.
+     *
+     * @param Cookie $cookie A cookie object
+     */
+    public function addCookie(Cookie $cookie): void
+    {
+        $this->cookieJar->addCookie($cookie);
+    }
+
+    public function handleRequest(RequestInterface $request, callable $next)
+    {
+        $this->cookieJar->clearExpiredCookies();
+        $request = $this->cookieJar->addCookieHeaders($request);
+
+        return $next($request);
+    }
+
+    public function handleResponse(RequestInterface $request, ResponseInterface $response, callable $next)
+    {
+        $this->cookieJar->processSetCookieHeaders($request, $response);
+
+        return $next($request, $response);
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Middleware/DigestAuthMiddleware.php b/vendor/kriswallsmith/buzz/lib/Middleware/DigestAuthMiddleware.php
new file mode 100644
index 0000000000000000000000000000000000000000..dcc0a839f8d0668d3fa47416a0b623efb883de3f
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Middleware/DigestAuthMiddleware.php
@@ -0,0 +1,833 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Middleware;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+class DigestAuthMiddleware implements MiddlewareInterface
+{
+    private $username;
+
+    private $password;
+
+    private $realm;
+
+    private $algorithm;
+
+    private $authenticationMethod;
+
+    private $clientNonce;
+
+    private $domain;
+
+    private $entityBody;
+
+    private $method;
+
+    private $nonce;
+
+    private $nonceCount;
+
+    private $opaque;
+
+    private $uri;
+
+    /** @var string[] Quality of Protection */
+    private $qop = [];
+
+    /**
+     * QOP options: Only one of the following can be set at any time. setOptions will throw an exception otherwise.
+     * OPTION_QOP_AUTH_INT       - Always use auth-int   (if available)
+     * OPTION_QOP_AUTH           - Always use auth       (even if auth-int available).
+     */
+    const OPTION_QOP_AUTH_INT = 1;
+
+    const OPTION_QOP_AUTH = 2;
+
+    /**
+     * Ignore server request to downgrade authentication from Digest to Basic.
+     * Breaks RFC compatibility, but ensures passwords are never sent using base64 which is trivial for an attacker to decode.
+     */
+    const OPTION_IGNORE_DOWNGRADE_REQUEST = 4;
+
+    /**
+     * Discard Client Nonce on each request.
+     */
+    const OPTION_DISCARD_CLIENT_NONCE = 8;
+
+    private $options;
+
+    /**
+     * Set OPTION_QOP_BEST_AVAILABLE and OPTION_DISCARD_CLIENT_NONCE by default.
+     */
+    public function __construct(string $username = null, string $password = null, string $realm = null)
+    {
+        $this->setUsername($username);
+        $this->setPassword($password);
+        $this->setRealm($realm);
+        $this->setOptions(self::OPTION_QOP_AUTH_INT & self::OPTION_DISCARD_CLIENT_NONCE);
+    }
+
+    /**
+     * Populates uri, method and entityBody used to generate the Authentication header using the specified request object.
+     * Appends the Authentication header if it is present and has been able to be calculated.
+     */
+    public function handleRequest(RequestInterface $request, callable $next)
+    {
+        $this->setUri($request->getUri()->getPath());
+        $this->setMethod(strtoupper($request->getMethod()));
+        $this->setEntityBody($request->getBody()->__toString());
+
+        if (null !== $header = $this->getHeader()) {
+            $request = $request->withHeader('Authorization', $header);
+        }
+
+        return $next($request);
+    }
+
+    /**
+     * Passes the returned server headers to parseServerHeaders() to check if any authentication variables need to be set.
+     * Inteprets the returned status code and attempts authentication if status is 401 (Authentication Required) by resending
+     * the last request with an Authentication header.
+     */
+    public function handleResponse(RequestInterface $request, ResponseInterface $response, callable $next)
+    {
+        $this->parseServerHeaders($response);
+
+        return $next($request, $response);
+    }
+
+    /**
+     * Sets the password to be used to authenticate the client.
+     *
+     * @param string $password The password
+     */
+    public function setPassword(?string $password): void
+    {
+        $this->password = $password;
+    }
+
+    /**
+     * Sets the realm to be used to authenticate the client.
+     *
+     * @param string $realm The realm
+     */
+    public function setRealm(?string $realm): void
+    {
+        $this->realm = $realm;
+    }
+
+    /**
+     * Sets the username to be used to authenticate the client.
+     *
+     * @param string $username The username
+     */
+    public function setUsername(?string $username): void
+    {
+        $this->username = $username;
+    }
+
+    /**
+     * Sets the options to be used by this class.
+     *
+     * @param mixed $options a bitmask of the constants defined in this class
+     */
+    public function setOptions($options): void
+    {
+        if ($options & self::OPTION_QOP_AUTH_INT) {
+            if ($options & self::OPTION_QOP_AUTH) {
+                throw new \InvalidArgumentException('DigestAuthMiddleware: Only one value of OPTION_QOP_AUTH_INT or OPTION_QOP_AUTH may be set.');
+            }
+            $this->options = $this->options | self::OPTION_QOP_AUTH_INT;
+        } elseif ($options & self::OPTION_QOP_AUTH) {
+            $this->options = $this->options | self::OPTION_QOP_AUTH;
+        }
+
+        if ($options & self::OPTION_IGNORE_DOWNGRADE_REQUEST) {
+            $this->options = $this->options | self::OPTION_IGNORE_DOWNGRADE_REQUEST;
+        }
+
+        if ($options & self::OPTION_DISCARD_CLIENT_NONCE) {
+            $this->options = $this->options | self::OPTION_DISCARD_CLIENT_NONCE;
+        }
+    }
+
+    /**
+     * Discards the Client Nonce forcing the generation of a new Client Nonce on the next request.
+     */
+    private function discardClientNonce(): void
+    {
+        $this->clientNonce = null;
+    }
+
+    /**
+     * Returns the hashing algorithm to be used to generate the digest value. Currently only returns MD5.
+     *
+     * @return string the hashing algorithm to be used
+     */
+    private function getAlgorithm(): ?string
+    {
+        if (null == $this->algorithm) {
+            $this->algorithm = 'MD5';
+        }
+
+        return $this->algorithm;
+    }
+
+    /**
+     * Returns the authentication method requested by the server.
+     * If OPTION_IGNORE_DOWNGRADE_REQUEST is set this will always return "Digest".
+     *
+     * @return string returns either "Digest" or "Basic"
+     */
+    private function getAuthenticationMethod(): ?string
+    {
+        if ($this->options & self::OPTION_IGNORE_DOWNGRADE_REQUEST) {
+            return 'Digest';
+        }
+
+        return $this->authenticationMethod;
+    }
+
+    /**
+     * Returns either the current value of clientNonce or generates a new value if clientNonce is null.
+     * Also increments nonceCount.
+     *
+     * @return string Returns either the current value of clientNonce the newly generated clientNonce;
+     */
+    private function getClientNonce(): ?string
+    {
+        if (null == $this->clientNonce) {
+            $this->clientNonce = uniqid();
+
+            if (null == $this->nonceCount) {
+                // If nonceCount is not set then set it to 00000001.
+                $this->nonceCount = '00000001';
+            } else {
+                // If it is set then increment it.
+                ++$this->nonceCount;
+                // Ensure nonceCount is zero-padded at the start of the string to a length of 8
+                while (\strlen($this->nonceCount) < 8) {
+                    $this->nonceCount = '0'.$this->nonceCount;
+                }
+            }
+        }
+
+        return $this->clientNonce;
+    }
+
+    /**
+     * Returns a space separated list of uris that the server nonce can be used to generate an authentication response against.
+     *
+     * @return string space separated list of uris
+     */
+    private function getDomain(): ?string
+    {
+        return $this->domain;
+    }
+
+    /**
+     * Returns the entity body of the current request.
+     * The entity body is the request before it has been encoded with the content-encoding and minus the request headers.
+     *
+     * @return string the full entity-body
+     */
+    private function getEntityBody(): ?string
+    {
+        return (string) $this->entityBody;
+    }
+
+    /**
+     * Calculates the value of HA1 according to RFC 2617 and RFC 2069.
+     *
+     * @return string|null The value of HA1
+     */
+    private function getHA1(): ?string
+    {
+        $username = $this->getUsername();
+        $password = $this->getPassword();
+        $realm = $this->getRealm();
+
+        if (($username) && ($password) && ($realm)) {
+            $algorithm = $this->getAlgorithm();
+
+            if ('MD5' === $algorithm) {
+                $A1 = "{$username}:{$realm}:{$password}";
+
+                return $this->hash($A1);
+            } elseif ('MD5-sess' === $algorithm) {
+                $nonce = $this->getNonce();
+                $cnonce = $this->getClientNonce();
+                if (($nonce) && ($cnonce)) {
+                    $A1 = $this->hash("{$username}:{$realm}:{$password}").":{$nonce}:{$cnonce}";
+
+                    return $this->hash($A1);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Calculates the value of HA2 according to RFC 2617 and RFC 2069.
+     *
+     * @return string The value of HA2
+     */
+    private function getHA2(): ?string
+    {
+        $method = $this->getMethod();
+        $uri = $this->getUri();
+
+        if (($method) && ($uri)) {
+            $qop = $this->getQOP();
+
+            if (null === $qop || 'auth' === $qop) {
+                $A2 = "{$method}:{$uri}";
+            } elseif ('auth-int' === $qop) {
+                $entityBody = (string) $this->getEntityBody();
+                $A2 = "{$method}:{$uri}:".(string) $this->hash($entityBody);
+            } else {
+                return null;
+            }
+
+            $HA2 = $this->hash($A2);
+
+            return $HA2;
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the full Authentication header for use in authenticating the client with either Digest or Basic authentication.
+     *
+     * @return string the Authentication header to be sent to the server
+     */
+    private function getHeader(): ?string
+    {
+        if ('Digest' == $this->getAuthenticationMethod()) {
+            $username = $this->getUsername();
+            $realm = $this->getRealm();
+            $nonce = $this->getNonce();
+            $response = $this->getResponse();
+            if (($username) && ($realm) && ($nonce) && ($response)) {
+                $uri = $this->getUri();
+                $opaque = $this->getOpaque();
+                $qop = $this->getQOP();
+
+                $header = 'Digest';
+                $header .= ' username="'.$username.'",';
+                $header .= ' realm="'.$realm.'",';
+                $header .= ' nonce="'.$nonce.'",';
+                $header .= ' response="'.$response.'",';
+
+                if ($uri) {
+                    $header .= ' uri="'.$uri.'",';
+                }
+                if ($opaque) {
+                    $header .= ' opaque="'.$opaque.'",';
+                }
+
+                if ($qop) {
+                    $header .= ' qop='.$qop.',';
+
+                    $cnonce = $this->getClientNonce();
+                    $nc = $this->getNonceCount();
+
+                    if ($cnonce) {
+                        $header .= ' nc='.$nc.',';
+                    }
+                    if ($cnonce) {
+                        $header .= ' cnonce="'.$cnonce.'",';
+                    }
+                }
+
+                // Remove the last comma from the header
+                $header = substr($header, 0, \strlen($header) - 1);
+                // Discard the Client Nonce if OPTION_DISCARD_CLIENT_NONCE is set.
+                if ($this->options & self::OPTION_DISCARD_CLIENT_NONCE) {
+                    $this->discardClientNonce();
+                }
+
+                return $header;
+            }
+        }
+        if ('Basic' == $this->getAuthenticationMethod()) {
+            $username = $this->getUsername();
+            $password = $this->getPassword();
+            if (($username) && ($password)) {
+                $header = 'Basic '.base64_encode("{$username}:{$password}");
+
+                return $header;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the HTTP method used in the current request.
+     *
+     * @return string one of GET,POST,PUT,DELETE or HEAD
+     */
+    private function getMethod(): ?string
+    {
+        return $this->method;
+    }
+
+    /**
+     * Returns the value of nonce we have received in the server headers.
+     *
+     * @return string the value of the server nonce
+     */
+    private function getNonce(): ?string
+    {
+        return $this->nonce;
+    }
+
+    /**
+     * Returns the current nonce counter for the client nonce.
+     *
+     * @return string an eight digit zero-padded string which reflects the number of times the clientNonce has been generated
+     */
+    private function getNonceCount(): ?string
+    {
+        return $this->nonceCount;
+    }
+
+    /**
+     * Returns the opaque value that was sent to us from the server.
+     *
+     * @return string the value of opaque
+     */
+    private function getOpaque(): ?string
+    {
+        return $this->opaque;
+    }
+
+    /**
+     * Returns the plaintext password for the client.
+     *
+     * @return string the value of password
+     */
+    private function getPassword(): ?string
+    {
+        return $this->password;
+    }
+
+    /**
+     * Returns either the realm specified by the client, or the realm specified by the server.
+     * If the server set the value of realm then anything set by our client is overwritten.
+     *
+     * @return string the value of realm
+     */
+    private function getRealm(): ?string
+    {
+        return $this->realm;
+    }
+
+    /**
+     * Calculates the value of response according to RFC 2617 and RFC 2069.
+     *
+     * @return string The value of response
+     */
+    private function getResponse(): ?string
+    {
+        $HA1 = $this->getHA1();
+        $nonce = $this->getNonce();
+        $HA2 = $this->getHA2();
+
+        if (null !== $HA1 && ($nonce) && null !== $HA2) {
+            $qop = $this->getQOP();
+
+            if (empty($qop)) {
+                $response = $this->hash("{$HA1}:{$nonce}:{$HA2}");
+
+                return $response;
+            }
+
+            $cnonce = $this->getClientNonce();
+            $nc = $this->getNonceCount();
+            if (($cnonce) && ($nc)) {
+                $response = $this->hash("{$HA1}:{$nonce}:{$nc}:{$cnonce}:{$qop}:{$HA2}");
+
+                return $response;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the Quality of Protection to be used when authenticating with the server.
+     *
+     * @return string this will either be auth-int or auth
+     */
+    private function getQOP(): ?string
+    {
+        // Has the server specified any options for Quality of Protection
+        if (\count($this->qop) > 0) {
+            if ($this->options & self::OPTION_QOP_AUTH_INT) {
+                if (\in_array('auth-int', $this->qop)) {
+                    return 'auth-int';
+                }
+                if (\in_array('auth', $this->qop)) {
+                    return 'auth';
+                }
+            }
+            if ($this->options & self::OPTION_QOP_AUTH) {
+                if (\in_array('auth', $this->qop)) {
+                    return 'auth';
+                }
+                if (\in_array('auth-int', $this->qop)) {
+                    return 'auth-int';
+                }
+            }
+        }
+        // Server has not specified any value for Quality of Protection so return null
+        return null;
+    }
+
+    /**
+     * Returns the username set by the client to authenticate with the server.
+     *
+     * @return string The value of username
+     */
+    private function getUsername(): ?string
+    {
+        return $this->username;
+    }
+
+    /**
+     * Returns the uri that we are requesting access to.
+     *
+     * @return string The value of uri
+     */
+    private function getUri(): ?string
+    {
+        return $this->uri;
+    }
+
+    /**
+     * Calculates the hash for a given value using the algorithm specified by the server.
+     *
+     * @param string $value The value to be hashed
+     *
+     * @return string the hashed value
+     */
+    private function hash($value): ?string
+    {
+        $algorithm = $this->getAlgorithm();
+        if (('MD5' == $algorithm) || ('MD5-sess' == $algorithm)) {
+            return hash('md5', $value);
+        }
+
+        return null;
+    }
+
+    /**
+     * Parses the Authentication-Info header received from the server and calls the relevant setter method on each variable received.
+     *
+     * @param string $authenticationInfo the full Authentication-Info header
+     */
+    private function parseAuthenticationInfoHeader(string $authenticationInfo): void
+    {
+        $nameValuePairs = $this->parseNameValuePairs($authenticationInfo);
+        foreach ($nameValuePairs as $name => $value) {
+            switch ($name) {
+                case 'message-qop':
+
+                    break;
+                case 'nextnonce':
+                    // This function needs to only set the Nonce once the rspauth has been verified.
+                    $this->setNonce($value);
+
+                    break;
+                case 'rspauth':
+                    // Check server rspauth value
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Parses a string of name=value pairs separated by commas and returns and array with the name as the index.
+     *
+     * @param string $nameValuePairs the string containing the name=value pairs
+     *
+     * @return array an array with the name used as the index and the values stored within
+     */
+    private function parseNameValuePairs(string $nameValuePairs): array
+    {
+        $parsedNameValuePairs = [];
+        $nameValuePairs = explode(',', $nameValuePairs);
+        foreach ($nameValuePairs as $nameValuePair) {
+            // Trim the Whitespace from the start and end of the name value pair string
+            $nameValuePair = trim($nameValuePair);
+            // Split $nameValuePair (name=value) into $name and $value
+            list($name, $value) = explode('=', $nameValuePair, 2);
+            // Remove quotes if the string is quoted
+            $value = $this->unquoteString($value);
+            // Add pair to array[name] => value
+            $parsedNameValuePairs[$name] = $value;
+        }
+
+        return $parsedNameValuePairs;
+    }
+
+    /**
+     * Parses the server headers received and checks for WWW-Authenticate and Authentication-Info headers.
+     * Calls parseWwwAuthenticateHeader() and parseAuthenticationInfoHeader() respectively if either of these headers are present.
+     *
+     * @param ResponseInterface $response
+     */
+    private function parseServerHeaders(ResponseInterface $response): void
+    {
+        // Check to see if the WWW-Authenticate header is present and if so set $authHeader
+        if (!empty($header = $response->getHeaderLine('www-authenticate'))) {
+            $this->parseWwwAuthenticateHeader($header);
+        }
+
+        // Check to see if the Authentication-Info header is present and if so set $authInfo
+        if (!empty($header = $response->getHeaderLine('authentication-info'))) {
+            $this->parseAuthenticationInfoHeader($header);
+        }
+    }
+
+    /**
+     * Parses the WWW-Authenticate header received from the server and calls the relevant setter method on each variable received.
+     *
+     * @param string $wwwAuthenticate the full WWW-Authenticate header
+     */
+    private function parseWwwAuthenticateHeader(string $wwwAuthenticate): void
+    {
+        if ('Digest ' == substr($wwwAuthenticate, 0, 7)) {
+            $this->setAuthenticationMethod('Digest');
+            // Remove "Digest " from start of header
+            $wwwAuthenticate = substr($wwwAuthenticate, 7, \strlen($wwwAuthenticate) - 7);
+
+            $nameValuePairs = $this->parseNameValuePairs($wwwAuthenticate);
+
+            foreach ($nameValuePairs as $name => $value) {
+                switch ($name) {
+                    case 'algorithm':
+                        $this->setAlgorithm($value);
+
+                        break;
+                    case 'domain':
+                        $this->setDomain($value);
+
+                        break;
+                    case 'nonce':
+                        $this->setNonce($value);
+
+                        break;
+                    case 'realm':
+                        $this->setRealm($value);
+
+                        break;
+                    case 'opaque':
+                        $this->setOpaque($value);
+
+                        break;
+                    case 'qop':
+                        $this->setQOP(explode(',', $value));
+
+                        break;
+                }
+            }
+        }
+        if ('Basic ' == substr($wwwAuthenticate, 0, 6)) {
+            $this->setAuthenticationMethod('Basic');
+            // Remove "Basic " from start of header
+            $wwwAuthenticate = substr($wwwAuthenticate, 6, \strlen($wwwAuthenticate) - 6);
+
+            $nameValuePairs = $this->parseNameValuePairs($wwwAuthenticate);
+
+            foreach ($nameValuePairs as $name => $value) {
+                switch ($name) {
+                    case 'realm':
+                        $this->setRealm($value);
+
+                        break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets the hashing algorithm to be used. Currently only uses MD5 specified by either MD5 or MD5-sess.
+     * RFCs are currently in draft stage for the proposal of SHA-256 and SHA-512-256.
+     * Support will be added once the RFC leaves the draft stage.
+     *
+     * @param string $algorithm the algorithm the server has requested to use
+     *
+     * @throws \InvalidArgumentException if $algorithm is set to anything other than MD5 or MD5-sess
+     */
+    private function setAlgorithm(string $algorithm): void
+    {
+        if (('MD5' == $algorithm) || ('MD5-sess' == $algorithm)) {
+            $this->algorithm = $algorithm;
+        } else {
+            throw new \InvalidArgumentException('DigestAuthMiddleware: Only MD5 and MD5-sess algorithms are currently supported.');
+        }
+    }
+
+    /**
+     * Sets authentication method to be used. Options are "Digest" and "Basic".
+     * If the server and the client are unable to authenticate using Digest then the RFCs state that the server should attempt
+     * to authenticate the client using Basic authentication. This ensures that we adhere to that behaviour.
+     * This does however create the possibilty of a downgrade attack so it may be an idea to add a way of disabling this functionality
+     * as Basic authentication is trivial to decrypt and exposes the username/password to a man-in-the-middle attack.
+     *
+     * @param string $authenticationMethod the authentication method requested by the server
+     *
+     * @throws \InvalidArgumentException If $authenticationMethod is set to anything other than Digest or Basic
+     */
+    private function setAuthenticationMethod(string $authenticationMethod): void
+    {
+        if ('Digest' === $authenticationMethod || 'Basic' === $authenticationMethod) {
+            $this->authenticationMethod = $authenticationMethod;
+        } else {
+            throw new \InvalidArgumentException('DigestAuthMiddleware: Only Digest and Basic authentication methods are currently supported.');
+        }
+    }
+
+    /**
+     * Sets the domain to be authenticated against. THIS IS NOT TO BE CONFUSED WITH THE HOSTNAME/DOMAIN.
+     * This is specified by the RFC to be a list of uris separated by spaces that the client will be allowed to access.
+     * An RFC in draft stage is proposing the removal of this functionality, it does not seem to be in widespread use.
+     *
+     * @param string $domain the list of uris separated by spaces that the client will be able to access upon successful authentication
+     */
+    private function setDomain(string $domain): void
+    {
+        $this->domain = $domain;
+    }
+
+    /**
+     * Sets the Entity Body of the Request for use with qop=auth-int.
+     *
+     * @param string $entityBody the body of the entity (The unencoded request minus the headers)
+     */
+    private function setEntityBody(string $entityBody = null): void
+    {
+        $this->entityBody = $entityBody;
+    }
+
+    /**
+     * Sets the HTTP method being used for the request.
+     *
+     * @param string $method The HTTP method
+     *
+     * @throws \InvalidArgumentException if $method is set to anything other than GET,POST,PUT,DELETE or HEAD
+     */
+    private function setMethod(string $method = null): void
+    {
+        if ('GET' == $method) {
+            $this->method = 'GET';
+
+            return;
+        }
+        if ('POST' == $method) {
+            $this->method = 'POST';
+
+            return;
+        }
+        if ('PUT' == $method) {
+            $this->method = 'PUT';
+
+            return;
+        }
+        if ('DELETE' == $method) {
+            $this->method = 'DELETE';
+
+            return;
+        }
+        if ('HEAD' == $method) {
+            $this->method = 'HEAD';
+
+            return;
+        }
+
+        throw new \InvalidArgumentException('DigestAuthMiddleware: Only GET,POST,PUT,DELETE,HEAD HTTP methods are currently supported.');
+    }
+
+    /**
+     * Sets the value of nonce.
+     *
+     * @param string $nonce The server nonce value
+     */
+    private function setNonce(string $nonce = null): void
+    {
+        $this->nonce = $nonce;
+    }
+
+    /**
+     * Sets the value of opaque.
+     *
+     * @param string $opaque The opaque value
+     */
+    private function setOpaque(string $opaque): void
+    {
+        $this->opaque = $opaque;
+    }
+
+    /**
+     * Sets the acceptable value(s) for the quality of protection used by the server. Supported values are auth and auth-int.
+     * TODO: This method should give precedence to using qop=auth-int first as this offers integrity protection.
+     *
+     * @param array $qop an array with the values of qop that the server has specified it will accept
+     *
+     * @throws \InvalidArgumentException if $qop contains any values other than auth-int or auth
+     */
+    private function setQOP(array $qop = []): void
+    {
+        $this->qop = [];
+        foreach ($qop as $protection) {
+            $protection = trim($protection);
+            if ('auth-int' == $protection) {
+                $this->qop[] = 'auth-int';
+            } elseif ('auth' == $protection) {
+                $this->qop[] = 'auth';
+            } else {
+                throw new \InvalidArgumentException('DigestAuthMiddleware: Only auth-int and auth are supported Quality of Protection mechanisms.');
+            }
+        }
+    }
+
+    /**
+     * Sets the value of uri.
+     *
+     * @param string $uri The uri
+     */
+    private function setUri(string $uri = null): void
+    {
+        $this->uri = $uri;
+    }
+
+    /**
+     * If a string contains quotation marks at either end this function will strip them. Otherwise it will remain unchanged.
+     *
+     * @param string $str the string to be stripped of quotation marks
+     *
+     * @return string returns the original string without the quotation marks at either end
+     */
+    private function unquoteString(string $str = null): ?string
+    {
+        if ($str) {
+            if ('"' == substr($str, 0, 1)) {
+                $str = substr($str, 1, \strlen($str) - 1);
+            }
+            if ('"' == substr($str, \strlen($str) - 1, 1)) {
+                $str = substr($str, 0, \strlen($str) - 1);
+            }
+        }
+
+        return $str;
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Middleware/History/Entry.php b/vendor/kriswallsmith/buzz/lib/Middleware/History/Entry.php
new file mode 100644
index 0000000000000000000000000000000000000000..36a0a5d7c3352824cb19de3639963109c62bfe0b
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Middleware/History/Entry.php
@@ -0,0 +1,44 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Middleware\History;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+class Entry
+{
+    private $request;
+
+    private $response;
+
+    private $duration;
+
+    /**
+     * @param RequestInterface  $request  The request
+     * @param ResponseInterface $response The response
+     * @param null|float        $duration The duration in seconds
+     */
+    public function __construct(RequestInterface $request, ResponseInterface $response, float $duration = null)
+    {
+        $this->request = $request;
+        $this->response = $response;
+        $this->duration = $duration;
+    }
+
+    public function getRequest(): RequestInterface
+    {
+        return $this->request;
+    }
+
+    public function getResponse(): ResponseInterface
+    {
+        return $this->response;
+    }
+
+    public function getDuration(): ?float
+    {
+        return $this->duration;
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Middleware/History/Journal.php b/vendor/kriswallsmith/buzz/lib/Middleware/History/Journal.php
new file mode 100644
index 0000000000000000000000000000000000000000..77f0933ea977c0568a6aa74beddeefa43385a262
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Middleware/History/Journal.php
@@ -0,0 +1,97 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Middleware\History;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+class Journal implements \Countable, \IteratorAggregate
+{
+    private $entries = [];
+
+    private $limit = 10;
+
+    public function __construct(int $limit = 10)
+    {
+        $this->limit = $limit;
+    }
+
+    /**
+     * Records an entry in the journal.
+     *
+     * @param RequestInterface  $request  The request
+     * @param ResponseInterface $response The response
+     * @param null|float        $duration The duration in seconds
+     */
+    public function record(RequestInterface $request, ResponseInterface $response, float $duration = null): void
+    {
+        $this->addEntry(new Entry($request, $response, $duration));
+    }
+
+    public function addEntry(Entry $entry): void
+    {
+        array_push($this->entries, $entry);
+        $this->entries = \array_slice($this->entries, $this->getLimit() * -1);
+        end($this->entries);
+    }
+
+    /**
+     * @return Entry[]
+     */
+    public function getEntries(): array
+    {
+        return $this->entries;
+    }
+
+    public function getLast(): ?Entry
+    {
+        $entry = end($this->entries);
+
+        return false === $entry ? null : $entry;
+    }
+
+    public function getLastRequest(): ?RequestInterface
+    {
+        if (null === $entry = $this->getLast()) {
+            return null;
+        }
+
+        return $entry->getRequest();
+    }
+
+    public function getLastResponse(): ?ResponseInterface
+    {
+        if (null === $entry = $this->getLast()) {
+            return null;
+        }
+
+        return $entry->getResponse();
+    }
+
+    public function clear(): void
+    {
+        $this->entries = [];
+    }
+
+    public function count(): int
+    {
+        return \count($this->entries);
+    }
+
+    public function setLimit(int $limit): void
+    {
+        $this->limit = $limit;
+    }
+
+    public function getLimit(): int
+    {
+        return $this->limit;
+    }
+
+    public function getIterator(): \ArrayIterator
+    {
+        return new \ArrayIterator(array_reverse($this->entries));
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Middleware/HistoryMiddleware.php b/vendor/kriswallsmith/buzz/lib/Middleware/HistoryMiddleware.php
new file mode 100644
index 0000000000000000000000000000000000000000..e6c1eafa7f30be0353bf8d214086fc0dae450ba0
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Middleware/HistoryMiddleware.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Middleware;
+
+use Buzz\Middleware\History\Journal;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+class HistoryMiddleware implements MiddlewareInterface
+{
+    private $journal;
+
+    private $startTime;
+
+    public function __construct(Journal $journal)
+    {
+        $this->journal = $journal;
+    }
+
+    public function getJournal(): Journal
+    {
+        return $this->journal;
+    }
+
+    public function handleRequest(RequestInterface $request, callable $next)
+    {
+        $this->startTime = microtime(true);
+
+        return $next($request);
+    }
+
+    public function handleResponse(RequestInterface $request, ResponseInterface $response, callable $next)
+    {
+        $this->journal->record($request, $response, microtime(true) - $this->startTime);
+
+        return $next($request, $response);
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Middleware/LoggerMiddleware.php b/vendor/kriswallsmith/buzz/lib/Middleware/LoggerMiddleware.php
new file mode 100644
index 0000000000000000000000000000000000000000..79f0ea9b3edc20bae165d0a3b49e858dfae0d124
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Middleware/LoggerMiddleware.php
@@ -0,0 +1,47 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Middleware;
+
+use Psr\Http\Message\ResponseInterface;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
+class LoggerMiddleware implements MiddlewareInterface
+{
+    private $logger;
+
+    private $level;
+
+    private $prefix;
+
+    private $startTime;
+
+    /**
+     * @param LoggerInterface $logger
+     * @param string          $level
+     * @param string|null     $prefix
+     */
+    public function __construct(LoggerInterface $logger = null, $level = 'info', $prefix = null)
+    {
+        $this->logger = $logger ?: new NullLogger();
+        $this->level = $level;
+        $this->prefix = $prefix;
+    }
+
+    public function handleRequest(\Psr\Http\Message\RequestInterface $request, callable $next)
+    {
+        $this->startTime = microtime(true);
+
+        return $next($request);
+    }
+
+    public function handleResponse(\Psr\Http\Message\RequestInterface $request, ResponseInterface $response, callable $next)
+    {
+        $seconds = microtime(true) - $this->startTime;
+        $this->logger->log($this->level, sprintf('%sSent "%s %s" in %dms', $this->prefix, $request->getMethod(), $request->getUri(), round($seconds * 1000)));
+
+        return $next($request, $response);
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Middleware/MiddlewareInterface.php b/vendor/kriswallsmith/buzz/lib/Middleware/MiddlewareInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..d24a2fffebdcdeb3b0e31230284e442e37bca444
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Middleware/MiddlewareInterface.php
@@ -0,0 +1,44 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Middleware;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * A middleware gets called twice per request. One time before we send the request
+ * and once after the response is received. A middleware may modify/change the
+ * request and the response. Just be aware that they are immutable.
+ *
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ */
+interface MiddlewareInterface
+{
+    /**
+     * Handle a request.
+     *
+     * End this function by calling:
+     *   <code>
+     *      return $next($request);
+     *   </code
+     *
+     * @param RequestInterface $request
+     * @param callable         $next    next middleware
+     */
+    public function handleRequest(RequestInterface $request, callable $next);
+
+    /**
+     * Handle a response.
+     *
+     * End this function by calling:
+     *   <code>
+     *      return $next($request, $response);
+     *   </code
+     *
+     * @param RequestInterface $request
+     * @param callable         $next    next middleware
+     */
+    public function handleResponse(RequestInterface $request, ResponseInterface $response, callable $next);
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Util/Cookie.php b/vendor/kriswallsmith/buzz/lib/Util/Cookie.php
new file mode 100644
index 0000000000000000000000000000000000000000..a8f286705245814d907b2a5cc9e27daae852e07e
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Util/Cookie.php
@@ -0,0 +1,232 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Util;
+
+use Psr\Http\Message\RequestInterface;
+
+class Cookie
+{
+    const ATTR_DOMAIN = 'domain';
+
+    const ATTR_PATH = 'path';
+
+    const ATTR_SECURE = 'secure';
+
+    const ATTR_MAX_AGE = 'max-age';
+
+    const ATTR_EXPIRES = 'expires';
+
+    protected $name;
+
+    protected $value;
+
+    protected $attributes = [];
+
+    protected $createdAt;
+
+    /**
+     * Constructor.
+     */
+    public function __construct()
+    {
+        $this->createdAt = time();
+    }
+
+    /**
+     * Returns true if the current cookie matches the supplied request.
+     *
+     * @param RequestInterface $request A request object
+     *
+     * @return bool
+     */
+    public function matchesRequest(RequestInterface $request): bool
+    {
+        $uri = $request->getUri();
+        // domain
+        if (!$this->matchesDomain($uri->getHost())) {
+            return false;
+        }
+
+        // path
+        if (!$this->matchesPath($uri->getPath())) {
+            return false;
+        }
+
+        // secure
+        if ($this->hasAttribute(static::ATTR_SECURE) && 'https' !== $uri->getScheme()) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns true of the current cookie has expired.
+     *
+     * Checks the max-age and expires attributes.
+     *
+     * @return bool Whether the current cookie has expired
+     */
+    public function isExpired(): bool
+    {
+        $maxAge = $this->getAttribute(static::ATTR_MAX_AGE);
+        if ($maxAge && time() - $this->getCreatedAt() > $maxAge) {
+            return true;
+        }
+
+        $expires = $this->getAttribute(static::ATTR_EXPIRES);
+        if ($expires && strtotime($expires) < time()) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns true if the current cookie matches the supplied domain.
+     *
+     * @param string $domain A domain hostname
+     *
+     * @return bool
+     */
+    public function matchesDomain(string $domain): bool
+    {
+        $cookieDomain = $this->getAttribute(static::ATTR_DOMAIN);
+
+        if (0 === strpos($cookieDomain, '.')) {
+            $pattern = '/\b'.preg_quote(substr($cookieDomain, 1), '/').'$/i';
+
+            return (bool) preg_match($pattern, $domain);
+        } else {
+            return 0 == strcasecmp($cookieDomain, $domain);
+        }
+    }
+
+    /**
+     * Returns true if the current cookie matches the supplied path.
+     *
+     * @param string $path A path
+     *
+     * @return bool
+     */
+    public function matchesPath(string $path): bool
+    {
+        $needle = $this->getAttribute(static::ATTR_PATH);
+
+        return null === $needle || 0 === strpos($path, $needle);
+    }
+
+    /**
+     * Populates the current cookie with data from the supplied Set-Cookie header.
+     *
+     * @param string $header        A Set-Cookie header
+     * @param string $issuingDomain The domain that issued the header
+     */
+    public function fromSetCookieHeader(string $header, string $issuingDomain): void
+    {
+        list($this->name, $header) = explode('=', $header, 2);
+        if (false === strpos($header, ';')) {
+            $this->value = $header;
+            $header = null;
+        } else {
+            list($this->value, $header) = explode(';', $header, 2);
+        }
+
+        $this->clearAttributes();
+        if (null !== $header) {
+            foreach (array_map('trim', explode(';', trim($header))) as $pair) {
+                if (false === strpos($pair, '=')) {
+                    $name = $pair;
+                    $value = null;
+                } else {
+                    list($name, $value) = explode('=', $pair);
+                }
+
+                $this->setAttribute($name, $value);
+            }
+        }
+
+        if (!$this->getAttribute(static::ATTR_DOMAIN)) {
+            $this->setAttribute(static::ATTR_DOMAIN, $issuingDomain);
+        }
+    }
+
+    /**
+     * Formats a Cookie header for the current cookie.
+     *
+     * @return string An HTTP request Cookie header
+     */
+    public function toCookieHeader(): string
+    {
+        return $this->getName().'='.$this->getValue();
+    }
+
+    public function setName(string $name): void
+    {
+        $this->name = $name;
+    }
+
+    public function getName(): string
+    {
+        return $this->name;
+    }
+
+    public function setValue(string $value): void
+    {
+        $this->value = $value;
+    }
+
+    public function getValue(): string
+    {
+        return $this->value;
+    }
+
+    public function setAttributes(array $attributes)
+    {
+        // attributes are case insensitive
+        $this->attributes = array_change_key_case($attributes);
+    }
+
+    public function setAttribute(string $name, ?string $value): void
+    {
+        $this->attributes[strtolower($name)] = $value;
+    }
+
+    public function getAttributes(): array
+    {
+        return $this->attributes;
+    }
+
+    public function getAttribute(string $name): ?string
+    {
+        $name = strtolower($name);
+
+        if (isset($this->attributes[$name])) {
+            return $this->attributes[$name];
+        }
+
+        return null;
+    }
+
+    public function hasAttribute(string $name): bool
+    {
+        return array_key_exists($name, $this->attributes);
+    }
+
+    public function clearAttributes(): void
+    {
+        $this->setAttributes([]);
+    }
+
+    public function setCreatedAt(int $createdAt): void
+    {
+        $this->createdAt = $createdAt;
+    }
+
+    public function getCreatedAt(): int
+    {
+        return $this->createdAt;
+    }
+}
diff --git a/vendor/kriswallsmith/buzz/lib/Util/CookieJar.php b/vendor/kriswallsmith/buzz/lib/Util/CookieJar.php
new file mode 100644
index 0000000000000000000000000000000000000000..6732e9019ec84aeb08bedc4fb01523abd6afcb4d
--- /dev/null
+++ b/vendor/kriswallsmith/buzz/lib/Util/CookieJar.php
@@ -0,0 +1,94 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Buzz\Util;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+class CookieJar
+{
+    /** @var Cookie[] */
+    private $cookies = [];
+
+    public function clear(): void
+    {
+        $this->cookies = [];
+    }
+
+    public function setCookies(array $cookies): void
+    {
+        $this->cookies = [];
+        foreach ($cookies as $cookie) {
+            $this->addCookie($cookie);
+        }
+    }
+
+    /**
+     * @return Cookie[]
+     */
+    public function getCookies(): array
+    {
+        return $this->cookies;
+    }
+
+    /**
+     * Adds a cookie to the current cookie jar.
+     *
+     * @param Cookie $cookie A cookie object
+     */
+    public function addCookie(Cookie $cookie): void
+    {
+        $this->cookies[] = $cookie;
+    }
+
+    /**
+     * Adds Cookie headers to the supplied request.
+     *
+     * @param RequestInterface $request A request object
+     */
+    public function addCookieHeaders(RequestInterface $request): RequestInterface
+    {
+        foreach ($this->getCookies() as $cookie) {
+            if ($cookie->matchesRequest($request)) {
+                $request = $request->withHeader('Cookie', $cookie->toCookieHeader());
+            }
+        }
+
+        return $request;
+    }
+
+    /**
+     * Processes Set-Cookie headers from a request/response pair.
+     *
+     * @param RequestInterface  $request  A request object
+     * @param ResponseInterface $response A response object
+     */
+    public function processSetCookieHeaders(RequestInterface $request, ResponseInterface $response): void
+    {
+        $host = $request->getUri()->getHost();
+        foreach ($response->getHeader('Set-Cookie') as $header) {
+            $cookie = new Cookie();
+            $cookie->fromSetCookieHeader($header, $host);
+
+            $this->addCookie($cookie);
+        }
+    }
+
+    /**
+     * Removes expired cookies.
+     */
+    public function clearExpiredCookies(): void
+    {
+        $cookies = $this->getCookies();
+        foreach ($cookies as $i => $cookie) {
+            if ($cookie->isExpired()) {
+                unset($cookies[$i]);
+            }
+        }
+
+        $this->clear();
+        $this->setCookies(array_values($cookies));
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/.gitignore b/vendor/m4tthumphrey/php-gitlab-api/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..d1502b087b4d4ede40ddbbf64a8b87a6348b17d8
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/.gitignore
@@ -0,0 +1,2 @@
+vendor/
+composer.lock
diff --git a/vendor/m4tthumphrey/php-gitlab-api/.travis.yml b/vendor/m4tthumphrey/php-gitlab-api/.travis.yml
new file mode 100644
index 0000000000000000000000000000000000000000..97190200d2640da65bc8b33ecd6a0beff1166e1f
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/.travis.yml
@@ -0,0 +1,17 @@
+language: php
+
+sudo: false
+
+php:
+  - 5.3
+  - 5.4
+  - 5.5
+  - 5.6
+  - 7.0
+
+before_script:
+  - travis_retry composer self-update
+  - travis_retry composer install --no-interaction --prefer-source
+
+script:
+  - vendor/bin/phpunit --verbose --coverage-text
diff --git a/vendor/m4tthumphrey/php-gitlab-api/README.md b/vendor/m4tthumphrey/php-gitlab-api/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..b8cbdb352a53aab50dd2c195921cf3f0844a425e
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/README.md
@@ -0,0 +1,96 @@
+A PHP wrapper to be used with [Gitlab's API](https://github.com/gitlabhq/gitlabhq/tree/master/doc/api).
+==============
+
+[![Build Status](https://travis-ci.org/m4tthumphrey/php-gitlab-api.svg?branch=master)](https://travis-ci.org/m4tthumphrey/php-gitlab-api)
+
+Based on [php-github-api](https://github.com/m4tthumphrey/php-github-api) and code from [KnpLabs](https://github.com/KnpLabs/php-github-api).
+
+Installation
+------------
+1. Install Composer
+
+    ```bash
+    $ curl -sS https://getcomposer.org/installer | php
+    $ sudo mv composer.phar /usr/local/bin/composer
+    ```
+
+2. Add the following to your require block in composer.json config.
+
+    > Note: be careful when using the `dev-master` tag as this may have unexpected results depending on your version of
+    Gitlab. See the Versioning section below for more information.
+
+    `php composer.phar require m4tthumphrey/php-gitlab-api:dev-master`
+
+3. Include Composer's autoloader:
+
+    ```php
+    require_once dirname(__DIR__).'/vendor/autoload.php';
+    ```
+
+Versioning
+----------
+
+From the 6.0 stable release of Gitlab, I shall now be matching the client version with the Gitlab version. For example
+when Gitlab 6.1 is released I will release version 6.1.0 of the API client. If I need to make future updates to the client
+before the next API version is released, I'll simply use a 3rd build version - `6.1.1`, `6.1.2` etc for example.
+
+It is recommended that you keep your composer file in sync with whatever version of Gitlab you are currently running:
+if you are using 6.0, you should require `6.0.*`; 6.1 should be `6.1.*`...
+
+General API Usage
+-----------------
+
+```php
+$client = new \Gitlab\Client('http://git.yourdomain.com/api/v3/');               // change here
+$client->authenticate('your_gitlab_token_here', \Gitlab\Client::AUTH_URL_TOKEN); // change here
+
+$project = $client->api('projects')->create('My Project', array(
+  'description' => 'This is a project',
+  'issues_enabled' => false
+));
+
+```
+
+Model Usage
+-----------
+
+You can also use the library in an object oriented manner:
+
+```php
+$client = new \Gitlab\Client('http://git.yourdomain.com/api/v3/');               // change here
+$client->authenticate('your_gitlab_token_here', \Gitlab\Client::AUTH_URL_TOKEN); // change here
+
+# Creating a new project
+$project = \Gitlab\Model\Project::create($client, 'My Project', array(
+  'description' => 'This is my project',
+  'issues_enabled' => false
+));
+
+$project->addHook('http://mydomain.com/hook/push/1');
+
+# Creating a new issue
+$project = new \Gitlab\Model\Project(1, $client);
+$issue = $project->createIssue('This does not work.', array(
+  'description' => 'This doesn\'t work properly. Please fix.',
+  'assignee_id' => 2
+));
+
+# Closing that issue
+$issue->close();
+```
+
+You get the idea! Take a look around ([API methods](https://github.com/m4tthumphrey/php-gitlab-api/tree/master/lib/Gitlab/Api),
+[models](https://github.com/m4tthumphrey/php-gitlab-api/tree/master/lib/Gitlab/Model)) and please feel free to report any bugs.
+
+Framework Integrations
+----------------------
+- **Symfony** - https://github.com/Zeichen32/GitLabApiBundle
+- **Laravel** - https://github.com/vinkla/gitlab
+
+If you have integrated GitLab into a popular PHP framework, let us know!
+
+Contributing
+------------
+
+There are many parts of Gitlab that I have not added to this as it was originally created for personal use, hence the
+lack of tests. Feel free to fork and add new functionality and tests, I'll gladly accept decent pull requests.
diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/HttpClient.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/HttpClient.php
new file mode 100644
index 0000000000000000000000000000000000000000..7a84bfb436af3bfad1ff1a6d01f6bcd58ea39644
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/HttpClient.php
@@ -0,0 +1,231 @@
+<?php namespace Gitlab\HttpClient;
+
+use Buzz\Client\ClientInterface;
+use Buzz\Listener\ListenerInterface;
+use Buzz\Message\Form\FormUpload;
+
+use Gitlab\Exception\ErrorException;
+use Gitlab\Exception\RuntimeException;
+use Gitlab\HttpClient\Listener\ErrorListener;
+use Gitlab\HttpClient\Message\Request;
+use Gitlab\HttpClient\Message\Response;
+use Gitlab\HttpClient\Message\FormRequest;
+
+/**
+ * Performs requests on Gitlab API. API documentation should be self-explanatory.
+ *
+ * @author Joseph Bielawski <stloyd@gmail.com>
+ * @author Matt Humphrey <matt@m4tt.co>
+ */
+class HttpClient implements HttpClientInterface
+{
+    /**
+     * @var array
+     */
+    protected $options = array(
+        'user_agent'  => 'php-gitlab-api (http://github.com/m4tthumphrey/php-gitlab-api)',
+        'timeout'     => 10,
+    );
+
+    /**
+     * @var string
+     */
+    protected $baseUrl;
+
+    /**
+     * @var ListenerInterface[]
+     */
+    protected $listeners = array();
+    /**
+     * @var array
+     */
+    protected $headers = array();
+
+    /**
+     * @var Response
+     */
+    private $lastResponse;
+
+    /**
+     * @var Request
+     */
+    private $lastRequest;
+
+    /**
+     * @param string $baseUrl
+     * @param array $options
+     * @param ClientInterface $client
+     */
+    public function __construct($baseUrl, array $options, ClientInterface $client)
+    {
+        $this->baseUrl = $baseUrl;
+        $this->options = array_merge($this->options, $options);
+        $this->client  = $client;
+
+        $this->addListener(new ErrorListener($this->options));
+
+        $this->clearHeaders();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setOption($name, $value)
+    {
+        $this->options[$name] = $value;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setHeaders(array $headers)
+    {
+        $this->headers = array_merge($this->headers, $headers);
+    }
+
+    /**
+     * Clears used headers
+     */
+    public function clearHeaders()
+    {
+        $this->headers = array();
+    }
+
+    /**
+     * @param ListenerInterface $listener
+     */
+    public function addListener(ListenerInterface $listener)
+    {
+        $this->listeners[get_class($listener)] = $listener;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function get($path, array $parameters = array(), array $headers = array())
+    {
+        if (0 < count($parameters)) {
+            $path .= (false === strpos($path, '?') ? '?' : '&').http_build_query($parameters, '', '&');
+        }
+
+        return $this->request($path, array(), 'GET', $headers);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function post($path, array $parameters = array(), array $headers = array(), array $files = array())
+    {
+        return $this->request($path, $parameters, 'POST', $headers, $files);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function patch($path, array $parameters = array(), array $headers = array())
+    {
+        return $this->request($path, $parameters, 'PATCH', $headers);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function delete($path, array $parameters = array(), array $headers = array())
+    {
+        return $this->request($path, $parameters, 'DELETE', $headers);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function put($path, array $parameters = array(), array $headers = array())
+    {
+        return $this->request($path, $parameters, 'PUT', $headers);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function request($path, array $parameters = array(), $httpMethod = 'GET', array $headers = array(), array $files = array())
+    {
+        $path = trim($this->baseUrl.$path, '/');
+
+        $request = $this->createRequest($httpMethod, $path, $parameters, $headers, $files);
+
+        $hasListeners = 0 < count($this->listeners);
+        if ($hasListeners) {
+            foreach ($this->listeners as $listener) {
+                $listener->preSend($request);
+            }
+        }
+
+        $response = new Response();
+
+        try {
+            $this->client->send($request, $response);
+        } catch (\LogicException $e) {
+            throw new ErrorException($e->getMessage());
+        } catch (\RuntimeException $e) {
+            throw new RuntimeException($e->getMessage());
+        }
+
+        $this->lastRequest  = $request;
+        $this->lastResponse = $response;
+
+        if ($hasListeners) {
+            foreach ($this->listeners as $listener) {
+                $listener->postSend($request, $response);
+            }
+        }
+
+        return $response;
+    }
+
+    /**
+     * @return Request
+     */
+    public function getLastRequest()
+    {
+        return $this->lastRequest;
+    }
+
+    /**
+     * @return Response
+     */
+    public function getLastResponse()
+    {
+        return $this->lastResponse;
+    }
+
+    /**
+     * @param string $httpMethod
+     * @param string $url
+     * @param array $parameters
+     * @param array $headers
+     * @param array $files
+     *
+     * @return FormRequest|Request
+     */
+    private function createRequest($httpMethod, $url, array $parameters, array $headers, array $files)
+    {
+        if (empty($files)) {
+            $request = new Request($httpMethod);
+            $request->setContent(http_build_query($parameters));
+        } else {
+            $request = new FormRequest($httpMethod);
+            foreach ($parameters as $name => $value) {
+                $request->setField($name, $value);
+            }
+
+            foreach ($files as $name => $file) {
+                $upload = new FormUpload($file);
+                $request->setField($name, $upload);
+            }
+        }
+        $request->setHeaders($this->headers);
+        $request->fromUrl($url);
+        $request->addHeaders($headers);
+
+        return $request;
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/HttpClientInterface.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/HttpClientInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..674987becc99373bf338170cb5727f010323b59d
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/HttpClientInterface.php
@@ -0,0 +1,98 @@
+<?php namespace Gitlab\HttpClient;
+
+use Gitlab\Exception\InvalidArgumentException;
+
+/**
+ * Performs requests on Gitlab API. API documentation should be self-explanatory.
+ *
+ * @author Joseph Bielawski <stloyd@gmail.com>
+ * @author Matt Humphrey <matt@m4tt.co>
+ */
+interface HttpClientInterface
+{
+    /**
+     * Send a GET request
+     *
+     * @param string $path       Request path
+     * @param array  $parameters GET Parameters
+     * @param array  $headers    Reconfigure the request headers for this call only
+     *
+     * @return array Data
+     */
+    public function get($path, array $parameters = array(), array $headers = array());
+
+    /**
+     * Send a POST request
+     *
+     * @param string $path Request path
+     * @param array $parameters POST Parameters
+     * @param array $headers Reconfigure the request headers for this call only
+     * @param array $files Files paths of files to upload
+     *
+     * @return array Data
+     */
+    public function post($path, array $parameters = array(), array $headers = array(), array $files = array());
+
+    /**
+     * Send a PATCH request
+     *
+     * @param string $path       Request path
+     * @param array  $parameters PATCH Parameters
+     * @param array  $headers    Reconfigure the request headers for this call only
+     *
+     * @return array Data
+     */
+    public function patch($path, array $parameters = array(), array $headers = array());
+
+    /**
+     * Send a PUT request
+     *
+     * @param string $path       Request path
+     * @param array  $parameters PUT Parameters
+     * @param array  $headers    Reconfigure the request headers for this call only
+     *
+     * @return array Data
+     */
+    public function put($path, array $parameters = array(), array $headers = array());
+
+    /**
+     * Send a DELETE request
+     *
+     * @param string $path       Request path
+     * @param array  $parameters DELETE Parameters
+     * @param array  $headers    Reconfigure the request headers for this call only
+     *
+     * @return array Data
+     */
+    public function delete($path, array $parameters = array(), array $headers = array());
+
+    /**
+     * Send a request to the server, receive a response,
+     * decode the response and returns an associative array
+     *
+     * @param string $path       Request API path
+     * @param array  $parameters Parameters
+     * @param string $httpMethod HTTP method to use
+     * @param array  $headers    Request headers
+     *
+     * @return array Data
+     */
+    public function request($path, array $parameters = array(), $httpMethod = 'GET', array $headers = array());
+
+    /**
+     * Change an option value.
+     *
+     * @param string $name  The option name
+     * @param mixed  $value The value
+     *
+     * @throws InvalidArgumentException
+     */
+    public function setOption($name, $value);
+
+    /**
+     * Set HTTP headers
+     *
+     * @param array $headers
+     */
+    public function setHeaders(array $headers);
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/AuthListener.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/AuthListener.php
new file mode 100644
index 0000000000000000000000000000000000000000..27164112280cd918457cbaa4af00b0fc4afc453c
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/AuthListener.php
@@ -0,0 +1,95 @@
+<?php namespace Gitlab\HttpClient\Listener;
+
+use Gitlab\Client;
+use Gitlab\Exception\InvalidArgumentException;
+
+use Buzz\Listener\ListenerInterface;
+use Buzz\Message\MessageInterface;
+use Buzz\Message\RequestInterface;
+use Buzz\Util\Url;
+
+/**
+ * @author Joseph Bielawski <stloyd@gmail.com>
+ * @author Matt Humphrey <matt@m4tt.co>
+ */
+class AuthListener implements ListenerInterface
+{
+    /**
+     * @var string
+     */
+    private $method;
+
+    /**
+     * @var string
+     */
+    private $token;
+
+    /**
+     * @var string|null
+     */
+    private $sudo;
+
+    /**
+     * @param string      $method
+     * @param string      $token
+     * @param string|null $sudo
+     */
+    public function __construct($method, $token, $sudo = null)
+    {
+        $this->method  = $method;
+        $this->token = $token;
+        $this->sudo = $sudo;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws InvalidArgumentException
+     */
+    public function preSend(RequestInterface $request)
+    {
+        // Skip by default
+        if (null === $this->method) {
+            return;
+        }
+
+        switch ($this->method) {
+            case Client::AUTH_HTTP_TOKEN:
+                $request->addHeader('PRIVATE-TOKEN: '.$this->token);
+                if (!is_null($this->sudo)) {
+                    $request->addHeader('SUDO: '.$this->sudo);
+                }
+                break;
+
+            case Client::AUTH_URL_TOKEN:
+                $url  = $request->getUrl();
+
+                $query = array(
+                    'private_token' => $this->token
+                );
+
+                if (!is_null($this->sudo)) {
+                    $query['sudo'] = $this->sudo;
+                }
+
+                $url .= (false === strpos($url, '?') ? '?' : '&').utf8_encode(http_build_query($query, '', '&'));
+
+                $request->fromUrl(new Url($url));
+                break;
+
+            case Client::AUTH_OAUTH_TOKEN:
+                $request->addHeader('Authorization: Bearer '.$this->token);
+                if (!is_null($this->sudo)) {
+                    $request->addHeader('SUDO: '.$this->sudo);
+                }
+                break;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function postSend(RequestInterface $request, MessageInterface $response)
+    {
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/ErrorListener.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/ErrorListener.php
new file mode 100644
index 0000000000000000000000000000000000000000..cb2acc68b21af5b17ef4b4b822333c4f7da757fd
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/ErrorListener.php
@@ -0,0 +1,97 @@
+<?php namespace Gitlab\HttpClient\Listener;
+
+use Buzz\Listener\ListenerInterface;
+use Buzz\Message\MessageInterface;
+use Buzz\Message\RequestInterface;
+use Gitlab\Exception\ErrorException;
+use Gitlab\Exception\RuntimeException;
+
+/**
+ * @author Joseph Bielawski <stloyd@gmail.com>
+ * @author Matt Humphrey <git@m4tt.co>
+ */
+class ErrorListener implements ListenerInterface
+{
+    /**
+     * @var array
+     */
+    private $options;
+
+    /**
+     * @param array $options
+     */
+    public function __construct(array $options)
+    {
+        $this->options = $options;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function preSend(RequestInterface $request)
+    {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function postSend(RequestInterface $request, MessageInterface $response)
+    {
+        /** @var $response \Gitlab\HttpClient\Message\Response */
+        if ($response->isClientError() || $response->isServerError()) {
+            $content = $response->getContent();
+            if (is_array($content) && isset($content['message'])) {
+                if (400 == $response->getStatusCode()) {
+                    $message = $this->parseMessage($content['message']);
+
+                    throw new ErrorException($message, 400);
+                }
+            }
+
+            $errorMessage = null;
+            if (isset($content['error'])) {
+                $errorMessage = $content['error'];
+                if (is_array($content['error'])) {
+                    $errorMessage = implode("\n", $content['error']);
+                }
+            } elseif (isset($content['message'])) {
+                $errorMessage = $this->parseMessage($content['message']);
+            } else {
+                $errorMessage = $content;
+            }
+
+            throw new RuntimeException($errorMessage, $response->getStatusCode());
+        }
+    }
+
+    /**
+     * @param mixed $message
+     * @return string
+     */
+    protected function parseMessage($message)
+    {
+        $string = $message;
+
+        if (is_array($message)) {
+            $format = '"%s" %s';
+            $errors = array();
+
+            foreach ($message as $field => $messages) {
+                if (is_array($messages)) {
+                    $messages = array_unique($messages);
+                    foreach ($messages as $error) {
+                        $errors[] = sprintf($format, $field, $error);
+                    }
+                } elseif (is_integer($field)) {
+                    $errors[] = $messages;
+                } else {
+                    $errors[] = sprintf($format, $field, $messages);
+                }
+            }
+
+            $string = implode(', ', $errors);
+        }
+
+        return $string;
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/PaginationListener.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/PaginationListener.php
new file mode 100644
index 0000000000000000000000000000000000000000..80ba39ea96586fc667251b33f7824cb3b3dd4f3d
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Listener/PaginationListener.php
@@ -0,0 +1,38 @@
+<?php namespace Gitlab\HttpClient\Listener;
+
+use Buzz\Listener\ListenerInterface;
+use Buzz\Message\MessageInterface;
+use Buzz\Message\RequestInterface;
+
+class PaginationListener implements ListenerInterface
+{
+    /**
+     * {@inheritDoc}
+     */
+    public function preSend(RequestInterface $request)
+    {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function postSend(RequestInterface $request, MessageInterface $response)
+    {
+        $header = $response->getHeader('Link');
+
+        if (empty($header)) {
+            return null;
+        }
+
+        $pagination = array();
+        foreach (explode(',', $header) as $link) {
+            preg_match('/<(.*)>; rel="(.*)"/i', trim($link, ','), $match);
+
+            if (3 === count($match)) {
+                $pagination[$match[2]] = $match[1];
+            }
+        }
+
+        $response->setPagination($pagination);
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/FormRequest.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/FormRequest.php
new file mode 100644
index 0000000000000000000000000000000000000000..ecd05e17d8edbb957f530b78e12c66bd4c42a935
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/FormRequest.php
@@ -0,0 +1,8 @@
+<?php namespace Gitlab\HttpClient\Message;
+
+use Buzz\Message\Form\FormRequest as BaseFormRequest;
+
+class FormRequest extends BaseFormRequest
+{
+
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/Request.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/Request.php
new file mode 100644
index 0000000000000000000000000000000000000000..41a950f35f3292a50ec7a06a4d4440ce3a20eadc
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/Request.php
@@ -0,0 +1,8 @@
+<?php namespace Gitlab\HttpClient\Message;
+
+use Buzz\Message\Request as BaseRequest;
+
+class Request extends BaseRequest
+{
+
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/Response.php b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/Response.php
new file mode 100644
index 0000000000000000000000000000000000000000..e5746e0b8c26ff6aa5517c9fd2363a7b02c717a6
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/lib/Gitlab/HttpClient/Message/Response.php
@@ -0,0 +1,48 @@
+<?php namespace Gitlab\HttpClient\Message;
+
+use Buzz\Message\Response as BaseResponse;
+
+class Response extends BaseResponse
+{
+    /**
+     * @var array pagination
+     * See PaginationListener
+     */
+    protected $pagination = array();
+
+    /**
+     * @return mixed
+     */
+    public function getPagination()
+    {
+        return $this->pagination;
+    }
+
+    /**
+     * @param array $pagination
+     */
+    public function setPagination(array $pagination)
+    {
+        $this->pagination = $pagination;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getContent()
+    {
+        $response = parent::getContent();
+
+        if ($this->getHeader('Content-Type') === 'application/json') {
+            $content  = json_decode($response, true);
+    
+            if (JSON_ERROR_NONE !== json_last_error()) {
+                return $response;
+            }
+    
+            return $content;
+        }
+
+        return $response;
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/phpunit.xml.dist b/vendor/m4tthumphrey/php-gitlab-api/phpunit.xml.dist
new file mode 100644
index 0000000000000000000000000000000000000000..e01386f15e1d1938f4f166c60ffdb9a2d34f418c
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/phpunit.xml.dist
@@ -0,0 +1,25 @@
+<?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"
+				 bootstrap="test/bootstrap.php"
+	>
+	<testsuites>
+		<testsuite name="php-gitlab-api Test Suite">
+			<directory>./test/Gitlab/</directory>
+		</testsuite>
+	</testsuites>
+
+	<filter>
+		<whitelist>
+			<directory suffix=".php">./lib/Gitlab/</directory>
+		</whitelist>
+	</filter>
+</phpunit>
\ No newline at end of file
diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/AbstractApiTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/AbstractApiTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..aa1eeaea2885e0a0e712c67c1f53414bfb52493b
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/AbstractApiTest.php
@@ -0,0 +1,177 @@
+<?php namespace Gitlab\Tests\Api;
+
+use Gitlab\Api\AbstractApi;
+use Gitlab\Client;
+use Gitlab\HttpClient\Message\Response;
+
+class AbstractApiTest extends TestCase
+{
+    /**
+     * @test
+     */
+    public function shouldPassGETRequestToClient()
+    {
+        $response = $this->getResponse('value');
+
+        $httpClient = $this->getHttpMock();
+        $httpClient
+            ->expects($this->any())
+            ->method('get')
+            ->with('/path', array('param1' => 'param1value'), array('header1' => 'header1value'))
+            ->will($this->returnValue($response));
+
+        $client = $this->getClientMock();
+        $client->setHttpClient($httpClient);
+
+        $api = $this->getAbstractApiObject($client);
+        $this->assertEquals('value', $api->get('/path', array('param1' => 'param1value'), array('header1' => 'header1value')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldPassPOSTRequestToClient()
+    {
+        $response = $this->getResponse('value');
+
+        $httpClient = $this->getHttpMock();
+        $httpClient
+            ->expects($this->any())
+            ->method('post')
+            ->with('/path', array('param1' => 'param1value'), array('header1' => 'header1value'))
+            ->will($this->returnValue($response));
+
+        $client = $this->getClientMock();
+        $client->setHttpClient($httpClient);
+
+        $api = $this->getAbstractApiObject($client);
+        $this->assertEquals('value', $api->post('/path', array('param1' => 'param1value'), array('header1' => 'header1value')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldPassPUTRequestToClient()
+    {
+        $response = $this->getResponse('value');
+
+        $httpClient = $this->getHttpMock();
+        $httpClient
+            ->expects($this->any())
+            ->method('put')
+            ->with('/path', array('param1' => 'param1value'), array('header1' => 'header1value'))
+            ->will($this->returnValue($response));
+
+        $client = $this->getClientMock();
+        $client->setHttpClient($httpClient);
+
+        $api = $this->getAbstractApiObject($client);
+        $this->assertEquals('value', $api->put('/path', array('param1' => 'param1value'), array('header1' => 'header1value')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldPassDELETERequestToClient()
+    {
+        $response = $this->getResponse('value');
+
+        $httpClient = $this->getHttpMock();
+        $httpClient
+            ->expects($this->any())
+            ->method('delete')
+            ->with('/path', array('param1' => 'param1value'), array('header1' => 'header1value'))
+            ->will($this->returnValue($response));
+
+        $client = $this->getClientMock();
+        $client->setHttpClient($httpClient);
+
+        $api = $this->getAbstractApiObject($client);
+        $this->assertEquals('value', $api->delete('/path', array('param1' => 'param1value'), array('header1' => 'header1value')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldPassPATCHRequestToClient()
+    {
+        $response = $this->getResponse('value');
+
+        $httpClient = $this->getHttpMock();
+        $httpClient
+            ->expects($this->any())
+            ->method('patch')
+            ->with('/path', array('param1' => 'param1value'), array('header1' => 'header1value'))
+            ->will($this->returnValue($response));
+
+        $client = $this->getClientMock();
+        $client->setHttpClient($httpClient);
+
+        $api = $this->getAbstractApiObject($client);
+        $this->assertEquals('value', $api->patch('/path', array('param1' => 'param1value'), array('header1' => 'header1value')));
+    }
+
+    /**
+     * @param mixed $value
+     * @return Response
+     */
+    protected function getResponse($value)
+    {
+        $response = new Response();
+        $response->setContent($value);
+
+        return $response;
+    }
+
+    /**
+     * @param Client $client
+     * @return AbstractApiTestInstance
+     */
+    protected function getAbstractApiObject(Client $client)
+    {
+        return new AbstractApiTestInstance($client);
+    }
+}
+
+class AbstractApiTestInstance extends AbstractApi
+{
+    /**
+     * {@inheritDoc}
+     */
+    public function get($path, array $parameters = array(), $requestHeaders = array())
+    {
+        return parent::get($path, $parameters, $requestHeaders);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function post($path, array $parameters = array(), $requestHeaders = array(), array $files = array())
+    {
+        return parent::post($path, $parameters, $requestHeaders, $files);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function patch($path, array $parameters = array(), $requestHeaders = array())
+    {
+        return parent::patch($path, $parameters, $requestHeaders);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function put($path, array $parameters = array(), $requestHeaders = array())
+    {
+        return parent::put($path, $parameters, $requestHeaders);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function delete($path, array $parameters = array(), $requestHeaders = array())
+    {
+        return parent::delete($path, $parameters, $requestHeaders);
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ApiTestCase.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ApiTestCase.php
new file mode 100644
index 0000000000000000000000000000000000000000..a695d97cb8007b5ef05014099192368a099403bc
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ApiTestCase.php
@@ -0,0 +1,23 @@
+<?php namespace Gitlab\Tests\Api;
+
+abstract class ApiTestCase extends TestCase
+{
+    abstract protected function getApiClass();
+
+    /**
+     * @param array $methods
+     * @return \PHPUnit_Framework_MockObject_MockObject|mixed
+     */
+    protected function getApiMock($methods = array())
+    {
+        $client = $this->getClientMock();
+
+        $methods = array_merge(array('get', 'post', 'postRaw', 'patch', 'delete', 'put', 'head'), $methods);
+
+        return $this->getMockBuilder($this->getApiClass())
+            ->setMethods($methods)
+            ->setConstructorArgs(array($client))
+            ->getMock()
+        ;
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/DeployKeysTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/DeployKeysTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..a17dd312c3d16fa85fc8f5f4d3e24ef3e4755c06
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/DeployKeysTest.php
@@ -0,0 +1,51 @@
+<?php namespace Gitlab\Tests\Api;
+
+class DeployKeysTest extends ApiTestCase
+{
+    /**
+     * @test
+     */
+    public function shouldGetAllDeployKeys()
+    {
+        $expectedArray = $this->getMultipleDeployKeysData();
+
+        $api = $this->getMultipleDeployKeysRequestMock('deploy_keys', $expectedArray);
+
+        $this->assertEquals($expectedArray, $api->all());
+    }
+
+    protected function getMultipleDeployKeysRequestMock($path, $expectedArray = array(), $page = 1, $per_page = 20, $order_by = 'id', $sort = 'asc')
+    {
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with($path, array('page' => $page, 'per_page' => $per_page, 'order_by' => $order_by, 'sort' => $sort))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        return $api;
+    }
+
+    protected function getMultipleDeployKeysData()
+    {
+        return array(
+            array(
+                'id' => 1,
+                'title' => 'Public key',
+                'key' => 'ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=',
+                'created_at' => '2013-10-02T10:12:29Z'
+            ),
+            array(
+                'id' => 3,
+                'title' => 'Another Public key',
+                'key' => 'ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=',
+                'created_at' => '2013-10-02T11:12:29Z'
+            )
+        );
+    }
+
+    protected function getApiClass()
+    {
+        return 'Gitlab\Api\DeployKeys';
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/GroupsTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/GroupsTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..20fd1fd58f3590d329e2fcae590ae36829e9628d
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/GroupsTest.php
@@ -0,0 +1,264 @@
+<?php namespace Gitlab\Tests\Api;
+
+use Gitlab\Api\AbstractApi;
+
+class GroupsTest extends ApiTestCase
+{
+    /**
+     * @test
+     */
+    public function shouldGetAllGroups()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'name' => 'A group'),
+            array('id' => 2, 'name' => 'Another group'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('groups', array('page' => 1, 'per_page' => 10))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->all(1, 10));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldNotNeedPaginationWhenGettingGroups()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'name' => 'A group'),
+            array('id' => 2, 'name' => 'Another group'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('groups', array('page' => 1, 'per_page' => AbstractApi::PER_PAGE))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->all());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldSearchGroups()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'name' => 'A group'),
+            array('id' => 2, 'name' => 'Another group'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('groups?search=some%20group', array('page' => 1, 'per_page' => AbstractApi::PER_PAGE))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->search('some group'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldSearchGroupsWithPagination()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'name' => 'A group'),
+            array('id' => 2, 'name' => 'Another group'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('groups?search=group', array('page' => 2, 'per_page' => 5))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->search('group', 2, 5));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldShowGroup()
+    {
+        $expectedArray = array('id' => 1, 'name' => 'A group');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('groups/1')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->show(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateGroup()
+    {
+        $expectedArray = array('id' => 1, 'name' => 'A new group');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('groups', array('name' => 'A new group', 'path' => 'a-new-group', 'description' => null, 'visibility_level' => 0))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->create('A new group', 'a-new-group'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateGroupWithDescriptionAndVisLevel()
+    {
+        $expectedArray = array('id' => 1, 'name' => 'A new group', 'visibility_level' => 2);
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('groups', array('name' => 'A new group', 'path' => 'a-new-group', 'description' => 'Description', 'visibility_level' => 2))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->create('A new group', 'a-new-group', 'Description', 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldUpdateGroup()
+    {
+        $expectedArray = array('id' => 3, 'name' => 'Group name', 'path' => 'group-path');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('groups/3', array('name' => 'Group name', 'path' => 'group-path'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->update(3, array('name' => 'Group name', 'path' => 'group-path')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldTransferProjectToGroup()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('groups/1/projects/2')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->transfer(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetMembers()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'name' => 'Matt'),
+            array('id' => 2, 'name' => 'Bob')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('groups/1/members')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->members(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldAddMember()
+    {
+        $expectedArray = array('id' => 1, 'name' => 'Matt');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('groups/1/members', array('user_id' => 2, 'access_level' => 3))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->addMember(1, 2, 3));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldSaveMember()
+    {
+        $expectedArray = array('id' => 1, 'name' => 'Matt');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('groups/1/members/2', array('access_level' => 4))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->saveMember(1, 2, 4));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldRemoveMember()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('groups/1/members/2')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->removeMember(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldRemoveGroup()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('groups/1')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->remove(1));
+    }
+
+    protected function getApiClass()
+    {
+        return 'Gitlab\Api\Groups';
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/IssuesTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/IssuesTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..8c656f003a2878d7a11d25e0098cdd181499cea2
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/IssuesTest.php
@@ -0,0 +1,279 @@
+<?php namespace Gitlab\Tests\Api;
+
+use Gitlab\Api\AbstractApi;
+
+class IssuesTest extends ApiTestCase
+{
+    /**
+     * @test
+     */
+    public function shouldGetAllIssues()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'title' => 'An issue'),
+            array('id' => 2, 'title' => 'Another issue'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('issues', array('page' => 1, 'per_page' => AbstractApi::PER_PAGE))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->all());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetProjectIssuesWithPagination()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'title' => 'An issue'),
+            array('id' => 2, 'title' => 'Another issue'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/issues', array('page' => 2, 'per_page' => 5))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->all(1, 2, 5));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetProjectIssuesWithParams()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'title' => 'An issue'),
+            array('id' => 2, 'title' => 'Another issue'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/issues', array('page' => 2, 'per_page' => 5, 'order_by' => 'created_at', 'sort' => 'desc', 'labels' => 'foo,bar', 'state' => 'open'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->all(1, 2, 5, array('order_by' => 'created_at', 'sort' => 'desc', 'labels' => 'foo,bar', 'state' => 'open')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldShowIssue()
+    {
+        $expectedArray = array('id' => 2, 'title' => 'Another issue');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/issues?iid=2')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->show(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateIssue()
+    {
+        $expectedArray = array('id' => 3, 'title' => 'A new issue');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/issues', array('title' => 'A new issue', 'labels' => 'foo,bar'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->create(1, array('title' => 'A new issue', 'labels' => 'foo,bar')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldUpdateIssue()
+    {
+        $expectedArray = array('id' => 2, 'title' => 'A renamed issue');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('projects/1/issues/2', array('title' => 'A renamed issue', 'labels' => 'foo'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->update(1, 2, array('title' => 'A renamed issue', 'labels' => 'foo')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetIssueComments()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'body' => 'A comment'),
+            array('id' => 2, 'body' => 'Another comment')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/issues/2/notes')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->showComments(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetIssueComment()
+    {
+        $expectedArray = array('id' => 3, 'body' => 'A new comment');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/issues/2/notes/3')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->showComment(1, 2, 3));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateComment()
+    {
+        $expectedArray = array('id' => 3, 'body' => 'A new comment');
+
+        $api = $this->getApiMock();
+        $api->expects($this->exactly(2))
+            ->method('post')
+            ->with('projects/1/issues/2/notes', array('body' => 'A new comment'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->addComment(1, 2, array('body' => 'A new comment')));
+        $this->assertEquals($expectedArray, $api->addComment(1, 2, 'A new comment'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldUpdateComment()
+    {
+        $expectedArray = array('id' => 3, 'body' => 'An edited comment');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('projects/1/issues/2/notes/3', array('body' => 'An edited comment'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->updateComment(1, 2, 3, 'An edited comment'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldSetTimeEstimate()
+    {
+        $expectedArray = array('time_estimate' => 14400, 'total_time_spent' => 0, 'human_time_estimate' => '4h', 'human_total_time_spent' => null);
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/issues/2/time_estimate', array('duration' => '4h'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->setTimeEstimate(1, 2, '4h'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldResetTimeEstimate()
+    {
+        $expectedArray = array('time_estimate' => 0, 'total_time_spent' => 0, 'human_time_estimate' => null, 'human_total_time_spent' => null);
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/issues/2/reset_time_estimate')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->resetTimeEstimate(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldAddSpentTime()
+    {
+        $expectedArray = array('time_estimate' => 0, 'total_time_spent' => 14400, 'human_time_estimate' => null, 'human_total_time_spent' => '4h');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/issues/2/add_spent_time', array('duration' => '4h'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->addSpentTime(1, 2, '4h'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldResetSpentTime()
+    {
+        $expectedArray = array('time_estimate' => 0, 'total_time_spent' => 0, 'human_time_estimate' => null, 'human_total_time_spent' => null);
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/issues/2/reset_spent_time')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->resetSpentTime(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetIssueTimeStats()
+    {
+        $expectedArray = array('time_estimate' => 14400, 'total_time_spent' => 5400, 'human_time_estimate' => '4h', 'human_total_time_spent' => '1h 30m');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/issues/2/time_stats')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->getTimeStats(1, 2));
+    }
+
+    protected function getApiClass()
+    {
+        return 'Gitlab\Api\Issues';
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/KeysTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/KeysTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..2f9e94b7a58b5c4c4d55e8068eb29f2049c258df
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/KeysTest.php
@@ -0,0 +1,26 @@
+<?php namespace Gitlab\Tests\Api;
+
+use Gitlab\Api\AbstractApi;
+
+class KeysTest extends ApiTestCase
+{
+    /**
+     * @test
+     */
+    public function shouldShowKey()
+    {
+        $expectedArray = array('id' => 1, 'title' => 'A key', 'key' => 'ssh-rsa key', 'created_at' => '2016-01-01T01:00:00.000Z');
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('keys/1')
+            ->will($this->returnValue($expectedArray));
+
+        $this->assertEquals($expectedArray, $api->show(1));
+    }
+
+    protected function getApiClass()
+    {
+        return 'Gitlab\Api\Keys';
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/MergeRequestsTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/MergeRequestsTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..bd6e6eb2b70523e3ea2b0ad365c2312e34b19daf
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/MergeRequestsTest.php
@@ -0,0 +1,368 @@
+<?php namespace Gitlab\Tests\Api;
+
+use Gitlab\Api\AbstractApi;
+use Gitlab\Api\MergeRequests;
+
+class MergeRequestsTest extends ApiTestCase
+{
+    /**
+     * @test
+     */
+    public function shouldGetMergeRequestListWithDefaultParams()
+    {
+        $expectedArray = $this->getMultipleMergeRequestsData();
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/merge_requests', array('page' => 1, 'per_page' => AbstractApi::PER_PAGE, 'state' => MergeRequests::STATE_ALL, 'order_by' => MergeRequests::ORDER_BY, 'sort' => MergeRequests::SORT))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->getList(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetAll()
+    {
+        $expectedArray = $this->getMultipleMergeRequestsData();
+
+        $api = $this->getApiMock(array('getList'));
+        $api->expects($this->once())
+            ->method('getList')
+            ->with(1, MergeRequests::STATE_ALL, 1, AbstractApi::PER_PAGE, MergeRequests::ORDER_BY, MergeRequests::SORT)
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->all(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetAllWithParams()
+    {
+        $expectedArray = $this->getMultipleMergeRequestsData();
+
+        $api = $this->getApiMock(array('getList'));
+        $api->expects($this->once())
+            ->method('getList')
+            ->with(1, MergeRequests::STATE_ALL, 2, 5,  'title', 'desc')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->all(1, 2, 5, 'title', 'desc'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetMerged()
+    {
+        $expectedArray = $this->getMultipleMergeRequestsData();
+
+        $api = $this->getApiMock(array('getList'));
+        $api->expects($this->once())
+            ->method('getList')
+            ->with(1, MergeRequests::STATE_MERGED, 1, AbstractApi::PER_PAGE, MergeRequests::ORDER_BY, MergeRequests::SORT)
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->merged(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetMergedWithParams()
+    {
+        $expectedArray = $this->getMultipleMergeRequestsData();
+
+        $api = $this->getApiMock(array('getList'));
+        $api->expects($this->once())
+            ->method('getList')
+            ->with(1, MergeRequests::STATE_MERGED, 3, 15, 'updated_at', 'asc')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->merged(1, 3, 15, 'updated_at', 'asc'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetOpened()
+    {
+        $expectedArray = $this->getMultipleMergeRequestsData();
+
+        $api = $this->getApiMock(array('getList'));
+        $api->expects($this->once())
+            ->method('getList')
+            ->with(1, MergeRequests::STATE_OPENED, 1, AbstractApi::PER_PAGE, MergeRequests::ORDER_BY, MergeRequests::SORT)
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->opened(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetOpenedWithParams()
+    {
+        $expectedArray = $this->getMultipleMergeRequestsData();
+
+        $api = $this->getApiMock(array('getList'));
+        $api->expects($this->once())
+            ->method('getList')
+            ->with(1, MergeRequests::STATE_OPENED, 2, 4, 'title', 'desc')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->opened(1, 2, 4, 'title', 'desc'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetClosed()
+    {
+        $expectedArray = $this->getMultipleMergeRequestsData();
+
+        $api = $this->getApiMock(array('getList'));
+        $api->expects($this->once())
+            ->method('getList')
+            ->with(1, MergeRequests::STATE_CLOSED, 1, AbstractApi::PER_PAGE, MergeRequests::ORDER_BY, MergeRequests::SORT)
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->closed(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetClosedWithParams()
+    {
+        $expectedArray = $this->getMultipleMergeRequestsData();
+
+        $api = $this->getApiMock(array('getList'));
+        $api->expects($this->once())
+            ->method('getList')
+            ->with(1, MergeRequests::STATE_CLOSED, 2, 4, 'title', 'desc')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->closed(1, 2, 4, 'title', 'desc'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldShowMergeRequest()
+    {
+        $expectedArray = array('id' => 2, 'name' => 'A merge request');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/merge_request/2')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->show(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateMergeRequestWithoutOptionalParams()
+    {
+        $expectedArray = array('id' => 3, 'title' => 'Merge Request');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/merge_requests', array(
+                'title' => 'Merge Request',
+                'target_branch' => 'master',
+                'source_branch' => 'develop',
+                'description' => null,
+                'assignee_id' => null,
+                'target_project_id' => null
+            ))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->create(1, 'develop', 'master', 'Merge Request'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateMergeRequestWithOptionalParams()
+    {
+        $expectedArray = array('id' => 3, 'title' => 'Merge Request');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/merge_requests', array(
+                'title' => 'Merge Request',
+                'target_branch' => 'master',
+                'source_branch' => 'develop',
+                'description' => 'Some changes',
+                'assignee_id' => 6,
+                'target_project_id' => 20
+            ))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->create(1, 'develop', 'master', 'Merge Request', 6, 20, 'Some changes'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldUpdateMergeRequest()
+    {
+        $expectedArray = array('id' => 2, 'title' => 'Updated title');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('projects/1/merge_request/2', array('title' => 'Updated title', 'description' => 'No so many changes now', 'state_event' => 'close'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->update(1, 2, array(
+            'title' => 'Updated title',
+            'description' => 'No so many changes now',
+            'state_event' => 'close'
+        )));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldMergeMergeRequest()
+    {
+        $expectedArray = array('id' => 2, 'title' => 'Updated title');
+
+        $api = $this->getApiMock();
+        $api->expects($this->exactly(2))
+            ->method('put')
+            ->with('projects/1/merge_request/2/merge', array('merge_commit_message' => 'Accepted'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->merge(1, 2, 'Accepted'));
+        $this->assertEquals($expectedArray, $api->merge(1, 2, array('merge_commit_message' => 'Accepted')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetMergeRequestNotes()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'body' => 'A comment'),
+            array('id' => 2, 'body' => 'Another comment')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/merge_requests/2/notes')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->showNotes(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetMergeRequestComments()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'note' => 'A comment'),
+            array('id' => 2, 'note' => 'Another comment')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/merge_request/2/comments')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->showComments(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldAddMergeRequestComment()
+    {
+        $expectedArray = array('id' => 2, 'title' => 'A comment');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/merge_request/2/comments', array('note' => 'A comment'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->addComment(1, 2, 'A comment'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetMergeRequestChanges()
+    {
+        $expectedArray = array('id' => 1, 'title' => 'A merge request');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/merge_request/2/changes')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->changes(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetMergeRequestByIid()
+    {
+        $expectedArray = array('id' => 1, 'title' => 'A merge request');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/merge_requests', array('iid' => 2))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->getByIid(1, 2));
+    }
+
+    protected function getMultipleMergeRequestsData()
+    {
+        return array(
+            array('id' => 1, 'title' => 'A merge request'),
+            array('id' => 2, 'title' => 'Another merge request')
+        );
+    }
+
+    protected function getApiClass()
+    {
+        return 'Gitlab\Api\MergeRequests';
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/MilestonesTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/MilestonesTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..e22c2370ff9dfbc49db5087284329da980253438
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/MilestonesTest.php
@@ -0,0 +1,100 @@
+<?php namespace Gitlab\Tests\Api;
+
+class MilestonesTest extends ApiTestCase
+{
+    /**
+     * @test
+     */
+    public function shouldGetAllMilestones()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'title' => 'A milestone'),
+            array('id' => 2, 'title' => 'Another milestone'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/milestones')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->all(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldShowMilestone()
+    {
+        $expectedArray = array('id' => 1, 'name' => 'A milestone');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/milestones/2')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->show(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateMilestone()
+    {
+        $expectedArray = array('id' => 3, 'title' => 'A new milestone');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/milestones', array('description' => 'Some text', 'title' => 'A new milestone'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->create(1, array('description' => 'Some text', 'title' => 'A new milestone')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldUpdateMilestone()
+    {
+        $expectedArray = array('id' => 3, 'title' => 'Updated milestone');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('projects/1/milestones/3', array('title' => 'Updated milestone', 'due_date' => '2015-04-01', 'state_event' => 'close'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->update(1, 3, array('title' => 'Updated milestone', 'due_date' => '2015-04-01', 'state_event' => 'close')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetMilestonesIssues()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'title' => 'An issue'),
+            array('id' => 2, 'title' => 'Another issue'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/milestones/3/issues')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->issues(1, 3));
+    }
+
+    protected function getApiClass()
+    {
+        return 'Gitlab\Api\Milestones';
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ProjectNamespacesTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ProjectNamespacesTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..1feb5ff1f4146d0b9e020a2ec4f601364aebf5ce
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ProjectNamespacesTest.php
@@ -0,0 +1,70 @@
+<?php namespace Gitlab\Tests\Api;
+
+use Gitlab\Api\AbstractApi;
+
+class ProjectNamespacesTest extends ApiTestCase
+{
+    /**
+     * @test
+     */
+    public function shouldGetAllNamespaces()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'name' => 'bespokes'),
+            array('id' => 2, 'name' => 'internal')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('namespaces', array('page' => 1, 'per_page' => 10))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->all(1, 10));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldNotNeedPaginationWhenGettingNamespaces()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'name' => 'bespokes'),
+            array('id' => 2, 'name' => 'internal')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('namespaces', array('page' => 1, 'per_page' => AbstractApi::PER_PAGE))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->all());
+    }
+    /**
+     * @test
+     */
+    public function shouldSearchNamespaces()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'name' => 'bespokes'),
+            array('id' => 2, 'name' => 'internal')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('namespaces', array('search' => 'term', 'page' => 1, 'per_page' => 10))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->search('term', 1, 10));
+    }
+
+    protected function getApiClass()
+    {
+        return 'Gitlab\Api\ProjectNamespaces';
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ProjectsTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ProjectsTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..47bb26432f1ddc721cf609e2b524ffcd2a31c65c
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/ProjectsTest.php
@@ -0,0 +1,1098 @@
+<?php namespace Gitlab\Tests\Api;
+
+use Gitlab\Api\AbstractApi;
+use Gitlab\Api\Projects;
+
+class ProjectsTest extends ApiTestCase
+{
+    /**
+     * @test
+     */
+    public function shouldGetAllProjects()
+    {
+        $expectedArray = $this->getMultipleProjectsData();
+
+        $api = $this->getMultipleProjectsRequestMock('projects/all', $expectedArray);
+
+        $this->assertEquals($expectedArray, $api->all());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetAllProjectsSortedByName()
+    {
+        $expectedArray = $this->getMultipleProjectsData();
+
+        $api = $this->getMultipleProjectsRequestMock('projects/all', $expectedArray, 1, 5, 'name', 'asc');
+
+        $this->assertEquals($expectedArray, $api->all(1, 5, 'name'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldNotNeedPaginationWhenGettingProjects()
+    {
+        $expectedArray = $this->getMultipleProjectsData();
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/all', array('page' => 1, 'per_page' => AbstractApi::PER_PAGE, 'order_by' => Projects::ORDER_BY, 'sort' => Projects::SORT))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->all());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetAccessibleProjects()
+    {
+        $expectedArray = $this->getMultipleProjectsData();
+
+        $api = $this->getMultipleProjectsRequestMock('projects', $expectedArray, 2, 7);
+
+        $this->assertEquals($expectedArray, $api->accessible(2, 7));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetOwnedProjects()
+    {
+        $expectedArray = $this->getMultipleProjectsData();
+
+        $api = $this->getMultipleProjectsRequestMock('projects?owned=1', $expectedArray, 3, 50);
+
+        $this->assertEquals($expectedArray, $api->owned(3, 50));
+    }
+
+
+    /**
+     * @test
+     */
+    public function shouldSearchProjects()
+    {
+        $expectedArray = $this->getMultipleProjectsData();
+
+        $api = $this->getMultipleProjectsRequestMock('projects/search/a%20project', $expectedArray);
+        $this->assertEquals($expectedArray, $api->search('a project'));
+
+        $api = $this->getMultipleProjectsRequestMock('projects/search/a%2Eproject', $expectedArray);
+        $this->assertEquals($expectedArray, $api->search('a.project'));
+
+        $api = $this->getMultipleProjectsRequestMock('projects/search/a%2Fproject', $expectedArray);
+        $this->assertEquals($expectedArray, $api->search('a/project'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldShowProject()
+    {
+        $expectedArray = array('id' => 1, 'name' => 'Project Name');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->show(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateProject()
+    {
+        $expectedArray = array('id' => 1, 'name' => 'Project Name');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects', array('name' => 'Project Name', 'issues_enabled' => true))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->create('Project Name', array(
+            'issues_enabled' => true
+        )));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldUpdateProject()
+    {
+        $expectedArray = array('id' => 1, 'name' => 'Updated Name');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('projects/1', array('name' => 'Updated Name', 'issues_enabled' => true))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->update(1, array(
+            'name' => 'Updated Name',
+            'issues_enabled' => true
+        )));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldArchiveProject()
+    {
+        $expectedArray = array('id' => 1, 'archived' => true);
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/archive')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->archive(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldUnarchiveProject()
+    {
+        $expectedArray = array('id' => 1, 'archived' => false);
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/unarchive')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->unarchive(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateProjectForUser()
+    {
+        $expectedArray = array('id' => 1, 'name' => 'Project Name');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/user/1', array('name' => 'Project Name', 'issues_enabled' => true))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->createForUser(1, 'Project Name', array(
+            'issues_enabled' => true
+        )));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldRemoveProject()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('projects/1')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->remove(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetBuilds()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'status' => 'success'),
+            array('id' => 2, 'status' => 'failed')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/builds')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->builds(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetBuildsWithScope()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'status' => 'success'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/builds', array('scope' => 'success'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->builds(1, 'success'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetBuildsWithMultipleScopes()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'status' => 'success'),
+            array('id' => 1, 'status' => 'failed'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/builds', array('scope' => array('success', 'failed')))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->builds(1, array('success', 'failed')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetBuild()
+    {
+        $expectedArray = array('id' => 2);
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/builds/2')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->build(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetTrace()
+    {
+        $expectedString = 'runner trace of a specific build';
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/builds/2/trace')
+            ->will($this->returnValue($expectedString))
+        ;
+
+        $this->assertEquals($expectedString, $api->trace(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetPipelines()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'status' => 'success','ref' => 'new-pipeline'),
+            array('id' => 2, 'status' => 'failed', 'ref' => 'new-pipeline'),
+            array('id' => 3, 'status' => 'pending', 'ref'=> 'test-pipeline')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/pipelines')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->pipelines(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetPipeline()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'status' => 'success','ref' => 'new-pipeline'),
+            array('id' => 2, 'status' => 'failed', 'ref' => 'new-pipeline'),
+            array('id' => 3, 'status' => 'pending', 'ref'=> 'test-pipeline')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/pipelines/3')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->pipeline(1, 3));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreatePipeline()
+    {
+        $expectedArray = array(
+            array('id' => 4, 'status' => 'created', 'ref'=> 'test-pipeline')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/pipelines', array('ref' => 'test-pipeline'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->createPipeline(1, 'test-pipeline'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldRetryPipeline()
+    {
+        $expectedArray = array(
+            array('id' => 5, 'status' => 'pending', 'ref'=> 'test-pipeline')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/pipelines/4/retry')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->retryPipeline(1, 4));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCancelPipeline()
+    {
+        $expectedArray = array(
+            array('id' => 6, 'status' => 'cancelled', 'ref'=> 'test-pipeline')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/pipelines/6/cancel')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->cancelPipeline(1, 6));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetMembers()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'name' => 'Matt'),
+            array('id' => 2, 'name' => 'Bob')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/members')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->members(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetMembersWithQuery()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'name' => 'Matt')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/members', array('query' => 'at'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->members(1, 'at'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetMember()
+    {
+        $expectedArray = array('id' => 2, 'name' => 'Matt');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/members/2')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->member(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldAddMember()
+    {
+        $expectedArray = array('id' => 1, 'name' => 'Matt');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/members', array('user_id' => 2, 'access_level' => 3))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->addMember(1, 2, 3));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldSaveMember()
+    {
+        $expectedArray = array('id' => 1, 'name' => 'Matt');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('projects/1/members/2', array('access_level' => 4))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->saveMember(1, 2, 4));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldRemoveMember()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('projects/1/members/2')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->removeMember(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetHooks()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'name' => 'Test hook'),
+            array('id' => 2, 'name' => 'Another hook'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/hooks')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->hooks(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetHook()
+    {
+        $expectedArray = array('id' => 2, 'name' => 'Another hook');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/hooks/2')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->hook(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldAddHook()
+    {
+        $expectedArray = array('id' => 3, 'name' => 'A new hook', 'url' => 'http://www.example.com');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/hooks', array('url' => 'http://www.example.com', 'push_events' => true, 'issues_events' => true, 'merge_requests_events' => true))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->addHook(1, 'http://www.example.com', array('push_events' => true, 'issues_events' => true, 'merge_requests_events' => true)));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldAddHookWithOnlyUrl()
+    {
+        $expectedArray = array('id' => 3, 'name' => 'A new hook', 'url' => 'http://www.example.com');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/hooks', array('url' => 'http://www.example.com', 'push_events' => true))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->addHook(1, 'http://www.example.com'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldAddHookWithoutPushEvents()
+    {
+        $expectedArray = array('id' => 3, 'name' => 'A new hook', 'url' => 'http://www.example.com');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/hooks', array('url' => 'http://www.example.com', 'push_events' => false))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->addHook(1, 'http://www.example.com', array('push_events' => false)));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldUpdateHook()
+    {
+        $expectedArray = array('id' => 3, 'name' => 'A new hook', 'url' => 'http://www.example.com');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('projects/1/hooks/3', array('url' => 'http://www.example-test.com', 'push_events' => false))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->updateHook(1, 3, array('url' => 'http://www.example-test.com', 'push_events' => false)));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldRemoveHook()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('projects/1/hooks/2')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->removeHook(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetKeys()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'title' => 'test-key'),
+            array('id' => 2, 'title' => 'another-key')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/keys')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->keys(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetKey()
+    {
+        $expectedArray = array('id' => 2, 'title' => 'another-key');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/keys/2')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->key(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldAddKey()
+    {
+        $expectedArray = array('id' => 3, 'title' => 'new-key');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/keys', array('title' => 'new-key', 'key' => '...'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->addKey(1, 'new-key', '...'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldRemoveKey()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('projects/1/keys/3')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->removeKey(1, 3));
+    }
+
+    /**
+     * @test
+     */
+    public function shoudEnableKey()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/keys/3/enable')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->enableKey(1, 3));
+    }
+
+    /**
+     * @test
+     */
+    public function shoudDisableKey()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('projects/1/keys/3/disable')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->disableKey(1, 3));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetEvents()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'title' => 'An event'),
+            array('id' => 2, 'title' => 'Another event')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/events', array(
+                'page' => 1,
+                'per_page' => AbstractApi::PER_PAGE
+            ))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->events(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetEventsWithPagination()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'title' => 'An event'),
+            array('id' => 2, 'title' => 'Another event')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/events', array(
+                'page' => 2,
+                'per_page' => 15
+            ))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->events(1, 2, 15));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetLabels()
+    {
+        $expectedArray = array(
+            array('name' => 'bug', 'color' => '#000000'),
+            array('name' => 'feature', 'color' => '#ff0000')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/labels')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->labels(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldAddLabel()
+    {
+        $expectedArray = array('name' => 'bug', 'color' => '#000000');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/labels', array('name' => 'wont-fix', 'color' => '#ffffff'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->addLabel(1, array('name' => 'wont-fix', 'color' => '#ffffff')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldUpdateLabel()
+    {
+        $expectedArray = array('name' => 'bug', 'color' => '#00ffff');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('projects/1/labels', array('name' => 'bug', 'new_name' => 'big-bug', 'color' => '#00ffff'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->updateLabel(1, array('name' => 'bug', 'new_name' => 'big-bug', 'color' => '#00ffff')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldRemoveLabel()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('projects/1/labels', array('name' => 'bug'))
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->removeLabel(1, 'bug'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateForkRelation()
+    {
+        $expectedArray = array('project_id' => 1, 'forked_id' => 2);
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/fork/2')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->createForkRelation(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldRemoveForkRelation()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('projects/2/fork')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->removeForkRelation(2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldSetService()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('projects/1/services/hipchat', array('param' => 'value'))
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->setService(1, 'hipchat', array('param' => 'value')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldRemoveService()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('projects/1/services/hipchat')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->removeService(1, 'hipchat'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetVariables()
+    {
+        $expectedArray = array(
+            array('key' => 'ftp_username', 'value' => 'ftp'),
+            array('key' => 'ftp_password', 'value' => 'somepassword')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/variables')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->variables(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetVariable()
+    {
+        $expectedArray = array('key' => 'ftp_username', 'value' => 'ftp');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/variables/ftp_username')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->variable(1, 'ftp_username'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldAddVariable()
+    {
+        $expectedKey   = 'ftp_port';
+        $expectedValue = '21';
+
+        $expectedArray = array(
+            'key'   => $expectedKey,
+            'value' => $expectedValue,
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/variables', $expectedArray)
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->addVariable(1, $expectedKey, $expectedValue));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldUpdateVariable()
+    {
+        $expectedKey   = 'ftp_port';
+        $expectedValue = '22';
+
+        $expectedArray = array(
+            'key'   => 'ftp_port',
+            'value' => '22',
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('projects/1/variables/'.$expectedKey, array('value' => $expectedValue))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->updateVariable(1, $expectedKey, $expectedValue));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldRemoveVariable()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('projects/1/variables/ftp_password')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->removeVariable(1, 'ftp_password'));
+    }
+
+    protected function getMultipleProjectsRequestMock($path, $expectedArray = array(), $page = 1, $per_page = 20, $order_by = 'created_at', $sort = 'asc')
+    {
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with($path, array('page' => $page, 'per_page' => $per_page, 'order_by' => $order_by, 'sort' => $sort))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        return $api;
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetDeployments()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'sha' => '0000001'),
+            array('id' => 2, 'sha' => '0000002'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/deployments', array(
+                'page' => 1,
+                'per_page' => AbstractApi::PER_PAGE
+            ))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->deployments(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetDeploymentsWithPagination()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'sha' => '0000001'),
+            array('id' => 2, 'sha' => '0000002'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/deployments', array(
+                'page' => 2,
+                'per_page' => 15
+            ))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->deployments(1, 2, 15));
+    }
+
+    protected function getMultipleProjectsData()
+    {
+        return array(
+            array('id' => 1, 'name' => 'A project'),
+            array('id' => 2, 'name' => 'Another project')
+        );
+    }
+
+    protected function getApiClass()
+    {
+        return 'Gitlab\Api\Projects';
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/RepositoriesTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/RepositoriesTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..a6bffd100c20268850d754ec4226c5aeaeafd027
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/RepositoriesTest.php
@@ -0,0 +1,708 @@
+<?php namespace Gitlab\Tests\Api;
+
+use Gitlab\Api\AbstractApi;
+
+class RepositoriesTest extends ApiTestCase
+{
+    /**
+     * @test
+     */
+    public function shouldGetBranches()
+    {
+        $expectedArray = array(
+            array('name' => 'master'),
+            array('name' => 'develop')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/repository/branches')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->branches(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetBranch()
+    {
+        $expectedArray = array('name' => 'master');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/repository/branches/master')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->branch(1, 'master'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateBranch()
+    {
+        $expectedArray = array('name' => 'feature');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/repository/branches', array('branch_name' => 'feature', 'ref' => 'master'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->createBranch(1, 'feature', 'master'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldDeleteBranch()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('projects/1/repository/branches/master')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->deleteBranch(1, 'master'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldProtectBranch()
+    {
+        $expectedArray = array('name' => 'master');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('projects/1/repository/branches/master/protect', array('developers_can_push' => false, 'developers_can_merge' => false))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->protectBranch(1, 'master'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldProtectBranchWithPermissions()
+    {
+        $expectedArray = array('name' => 'master');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('projects/1/repository/branches/master/protect', array('developers_can_push' => true, 'developers_can_merge' => true))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->protectBranch(1, 'master', true, true));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldUnprotectBranch()
+    {
+        $expectedArray = array('name' => 'master');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('projects/1/repository/branches/master/unprotect')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->unprotectBranch(1, 'master'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetTags()
+    {
+        $expectedArray = array(
+            array('name' => '1.0'),
+            array('name' => '1.1')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/repository/tags')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->tags(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateTag()
+    {
+        $expectedArray = array('name' => '1.0');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/repository/tags', array(
+                'tag_name' => '1.0',
+                'ref' => 'abcd1234',
+                'message' => '1.0 release'
+            ))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->createTag(1, '1.0', 'abcd1234', '1.0 release'));
+    }
+
+	/**
+	 * @test
+	 */
+	public function shouldCreateRelease() {
+		$project_id  = 1;
+		$tagName     = 'sometag';
+		$description = '1.0 release';
+
+		$expectedArray = array( 'name' => $tagName );
+
+		$api = $this->getApiMock();
+		$api->expects( $this->once())
+		    ->method('post')
+		    ->with( 'projects/' . $project_id . '/repository/tags/' . $tagName . '/release', array(
+			    'id' => $project_id,
+			    'tag_name' => $tagName,
+			    'description' => $description
+		    ))
+		    ->will($this->returnValue($expectedArray))
+		;
+
+		$this->assertEquals( $expectedArray, $api->createRelease( $project_id, $tagName, $description ) );
+	}
+
+	/**
+	 * @test
+	 */
+	public function shouldUpdateRelease() {
+		$project_id  = 1;
+		$tagName     = 'sometag';
+		$description = '1.0 release';
+
+		$expectedArray = array( 'description' => $tagName );
+
+		$api = $this->getApiMock();
+		$api->expects( $this->once())
+		    ->method('put')
+		    ->with( 'projects/' . $project_id . '/repository/tags/' . $tagName . '/release', array(
+			    'id' => $project_id,
+			    'tag_name' => $tagName,
+			    'description' => $description
+		    ))
+		    ->will($this->returnValue($expectedArray))
+		;
+
+		$this->assertEquals( $expectedArray, $api->updateRelease( $project_id, $tagName, $description ) );
+	}
+
+    /**
+     * @test
+     */
+    public function shouldGetCommits()
+    {
+        $expectedArray = array(
+            array('id' => 'abcd1234', 'title' => 'A commit'),
+            array('id' => 'efgh5678', 'title' => 'Another commit')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/repository/commits', array('page' => 0, 'per_page' => AbstractApi::PER_PAGE, 'ref_name' => null))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->commits(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetCommitBuilds()
+    {
+        $expectedArray = array(
+            array('id' => 'abcd1234', 'status' => 'failed'),
+            array('id' => 'efgh5678', 'status' => 'success')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/repository/commits/abcd12345/builds', array('page' => 0, 'per_page' => AbstractApi::PER_PAGE, 'scope' => null))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->commitBuilds(1, 'abcd12345'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetCommitBuildsWithScope()
+    {
+        $expectedArray = array(
+            array('id' => 'abcd1234', 'status' => 'success'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/repository/commits/abcd12345/builds', array('page' => 0, 'per_page' => AbstractApi::PER_PAGE, 'scope' => 'success'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->commitBuilds(1, 'abcd12345', 'success'));
+    }
+
+
+    /**
+     * @test
+     */
+    public function shouldGetCommitsWithParams()
+    {
+        $expectedArray = array(
+            array('id' => 'abcd1234', 'title' => 'A commit'),
+            array('id' => 'efgh5678', 'title' => 'Another commit')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/repository/commits', array('page' => 2, 'per_page' => 25, 'ref_name' => 'master'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->commits(1, 2, 25, 'master'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetCommit()
+    {
+        $expectedArray = array('id' => 'abcd1234', 'title' => 'A commit');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/repository/commits/abcd1234')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->commit(1, 'abcd1234'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetCommitComments()
+    {
+        $expectedArray = array(
+            array('note' => 'A commit message'),
+            array('note' => 'Another commit message')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/repository/commits/abcd1234/comments')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->commitComments(1, 'abcd1234'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateCommitComment()
+    {
+        $expectedArray = array('id' => 2, 'title' => 'A new comment');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/repository/commits/abcd1234/comments', array('note' => 'A new comment'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->createCommitComment(1, 'abcd1234', 'A new comment'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateCommitCommentWithParams()
+    {
+        $expectedArray = array('id' => 2, 'title' => 'A new comment');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/repository/commits/abcd1234/comments', array(
+                'note' => 'A new comment',
+                'path' => '/some/file.txt',
+                'line' => 123, 'line_type' => 'old'
+            ))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->createCommitComment(1, 'abcd1234', 'A new comment', array(
+            'path' => '/some/file.txt',
+            'line' => 123,
+            'line_type' => 'old'
+        )));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCompare()
+    {
+        $expectedArray = array('commit' => 'object');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/repository/compare?from=master&to=feature')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->compare(1, 'master', 'feature'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetDiff()
+    {
+        $expectedArray = array(
+            array('diff' => '--- ...'),
+            array('diff' => '+++ ...')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/repository/commits/abcd1234/diff')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->diff(1, 'abcd1234'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetTree()
+    {
+        $expectedArray = array(
+            array('name' => 'file1.txt'),
+            array('name' => 'file2.csv')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/repository/tree')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->tree(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetTreeWithParams()
+    {
+        $expectedArray = array(
+            array('name' => 'dir/file1.txt'),
+            array('name' => 'dir/file2.csv')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/repository/tree', array('path' => 'dir/', 'ref_name' => 'master'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->tree(1, array('path' => 'dir/', 'ref_name' => 'master')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetBlob()
+    {
+        $expectedString = 'something in a file';
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/repository/commits/abcd1234/blob', array('filepath' => 'dir/file1.txt'))
+            ->will($this->returnValue($expectedString))
+        ;
+
+        $this->assertEquals($expectedString, $api->blob(1, 'abcd1234', 'dir/file1.txt'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetFile()
+    {
+        $expectedArray = array('file_name' => 'file1.txt', 'file_path' => 'dir/file1.txt');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/repository/files', array('file_path' => 'dir/file1.txt', 'ref' => 'abcd1234'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->getFile(1, 'dir/file1.txt', 'abcd1234'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateFile()
+    {
+        $expectedArray = array('file_name' => 'file1.txt', 'file_path' => 'dir/file1.txt');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/repository/files', array(
+                'file_path' => 'dir/file1.txt',
+                'branch_name' => 'master',
+                'encoding' => null,
+                'content' => 'some contents',
+                'commit_message' => 'Added new file',
+                'author_email' => null,
+                'author_name' => null,
+            ))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->createFile(1, 'dir/file1.txt', 'some contents', 'master', 'Added new file'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateFileWithEncoding()
+    {
+        $expectedArray = array('file_name' => 'file1.txt', 'file_path' => 'dir/file1.txt');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/repository/files', array(
+                'file_path' => 'dir/file1.txt',
+                'branch_name' => 'master',
+                'encoding' => 'text',
+                'content' => 'some contents',
+                'commit_message' => 'Added new file',
+                'author_email' => null,
+                'author_name' => null,
+            ))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->createFile(1, 'dir/file1.txt', 'some contents', 'master', 'Added new file', 'text'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateFileWithAuthor()
+    {
+        $expectedArray = array('file_name' => 'file1.txt', 'file_path' => 'dir/file1.txt');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/repository/files', array(
+                'file_path' => 'dir/file1.txt',
+                'branch_name' => 'master',
+                'encoding' => null,
+                'content' => 'some contents',
+                'commit_message' => 'Added new file',
+                'author_email' => 'gitlab@example.com',
+                'author_name' => 'GitLab User',
+            ))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->createFile(1, 'dir/file1.txt', 'some contents', 'master', 'Added new file', null, 'gitlab@example.com', 'GitLab User'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldUpdateFile()
+    {
+        $expectedArray = array('file_name' => 'file1.txt', 'file_path' => 'dir/file1.txt');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('projects/1/repository/files', array(
+                'file_path' => 'dir/file1.txt',
+                'branch_name' => 'master',
+                'encoding' => null,
+                'content' => 'some new contents',
+                'commit_message' => 'Updated new file',
+                'author_email' => null,
+                'author_name' => null,
+            ))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->updateFile(1, 'dir/file1.txt', 'some new contents', 'master', 'Updated new file'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldUpdateFileWithEncoding()
+    {
+        $expectedArray = array('file_name' => 'file1.txt', 'file_path' => 'dir/file1.txt');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('projects/1/repository/files', array(
+                'file_path' => 'dir/file1.txt',
+                'branch_name' => 'master',
+                'encoding' => 'base64',
+                'content' => 'some new contents',
+                'commit_message' => 'Updated file',
+                'author_email' => null,
+                'author_name' => null,
+            ))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->updateFile(1, 'dir/file1.txt', 'some new contents', 'master', 'Updated file', 'base64'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldUpdateFileWithAuthor()
+    {
+        $expectedArray = array('file_name' => 'file1.txt', 'file_path' => 'dir/file1.txt');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('projects/1/repository/files', array(
+                'file_path' => 'dir/file1.txt',
+                'branch_name' => 'master',
+                'encoding' => null,
+                'content' => 'some new contents',
+                'commit_message' => 'Updated file',
+                'author_email' => 'gitlab@example.com',
+                'author_name' => 'GitLab User',
+            ))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->updateFile(1, 'dir/file1.txt', 'some new contents', 'master', 'Updated file', null, 'gitlab@example.com', 'GitLab User'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldDeleteFile()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('projects/1/repository/files', array(
+                'file_path' => 'dir/file1.txt',
+                'branch_name' => 'master',
+                'commit_message' => 'Deleted file',
+                'author_email' => null,
+                'author_name' => null,
+            ))
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->deleteFile(1, 'dir/file1.txt', 'master', 'Deleted file'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldDeleteFileWithAuthor()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('projects/1/repository/files', array(
+                'file_path' => 'dir/file1.txt',
+                'branch_name' => 'master',
+                'commit_message' => 'Deleted file',
+                'author_email' => 'gitlab@example.com',
+                'author_name' => 'GitLab User',
+            ))
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->deleteFile(1, 'dir/file1.txt', 'master', 'Deleted file', 'gitlab@example.com', 'GitLab User'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetContributors()
+    {
+        $expectedArray = array(
+            array('name' => 'Matt'),
+            array('name' => 'Bob')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/repository/contributors')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->contributors(1));
+    }
+
+    protected function getApiClass()
+    {
+        return 'Gitlab\Api\Repositories';
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/SnippetsTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/SnippetsTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..694aac5959ddecfade4f6b1bedf6d32580003517
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/SnippetsTest.php
@@ -0,0 +1,114 @@
+<?php namespace Gitlab\Tests\Api;
+
+class SnippetsTest extends ApiTestCase
+{
+    /**
+     * @test
+     */
+    public function shouldGetAllSnippets()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'title' => 'A snippet'),
+            array('id' => 2, 'title' => 'Another snippet'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/snippets')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->all(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldShowSnippet()
+    {
+        $expectedArray = array('id' => 2, 'title' => 'Another snippet');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/snippets/2')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->show(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateSnippet()
+    {
+        $expectedArray = array('id' => 3, 'title' => 'A new snippet');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/snippets', array('title' => 'A new snippet', 'code' => 'A file', 'file_name' => 'file.txt'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->create(1, 'A new snippet', 'file.txt', 'A file'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldUpdateSnippet()
+    {
+        $expectedArray = array('id' => 3, 'title' => 'Updated snippet');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('projects/1/snippets/3', array('title' => 'Updated snippet', 'code' => 'New content', 'file_name' => 'new_file.txt'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->update(1, 3, array('file_name' => 'new_file.txt', 'code' => 'New content', 'title' => 'Updated snippet')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldShowContent()
+    {
+        $expectedString = 'New content';
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/snippets/3/raw')
+            ->will($this->returnValue($expectedString))
+        ;
+
+        $this->assertEquals($expectedString, $api->content(1, 3));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldRemoveSnippet()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('projects/1/snippets/3')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->remove(1, 3));
+    }
+
+    protected function getApiClass()
+    {
+        return 'Gitlab\Api\Snippets';
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/SystemHooksTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/SystemHooksTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..3bf16bce6bcd948fad1f0d3c280fc4a4a478e7cb
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/SystemHooksTest.php
@@ -0,0 +1,80 @@
+<?php namespace Gitlab\Tests\Api;
+
+class SystemHooksTest extends ApiTestCase
+{
+    /**
+     * @test
+     */
+    public function shouldGetAllHooks()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'url' => 'http://www.example.com'),
+            array('id' => 2, 'url' => 'http://www.example.org'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('hooks')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->all());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateHook()
+    {
+        $expectedArray = array('id' => 3, 'url' => 'http://www.example.net');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('hooks', array('url' => 'http://www.example.net'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->create('http://www.example.net'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldTestHook()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('hooks/3')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->test(3));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldRemoveHook()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('hooks/3')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->remove(3));
+    }
+
+    protected function getApiClass()
+    {
+        return 'Gitlab\Api\SystemHooks';
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/TagsTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/TagsTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b6aaafa6fcaef360599d0818b6455275ebe2383b
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/TagsTest.php
@@ -0,0 +1,87 @@
+<?php namespace Gitlab\Tests\Api;
+
+use Gitlab\Api\AbstractApi;
+
+class TagsTest extends ApiTestCase
+{
+    /**
+     * @test
+     */
+    public function shouldGetAllTags()
+    {
+        $expectedArray = array(
+            array('name' => 'v1.0.0'),
+            array('name' => 'v1.1.0'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/repository/tags')
+            ->will($this->returnValue($expectedArray));
+        $this->assertEquals($expectedArray, $api->all(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldShowTag()
+    {
+        $expectedArray = array(
+            array('name' => 'v1.0.0'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/repository/tags/v1.0.0')
+            ->will($this->returnValue($expectedArray));
+        $this->assertEquals($expectedArray, $api->show(1, 'v1.0.0'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateTag()
+    {
+        $expectedArray = array(
+            array('name' => 'v1.1.0'),
+        );
+
+        $params = array(
+            'id'       => 1,
+            'tag_name' => 'v1.1.0',
+            'ref'      => 'ref/heads/master'
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('projects/1/repository/tags', $params)
+            ->will($this->returnValue($expectedArray));
+
+        $this->assertEquals($expectedArray, $api->create(1, $params));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldRemoveTag()
+    {
+        $expectedArray = array(
+            array('name' => 'v1.1.0'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('projects/1/repository/tags/v1.1.0')
+            ->will($this->returnValue($expectedArray));
+        $this->assertEquals($expectedArray, $api->remove(1, 'v1.1.0'));
+    }
+
+    protected function getApiClass()
+    {
+        return 'Gitlab\Api\Tags';
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/TestCase.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/TestCase.php
new file mode 100644
index 0000000000000000000000000000000000000000..cba51e5e26a9bf8cdc1554ae094450706f3eef9b
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/TestCase.php
@@ -0,0 +1,38 @@
+<?php namespace Gitlab\Tests\Api;
+
+use Buzz\Client\Curl;
+
+use Gitlab\Client;
+use Gitlab\HttpClient\HttpClientInterface;
+
+abstract class TestCase extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @return Client
+     */
+    protected function getClientMock()
+    {
+        return new Client($this->getHttpMock());
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|HttpClientInterface
+     */
+    protected function getHttpMock()
+    {
+        return $this->getMock('Gitlab\HttpClient\HttpClient', array(), array(null, array(), $this->getHttpClientMock()));
+    }
+
+    /**
+     * @return \PHPUnit_Framework_MockObject_MockObject|Curl
+     */
+    protected function getHttpClientMock()
+    {
+        $httpClient = $this->getMock('Buzz\Client\Curl', array('send'));
+        $httpClient
+            ->expects($this->any())
+            ->method('send');
+
+        return $httpClient;
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/UsersTest.php b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/UsersTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..a1441da0089ff4af29ffcfcb2bb3e2943bbde6c5
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/test/Gitlab/Tests/Api/UsersTest.php
@@ -0,0 +1,459 @@
+<?php namespace Gitlab\Tests\Api;
+
+use Gitlab\Api\AbstractApi;
+
+class UsersTest extends ApiTestCase
+{
+    /**
+     * @test
+     */
+    public function shouldGetAllUsers()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'name' => 'Matt'),
+            array('id' => 2, 'name' => 'John'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('users', array('active' => null, 'page' => 1, 'per_page' => 10))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->all(null, 1, 10));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetActiveUsers()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'name' => 'Matt'),
+            array('id' => 2, 'name' => 'John'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('users', array('active' => true, 'page' => 1, 'per_page' => AbstractApi::PER_PAGE))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->all(true));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldNotNeedPaginationWhenGettingUsers()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'name' => 'Matt'),
+            array('id' => 2, 'name' => 'John'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('users', array('active' => null, 'page' => 1, 'per_page' => AbstractApi::PER_PAGE))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->all());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldSearchUsers()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'name' => 'Matt')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('users', array('search' => 'ma', 'active' => null, 'page' => 1, 'per_page' => AbstractApi::PER_PAGE))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->search('ma'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldSearchActiveUsers()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'name' => 'Matt')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('users', array('search' => 'ma', 'active' => true, 'page' => 1, 'per_page' => AbstractApi::PER_PAGE))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->search('ma', true));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldSearchActiveUsersWithPagination()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'name' => 'Matt')
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('users', array('search' => 'ma', 'active' => true, 'page' => 2, 'per_page' => 5))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->search('ma', true, 2, 5));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldShowUser()
+    {
+        $expectedArray = array('id' => 1, 'name' => 'Matt');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('users/1')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->show(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateUser()
+    {
+        $expectedArray = array('id' => 3, 'name' => 'Billy');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('users', array('email' => 'billy@example.com', 'password' => 'password'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->create('billy@example.com', 'password'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateUserWithAdditionalInfo()
+    {
+        $expectedArray = array('id' => 3, 'name' => 'Billy');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('users', array('email' => 'billy@example.com', 'password' => 'password', 'name' => 'Billy', 'bio' => 'A person'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->create('billy@example.com', 'password', array('name' => 'Billy', 'bio' => 'A person')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldUpdateUser()
+    {
+        $expectedArray = array('id' => 3, 'name' => 'Billy Bob');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('users/3', array('name' => 'Billy Bob'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->update(3, array('name' => 'Billy Bob')));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldRemoveUser()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('users/1')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->remove(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldBlockUser()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('users/1/block')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->block(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldUnblockUser()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('put')
+            ->with('users/1/unblock')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->unblock(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldShowCurrentUser()
+    {
+        $expectedArray = array('id' => 1, 'name' => 'Matt');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('user')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->me());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetCurrentUserKeys()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'title' => 'A key'),
+            array('id' => 2, 'name' => 'Another key'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('user/keys')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->keys(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetCurrentUserKey()
+    {
+        $expectedArray = array('id' => 1, 'title' => 'A key');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('user/keys/1')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->key(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateKeyForCurrentUser()
+    {
+        $expectedArray = array('id' => 3, 'title' => 'A new key');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('user/keys', array('title' => 'A new key', 'key' => '...'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->createKey('A new key', '...'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldDeleteKeyForCurrentUser()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('user/keys/3')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->removeKey(3));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetUserKeys()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'title' => 'A key'),
+            array('id' => 2, 'name' => 'Another key'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('users/1/keys')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->userKeys(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetUserKey()
+    {
+        $expectedArray = array('id' => 2, 'title' => 'Another key');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('users/1/keys/2')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->userKey(1, 2));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldCreateKeyForUser()
+    {
+        $expectedArray = array('id' => 3, 'title' => 'A new key');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('post')
+            ->with('users/1/keys', array('title' => 'A new key', 'key' => '...'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->createKeyForUser(1, 'A new key', '...'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldDeleteKeyForUser()
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('users/1/keys/3')
+            ->will($this->returnValue($expectedBool))
+        ;
+
+        $this->assertEquals($expectedBool, $api->removeUserKey(1, 3));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldAttemptLogin()
+    {
+        $expectedArray = array('id' => 1, 'name' => 'Matt');
+
+        $api = $this->getApiMock();
+        $api->expects($this->exactly(2))
+            ->method('post')
+            ->with('session', array('login' => 'matt', 'password' => 'password', 'email' => 'matt'))
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->session('matt', 'password'));
+        $this->assertEquals($expectedArray, $api->login('matt', 'password'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetUserEmails()
+    {
+        $expectedArray = array(
+            array('id' => 1, 'email' => 'foo@bar.baz'),
+            array('id' => 2, 'email' => 'foo@bar.qux'),
+        );
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('user/emails')
+            ->will($this->returnValue($expectedArray));
+
+        $this->assertEquals($expectedArray, $api->emails());
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetSpecificUserEmail()
+    {
+        $expectedArray = array('id' => 1, 'email' => 'foo@bar.baz');
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('user/emails/1')
+            ->will($this->returnValue($expectedArray));
+
+        $this->assertEquals($expectedArray, $api->email(1));
+    }
+
+    protected function getApiClass()
+    {
+        return 'Gitlab\Api\Users';
+    }
+}
diff --git a/vendor/m4tthumphrey/php-gitlab-api/test/bootstrap.php b/vendor/m4tthumphrey/php-gitlab-api/test/bootstrap.php
new file mode 100644
index 0000000000000000000000000000000000000000..83bc8b088a4fee7016d39ea78c45258cde578119
--- /dev/null
+++ b/vendor/m4tthumphrey/php-gitlab-api/test/bootstrap.php
@@ -0,0 +1,18 @@
+<?php
+
+function includeIfExists($file)
+{
+    if (file_exists($file)) {
+        return include $file;
+    }
+}
+
+if ((!$loader = includeIfExists(__DIR__.'/../vendor/autoload.php')) && (!$loader = includeIfExists(__DIR__.'/../../../.composer/autoload.php'))) {
+    die('You must set up the project dependencies, run the following commands:'.PHP_EOL.
+        'curl -s http://getcomposer.org/installer | php'.PHP_EOL.
+        'php composer.phar install'.PHP_EOL);
+}
+
+$loader->add('Gitlab\Tests', __DIR__);
+
+return $loader;
\ No newline at end of file
diff --git a/vendor/nyholm/psr7/CHANGELOG.md b/vendor/nyholm/psr7/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..dc4c5134131112d4a65e2de08a951a099cec73f8
--- /dev/null
+++ b/vendor/nyholm/psr7/CHANGELOG.md
@@ -0,0 +1,54 @@
+# Changelog
+
+All notable changes to this project will be documented in this file, in reverse chronological order by release.
+
+## 1.0.1
+
+### Fixed
+
+- Handle `fopen` failing in createStreamFromFile according to PSR-7.
+- Reduce execution path to speed up performance. 
+- Fixed typos.
+- Code style.
+
+## 1.0.0
+
+### Added
+
+- Support for final PSR-17 (HTTP factories). (`Psr17Factory`)
+- Support for numeric header values. 
+- Support for empty header values. 
+- All classes are final
+- `HttplugFactory` that implements factory interfaces from HTTPlug. 
+
+### Changed
+
+- `ServerRequest` does not extend `Request`.
+
+### Removed
+
+- The HTTPlug discovery strategy was removed since it is included in php-http/discovery 1.4.
+- `UploadedFileFactory()` was removed in favor for `Psr17Factory`.  
+- `ServerRequestFactory()` was removed in favor for `Psr17Factory`.  
+- `StreamFactory`, `UriFactory`, abd `MessageFactory`. Use `HttplugFactory` instead.  
+- `ServerRequestFactory::createServerRequestFromArray`, `ServerRequestFactory::createServerRequestFromArrays` and 
+  `ServerRequestFactory::createServerRequestFromGlobals`. Please use the new `nyholm/psr7-server` instead. 
+
+## 0.3.0
+
+### Added
+
+- Return types.
+- Many `InvalidArgumentException`s are thrown when you use invalid arguments. 
+- Integration tests for `UploadedFile` and `ServerRequest`.
+
+### Changed
+
+- We dropped PHP7.0 support. 
+- PSR-17 factories have been marked as internal. They do not fall under our BC promise until PSR-17 is accepted.  
+- `UploadedFileFactory::createUploadedFile` does not accept a string file path. 
+
+## 0.2.3
+
+No changelog before this release
+
diff --git a/vendor/nyholm/psr7/LICENSE b/vendor/nyholm/psr7/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..d6c52312c64110d4bf27b8f2e7add50cb3de5e02
--- /dev/null
+++ b/vendor/nyholm/psr7/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 Tobias Nyholm
+
+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/nyholm/psr7/README.md b/vendor/nyholm/psr7/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..221d3ef2a1f5449845f972dd3ca351e7de775aa8
--- /dev/null
+++ b/vendor/nyholm/psr7/README.md
@@ -0,0 +1,94 @@
+# PSR-7 implementation
+
+[![Latest Version](https://img.shields.io/github/release/Nyholm/psr7.svg?style=flat-square)](https://github.com/Nyholm/psr7/releases)
+[![Build Status](https://img.shields.io/travis/Nyholm/psr7/master.svg?style=flat-square)](https://travis-ci.org/Nyholm/psr7)
+[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/Nyholm/psr7.svg?style=flat-square)](https://scrutinizer-ci.com/g/Nyholm/psr7)
+[![Quality Score](https://img.shields.io/scrutinizer/g/Nyholm/psr7.svg?style=flat-square)](https://scrutinizer-ci.com/g/Nyholm/psr7)
+[![Total Downloads](https://poser.pugx.org/nyholm/psr7/downloads)](https://packagist.org/packages/nyholm/psr7)
+[![Monthly Downloads](https://poser.pugx.org/nyholm/psr7/d/monthly.png)](https://packagist.org/packages/nyholm/psr7)
+[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
+
+
+A super lightweight PSR-7 implementation. Very strict and very fast.
+
+| Description | Guzzle | Zend | Slim | Nyholm |
+| ---- | ------ | ---- | ---- | ------ |
+| Lines of code | 3 000 | 3 000 | 1 700 | 1 100 |
+| PHP7 | No | No | No | Yes |
+| PSR-7* | 66% | 92% | 75% | 100% |
+| PSR-17 | No | No | No | Yes |
+| HTTPlug | No | No | No | Yes |
+| Performance** | 1.49x | 1x | 1.17x | 1.69x |
+
+\* Percent of completed tests in https://github.com/php-http/psr7-integration-tests
+
+\** See benchmark at https://github.com/Nyholm/http-client-benchmark (higher is better)
+
+## Installation
+
+```bash
+composer require nyholm/psr7
+```
+
+If you are using Symfony Flex then you get all message factories registered as services. 
+
+## Usage
+
+The PSR-7 objects do not contain any other public methods then those defined in
+the [PSR-7 specification](https://www.php-fig.org/psr/psr-7/). 
+
+### Create objects
+
+Use the PSR-17 factory to create requests, streams, URIs etc.  
+
+```php
+$factory = new \Nyholm\Psr7\Factory\Psr17Factory();
+$request = $factory->createRequest('GET', 'http://tnyholm.se');
+$stream = $factory->createStream('foobar');
+```
+
+### Sending a request
+
+With [HTTPlug](http://httplug.io/) or any other PSR-18 (HTTP client) you may send requests like: 
+
+```bash
+composer require kriswallsmith/buzz
+```
+
+```php
+$psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();
+$psr18Client = new Buzz\Client\Curl([], $psr17Factory);
+
+$request = (new Psr17Factory())->createRequest('GET', 'http://tnyholm.se');
+$response = $psr18Client->sendRequest($request);
+```
+
+### Create server requests
+
+```bash
+composer require nyholm/psr7-server
+```
+
+```php
+$psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();
+
+$creator = new ServerRequestCreator(
+    $psr17Factory,
+    $psr17Factory,
+    $psr17Factory,
+    $psr17Factory
+);
+
+$serverRequest = $creator->fromGlobals();
+```
+
+### Emitting a response
+
+```bash
+composer require zendframework/zend-httphandlerrunner
+```
+
+```php
+$response = (new Psr17Factory())->createReponse('200', 'Hello world');
+(new \Zend\HttpHandlerRunner\Emitter\SapiEmitter())->emit($response);
+```
diff --git a/vendor/nyholm/psr7/composer.json b/vendor/nyholm/psr7/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..397f38eb357372a660d820270b9e7fd0f49fcfcb
--- /dev/null
+++ b/vendor/nyholm/psr7/composer.json
@@ -0,0 +1,51 @@
+{
+    "name": "nyholm/psr7",
+    "description": "A fast PHP7 implementation of PSR-7",
+    "license": "MIT",
+    "keywords": ["psr-7", "psr-17"],
+    "homepage": "http://tnyholm.se",
+    "authors": [
+        {
+            "name": "Tobias Nyholm",
+            "email": "tobias.nyholm@gmail.com"
+        },
+        {
+            "name": "Martijn van der Ven",
+            "email": "martijn@vanderven.se"
+        }
+    ],
+    "require": {
+        "php": "^7.1",
+        "psr/http-message": "^1.0",
+        "php-http/message-factory": "^1.0",
+        "psr/http-factory": "^1.0"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "^7.0",
+        "php-http/psr7-integration-tests": "dev-master",
+        "http-interop/http-factory-tests": "dev-master"
+    },
+    "provide": {
+        "psr/http-message-implementation": "1.0",
+        "psr/http-factory-implementation": "1.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Nyholm\\Psr7\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Tests\\Nyholm\\Psr7\\": "tests/"
+        }
+    },
+    "scripts": {
+        "test": "vendor/bin/phpunit",
+        "test-ci": "vendor/bin/phpunit --coverage-text --coverage-clover=build/coverage.xml"
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.0-dev"
+        }
+    }
+}
diff --git a/vendor/nyholm/psr7/src/Factory/HttplugFactory.php b/vendor/nyholm/psr7/src/Factory/HttplugFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..c17878ac39b56d2fd3f771cabf9b2e7914322e54
--- /dev/null
+++ b/vendor/nyholm/psr7/src/Factory/HttplugFactory.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Nyholm\Psr7\Factory;
+
+use Http\Message\{MessageFactory, StreamFactory, UriFactory};
+use Nyholm\Psr7\{Request, Response, Stream, Uri};
+use Psr\Http\Message\UriInterface;
+
+/**
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ * @author Martijn van der Ven <martijn@vanderven.se>
+ */
+final class HttplugFactory implements MessageFactory, StreamFactory, UriFactory
+{
+    public function createRequest($method, $uri, array $headers = [], $body = null, $protocolVersion = '1.1')
+    {
+        return new Request($method, $uri, $headers, $body, $protocolVersion);
+    }
+
+    public function createResponse($statusCode = 200, $reasonPhrase = null, array $headers = [], $body = null, $version = '1.1')
+    {
+        return new Response((int) $statusCode, $headers, $body, $version, $reasonPhrase);
+    }
+
+    public function createStream($body = null)
+    {
+        return Stream::create(null === $body ? '' : $body);
+    }
+
+    public function createUri($uri = ''): UriInterface
+    {
+        if ($uri instanceof UriInterface) {
+            return $uri;
+        }
+
+        return new Uri($uri);
+    }
+}
diff --git a/vendor/nyholm/psr7/src/Factory/Psr17Factory.php b/vendor/nyholm/psr7/src/Factory/Psr17Factory.php
new file mode 100644
index 0000000000000000000000000000000000000000..218122c692f72c9f96da88b251da08600182d668
--- /dev/null
+++ b/vendor/nyholm/psr7/src/Factory/Psr17Factory.php
@@ -0,0 +1,68 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Nyholm\Psr7\Factory;
+
+use Nyholm\Psr7\{Request, Response, ServerRequest, Stream, UploadedFile, Uri};
+use Psr\Http\Message\{RequestFactoryInterface, RequestInterface, ResponseFactoryInterface, ResponseInterface, ServerRequestFactoryInterface, ServerRequestInterface, StreamFactoryInterface, StreamInterface, UploadedFileFactoryInterface, UploadedFileInterface, UriFactoryInterface, UriInterface};
+
+/**
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ * @author Martijn van der Ven <martijn@vanderven.se>
+ */
+final class Psr17Factory implements RequestFactoryInterface, ResponseFactoryInterface, ServerRequestFactoryInterface, StreamFactoryInterface, UploadedFileFactoryInterface, UriFactoryInterface
+{
+    public function createRequest(string $method, $uri): RequestInterface
+    {
+        return new Request($method, $uri);
+    }
+
+    public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface
+    {
+        return new Response($code, [], null, '1.1', $reasonPhrase);
+    }
+
+    public function createStream(string $content = ''): StreamInterface
+    {
+        return Stream::create($content);
+    }
+
+    public function createStreamFromFile(string $filename, string $mode = 'r'): StreamInterface
+    {
+        $resource = @\fopen($filename, $mode);
+        if (false === $resource) {
+            if (0 === \strlen($mode) || false === \in_array($mode[0], ['r', 'w', 'a', 'x', 'c'])) {
+                throw new \InvalidArgumentException('The mode '.$mode.' is invalid.');
+            }
+
+            throw new \RuntimeException('The file '.$filename.' cannot be opened.');
+        }
+
+        return Stream::create($resource);
+    }
+
+    public function createStreamFromResource($resource): StreamInterface
+    {
+        return Stream::create($resource);
+    }
+
+    public function createUploadedFile(StreamInterface $stream, int $size = null, int $error = \UPLOAD_ERR_OK, string $clientFilename = null, string $clientMediaType = null): UploadedFileInterface
+    {
+        if (null === $size) {
+            $size = $stream->getSize();
+        }
+
+        return new UploadedFile($stream, $size, $error, $clientFilename, $clientMediaType);
+    }
+
+    public function createUri(string $uri = ''): UriInterface
+    {
+        return new Uri($uri);
+    }
+
+    public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface
+    {
+        return new ServerRequest($method, $uri, [], null, '1.1', $serverParams);
+    }
+}
diff --git a/vendor/nyholm/psr7/src/MessageTrait.php b/vendor/nyholm/psr7/src/MessageTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..1e811741dfe619ea2d02abedbd584ee44a94d71b
--- /dev/null
+++ b/vendor/nyholm/psr7/src/MessageTrait.php
@@ -0,0 +1,202 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Nyholm\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Trait implementing functionality common to requests and responses.
+ *
+ * @author Michael Dowling and contributors to guzzlehttp/psr7
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ * @author Martijn van der Ven <martijn@vanderven.se>
+ *
+ * @internal should not be used outside of Nyholm/Psr7 as it does not fall under our BC promise
+ */
+trait MessageTrait
+{
+    /** @var array Map of all registered headers, as original name => array of values */
+    private $headers = [];
+
+    /** @var array Map of lowercase header name => original name at registration */
+    private $headerNames = [];
+
+    /** @var string */
+    private $protocol = '1.1';
+
+    /** @var StreamInterface */
+    private $stream;
+
+    public function getProtocolVersion(): string
+    {
+        return $this->protocol;
+    }
+
+    public function withProtocolVersion($version): self
+    {
+        if ($this->protocol === $version) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->protocol = $version;
+
+        return $new;
+    }
+
+    public function getHeaders(): array
+    {
+        return $this->headers;
+    }
+
+    public function hasHeader($header): bool
+    {
+        return isset($this->headerNames[\strtolower($header)]);
+    }
+
+    public function getHeader($header): array
+    {
+        $header = \strtolower($header);
+        if (!isset($this->headerNames[$header])) {
+            return [];
+        }
+
+        $header = $this->headerNames[$header];
+
+        return $this->headers[$header];
+    }
+
+    public function getHeaderLine($header): string
+    {
+        return \implode(', ', $this->getHeader($header));
+    }
+
+    public function withHeader($header, $value): self
+    {
+        $value = $this->validateAndTrimHeader($header, $value);
+        $normalized = \strtolower($header);
+
+        $new = clone $this;
+        if (isset($new->headerNames[$normalized])) {
+            unset($new->headers[$new->headerNames[$normalized]]);
+        }
+        $new->headerNames[$normalized] = $header;
+        $new->headers[$header] = $value;
+
+        return $new;
+    }
+
+    public function withAddedHeader($header, $value): self
+    {
+        if (!\is_string($header) || '' === $header) {
+            throw new \InvalidArgumentException('Header name must be an RFC 7230 compatible string.');
+        }
+
+        $new = clone $this;
+        $new->setHeaders([$header => $value]);
+
+        return $new;
+    }
+
+    public function withoutHeader($header): self
+    {
+        $normalized = \strtolower($header);
+        if (!isset($this->headerNames[$normalized])) {
+            return $this;
+        }
+
+        $header = $this->headerNames[$normalized];
+        $new = clone $this;
+        unset($new->headers[$header], $new->headerNames[$normalized]);
+
+        return $new;
+    }
+
+    public function getBody(): StreamInterface
+    {
+        if (!$this->stream) {
+            $this->stream = Stream::create('');
+        }
+
+        return $this->stream;
+    }
+
+    public function withBody(StreamInterface $body): self
+    {
+        if ($body === $this->stream) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->stream = $body;
+
+        return $new;
+    }
+
+    private function setHeaders(array $headers): void
+    {
+        foreach ($headers as $header => $value) {
+            $value = $this->validateAndTrimHeader($header, $value);
+            $normalized = \strtolower($header);
+            if (isset($this->headerNames[$normalized])) {
+                $header = $this->headerNames[$normalized];
+                $this->headers[$header] = \array_merge($this->headers[$header], $value);
+            } else {
+                $this->headerNames[$normalized] = $header;
+                $this->headers[$header] = $value;
+            }
+        }
+    }
+
+    /**
+     * Make sure the header complies with RFC 7230.
+     *
+     * Header names must be a non-empty string consisting of token characters.
+     *
+     * Header values must be strings consisting of visible characters with all optional
+     * leading and trailing whitespace stripped. This method will always strip such
+     * optional whitespace. Note that the method does not allow folding whitespace within
+     * the values as this was deprecated for almost all instances by the RFC.
+     *
+     * header-field = field-name ":" OWS field-value OWS
+     * field-name   = 1*( "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / "^"
+     *              / "_" / "`" / "|" / "~" / %x30-39 / ( %x41-5A / %x61-7A ) )
+     * OWS          = *( SP / HTAB )
+     * field-value  = *( ( %x21-7E / %x80-FF ) [ 1*( SP / HTAB ) ( %x21-7E / %x80-FF ) ] )
+     *
+     * @see https://tools.ietf.org/html/rfc7230#section-3.2.4
+     */
+    private function validateAndTrimHeader($header, $values): array
+    {
+        if (!\is_string($header) || 1 !== \preg_match("@^[!#$%&'*+.^_`|~0-9A-Za-z-]+$@", $header)) {
+            throw new \InvalidArgumentException('Header name must be an RFC 7230 compatible string.');
+        }
+
+        if (!\is_array($values)) {
+            // This is simple, just one value.
+            if ((!\is_numeric($values) && !\is_string($values)) || 1 !== \preg_match("@^[ \t\x21-\x7E\x80-\xFF]*$@", (string) $values)) {
+                throw new \InvalidArgumentException('Header values must be RFC 7230 compatible strings.');
+            }
+
+            return [\trim((string) $values, " \t")];
+        }
+
+        if (empty($values)) {
+            throw new \InvalidArgumentException('Header values must be a string or an array of strings, empty array given.');
+        }
+
+        // Assert Non empty array
+        $returnValues = [];
+        foreach ($values as $v) {
+            if ((!\is_numeric($v) && !\is_string($v)) || 1 !== \preg_match("@^[ \t\x21-\x7E\x80-\xFF]*$@", (string) $v)) {
+                throw new \InvalidArgumentException('Header values must be RFC 7230 compatible strings.');
+            }
+
+            $returnValues[] = \trim((string) $v, " \t");
+        }
+
+        return $returnValues;
+    }
+}
diff --git a/vendor/nyholm/psr7/src/Request.php b/vendor/nyholm/psr7/src/Request.php
new file mode 100644
index 0000000000000000000000000000000000000000..5d19086487e00b3671b494116b1d7ba7d10d5f5c
--- /dev/null
+++ b/vendor/nyholm/psr7/src/Request.php
@@ -0,0 +1,44 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Nyholm\Psr7;
+
+use Psr\Http\Message\{RequestInterface, StreamInterface, UriInterface};
+
+/**
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ * @author Martijn van der Ven <martijn@vanderven.se>
+ */
+final class Request implements RequestInterface
+{
+    use MessageTrait;
+    use RequestTrait;
+
+    /**
+     * @param string                               $method  HTTP method
+     * @param string|UriInterface                  $uri     URI
+     * @param array                                $headers Request headers
+     * @param string|null|resource|StreamInterface $body    Request body
+     * @param string                               $version Protocol version
+     */
+    public function __construct(string $method, $uri, array $headers = [], $body = null, string $version = '1.1')
+    {
+        if (!($uri instanceof UriInterface)) {
+            $uri = new Uri($uri);
+        }
+
+        $this->method = $method;
+        $this->uri = $uri;
+        $this->setHeaders($headers);
+        $this->protocol = $version;
+
+        if (!$this->hasHeader('Host')) {
+            $this->updateHostFromUri();
+        }
+
+        if ('' !== $body && null !== $body) {
+            $this->stream = Stream::create($body);
+        }
+    }
+}
diff --git a/vendor/nyholm/psr7/src/RequestTrait.php b/vendor/nyholm/psr7/src/RequestTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..1ab6215c43ed334f3abf56c684deb0879ad6615a
--- /dev/null
+++ b/vendor/nyholm/psr7/src/RequestTrait.php
@@ -0,0 +1,113 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Nyholm\Psr7;
+
+use Psr\Http\Message\UriInterface;
+
+/**
+ * @author Michael Dowling and contributors to guzzlehttp/psr7
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ * @author Martijn van der Ven <martijn@vanderven.se>
+ *
+ * @internal should not be used outside of Nyholm/Psr7 as it does not fall under our BC promise
+ */
+trait RequestTrait
+{
+    /** @var string */
+    private $method;
+
+    /** @var null|string */
+    private $requestTarget;
+
+    /** @var null|UriInterface */
+    private $uri;
+
+    public function getRequestTarget(): string
+    {
+        if (null !== $this->requestTarget) {
+            return $this->requestTarget;
+        }
+
+        if ('' === $target = $this->uri->getPath()) {
+            $target = '/';
+        }
+        if ('' !== $this->uri->getQuery()) {
+            $target .= '?'.$this->uri->getQuery();
+        }
+
+        return $target;
+    }
+
+    public function withRequestTarget($requestTarget): self
+    {
+        if (\preg_match('#\s#', $requestTarget)) {
+            throw new \InvalidArgumentException('Invalid request target provided; cannot contain whitespace');
+        }
+
+        $new = clone $this;
+        $new->requestTarget = $requestTarget;
+
+        return $new;
+    }
+
+    public function getMethod(): string
+    {
+        return $this->method;
+    }
+
+    public function withMethod($method): self
+    {
+        if (!\is_string($method)) {
+            throw new \InvalidArgumentException('Method must be a string');
+        }
+
+        $new = clone $this;
+        $new->method = $method;
+
+        return $new;
+    }
+
+    public function getUri(): UriInterface
+    {
+        return $this->uri;
+    }
+
+    public function withUri(UriInterface $uri, $preserveHost = false): self
+    {
+        if ($uri === $this->uri) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->uri = $uri;
+
+        if (!$preserveHost || !$this->hasHeader('Host')) {
+            $new->updateHostFromUri();
+        }
+
+        return $new;
+    }
+
+    private function updateHostFromUri(): void
+    {
+        if ('' === $host = $this->uri->getHost()) {
+            return;
+        }
+
+        if (null !== ($port = $this->uri->getPort())) {
+            $host .= ':'.$port;
+        }
+
+        if (isset($this->headerNames['host'])) {
+            $header = $this->headerNames['host'];
+        } else {
+            $header = 'Host';
+            $this->headerNames['host'] = 'Host';
+        }
+        // Ensure Host is the first header.
+        // See: http://tools.ietf.org/html/rfc7230#section-5.4
+        $this->headers = [$header => [$host]] + $this->headers;
+    }
+}
diff --git a/vendor/nyholm/psr7/src/Response.php b/vendor/nyholm/psr7/src/Response.php
new file mode 100644
index 0000000000000000000000000000000000000000..c74e697271eb66d5099ceee0b4bc4a26f7d9c4fe
--- /dev/null
+++ b/vendor/nyholm/psr7/src/Response.php
@@ -0,0 +1,87 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Nyholm\Psr7;
+
+use Psr\Http\Message\{ResponseInterface, StreamInterface};
+
+/**
+ * @author Michael Dowling and contributors to guzzlehttp/psr7
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ * @author Martijn van der Ven <martijn@vanderven.se>
+ */
+final class Response implements ResponseInterface
+{
+    use MessageTrait;
+
+    /** @var array Map of standard HTTP status code/reason phrases */
+    private static $phrases = [
+        100 => 'Continue', 101 => 'Switching Protocols', 102 => 'Processing',
+        200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 207 => 'Multi-status', 208 => 'Already Reported',
+        300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => 'Switch Proxy', 307 => 'Temporary Redirect',
+        400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', 418 => 'I\'m a teapot', 422 => 'Unprocessable Entity', 423 => 'Locked', 424 => 'Failed Dependency', 425 => 'Unordered Collection', 426 => 'Upgrade Required', 428 => 'Precondition Required', 429 => 'Too Many Requests', 431 => 'Request Header Fields Too Large', 451 => 'Unavailable For Legal Reasons',
+        500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported', 506 => 'Variant Also Negotiates', 507 => 'Insufficient Storage', 508 => 'Loop Detected', 511 => 'Network Authentication Required',
+    ];
+
+    /** @var string */
+    private $reasonPhrase = '';
+
+    /** @var int */
+    private $statusCode = 200;
+
+    /**
+     * @param int                                  $status  Status code
+     * @param array                                $headers Response headers
+     * @param string|null|resource|StreamInterface $body    Response body
+     * @param string                               $version Protocol version
+     * @param string|null                          $reason  Reason phrase (when empty a default will be used based on the status code)
+     */
+    public function __construct(int $status = 200, array $headers = [], $body = null, string $version = '1.1', string $reason = null)
+    {
+        $this->statusCode = (int) $status;
+        if ('' !== $body && null !== $body) {
+            $this->stream = Stream::create($body);
+        }
+
+        $this->setHeaders($headers);
+        if (null === $reason && isset(self::$phrases[$this->statusCode])) {
+            $this->reasonPhrase = self::$phrases[$status];
+        } else {
+            $this->reasonPhrase = (string) $reason;
+        }
+
+        $this->protocol = $version;
+    }
+
+    public function getStatusCode(): int
+    {
+        return $this->statusCode;
+    }
+
+    public function getReasonPhrase(): string
+    {
+        return $this->reasonPhrase;
+    }
+
+    public function withStatus($code, $reasonPhrase = ''): self
+    {
+        if (!\is_int($code) && !\is_string($code)) {
+            throw new \InvalidArgumentException('Status code has to be an integer');
+        }
+
+        $code = (int) $code;
+        if ($code < 100 || $code > 599) {
+            throw new \InvalidArgumentException('Status code has to be an integer between 100 and 599');
+        }
+
+        $new = clone $this;
+        $new->statusCode = (int) $code;
+        if ((null === $reasonPhrase || '' === $reasonPhrase) && isset(self::$phrases[$new->statusCode])) {
+            $reasonPhrase = self::$phrases[$new->statusCode];
+        }
+        $new->reasonPhrase = $reasonPhrase;
+
+        return $new;
+    }
+}
diff --git a/vendor/nyholm/psr7/src/ServerRequest.php b/vendor/nyholm/psr7/src/ServerRequest.php
new file mode 100644
index 0000000000000000000000000000000000000000..75763f13c59ec953ef6b27bf667e3f10af65eb08
--- /dev/null
+++ b/vendor/nyholm/psr7/src/ServerRequest.php
@@ -0,0 +1,161 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Nyholm\Psr7;
+
+use Psr\Http\Message\{ServerRequestInterface, StreamInterface, UploadedFileInterface, UriInterface};
+
+/**
+ * @author Michael Dowling and contributors to guzzlehttp/psr7
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ * @author Martijn van der Ven <martijn@vanderven.se>
+ */
+final class ServerRequest implements ServerRequestInterface
+{
+    use MessageTrait;
+    use RequestTrait;
+
+    /** @var array */
+    private $attributes = [];
+
+    /** @var array */
+    private $cookieParams = [];
+
+    /** @var null|array|object */
+    private $parsedBody;
+
+    /** @var array */
+    private $queryParams = [];
+
+    /** @var array */
+    private $serverParams;
+
+    /** @var UploadedFileInterface[] */
+    private $uploadedFiles = [];
+
+    /**
+     * @param string                               $method       HTTP method
+     * @param string|UriInterface                  $uri          URI
+     * @param array                                $headers      Request headers
+     * @param string|null|resource|StreamInterface $body         Request body
+     * @param string                               $version      Protocol version
+     * @param array                                $serverParams Typically the $_SERVER superglobal
+     */
+    public function __construct(string $method, $uri, array $headers = [], $body = null, string $version = '1.1', array $serverParams = [])
+    {
+        $this->serverParams = $serverParams;
+
+        if (!($uri instanceof UriInterface)) {
+            $uri = new Uri($uri);
+        }
+
+        $this->method = $method;
+        $this->uri = $uri;
+        $this->setHeaders($headers);
+        $this->protocol = $version;
+
+        if (!$this->hasHeader('Host')) {
+            $this->updateHostFromUri();
+        }
+
+        if ('' !== $body && null !== $body) {
+            $this->stream = Stream::create($body);
+        }
+    }
+
+    public function getServerParams(): array
+    {
+        return $this->serverParams;
+    }
+
+    public function getUploadedFiles(): array
+    {
+        return $this->uploadedFiles;
+    }
+
+    public function withUploadedFiles(array $uploadedFiles)
+    {
+        $new = clone $this;
+        $new->uploadedFiles = $uploadedFiles;
+
+        return $new;
+    }
+
+    public function getCookieParams(): array
+    {
+        return $this->cookieParams;
+    }
+
+    public function withCookieParams(array $cookies)
+    {
+        $new = clone $this;
+        $new->cookieParams = $cookies;
+
+        return $new;
+    }
+
+    public function getQueryParams(): array
+    {
+        return $this->queryParams;
+    }
+
+    public function withQueryParams(array $query)
+    {
+        $new = clone $this;
+        $new->queryParams = $query;
+
+        return $new;
+    }
+
+    public function getParsedBody()
+    {
+        return $this->parsedBody;
+    }
+
+    public function withParsedBody($data)
+    {
+        if (!\is_array($data) && !\is_object($data) && null !== $data) {
+            throw new \InvalidArgumentException('First parameter to withParsedBody MUST be object, array or null');
+        }
+
+        $new = clone $this;
+        $new->parsedBody = $data;
+
+        return $new;
+    }
+
+    public function getAttributes(): array
+    {
+        return $this->attributes;
+    }
+
+    public function getAttribute($attribute, $default = null)
+    {
+        if (false === \array_key_exists($attribute, $this->attributes)) {
+            return $default;
+        }
+
+        return $this->attributes[$attribute];
+    }
+
+    public function withAttribute($attribute, $value): self
+    {
+        $new = clone $this;
+        $new->attributes[$attribute] = $value;
+
+        return $new;
+    }
+
+    public function withoutAttribute($attribute): self
+    {
+        if (false === \array_key_exists($attribute, $this->attributes)) {
+            return $this;
+        }
+
+        $new = clone $this;
+        unset($new->attributes[$attribute]);
+
+        return $new;
+    }
+}
diff --git a/vendor/nyholm/psr7/src/Stream.php b/vendor/nyholm/psr7/src/Stream.php
new file mode 100644
index 0000000000000000000000000000000000000000..61b7b54face32648e54c64d72e04ff32c1636b89
--- /dev/null
+++ b/vendor/nyholm/psr7/src/Stream.php
@@ -0,0 +1,253 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Nyholm\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * @author Michael Dowling and contributors to guzzlehttp/psr7
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ * @author Martijn van der Ven <martijn@vanderven.se>
+ */
+final class Stream implements StreamInterface
+{
+    /** @var resource A resource reference */
+    private $stream;
+
+    /** @var bool */
+    private $seekable;
+
+    /** @var bool */
+    private $readable;
+
+    /** @var bool */
+    private $writable;
+
+    /** @var array|mixed|null|void */
+    private $uri;
+
+    /** @var int */
+    private $size;
+
+    /** @var array Hash of readable and writable stream types */
+    private static $readWriteHash = [
+        'read' => [
+            'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true,
+            'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true,
+            'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true,
+            'x+t' => true, 'c+t' => true, 'a+' => true,
+        ],
+        'write' => [
+            'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true,
+            'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true,
+            'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true,
+            'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true,
+        ],
+    ];
+
+    private function __construct()
+    {
+    }
+
+    /**
+     * Creates a new PSR-7 stream.
+     *
+     * @param string|resource|StreamInterface $body
+     *
+     * @return StreamInterface
+     *
+     * @throws \InvalidArgumentException
+     */
+    public static function create($body = ''): StreamInterface
+    {
+        if ($body instanceof StreamInterface) {
+            return $body;
+        }
+
+        if (\is_string($body)) {
+            $resource = \fopen('php://temp', 'rw+');
+            \fwrite($resource, $body);
+            $body = $resource;
+        }
+
+        if ('resource' === \gettype($body)) {
+            $obj = new self();
+            $obj->stream = $body;
+            $meta = \stream_get_meta_data($obj->stream);
+            $obj->seekable = $meta['seekable'];
+            $obj->readable = isset(self::$readWriteHash['read'][$meta['mode']]);
+            $obj->writable = isset(self::$readWriteHash['write'][$meta['mode']]);
+            $obj->uri = $obj->getMetadata('uri');
+
+            return $obj;
+        }
+
+        throw new \InvalidArgumentException('First argument to Stream::create() must be a string, resource or StreamInterface.');
+    }
+
+    /**
+     * Closes the stream when the destructed.
+     */
+    public function __destruct()
+    {
+        $this->close();
+    }
+
+    public function __toString(): string
+    {
+        try {
+            if ($this->isSeekable()) {
+                $this->seek(0);
+            }
+
+            return $this->getContents();
+        } catch (\Exception $e) {
+            return '';
+        }
+    }
+
+    public function close(): void
+    {
+        if (isset($this->stream)) {
+            if (\is_resource($this->stream)) {
+                \fclose($this->stream);
+            }
+            $this->detach();
+        }
+    }
+
+    public function detach()
+    {
+        if (!isset($this->stream)) {
+            return null;
+        }
+
+        $result = $this->stream;
+        unset($this->stream);
+        $this->size = $this->uri = null;
+        $this->readable = $this->writable = $this->seekable = false;
+
+        return $result;
+    }
+
+    public function getSize(): ?int
+    {
+        if (null !== $this->size) {
+            return $this->size;
+        }
+
+        if (!isset($this->stream)) {
+            return null;
+        }
+
+        // Clear the stat cache if the stream has a URI
+        if ($this->uri) {
+            \clearstatcache(true, $this->uri);
+        }
+
+        $stats = \fstat($this->stream);
+        if (isset($stats['size'])) {
+            $this->size = $stats['size'];
+
+            return $this->size;
+        }
+
+        return null;
+    }
+
+    public function tell(): int
+    {
+        if (false === $result = \ftell($this->stream)) {
+            throw new \RuntimeException('Unable to determine stream position');
+        }
+
+        return $result;
+    }
+
+    public function eof(): bool
+    {
+        return !$this->stream || \feof($this->stream);
+    }
+
+    public function isSeekable(): bool
+    {
+        return $this->seekable;
+    }
+
+    public function seek($offset, $whence = \SEEK_SET): void
+    {
+        if (!$this->seekable) {
+            throw new \RuntimeException('Stream is not seekable');
+        } elseif (\fseek($this->stream, $offset, $whence) === -1) {
+            throw new \RuntimeException('Unable to seek to stream position '.$offset.' with whence '.\var_export($whence, true));
+        }
+    }
+
+    public function rewind(): void
+    {
+        $this->seek(0);
+    }
+
+    public function isWritable(): bool
+    {
+        return $this->writable;
+    }
+
+    public function write($string): int
+    {
+        if (!$this->writable) {
+            throw new \RuntimeException('Cannot write to a non-writable stream');
+        }
+
+        // We can't know the size after writing anything
+        $this->size = null;
+
+        if (false === $result = \fwrite($this->stream, $string)) {
+            throw new \RuntimeException('Unable to write to stream');
+        }
+
+        return $result;
+    }
+
+    public function isReadable(): bool
+    {
+        return $this->readable;
+    }
+
+    public function read($length): string
+    {
+        if (!$this->readable) {
+            throw new \RuntimeException('Cannot read from non-readable stream');
+        }
+
+        return \fread($this->stream, $length);
+    }
+
+    public function getContents(): string
+    {
+        if (!isset($this->stream)) {
+            throw new \RuntimeException('Unable to read stream contents');
+        }
+
+        if (false === $contents = \stream_get_contents($this->stream)) {
+            throw new \RuntimeException('Unable to read stream contents');
+        }
+
+        return $contents;
+    }
+
+    public function getMetadata($key = null)
+    {
+        if (!isset($this->stream)) {
+            return $key ? null : [];
+        } elseif (null === $key) {
+            return \stream_get_meta_data($this->stream);
+        }
+
+        $meta = \stream_get_meta_data($this->stream);
+
+        return isset($meta[$key]) ? $meta[$key] : null;
+    }
+}
diff --git a/vendor/nyholm/psr7/src/UploadedFile.php b/vendor/nyholm/psr7/src/UploadedFile.php
new file mode 100644
index 0000000000000000000000000000000000000000..4cf18100eb5bc4407b1091bbec3c84ea4fc8a674
--- /dev/null
+++ b/vendor/nyholm/psr7/src/UploadedFile.php
@@ -0,0 +1,249 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Nyholm\Psr7;
+
+use Psr\Http\Message\{StreamInterface, UploadedFileInterface};
+
+/**
+ * @author Michael Dowling and contributors to guzzlehttp/psr7
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ * @author Martijn van der Ven <martijn@vanderven.se>
+ */
+final class UploadedFile implements UploadedFileInterface
+{
+    /** @var int[] */
+    private static $errors = [
+        \UPLOAD_ERR_OK, \UPLOAD_ERR_INI_SIZE, \UPLOAD_ERR_FORM_SIZE, \UPLOAD_ERR_PARTIAL, \UPLOAD_ERR_NO_FILE,
+        \UPLOAD_ERR_NO_TMP_DIR, \UPLOAD_ERR_CANT_WRITE, \UPLOAD_ERR_EXTENSION,
+    ];
+
+    /** @var string */
+    private $clientFilename;
+
+    /** @var string */
+    private $clientMediaType;
+
+    /** @var int */
+    private $error;
+
+    /** @var null|string */
+    private $file;
+
+    /** @var bool */
+    private $moved = false;
+
+    /** @var null|int */
+    private $size;
+
+    /** @var null|StreamInterface */
+    private $stream;
+
+    /**
+     * @param StreamInterface|string|resource $streamOrFile
+     * @param int                             $size
+     * @param int                             $errorStatus
+     * @param string|null                     $clientFilename
+     * @param string|null                     $clientMediaType
+     */
+    public function __construct($streamOrFile, $size, $errorStatus, $clientFilename = null, $clientMediaType = null)
+    {
+        $this->setError($errorStatus);
+        $this->setSize($size);
+        $this->setClientFilename($clientFilename);
+        $this->setClientMediaType($clientMediaType);
+
+        if ($this->isOk()) {
+            $this->setStreamOrFile($streamOrFile);
+        }
+    }
+
+    /**
+     * Depending on the value set file or stream variable.
+     *
+     * @param string|resource|StreamInterface $streamOrFile
+     *
+     * @throws \InvalidArgumentException
+     */
+    private function setStreamOrFile($streamOrFile): void
+    {
+        if (\is_string($streamOrFile)) {
+            $this->file = $streamOrFile;
+        } elseif (\is_resource($streamOrFile)) {
+            $this->stream = Stream::create($streamOrFile);
+        } elseif ($streamOrFile instanceof StreamInterface) {
+            $this->stream = $streamOrFile;
+        } else {
+            throw new \InvalidArgumentException('Invalid stream or file provided for UploadedFile');
+        }
+    }
+
+    private function setError($error): void
+    {
+        if (false === \is_int($error)) {
+            throw new \InvalidArgumentException('Upload file error status must be an integer');
+        }
+
+        if (false === \in_array($error, self::$errors)) {
+            throw new \InvalidArgumentException('Invalid error status for UploadedFile');
+        }
+
+        $this->error = $error;
+    }
+
+    private function setSize($size): void
+    {
+        if (false === \is_int($size)) {
+            throw new \InvalidArgumentException('Upload file size must be an integer');
+        }
+
+        $this->size = $size;
+    }
+
+    private function isStringOrNull($param): bool
+    {
+        return \in_array(\gettype($param), ['string', 'NULL']);
+    }
+
+    private function isStringNotEmpty($param): bool
+    {
+        return \is_string($param) && false === empty($param);
+    }
+
+    private function setClientFilename($clientFilename): void
+    {
+        if (false === $this->isStringOrNull($clientFilename)) {
+            throw new \InvalidArgumentException('Upload file client filename must be a string or null');
+        }
+
+        $this->clientFilename = $clientFilename;
+    }
+
+    private function setClientMediaType($clientMediaType): void
+    {
+        if (false === $this->isStringOrNull($clientMediaType)) {
+            throw new \InvalidArgumentException('Upload file client media type must be a string or null');
+        }
+
+        $this->clientMediaType = $clientMediaType;
+    }
+
+    /**
+     * @return bool return true if there is no upload error
+     */
+    private function isOk(): bool
+    {
+        return \UPLOAD_ERR_OK === $this->error;
+    }
+
+    /**
+     * @throws \RuntimeException if is moved or not ok
+     */
+    private function validateActive(): void
+    {
+        if (false === $this->isOk()) {
+            throw new \RuntimeException('Cannot retrieve stream due to upload error');
+        }
+
+        if ($this->moved) {
+            throw new \RuntimeException('Cannot retrieve stream after it has already been moved');
+        }
+    }
+
+    public function getStream(): StreamInterface
+    {
+        $this->validateActive();
+
+        if ($this->stream instanceof StreamInterface) {
+            return $this->stream;
+        }
+
+        $resource = \fopen($this->file, 'r');
+
+        return Stream::create($resource);
+    }
+
+    public function moveTo($targetPath): void
+    {
+        $this->validateActive();
+
+        if (false === $this->isStringNotEmpty($targetPath)) {
+            throw new \InvalidArgumentException('Invalid path provided for move operation; must be a non-empty string');
+        }
+
+        if (null !== $this->file) {
+            $this->moved = 'cli' === PHP_SAPI ? \rename($this->file, $targetPath) : \move_uploaded_file($this->file, $targetPath);
+        } else {
+            $stream = $this->getStream();
+            if ($stream->isSeekable()) {
+                $stream->rewind();
+            }
+            $this->copyToStream($stream, Stream::create(\fopen($targetPath, 'w')));
+            $this->moved = true;
+        }
+
+        if (false === $this->moved) {
+            throw new \RuntimeException(\sprintf('Uploaded file could not be moved to %s', $targetPath));
+        }
+    }
+
+    public function getSize(): ?int
+    {
+        return $this->size;
+    }
+
+    public function getError(): int
+    {
+        return $this->error;
+    }
+
+    public function getClientFilename(): ?string
+    {
+        return $this->clientFilename;
+    }
+
+    public function getClientMediaType(): ?string
+    {
+        return $this->clientMediaType;
+    }
+
+    /**
+     * Copy the contents of a stream into another stream until the given number
+     * of bytes have been read.
+     *
+     * @author Michael Dowling and contributors to guzzlehttp/psr7
+     *
+     * @param StreamInterface $source Stream to read from
+     * @param StreamInterface $dest   Stream to write to
+     * @param int             $maxLen Maximum number of bytes to read. Pass -1
+     *                                to read the entire stream
+     *
+     * @throws \RuntimeException on error
+     */
+    private function copyToStream(StreamInterface $source, StreamInterface $dest, $maxLen = -1)
+    {
+        if ($maxLen === -1) {
+            while (!$source->eof()) {
+                if (!$dest->write($source->read(1048576))) {
+                    break;
+                }
+            }
+
+            return;
+        }
+
+        $bytes = 0;
+        while (!$source->eof()) {
+            $buf = $source->read($maxLen - $bytes);
+            if (!($len = \strlen($buf))) {
+                break;
+            }
+            $bytes += $len;
+            $dest->write($buf);
+            if ($bytes === $maxLen) {
+                break;
+            }
+        }
+    }
+}
diff --git a/vendor/nyholm/psr7/src/Uri.php b/vendor/nyholm/psr7/src/Uri.php
new file mode 100644
index 0000000000000000000000000000000000000000..8376604333052807e15279581b25c7f79ee3b708
--- /dev/null
+++ b/vendor/nyholm/psr7/src/Uri.php
@@ -0,0 +1,329 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Nyholm\Psr7;
+
+use Psr\Http\Message\UriInterface;
+
+/**
+ * PSR-7 URI implementation.
+ *
+ * @author Michael Dowling
+ * @author Tobias Schultze
+ * @author Matthew Weier O'Phinney
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
+ * @author Martijn van der Ven <martijn@vanderven.se>
+ */
+final class Uri implements UriInterface
+{
+    private static $schemes = ['http' => 80, 'https' => 443];
+
+    private static $charUnreserved = 'a-zA-Z0-9_\-\.~';
+
+    private static $charSubDelims = '!\$&\'\(\)\*\+,;=';
+
+    /** @var string Uri scheme. */
+    private $scheme = '';
+
+    /** @var string Uri user info. */
+    private $userInfo = '';
+
+    /** @var string Uri host. */
+    private $host = '';
+
+    /** @var int|null Uri port. */
+    private $port;
+
+    /** @var string Uri path. */
+    private $path = '';
+
+    /** @var string Uri query string. */
+    private $query = '';
+
+    /** @var string Uri fragment. */
+    private $fragment = '';
+
+    public function __construct(string $uri = '')
+    {
+        if ('' !== $uri) {
+            if (false === $parts = \parse_url($uri)) {
+                throw new \InvalidArgumentException("Unable to parse URI: $uri");
+            }
+
+            $this->applyParts($parts);
+        }
+    }
+
+    public function __toString(): string
+    {
+        return self::createUriString($this->scheme, $this->getAuthority(), $this->path, $this->query, $this->fragment);
+    }
+
+    public function getScheme(): string
+    {
+        return $this->scheme;
+    }
+
+    public function getAuthority(): string
+    {
+        if ('' === $this->host) {
+            return '';
+        }
+
+        $authority = $this->host;
+        if ('' !== $this->userInfo) {
+            $authority = $this->userInfo.'@'.$authority;
+        }
+
+        if (null !== $this->port) {
+            $authority .= ':'.$this->port;
+        }
+
+        return $authority;
+    }
+
+    public function getUserInfo(): string
+    {
+        return $this->userInfo;
+    }
+
+    public function getHost(): string
+    {
+        return $this->host;
+    }
+
+    public function getPort(): ?int
+    {
+        return $this->port;
+    }
+
+    public function getPath(): string
+    {
+        return $this->path;
+    }
+
+    public function getQuery(): string
+    {
+        return $this->query;
+    }
+
+    public function getFragment(): string
+    {
+        return $this->fragment;
+    }
+
+    public function withScheme($scheme): self
+    {
+        if ($this->scheme === $scheme = $this->filterScheme($scheme)) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->scheme = $scheme;
+        $new->port = $new->filterPort($new->port);
+
+        return $new;
+    }
+
+    public function withUserInfo($user, $password = null): self
+    {
+        $info = $user;
+        if (null !== $password && '' !== $password) {
+            $info .= ':'.$password;
+        }
+
+        if ($this->userInfo === $info) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->userInfo = $info;
+
+        return $new;
+    }
+
+    public function withHost($host): self
+    {
+        if ($this->host === $host = $this->filterHost($host)) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->host = $host;
+
+        return $new;
+    }
+
+    public function withPort($port): self
+    {
+        if ($this->port === $port = $this->filterPort($port)) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->port = $port;
+
+        return $new;
+    }
+
+    public function withPath($path): self
+    {
+        if ($this->path === $path = $this->filterPath($path)) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->path = $path;
+
+        return $new;
+    }
+
+    public function withQuery($query): self
+    {
+        if ($this->query === $query = $this->filterQueryAndFragment($query)) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->query = $query;
+
+        return $new;
+    }
+
+    public function withFragment($fragment): self
+    {
+        if ($this->fragment === $fragment = $this->filterQueryAndFragment($fragment)) {
+            return $this;
+        }
+
+        $new = clone $this;
+        $new->fragment = $fragment;
+
+        return $new;
+    }
+
+    /**
+     * Apply parse_url parts to a URI.
+     *
+     * @param array $parts Array of parse_url parts to apply
+     */
+    private function applyParts(array $parts): void
+    {
+        $this->scheme = isset($parts['scheme']) ? $this->filterScheme($parts['scheme']) : '';
+        $this->userInfo = isset($parts['user']) ? $parts['user'] : '';
+        $this->host = isset($parts['host']) ? $this->filterHost($parts['host']) : '';
+        $this->port = isset($parts['port']) ? $this->filterPort($parts['port']) : null;
+        $this->path = isset($parts['path']) ? $this->filterPath($parts['path']) : '';
+        $this->query = isset($parts['query']) ? $this->filterQueryAndFragment($parts['query']) : '';
+        $this->fragment = isset($parts['fragment']) ? $this->filterQueryAndFragment($parts['fragment']) : '';
+        if (isset($parts['pass'])) {
+            $this->userInfo .= ':'.$parts['pass'];
+        }
+    }
+
+    /**
+     * Create a URI string from its various parts.
+     */
+    private static function createUriString(string $scheme, string $authority, string $path, string $query, string $fragment): string
+    {
+        $uri = '';
+        if ('' !== $scheme) {
+            $uri .= $scheme.':';
+        }
+
+        if ('' !== $authority) {
+            $uri .= '//'.$authority;
+        }
+
+        if ('' !== $path) {
+            if ('/' !== $path[0]) {
+                if ('' !== $authority) {
+                    // If the path is rootless and an authority is present, the path MUST be prefixed by "/"
+                    $path = '/'.$path;
+                }
+            } elseif (isset($path[1]) && '/' === $path[1]) {
+                if ('' === $authority) {
+                    // If the path is starting with more than one "/" and no authority is present, the
+                    // starting slashes MUST be reduced to one.
+                    $path = '/'.\ltrim($path, '/');
+                }
+            }
+
+            $uri .= $path;
+        }
+
+        if ('' !== $query) {
+            $uri .= '?'.$query;
+        }
+
+        if ('' !== $fragment) {
+            $uri .= '#'.$fragment;
+        }
+
+        return $uri;
+    }
+
+    /**
+     * Is a given port non-standard for the current scheme?
+     */
+    private static function isNonStandardPort(string $scheme, int $port): bool
+    {
+        return !isset(self::$schemes[$scheme]) || $port !== self::$schemes[$scheme];
+    }
+
+    private function filterScheme($scheme): string
+    {
+        if (!\is_string($scheme)) {
+            throw new \InvalidArgumentException('Scheme must be a string');
+        }
+
+        return \strtolower($scheme);
+    }
+
+    private function filterHost($host): string
+    {
+        if (!\is_string($host)) {
+            throw new \InvalidArgumentException('Host must be a string');
+        }
+
+        return \strtolower($host);
+    }
+
+    private function filterPort($port): ?int
+    {
+        if (null === $port) {
+            return null;
+        }
+
+        $port = (int) $port;
+        if (1 > $port || 0xffff < $port) {
+            throw new \InvalidArgumentException(\sprintf('Invalid port: %d. Must be between 1 and 65535', $port));
+        }
+
+        return self::isNonStandardPort($this->scheme, $port) ? $port : null;
+    }
+
+    private function filterPath($path): string
+    {
+        if (!\is_string($path)) {
+            throw new \InvalidArgumentException('Path must be a string');
+        }
+
+        return \preg_replace_callback('/(?:[^'.self::$charUnreserved.self::$charSubDelims.'%:@\/]++|%(?![A-Fa-f0-9]{2}))/', [$this, 'rawurlencodeMatchZero'], $path);
+    }
+
+    private function filterQueryAndFragment($str): string
+    {
+        if (!\is_string($str)) {
+            throw new \InvalidArgumentException('Query and fragment must be a string');
+        }
+
+        return \preg_replace_callback('/(?:[^'.self::$charUnreserved.self::$charSubDelims.'%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/', [$this, 'rawurlencodeMatchZero'], $str);
+    }
+
+    private function rawurlencodeMatchZero(array $match): string
+    {
+        return \rawurlencode($match[0]);
+    }
+}
diff --git a/vendor/psr/http-client/CHANGELOG.md b/vendor/psr/http-client/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..0bd22bb32b520e2acf61d648856f7f45ae163fc2
--- /dev/null
+++ b/vendor/psr/http-client/CHANGELOG.md
@@ -0,0 +1,4 @@
+# Changelog
+
+All notable changes to this project will be documented in this file, in reverse chronological order by release.
+
diff --git a/vendor/psr/http-client/LICENSE b/vendor/psr/http-client/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..cd5e0020afc2f89778937db2a8e3fc31c32b82b6
--- /dev/null
+++ b/vendor/psr/http-client/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2017 PHP Framework Interoperability Group
+
+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/psr/http-client/README.md b/vendor/psr/http-client/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..4892c32d88900ea23b9a12cce6e27f4ae098e665
--- /dev/null
+++ b/vendor/psr/http-client/README.md
@@ -0,0 +1,14 @@
+PSR Http Client
+===============
+
+This repository holds all interfaces/classes/traits related to
+[PSR-18](http://www.php-fig.org/psr/psr-18/).
+
+Note that this is not an HTTP client implementation of its own. It is merely an
+interface that describes an HTTP client. See 
+[the specification](https://github.com/php-fig/fig-standards/blob/master/proposed/http-client/http-client.md)
+for more details.
+
+You can find implementations of the specification by looking for packages providing 
+the [psr/http-client-implementation](https://packagist.org/providers/psr/http-client-implementation)
+virtual package.
diff --git a/vendor/psr/http-client/composer.json b/vendor/psr/http-client/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..78cb39b6d037cd672e089ee236029d29ce659032
--- /dev/null
+++ b/vendor/psr/http-client/composer.json
@@ -0,0 +1,27 @@
+{
+    "name": "psr/http-client",
+    "description": "Common interface for HTTP clients",
+    "keywords": ["psr", "psr-18", "http", "http-client"],
+    "homepage": "https://github.com/php-fig/http-client",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "PHP-FIG",
+            "homepage": "http://www.php-fig.org/"
+        }
+    ],
+    "require": {
+        "php": "^7.0",
+        "psr/http-message": "^1.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Psr\\Http\\Client\\": "src/"
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.0.x-dev"
+        }
+    }
+}
diff --git a/vendor/psr/http-client/src/ClientException.php b/vendor/psr/http-client/src/ClientException.php
new file mode 100644
index 0000000000000000000000000000000000000000..6c6be1691178e4bc2e54141d54c329bfa8e3def9
--- /dev/null
+++ b/vendor/psr/http-client/src/ClientException.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace Psr\Http\Client;
+
+/**
+ * Every HTTP client related Exception MUST implement this interface.
+ */
+interface ClientException extends \Throwable
+{
+}
\ No newline at end of file
diff --git a/vendor/psr/http-client/src/ClientInterface.php b/vendor/psr/http-client/src/ClientInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..d29ef796f40b1569532018443aed1fcfbf3528fd
--- /dev/null
+++ b/vendor/psr/http-client/src/ClientInterface.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Psr\Http\Client;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+interface ClientInterface
+{
+    /**
+     * Sends a PSR-7 request and returns a PSR-7 response.
+     *
+     * Every technically correct HTTP response MUST be returned as is, even if it represents a HTTP
+     * error response or a redirect instruction. The only special case is 1xx responses, which MUST
+     * be assembled in the HTTP client.
+     *
+     * The client MAY do modifications to the Request before sending it. Because PSR-7 objects are
+     * immutable, one cannot assume that the object passed to ClientInterface::sendRequest() will be the same
+     * object that is actually sent. For example the Request object that is returned by an exception MAY
+     * be a different object than the one passed to sendRequest, so comparison by reference (===) is not possible.
+     *
+     * {@link https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message-meta.md#why-value-objects}
+     *
+     * @param RequestInterface $request
+     *
+     * @return ResponseInterface
+     *
+     * @throws \Psr\Http\Client\ClientException If an error happens during processing the request.
+     */
+    public function sendRequest(RequestInterface $request): ResponseInterface;
+}
diff --git a/vendor/psr/http-client/src/Exception/NetworkException.php b/vendor/psr/http-client/src/Exception/NetworkException.php
new file mode 100644
index 0000000000000000000000000000000000000000..3b24090352bae04a637949c10ba6ec16084875c1
--- /dev/null
+++ b/vendor/psr/http-client/src/Exception/NetworkException.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Psr\Http\Client\Exception;
+
+use Psr\Http\Client\ClientException;
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * Thrown when the request cannot be completed because of network issues.
+ *
+ * There is no response object as this exception is thrown when no response has been received.
+ *
+ * Example: the target host name can not be resolved or the connection failed.
+ */
+interface NetworkException extends ClientException
+{
+    /**
+     * Returns the request.
+     *
+     * The request object MAY be a different object from the one passed to ClientInterface::sendRequest()
+     *
+     * @return RequestInterface
+     */
+    public function getRequest(): RequestInterface;
+}
\ No newline at end of file
diff --git a/vendor/psr/http-client/src/Exception/RequestException.php b/vendor/psr/http-client/src/Exception/RequestException.php
new file mode 100644
index 0000000000000000000000000000000000000000..4c392bdc64940baff98f11b6362d16cc10d438e5
--- /dev/null
+++ b/vendor/psr/http-client/src/Exception/RequestException.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Psr\Http\Client\Exception;
+
+use Psr\Http\Client\ClientException;
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * Exception for when a request failed.
+ *
+ * Examples:
+ *      - Request is invalid (e.g. method is missing)
+ *      - Runtime request errors (e.g. the body stream is not seekable)
+ */
+interface RequestException extends ClientException
+{
+    /**
+     * Returns the request.
+     *
+     * The request object MAY be a different object from the one passed to ClientInterface::sendRequest()
+     *
+     * @return RequestInterface
+     */
+    public function getRequest(): RequestInterface;
+}
diff --git a/vendor/psr/http-factory/.gitignore b/vendor/psr/http-factory/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..d8a7996ab38543fe86834f6cffca7af09b935ec8
--- /dev/null
+++ b/vendor/psr/http-factory/.gitignore
@@ -0,0 +1,2 @@
+composer.lock
+vendor/
diff --git a/vendor/psr/http-factory/.pullapprove.yml b/vendor/psr/http-factory/.pullapprove.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8cf081942c727499edb57c8f33df8701ef664748
--- /dev/null
+++ b/vendor/psr/http-factory/.pullapprove.yml
@@ -0,0 +1,7 @@
+extends: default
+reviewers:
+    -
+        name: contributors
+        required: 1
+        teams:
+            - http-factory-contributors
diff --git a/vendor/psr/http-factory/LICENSE b/vendor/psr/http-factory/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..3f1559b2ad6a91d451af17c73208f58984d08e66
--- /dev/null
+++ b/vendor/psr/http-factory/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 PHP-FIG
+
+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/psr/http-factory/README.md b/vendor/psr/http-factory/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..f86868a0cff084dad3950929d35d113bdae4ee95
--- /dev/null
+++ b/vendor/psr/http-factory/README.md
@@ -0,0 +1,7 @@
+HTTP Factories
+==============
+
+This is the implementation of [PSR-17 (HTTP Message Factories)][psr-17]. Please refer to the
+specification for a description.
+
+[psr-17]: https://www.php-fig.org/psr/psr-17/
diff --git a/vendor/psr/http-factory/composer.json b/vendor/psr/http-factory/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..af62b290f918398db8fd557b2bcf6a5cae1e62f3
--- /dev/null
+++ b/vendor/psr/http-factory/composer.json
@@ -0,0 +1,35 @@
+{
+    "name": "psr/http-factory",
+    "description": "Common interfaces for PSR-7 HTTP message factories",
+    "keywords": [
+        "psr",
+        "psr-7",
+        "psr-17",
+        "http",
+        "factory",
+        "message",
+        "request",
+        "response"
+    ],
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "PHP-FIG",
+            "homepage": "http://www.php-fig.org/"
+        }
+    ],
+    "require": {
+        "php": ">=7.0.0",
+        "psr/http-message": "^1.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "Psr\\Http\\Message\\": "src/"
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.0.x-dev"
+        }
+    }
+}
diff --git a/vendor/psr/http-factory/src/RequestFactoryInterface.php b/vendor/psr/http-factory/src/RequestFactoryInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..cb39a08bf5c9969d9d8f8fc6bab847868583c465
--- /dev/null
+++ b/vendor/psr/http-factory/src/RequestFactoryInterface.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Psr\Http\Message;
+
+interface RequestFactoryInterface
+{
+    /**
+     * Create a new request.
+     *
+     * @param string $method The HTTP method associated with the request.
+     * @param UriInterface|string $uri The URI associated with the request. If
+     *     the value is a string, the factory MUST create a UriInterface
+     *     instance based on it.
+     *
+     * @return RequestInterface
+     */
+    public function createRequest(string $method, $uri): RequestInterface;
+}
diff --git a/vendor/psr/http-factory/src/ResponseFactoryInterface.php b/vendor/psr/http-factory/src/ResponseFactoryInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..212562f00f3cb66e00d8534eb7dc88ec4b46f72d
--- /dev/null
+++ b/vendor/psr/http-factory/src/ResponseFactoryInterface.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Psr\Http\Message;
+
+interface ResponseFactoryInterface
+{
+    /**
+     * Create a new response.
+     *
+     * @param int $code HTTP status code; defaults to 200
+     * @param string $reasonPhrase Reason phrase to associate with status code
+     *     in generated response; if none is provided implementations MAY use
+     *     the defaults as suggested in the HTTP specification.
+     *
+     * @return ResponseInterface
+     */
+    public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface;
+}
diff --git a/vendor/psr/http-factory/src/ServerRequestFactoryInterface.php b/vendor/psr/http-factory/src/ServerRequestFactoryInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..9fe031a8f4a1989a0779f45b7fb0584ff29fda61
--- /dev/null
+++ b/vendor/psr/http-factory/src/ServerRequestFactoryInterface.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Psr\Http\Message;
+
+interface ServerRequestFactoryInterface
+{
+    /**
+     * Create a new server request.
+     *
+     * Note that server-params are taken precisely as given - no parsing/processing
+     * of the given values is performed, and, in particular, no attempt is made to
+     * determine the HTTP method or URI, which must be provided explicitly.
+     *
+     * @param string $method The HTTP method associated with the request.
+     * @param UriInterface|string $uri The URI associated with the request. If
+     *     the value is a string, the factory MUST create a UriInterface
+     *     instance based on it.
+     * @param array $serverParams Array of SAPI parameters with which to seed
+     *     the generated request instance.
+     *
+     * @return ServerRequestInterface
+     */
+    public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface;
+}
diff --git a/vendor/psr/http-factory/src/StreamFactoryInterface.php b/vendor/psr/http-factory/src/StreamFactoryInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..ef4a936060ebf9b720eaae49dc7ba5a139a7aed5
--- /dev/null
+++ b/vendor/psr/http-factory/src/StreamFactoryInterface.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Psr\Http\Message;
+
+interface StreamFactoryInterface
+{
+    /**
+     * Create a new stream from a string.
+     *
+     * The stream SHOULD be created with a temporary resource.
+     *
+     * @param string $content String content with which to populate the stream.
+     *
+     * @return StreamInterface
+     */
+    public function createStream(string $content = ''): StreamInterface;
+
+    /**
+     * Create a stream from an existing file.
+     *
+     * The file MUST be opened using the given mode, which may be any mode
+     * supported by the `fopen` function.
+     *
+     * The `$filename` MAY be any string supported by `fopen()`.
+     *
+     * @param string $filename Filename or stream URI to use as basis of stream.
+     * @param string $mode Mode with which to open the underlying filename/stream.
+     *
+     * @return StreamInterface
+     */
+    public function createStreamFromFile(string $filename, string $mode = 'r'): StreamInterface;
+
+    /**
+     * Create a new stream from an existing resource.
+     *
+     * The stream MUST be readable and may be writable.
+     *
+     * @param resource $resource PHP resource to use as basis of stream.
+     *
+     * @return StreamInterface
+     */
+    public function createStreamFromResource($resource): StreamInterface;
+}
diff --git a/vendor/psr/http-factory/src/UploadedFileFactoryInterface.php b/vendor/psr/http-factory/src/UploadedFileFactoryInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..7db4e30af736bffb28181cf8f454ea3328d0e833
--- /dev/null
+++ b/vendor/psr/http-factory/src/UploadedFileFactoryInterface.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Psr\Http\Message;
+
+interface UploadedFileFactoryInterface
+{
+    /**
+     * Create a new uploaded file.
+     *
+     * If a size is not provided it will be determined by checking the size of
+     * the file.
+     *
+     * @see http://php.net/manual/features.file-upload.post-method.php
+     * @see http://php.net/manual/features.file-upload.errors.php
+     *
+     * @param StreamInterface $stream Underlying stream representing the
+     *     uploaded file content.
+     * @param int $size in bytes
+     * @param int $error PHP file upload error
+     * @param string $clientFilename Filename as provided by the client, if any.
+     * @param string $clientMediaType Media type as provided by the client, if any.
+     *
+     * @return UploadedFileInterface
+     *
+     * @throws \InvalidArgumentException If the file resource is not readable.
+     */
+    public function createUploadedFile(
+        StreamInterface $stream,
+        int $size = null,
+        int $error = \UPLOAD_ERR_OK,
+        string $clientFilename = null,
+        string $clientMediaType = null
+    ): UploadedFileInterface;
+}
diff --git a/vendor/psr/http-factory/src/UriFactoryInterface.php b/vendor/psr/http-factory/src/UriFactoryInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..06df0b46acd072a490ca9df1182abb2b01acbc14
--- /dev/null
+++ b/vendor/psr/http-factory/src/UriFactoryInterface.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Psr\Http\Message;
+
+interface UriFactoryInterface
+{
+    /**
+     * Create a new URI.
+     *
+     * @param string $uri
+     *
+     * @return UriInterface
+     *
+     * @throws \InvalidArgumentException If the given URI cannot be parsed.
+     */
+    public function createUri(string $uri = ''): UriInterface;
+}
diff --git a/vendor/respect/validation/docs/concrete-api.md b/vendor/respect/validation/docs/concrete-api.md
new file mode 100644
index 0000000000000000000000000000000000000000..735b6283d558ed32516a61b8ef6e5c87a2ee75da
--- /dev/null
+++ b/vendor/respect/validation/docs/concrete-api.md
@@ -0,0 +1,77 @@
+# Concrete API
+
+There are many micro-frameworks that rely on magic methods. We don't. In this
+document we're gonna explore the Respect\Validation API without fluent interfaces
+or magic methods. We'll use a traditional dependency injection approach.
+
+```php
+use Respect\Validation\Validator as v;
+
+$usernameValidator = v::alnum()->noWhitespace()->length(1,15);
+$usernameValidator->validate('alganet'); // true
+```
+
+If you `var_dump($usernameValidator)`, you'll see a composite of objects with
+`Respect\Validation\Rules\Alnum`, `Respect\Validation\Rules\NoWhitespace` and
+`Respect\Validation\Rules\Length`. There is a specific object for each rule, and
+the chain only builds the structure. You can build it by yourself:
+
+```php
+use Respect\Validation\Rules;
+
+$usernameValidator = new Rules\AllOf(
+    new Rules\Alnum(),
+    new Rules\NoWhitespace(),
+    new Rules\Length(1, 15)
+);
+$usernameValidator->validate('alganet'); // true
+```
+
+This is still a very lean API. You can use it in any dependency injection
+container or test it in the way you want. Nesting is still possible:
+
+```php
+use Respect\Validation\Rules;
+
+$usernameValidator = new Rules\AllOf(
+    new Rules\Alnum(),
+    new Rules\NoWhitespace(),
+    new Rules\Length(1, 15)
+);
+$userValidator = new Rules\Key('name', $usernameValidator);
+$userValidator->validate(['name' => 'alganet']); // true
+```
+
+## How It Works?
+
+The Respect\Validation chain is an
+[internal DSL](http://martinfowler.com/bliki/InternalDslStyle.html).
+It acts in the creational realm of objects (where Abstract Factories and Builders
+live), and it's only job is to make rule construction terse and fluent.
+
+## FAQ
+
+> Is `v` in `v::something` a class name?
+
+No! The class is `Respect\Validation\Validator`, we suggest `v` as a very short alias.
+
+> Is `v::something()` a static call?
+
+Yes. Just like the default `DateTime::createFromFormat()` or
+`Doctrine\ORM\Tools\Setup::createAnnotationMetadataConfiguration()`. It builds
+something complex and returns for you.
+
+> I really don't like static calls, can I avoid it?
+
+Yes. Just use `$validator = new Validator();` each time you want a new validator,
+and continue from there.
+
+> Do you have a static method for each rule?
+
+No. We use `__callStatic()`.
+
+> Magic methods are slow! Why do you use them?
+
+They're optional. If you use the `new` interface, they won't be called.
+
+(still, do some benchmarks, you'd be surprised with our implementation).
diff --git a/vendor/respect/validation/docs/feature-guide.md b/vendor/respect/validation/docs/feature-guide.md
new file mode 100644
index 0000000000000000000000000000000000000000..5d96e007909c98625bbe10fb9f04f61a5ed98535
--- /dev/null
+++ b/vendor/respect/validation/docs/feature-guide.md
@@ -0,0 +1,301 @@
+# Feature Guide
+
+## Namespace import
+
+Respect\Validation is namespaced, but you can make your life easier by importing
+a single class into your context:
+
+```php
+use Respect\Validation\Validator as v;
+```
+
+## Simple validation
+
+The Hello World validator is something like this:
+
+```php
+$number = 123;
+v::numeric()->validate($number); // true
+```
+
+## Chained validation
+
+It is possible to use validators in a chain. Sample below validates a string
+containing numbers and letters, no whitespace and length between 1 and 15.
+
+```php
+$usernameValidator = v::alnum()->noWhitespace()->length(1, 15);
+$usernameValidator->validate('alganet'); // true
+```
+
+## Validating object attributes
+
+Given this simple object:
+
+```php
+$user = new stdClass;
+$user->name = 'Alexandre';
+$user->birthdate = '1987-07-01';
+```
+
+Is possible to validate its attributes in a single chain:
+
+```php
+$userValidator = v::attribute('name', v::stringType()->length(1,32))
+                  ->attribute('birthdate', v::date()->age(18));
+
+$userValidator->validate($user); // true
+```
+
+Validating array keys is also possible using `v::key()`
+
+Note that we used `v::stringType()` and `v::date()` in the beginning of the validator.
+Although is not mandatory, it is a good practice to use the type of the
+validated object as the first node in the chain.
+
+## Input optional
+
+On oldest versions of Respect\Validation all validators treat input as optional
+and accept an empty string input as valid. Even though a useful feature that
+caused a lot of troubles for our team and neither was an obvious behavior. Also
+there was some people who likes to accept `null` as optional value, not only an
+empty string.
+
+For that reason all rules are mandatory now but if you want to treat a value as
+optional you can use `v::optional()` rule:
+
+```php
+v::alpha()->validate(''); // false input required
+v::alpha()->validate(null); // false input required
+
+v::optional(v::alpha())->validate(''); // true
+v::optional(v::alpha())->validate(null); // true
+```
+
+By _optional_ we consider `null` or an empty string (`''`).
+
+See more on [Optional](rules/Optional.md).
+
+## Negating rules
+
+You can use the `v::not()` to negate any rule:
+
+```php
+v::not(v::intVal())->validate(10); // false, input must not be integer
+```
+
+## Validator reuse
+
+Once created, you can reuse your validator anywhere. Remember `$usernameValidator`?
+
+```php
+$usernameValidator->validate('respect');            //true
+$usernameValidator->validate('alexandre gaigalas'); // false
+$usernameValidator->validate('#$%');                //false
+```
+
+## Exception types
+
+* `Respect\Validation\Exceptions\ExceptionInterface`:
+    * All exceptions implement this interface;
+* `Respect\Validation\Exceptions\ValidationException`:
+    * Implements the `Respect\Validation\Exceptions\ExceptionInterface` interface
+    * Thrown when the `check()` fails
+    * All validation exceptions extend this class
+    * Available methods:
+        * `getMainMessage()`;
+        * `setMode($mode)`;
+        * `setName($name)`;
+        * `setParam($name, $value)`;
+        * `setTemplate($template)`;
+        * more...
+* `Respect\Validation\Exceptions\NestedValidationException`:
+    * Extends the `Respect\Validation\Exceptions\ValidationException` class
+    * Usually thrown when the `assert()` fails
+    * Available methods:
+        * `findMessages()`;
+        * `getFullMessage()`;
+        * `getMessages()`;
+        * more...
+
+## Informative exceptions
+
+When something goes wrong, Validation can tell you exactly what's going on. For this,
+we use the `assert()` method instead of `validate()`:
+
+```php
+use Respect\Validation\Exceptions\NestedValidationException;
+
+try {
+    $usernameValidator->assert('really messed up screen#name');
+} catch(NestedValidationException $exception) {
+   echo $exception->getFullMessage();
+}
+```
+
+The printed message is exactly this, as a nested Markdown list:
+
+```no-highlight
+- All of the required rules must pass for "really messed up screen#name"
+  - "really messed up screen#name" must contain only letters (a-z) and digits (0-9)
+  - "really messed up screen#name" must not contain whitespace
+  - "really messed up screen#name" must have a length between 1 and 15
+```
+
+## Getting all messages as an array
+
+The Markdown list is fine, but unusable on a HTML form or something more custom.
+For that you can use `getMessages()`.
+
+It will return all messages from the rules that did not pass the validation.
+
+```php
+try {
+    $usernameValidator->assert('really messed up screen#name');
+} catch(NestedValidationException $exception) {
+    print_r($exception->getMessages());
+}
+```
+
+The code above may display something like:
+
+```no-highlight
+Array
+(
+    [0] => "really messed up screen#name" must contain only letters (a-z) and digits (0-9)
+    [1] => "really messed up screen#name" must not contain whitespace
+    [2] => "really messed up screen#name" must have a length between 1 and 15
+)
+```
+
+## Getting messages as an array by name
+
+If you want to get specific message by name you can use `findMessages()` passing
+the names of the rules you want:
+
+```php
+try {
+    $usernameValidator->assert('really messed up screen#name');
+} catch(NestedValidationException $exception) {
+    print_r($exception->findMessages(['alnum', 'noWhitespace']));
+}
+```
+
+The `findMessages()` returns an array with messages from the requested validators,
+like this:
+
+```no-highlight
+Array
+(
+    [alnum] => "really messed up screen#name" must contain only letters (a-z) and digits (0-9)
+    [noWhitespace] => "really messed up screen#name" must not contain whitespace
+)
+```
+
+## Custom messages
+
+Getting messages as an array is fine, but sometimes you need to customize them in order
+to present them to the user. This is possible using the `findMessages()` method as well:
+
+```php
+$errors = $exception->findMessages([
+    'alnum' => '{{name}} must contain only letters and digits',
+    'length' => '{{name}} must not have more than 15 chars',
+    'noWhitespace' => '{{name}} cannot contain spaces'
+]);
+```
+
+For all messages, the `{{name}}` variable is available for templates. If you
+do not define a name it uses the input to replace this placeholder.
+
+## Message localization
+
+You're also able to translate your message to another language with Validation.
+The only thing one must do is to define the param `translator` as a callable that
+will handle the translation:
+
+```php
+$exception->setParam('translator', 'gettext');
+```
+
+The example above uses `gettext()` but you can use any other callable value, like
+`[$translator, 'trans']` or `you_custom_function()`.
+
+After that, if you call `getMainMessage()` or `getFullMessage()` (for nested),
+the message will be translated.
+
+Note that `getMessage()` will keep the original message.
+
+## Custom rules
+
+You also can use your own rules:
+
+```php
+namespace My\Validation\Rules;
+
+use Respect\Validation\Rules\AbstractRule;
+
+class MyRule extends AbstractRule
+{
+    public function validate($input)
+    {
+        // Do something here with the $input and return a boolean value
+    }
+}
+```
+
+If you do want Validation to execute you rule (or rules) in the chain, you must
+use `v::with()` passing your rule's namespace as an argument:
+
+```php
+v::with('My\\Validation\\Rules\\');
+v::myRule(); // Try to load "My\Validation\Rules\MyRule" if any
+```
+
+By default `with()` appends the given prefix, but you can change this behavior
+in order to overwrite default rules:
+
+```php
+v::with('My\\Validation\\Rules', true);
+v::alnum(); // Try to use "My\Validation\Rules\Alnum" if any
+```
+
+## Validator name
+
+On `v::attribute()` and `v::key()`, `{{name}}` is the attribute/key name. For others,
+is the same as the input. You can customize a validator name using:
+
+```php
+v::date('Y-m-d')->between('1980-02-02', 'now')->setName('Member Since');
+```
+
+## Zend/Symfony validators
+
+It is also possible to reuse validators from other frameworks if they are installed:
+
+```php
+$hostnameValidator = v::zend('Hostname')->assert('google.com');
+$timeValidator     = v::sf('Time')->assert('22:00:01');
+```
+
+## Validation methods
+
+We've seen `validate()` that returns true or false and `assert()` that throws a complete
+validation report. There is also a `check()` method that returns an Exception
+only with the first error found:
+
+```php
+use Respect\Validation\Exceptions\ValidationException;
+
+try {
+    $usernameValidator->check('really messed up screen#name');
+} catch(ValidationException $exception) {
+    echo $exception->getMainMessage();
+}
+```
+
+Message:
+
+```no-highlight
+"really messed up screen#name" must contain only letters (a-z) and digits (0-9)
+```
diff --git a/vendor/respect/validation/docs/index.md b/vendor/respect/validation/docs/index.md
new file mode 100644
index 0000000000000000000000000000000000000000..433d433767db92c9f6814383afef5107525557e9
--- /dev/null
+++ b/vendor/respect/validation/docs/index.md
@@ -0,0 +1,16 @@
+# Overview
+
+[![Build Status](https://img.shields.io/travis/Respect/Validation/master.svg?style=flat-square)](http://travis-ci.org/Respect/Validation)
+[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/Respect/Validation/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/Respect/Validation/?branch=master)
+[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/Respect/Validation/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/Respect/Validation/?branch=master)
+[![Latest Stable Version](https://img.shields.io/packagist/v/respect/validation.svg?style=flat-square)](https://packagist.org/packages/respect/validation)
+[![Total Downloads](https://img.shields.io/packagist/dt/respect/validation.svg?style=flat-square)](https://packagist.org/packages/respect/validation)
+[![License](https://img.shields.io/packagist/l/respect/validation.svg?style=flat-square)](https://packagist.org/packages/respect/validation)
+
+[The most awesome validation engine ever created for PHP.](http://bit.ly/1a1oeQv)
+
+- Complex rules made simple: `v::numeric()->positive()->between(1, 255)->validate($input)`.
+- [Granularity control](feature-guide.md#validation-methods) for advanced reporting.
+- More than 100 (fully tested) validators.
+- [A concrete API](concrete-api.md) for non fluent usage.
+- Works on PHP 5.4+ or HHVM 3.3+
diff --git a/vendor/respect/validation/docs/installation.md b/vendor/respect/validation/docs/installation.md
new file mode 100644
index 0000000000000000000000000000000000000000..9c74c657c944606dd752ed2426b06bfc3d26641b
--- /dev/null
+++ b/vendor/respect/validation/docs/installation.md
@@ -0,0 +1,10 @@
+# Installation
+
+Package is available on [Packagist](http://packagist.org/packages/respect/validation),
+you can install it using [Composer](http://getcomposer.org).
+
+```shell
+composer require respect/validation
+```
+
+[PHP](https://php.net) 5.4+ or [HHVM](http://hhvm.com) 3.3+ are required.
diff --git a/vendor/respect/validation/docs/license.md b/vendor/respect/validation/docs/license.md
new file mode 100644
index 0000000000000000000000000000000000000000..ee76d0a384117a5fbdb0c17272b9cb04a6bc79b4
--- /dev/null
+++ b/vendor/respect/validation/docs/license.md
@@ -0,0 +1,30 @@
+# License
+
+Copyright (c) 2009-2015, [Alexandre Gomes Gaigalas](http://github.com/alganet).
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+  this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of Alexandre Gomes Gaigalas nor the names of its
+  contributors may be used to endorse or promote products derived from this
+  software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/respect/validation/docs/list-of-rules.md b/vendor/respect/validation/docs/list-of-rules.md
new file mode 100644
index 0000000000000000000000000000000000000000..057be7bf323dd76f31ff934b7c594be7b327f37b
--- /dev/null
+++ b/vendor/respect/validation/docs/list-of-rules.md
@@ -0,0 +1,329 @@
+# List of rules
+
+## Types
+
+  * [ArrayVal](rules/ArrayVal.md)
+  * [ArrayType](rules/ArrayType.md)
+  * [BoolVal](rules/BoolVal.md)
+  * [BoolType](rules/BoolType.md)
+  * [CallableType](rules/CallableType.md)
+  * [Countable](rules/Countable.md)
+  * [Date](rules/Date.md)
+  * [FalseVal](rules/FalseVal.md)
+  * [FloatVal](rules/FloatVal.md)
+  * [FloatType](rules/FloatType.md)
+  * [Instance](rules/Instance.md)
+  * [IntVal](rules/IntVal.md)
+  * [IntType](rules/IntType.md)
+  * [IterableType](rules/IterableType.md)
+  * [NullType](rules/NullType.md)
+  * [Numeric](rules/Numeric.md)
+  * [ObjectType](rules/ObjectType.md)
+  * [ResourceType](rules/ResourceType.md)
+  * [ScalarVal](rules/ScalarVal.md)
+  * [StringType](rules/StringType.md)
+  * [TrueVal](rules/TrueVal.md)
+  * [Type](rules/Type.md)
+  * [Xdigit](rules/Xdigit.md)
+
+## Generics
+
+  * [AlwaysInvalid](rules/AlwaysInvalid.md)
+  * [AlwaysValid](rules/AlwaysValid.md)
+  * [Call](rules/Call.md)
+  * [Callback](rules/Callback.md)
+  * [FilterVar](rules/FilterVar.md)
+  * [Not](rules/Not.md)
+  * [Optional](rules/Optional.md)
+  * [Type](rules/Type.md)
+  * [When](rules/When.md)
+
+## Comparing Values
+
+  * [Age](rules/Age.md)
+  * [Between](rules/Between.md)
+  * [Equals](rules/Equals.md)
+  * [Identical](rules/Identical.md)
+  * [Max](rules/Max.md)
+  * [Min](rules/Min.md)
+
+## Numeric
+
+  * [Between](rules/Between.md)
+  * [BoolType](rules/BoolType.md)
+  * [Even](rules/Even.md)
+  * [Factor](rules/Factor.md)
+  * [Fibonacci](rules/Fibonacci.md)
+  * [Finite](rules/Finite.md)
+  * [FloatVal](rules/FloatVal.md)
+  * [FloatType](rules/FloatType.md)
+  * [Infinite](rules/Infinite.md)
+  * [IntVal](rules/IntVal.md)
+  * [IntType](rules/IntType.md)
+  * [Multiple](rules/Multiple.md)
+  * [Negative](rules/Negative.md)
+  * [NotEmpty](rules/NotEmpty.md)
+  * [Numeric](rules/Numeric.md)
+  * [Odd](rules/Odd.md)
+  * [PerfectSquare](rules/PerfectSquare.md)
+  * [Positive](rules/Positive.md)
+  * [PrimeNumber](rules/PrimeNumber.md)
+  * [Roman](rules/Roman.md)
+  * [Xdigit](rules/Xdigit.md)
+
+## String
+
+  * [Alnum](rules/Alnum.md)
+  * [Alpha](rules/Alpha.md)
+  * [Between](rules/Between.md)
+  * [Charset](rules/Charset.md)
+  * [Cntrl](rules/Cntrl.md)
+  * [Consonant](rules/Consonant.md)
+  * [Contains](rules/Contains.md)
+  * [Digit](rules/Digit.md)
+  * [EndsWith](rules/EndsWith.md)
+  * [Graph](rules/Graph.md)
+  * [In](rules/In.md)
+  * [Length](rules/Length.md)
+  * [Lowercase](rules/Lowercase.md)
+  * [NotEmpty](rules/NotEmpty.md)
+  * [NoWhitespace](rules/NoWhitespace.md)
+  * [PhpLabel](rules/PhpLabel.md)
+  * [Prnt](rules/Prnt.md)
+  * [Punct](rules/Punct.md)
+  * [Regex](rules/Regex.md)
+  * [ResourceType](rules/ResourceType.md)
+  * [Slug](rules/Slug.md)
+  * [Space](rules/Space.md)
+  * [StartsWith](rules/StartsWith.md)
+  * [Uppercase](rules/Uppercase.md)
+  * [Version](rules/Version.md)
+  * [Vowel](rules/Vowel.md)
+  * [Xdigit](rules/Xdigit.md)
+
+## Arrays
+
+  * [ArrayVal](rules/ArrayVal.md)
+  * [ArrayType](rules/ArrayType.md)
+  * [Contains](rules/Contains.md)
+  * [Each](rules/Each.md)
+  * [EndsWith](rules/EndsWith.md)
+  * [In](rules/In.md)
+  * [Key](rules/Key.md)
+  * [KeyNested](rules/KeyNested.md)
+  * [KeySet](rules/KeySet.md)
+  * [KeyValue](rules/KeyValue.md)
+  * [Length](rules/Length.md)
+  * [NotEmpty](rules/NotEmpty.md)
+  * [StartsWith](rules/StartsWith.md)
+
+## Objects
+
+  * [Attribute](rules/Attribute.md)
+  * [Instance](rules/Instance.md)
+  * [Length](rules/Length.md)
+
+## Date and Time
+
+  * [Age](rules/Age.md)
+  * [Between](rules/Between.md)
+  * [Date](rules/Date.md)
+  * [LeapDate](rules/LeapDate.md)
+  * [LeapYear](rules/LeapYear.md)
+  * [MinimumAge](rules/MinimumAge.md)
+
+## Group Validators
+
+  * [AllOf](rules/AllOf.md)
+  * [NoneOf](rules/NoneOf.md)
+  * [OneOf](rules/OneOf.md)
+
+## Regional
+
+  * [CountryCode](rules/CountryCode.md)
+  * [CurrencyCode](rules/CurrencyCode.md)
+  * [IdentityCard](rules/IdentityCard.md)
+  * [LanguageCode](rules/LanguageCode.md)
+  * [PostalCode](rules/PostalCode.md)
+  * [SubdivisionCode](rules/SubdivisionCode.md)
+  * [Tld](rules/Tld.md)
+
+## Files
+
+  * [Directory](rules/Directory.md)
+  * [Executable](rules/Executable.md)
+  * [Exists](rules/Exists.md)
+  * [Extension](rules/Extension.md)
+  * [File](rules/File.md)
+  * [Image](rules/Image.md)
+  * [Mimetype](rules/Mimetype.md)
+  * [Readable](rules/Readable.md)
+  * [Size](rules/Size.md)
+  * [SymbolicLink](rules/SymbolicLink.md)
+  * [Uploaded](rules/Uploaded.md)
+  * [Writable](rules/Writable.md)
+
+## Banking
+
+  * [Bank](rules/Bank.md)
+  * [BankAccount](rules/BankAccount.md)
+  * [Bic](rules/Bic.md)
+
+## Other
+
+  * [Bsn](rules/Bsn.md)
+  * [Cnh](rules/Cnh.md)
+  * [Cnpj](rules/Cnpj.md)
+  * [Cpf](rules/Cpf.md)
+  * [Domain](rules/Domain.md)
+  * [Email](rules/Email.md)
+  * [HexRgbColor](rules/HexRgbColor.md)
+  * [Imei](rules/Imei.md)
+  * [Ip](rules/Ip.md)
+  * [Json](rules/Json.md)
+  * [MacAddress](rules/MacAddress.md)
+  * [NfeAccessKey](rules/NfeAccessKey.md)
+  * [NotBlank](rules/NotBlank.md)
+  * [NotOptional](rules/NotOptional.md)
+  * [Pesel](rules/Pesel.md)
+  * [Phone](rules/Phone.md)
+  * [Sf](rules/Sf.md)
+  * [Url](rules/Url.md)
+  * [VideoUrl](rules/VideoUrl.md)
+  * [Zend](rules/Zend.md)
+
+## Yes/No
+
+  * [No](rules/No.md)
+  * [Yes](rules/Yes.md)
+
+## Alphabetically
+
+  * [Age](rules/Age.md)
+  * [AllOf](rules/AllOf.md)
+  * [Alnum](rules/Alnum.md)
+  * [Alpha](rules/Alpha.md)
+  * [AlwaysInvalid](rules/AlwaysInvalid.md)
+  * [AlwaysValid](rules/AlwaysValid.md)
+  * [ArrayVal](rules/ArrayVal.md)
+  * [ArrayType](rules/ArrayType.md)
+  * [Attribute](rules/Attribute.md)
+  * [Bank](rules/Bank.md)
+  * [BankAccount](rules/BankAccount.md)
+  * [Between](rules/Between.md)
+  * [Bic](rules/Bic.md)
+  * [BoolType](rules/BoolType.md)
+  * [Bsn](rules/Bsn.md)
+  * [Call](rules/Call.md)
+  * [CallableType](rules/CallableType.md)
+  * [Callback](rules/Callback.md)
+  * [Charset](rules/Charset.md)
+  * [Cnh](rules/Cnh.md)
+  * [Cnpj](rules/Cnpj.md)
+  * [Cntrl](rules/Cntrl.md)
+  * [Consonant](rules/Consonant.md)
+  * [Contains](rules/Contains.md)
+  * [Countable](rules/Countable.md)
+  * [CountryCode](rules/CountryCode.md)
+  * [Cpf](rules/Cpf.md)
+  * [CreditCard](rules/CreditCard.md)
+  * [Date](rules/Date.md)
+  * [Digit](rules/Digit.md)
+  * [Directory](rules/Directory.md)
+  * [Domain](rules/Domain.md)
+  * [Each](rules/Each.md)
+  * [Email](rules/Email.md)
+  * [EndsWith](rules/EndsWith.md)
+  * [Equals](rules/Equals.md)
+  * [Even](rules/Even.md)
+  * [Executable](rules/Executable.md)
+  * [Exists](rules/Exists.md)
+  * [Extension](rules/Extension.md)
+  * [Factor](rules/Factor.md)
+  * [FalseVal](rules/FalseVal.md)
+  * [Fibonacci](rules/Fibonacci.md)
+  * [File](rules/File.md)
+  * [FilterVar](rules/FilterVar.md)
+  * [Finite](rules/Finite.md)
+  * [FloatVal](rules/FloatVal.md)
+  * [FloatType](rules/FloatType.md)
+  * [Graph](rules/Graph.md)
+  * [HexRgbColor](rules/HexRgbColor.md)
+  * [Identical](rules/Identical.md)
+  * [IdentityCard](rules/IdentityCard.md)
+  * [Image](rules/Image.md)
+  * [Imei](rules/Imei.md)
+  * [In](rules/In.md)
+  * [Infinite](rules/Infinite.md)
+  * [Instance](rules/Instance.md)
+  * [IntVal](rules/IntVal.md)
+  * [IntType](rules/IntType.md)
+  * [Ip](rules/Ip.md)
+  * [IterableType](rules/IterableType.md)
+  * [Json](rules/Json.md)
+  * [Key](rules/Key.md)
+  * [KeyNested](rules/KeyNested.md)
+  * [KeySet](rules/KeySet.md)
+  * [KeyValue](rules/KeyValue.md)
+  * [LanguageCode](rules/LanguageCode.md)
+  * [LeapDate](rules/LeapDate.md)
+  * [LeapYear](rules/LeapYear.md)
+  * [Length](rules/Length.md)
+  * [Lowercase](rules/Lowercase.md)
+  * [MacAddress](rules/MacAddress.md)
+  * [Max](rules/Max.md)
+  * [Mimetype](rules/Mimetype.md)
+  * [Min](rules/Min.md)
+  * [MinimumAge](rules/MinimumAge.md)
+  * [Multiple](rules/Multiple.md)
+  * [Negative](rules/Negative.md)
+  * [NfeAccessKey](rules/NfeAccessKey.md)
+  * [No](rules/No.md)
+  * [NoWhitespace](rules/NoWhitespace.md)
+  * [NoneOf](rules/NoneOf.md)
+  * [Not](rules/Not.md)
+  * [NotBlank](rules/NotBlank.md)
+  * [NotEmpty](rules/NotEmpty.md)
+  * [NotOptional](rules/NotOptional.md)
+  * [NullType](rules/NullType.md)
+  * [Numeric](rules/Numeric.md)
+  * [ObjectType](rules/ObjectType.md)
+  * [Odd](rules/Odd.md)
+  * [OneOf](rules/OneOf.md)
+  * [Optional](rules/Optional.md)
+  * [PerfectSquare](rules/PerfectSquare.md)
+  * [Pesel](rules/Pesel.md)
+  * [Phone](rules/Phone.md)
+  * [PhpLabel](rules/PhpLabel.md)
+  * [Positive](rules/Positive.md)
+  * [PostalCode](rules/PostalCode.md)
+  * [PrimeNumber](rules/PrimeNumber.md)
+  * [Prnt](rules/Prnt.md)
+  * [Punct](rules/Punct.md)
+  * [Readable](rules/Readable.md)
+  * [Regex](rules/Regex.md)
+  * [ResourceType](rules/ResourceType.md)
+  * [Roman](rules/Roman.md)
+  * [ScalarVal](rules/ScalarVal.md)
+  * [Sf](rules/Sf.md)
+  * [Size](rules/Size.md)
+  * [Slug](rules/Slug.md)
+  * [Space](rules/Space.md)
+  * [StartsWith](rules/StartsWith.md)
+  * [StringType](rules/StringType.md)
+  * [SubdivisionCode](rules/SubdivisionCode.md)
+  * [SymbolicLink](rules/SymbolicLink.md)
+  * [Tld](rules/Tld.md)
+  * [TrueVal](rules/TrueVal.md)
+  * [Type](rules/Type.md)
+  * [Uploaded](rules/Uploaded.md)
+  * [Uppercase](rules/Uppercase.md)
+  * [Url](rules/Url.md)
+  * [Version](rules/Version.md)
+  * [VideoUrl](rules/VideoUrl.md)
+  * [Vowel](rules/Vowel.md)
+  * [When](rules/When.md)
+  * [Writable](rules/Writable.md)
+  * [Xdigit](rules/Xdigit.md)
+  * [Yes](rules/Yes.md)
+  * [Zend](rules/Zend.md)
diff --git a/vendor/respect/validation/docs/rules/Age.md b/vendor/respect/validation/docs/rules/Age.md
new file mode 100644
index 0000000000000000000000000000000000000000..740d7279b676e08c6062a1fdae87bc675fc6bbbc
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Age.md
@@ -0,0 +1,46 @@
+# Age
+
+- `v::age(int $minAge)`
+- `v::age(int $minAge, int $maxAge)`
+- `v::age(null, int $maxAge)`
+
+Validates ranges of years.
+
+The validated values can be any date value; internally they will be transformed
+into [DateTime](http://php.net/manual/en/class.datetime.php) objects according
+to the defined locale settings.
+
+The examples below validate if the given dates are lower or equal to 18 years ago:
+```php
+v::age(18)->validate('17 years ago'); // false
+v::age(18)->validate('18 years ago'); // true
+v::age(18)->validate('19 years ago'); // true
+v::age(18)->validate('1970-01-01'); // true
+v::age(18)->validate('today'); // false
+```
+
+The examples below validate if the given dates are between 10 and 50 years ago:
+```php
+v::age(10, 50)->validate('9 years ago'); // false
+v::age(10, 50)->validate('10 years ago'); // true
+v::age(10, 50)->validate('30 years ago'); // true
+v::age(10, 50)->validate('50 years ago'); // true
+v::age(10, 50)->validate('51 years ago'); // false
+```
+
+The examples below validate if the given dates are greater than or equal to 70 years ago:
+```php
+v::age(null, 70)->validate('today'); // true
+v::age(null, 70)->validate('70 years ago'); // true
+v::age(null, 70)->validate('71 years ago'); // false
+```
+
+Message template for this validator includes `{{minAge}}` and `{{maxAge}}`.
+
+***
+See also:
+
+  * [Between](Between.md)
+  * [Date](Date.md)
+  * [Max](Max.md)
+  * [Min](Min.md)
diff --git a/vendor/respect/validation/docs/rules/AllOf.md b/vendor/respect/validation/docs/rules/AllOf.md
new file mode 100644
index 0000000000000000000000000000000000000000..7bd2ecf0203be63de28fd5a27c59f0eb7dbe8ddc
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/AllOf.md
@@ -0,0 +1,30 @@
+# AllOf
+
+- `v::allOf(v $v1, v $v2, v $v3...)`
+
+Will validate if all inner validators validates.
+
+```php
+v::allOf(
+    v::intVal(),
+    v::positive()
+)->validate(15); // true
+```
+
+This is similar to the chain (which is an allOf already), but
+its syntax allows you to set custom names for every node:
+
+```php
+v::allOf(
+    v::intVal()->setName('Account Number'),
+    v::positive()->setName('Higher Than Zero')
+)->setName('Positive integer')
+ ->validate(15); // true
+```
+
+***
+See also:
+
+  * [OneOf](OneOf.md)
+  * [NoneOf](NoneOf.md)
+  * [When](When.md)
diff --git a/vendor/respect/validation/docs/rules/Alnum.md b/vendor/respect/validation/docs/rules/Alnum.md
new file mode 100644
index 0000000000000000000000000000000000000000..184c1e2cfb64c88fc4ee5340077c6a22e8a3710c
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Alnum.md
@@ -0,0 +1,44 @@
+# Alnum
+
+- `v::alnum()`
+- `v::alnum(string $additionalChars)`
+
+Validates alphanumeric characters from a-Z and 0-9.
+
+```php
+v::alnum()->validate('foo 123'); // true
+v::alnum()->validate('number 100%'); // false
+v::alnum('%')->validate('number 100%'); // true
+```
+
+Because this rule allows whitespaces by default, you can separate additional
+characters with a whitespace:
+
+```php
+v::alnum('- ! :')->validate('foo :- 123 !'); // true
+```
+
+This validator allows whitespace, if you want to
+remove them add `->noWhitespace()` to the chain:
+
+```php
+v::alnum()->noWhitespace()->validate('foo 123'); // false
+```
+
+You can restrict case using the `->lowercase()` and
+`->uppercase()` validators:
+
+```php
+v::alnum()->uppercase()->validate('aaa'); // false
+```
+
+Message template for this validator includes `{{additionalChars}}` as
+the string of extra chars passed as the parameter.
+
+***
+See also:
+
+  * [Alpha](Alpha.md)
+  * [Digit](Digit.md)
+  * [Consonant](Consonant.md)
+  * [Vowel](Vowel.md)
diff --git a/vendor/respect/validation/docs/rules/Alpha.md b/vendor/respect/validation/docs/rules/Alpha.md
new file mode 100644
index 0000000000000000000000000000000000000000..44f4dc3714c3ebfd64c46a9da17bfa8a91c091f4
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Alpha.md
@@ -0,0 +1,14 @@
+# Alpha
+
+- `v::alpha()`
+- `v::alpha(string $additionalChars)`
+
+This is similar to `v::alnum()`, but it doesn't allow numbers.
+
+***
+See also:
+
+  * [Alnum](Alnum.md)
+  * [Digit](Digit.md)
+  * [Consonant](Consonant.md)
+  * [Vowel](Vowel.md)
diff --git a/vendor/respect/validation/docs/rules/AlwaysInvalid.md b/vendor/respect/validation/docs/rules/AlwaysInvalid.md
new file mode 100644
index 0000000000000000000000000000000000000000..34b364deb352ea110706b05ade042e04f0ee079f
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/AlwaysInvalid.md
@@ -0,0 +1,14 @@
+# AlwaysInvalid
+
+- `v::alwaysInvalid()`
+
+Always return false.
+
+```php
+v::alwaysInvalid()->validate($whatever); // false
+```
+
+***
+See also:
+
+  * [AlwaysValid](AlwaysValid.md)
diff --git a/vendor/respect/validation/docs/rules/AlwaysValid.md b/vendor/respect/validation/docs/rules/AlwaysValid.md
new file mode 100644
index 0000000000000000000000000000000000000000..6a020bb420dfde6d6fe61722c7a15bbccd39a0bd
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/AlwaysValid.md
@@ -0,0 +1,14 @@
+# AlwaysValid
+
+- `v::alwaysValid()`
+
+Always returns true.
+
+```php
+v::alwaysValid()->validate($whatever); // true
+```
+
+***
+See also:
+
+  * [AlwaysInvalid](AlwaysInvalid.md)
diff --git a/vendor/respect/validation/docs/rules/ArrayType.md b/vendor/respect/validation/docs/rules/ArrayType.md
new file mode 100644
index 0000000000000000000000000000000000000000..ed72eb393ccccd6ccfdb443b8c9c94891a5d7fa9
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/ArrayType.md
@@ -0,0 +1,27 @@
+# ArrayType
+
+- `v::arrayType()`
+
+Validates whether the type of an input is array.
+
+```php
+v::arrayType()->validate([]); // true
+v::arrayType()->validate([1, 2, 3]); // true
+v::arrayType()->validate(new ArrayObject()); // false
+```
+
+***
+See also:
+
+  * [ArrayVal](ArrayVal.md)
+  * [BoolType](BoolType.md)
+  * [CallableType](CallableType.md)
+  * [Countable](Countable.md)
+  * [FloatType](FloatType.md)
+  * [IntType](IntType.md)
+  * [IterableType](IterableType.md)
+  * [NullType](NullType.md)
+  * [ObjectType](ObjectType.md)
+  * [ResourceType](ResourceType.md)
+  * [StringType](StringType.md)
+  * [Type](Type.md)
diff --git a/vendor/respect/validation/docs/rules/ArrayVal.md b/vendor/respect/validation/docs/rules/ArrayVal.md
new file mode 100644
index 0000000000000000000000000000000000000000..ec9eaf066a3b42a0ea67dd7351b818bf828f0cd6
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/ArrayVal.md
@@ -0,0 +1,22 @@
+# ArrayVal
+
+- `v::arrayVal()`
+
+Validates if the input is an array or if the input can be used as an array
+(instance of `ArrayAccess`).
+
+```php
+v::arrayVal()->validate([]); // true
+v::arrayVal()->validate(new ArrayObject); // true
+```
+
+***
+See also:
+
+  * [ArrayType](ArrayType.md)
+  * [Countable](Countable.md)
+  * [Each](Each.md)
+  * [IterableType](IterableType.md)
+  * [Key](Key.md)
+  * [KeySet](KeySet.md)
+  * [KeyValue](KeyValue.md)
diff --git a/vendor/respect/validation/docs/rules/Attribute.md b/vendor/respect/validation/docs/rules/Attribute.md
new file mode 100644
index 0000000000000000000000000000000000000000..e48b6b664bb2c58027149a4870bacf6468659b4a
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Attribute.md
@@ -0,0 +1,33 @@
+# Attribute
+
+- `v::attribute(string $name)`
+- `v::attribute(string $name, v $validator)`
+- `v::attribute(string $name, v $validator, boolean $mandatory = true)`
+
+Validates an object attribute.
+
+```php
+$obj = new stdClass;
+$obj->foo = 'bar';
+
+v::attribute('foo')->validate($obj); // true
+```
+
+You can also validate the attribute itself:
+
+```php
+v::attribute('foo', v::equals('bar'))->validate($obj); // true
+```
+
+Third parameter makes the attribute presence optional:
+
+```php
+v::attribute('lorem', v::stringType(), false)->validate($obj); // true
+```
+
+The name of this validator is automatically set to the attribute name.
+
+***
+See also:
+
+  * [Key](Key.md)
diff --git a/vendor/respect/validation/docs/rules/Bank.md b/vendor/respect/validation/docs/rules/Bank.md
new file mode 100644
index 0000000000000000000000000000000000000000..028d71985d50b871bffeb9a9889c0cd8d81b27f2
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Bank.md
@@ -0,0 +1,20 @@
+# Bank
+
+- `v::bank(string $countryCode)`
+
+Validates a bank.
+
+```php
+v::bank("de")->validate("70169464"); // true
+v::bank("de")->validate("12345"); // false
+```
+
+These country codes are supported:
+
+ * "de" (Germany) - You must add `"malkusch/bav": "~1.0"` to your `require` property on composer.json file.
+
+***
+See also:
+
+  * [BankAccount](BankAccount.md)
+  * [Bic](Bic.md)
diff --git a/vendor/respect/validation/docs/rules/BankAccount.md b/vendor/respect/validation/docs/rules/BankAccount.md
new file mode 100644
index 0000000000000000000000000000000000000000..f9d319d7f82c2ba7d6d681ab7f33ed43df3abfa4
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/BankAccount.md
@@ -0,0 +1,20 @@
+# BankAccount
+
+- `v::bankAccount(string $countryCode, string $bank)`
+
+Validates a bank account for a given bank.
+
+```php
+v::bankAccount("de", "70169464")->validate("1112"); // true
+v::bankAccount("de", "70169464")->validate("1234"); // false
+```
+
+These country codes are supported:
+
+ * "de" (Germany) - You must add `"malkusch/bav": "~1.0"` to your `require` property on composer.json file.
+
+***
+See also:
+
+  * [Bank](Bank.md)
+  * [Bic](Bic.md)
diff --git a/vendor/respect/validation/docs/rules/Between.md b/vendor/respect/validation/docs/rules/Between.md
new file mode 100644
index 0000000000000000000000000000000000000000..5c0204afc9aed1fb1834d82d0050cb274b060152
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Between.md
@@ -0,0 +1,44 @@
+# Between
+
+- `v::between(mixed $start, mixed $end)`
+- `v::between(mixed $start, mixed $end, boolean $inclusive = true)`
+
+Validates ranges. Most simple example:
+
+```php
+v::intVal()->between(10, 20)->validate(15); // true
+```
+
+The type as the first validator in a chain is a good practice,
+since between accepts many types:
+
+```php
+v::stringType()->between('a', 'f')->validate('c'); // true
+```
+
+Also very powerful with dates:
+
+```php
+v::date()->between('2009-01-01', '2013-01-01')->validate('2010-01-01'); // true
+```
+
+Date ranges accept strtotime values:
+
+```php
+v::date()->between('yesterday', 'tomorrow')->validate('now'); // true
+```
+
+A third parameter may be passed to validate the passed values inclusive:
+
+```php
+v::date()->between(10, 20, true)->validate(20); // true
+```
+
+Message template for this validator includes `{{minValue}}` and `{{maxValue}}`.
+
+***
+See also:
+
+  * [Length](Length.md)
+  * [Min](Min.md)
+  * [Max](Max.md)
diff --git a/vendor/respect/validation/docs/rules/Bic.md b/vendor/respect/validation/docs/rules/Bic.md
new file mode 100644
index 0000000000000000000000000000000000000000..82c67e8810a5caaadcd36cf798b35969935b1567
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Bic.md
@@ -0,0 +1,20 @@
+# Bic
+
+- `v::bic(string $countryCode)`
+
+Validates a BIC (Bank Identifier Code) for a given country.
+
+```php
+v::bic("de")->validate("VZVDDED1XXX"); // true
+v::bic("de")->validate("VZVDDED1"); // true
+```
+
+Theses country codes are supported:
+
+ * "de" (Germany) - You must add `"malkusch/bav": "~1.0"` to your `require` property on composer.json file.
+
+***
+See also:
+
+  * [Bank](Bank.md)
+  * [BankAccount](BankAccount.md)
diff --git a/vendor/respect/validation/docs/rules/BoolType.md b/vendor/respect/validation/docs/rules/BoolType.md
new file mode 100644
index 0000000000000000000000000000000000000000..979562189815b5d858b03e316120796971063de5
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/BoolType.md
@@ -0,0 +1,27 @@
+# BoolType
+
+- `v::boolType()`
+
+Validates if the input is a boolean value:
+
+```php
+v::boolType()->validate(true); // true
+v::boolType()->validate(false); // true
+```
+
+***
+See also:
+
+  * [ArrayType](ArrayType.md)
+  * [CallableType](CallableType.md)
+  * [FloatType](FloatType.md)
+  * [FloatVal](FloatVal.md)
+  * [IntType](IntType.md)
+  * [No](No.md)
+  * [NullType](NullType.md)
+  * [ObjectType](ObjectType.md)
+  * [ResourceType](ResourceType.md)
+  * [StringType](StringType.md)
+  * [TrueVal](TrueVal.md)
+  * [Type](Type.md)
+  * [Yes](Yes.md)
diff --git a/vendor/respect/validation/docs/rules/BoolVal.md b/vendor/respect/validation/docs/rules/BoolVal.md
new file mode 100644
index 0000000000000000000000000000000000000000..26bf9a6d0d137efdb9707647190daf17041886ba
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/BoolVal.md
@@ -0,0 +1,31 @@
+# BoolVal
+
+- `v::boolVal()`
+
+Validates if the input results in a boolean value:
+
+```php
+v::boolVal()->validate('on'); // true
+v::boolVal()->validate('off'); // true
+v::boolVal()->validate('yes'); // true
+v::boolVal()->validate('no'); // true
+v::boolVal()->validate(1); // true
+v::boolVal()->validate(0); // true
+```
+
+***
+See also:
+
+  * [BoolType](BoolType.md)
+  * [CallableType](CallableType.md)
+  * [FloatType](FloatType.md)
+  * [FloatVal](FloatVal.md)
+  * [IntType](IntType.md)
+  * [No](No.md)
+  * [NullType](NullType.md)
+  * [ObjectType](ObjectType.md)
+  * [ResourceType](ResourceType.md)
+  * [StringType](StringType.md)
+  * [TrueVal](TrueVal.md)
+  * [Type](Type.md)
+  * [Yes](Yes.md)
diff --git a/vendor/respect/validation/docs/rules/Bsn.md b/vendor/respect/validation/docs/rules/Bsn.md
new file mode 100644
index 0000000000000000000000000000000000000000..e67f55e70d22aeddfe110578ff0cd57ff05fb2e3
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Bsn.md
@@ -0,0 +1,16 @@
+# Bsn
+
+- `v::bsn()`
+
+Validates a Dutch citizen service number ([BSN](https://nl.wikipedia.org/wiki/Burgerservicenummer)).
+
+```php
+v::bsn()->validate('612890053'); // true
+```
+
+***
+See also:
+
+  * [Cnh](Cnh.md)
+  * [Cnpj](Cnpj.md)
+  * [Cpf](Cpf.md)
diff --git a/vendor/respect/validation/docs/rules/Call.md b/vendor/respect/validation/docs/rules/Call.md
new file mode 100644
index 0000000000000000000000000000000000000000..f56b0a8e5e73b80188026d98206c916ae0fe8969
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Call.md
@@ -0,0 +1,51 @@
+# Call
+
+- `v::call(callable $callback)`
+
+This is a very low level validator. It calls a function, method or closure
+for the input and then validates it. Consider the following variable:
+
+```php
+$url = 'http://www.google.com/search?q=respect.github.com'
+```
+
+To validate every part of this URL we could use the native `parse_url`
+function to break its parts:
+
+```php
+$parts = parse_url($url);
+```
+
+This function returns an array containing `scheme`, `host`, `path` and `query`.
+We can validate them this way:
+
+```php
+v::arrayVal()->key('scheme', v::startsWith('http'))
+        ->key('host',   v::domain())
+        ->key('path',   v::stringType())
+        ->key('query',  v::notEmpty());
+```
+
+Using `v::call()` you can do this in a single chain:
+
+```php
+v::call(
+    'parse_url',
+     v::arrayVal()->key('scheme', v::startsWith('http'))
+        ->key('host',   v::domain())
+        ->key('path',   v::stringType())
+        ->key('query',  v::notEmpty())
+)->validate($url);
+```
+
+It is possible to call methods and closures as the first parameter:
+
+```php
+v::call([$myObj, 'methodName'], v::intVal())->validate($myInput);
+v::call(function($input) {}, v::intVal())->validate($myInput);
+```
+
+***
+See also:
+
+  * [Callback](Callback.md)
diff --git a/vendor/respect/validation/docs/rules/CallableType.md b/vendor/respect/validation/docs/rules/CallableType.md
new file mode 100644
index 0000000000000000000000000000000000000000..7f24dca7b0dfcbb4ae4591061d75f75793d35709
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/CallableType.md
@@ -0,0 +1,25 @@
+# CallableType
+
+- `v::callableType()`
+
+Validates if the input is a callable value.
+
+```php
+v::callableType()->validate(function () {}); // true
+v::callableType()->validate('trim'); // true
+v::callableType()->validate([new ObjectType, 'methodName']); // true
+```
+
+***
+See also:
+
+  * [ArrayType](ArrayType.md)
+  * [BoolType](BoolType.md)
+  * [Callback](Callback.md)
+  * [FloatType](FloatType.md)
+  * [IntType](IntType.md)
+  * [NullType](NullType.md)
+  * [ObjectType](ObjectType.md)
+  * [ResourceType](ResourceType.md)
+  * [StringType](StringType.md)
+  * [Type](Type.md)
diff --git a/vendor/respect/validation/docs/rules/Callback.md b/vendor/respect/validation/docs/rules/Callback.md
new file mode 100644
index 0000000000000000000000000000000000000000..d5ec6b86c62bea13d0b8b1e999129775b67e20e2
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Callback.md
@@ -0,0 +1,21 @@
+# Callback
+
+- `v::callback(callable $callback)`
+
+This is a wildcard validator, it uses a function name, method or closure
+to validate something:
+
+```php
+v::callback('is_int')->validate(10); // true
+```
+
+(Please note that this is a sample, the `v::intVal()` validator is much better).
+
+As in `v::call()`, you can pass a method or closure to it.
+
+***
+See also:
+
+  * [Call](Call.md)
+  * [CallableType](CallableType.md)
+  * [FilterVar](FilterVar.md)
diff --git a/vendor/respect/validation/docs/rules/Charset.md b/vendor/respect/validation/docs/rules/Charset.md
new file mode 100644
index 0000000000000000000000000000000000000000..43d889bdc687c4221eb82510f6fada2b39db8048
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Charset.md
@@ -0,0 +1,19 @@
+# Charset
+
+- `v::charset(mixed $charset)`
+
+Validates if a string is in a specific charset.
+
+```php
+v::charset('ASCII')->validate('açúcar'); // false
+v::charset('ASCII')->validate('sugar');  //true
+v::charset(['ISO-8859-1', 'EUC-JP'])->validate('日本国'); // true
+```
+
+The array format is a logic OR, not AND.
+
+***
+See also:
+
+  * [Alnum](Alnum.md)
+  * [Alpha](Alpha.md)
diff --git a/vendor/respect/validation/docs/rules/Cnh.md b/vendor/respect/validation/docs/rules/Cnh.md
new file mode 100644
index 0000000000000000000000000000000000000000..6815929a3910d537c8e5131828dd68fd176d9d29
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Cnh.md
@@ -0,0 +1,15 @@
+# Cnh
+
+- `v::cnh()`
+
+Validates a Brazillian driver's license.
+
+```php
+v::cnh()->validate('02650306461'); // true
+```
+
+***
+See also:
+
+  * [Cnpj](Cnpj.md)
+  * [Cpf](Cpf.md)
diff --git a/vendor/respect/validation/docs/rules/Cnpj.md b/vendor/respect/validation/docs/rules/Cnpj.md
new file mode 100644
index 0000000000000000000000000000000000000000..846d391762c2523cc527c61829c227ea3909f502
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Cnpj.md
@@ -0,0 +1,12 @@
+# Cnpj
+
+- `v::cnpj()`
+
+Validates the Brazillian CNPJ number. Ignores non-digit chars, so
+use `->digit()` if needed.
+
+***
+See also:
+
+  * [Cpf](Cpf.md)
+  * [Cnh](Cnh.md)
diff --git a/vendor/respect/validation/docs/rules/Cntrl.md b/vendor/respect/validation/docs/rules/Cntrl.md
new file mode 100644
index 0000000000000000000000000000000000000000..7a6552aa8e0529dcbc08cd4017f9bd65434a1dc2
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Cntrl.md
@@ -0,0 +1,17 @@
+# Cntrl
+
+- `v::cntrl()`
+- `v::cntrl(string $additionalChars)`
+
+This is similar to `v::alnum()`, but only accepts control characters:
+
+```php
+v::cntrl()->validate("\n\r\t"); // true
+```
+
+***
+See also:
+
+  * [Alnum](Alnum.md)
+  * [Prnt](Prnt.md)
+  * [Space](Space.md)
diff --git a/vendor/respect/validation/docs/rules/Consonant.md b/vendor/respect/validation/docs/rules/Consonant.md
new file mode 100644
index 0000000000000000000000000000000000000000..fac1fe53319d7ba5afe0f39aef44f4116a1b6c71
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Consonant.md
@@ -0,0 +1,18 @@
+# Consonant
+
+- `v::consonant()`
+- `v::consonant(string $additionalChars)`
+
+Similar to `v::alnum()`. Validates strings that contain only consonants:
+
+```php
+v::consonant()->validate('xkcd'); // true
+```
+
+***
+See also:
+
+  * [Alnum](Alnum.md)
+  * [Digit](Digit.md)
+  * [Alpha](Alpha.md)
+  * [Vowel](Vowel.md)
diff --git a/vendor/respect/validation/docs/rules/Contains.md b/vendor/respect/validation/docs/rules/Contains.md
new file mode 100644
index 0000000000000000000000000000000000000000..6036494fe43cbbb06015a2e7edcc9fd4871d6d00
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Contains.md
@@ -0,0 +1,28 @@
+# Contains
+
+- `v::contains(mixed $value)`
+- `v::contains(mixed $value, boolean $identical = false)`
+
+For strings:
+
+```php
+v::contains('ipsum')->validate('lorem ipsum'); // true
+```
+
+For arrays:
+
+```php
+v::contains('ipsum')->validate(['ipsum', 'lorem']); // true
+```
+
+A second parameter may be passed for identical comparison instead
+of equal comparison.
+
+Message template for this validator includes `{{containsValue}}`.
+
+***
+See also:
+
+  * [StartsWith](StartsWith.md)
+  * [EndsWith](EndsWith.md)
+  * [In](In.md)
diff --git a/vendor/respect/validation/docs/rules/Countable.md b/vendor/respect/validation/docs/rules/Countable.md
new file mode 100644
index 0000000000000000000000000000000000000000..0353de4d7a748ed9f490149b8f07d1343f084036
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Countable.md
@@ -0,0 +1,19 @@
+# Countable
+
+- `v::countable()`
+
+Validates if the input is countable, in other words, if you're allowed to use
+[count()](http://php.net/count) function on it.
+
+```php
+v::countable()->validate([]); // true
+v::countable()->validate(new ArrayObject()); // true
+v::countable()->validate('string'); // false
+```
+
+***
+See also:
+
+  * [ArrayVal](ArrayVal.md)
+  * [Instance](Instance.md)
+  * [IterableType](IterableType.md)
diff --git a/vendor/respect/validation/docs/rules/CountryCode.md b/vendor/respect/validation/docs/rules/CountryCode.md
new file mode 100644
index 0000000000000000000000000000000000000000..469313db2dcb66a7306d53510b54c136afa758a8
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/CountryCode.md
@@ -0,0 +1,14 @@
+# CountryCode
+
+- `v::countryCode()`
+
+Validates an ISO country code like US or BR.
+
+```php
+v::countryCode()->validate('BR'); // true
+```
+
+***
+See also:
+
+  * [Tld](Tld.md)
diff --git a/vendor/respect/validation/docs/rules/Cpf.md b/vendor/respect/validation/docs/rules/Cpf.md
new file mode 100644
index 0000000000000000000000000000000000000000..0fc3a717af13febc2d111218e534d9f61271e037
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Cpf.md
@@ -0,0 +1,28 @@
+# Cpf
+
+- `v::cpf()`
+
+Validates a Brazillian CPF number.
+
+```php
+v::cpf()->validate('44455566820'); // true
+```
+
+It ignores any non-digit char:
+
+```php
+v::cpf()->validate('444.555.668-20'); // true
+```
+
+If you need to validate digits only, add `->digit()` to
+the chain:
+
+```php
+v::digit()->cpf()->validate('44455566820'); // true
+```
+
+***
+See also:
+
+  * [Cnpj](Cnpj.md)
+  * [Cnh](Cnh.md)
diff --git a/vendor/respect/validation/docs/rules/CreditCard.md b/vendor/respect/validation/docs/rules/CreditCard.md
new file mode 100644
index 0000000000000000000000000000000000000000..aa9783a23304444b5871f7965eec499ec9de7408
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/CreditCard.md
@@ -0,0 +1,39 @@
+# CreditCard
+
+- `v::creditCard()`
+- `v::creditCard(string $brand)`
+
+Validates a credit card number.
+
+```php
+v::creditCard()->validate('5376 7473 9720 8720'); // true
+
+v::creditCard('American Express')->validate('340316193809364'); // true
+v::creditCard('Diners Club')->validate('30351042633884'); // true
+v::creditCard('Discover')->validate('6011000990139424'); // true
+v::creditCard('JCB')->validate('3566002020360505'); // true
+v::creditCard('Master')->validate('5376747397208720'); // true
+v::creditCard('Visa')->validate('4024007153361885'); // true
+```
+
+The current supported brands are:
+
+- American Express
+- Diners Club
+- Discover
+- JCB
+- MasterCard
+- Visa
+
+It ignores any non-digit chars, so use `->digit()` when appropriate.
+
+```php
+v::digit()->creditCard()->validate('5376747397208720'); // true
+```
+
+***
+See also:
+
+  * [Bank](Bank.md)
+  * [BankAccount](BankAccount.md)
+  * [Bic](Bic.md)
diff --git a/vendor/respect/validation/docs/rules/CurrencyCode.md b/vendor/respect/validation/docs/rules/CurrencyCode.md
new file mode 100644
index 0000000000000000000000000000000000000000..95c13fae47325b977071590ec7304b7afdd5b3b6
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/CurrencyCode.md
@@ -0,0 +1,15 @@
+# CurrencyCode
+
+- `v::currencyCode()`
+
+Validates an [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code like GBP or EUR.
+
+```php
+v::currencyCode()->validate('GBP'); // true
+```
+
+***
+See also:
+
+  * [CountryCode](CountryCode.md)
+  * [SubdivisionCode](SubdivisionCode.md)
diff --git a/vendor/respect/validation/docs/rules/Date.md b/vendor/respect/validation/docs/rules/Date.md
new file mode 100644
index 0000000000000000000000000000000000000000..4afb9ecbd56a4b49614375508f38bc18cb1b5170
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Date.md
@@ -0,0 +1,40 @@
+# Date
+
+- `v::date()`
+- `v::date(string $format)`
+
+Validates if input is a date:
+
+```php
+v::date()->validate('2009-01-01'); // true
+```
+
+Also accepts strtotime values:
+
+```php
+v::date()->validate('now'); // true
+```
+
+And DateTime instances:
+
+```php
+v::date()->validate(new DateTime); // true
+```
+
+You can pass a format when validating strings:
+
+```php
+v::date('Y-m-d')->validate('01-01-2009'); // false
+```
+
+Format has no effect when validating DateTime instances.
+
+Message template for this validator includes `{{format}}`.
+
+***
+See also:
+
+  * [Between](Between.md)
+  * [MinimumAge](MinimumAge.md)
+  * [LeapDate](LeapDate.md)
+  * [LeapYear](LeapYear.md)
diff --git a/vendor/respect/validation/docs/rules/Digit.md b/vendor/respect/validation/docs/rules/Digit.md
new file mode 100644
index 0000000000000000000000000000000000000000..c4638483f4d6734817263c9891263a1ea2d41deb
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Digit.md
@@ -0,0 +1,13 @@
+# Digit
+
+- `v::digit()`
+
+This is similar to `v::alnum()`, but it doesn't allow a-Z.
+
+***
+See also:
+
+  * [Alnum](Alnum.md)
+  * [Alpha](Alpha.md)
+  * [Vowel](Vowel.md)
+  * [Consonant](Consonant.md)
diff --git a/vendor/respect/validation/docs/rules/Directory.md b/vendor/respect/validation/docs/rules/Directory.md
new file mode 100644
index 0000000000000000000000000000000000000000..7d49c63b726e0bec8c8d615092d3ed7ce275755c
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Directory.md
@@ -0,0 +1,30 @@
+# Directory
+
+- `v::directory()`
+
+Validates directories.
+
+```php
+v::directory()->validate(__DIR__); // true
+v::directory()->validate(__FILE__); // false
+```
+
+This validator will consider SplFileInfo instances, so you can do something like:
+
+```php
+v::directory()->validate(new \SplFileInfo($directory));
+```
+
+***
+See also:
+
+  * [Executable](Executable.md)
+  * [Exists](Exists.md)
+  * [Extension](Extension.md)
+  * [File](File.md)
+  * [Mimetype](Mimetype.md)
+  * [Readable](Readable.md)
+  * [Size](Size.md)
+  * [SymbolicLink](SymbolicLink.md)
+  * [Uploaded](Uploaded.md)
+  * [Writable](Writable.md)
diff --git a/vendor/respect/validation/docs/rules/Domain.md b/vendor/respect/validation/docs/rules/Domain.md
new file mode 100644
index 0000000000000000000000000000000000000000..ac5d76a7b77d89952de912a75c614fa3479f99aa
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Domain.md
@@ -0,0 +1,40 @@
+# Domain
+
+- `v::domain()`
+- `v::domain(boolean $tldCheck = true)`
+
+Validates domain names.
+
+```php
+v::domain()->validate('google.com');
+```
+
+You can skip *top level domain* (TLD) checks to validate internal
+domain names:
+
+```php
+v::domain(false)->validate('dev.machine.local');
+```
+
+This is a composite validator, it validates several rules
+internally:
+
+  * If input is an IP address, it fails.
+  * If input contains whitespace, it fails.
+  * If input does not contain any dots, it fails.
+  * If input has less than two parts, it fails.
+  * Input must end with a top-level-domain to pass (if not skipped).
+  * Each part must be alphanumeric and not start with an hyphen.
+  * [PunnyCode][] is accepted for [Internationalizing Domain Names in Applications][IDNA].
+
+Messages for this validator will reflect rules above.
+
+***
+See also:
+
+  * [Ip](Ip.md)
+  * [MacAddress](MacAddress.md)
+  * [Tld](Tld.md)
+
+[PunnyCode]: http://en.wikipedia.org/wiki/Punycode "Wikipedia: Punnycode"
+[IDNA]: http://en.wikipedia.org/wiki/Internationalized_domain_name#Internationalizing_Domain_Names_in_Applications "Wikipedia: Internationalized domain name"
diff --git a/vendor/respect/validation/docs/rules/Each.md b/vendor/respect/validation/docs/rules/Each.md
new file mode 100644
index 0000000000000000000000000000000000000000..ecc1c666359e921a9a33cdebe1a2c3ee5a6c4a92
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Each.md
@@ -0,0 +1,27 @@
+# Each
+
+- `v::each(v $validatorForValue)`
+- `v::each(null, v $validatorForKey)`
+- `v::each(v $validatorForValue, v $validatorForKey)`
+
+Iterates over an array or Iterator and validates the value or key
+of each entry:
+
+```php
+$releaseDates = [
+    'validation' => '2010-01-01',
+    'template'   => '2011-01-01',
+    'relational' => '2011-02-05',
+];
+
+v::arrayVal()->each(v::date())->validate($releaseDates); // true
+v::arrayVal()->each(v::date(), v::stringType()->lowercase())->validate($releaseDates); // true
+```
+
+Using `arrayVal()` before `each()` is a best practice.
+
+***
+See also:
+
+  * [Key](Key.md)
+  * [ArrayVal](ArrayVal.md)
diff --git a/vendor/respect/validation/docs/rules/Email.md b/vendor/respect/validation/docs/rules/Email.md
new file mode 100644
index 0000000000000000000000000000000000000000..64a4d7cf3c4896dd1786e20c99b74bd89adbf184
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Email.md
@@ -0,0 +1,16 @@
+# Email
+
+- `v::email()`
+
+Validates an email address.
+
+```php
+v::email()->validate('alexandre@gaigalas.net'); // true
+```
+
+***
+See also:
+
+  * [Phone](Phone.md)
+  * [Url](Url.md)
+  * [VideoUrl](VideoUrl.md)
diff --git a/vendor/respect/validation/docs/rules/EndsWith.md b/vendor/respect/validation/docs/rules/EndsWith.md
new file mode 100644
index 0000000000000000000000000000000000000000..99ec5b215a194760e80132ea1083bd791b903b0e
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/EndsWith.md
@@ -0,0 +1,31 @@
+# EndsWith
+
+- `v::endsWith(mixed $value)`
+- `v::endsWith(mixed $value, boolean $identical = false)`
+
+This validator is similar to `v::contains()`, but validates
+only if the value is at the end of the input.
+
+For strings:
+
+```php
+v::endsWith('ipsum')->validate('lorem ipsum'); // true
+```
+
+For arrays:
+
+```php
+v::endsWith('ipsum')->validate(['lorem', 'ipsum']); // true
+```
+
+A second parameter may be passed for identical comparison instead
+of equal comparison.
+
+Message template for this validator includes `{{endValue}}`.
+
+***
+See also:
+
+  * [StartsWith](StartsWith.md)
+  * [Contains](Contains.md)
+  * [In](In.md)
diff --git a/vendor/respect/validation/docs/rules/Equals.md b/vendor/respect/validation/docs/rules/Equals.md
new file mode 100644
index 0000000000000000000000000000000000000000..e28f783dfcfd4128c7eecba94307e4ae53aeff1c
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Equals.md
@@ -0,0 +1,17 @@
+# Equals
+
+- `v::equals(mixed $value)`
+
+Validates if the input is equal to some value.
+
+```php
+v::equals('alganet')->validate('alganet'); // true
+```
+
+Message template for this validator includes `{{compareTo}}`.
+
+***
+See also:
+
+  * [Contains](Contains.md)
+  * [Identical](Identical.md)
diff --git a/vendor/respect/validation/docs/rules/Even.md b/vendor/respect/validation/docs/rules/Even.md
new file mode 100644
index 0000000000000000000000000000000000000000..da52597b85c0793e85862dd42611df0f4da6a7cd
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Even.md
@@ -0,0 +1,17 @@
+# Even
+
+- `v::even()`
+
+Validates an even number.
+
+```php
+v::intVal()->even()->validate(2); // true
+```
+
+Using `int()` before `even()` is a best practice.
+
+***
+See also:
+
+  * [Odd](Odd.md)
+  * [Multiple](Multiple.md)
diff --git a/vendor/respect/validation/docs/rules/Executable.md b/vendor/respect/validation/docs/rules/Executable.md
new file mode 100644
index 0000000000000000000000000000000000000000..7c1e1303dc350d4034dc1f8c6454e835943187e2
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Executable.md
@@ -0,0 +1,23 @@
+# Executable
+
+- `v::executable()`
+
+Validates if a file is an executable.
+
+```php
+v::email()->executable('script.sh'); // true
+```
+
+***
+See also:
+
+  * [Directory](Directory.md)
+  * [Exists](Exists.md)
+  * [Extension](Extension.md)
+  * [File](File.md)
+  * [Mimetype](Mimetype.md)
+  * [Readable](Readable.md)
+  * [Size](Size.md)
+  * [SymbolicLink](SymbolicLink.md)
+  * [Uploaded](Uploaded.md)
+  * [Writable](Writable.md)
diff --git a/vendor/respect/validation/docs/rules/Exists.md b/vendor/respect/validation/docs/rules/Exists.md
new file mode 100644
index 0000000000000000000000000000000000000000..681c50743b70cd7b36548571ca47ae2d51cc4075
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Exists.md
@@ -0,0 +1,30 @@
+# Exists
+
+- `v::exists()`
+
+Validates files or directories.
+
+```php
+v::exists()->validate(__FILE__); // true
+v::exists()->validate(__DIR__); // true
+```
+
+This validator will consider SplFileInfo instances, so you can do something like:
+
+```php
+v::exists()->validate(new \SplFileInfo($file));
+```
+
+***
+See also:
+
+  * [Directory](Directory.md)
+  * [Executable](Executable.md)
+  * [Extension](Extension.md)
+  * [File](File.md)
+  * [Mimetype](Mimetype.md)
+  * [Readable](Readable.md)
+  * [Size](Size.md)
+  * [SymbolicLink](SymbolicLink.md)
+  * [Uploaded](Uploaded.md)
+  * [Writable](Writable.md)
diff --git a/vendor/respect/validation/docs/rules/Extension.md b/vendor/respect/validation/docs/rules/Extension.md
new file mode 100644
index 0000000000000000000000000000000000000000..4abc0d5ba51f701827dd166daac098ec79c65e3f
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Extension.md
@@ -0,0 +1,25 @@
+# Extension
+
+- `v::extension(string $extension)`
+
+Validates if the file extension matches the expected one:
+
+```php
+v::extension('png')->validate('image.png'); // true
+```
+
+This rule is case-sensitive.
+
+***
+See also:
+
+  * [Directory](Directory.md)
+  * [Executable](Executable.md)
+  * [Exists](Exists.md)
+  * [File](File.md)
+  * [Mimetype](Mimetype.md)
+  * [Readable](Readable.md)
+  * [Size](Size.md)
+  * [SymbolicLink](SymbolicLink.md)
+  * [Uploaded](Uploaded.md)
+  * [Writable](Writable.md)
diff --git a/vendor/respect/validation/docs/rules/Factor.md b/vendor/respect/validation/docs/rules/Factor.md
new file mode 100644
index 0000000000000000000000000000000000000000..1e04276432d1bc8583fa89da6c004d43abdb2568
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Factor.md
@@ -0,0 +1,19 @@
+# Factor
+
+- `v::factor(int $dividend)`
+
+Validates if the input is a factor of the defined dividend.
+
+```php
+v::factor(0)->validate(5); // true
+v::factor(4)->validate(2); // true
+v::factor(4)->validate(3); // false
+```
+
+***
+See also:
+
+  * [Digit](Digit.md)
+  * [Finite](Finite.md)
+  * [Infinite](Infinite.md)
+  * [Numeric](Numeric.md)
diff --git a/vendor/respect/validation/docs/rules/FalseVal.md b/vendor/respect/validation/docs/rules/FalseVal.md
new file mode 100644
index 0000000000000000000000000000000000000000..6ca060d33ca40dd587efb0e4583b7606b1000f03
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/FalseVal.md
@@ -0,0 +1,21 @@
+# FalseVal
+
+- `v::falseVal()`
+
+Validates if a value is considered as `false`.
+
+```php
+v::falseVal()->validate(false); // true
+v::falseVal()->validate(0); // true
+v::falseVal()->validate('0'); // true
+v::falseVal()->validate('false'); // true
+v::falseVal()->validate('off'); // true
+v::falseVal()->validate('no'); // true
+v::falseVal()->validate('0.5'); // false
+v::falseVal()->validate('2'); // false
+```
+
+***
+See also:
+
+  * [TrueVal](TrueVal.md)
diff --git a/vendor/respect/validation/docs/rules/Fibonacci.md b/vendor/respect/validation/docs/rules/Fibonacci.md
new file mode 100644
index 0000000000000000000000000000000000000000..fe8c5235b775e98fda3c81e43f1736e7e18dc30f
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Fibonacci.md
@@ -0,0 +1,17 @@
+# Fibonacci
+
+ - `v::fibonacci()`
+
+Validates whether the input follows the Fibonacci integer sequence.
+
+```php
+v::fibonacci()->validate(1); // true
+v::fibonacci()->validate('34'); // true
+v::fibonacci()->validate(6); // false
+```
+
+***
+See also:
+
+  * [PrimeNumber](PrimeNumber.md)
+  * [PerfectSquare](PerfectSquare.md)
\ No newline at end of file
diff --git a/vendor/respect/validation/docs/rules/File.md b/vendor/respect/validation/docs/rules/File.md
new file mode 100644
index 0000000000000000000000000000000000000000..8454d0258d2d1922fc5105c71f1e6ea3a3ab28fb
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/File.md
@@ -0,0 +1,30 @@
+# File
+
+- `v::file()`
+
+Validates files.
+
+```php
+v::file()->validate(__FILE__); // true
+v::file()->validate(__DIR__); // false
+```
+
+This validator will consider SplFileInfo instances, so you can do something like:
+
+```php
+v::file()->validate(new \SplFileInfo($file));
+```
+
+***
+See also:
+
+  * [Directory](Directory.md)
+  * [Executable](Executable.md)
+  * [Exists](Exists.md)
+  * [Extension](Extension.md)
+  * [Mimetype](Mimetype.md)
+  * [Readable](Readable.md)
+  * [Size](Size.md)
+  * [SymbolicLink](SymbolicLink.md)
+  * [Uploaded](Uploaded.md)
+  * [Writable](Writable.md)
diff --git a/vendor/respect/validation/docs/rules/FilterVar.md b/vendor/respect/validation/docs/rules/FilterVar.md
new file mode 100644
index 0000000000000000000000000000000000000000..a13d4fa091ffab6afb9a9df90b13d9adba374d64
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/FilterVar.md
@@ -0,0 +1,18 @@
+# FilterVar
+
+- `v::filterVar(int $filter)`
+- `v::filterVar(int $filter, mixed $options)`
+
+A wrapper for PHP's [filter_var()](http://php.net/filter_var) function.
+
+```php
+v::filterVar(FILTER_VALIDATE_EMAIL)->validate('bob@example.com'); // true
+v::filterVar(FILTER_VALIDATE_URL)->validate('http://example.com'); // true
+v::filterVar(FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED)->validate('http://example.com'); // false
+v::filterVar(FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED)->validate('http://example.com/path'); // true
+```
+
+***
+See also:
+
+  * [Callback](Callback.md)
diff --git a/vendor/respect/validation/docs/rules/Finite.md b/vendor/respect/validation/docs/rules/Finite.md
new file mode 100644
index 0000000000000000000000000000000000000000..42ef9f30d6d833305e0140c368d87694dd9b887c
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Finite.md
@@ -0,0 +1,18 @@
+# Finite
+
+- `v::finite()`
+
+Validates if the input is a finite number.
+
+```php
+v::finite()->validate('10'); // true
+v::finite()->validate(10); // true
+```
+
+***
+See also:
+
+  * [Digit](Digit.md)
+  * [Infinite](Infinite.md)
+  * [IntVal](IntVal.md)
+  * [Numeric](Numeric.md)
diff --git a/vendor/respect/validation/docs/rules/FloatType.md b/vendor/respect/validation/docs/rules/FloatType.md
new file mode 100644
index 0000000000000000000000000000000000000000..87efee83511a971477a7ec4ca2f84fc4ab81dbcc
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/FloatType.md
@@ -0,0 +1,25 @@
+# FloatType
+
+- `v::floatType()`
+
+Validates whether the type of a value is float.
+
+```php
+v::floatType()->validate(1.5); // true
+v::floatType()->validate('1.5'); // false
+v::floatType()->validate(0e5); // true
+```
+
+***
+See also:
+
+  * [BoolType](BoolType.md)
+  * [CallableType](CallableType.md)
+  * [FloatVal](FloatVal.md)
+  * [IntType](IntType.md)
+  * [IntVal](IntVal.md)
+  * [NullType](NullType.md)
+  * [ObjectType](ObjectType.md)
+  * [ResourceType](ResourceType.md)
+  * [StringType](StringType.md)
+  * [Type](Type.md)
diff --git a/vendor/respect/validation/docs/rules/FloatVal.md b/vendor/respect/validation/docs/rules/FloatVal.md
new file mode 100644
index 0000000000000000000000000000000000000000..f73c2fc073094600820a00cba34c34ec6c378bd3
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/FloatVal.md
@@ -0,0 +1,17 @@
+# FloatVal
+
+- `v::floatVal()`
+
+Validates a floating point number.
+
+```php
+v::floatVal()->validate(1.5); // true
+v::floatVal()->validate('1e5'); // true
+```
+
+***
+See also:
+
+  * [FloatType](FloatType.md)
+  * [IntType](IntType.md)
+  * [IntVal](IntVal.md)
diff --git a/vendor/respect/validation/docs/rules/Graph.md b/vendor/respect/validation/docs/rules/Graph.md
new file mode 100644
index 0000000000000000000000000000000000000000..2c7503ad785414e4459968261f12b844a93db206
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Graph.md
@@ -0,0 +1,15 @@
+# Graph
+
+- `v::graph()`
+- `v::graph(string $additionalChars)`
+
+Validates all characters that are graphically represented.
+
+```php
+v::graph()->validate('LKM@#$%4;'); // true
+```
+
+***
+See also:
+
+  * [Prnt](Prnt.md)
diff --git a/vendor/respect/validation/docs/rules/HexRgbColor.md b/vendor/respect/validation/docs/rules/HexRgbColor.md
new file mode 100644
index 0000000000000000000000000000000000000000..3e4a1e98d9e538d3f90f4aa2f25ab88e85bfaf8e
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/HexRgbColor.md
@@ -0,0 +1,16 @@
+# HexRgbColor
+
+- `v::hexRgbColor()`
+
+Validates a hex RGB color
+
+```php
+v::hexRgbColor()->validate('#FFFAAA'); // true
+v::hexRgbColor()->validate('123123'); // true
+v::hexRgbColor()->validate('FCD'); // true
+```
+
+***
+See also:
+
+  * [Vxdigit](Vxdigit.md)
diff --git a/vendor/respect/validation/docs/rules/Identical.md b/vendor/respect/validation/docs/rules/Identical.md
new file mode 100644
index 0000000000000000000000000000000000000000..0a280678abbaa25cb0749d159cf47b5b117d7793
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Identical.md
@@ -0,0 +1,18 @@
+# Identical
+
+- `v::identical(mixed $value)`
+
+Validates if the input is identical to some value.
+
+```php
+v::identical(42)->validate(42); // true
+v::identical(42)->validate('42'); // false
+```
+
+Message template for this validator includes `{{compareTo}}`.
+
+***
+See also:
+
+  * [Contains](Contains.md)
+  * [Equals](Equals.md)
diff --git a/vendor/respect/validation/docs/rules/IdentityCard.md b/vendor/respect/validation/docs/rules/IdentityCard.md
new file mode 100644
index 0000000000000000000000000000000000000000..052eca1a28fd616d8538eae8d56d50cc2042a729
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/IdentityCard.md
@@ -0,0 +1,21 @@
+# IdentityCard
+
+- `v::identityCard(string $countryCode)`
+
+Validates Identity Card numbers according to the defined country.
+
+```php
+v::identityCard('PL')->validate('AYW036733'); // true
+v::identityCard('PL')->validate('APH505567'); // true
+v::identityCard('PL')->validate('APH 505567'); // false
+v::identityCard('PL')->validate('AYW036731'); // false
+```
+
+For now this rule only accepts Polish Identity Card numbers (Dowód Osobisty).
+
+***
+See also:
+
+  * [Bank](Bank.md)
+  * [Pesel](Pesel.md)
+  * [SubdivisionCode](SubdivisionCode.md)
diff --git a/vendor/respect/validation/docs/rules/Image.md b/vendor/respect/validation/docs/rules/Image.md
new file mode 100644
index 0000000000000000000000000000000000000000..5a84facf66d6e3f41314691b25d1fee0db21c383
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Image.md
@@ -0,0 +1,32 @@
+# Image
+
+- `v::image()`
+- `v::image(finfo $fileInfo)`
+
+Validates if the file is a valid image by checking its MIME type.
+
+```php
+v::image()->validate('image.gif'); // true
+v::image()->validate('image.jpg'); // true
+v::image()->validate('image.png'); // true
+```
+
+All the validations above must return `false` if the input is not a valid file
+or of the MIME doesn't match with the file extension.
+
+This rule relies on [fileinfo](http://php.net/fileinfo) PHP extension.
+
+***
+See also:
+
+  * [Directory](Directory.md)
+  * [Executable](Executable.md)
+  * [Exists](Exists.md)
+  * [Extension](Extension.md)
+  * [File](File.md)
+  * [Mimetype](Mimetype.md)
+  * [Readable](Readable.md)
+  * [Size](Size.md)
+  * [SymbolicLink](SymbolicLink.md)
+  * [Uploaded](Uploaded.md)
+  * [Writable](Writable.md)
diff --git a/vendor/respect/validation/docs/rules/Imei.md b/vendor/respect/validation/docs/rules/Imei.md
new file mode 100644
index 0000000000000000000000000000000000000000..96ed8e0a2a9fd0175165d0c7dd96bdee6ae8d76b
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Imei.md
@@ -0,0 +1,20 @@
+# Imei
+
+- `v::imei()`
+
+Validates is the input is a valid [IMEI][].
+
+```php
+v::imei()->validate('35-209900-176148-1'); // true
+v::imei()->validate('490154203237518'); // true
+```
+
+***
+See also:
+
+  * [Bsn](Bsn.md)
+  * [Cnh](Cnh.md)
+  * [Cnpj](Cnpj.md)
+  * [Cpf](Cpf.md)
+
+[IMEI]: https://en.wikipedia.org/wiki/International_Mobile_Station_Equipment_Identity "International Mobile Station Equipment Identity"
diff --git a/vendor/respect/validation/docs/rules/In.md b/vendor/respect/validation/docs/rules/In.md
new file mode 100644
index 0000000000000000000000000000000000000000..a8aa698141b844e55c90845c8acdefca31f05ba1
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/In.md
@@ -0,0 +1,30 @@
+# In
+
+- `v::in(mixed $haystack)`
+- `v::in(mixed $haystack, boolean $identical = false)`
+
+Validates if the input is contained in a specific haystack.
+
+For strings:
+
+```php
+v::in('lorem ipsum')->validate('ipsum'); // true
+```
+
+For arrays:
+
+```php
+v::in(['lorem', 'ipsum'])->validate('lorem'); // true
+```
+
+A second parameter may be passed for identical comparison instead
+of equal comparison.
+
+Message template for this validator includes `{{haystack}}`.
+
+***
+See also:
+
+  * [StartsWith](StartsWith.md)
+  * [EndsWith](EndsWith.md)
+  * [Contains](Contains.md)
diff --git a/vendor/respect/validation/docs/rules/Infinite.md b/vendor/respect/validation/docs/rules/Infinite.md
new file mode 100644
index 0000000000000000000000000000000000000000..d29c666a4f11afd0ae697fca58c4c837705a72b1
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Infinite.md
@@ -0,0 +1,17 @@
+# Infinite
+
+- `v::infinite()`
+
+Validates if the input is an infinite number.
+
+```php
+v::infinite()->validate(INF); // true
+```
+
+***
+See also:
+
+  * [Digit](Digit.md)
+  * [Finite](Finite.md)
+  * [IntVal](IntVal.md)
+  * [Numeric](Numeric.md)
diff --git a/vendor/respect/validation/docs/rules/Instance.md b/vendor/respect/validation/docs/rules/Instance.md
new file mode 100644
index 0000000000000000000000000000000000000000..bbb45dd0c888a24183525f1d9e539c59bbd3c8cf
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Instance.md
@@ -0,0 +1,17 @@
+# Instance
+
+- `v::instance(string $instanceName)`
+
+Validates if the input is an instance of the given class or interface.
+
+```php
+v::instance('DateTime')->validate(new DateTime); // true
+v::instance('Traversable')->validate(new ArrayObject); // true
+```
+
+Message template for this validator includes `{{instanceName}}`.
+
+***
+See also:
+
+  * [ObjectType](ObjectType.md)
diff --git a/vendor/respect/validation/docs/rules/IntType.md b/vendor/respect/validation/docs/rules/IntType.md
new file mode 100644
index 0000000000000000000000000000000000000000..d4313657fccba3df7b4c9c9a4fd95744a6d10950
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/IntType.md
@@ -0,0 +1,27 @@
+# IntType
+
+- `v::intType()`
+
+Validates whether the type of a value is integer.
+
+```php
+v::intType()->validate(42); // true
+v::intType()->validate('10'); // false
+```
+
+***
+See also:
+
+  * [BoolType](BoolType.md)
+  * [CallableType](CallableType.md)
+  * [Digit](Digit.md)
+  * [Finite](Finite.md)
+  * [FloatType](FloatType.md)
+  * [Infinite](Infinite.md)
+  * [IntVal](IntVal.md)
+  * [NullType](NullType.md)
+  * [Numeric](Numeric.md)
+  * [ObjectType](ObjectType.md)
+  * [ResourceType](ResourceType.md)
+  * [StringType](StringType.md)
+  * [Type](Type.md)
diff --git a/vendor/respect/validation/docs/rules/IntVal.md b/vendor/respect/validation/docs/rules/IntVal.md
new file mode 100644
index 0000000000000000000000000000000000000000..18a04046192e8e3e6642d635d394946f16ec078f
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/IntVal.md
@@ -0,0 +1,18 @@
+# IntVal
+
+- `v::intVal()`
+
+Validates if the input is an integer.
+
+```php
+v::intVal()->validate('10'); // true
+v::intVal()->validate(10); // true
+```
+
+***
+See also:
+
+  * [Digit](Digit.md)
+  * [Finite](Finite.md)
+  * [Infinite](Infinite.md)
+  * [Numeric](Numeric.md)
diff --git a/vendor/respect/validation/docs/rules/Ip.md b/vendor/respect/validation/docs/rules/Ip.md
new file mode 100644
index 0000000000000000000000000000000000000000..927dbc7ef618932495bd9e39f38e735675d27d43
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Ip.md
@@ -0,0 +1,24 @@
+# Ip
+
+- `v::ip()`
+- `v::ip(mixed $options)`
+
+Validates IP Addresses. This validator uses the native filter_var()
+PHP function.
+
+```php
+v::ip()->validate('192.168.0.1');
+```
+
+You can pass a parameter with filter_var flags for IP.
+
+```php
+v::ip(FILTER_FLAG_NO_PRIV_RANGE)->validate('127.0.0.1'); // false
+```
+
+***
+See also:
+
+  * [Domain](Domain.md)
+  * [MacAddress](MacAddress.md)
+  * [Tld](Tld.md)
diff --git a/vendor/respect/validation/docs/rules/Iterable.md b/vendor/respect/validation/docs/rules/Iterable.md
new file mode 100644
index 0000000000000000000000000000000000000000..b64abe28899857ddec73717780d5e5acc4a4560c
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Iterable.md
@@ -0,0 +1,5 @@
+# Iterable
+
+- `v::iterable()`
+
+**Deprecated**: Use [IterableType](IterableType.md) instead.
diff --git a/vendor/respect/validation/docs/rules/IterableType.md b/vendor/respect/validation/docs/rules/IterableType.md
new file mode 100644
index 0000000000000000000000000000000000000000..a246af271f9cba26a1b0dd538f1eb637e6056e14
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/IterableType.md
@@ -0,0 +1,20 @@
+# IterableType
+
+- `v::iterableType()`
+
+Validates if the input is iterable, in other words, if you're able to iterate
+over it with [foreach](http://php.net/foreach) language construct.
+
+```php
+v::iterableType()->validate([]); // true
+v::iterableType()->validate(new ArrayObject()); // true
+v::iterableType()->validate(new stdClass()); // true
+v::iterableType()->validate('string'); // false
+```
+
+***
+See also:
+
+  * [ArrayVal](ArrayVal.md)
+  * [Countable](Countable.md)
+  * [Instance](Instance.md)
diff --git a/vendor/respect/validation/docs/rules/Json.md b/vendor/respect/validation/docs/rules/Json.md
new file mode 100644
index 0000000000000000000000000000000000000000..eb41367ceaf4ffe1b8060977dada30cad6981013
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Json.md
@@ -0,0 +1,18 @@
+# Json
+
+- `v::json()`
+
+Validates if the given input is a valid JSON.
+
+```php
+v::json()->validate('{"foo":"bar"}'); // true
+```
+
+***
+See also:
+
+  * [Domain](Domain.md)
+  * [Email](Email.md)
+  * [FilterVar](FilterVar.md)
+  * [Phone](Phone.md)
+  * [VideoUrl](VideoUrl.md)
diff --git a/vendor/respect/validation/docs/rules/Key.md b/vendor/respect/validation/docs/rules/Key.md
new file mode 100644
index 0000000000000000000000000000000000000000..70a74c07a5a2836c4879799cddfbae89d377a23a
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Key.md
@@ -0,0 +1,34 @@
+# Key
+
+- `v::key(string $name)`
+- `v::key(string $name, v $validator)`
+- `v::key(string $name, v $validator, boolean $mandatory = true)`
+
+Validates an array key.
+
+```php
+$dict = [
+    'foo' => 'bar'
+];
+
+v::key('foo')->validate($dict); // true
+```
+
+You can also validate the key value itself:
+
+```php
+v::key('foo', v::equals('bar'))->validate($dict); // true
+```
+
+Third parameter makes the key presence optional:
+
+```php
+v::key('lorem', v::stringType(), false)->validate($dict); // true
+```
+
+The name of this validator is automatically set to the key name.
+
+***
+See also:
+
+  * [Attribute](Attribute.md)
diff --git a/vendor/respect/validation/docs/rules/KeyNested.md b/vendor/respect/validation/docs/rules/KeyNested.md
new file mode 100644
index 0000000000000000000000000000000000000000..3129dde31c723082fab3332c9c0fa6a3ef81bd2e
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/KeyNested.md
@@ -0,0 +1,40 @@
+# KeyNested
+
+- `v::keyNested(string $name)`
+- `v::keyNested(string $name, v $validator)`
+- `v::keyNested(string $name, v $validator, boolean $mandatory = true)`
+
+Validates an array key or an object property using `.` to represent nested data.
+
+Validating keys from arrays or `ArrayAccess` instances:
+
+```php
+$array = [
+    'foo' => [
+        'bar' => 123,
+    ],
+];
+
+v::keyNested('foo.bar')->validate($array); // true
+```
+
+Validating object properties:
+
+```php
+$object = new stdClass();
+$object->foo = new stdClass();
+$object->foo->bar = 42;
+
+v::keyNested('foo.bar')->validate($object); // true
+```
+
+This rule was inspired by [Yii2 ArrayHelper][].
+
+***
+See also:
+
+  * [Attribute](Attribute.md)
+  * [Key](Key.md)
+
+
+[Yii2 ArrayHelper]: https://github.com/yiisoft/yii2/blob/68c30c1/framework/helpers/BaseArrayHelper.php "Yii2 ArrayHelper"
diff --git a/vendor/respect/validation/docs/rules/KeySet.md b/vendor/respect/validation/docs/rules/KeySet.md
new file mode 100644
index 0000000000000000000000000000000000000000..faf40818aafbd384335127681e039e5c744ce1dc
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/KeySet.md
@@ -0,0 +1,51 @@
+# KeySet
+
+- `v::keySet(Key $rule...)`
+
+Validates a keys in a defined structure.
+
+```php
+$dict = ['foo' => 42];
+
+v::keySet(
+    v::key('foo', v::intVal())
+)->validate($dict); // true
+```
+
+Extra keys are not allowed:
+```php
+$dict = ['foo' => 42, 'bar' => 'String'];
+
+v::keySet(
+    v::key('foo', v::intVal())
+)->validate($dict); // false
+```
+
+Missing required keys are not allowed:
+```php
+$dict = ['foo' => 42, 'bar' => 'String'];
+
+v::keySet(
+    v::key('foo', v::intVal()),
+    v::key('bar', v::stringType()),
+    v::key('baz', v::boolType())
+)->validate($dict); // false
+```
+
+Missing non-required keys are allowed:
+```php
+$dict = ['foo' => 42, 'bar' => 'String'];
+
+v::keySet(
+    v::key('foo', v::intVal()),
+    v::key('bar', v::stringType()),
+    v::key('baz', v::boolType(), false)
+)->validate($dict); // true
+```
+
+The keys' order is not considered in the validation.
+
+***
+See also:
+
+  * [Key](Key.md)
diff --git a/vendor/respect/validation/docs/rules/KeyValue.md b/vendor/respect/validation/docs/rules/KeyValue.md
new file mode 100644
index 0000000000000000000000000000000000000000..98ee9db8731581b6fbda57e80f05742a5e4b4b0c
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/KeyValue.md
@@ -0,0 +1,62 @@
+# KeyValue
+
+- `keyValue(string $comparedKey, string $ruleName, string $baseKey)`
+
+Performs validation of `$comparedKey` using the rule named on `$ruleName` with
+`$baseKey` as base.
+
+Sometimes, when validating arrays, the validation of a key value depends on
+another key value and that may cause some ugly code since you need the input
+before the validation, making some checking manually:
+
+```php
+v::key('password')->check($_POST);
+v::key('password_confirmation', v::equals($_POST['password']))->check($_POST);
+```
+
+The problem with the above code is because you do not know if `password` is a
+valid key, so you must check it manually before performing the validation on
+`password_confirmation`.
+
+The `keyValue()` rule makes this job easier by creating a rule named on
+`$ruleName` passing `$baseKey` as the first argument of this rule, see an example:
+
+```php
+v::keyValue('password_confirmation', 'equals', 'password')->validate($_POST);
+```
+
+The above code will result on `true` if _`$_POST['password_confirmation']` is
+[equals](Equals.md) to `$_POST['password']`_, it's the same of:
+
+See another example:
+
+```php
+v::keyValue('state', 'subdivisionCode', 'country')->validate($_POST);
+```
+
+The above code will result on `true` if _`$_POST['state']` is a
+[subdivision code](SubdivisionCode.md) of `$_POST['country']`_:
+
+This rule will invalidate the input if `$comparedKey` or `$baseKey` don't exist,
+or if the rule named on `$ruleName` could not be created (or don't exist).
+
+When using `assert()` or `check()` methods and the rule do not pass, it overwrites
+all values in the validation exceptions with `$baseKey` and `$comparedKey`.
+
+```php
+v::keyValue('password_confirmation', 'equals', 'password')->check($input);
+```
+
+The above code may generate the message:
+
+```
+password_confirmation must be equals "password"
+```
+
+***
+See also:
+
+  * [Key](Key.md)
+  * [KeyNested](KeyNested.md)
+  * [KeySet](KeySet.md)
+
diff --git a/vendor/respect/validation/docs/rules/LanguageCode.md b/vendor/respect/validation/docs/rules/LanguageCode.md
new file mode 100644
index 0000000000000000000000000000000000000000..a990f315a9a6f8d8b5261bfeab954fa5890a1bed
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/LanguageCode.md
@@ -0,0 +1,20 @@
+# LanguageCode
+
+- `v::languageCode()`
+
+Validates a language code based on ISO 639:
+
+```php
+v::languageCode()->validate('pt'); // true
+v::languageCode()->validate('en'); // true
+v::languageCode()->validate('it'); // true
+v::languageCode('alpha-3')->validate('ita'); // true
+v::languageCode('alpha-3')->validate('eng'); // true
+```
+
+You can choose between alpha-2 and alpha-3, alpha-2 is set by default.
+
+***
+See also:
+
+  * [CountryCode](CountryCode.md)
\ No newline at end of file
diff --git a/vendor/respect/validation/docs/rules/LeapDate.md b/vendor/respect/validation/docs/rules/LeapDate.md
new file mode 100644
index 0000000000000000000000000000000000000000..aa0e7581f7ab4207e3f863dcd14a6c800cd5bd14
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/LeapDate.md
@@ -0,0 +1,18 @@
+# LeapDate
+
+- `v::leapDate(string $format)`
+
+Validates if a date is leap.
+
+```php
+v::leapDate('Y-m-d')->validate('1988-02-29'); // true
+```
+
+This validator accepts DateTime instances as well. The $format
+parameter is mandatory.
+
+***
+See also:
+
+  * [Date](Date.md)
+  * [LeapYear](LeapYear.md)
diff --git a/vendor/respect/validation/docs/rules/LeapYear.md b/vendor/respect/validation/docs/rules/LeapYear.md
new file mode 100644
index 0000000000000000000000000000000000000000..db208993164f717ab648968850f78013a8794f74
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/LeapYear.md
@@ -0,0 +1,17 @@
+# LeapYear
+
+- `v::leapYear()`
+
+Validates if a year is leap.
+
+```php
+v::leapYear()->validate('1988'); // true
+```
+
+This validator accepts DateTime instances as well.
+
+***
+See also:
+
+  * [Date](Date.md)
+  * [LeapDate](LeapDate.md)
diff --git a/vendor/respect/validation/docs/rules/Length.md b/vendor/respect/validation/docs/rules/Length.md
new file mode 100644
index 0000000000000000000000000000000000000000..2fbf519536b85d41e705750ef6d05b57d0d59645
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Length.md
@@ -0,0 +1,44 @@
+# Length
+
+- `v::length(int $min, int $max)`
+- `v::length(int $min, null)`
+- `v::length(null, int $max)`
+- `v::length(int $min, int $max, boolean $inclusive = true)`
+
+Validates lengths. Most simple example:
+
+```php
+v::stringType()->length(1, 5)->validate('abc'); // true
+```
+
+You can also validate only minimum length:
+
+```php
+v::stringType()->length(5, null)->validate('abcdef'); // true
+```
+
+Only maximum length:
+
+```php
+v::stringType()->length(null, 5)->validate('abc'); // true
+```
+
+The type as the first validator in a chain is a good practice,
+since length accepts many types:
+
+```php
+v::arrayVal()->length(1, 5)->validate(['foo', 'bar']); // true
+```
+
+A third parameter may be passed to validate the passed values inclusive:
+
+```php
+v::stringType()->length(1, 5, true)->validate('a'); // true
+```
+
+Message template for this validator includes `{{minValue}}` and `{{maxValue}}`.
+
+***
+See also:
+
+  * [Between](Between.md)
diff --git a/vendor/respect/validation/docs/rules/Lowercase.md b/vendor/respect/validation/docs/rules/Lowercase.md
new file mode 100644
index 0000000000000000000000000000000000000000..a79f82b76fdb91a36d71b1ceffa09daa00ea1b07
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Lowercase.md
@@ -0,0 +1,14 @@
+# Lowercase
+
+- `v::lowercase()`
+
+Validates if string characters are lowercase in the input:
+
+```php
+v::stringType()->lowercase()->validate('xkcd'); // true
+```
+
+***
+See also:
+
+  * [Uppercase](Uppercase.md)
diff --git a/vendor/respect/validation/docs/rules/MacAddress.md b/vendor/respect/validation/docs/rules/MacAddress.md
new file mode 100644
index 0000000000000000000000000000000000000000..92f427af302b90114e4c6705be4687b1c87a3c29
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/MacAddress.md
@@ -0,0 +1,17 @@
+# MacAddress
+
+- `v::macAddress()`
+
+Validates a Mac Address.
+
+```php
+v::macAddress()->validate('00:11:22:33:44:55'); // true
+v::macAddress()->validate('af-AA-22-33-44-55'); // true
+```
+
+***
+See also:
+
+  * [Domain](Domain.md)
+  * [Ip](Ip.md)
+  * [Tld](Tld.md)
diff --git a/vendor/respect/validation/docs/rules/Max.md b/vendor/respect/validation/docs/rules/Max.md
new file mode 100644
index 0000000000000000000000000000000000000000..1c199b9497ad9067e2241117fbc8893543443996
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Max.md
@@ -0,0 +1,36 @@
+# Max
+
+- `v::max(mixed $maxValue)`
+- `v::max(mixed $maxValue, boolean $inclusive = true)`
+
+Validates if the input doesn't exceed the maximum value.
+
+```php
+v::intVal()->max(15)->validate(20); // false
+v::intVal()->max(20)->validate(20); // false
+v::intVal()->max(20, true)->validate(20); // true
+```
+
+Also accepts dates:
+
+```php
+v::date()->max('2012-01-01')->validate('2010-01-01'); // true
+```
+
+Also date intervals:
+
+```php
+// Same of minimum age validation
+v::date()->max('-18 years')->validate('1988-09-09'); // true
+```
+
+`true` may be passed as a parameter to indicate that inclusive
+values must be used.
+
+Message template for this validator includes `{{maxValue}}`.
+
+***
+See also:
+
+  * [Min](Min.md)
+  * [Between](Between.md)
diff --git a/vendor/respect/validation/docs/rules/Mimetype.md b/vendor/respect/validation/docs/rules/Mimetype.md
new file mode 100644
index 0000000000000000000000000000000000000000..a2af05e8c74aec337944d3a1566a5a050ba822b4
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Mimetype.md
@@ -0,0 +1,25 @@
+# Mimetype
+
+- `v::mimetype(string $mimetype)`
+
+Validates if the file mimetype matches the expected one:
+
+```php
+v::mimetype('image/png')->validate('image.png'); // true
+```
+
+This rule is case-sensitive and requires [fileinfo](http://php.net/fileinfo) PHP extension.
+
+***
+See also:
+
+  * [Directory](Directory.md)
+  * [Executable](Executable.md)
+  * [Exists](Exists.md)
+  * [Extension](Extension.md)
+  * [File](File.md)
+  * [Readable](Readable.md)
+  * [Size](Size.md)
+  * [SymbolicLink](SymbolicLink.md)
+  * [Uploaded](Uploaded.md)
+  * [Writable](Writable.md)
diff --git a/vendor/respect/validation/docs/rules/Min.md b/vendor/respect/validation/docs/rules/Min.md
new file mode 100644
index 0000000000000000000000000000000000000000..460154f25f8435fe489caff1681dd0c1ee49ce37
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Min.md
@@ -0,0 +1,29 @@
+# Min
+
+- `v::min(mixed $minValue)`
+- `v::min(mixed $minValue, boolean $inclusive = true)`
+
+Validates if the input is greater than the minimum value.
+
+```php
+v::intVal()->min(15)->validate(5); // false
+v::intVal()->min(5)->validate(5); // false
+v::intVal()->min(5, true)->validate(5); // true
+```
+
+Also accepts dates:
+
+```php
+v::date()->min('2012-01-01')->validate('2015-01-01'); // true
+```
+
+`true` may be passed as a parameter to indicate that inclusive
+values must be used.
+
+Message template for this validator includes `{{minValue}}`.
+
+***
+See also:
+
+  * [Max](Max.md)
+  * [Between](Between.md)
diff --git a/vendor/respect/validation/docs/rules/MinimumAge.md b/vendor/respect/validation/docs/rules/MinimumAge.md
new file mode 100644
index 0000000000000000000000000000000000000000..33b457ae0b0ad2d3b34288a56569dee05e6e6188
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/MinimumAge.md
@@ -0,0 +1,23 @@
+# MinimumAge
+
+This is going to be deprecated, please use [Age](Age.md) instead.
+
+- `v::minimumAge(int $age)`
+- `v::minimumAge(int $age, string $format)`
+
+Validates a minimum age for a given date.
+
+```php
+v::minimumAge(18)->validate('1987-01-01'); // true
+v::minimumAge(18, 'd/m/Y')->validate('01/01/1987'); // true
+```
+
+Using `date()` before is a best-practice.
+
+Message template for this validator includes `{{age}}`.
+
+***
+See also:
+
+  * [Age](Age.md)
+  * [Date](Date.md)
diff --git a/vendor/respect/validation/docs/rules/Multiple.md b/vendor/respect/validation/docs/rules/Multiple.md
new file mode 100644
index 0000000000000000000000000000000000000000..b6d8c7ff02ce91038d3b57c4abff84308cacc737
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Multiple.md
@@ -0,0 +1,14 @@
+# Multiple
+
+- `v::multiple(int $multipleOf)`
+
+Validates if the input is a multiple of the given parameter
+
+```php
+v::intVal()->multiple(3)->validate(9); // true
+```
+
+***
+See also:
+
+  * [PrimeNumber](PrimeNumber.md)
diff --git a/vendor/respect/validation/docs/rules/Negative.md b/vendor/respect/validation/docs/rules/Negative.md
new file mode 100644
index 0000000000000000000000000000000000000000..efecb52018b0763c49d61deec12b3f45b1bbec7c
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Negative.md
@@ -0,0 +1,14 @@
+# Negative
+
+- `v::negative()`
+
+Validates if a number is lower than zero
+
+```php
+v::numeric()->negative()->validate(-15); // true
+```
+
+***
+See also:
+
+  * [Positive](Positive.md)
diff --git a/vendor/respect/validation/docs/rules/NfeAccessKey.md b/vendor/respect/validation/docs/rules/NfeAccessKey.md
new file mode 100644
index 0000000000000000000000000000000000000000..2f9188f1b0ce52d960b4ef30f58dd17b203c6d61
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/NfeAccessKey.md
@@ -0,0 +1,16 @@
+# NfeAccessKey
+
+- `v::nfeAccessKey(string $accessKey)`
+
+Validates the access key of the Brazilian electronic invoice (NFe).
+
+```php
+v::nfeAccessKey()->validate('31841136830118868211870485416765268625116906'); // true
+```
+
+***
+See also:
+
+  * [Cnh](Cnh.md)
+  * [Cnpj](Cnpj.md)
+  * [Cpf](Cpf.md)
diff --git a/vendor/respect/validation/docs/rules/No.md b/vendor/respect/validation/docs/rules/No.md
new file mode 100644
index 0000000000000000000000000000000000000000..e50b4c72becd343ad8682869488b9522d8fd456f
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/No.md
@@ -0,0 +1,24 @@
+# No
+
+- `v::no()`
+- `v::no(boolean $locale)`
+
+Validates if value is considered as "No".
+
+```php
+v::no()->validate('N'); // true
+v::no()->validate('Nay'); // true
+v::no()->validate('Nix'); // true
+v::no()->validate('No'); // true
+v::no()->validate('Nope'); // true
+v::no()->validate('Not'); // true
+```
+
+This rule is case insensitive.
+
+If `$locale` is TRUE, uses the value of [nl_langinfo()](http://php.net/nl_langinfo) with `NOEXPR` constant.
+
+***
+See also:
+
+  * [Yes](Yes.md)
diff --git a/vendor/respect/validation/docs/rules/NoWhitespace.md b/vendor/respect/validation/docs/rules/NoWhitespace.md
new file mode 100644
index 0000000000000000000000000000000000000000..052cfcdee028d66c24fe55bb1b6bce6ee64e7a27
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/NoWhitespace.md
@@ -0,0 +1,21 @@
+# NoWhitespace
+
+- `v::noWhitespace()`
+
+Validates if a string contains no whitespace (spaces, tabs and line breaks);
+
+```php
+v::noWhitespace()->validate('foo bar');  //false
+v::noWhitespace()->validate("foo\nbar"); // false
+```
+
+This is most useful when chaining with other validators such as `v::alnum()`
+
+***
+See also:
+
+  * [Alnum](Alnum.md)
+  * [Alpha](Alpha.md)
+  * [NotBlank](NotBlank.md)
+  * [NotEmpty](NotEmpty.md)
+  * [NotOptional](NotOptional.md)
diff --git a/vendor/respect/validation/docs/rules/NoneOf.md b/vendor/respect/validation/docs/rules/NoneOf.md
new file mode 100644
index 0000000000000000000000000000000000000000..44e91537e8754135bf7f3a0400e0d5259d036188
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/NoneOf.md
@@ -0,0 +1,21 @@
+# NoneOf
+
+- `v::noneOf(v $v1, v $v2, v $v3...)`
+
+Validates if NONE of the given validators validate:
+
+```php
+v::noneOf(
+    v::intVal(),
+    v::floatVal()
+)->validate('foo'); // true
+```
+
+In the sample above, 'foo' isn't a integer nor a float, so noneOf returns true.
+
+***
+See also:
+
+  * [Not](Not.md)
+  * [AllOf](AllOf.md)
+  * [OneOf](OneOf.md)
diff --git a/vendor/respect/validation/docs/rules/Not.md b/vendor/respect/validation/docs/rules/Not.md
new file mode 100644
index 0000000000000000000000000000000000000000..31f575bca1748ce768a9dbc54e4b9ed4d7d7e4c3
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Not.md
@@ -0,0 +1,24 @@
+# Not
+
+- `v::not(v $negatedValidator)`
+
+Negates any rule.
+
+```php
+v::not(v::ip())->validate('foo'); // true
+```
+
+In the sample above, validator returns true because 'foo' isn't an IP Address.
+
+You can negate complex, grouped or chained validators as well:
+
+```php
+v::not(v::intVal()->positive())->validate(-1.5); // true
+```
+
+Each other validation has custom messages for negated rules.
+
+***
+See also:
+
+  * [NoneOf](NoneOf.md)
diff --git a/vendor/respect/validation/docs/rules/NotBlank.md b/vendor/respect/validation/docs/rules/NotBlank.md
new file mode 100644
index 0000000000000000000000000000000000000000..35a5ef2de273e29918c98c4a743603b026ffd3ca
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/NotBlank.md
@@ -0,0 +1,34 @@
+# NotBlank
+
+- `v::notBlank()`
+
+Validates if the given input is not a blank value (`null`, zeros, empty strings
+or empty arrays, recursively).
+
+```php
+v::notBlank()->validate(null); // false
+v::notBlank()->validate(''); // false
+v::notBlank()->validate([]); // false
+v::notBlank()->validate(' '); // false
+v::notBlank()->validate(0); // false
+v::notBlank()->validate('0'); // false
+v::notBlank()->validate(0); // false
+v::notBlank()->validate('0.0'); // false
+v::notBlank()->validate(false); // false
+v::notBlank()->validate(['']); // false
+v::notBlank()->validate([' ']); // false
+v::notBlank()->validate([0]); // false
+v::notBlank()->validate(['0']); // false
+v::notBlank()->validate([false]); // false
+v::notBlank()->validate([[''], [0]]); // false
+v::notBlank()->validate(new stdClass()); // false
+```
+
+It's similar to [NotEmpty](NotEmpty.md) but it's way more strict.
+
+***
+See also:
+
+  * [NoWhitespace](NoWhitespace.md)
+  * [NotEmpty](NotEmpty.md)
+  * [NullType](NullType.md)
diff --git a/vendor/respect/validation/docs/rules/NotEmpty.md b/vendor/respect/validation/docs/rules/NotEmpty.md
new file mode 100644
index 0000000000000000000000000000000000000000..ab888f6a641664ad296b7de9cb616c4421500f08
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/NotEmpty.md
@@ -0,0 +1,42 @@
+# NotEmpty
+
+- `v::notEmpty()`
+
+Validates if the given input is not empty or in other words is input mandatory and
+required. This function also takes whitespace into account, use `noWhitespace()`
+if no spaces or linebreaks and other whitespace anywhere in the input is desired.
+
+```php
+v::stringType()->notEmpty()->validate(''); // false
+```
+
+Null values are empty:
+
+```php
+v::notEmpty()->validate(null); // false
+```
+
+Numbers:
+
+```php
+v::intVal()->notEmpty()->validate(0); // false
+```
+
+Empty arrays:
+
+```php
+v::arrayVal()->notEmpty()->validate([]); // false
+```
+
+Whitespace:
+
+```php
+v::stringType()->notEmpty()->validate('        ');  //false
+v::stringType()->notEmpty()->validate("\t \n \r");  //false
+```
+
+***
+See also:
+
+  * [NoWhitespace](NoWhitespace.md)
+  * [NullType](NullType.md)
diff --git a/vendor/respect/validation/docs/rules/NotOptional.md b/vendor/respect/validation/docs/rules/NotOptional.md
new file mode 100644
index 0000000000000000000000000000000000000000..487e7f1ef253ff82ead7b3f150f7c07a3163209b
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/NotOptional.md
@@ -0,0 +1,39 @@
+# NotOptional
+
+- `v::notOptional()`
+
+Validates if the given input is not optional. By _optional_ we consider `null`
+or an empty string (`''`).
+
+```php
+v::notOptional()->validate(''); // false
+v::notOptional()->validate(null); // false
+```
+
+Other values:
+
+```php
+v::notOptional()->validate([]); // true
+v::notOptional()->validate(' '); // true
+v::notOptional()->validate(0); // true
+v::notOptional()->validate('0'); // true
+v::notOptional()->validate(0); // true
+v::notOptional()->validate('0.0'); // true
+v::notOptional()->validate(false); // true
+v::notOptional()->validate(['']); // true
+v::notOptional()->validate([' ']); // true
+v::notOptional()->validate([0]); // true
+v::notOptional()->validate(['0']); // true
+v::notOptional()->validate([false]); // true
+v::notOptional()->validate([[''), [0]]); // true
+v::notOptional()->validate(new stdClass()); // true
+```
+
+***
+See also:
+
+  * [NoWhitespace](NoWhitespace.md)
+  * [NotBlank](NotBlank.md)
+  * [NotEmpty](NotEmpty.md)
+  * [NullType](NullType.md)
+  * [Optional](Optional.md)
diff --git a/vendor/respect/validation/docs/rules/NullType.md b/vendor/respect/validation/docs/rules/NullType.md
new file mode 100644
index 0000000000000000000000000000000000000000..5b39feb6c2d0eb36a964fc982d2fcd58aa73cea8
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/NullType.md
@@ -0,0 +1,25 @@
+# NullType
+
+- `v::nullType()`
+
+Validates if the input is null.
+
+```php
+v::nullType()->validate(null); // true
+```
+
+***
+See also:
+
+  * [BoolType](BoolType.md)
+  * [CallableType](CallableType.md)
+  * [FloatType](FloatType.md)
+  * [IntType](IntType.md)
+  * [NotBlank](NotBlank.md)
+  * [NotEmpty](NotEmpty.md)
+  * [NotOptional](NotOptional.md)
+  * [NullType](NullType.md)
+  * [ObjectType](ObjectType.md)
+  * [ResourceType](ResourceType.md)
+  * [StringType](StringType.md)
+  * [Type](Type.md)
diff --git a/vendor/respect/validation/docs/rules/Numeric.md b/vendor/respect/validation/docs/rules/Numeric.md
new file mode 100644
index 0000000000000000000000000000000000000000..d8121fb61c9ae621e4ebe74b45b80853e5c95721
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Numeric.md
@@ -0,0 +1,18 @@
+# Numeric
+
+- `v::numeric()`
+
+Validates on any numeric value.
+
+```php
+v::numeric()->validate(-12); // true
+v::numeric()->validate('135.0'); // true
+```
+
+***
+See also:
+
+  * [Digit](Digit.md)
+  * [Finite](Finite.md)
+  * [Infinite](Infinite.md)
+  * [IntVal](IntVal.md)
diff --git a/vendor/respect/validation/docs/rules/ObjectType.md b/vendor/respect/validation/docs/rules/ObjectType.md
new file mode 100644
index 0000000000000000000000000000000000000000..39212603155048570ca891161b495f1f8f41792c
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/ObjectType.md
@@ -0,0 +1,24 @@
+# ObjectType
+
+- `v::objectType()`
+
+Validates if the input is an object.
+
+```php
+v::objectType()->validate(new stdClass); // true
+```
+
+***
+See also:
+
+  * [Attribute](Attribute.md)
+  * [BoolType](BoolType.md)
+  * [CallableType](CallableType.md)
+  * [FloatType](FloatType.md)
+  * [Instance](Instance.md)
+  * [IntType](IntType.md)
+  * [NullType](NullType.md)
+  * [ObjectType](ObjectType.md)
+  * [ResourceType](ResourceType.md)
+  * [StringType](StringType.md)
+  * [Type](Type.md)
diff --git a/vendor/respect/validation/docs/rules/Odd.md b/vendor/respect/validation/docs/rules/Odd.md
new file mode 100644
index 0000000000000000000000000000000000000000..b1a8d05fc978f94cd5e074288b5988969c161868
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Odd.md
@@ -0,0 +1,17 @@
+# Odd
+
+- `v::odd()`
+
+Validates an odd number.
+
+```php
+v::intVal()->odd()->validate(3); // true
+```
+
+Using `int()` before `odd()` is a best practice.
+
+***
+See also:
+
+  * [Even](Even.md)
+  * [Multiple](Multiple.md)
diff --git a/vendor/respect/validation/docs/rules/OneOf.md b/vendor/respect/validation/docs/rules/OneOf.md
new file mode 100644
index 0000000000000000000000000000000000000000..e16be50df043ad94aa622584da26a71746077ee0
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/OneOf.md
@@ -0,0 +1,25 @@
+# OneOf
+
+- `v::oneOf(v $v1, v $v2, v $v3...)`
+
+This is a group validator that acts as an OR operator.
+
+```php
+v::oneOf(
+    v::intVal(),
+    v::floatVal()
+)->validate(15.5); // true
+```
+
+In the sample above, `v::intVal()` doesn't validates, but
+`v::floatVal()` validates, so oneOf returns true.
+
+`v::oneOf` returns true if at least one inner validator
+passes.
+
+***
+See also:
+
+  * [AllOf](AllOf.md)
+  * [NoneOf](NoneOf.md)
+  * [When](When.md)
diff --git a/vendor/respect/validation/docs/rules/Optional.md b/vendor/respect/validation/docs/rules/Optional.md
new file mode 100644
index 0000000000000000000000000000000000000000..1a4aa5ec091f9731c70ae017489abc01c6424de1
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Optional.md
@@ -0,0 +1,21 @@
+# Optional
+
+- `v::optional(v $rule)`
+
+Validates if the given input is optional or not. By _optional_ we consider `null`
+or an empty string (`''`).
+
+```php
+v::optional(v::alpha())->validate(''); // true
+v::optional(v::digit())->validate(null); // true
+```
+
+
+***
+See also:
+
+  * [NoWhitespace](NoWhitespace.md)
+  * [NotBlank](NotBlank.md)
+  * [NotEmpty](NotEmpty.md)
+  * [NotOptional](NotOptional.md)
+  * [NullType](NullType.md)
diff --git a/vendor/respect/validation/docs/rules/PerfectSquare.md b/vendor/respect/validation/docs/rules/PerfectSquare.md
new file mode 100644
index 0000000000000000000000000000000000000000..3a344b27c9125bd28f0ac6afc1fa8b3d485d372b
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/PerfectSquare.md
@@ -0,0 +1,16 @@
+# PerfectSquare
+
+- `v::perfectSquare()`
+
+Validates a perfect square.
+
+```php
+v::perfectSquare()->validate(25); // true (5*5)
+v::perfectSquare()->validate(9); // true (3*3)
+```
+
+***
+See also:
+
+  * [Factor](Factor.md)
+  * [PrimeNumber](PrimeNumber.md)
diff --git a/vendor/respect/validation/docs/rules/Pesel.md b/vendor/respect/validation/docs/rules/Pesel.md
new file mode 100644
index 0000000000000000000000000000000000000000..275ff7f8d3117304f091e08f9b3424aff30cbf65
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Pesel.md
@@ -0,0 +1,12 @@
+# Pesel
+
+- `v::pesel()`
+
+Validates PESEL (Polish human identification number).
+
+```php
+v::pesel()->validate('21120209256'); // true
+v::pesel()->validate('97072704800'); // true
+v::pesel()->validate('97072704801'); // false
+v::pesel()->validate('PESEL123456'); // false
+```
diff --git a/vendor/respect/validation/docs/rules/Phone.md b/vendor/respect/validation/docs/rules/Phone.md
new file mode 100644
index 0000000000000000000000000000000000000000..ac3cba9b9582c7192467e65376d4c0e7dd8b0899
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Phone.md
@@ -0,0 +1,22 @@
+# Phone
+
+- `v::phone()`
+
+Validates a valid 7, 10, 11 digit phone number (North America, Europe and most
+Asian and Middle East countries), supporting country and area codes (in dot,
+space or dashed notations) such as:
+
+    (555)555-5555
+    555 555 5555
+    +5(555)555.5555
+    33(1)22 22 22 22
+    +33(1)22 22 22 22
+    +33(020)7777 7777
+    03-6106666
+
+***
+See also:
+
+  * [Email](Email.md)
+  * [Url](Url.md)
+  * [VideoUrl](VideoUrl.md)
diff --git a/vendor/respect/validation/docs/rules/PhpLabel.md b/vendor/respect/validation/docs/rules/PhpLabel.md
new file mode 100644
index 0000000000000000000000000000000000000000..4295267d72fb63767b02985ce2fd2f4479226e43
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/PhpLabel.md
@@ -0,0 +1,24 @@
+# PhpLabel
+
+- `v::phpLabel()`
+
+Validates if a value is considered a valid PHP Label, 
+so that it can be used as a *variable*, *function* or *class* name, for example.
+
+Reference:
+http://php.net/manual/en/language.variables.basics.php
+
+```php
+v::phpLabel()->validate('person'); //true
+v::phpLabel()->validate('foo'); //true
+v::phpLabel()->validate('4ccess'); //false
+```
+
+***
+See also:
+
+  * [Regex](Regex.md)
+  * [ResourceType](ResourceType.md)
+  * [Slug](Slug.md)
+  * [Charset](Charset.md)
+  
\ No newline at end of file
diff --git a/vendor/respect/validation/docs/rules/Positive.md b/vendor/respect/validation/docs/rules/Positive.md
new file mode 100644
index 0000000000000000000000000000000000000000..ecf75d9f00fd536907b1e56462fda4f184920501
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Positive.md
@@ -0,0 +1,14 @@
+# Positive
+
+- `v::positive()`
+
+Validates if a number is higher than zero
+
+```php
+v::numeric()->positive()->validate(-15); // false
+```
+
+***
+See also:
+
+  * [Negative](Negative.md)
diff --git a/vendor/respect/validation/docs/rules/PostalCode.md b/vendor/respect/validation/docs/rules/PostalCode.md
new file mode 100644
index 0000000000000000000000000000000000000000..c18ec96ede6a146bddb14f0c964cb95b278a4670
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/PostalCode.md
@@ -0,0 +1,22 @@
+# PostalCode
+
+- `v::postalCode(string $countryCode)`
+
+Validates a postal code according to the given country code.
+
+```php
+v::postalCode('BR')->validate('02179000'); // true
+v::postalCode('BR')->validate('02179-000'); // true
+v::postalCode('US')->validate('02179-000'); // false
+v::postalCode('US')->validate('55372'); // true
+v::postalCode('PL')->validate('99-300'); // true
+```
+
+Message template for this validator includes `{{countryCode}}`.
+
+Extracted from [GeoNames](http://www.geonames.org/).
+
+***
+See also:
+
+  * [CountryCode](CountryCode.md)
diff --git a/vendor/respect/validation/docs/rules/PrimeNumber.md b/vendor/respect/validation/docs/rules/PrimeNumber.md
new file mode 100644
index 0000000000000000000000000000000000000000..dee58eabd4c93b89b4e351fd856d9608beeddfa6
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/PrimeNumber.md
@@ -0,0 +1,16 @@
+# PrimeNumber
+
+- `v::primeNumber()`
+
+Validates a prime number
+
+```php
+v::primeNumber()->validate(7); // true
+```
+
+***
+See also:
+
+  * [Factor](Factor.md)
+  * [PerfectSquare](PerfectSquare.md)
+  * [PrimeNumber](PrimeNumber.md)
diff --git a/vendor/respect/validation/docs/rules/Prnt.md b/vendor/respect/validation/docs/rules/Prnt.md
new file mode 100644
index 0000000000000000000000000000000000000000..29dc6d76397b0d52e1cb0b8a269d4f4395952913
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Prnt.md
@@ -0,0 +1,15 @@
+# Prnt
+
+- `v::prnt()`
+- `v::prnt(string $additionalChars)`
+
+Similar to `v::graph` but accepts whitespace.
+
+```php
+v::prnt()->validate('LMKA0$% _123'); // true
+```
+
+***
+See also:
+
+  * [Graph](Graph.md)
diff --git a/vendor/respect/validation/docs/rules/Punct.md b/vendor/respect/validation/docs/rules/Punct.md
new file mode 100644
index 0000000000000000000000000000000000000000..d49411aa90978c85ffe136eb0afdf574dc985fd5
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Punct.md
@@ -0,0 +1,17 @@
+# Punct
+
+- `v::punct()`
+- `v::punct(string $additionalChars)`
+
+Accepts only punctuation characters:
+
+```php
+v::punct()->validate('&,.;[]'); // true
+```
+
+***
+See also:
+
+  * [Cntrl](Cntrl.md)
+  * [Graph](Graph.md)
+  * [Prnt](Prnt.md)
diff --git a/vendor/respect/validation/docs/rules/Readable.md b/vendor/respect/validation/docs/rules/Readable.md
new file mode 100644
index 0000000000000000000000000000000000000000..00a074eb9142aa4700965e2bf593d6b3cbd67e3d
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Readable.md
@@ -0,0 +1,23 @@
+# Readable
+
+- `v::readable()`
+
+Validates if the given data is a file exists and is readable.
+
+```php
+v::readable()->validate('/path/of/a/readable/file'); // true
+```
+
+***
+See also:
+
+  * [Directory](Directory.md)
+  * [Executable](Executable.md)
+  * [Exists](Exists.md)
+  * [Extension](Extension.md)
+  * [File](File.md)
+  * [Mimetype](Mimetype.md)
+  * [Size](Size.md)
+  * [SymbolicLink](SymbolicLink.md)
+  * [Uploaded](Uploaded.md)
+  * [Writable](Writable.md)
diff --git a/vendor/respect/validation/docs/rules/Regex.md b/vendor/respect/validation/docs/rules/Regex.md
new file mode 100644
index 0000000000000000000000000000000000000000..ed0d2517345e6fcb37c9ad8e68a3d83d98b815ab
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Regex.md
@@ -0,0 +1,21 @@
+# Regex
+
+- `v::regex(string $regex)`
+
+Evaluates a regex on the input and validates if matches
+
+```php
+v::regex('/[a-z]/')->validate('a'); // true
+```
+
+Message template for this validator includes `{{regex}}`
+
+***
+See also:
+
+  * [Alnum](Alnum.md)
+  * [Alpha](Alpha.md)
+  * [Contains](Contains.md)
+  * [Digit](Digit.md)
+  * [EndsWith](EndsWith.md)
+  * [StartsWith](StartsWith.md)
diff --git a/vendor/respect/validation/docs/rules/ResourceType.md b/vendor/respect/validation/docs/rules/ResourceType.md
new file mode 100644
index 0000000000000000000000000000000000000000..c21a2f1d89b7a85c966d8eefdd07e3abe4d4e565
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/ResourceType.md
@@ -0,0 +1,21 @@
+# ResourceType
+
+- `v::resourceType()`
+
+Validates if the input is a resource.
+
+```php
+v::resourceType()->validate(fopen('/path/to/file.txt', 'w')); // true
+```
+
+***
+See also:
+
+  * [BoolType](BoolType.md)
+  * [CallableType](CallableType.md)
+  * [FloatType](FloatType.md)
+  * [IntType](IntType.md)
+  * [NullType](NullType.md)
+  * [ObjectType](ObjectType.md)
+  * [StringType](StringType.md)
+  * [Type](Type.md)
diff --git a/vendor/respect/validation/docs/rules/Roman.md b/vendor/respect/validation/docs/rules/Roman.md
new file mode 100644
index 0000000000000000000000000000000000000000..b619930e1bf61ec67b70c246aa1aaa23bbfafb00
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Roman.md
@@ -0,0 +1,16 @@
+# Roman
+
+- `v::roman()`
+
+Validates roman numbers
+
+```php
+v::roman()->validate('IV'); // true
+```
+
+***
+See also:
+
+  * [In](In.md)
+  * [Regex](Regex.md)
+  * [Uppercase](Uppercase.md)
diff --git a/vendor/respect/validation/docs/rules/ScalarVal.md b/vendor/respect/validation/docs/rules/ScalarVal.md
new file mode 100644
index 0000000000000000000000000000000000000000..366657854d77bda4e2df7eac79e9787b4c392fc1
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/ScalarVal.md
@@ -0,0 +1,15 @@
+# ScalarVal
+
+- `v::scalarVal()`
+
+Validates if the input is a scalar value.
+
+```php
+v::scalarVal()->validate([]); // false
+v::scalarVal()->validate(135.0); // true
+```
+
+***
+See also:
+
+  * [Type](Type.md)
diff --git a/vendor/respect/validation/docs/rules/Sf.md b/vendor/respect/validation/docs/rules/Sf.md
new file mode 100644
index 0000000000000000000000000000000000000000..a7988a8b98d4027c45045556046a7d7169bc4a0b
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Sf.md
@@ -0,0 +1,19 @@
+# Sf
+
+- `v::sf(string $validator)`
+
+Use Symfony2 validators inside Respect\Validation flow. Messages
+are preserved.
+
+```php
+v::sf('Time')->validate('15:00:00');
+```
+
+
+You must add `"symfony/validator": "~2.6"` to your `require` property on composer.json file.
+
+
+***
+See also:
+
+  * [Zend](Zend.md)
diff --git a/vendor/respect/validation/docs/rules/Size.md b/vendor/respect/validation/docs/rules/Size.md
new file mode 100644
index 0000000000000000000000000000000000000000..13f5aee4e96aec1c383ef7bed1c78a471aa33f6f
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Size.md
@@ -0,0 +1,48 @@
+# Size
+
+- `v::size(string $minSize)`
+- `v::size(string $minSize, string $maxSize)`
+- `v::size(null, string $maxSize)`
+
+Validates file sizes:
+
+```php
+v::size('1KB')->validate($filename); // Must have at least 1KB size
+v::size('1MB', '2MB')->validate($filename); // Must have the size between 1MB and 2MB
+v::size(null, '1GB')->validate($filename); // Must not be greater than 1GB
+```
+
+Sizes are not case-sensitive and the accepted values are:
+
+- B
+- KB
+- MB
+- GB
+- TB
+- PB
+- EB
+- ZB
+- YB
+
+This validator will consider `SplFileInfo` instances, like:
+
+```php
+$fileInfo = new SplFileInfo($filename);
+v::size('1.5mb')->validate($fileInfo); // Will return true or false
+```
+
+Message template for this validator includes `{{minSize}}` and `{{maxSize}}`.
+
+***
+See also:
+
+  * [Directory](Directory.md)
+  * [Executable](Executable.md)
+  * [Exists](Exists.md)
+  * [Extension](Extension.md)
+  * [File](File.md)
+  * [Mimetype](Mimetype.md)
+  * [Readable](Readable.md)
+  * [SymbolicLink](SymbolicLink.md)
+  * [Uploaded](Uploaded.md)
+  * [Writable](Writable.md)
diff --git a/vendor/respect/validation/docs/rules/Slug.md b/vendor/respect/validation/docs/rules/Slug.md
new file mode 100644
index 0000000000000000000000000000000000000000..599bbbaac904fdef72ddcf7fc753511aa67fc8a7
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Slug.md
@@ -0,0 +1,17 @@
+# Slug
+
+- `v::slug()`
+
+Validates slug-like strings:
+
+```php
+v::slug()->validate('my-wordpress-title'); // true
+v::slug()->validate('my-wordpress--title'); // false
+v::slug()->validate('my-wordpress-title-'); // false
+```
+
+***
+See also:
+
+  * [Url](Url.md)
+  * [VideoUrl](VideoUrl.md)
diff --git a/vendor/respect/validation/docs/rules/Space.md b/vendor/respect/validation/docs/rules/Space.md
new file mode 100644
index 0000000000000000000000000000000000000000..a0faec57133569ef6457237c0eacd2f9e6782db0
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Space.md
@@ -0,0 +1,15 @@
+# Space
+
+- `v::space()`
+- `v::space(string $additionalChars)`
+
+Accepts only whitespace:
+
+```php
+v::space()->validate('    '); // true
+```
+
+***
+See also:
+
+  * [Cntrl](Cntrl.md)
diff --git a/vendor/respect/validation/docs/rules/StartsWith.md b/vendor/respect/validation/docs/rules/StartsWith.md
new file mode 100644
index 0000000000000000000000000000000000000000..842655d94b25b1460254e12d1d4618032cf77d5c
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/StartsWith.md
@@ -0,0 +1,31 @@
+# StartsWith
+
+- `v::startsWith(mixed $value)`
+- `v::startsWith(mixed $value, boolean $identical = false)`
+
+This validator is similar to `v::contains()`, but validates
+only if the value is at the beginning of the input.
+
+For strings:
+
+```php
+v::startsWith('lorem')->validate('lorem ipsum'); // true
+```
+
+For arrays:
+
+```php
+v::startsWith('lorem')->validate(['lorem', 'ipsum']); // true
+```
+
+`true` may be passed as a parameter to indicate identical comparison
+instead of equal.
+
+Message template for this validator includes `{{startValue}}`.
+
+***
+See also:
+
+  * [EndsWith](EndsWith.md)
+  * [Contains](Contains.md)
+  * [In](In.md)
diff --git a/vendor/respect/validation/docs/rules/StringType.md b/vendor/respect/validation/docs/rules/StringType.md
new file mode 100644
index 0000000000000000000000000000000000000000..624c3df8cfbe16b0774d69203d9d5d32a5c12f03
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/StringType.md
@@ -0,0 +1,22 @@
+# StringType
+
+- `v::stringType()`
+
+Validates a string.
+
+```php
+v::stringType()->validate('hi'); // true
+```
+
+***
+See also:
+
+  * [Alnum](Alnum.md)
+  * [BoolType](BoolType.md)
+  * [CallableType](CallableType.md)
+  * [FloatType](FloatType.md)
+  * [IntType](IntType.md)
+  * [NullType](NullType.md)
+  * [ObjectType](ObjectType.md)
+  * [ResourceType](ResourceType.md)
+  * [Type](Type.md)
diff --git a/vendor/respect/validation/docs/rules/SubdivisionCode.md b/vendor/respect/validation/docs/rules/SubdivisionCode.md
new file mode 100644
index 0000000000000000000000000000000000000000..3cb031922eff171dcac07b05827f9420049fa83c
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/SubdivisionCode.md
@@ -0,0 +1,284 @@
+# SubdivisionCode
+
+- `v::subdivisionCode(string $countryCode)`
+
+Validates subdivision country codes according to [ISO 3166-2][].
+
+The `$countryCode` must be a country in [ISO 3166-1 alpha-2][] format.
+
+```php
+v::subdivisionCode('BR')->validate('SP'); // true
+v::subdivisionCode('US')->validate('CA'); // true
+```
+
+This rule is case sensitive.
+
+## Available country codes
+
+- `AD`: Andorra
+- `AE`: United Arab Emirates
+- `AF`: Afghanistan
+- `AG`: Antigua and Barbuda
+- `AI`: Anguilla
+- `AL`: Albania
+- `AM`: Armenia
+- `AN`: AN.html
+- `AO`: Angola
+- `AQ`: Antarctica
+- `AR`: Argentina
+- `AS`: American Samoa
+- `AT`: Austria
+- `AU`: Australia
+- `AW`: Aruba
+- `AX`: Ã…land
+- `AZ`: Azerbaijan
+- `BA`: Bosnia and Herzegovina
+- `BB`: Barbados
+- `BD`: Bangladesh
+- `BE`: Belgium
+- `BF`: Burkina Faso
+- `BG`: Bulgaria
+- `BH`: Bahrain
+- `BI`: Burundi
+- `BJ`: Benin
+- `BL`: Saint Barthélemy
+- `BM`: Bermuda
+- `BN`: Brunei
+- `BO`: Bolivia
+- `BQ`: Bonaire
+- `BR`: Brazil
+- `BS`: Bahamas
+- `BT`: Bhutan
+- `BV`: Bouvet Island
+- `BW`: Botswana
+- `BY`: Belarus
+- `BZ`: Belize
+- `CA`: Canada
+- `CC`: Cocos [Keeling] Islands
+- `CD`: Democratic Republic of the Congo
+- `CF`: Central African Republic
+- `CG`: Republic of the Congo
+- `CH`: Switzerland
+- `CI`: Ivory Coast
+- `CK`: Cook Islands
+- `CL`: Chile
+- `CM`: Cameroon
+- `CN`: China
+- `CO`: Colombia
+- `CR`: Costa Rica
+- `CS`: CS.html
+- `CU`: Cuba
+- `CV`: Cape Verde
+- `CW`: Curacao
+- `CX`: Christmas Island
+- `CY`: Cyprus
+- `CZ`: Czech Republic
+- `DE`: Germany
+- `DJ`: Djibouti
+- `DK`: Denmark
+- `DM`: Dominica
+- `DO`: Dominican Republic
+- `DZ`: Algeria
+- `EC`: Ecuador
+- `EE`: Estonia
+- `EG`: Egypt
+- `EH`: Western Sahara
+- `ER`: Eritrea
+- `ES`: Spain
+- `ET`: Ethiopia
+- `FI`: Finland
+- `FJ`: Fiji
+- `FK`: Falkland Islands
+- `FM`: Micronesia
+- `FO`: Faroe Islands
+- `FR`: France
+- `GA`: Gabon
+- `GB`: United Kingdom
+- `GD`: Grenada
+- `GE`: Georgia
+- `GF`: French Guiana
+- `GG`: Guernsey
+- `GH`: Ghana
+- `GI`: Gibraltar
+- `GL`: Greenland
+- `GM`: Gambia
+- `GN`: Guinea
+- `GP`: Guadeloupe
+- `GQ`: Equatorial Guinea
+- `GR`: Greece
+- `GS`: South Georgia and the South Sandwich Islands
+- `GT`: Guatemala
+- `GU`: Guam
+- `GW`: Guinea-Bissau
+- `GY`: Guyana
+- `HK`: Hong Kong
+- `HM`: Heard Island and McDonald Islands
+- `HN`: Honduras
+- `HR`: Croatia
+- `HT`: Haiti
+- `HU`: Hungary
+- `ID`: Indonesia
+- `IE`: Ireland
+- `IL`: Israel
+- `IM`: Isle of Man
+- `IN`: India
+- `IO`: British Indian Ocean Territory
+- `IQ`: Iraq
+- `IR`: Iran
+- `IS`: Iceland
+- `IT`: Italy
+- `JE`: Jersey
+- `JM`: Jamaica
+- `JO`: Jordan
+- `JP`: Japan
+- `KE`: Kenya
+- `KG`: Kyrgyzstan
+- `KH`: Cambodia
+- `KI`: Kiribati
+- `KM`: Comoros
+- `KN`: Saint Kitts and Nevis
+- `KP`: North Korea
+- `KR`: South Korea
+- `KW`: Kuwait
+- `KY`: Cayman Islands
+- `KZ`: Kazakhstan
+- `LA`: Laos
+- `LB`: Lebanon
+- `LC`: Saint Lucia
+- `LI`: Liechtenstein
+- `LK`: Sri Lanka
+- `LR`: Liberia
+- `LS`: Lesotho
+- `LT`: Lithuania
+- `LU`: Luxembourg
+- `LV`: Latvia
+- `LY`: Libya
+- `MA`: Morocco
+- `MC`: Monaco
+- `MD`: Moldova
+- `ME`: Montenegro
+- `MF`: Saint Martin
+- `MG`: Madagascar
+- `MH`: Marshall Islands
+- `MK`: Macedonia
+- `ML`: Mali
+- `MM`: Myanmar [Burma]
+- `MN`: Mongolia
+- `MO`: Macao
+- `MP`: Northern Mariana Islands
+- `MQ`: Martinique
+- `MR`: Mauritania
+- `MS`: Montserrat
+- `MT`: Malta
+- `MU`: Mauritius
+- `MV`: Maldives
+- `MW`: Malawi
+- `MX`: Mexico
+- `MY`: Malaysia
+- `MZ`: Mozambique
+- `NA`: Namibia
+- `NC`: New Caledonia
+- `NE`: Niger
+- `NF`: Norfolk Island
+- `NG`: Nigeria
+- `NI`: Nicaragua
+- `NL`: Netherlands
+- `NO`: Norway
+- `NP`: Nepal
+- `NR`: Nauru
+- `NU`: Niue
+- `NZ`: New Zealand
+- `OM`: Oman
+- `PA`: Panama
+- `PE`: Peru
+- `PF`: French Polynesia
+- `PG`: Papua New Guinea
+- `PH`: Philippines
+- `PK`: Pakistan
+- `PL`: Poland
+- `PM`: Saint Pierre and Miquelon
+- `PN`: Pitcairn Islands
+- `PR`: Puerto Rico
+- `PS`: Palestine
+- `PT`: Portugal
+- `PW`: Palau
+- `PY`: Paraguay
+- `QA`: Qatar
+- `RE`: Réunion
+- `RO`: Romania
+- `RS`: Serbia
+- `RU`: Russia
+- `RW`: Rwanda
+- `SA`: Saudi Arabia
+- `SB`: Solomon Islands
+- `SC`: Seychelles
+- `SD`: Sudan
+- `SE`: Sweden
+- `SG`: Singapore
+- `SH`: Saint Helena
+- `SI`: Slovenia
+- `SJ`: Svalbard and Jan Mayen
+- `SK`: Slovakia
+- `SL`: Sierra Leone
+- `SM`: San Marino
+- `SN`: Senegal
+- `SO`: Somalia
+- `SR`: Suriname
+- `SS`: South Sudan
+- `ST`: São Tomé and Príncipe
+- `SV`: El Salvador
+- `SX`: Sint Maarten
+- `SY`: Syria
+- `SZ`: Swaziland
+- `TC`: Turks and Caicos Islands
+- `TD`: Chad
+- `TF`: French Southern Territories
+- `TG`: Togo
+- `TH`: Thailand
+- `TJ`: Tajikistan
+- `TK`: Tokelau
+- `TL`: East Timor
+- `TM`: Turkmenistan
+- `TN`: Tunisia
+- `TO`: Tonga
+- `TR`: Turkey
+- `TT`: Trinidad and Tobago
+- `TV`: Tuvalu
+- `TW`: Taiwan
+- `TZ`: Tanzania
+- `UA`: Ukraine
+- `UG`: Uganda
+- `UM`: U.S. Minor Outlying Islands
+- `US`: United States
+- `UY`: Uruguay
+- `UZ`: Uzbekistan
+- `VA`: Vatican City
+- `VC`: Saint Vincent and the Grenadines
+- `VE`: Venezuela
+- `VG`: British Virgin Islands
+- `VI`: U.S. Virgin Islands
+- `VN`: Vietnam
+- `VU`: Vanuatu
+- `WF`: Wallis and Futuna
+- `WS`: Samoa
+- `XK`: Kosovo
+- `YE`: Yemen
+- `YT`: Mayotte
+- `ZA`: South Africa
+- `ZM`: Zambia
+- `ZW`: Zimbabwe
+
+All data was extrated from [GeoNames][] which is licensed under a
+[Creative Commons Attribution 3.0 License][].
+
+***
+See also:
+
+  * [CountryCode](CountryCode.md)
+  * [Tld](Tld.md)
+
+
+[Creative Commons Attribution 3.0 License]: http://creativecommons.org/licenses/by/3.0 "Creative Commons Attribution 3.0 License"
+[GeoNames]: http://www.geonames.org "GetNames"
+[ISO 3166-1 alpha-2]: http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 "ISO 3166-1 alpha-2"
+[ISO 3166-2]: http://en.wikipedia.org/wiki/ISO_3166-2 "ISO 3166-2"
diff --git a/vendor/respect/validation/docs/rules/SymbolicLink.md b/vendor/respect/validation/docs/rules/SymbolicLink.md
new file mode 100644
index 0000000000000000000000000000000000000000..bc9019657bc68072e439020da43c0169169eab43
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/SymbolicLink.md
@@ -0,0 +1,23 @@
+# SymbolicLink
+
+- `v::symbolicLink()`
+
+Validates if the given data is a path of a valid symbolic link.
+
+```php
+v::symbolicLink()->validate('/path/of/valid/symbolic/link'); // true
+```
+
+***
+See also:
+
+  * [Directory](Directory.md)
+  * [Executable](Executable.md)
+  * [Exists](Exists.md)
+  * [Extension](Extension.md)
+  * [File](File.md)
+  * [Mimetype](Mimetype.md)
+  * [Readable](Readable.md)
+  * [Size](Size.md)
+  * [Uploaded](Uploaded.md)
+  * [Writable](Writable.md)
diff --git a/vendor/respect/validation/docs/rules/Tld.md b/vendor/respect/validation/docs/rules/Tld.md
new file mode 100644
index 0000000000000000000000000000000000000000..db05452a196958e19b794707740c69487c99d5a2
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Tld.md
@@ -0,0 +1,17 @@
+# Tld
+
+- `v::tld()`
+
+Validates a top-level domain
+
+```php
+v::tld()->validate('com'); // true
+v::tld()->validate('ly'); // true
+v::tld()->validate('org'); // true
+```
+
+***
+See also:
+
+ * [Domain](Domain.md)
+ * [CountryCode](CountryCode.md)
diff --git a/vendor/respect/validation/docs/rules/TrueVal.md b/vendor/respect/validation/docs/rules/TrueVal.md
new file mode 100644
index 0000000000000000000000000000000000000000..59869d200419e5db273de2f4fa7c6bb30847dbd7
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/TrueVal.md
@@ -0,0 +1,21 @@
+# TrueVal
+
+- `v::trueVal()`
+
+Validates if a value is considered as `true`.
+
+```php
+v::trueVal()->validate(true); // true
+v::trueVal()->validate(1); // true
+v::trueVal()->validate('1'); // true
+v::trueVal()->validate('true'); // true
+v::trueVal()->validate('on'); // true
+v::trueVal()->validate('yes'); // true
+v::trueVal()->validate('0.5'); // false
+v::trueVal()->validate('2'); // false
+```
+
+***
+See also:
+
+  * [FalseVal](FalseVal.md)
diff --git a/vendor/respect/validation/docs/rules/Type.md b/vendor/respect/validation/docs/rules/Type.md
new file mode 100644
index 0000000000000000000000000000000000000000..50f2abb9f9a75a2e1fa087a69b38274ad45d8631
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Type.md
@@ -0,0 +1,31 @@
+# Type
+
+- `v::type(string $type)`
+
+Validates the type of input.
+
+```php
+v::type('bool')->validate(true); // true
+v::type('callable')->validate(function (){}); // true
+v::type('object')->validate(new stdClass()); // true
+```
+
+***
+See also:
+
+  * [ArrayVal](ArrayVal.md)
+  * [BoolType](BoolType.md)
+  * [CallableType](CallableType.md)
+  * [Finite](Finite.md)
+  * [FloatType](FloatType.md)
+  * [FloatVal](FloatVal.md)
+  * [Infinite](Infinite.md)
+  * [Instance](Instance.md)
+  * [IntType](IntType.md)
+  * [IntVal](IntVal.md)
+  * [NullType](NullType.md)
+  * [ObjectType](ObjectType.md)
+  * [ResourceType](ResourceType.md)
+  * [ScalarVal](ScalarVal.md)
+  * [StringType](StringType.md)
+  * [Type](Type.md)
diff --git a/vendor/respect/validation/docs/rules/Uploaded.md b/vendor/respect/validation/docs/rules/Uploaded.md
new file mode 100644
index 0000000000000000000000000000000000000000..774f9bb8216835034c4fa9c02263dfe2c9f898f5
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Uploaded.md
@@ -0,0 +1,23 @@
+# Uploaded
+
+- `v::uploaded()`
+
+Validates if the given data is a file that was uploaded via HTTP POST.
+
+```php
+v::uploaded()->validate('/path/of/an/uploaded/file'); // true
+```
+
+***
+See also:
+
+  * [Directory](Directory.md)
+  * [Executable](Executable.md)
+  * [Exists](Exists.md)
+  * [Extension](Extension.md)
+  * [File](File.md)
+  * [Mimetype](Mimetype.md)
+  * [Readable](Readable.md)
+  * [Size](Size.md)
+  * [SymbolicLink](SymbolicLink.md)
+  * [Writable](Writable.md)
diff --git a/vendor/respect/validation/docs/rules/Uppercase.md b/vendor/respect/validation/docs/rules/Uppercase.md
new file mode 100644
index 0000000000000000000000000000000000000000..1dff7acf584a5ee43724526f6ff729879521ef2e
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Uppercase.md
@@ -0,0 +1,14 @@
+# Uppercase
+
+- `v::uppercase()`
+
+Validates if string characters are uppercase in the input:
+
+```php
+v::stringType()->uppercase()->validate('W3C'); // true
+```
+
+***
+See also:
+
+  * [Lowercase](Lowercase.md)
diff --git a/vendor/respect/validation/docs/rules/Url.md b/vendor/respect/validation/docs/rules/Url.md
new file mode 100644
index 0000000000000000000000000000000000000000..5638cf55f4b364edceea324d80904c3f87ce3178
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Url.md
@@ -0,0 +1,24 @@
+# Url
+
+- `v::url()`
+
+Validates if input is an URL:
+
+```php
+v::url()->validate('http://example.com'); // true
+v::url()->validate('https://www.youtube.com/watch?v=6FOUqQt3Kg0'); // true
+v::url()->validate('ldap://[::1]'); // true
+v::url()->validate('mailto:john.doe@example.com'); // true
+v::url()->validate('news:new.example.com'); // true
+```
+
+This rule uses [FilterVar](FilterVar.md)
+
+***
+See also:
+
+  * [Domain](Domain.md)
+  * [Email](Email.md)
+  * [FilterVar](FilterVar.md)
+  * [Phone](Phone.md)
+  * [VideoUrl](VideoUrl.md)
diff --git a/vendor/respect/validation/docs/rules/Version.md b/vendor/respect/validation/docs/rules/Version.md
new file mode 100644
index 0000000000000000000000000000000000000000..aad3ce8a3d02bc333d3bff0c11a9b359a67c66c7
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Version.md
@@ -0,0 +1,16 @@
+# Version
+
+- `v::version()`
+
+Validates version numbers using Semantic Versioning.
+
+```php
+v::version()->validate('1.0.0');
+```
+
+***
+See also:
+
+  * [Equals](Equals.md)
+  * [Regex](Regex.md)
+  * [Roman](Roman.md)
diff --git a/vendor/respect/validation/docs/rules/VideoUrl.md b/vendor/respect/validation/docs/rules/VideoUrl.md
new file mode 100644
index 0000000000000000000000000000000000000000..1ef962793bdc6082f57ad70d42f059a06095ca5a
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/VideoUrl.md
@@ -0,0 +1,35 @@
+# VideoUrl
+
+- `v::videoUrl()`
+- `v::videoUrl(string $service)`
+
+Validates if the input is a video URL value:
+
+```php
+v::videoUrl()->validate('https://player.vimeo.com/video/71787467'); // true
+v::videoUrl()->validate('https://vimeo.com/71787467'); // true
+v::videoUrl()->validate('https://www.youtube.com/embed/netHLn9TScY'); // true
+v::videoUrl()->validate('https://www.youtube.com/watch?v=netHLn9TScY'); // true
+v::videoUrl()->validate('https://youtu.be/netHLn9TScY'); // true
+
+v::videoUrl('youtube')->validate('https://www.youtube.com/watch?v=netHLn9TScY'); // true
+v::videoUrl('vimeo')->validate('https://vimeo.com/71787467'); // true
+
+v::videoUrl()->validate('https://youtube.com'); // false
+v::videoUrl('youtube')->validate('https://vimeo.com/71787467'); // false
+```
+
+The services accepted are:
+
+- YouTube
+- Vimeo
+
+The `$service` value is not case-sensitive.
+
+Message template for this validator includes `{{service}}`.
+
+
+***
+See also:
+
+  * [Url](Url.md)
diff --git a/vendor/respect/validation/docs/rules/Vowel.md b/vendor/respect/validation/docs/rules/Vowel.md
new file mode 100644
index 0000000000000000000000000000000000000000..98d806f5d90b39658d61d9e69dec3d86b5330e2f
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Vowel.md
@@ -0,0 +1,17 @@
+# Vowel
+
+- `v::vowel()`
+
+Similar to `v::alnum()`. Validates strings that contains only vowels:
+
+```php
+v::vowel()->validate('aei'); // true
+```
+
+***
+See also:
+
+  * [Alnum](Alnum.md)
+  * [Digit](Digit.md)
+  * [Alpha](Alpha.md)
+  * [Consonant](Consonant.md)
diff --git a/vendor/respect/validation/docs/rules/When.md b/vendor/respect/validation/docs/rules/When.md
new file mode 100644
index 0000000000000000000000000000000000000000..6254cb34d991733bd29c9b2281bad838f362a24e
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/When.md
@@ -0,0 +1,24 @@
+# When
+
+- `v::when(v $if, v $then, v $else)`
+- `v::when(v $if, v $then)`
+
+A ternary validator that accepts three parameters.
+
+When the `$if` validates, returns validation for `$then`.
+When the `$if` doesn't validate, returns validation for `$else`, if defined.
+
+```php
+v::when(v::intVal(), v::positive(), v::notEmpty())->validate($input);
+```
+
+In the sample above, if `$input` is an integer, then it must be positive.
+If `$input` is not an integer, then it must not me empty.
+When `$else` is not defined use [AlwaysInvalid](AlwaysInvalid.md)
+
+***
+See also:
+
+  * [AllOf](AllOf.md)
+  * [OneOf](OneOf.md)
+  * [NoneOf](NoneOf.md)
diff --git a/vendor/respect/validation/docs/rules/Writable.md b/vendor/respect/validation/docs/rules/Writable.md
new file mode 100644
index 0000000000000000000000000000000000000000..b7aa5e436534c6fee4bdf42b5fdafefc7494b08f
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Writable.md
@@ -0,0 +1,23 @@
+# Writable
+
+- `v::writable()`
+
+Validates if the given input is writable file.
+
+```php
+v::writable()->validate('/path/of/a/writable/file'); // true
+```
+
+***
+See also:
+
+  * [Directory](Directory.md)
+  * [Executable](Executable.md)
+  * [Exists](Exists.md)
+  * [Extension](Extension.md)
+  * [File](File.md)
+  * [Mimetype](Mimetype.md)
+  * [Readable](Readable.md)
+  * [Size](Size.md)
+  * [SymbolicLink](SymbolicLink.md)
+  * [Uploaded](Uploaded.md)
diff --git a/vendor/respect/validation/docs/rules/Xdigit.md b/vendor/respect/validation/docs/rules/Xdigit.md
new file mode 100644
index 0000000000000000000000000000000000000000..9e79d0fdfc6b52ac263f8748ce5dc03c778c9c81
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Xdigit.md
@@ -0,0 +1,22 @@
+# Xdigit
+
+- `v::xdigit()`
+
+Accepts an hexadecimal number:
+
+```php
+v::xdigit()->validate('abc123'); // true
+```
+
+Notice, however, that it doesn't accept strings starting with 0x:
+
+```php
+v::xdigit()->validate('0x1f'); // false
+```
+
+***
+See also:
+
+  * [Digit](Digit.md)
+  * [Alnum](Alnum.md)
+  * [HexRgbColor](HexRgbColor.md)
diff --git a/vendor/respect/validation/docs/rules/Yes.md b/vendor/respect/validation/docs/rules/Yes.md
new file mode 100644
index 0000000000000000000000000000000000000000..9fef6d8f4d6b0e02e6b9dab619c39451b9f83db6
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Yes.md
@@ -0,0 +1,23 @@
+# Yes
+
+- `v::yes()`
+- `v::yes(boolean $locale)`
+
+Validates if value is considered as "Yes".
+
+```php
+v::yes()->validate('Y'); // true
+v::yes()->validate('Yea'); // true
+v::yes()->validate('Yeah'); // true
+v::yes()->validate('Yep'); // true
+v::yes()->validate('Yes'); // true
+```
+
+This rule is case insensitive.
+
+If `$locale` is TRUE, uses the value of [nl_langinfo()](http://php.net/nl_langinfo) with `YESEXPR` constant.
+
+***
+See also:
+
+  * [No](No.md)
diff --git a/vendor/respect/validation/docs/rules/Zend.md b/vendor/respect/validation/docs/rules/Zend.md
new file mode 100644
index 0000000000000000000000000000000000000000..8ec0ae8ed7ff8daf197e3ef1b5e42ed594ca2b94
--- /dev/null
+++ b/vendor/respect/validation/docs/rules/Zend.md
@@ -0,0 +1,17 @@
+# Zend
+
+- `v::zend(mixed $validator)`
+
+Use Zend validators inside Respect\Validation flow. Messages
+are preserved.
+
+```php
+v::zend('Hostname')->validate('google.com');
+```
+
+You must add `"zendframework/zend-validator": "~2.3"` to your `require` property on composer.json file.
+
+***
+See also:
+
+  * [Sf](Sf.md)
diff --git a/vendor/setasign/fpdi/src/FpdfTplTrait.php b/vendor/setasign/fpdi/src/FpdfTplTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..11796b1f5ccff76673892c4f65a7aea085d91e50
--- /dev/null
+++ b/vendor/setasign/fpdi/src/FpdfTplTrait.php
@@ -0,0 +1,466 @@
+<?php
+/**
+ * This file is part of FPDI
+ *
+ * @package   setasign\Fpdi
+ * @copyright Copyright (c) 2018 Setasign - Jan Slabon (https://www.setasign.com)
+ * @license   http://opensource.org/licenses/mit-license The MIT License
+  */
+
+namespace setasign\Fpdi;
+
+/**
+ * Trait FpdfTplTrait
+ *
+ * This class adds a templating feature to tFPDF.
+ *
+ * @package setasign\Fpdi
+ */
+trait FpdfTplTrait
+{
+    /**
+     * Data of all created templates.
+     *
+     * @var array
+     */
+    protected $templates = [];
+
+    /**
+     * The template id for the currently created template.
+     *
+     * @var null|int
+     */
+    protected $currentTemplateId;
+
+    /**
+     * A counter for template ids.
+     *
+     * @var int
+     */
+    protected $templateId = 0;
+
+    /**
+     * Set the page format of the current page.
+     *
+     * @param array $size An array with two values defining the size.
+     * @param string $orientation "L" for landscape, "P" for portrait.
+     * @throws \BadMethodCallException
+     */
+    public function setPageFormat($size, $orientation)
+    {
+        if ($this->currentTemplateId !== null) {
+            throw new \BadMethodCallException('The page format cannot be changed when writing to a template.');
+        }
+
+        if (!\in_array($orientation, ['P', 'L'], true)) {
+            throw new \InvalidArgumentException(\sprintf(
+                'Invalid page orientation "%s"! Only "P" and "L" are allowed!',
+                $orientation
+            ));
+        }
+
+        $size = $this->_getpagesize($size);
+
+        if ($orientation != $this->CurOrientation
+            || $size[0] != $this->CurPageSize[0]
+            || $size[1] != $this->CurPageSize[1]
+        ) {
+            // New size or orientation
+            if ($orientation === 'P') {
+                $this->w = $size[0];
+                $this->h = $size[1];
+            } else {
+                $this->w = $size[1];
+                $this->h = $size[0];
+            }
+            $this->wPt = $this->w * $this->k;
+            $this->hPt = $this->h * $this->k;
+            $this->PageBreakTrigger = $this->h - $this->bMargin;
+            $this->CurOrientation = $orientation;
+            $this->CurPageSize = $size;
+
+            $this->PageInfo[$this->page]['size'] = array($this->wPt, $this->hPt);
+        }
+    }
+
+    /**
+     * Draws a template onto the page or another template.
+     *
+     * Omit one of the size parameters (width, height) to calculate the other one automatically in view to the aspect
+     * ratio.
+     *
+     * @param mixed $tpl The template id
+     * @param array|float|int $x The abscissa of upper-left corner. Alternatively you could use an assoc array
+     *                           with the keys "x", "y", "width", "height", "adjustPageSize".
+     * @param float|int $y The ordinate of upper-left corner.
+     * @param float|int|null $width The width.
+     * @param float|int|null $height The height.
+     * @param bool $adjustPageSize
+     * @return array The size
+     * @see FpdfTplTrait::getTemplateSize()
+     */
+    public function useTemplate($tpl, $x = 0, $y = 0, $width = null, $height = null, $adjustPageSize = false)
+    {
+        if (!isset($this->templates[$tpl])) {
+            throw new \InvalidArgumentException('Template does not exist!');
+        }
+
+        if (\is_array($x)) {
+            unset($x['tpl']);
+            \extract($x, EXTR_IF_EXISTS);
+            /** @noinspection NotOptimalIfConditionsInspection */
+            /** @noinspection CallableParameterUseCaseInTypeContextInspection */
+            if (\is_array($x)) {
+                $x = 0;
+            }
+        }
+
+        $template = $this->templates[$tpl];
+
+        $originalSize = $this->getTemplateSize($tpl);
+        $newSize = $this->getTemplateSize($tpl, $width, $height);
+        if ($adjustPageSize) {
+            $this->setPageFormat($newSize, $newSize['orientation']);
+        }
+
+        $this->_out(
+        // reset standard values, translate and scale
+            \sprintf(
+                'q 0 J 1 w 0 j 0 G 0 g %.4F 0 0 %.4F %.4F %.4F cm /%s Do Q',
+                ($newSize['width'] / $originalSize['width']),
+                ($newSize['height'] / $originalSize['height']),
+                $x * $this->k,
+                ($this->h - $y - $newSize['height']) * $this->k,
+                $template['id']
+            )
+        );
+
+        return $newSize;
+    }
+
+    /**
+     * Get the size of a template.
+     *
+     * Omit one of the size parameters (width, height) to calculate the other one automatically in view to the aspect
+     * ratio.
+     *
+     * @param mixed $tpl The template id
+     * @param float|int|null $width The width.
+     * @param float|int|null $height The height.
+     * @return array|bool An array with following keys: width, height, 0 (=width), 1 (=height), orientation (L or P)
+     */
+    public function getTemplateSize($tpl, $width = null, $height = null)
+    {
+        if (!isset($this->templates[$tpl])) {
+            return false;
+        }
+
+        if ($width === null && $height === null) {
+            $width = $this->templates[$tpl]['width'];
+            $height = $this->templates[$tpl]['height'];
+        } elseif ($width === null) {
+            $width = $height * $this->templates[$tpl]['width'] / $this->templates[$tpl]['height'];
+        }
+
+        if ($height === null) {
+            $height = $width * $this->templates[$tpl]['height'] / $this->templates[$tpl]['width'];
+        }
+
+        if ($height <= 0. || $width <= 0.) {
+            throw new \InvalidArgumentException('Width or height parameter needs to be larger than zero.');
+        }
+
+        return [
+            'width' => $width,
+            'height' => $height,
+            0 => $width,
+            1 => $height,
+            'orientation' => $width > $height ? 'L' : 'P'
+        ];
+    }
+
+    /**
+     * Begins a new template.
+     *
+     * @param float|int|null $width The width of the template. If null, the current page width is used.
+     * @param float|int|null $height The height of the template. If null, the current page height is used.
+     * @param bool $groupXObject Define the form XObject as a group XObject to support transparency (if used).
+     * @return int A template identifier.
+     */
+    public function beginTemplate($width = null, $height = null, $groupXObject = false)
+    {
+        if ($width === null) {
+            $width = $this->w;
+        }
+
+        if ($height === null) {
+            $height = $this->h;
+        }
+
+        $templateId = $this->getNextTemplateId();
+
+        // initiate buffer with current state of FPDF
+        $buffer = "2 J\n"
+            . \sprintf('%.2F w', $this->LineWidth * $this->k) . "\n";
+
+        if ($this->FontFamily) {
+            $buffer .= \sprintf("BT /F%d %.2F Tf ET\n", $this->CurrentFont['i'], $this->FontSizePt);
+        }
+
+        if ($this->DrawColor !== '0 G') {
+            $buffer .= $this->DrawColor . "\n";
+        }
+        if ($this->FillColor !== '0 g') {
+            $buffer .= $this->FillColor . "\n";
+        }
+
+        if ($groupXObject && \version_compare('1.4', $this->PDFVersion, '>')) {
+            $this->PDFVersion = '1.4';
+        }
+
+        $this->templates[$templateId] = [
+            'objectNumber' => null,
+            'id' => 'TPL' . $templateId,
+            'buffer' => $buffer,
+            'width' => $width,
+            'height' => $height,
+            'groupXObject' => $groupXObject,
+            'state' => [
+                'x' => $this->x,
+                'y' => $this->y,
+                'AutoPageBreak' => $this->AutoPageBreak,
+                'bMargin' => $this->bMargin,
+                'tMargin' => $this->tMargin,
+                'lMargin' => $this->lMargin,
+                'rMargin' => $this->rMargin,
+                'h' => $this->h,
+                'w' => $this->w,
+                'FontFamily' => $this->FontFamily,
+                'FontStyle' => $this->FontStyle,
+                'FontSizePt' => $this->FontSizePt,
+                'FontSize' => $this->FontSize,
+                'underline' => $this->underline,
+                'TextColor' => $this->TextColor,
+                'DrawColor' => $this->DrawColor,
+                'FillColor' => $this->FillColor,
+                'ColorFlag' => $this->ColorFlag
+            ]
+        ];
+
+        $this->SetAutoPageBreak(false);
+        $this->currentTemplateId = $templateId;
+
+        $this->h = $height;
+        $this->w = $width;
+
+        $this->SetXY($this->lMargin, $this->tMargin);
+        $this->SetRightMargin($this->w - $width + $this->rMargin);
+
+        return $templateId;
+    }
+
+    /**
+     * Ends a template.
+     *
+     * @return bool|int|null A template identifier.
+     */
+    public function endTemplate()
+    {
+        if (null === $this->currentTemplateId) {
+            return false;
+        }
+
+        $templateId = $this->currentTemplateId;
+        $template = $this->templates[$templateId];
+
+        $state = $template['state'];
+        $this->SetXY($state['x'], $state['y']);
+        $this->tMargin = $state['tMargin'];
+        $this->lMargin = $state['lMargin'];
+        $this->rMargin = $state['rMargin'];
+        $this->h = $state['h'];
+        $this->w = $state['w'];
+        $this->SetAutoPageBreak($state['AutoPageBreak'], $state['bMargin']);
+
+        $this->FontFamily = $state['FontFamily'];
+        $this->FontStyle = $state['FontStyle'];
+        $this->FontSizePt = $state['FontSizePt'];
+        $this->FontSize = $state['FontSize'];
+
+        $this->TextColor = $state['TextColor'];
+        $this->DrawColor = $state['DrawColor'];
+        $this->FillColor = $state['FillColor'];
+        $this->ColorFlag = $state['ColorFlag'];
+
+        $this->underline = $state['underline'];
+
+        $fontKey = $this->FontFamily . $this->FontStyle;
+        if ($fontKey) {
+            $this->CurrentFont =& $this->fonts[$fontKey];
+        } else {
+            unset($this->CurrentFont);
+        }
+
+        $this->currentTemplateId = null;
+
+        return $templateId;
+    }
+
+    /**
+     * Get the next template id.
+     *
+     * @return int
+     */
+    protected function getNextTemplateId()
+    {
+        return $this->templateId++;
+    }
+
+    /* overwritten FPDF methods: */
+
+    /**
+     * @inheritdoc
+     */
+    public function AddPage($orientation = '', $size = '', $rotation = 0)
+    {
+        if ($this->currentTemplateId !== null) {
+            throw new \BadMethodCallException('Pages cannot be added when writing to a template.');
+        }
+        parent::AddPage($orientation, $size, $rotation);
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function Link($x, $y, $w, $h, $link)
+    {
+        if ($this->currentTemplateId !== null) {
+            throw new \BadMethodCallException('Links cannot be set when writing to a template.');
+        }
+        parent::Link($x, $y, $w, $h, $link);
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function SetLink($link, $y = 0, $page = -1)
+    {
+        if ($this->currentTemplateId !== null) {
+            throw new \BadMethodCallException('Links cannot be set when writing to a template.');
+        }
+        return parent::SetLink($link, $y, $page);
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function SetDrawColor($r, $g = null, $b = null)
+    {
+        parent::SetDrawColor($r, $g, $b);
+        if ($this->page === 0 && $this->currentTemplateId !== null) {
+            $this->_out($this->DrawColor);
+        }
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function SetFillColor($r, $g = null, $b = null)
+    {
+        parent::SetFillColor($r, $g, $b);
+        if ($this->page === 0 && $this->currentTemplateId !== null) {
+            $this->_out($this->FillColor);
+        }
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function SetLineWidth($width)
+    {
+        parent::SetLineWidth($width);
+        if ($this->page === 0 && $this->currentTemplateId !== null) {
+            $this->_out(\sprintf('%.2F w', $width * $this->k));
+        }
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function SetFont($family, $style = '', $size = 0)
+    {
+        parent::SetFont($family, $style, $size);
+        if ($this->page === 0 && $this->currentTemplateId !== null) {
+            $this->_out(\sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
+        }
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function SetFontSize($size)
+    {
+        parent::SetFontSize($size);
+        if ($this->page === 0 && $this->currentTemplateId !== null) {
+            $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
+        }
+    }
+
+    /**
+     * @inheritdoc
+     */
+    protected function _putimages()
+    {
+        parent::_putimages();
+
+        foreach ($this->templates as $key => $template) {
+            $this->_newobj();
+            $this->templates[$key]['objectNumber'] = $this->n;
+
+            $this->_put('<</Type /XObject /Subtype /Form /FormType 1');
+            $this->_put(\sprintf('/BBox[0 0 %.2F %.2F]', $template['width'] * $this->k, $template['height'] * $this->k));
+            $this->_put('/Resources 2 0 R'); // default resources dictionary of FPDF
+
+            if ($this->compress) {
+                $buffer = \gzcompress($template['buffer']);
+                $this->_put('/Filter/FlateDecode');
+            } else {
+                $buffer = $template['buffer'];
+            }
+
+            $this->_put('/Length ' . \strlen($buffer));
+
+            if ($template['groupXObject']) {
+                $this->_put('/Group <</Type/Group/S/Transparency>>');
+            }
+
+            $this->_put('>>');
+            $this->_putstream($buffer);
+            $this->_put('endobj');
+        }
+    }
+
+    /**
+     * @inheritdoc
+     */
+    protected function _putxobjectdict()
+    {
+        foreach ($this->templates as $key => $template) {
+            $this->_put('/' . $template['id'] . ' ' . $template['objectNumber'] . ' 0 R');
+        }
+
+        parent::_putxobjectdict();
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function _out($s)
+    {
+        if ($this->currentTemplateId !== null) {
+            $this->templates[$this->currentTemplateId]['buffer'] .= $s . "\n";
+        } else {
+            parent::_out($s);
+        }
+    }
+}
\ No newline at end of file
diff --git a/vendor/setasign/fpdi/src/Tcpdf/Fpdi.php b/vendor/setasign/fpdi/src/Tcpdf/Fpdi.php
new file mode 100644
index 0000000000000000000000000000000000000000..27e209b3080265853f24cf0161c001fb20bafe0a
--- /dev/null
+++ b/vendor/setasign/fpdi/src/Tcpdf/Fpdi.php
@@ -0,0 +1,260 @@
+<?php
+
+namespace setasign\Fpdi\Tcpdf;
+
+use setasign\Fpdi\FpdiTrait;
+use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException;
+use setasign\Fpdi\PdfParser\Filter\AsciiHex;
+use setasign\Fpdi\PdfParser\PdfParserException;
+use setasign\Fpdi\PdfParser\Type\PdfHexString;
+use setasign\Fpdi\PdfParser\Type\PdfIndirectObject;
+use setasign\Fpdi\PdfParser\Type\PdfNull;
+use setasign\Fpdi\PdfParser\Type\PdfNumeric;
+use setasign\Fpdi\PdfParser\Type\PdfStream;
+use setasign\Fpdi\PdfParser\Type\PdfString;
+use setasign\Fpdi\PdfParser\Type\PdfType;
+use setasign\Fpdi\PdfParser\Type\PdfTypeException;
+
+/**
+ * Class Fpdi
+ *
+ * This class let you import pages of existing PDF documents into a reusable structure for TCPDF.
+ *
+ * @package setasign\Fpdi
+ */
+class Fpdi extends \TCPDF
+{
+    use FpdiTrait {
+        writePdfType as fpdiWritePdfType;
+        useImportedPage as fpdiUseImportedPage;
+    }
+
+    /**
+     * FPDI version
+     *
+     * @string
+     */
+    const VERSION = '2.1.0';
+
+    /**
+     * A counter for template ids.
+     *
+     * @var int
+     */
+    protected $templateId = 0;
+
+    /**
+     * The currently used object number.
+     *
+     * @var int
+     */
+    protected $currentObjectNumber;
+
+    /**
+     * Get the next template id.
+     *
+     * @return int
+     */
+    protected function getNextTemplateId()
+    {
+        return $this->templateId++;
+    }
+
+    /**
+     * Draws an imported page onto the page or another template.
+     *
+     * Omit one of the size parameters (width, height) to calculate the other one automatically in view to the aspect
+     * ratio.
+     *
+     * @param mixed $tpl The template id
+     * @param float|int|array $x The abscissa of upper-left corner. Alternatively you could use an assoc array
+     *                           with the keys "x", "y", "width", "height", "adjustPageSize".
+     * @param float|int $y The ordinate of upper-left corner.
+     * @param float|int|null $width The width.
+     * @param float|int|null $height The height.
+     * @param bool $adjustPageSize
+     * @return array The size
+     * @see FpdiTrait::getTemplateSize()
+     */
+    public function useTemplate($tpl, $x = 0, $y = 0, $width = null, $height = null, $adjustPageSize = false)
+    {
+        return $this->useImportedPage($tpl, $x, $y, $width, $height, $adjustPageSize);
+    }
+
+    /**
+     * Draws an imported page onto the page.
+     *
+     * Omit one of the size parameters (width, height) to calculate the other one automatically in view to the aspect
+     * ratio.
+     *
+     * @param mixed $pageId The page id
+     * @param float|int|array $x The abscissa of upper-left corner. Alternatively you could use an assoc array
+     *                           with the keys "x", "y", "width", "height", "adjustPageSize".
+     * @param float|int $y The ordinate of upper-left corner.
+     * @param float|int|null $width The width.
+     * @param float|int|null $height The height.
+     * @param bool $adjustPageSize
+     * @return array The size.
+     * @see Fpdi::getTemplateSize()
+     */
+    public function useImportedPage($pageId, $x = 0, $y = 0, $width = null, $height = null, $adjustPageSize = false)
+    {
+        $size = $this->fpdiUseImportedPage($pageId, $x, $y, $width, $height, $adjustPageSize);
+        if ($this->inxobj) {
+            $importedPage = $this->importedPages[$pageId];
+            $this->xobjects[$this->xobjid]['importedPages'][$importedPage['id']] = $pageId;
+        }
+
+        return $size;
+    }
+
+    /**
+     * Get the size of an imported page.
+     *
+     * Omit one of the size parameters (width, height) to calculate the other one automatically in view to the aspect
+     * ratio.
+     *
+     * @param mixed $tpl The template id
+     * @param float|int|null $width The width.
+     * @param float|int|null $height The height.
+     * @return array|bool An array with following keys: width, height, 0 (=width), 1 (=height), orientation (L or P)
+     */
+    public function getTemplateSize($tpl, $width = null, $height = null)
+    {
+        return $this->getImportedPageSize($tpl, $width, $height);
+    }
+
+    /**
+     * @inheritdoc
+     */
+    protected function _getxobjectdict()
+    {
+        $out = parent::_getxobjectdict();
+
+        foreach ($this->importedPages as $key => $pageData) {
+            $out .= '/' . $pageData['id'] . ' ' . $pageData['objectNumber'] . ' 0 R ';
+        }
+
+        return $out;
+    }
+
+    /**
+     * @inheritdoc
+     * @throws CrossReferenceException
+     * @throws PdfParserException
+     */
+    protected function _putxobjects()
+    {
+        foreach ($this->importedPages as $key => $pageData) {
+            $this->currentObjectNumber = $this->_newobj();
+            $this->importedPages[$key]['objectNumber'] = $this->currentObjectNumber;
+            $this->currentReaderId = $pageData['readerId'];
+            $this->writePdfType($pageData['stream']);
+            $this->_put('endobj');
+        }
+
+        foreach (\array_keys($this->readers) as $readerId) {
+            $parser = $this->getPdfReader($readerId)->getParser();
+            $this->currentReaderId = $readerId;
+
+            while (($objectNumber = \array_pop($this->objectsToCopy[$readerId])) !== null) {
+                try {
+                    $object = $parser->getIndirectObject($objectNumber);
+
+                } catch (CrossReferenceException $e) {
+                    if ($e->getCode() === CrossReferenceException::OBJECT_NOT_FOUND) {
+                        $object = PdfIndirectObject::create($objectNumber, 0, new PdfNull());
+                    } else {
+                        throw $e;
+                    }
+                }
+
+                $this->writePdfType($object);
+            }
+        }
+
+        // let's prepare resources for imported pages in templates
+        foreach ($this->xobjects as $xObjectId => $data) {
+            if (!isset($data['importedPages'])) {
+                continue;
+            }
+
+            foreach ($data['importedPages'] as $id => $pageKey) {
+                $page = $this->importedPages[$pageKey];
+                $this->xobjects[$xObjectId]['xobjects'][$id] = ['n' => $page['objectNumber']];
+            }
+        }
+
+
+        parent::_putxobjects();
+        $this->currentObjectNumber = null;
+    }
+
+    /**
+     * Append content to the buffer of TCPDF.
+     *
+     * @param string $s
+     * @param bool $newLine
+     */
+    protected function _put($s, $newLine = true)
+    {
+        if ($newLine) {
+            $this->setBuffer($s . "\n");
+        } else {
+            $this->setBuffer($s);
+        }
+    }
+
+    /**
+     * Begin a new object and return the object number.
+     *
+     * @param int|string $objid Object ID (leave empty to get a new ID).
+     * @return int object number
+     */
+    protected function _newobj($objid = '')
+    {
+        $this->_out($this->_getobj($objid));
+        return $this->n;
+    }
+
+    /**
+     * Writes a PdfType object to the resulting buffer.
+     *
+     * @param PdfType $value
+     * @throws PdfTypeException
+     */
+    protected function writePdfType(PdfType $value)
+    {
+        if (!$this->encrypted) {
+            $this->fpdiWritePdfType($value);
+            return;
+        }
+
+        if ($value instanceof PdfString) {
+            $string = PdfString::unescape($value->value);
+            $string = $this->_encrypt_data($this->currentObjectNumber, $string);
+            $value->value = \TCPDF_STATIC::_escape($string);
+
+        } elseif ($value instanceof PdfHexString) {
+            $filter = new AsciiHex();
+            $string = $filter->decode($value->value);
+            $string = $this->_encrypt_data($this->currentObjectNumber, $string);
+            $value->value = $filter->encode($string, true);
+
+        } elseif ($value instanceof PdfStream) {
+            $stream = $value->getStream();
+            $stream = $this->_encrypt_data($this->currentObjectNumber, $stream);
+            $dictionary = $value->value;
+            $dictionary->value['Length'] = PdfNumeric::create(\strlen($stream));
+            $value = PdfStream::create($dictionary, $stream);
+
+        } elseif ($value instanceof PdfIndirectObject) {
+            /**
+             * @var $value PdfIndirectObject
+             */
+            $this->currentObjectNumber = $this->objectMap[$this->currentReaderId][$value->objectNumber];
+        }
+
+        $this->fpdiWritePdfType($value);
+    }
+}
\ No newline at end of file
diff --git a/vendor/setasign/fpdi/src/Tfpdf/FpdfTpl.php b/vendor/setasign/fpdi/src/Tfpdf/FpdfTpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..0b527a783febd3c498f2a144b92b5c4ce265c4e0
--- /dev/null
+++ b/vendor/setasign/fpdi/src/Tfpdf/FpdfTpl.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * This file is part of FPDI
+ *
+ * @package   setasign\Fpdi
+ * @copyright Copyright (c) 2018 Setasign - Jan Slabon (https://www.setasign.com)
+ * @license   http://opensource.org/licenses/mit-license The MIT License
+  */
+
+namespace setasign\Fpdi\Tfpdf;
+
+use setasign\Fpdi\FpdfTplTrait;
+
+/**
+ * Class FpdfTpl
+ *
+ * We need to change some access levels and implement the setPageFormat() method to bring back compatibility to tFPDF.
+ *
+ * @package setasign\Fpdi\Tfpdf
+ */
+class FpdfTpl extends \tFPDF
+{
+    use FpdfTplTrait {
+        _putimages as _protectedPutimages;
+        _putxobjectdict as _protectedPutxobjectdict;
+    }
+
+    /**
+     * Make the method public as in tFPDF.
+     */
+    public function _putimages()
+    {
+        $this->_protectedPutimages();
+    }
+
+    /**
+     * Make the method public as in tFPDF.
+     */
+    public function _putxobjectdict()
+    {
+        $this->_protectedPutxobjectdict();
+    }
+
+    /**
+     * Set the page format of the current page.
+     *
+     * @param array $size An array with two values defining the size.
+     * @param string $orientation "L" for landscape, "P" for portrait.
+     * @throws \BadMethodCallException
+     */
+    public function setPageFormat($size, $orientation)
+    {
+        if ($this->currentTemplateId !== null) {
+            throw new \BadMethodCallException('The page format cannot be changed when writing to a template.');
+        }
+
+        if (!\in_array($orientation, ['P', 'L'], true)) {
+            throw new \InvalidArgumentException(\sprintf(
+                'Invalid page orientation "%s"! Only "P" and "L" are allowed!',
+                $orientation
+            ));
+        }
+
+        $size = $this->_getpagesize($size);
+
+        if ($orientation != $this->CurOrientation
+            || $size[0] != $this->CurPageSize[0]
+            || $size[1] != $this->CurPageSize[1]
+        ) {
+            // New size or orientation
+            if ($orientation === 'P') {
+                $this->w = $size[0];
+                $this->h = $size[1];
+            } else {
+                $this->w = $size[1];
+                $this->h = $size[0];
+            }
+            $this->wPt = $this->w * $this->k;
+            $this->hPt = $this->h * $this->k;
+            $this->PageBreakTrigger = $this->h - $this->bMargin;
+            $this->CurOrientation = $orientation;
+            $this->CurPageSize = $size;
+
+            $this->PageSizes[$this->page] = array($this->wPt, $this->hPt);
+        }
+    }
+
+    /**
+     * @inheritdoc
+     */
+    protected function _put($s, $newLine = true)
+    {
+        if ($newLine) {
+            $this->buffer .= $s . "\n";
+        } else {
+            $this->buffer .= $s;
+        }
+    }
+}
\ No newline at end of file
diff --git a/vendor/setasign/fpdi/src/Tfpdf/Fpdi.php b/vendor/setasign/fpdi/src/Tfpdf/Fpdi.php
new file mode 100644
index 0000000000000000000000000000000000000000..2e4a0fa3515ba6cb70c7a35115c0a3cdbe170eb2
--- /dev/null
+++ b/vendor/setasign/fpdi/src/Tfpdf/Fpdi.php
@@ -0,0 +1,158 @@
+<?php
+/**
+ * This file is part of FPDI
+ *
+ * @package   setasign\Fpdi
+ * @copyright Copyright (c) 2018 Setasign - Jan Slabon (https://www.setasign.com)
+ * @license   http://opensource.org/licenses/mit-license The MIT License
+  */
+
+namespace setasign\Fpdi\Tfpdf;
+
+use setasign\Fpdi\FpdiTrait;
+use setasign\Fpdi\PdfParser\CrossReference\CrossReferenceException;
+use setasign\Fpdi\PdfParser\PdfParserException;
+use setasign\Fpdi\PdfParser\Type\PdfIndirectObject;
+use setasign\Fpdi\PdfParser\Type\PdfNull;
+
+/**
+ * Class Fpdi
+ *
+ * This class let you import pages of existing PDF documents into a reusable structure for tFPDF.
+ *
+ * @package setasign\Fpdi
+ */
+class Fpdi extends FpdfTpl
+{
+    use FpdiTrait;
+
+    /**
+     * FPDI version
+     *
+     * @string
+     */
+    const VERSION = '2.1.0';
+
+    /**
+     * Draws an imported page or a template onto the page or another template.
+     *
+     * Omit one of the size parameters (width, height) to calculate the other one automatically in view to the aspect
+     * ratio.
+     *
+     * @param mixed $tpl The template id
+     * @param float|int|array $x The abscissa of upper-left corner. Alternatively you could use an assoc array
+     *                           with the keys "x", "y", "width", "height", "adjustPageSize".
+     * @param float|int $y The ordinate of upper-left corner.
+     * @param float|int|null $width The width.
+     * @param float|int|null $height The height.
+     * @param bool $adjustPageSize
+     * @return array The size
+     * @see Fpdi::getTemplateSize()
+     */
+    public function useTemplate($tpl, $x = 0, $y = 0, $width = null, $height = null, $adjustPageSize = false)
+    {
+        if (isset($this->importedPages[$tpl])) {
+            $size = $this->useImportedPage($tpl, $x, $y, $width, $height, $adjustPageSize);
+            if ($this->currentTemplateId !== null) {
+                $this->templates[$this->currentTemplateId]['resources']['templates']['importedPages'][$tpl] = $tpl;
+            }
+            return $size;
+        }
+
+        return parent::useTemplate($tpl, $x, $y, $width, $height, $adjustPageSize);
+    }
+
+    /**
+     * Get the size of an imported page or template.
+     *
+     * Omit one of the size parameters (width, height) to calculate the other one automatically in view to the aspect
+     * ratio.
+     *
+     * @param mixed $tpl The template id
+     * @param float|int|null $width The width.
+     * @param float|int|null $height The height.
+     * @return array|bool An array with following keys: width, height, 0 (=width), 1 (=height), orientation (L or P)
+     */
+    public function getTemplateSize($tpl, $width = null, $height = null)
+    {
+        $size = parent::getTemplateSize($tpl, $width, $height);
+        if ($size === false) {
+            return $this->getImportedPageSize($tpl, $width, $height);
+        }
+
+        return $size;
+    }
+
+    /**
+     * @inheritdoc
+     * @throws CrossReferenceException
+     * @throws PdfParserException
+     */
+    public function _putimages()
+    {
+        $this->currentReaderId = null;
+        parent::_putimages();
+
+        foreach ($this->importedPages as $key => $pageData) {
+            $this->_newobj();
+            $this->importedPages[$key]['objectNumber'] = $this->n;
+            $this->currentReaderId = $pageData['readerId'];
+            $this->writePdfType($pageData['stream']);
+            $this->_put('endobj');
+        }
+
+        foreach (\array_keys($this->readers) as $readerId) {
+            $parser = $this->getPdfReader($readerId)->getParser();
+            $this->currentReaderId = $readerId;
+
+            while (($objectNumber = \array_pop($this->objectsToCopy[$readerId])) !== null) {
+                try {
+                    $object = $parser->getIndirectObject($objectNumber);
+
+                } catch (CrossReferenceException $e) {
+                    if ($e->getCode() === CrossReferenceException::OBJECT_NOT_FOUND) {
+                        $object = PdfIndirectObject::create($objectNumber, 0, new PdfNull());
+                    } else {
+                        throw $e;
+                    }
+                }
+
+                $this->writePdfType($object);
+            }
+        }
+
+        $this->currentReaderId = null;
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function _putxobjectdict()
+    {
+        foreach ($this->importedPages as $key => $pageData) {
+            $this->_put('/' . $pageData['id'] . ' ' . $pageData['objectNumber'] . ' 0 R');
+        }
+
+        parent::_putxobjectdict();
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function _newobj($n = null)
+    {
+        // Begin a new object
+        if($n === null)
+            $n = ++$this->n;
+        $this->offsets[$n] = $this->_getoffset();
+        $this->_put($n.' 0 obj');
+    }
+
+    /**
+     * @inheritdoc
+     */
+    protected function _getoffset()
+    {
+        return strlen($this->buffer);
+    }
+}
\ No newline at end of file
diff --git a/vendor/slim/slim/Slim/Http/StatusCode.php b/vendor/slim/slim/Slim/Http/StatusCode.php
new file mode 100644
index 0000000000000000000000000000000000000000..301bdd932f46307050d7621a4b98b9d63cb9fbda
--- /dev/null
+++ b/vendor/slim/slim/Slim/Http/StatusCode.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * Slim Framework (https://slimframework.com)
+ *
+ * @link      https://github.com/slimphp/Slim
+ * @copyright Copyright (c) 2011-2017 Josh Lockhart
+ * @license   https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License)
+ */
+namespace Slim\Http;
+
+class StatusCode
+{
+    const HTTP_CONTINUE = 100;
+    const HTTP_SWITCHING_PROTOCOLS = 101;
+    const HTTP_PROCESSING = 102;
+
+    const HTTP_OK = 200;
+    const HTTP_CREATED = 201;
+    const HTTP_ACCEPTED = 202;
+    const HTTP_NONAUTHORITATIVE_INFORMATION = 203;
+    const HTTP_NO_CONTENT = 204;
+    const HTTP_RESET_CONTENT = 205;
+    const HTTP_PARTIAL_CONTENT = 206;
+    const HTTP_MULTI_STATUS = 207;
+    const HTTP_ALREADY_REPORTED = 208;
+    const HTTP_IM_USED = 226;
+
+    const HTTP_MULTIPLE_CHOICES = 300;
+    const HTTP_MOVED_PERMANENTLY = 301;
+    const HTTP_FOUND = 302;
+    const HTTP_SEE_OTHER = 303;
+    const HTTP_NOT_MODIFIED = 304;
+    const HTTP_USE_PROXY = 305;
+    const HTTP_UNUSED= 306;
+    const HTTP_TEMPORARY_REDIRECT = 307;
+    const HTTP_PERMANENT_REDIRECT = 308;
+
+    const HTTP_BAD_REQUEST = 400;
+    const HTTP_UNAUTHORIZED  = 401;
+    const HTTP_PAYMENT_REQUIRED = 402;
+    const HTTP_FORBIDDEN = 403;
+    const HTTP_NOT_FOUND = 404;
+    const HTTP_METHOD_NOT_ALLOWED = 405;
+    const HTTP_NOT_ACCEPTABLE = 406;
+    const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
+    const HTTP_REQUEST_TIMEOUT = 408;
+    const HTTP_CONFLICT = 409;
+    const HTTP_GONE = 410;
+    const HTTP_LENGTH_REQUIRED = 411;
+    const HTTP_PRECONDITION_FAILED = 412;
+    const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
+    const HTTP_REQUEST_URI_TOO_LONG = 414;
+    const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
+    const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+    const HTTP_EXPECTATION_FAILED = 417;
+    const HTTP_IM_A_TEAPOT = 418;
+    const HTTP_MISDIRECTED_REQUEST = 421;
+    const HTTP_UNPROCESSABLE_ENTITY = 422;
+    const HTTP_LOCKED = 423;
+    const HTTP_FAILED_DEPENDENCY = 424;
+    const HTTP_UPGRADE_REQUIRED = 426;
+    const HTTP_PRECONDITION_REQUIRED = 428;
+    const HTTP_TOO_MANY_REQUESTS = 429;
+    const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431;
+    const HTTP_CONNECTION_CLOSED_WITHOUT_RESPONSE = 444;
+    const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451;
+    const HTTP_CLIENT_CLOSED_REQUEST = 499;
+
+    const HTTP_INTERNAL_SERVER_ERROR = 500;
+    const HTTP_NOT_IMPLEMENTED = 501;
+    const HTTP_BAD_GATEWAY = 502;
+    const HTTP_SERVICE_UNAVAILABLE = 503;
+    const HTTP_GATEWAY_TIMEOUT = 504;
+    const HTTP_VERSION_NOT_SUPPORTED = 505;
+    const HTTP_VARIANT_ALSO_NEGOTIATES = 506;
+    const HTTP_INSUFFICIENT_STORAGE = 507;
+    const HTTP_LOOP_DETECTED = 508;
+    const HTTP_NOT_EXTENDED = 510;
+    const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511;
+    const HTTP_NETWORK_CONNECTION_TIMEOUT_ERROR = 599;
+}
diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php
new file mode 100644
index 0000000000000000000000000000000000000000..2a8f6e73b99301469991b2bb835324b39d96cd60
--- /dev/null
+++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php
@@ -0,0 +1,5 @@
+<?php
+
+// from Case_Ignorable in https://unicode.org/Public/UNIDATA/DerivedCoreProperties.txt
+
+return '/(?<![\x{0027}\x{002E}\x{003A}\x{005E}\x{0060}\x{00A8}\x{00AD}\x{00AF}\x{00B4}\x{00B7}\x{00B8}\x{02B0}-\x{02C1}\x{02C2}-\x{02C5}\x{02C6}-\x{02D1}\x{02D2}-\x{02DF}\x{02E0}-\x{02E4}\x{02E5}-\x{02EB}\x{02EC}\x{02ED}\x{02EE}\x{02EF}-\x{02FF}\x{0300}-\x{036F}\x{0374}\x{0375}\x{037A}\x{0384}-\x{0385}\x{0387}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0559}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{05F4}\x{0600}-\x{0605}\x{0610}-\x{061A}\x{061C}\x{0640}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DD}\x{06DF}-\x{06E4}\x{06E5}-\x{06E6}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{070F}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07F4}-\x{07F5}\x{07FA}\x{07FD}\x{0816}-\x{0819}\x{081A}\x{081B}-\x{0823}\x{0824}\x{0825}-\x{0827}\x{0828}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E2}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0971}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0CBC}\x{0CBF}\x{0CC6}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E46}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EB9}\x{0EBB}-\x{0EBC}\x{0EC6}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{10FC}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17D7}\x{17DD}\x{180B}-\x{180D}\x{180E}\x{1843}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AA7}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1C78}-\x{1C7D}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1D2C}-\x{1D6A}\x{1D78}\x{1D9B}-\x{1DBF}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{1FBD}\x{1FBF}-\x{1FC1}\x{1FCD}-\x{1FCF}\x{1FDD}-\x{1FDF}\x{1FED}-\x{1FEF}\x{1FFD}-\x{1FFE}\x{200B}-\x{200F}\x{2018}\x{2019}\x{2024}\x{2027}\x{202A}-\x{202E}\x{2060}-\x{2064}\x{2066}-\x{206F}\x{2071}\x{207F}\x{2090}-\x{209C}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2C7C}-\x{2C7D}\x{2CEF}-\x{2CF1}\x{2D6F}\x{2D7F}\x{2DE0}-\x{2DFF}\x{2E2F}\x{3005}\x{302A}-\x{302D}\x{3031}-\x{3035}\x{303B}\x{3099}-\x{309A}\x{309B}-\x{309C}\x{309D}-\x{309E}\x{30FC}-\x{30FE}\x{A015}\x{A4F8}-\x{A4FD}\x{A60C}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A67F}\x{A69C}-\x{A69D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A700}-\x{A716}\x{A717}-\x{A71F}\x{A720}-\x{A721}\x{A770}\x{A788}\x{A789}-\x{A78A}\x{A7F8}-\x{A7F9}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}\x{A9CF}\x{A9E5}\x{A9E6}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA70}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AADD}\x{AAEC}-\x{AAED}\x{AAF3}-\x{AAF4}\x{AAF6}\x{AB5B}\x{AB5C}-\x{AB5F}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1E}\x{FBB2}-\x{FBC1}\x{FE00}-\x{FE0F}\x{FE13}\x{FE20}-\x{FE2F}\x{FE52}\x{FE55}\x{FEFF}\x{FF07}\x{FF0E}\x{FF1A}\x{FF3E}\x{FF40}\x{FF70}\x{FF9E}-\x{FF9F}\x{FFE3}\x{FFF9}-\x{FFFB}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10F46}-\x{10F50}\x{11001}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{110BD}\x{110CD}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{11A01}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C3F}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16B40}-\x{16B43}\x{16F8F}-\x{16F92}\x{16F93}-\x{16F9F}\x{16FE0}-\x{16FE1}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{1F3FB}-\x{1F3FF}\x{E0001}\x{E0020}-\x{E007F}\x{E0100}-\x{E01EF}])(\pL)(\pL*+)/u';