summaryrefslogtreecommitdiff
path: root/vendor/fguillot
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/fguillot')
-rw-r--r--vendor/fguillot/json-rpc/LICENSE21
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Client.php194
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Exception/AccessDeniedException.php15
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Exception/AuthenticationFailureException.php15
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Exception/ConnectionFailureException.php15
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonFormatException.php15
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonRpcFormatException.php15
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Exception/ResponseEncodingFailureException.php15
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Exception/ResponseException.php62
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Exception/ServerErrorException.php15
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/HttpClient.php365
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareHandler.php114
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareInterface.php27
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/ProcedureHandler.php264
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Request/BatchRequestParser.php55
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestBuilder.php129
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestParser.php200
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseBuilder.php324
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseParser.php154
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Server.php386
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Validator/HostValidator.php30
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Validator/JsonEncodingValidator.php44
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Validator/JsonFormatValidator.php30
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Validator/RpcFormatValidator.php35
-rw-r--r--vendor/fguillot/json-rpc/src/JsonRPC/Validator/UserValidator.php21
-rw-r--r--vendor/fguillot/picodb/LICENSE21
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/Builder/BaseBuilder.php86
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php377
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php36
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php43
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php56
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/Database.php370
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php234
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/Driver/Mssql.php178
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php252
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php196
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php193
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/DriverFactory.php45
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/Hashtable.php112
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/LargeObject.php167
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/SQLException.php15
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/Schema.php119
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/StatementHandler.php353
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/Table.php721
-rw-r--r--vendor/fguillot/picodb/lib/PicoDb/UrlParser.php93
-rw-r--r--vendor/fguillot/simple-queue/LICENSE21
-rw-r--r--vendor/fguillot/simple-queue/src/Adapter/AmqpQueueAdapter.php138
-rw-r--r--vendor/fguillot/simple-queue/src/Adapter/AwsSqsQueueAdapter.php150
-rw-r--r--vendor/fguillot/simple-queue/src/Adapter/BeanstalkQueueAdapter.php120
-rw-r--r--vendor/fguillot/simple-queue/src/Adapter/DisqueQueueAdapter.php109
-rw-r--r--vendor/fguillot/simple-queue/src/Adapter/MemoryQueueAdapter.php100
-rw-r--r--vendor/fguillot/simple-queue/src/Exception/NotSupportedException.php14
-rw-r--r--vendor/fguillot/simple-queue/src/Job.php98
-rw-r--r--vendor/fguillot/simple-queue/src/Queue.php92
-rw-r--r--vendor/fguillot/simple-queue/src/QueueAdapterInterface.php58
-rw-r--r--vendor/fguillot/simple-validator/LICENSE21
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validator.php44
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Alpha.php15
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/AlphaNumeric.php15
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Base.php37
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Date.php45
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Email.php67
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Equals.php27
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Exists.php38
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/GreaterThan.php23
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/InArray.php23
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Integer.php25
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Ip.php15
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Length.php26
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MaxLength.php24
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MinLength.php24
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEmpty.php15
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEquals.php28
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotInArray.php15
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Numeric.php15
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Range.php33
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Required.php11
-rw-r--r--vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Unique.php48
-rw-r--r--vendor/fguillot/simpleLogger/LICENSE21
-rw-r--r--vendor/fguillot/simpleLogger/src/SimpleLogger/Base.php89
-rw-r--r--vendor/fguillot/simpleLogger/src/SimpleLogger/File.php48
-rw-r--r--vendor/fguillot/simpleLogger/src/SimpleLogger/Logger.php94
-rw-r--r--vendor/fguillot/simpleLogger/src/SimpleLogger/Stderr.php25
-rw-r--r--vendor/fguillot/simpleLogger/src/SimpleLogger/Stdout.php25
-rw-r--r--vendor/fguillot/simpleLogger/src/SimpleLogger/Syslog.php72
85 files changed, 8135 insertions, 0 deletions
diff --git a/vendor/fguillot/json-rpc/LICENSE b/vendor/fguillot/json-rpc/LICENSE
new file mode 100644
index 00000000..6a362bc1
--- /dev/null
+++ b/vendor/fguillot/json-rpc/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Frederic Guillot
+
+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/fguillot/json-rpc/src/JsonRPC/Client.php b/vendor/fguillot/json-rpc/src/JsonRPC/Client.php
new file mode 100644
index 00000000..fed1ce30
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Client.php
@@ -0,0 +1,194 @@
+<?php
+
+namespace JsonRPC;
+
+use Exception;
+use JsonRPC\Request\RequestBuilder;
+use JsonRPC\Response\ResponseParser;
+
+/**
+ * JsonRPC client class
+ *
+ * @package JsonRPC
+ * @author Frederic Guillot
+ */
+class Client
+{
+ /**
+ * If the only argument passed to a function is an array
+ * assume it contains named arguments
+ *
+ * @access private
+ * @var boolean
+ */
+ private $isNamedArguments = true;
+
+ /**
+ * Do not immediately throw an exception on error. Return it instead.
+ *
+ * @access public
+ * @var boolean
+ */
+ private $returnException = false;
+
+ /**
+ * True for a batch request
+ *
+ * @access private
+ * @var boolean
+ */
+ private $isBatch = false;
+
+ /**
+ * Batch payload
+ *
+ * @access private
+ * @var array
+ */
+ private $batch = array();
+
+ /**
+ * Http Client
+ *
+ * @access private
+ * @var HttpClient
+ */
+ private $httpClient;
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param string $url Server URL
+ * @param bool $returnException Return exceptions
+ * @param HttpClient $httpClient HTTP client object
+ */
+ public function __construct($url = '', $returnException = false, HttpClient $httpClient = null)
+ {
+ $this->httpClient = $httpClient ?: new HttpClient($url);
+ $this->returnException = $returnException;
+ }
+
+ /**
+ * Arguments passed are always positional
+ *
+ * @access public
+ * @return $this
+ */
+ public function withPositionalArguments()
+ {
+ $this->isNamedArguments = false;
+ return $this;
+ }
+
+ /**
+ * Get HTTP Client
+ *
+ * @access public
+ * @return HttpClient
+ */
+ public function getHttpClient()
+ {
+ return $this->httpClient;
+ }
+
+ /**
+ * Set username and password
+ *
+ * @access public
+ * @param string $username
+ * @param string $password
+ * @return $this
+ */
+ public function authentication($username, $password)
+ {
+ $this->httpClient
+ ->withUsername($username)
+ ->withPassword($password);
+
+ return $this;
+ }
+
+ /**
+ * Automatic mapping of procedures
+ *
+ * @access public
+ * @param string $method Procedure name
+ * @param array $params Procedure arguments
+ * @return mixed
+ */
+ public function __call($method, array $params)
+ {
+ if ($this->isNamedArguments && count($params) === 1 && is_array($params[0])) {
+ $params = $params[0];
+ }
+
+ return $this->execute($method, $params);
+ }
+
+ /**
+ * Start a batch request
+ *
+ * @access public
+ * @return Client
+ */
+ public function batch()
+ {
+ $this->isBatch = true;
+ $this->batch = array();
+ return $this;
+ }
+
+ /**
+ * Send a batch request
+ *
+ * @access public
+ * @return array
+ */
+ public function send()
+ {
+ $this->isBatch = false;
+ return $this->sendPayload('['.implode(', ', $this->batch).']');
+ }
+
+ /**
+ * Execute a procedure
+ *
+ * @access public
+ * @param string $procedure Procedure name
+ * @param array $params Procedure arguments
+ * @param array $reqattrs
+ * @return mixed
+ */
+ public function execute($procedure, array $params = array(), array $reqattrs = array())
+ {
+ $payload = RequestBuilder::create()
+ ->withProcedure($procedure)
+ ->withParams($params)
+ ->withRequestAttributes($reqattrs)
+ ->build();
+
+ if ($this->isBatch) {
+ $this->batch[] = $payload;
+ return $this;
+ }
+
+ return $this->sendPayload($payload);
+ }
+
+ /**
+ * Send payload
+ *
+ * @access private
+ * @throws Exception
+ * @param string $payload
+ * @return Exception|Client
+ */
+ private function sendPayload($payload)
+ {
+ return ResponseParser::create()
+ ->withReturnException($this->returnException)
+ ->withPayload($this->httpClient->execute($payload))
+ ->parse();
+ }
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Exception/AccessDeniedException.php b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/AccessDeniedException.php
new file mode 100644
index 00000000..8cb9bc2b
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/AccessDeniedException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace JsonRPC\Exception;
+
+use Exception;
+
+/**
+ * Class AccessDeniedException
+ *
+ * @package JsonRPC\Exception
+ * @author Frederic Guillot
+ */
+class AccessDeniedException extends Exception
+{
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Exception/AuthenticationFailureException.php b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/AuthenticationFailureException.php
new file mode 100644
index 00000000..6237256a
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/AuthenticationFailureException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace JsonRPC\Exception;
+
+use Exception;
+
+/**
+ * Class AuthenticationFailureException
+ *
+ * @package JsonRPC\Exception
+ * @author Frederic Guillot
+ */
+class AuthenticationFailureException extends Exception
+{
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ConnectionFailureException.php b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ConnectionFailureException.php
new file mode 100644
index 00000000..6f4985bc
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ConnectionFailureException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace JsonRPC\Exception;
+
+use Exception;
+
+/**
+ * Class ConnectionFailureException
+ *
+ * @package JsonRPC\Exception
+ * @author Frederic Guillot
+ */
+class ConnectionFailureException extends Exception
+{
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonFormatException.php b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonFormatException.php
new file mode 100644
index 00000000..e5bb31cd
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonFormatException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace JsonRPC\Exception;
+
+use Exception;
+
+/**
+ * Class InvalidJsonFormatException
+ *
+ * @package JsonRPC\Exception
+ * @author Frederic Guillot
+ */
+class InvalidJsonFormatException extends Exception
+{
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonRpcFormatException.php b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonRpcFormatException.php
new file mode 100644
index 00000000..e2277379
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/InvalidJsonRpcFormatException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace JsonRPC\Exception;
+
+use Exception;
+
+/**
+ * Class InvalidJsonRpcFormatException
+ *
+ * @package JsonRPC\Exception
+ * @author Frederic Guillot
+ */
+class InvalidJsonRpcFormatException extends Exception
+{
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ResponseEncodingFailureException.php b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ResponseEncodingFailureException.php
new file mode 100644
index 00000000..0a40d806
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ResponseEncodingFailureException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace JsonRPC\Exception;
+
+use Exception;
+
+/**
+ * Class ResponseEncodingFailureException
+ *
+ * @package JsonRPC\Exception
+ * @author Frederic Guillot
+ */
+class ResponseEncodingFailureException extends Exception
+{
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ResponseException.php b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ResponseException.php
new file mode 100644
index 00000000..98fb5c67
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ResponseException.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace JsonRPC\Exception;
+
+use Exception;
+
+/**
+ * Class ResponseException
+ *
+ * @package JsonRPC\Exception
+ * @author Frederic Guillot
+ */
+class ResponseException extends Exception
+{
+ /**
+ * A value that contains additional information about the error.
+ *
+ * @access protected
+ * @link http://www.jsonrpc.org/specification#error_object
+ * @var mixed
+ */
+ protected $data;
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param string $message [optional] The Exception message to throw.
+ * @param int $code [optional] The Exception code.
+ * @param Exception $previous [optional] The previous exception used for the exception chaining. Since 5.3.0
+ * @param mixed $data [optional] A value that contains additional information about the error.
+ */
+ public function __construct($message = '', $code = 0, Exception $previous = null, $data = null)
+ {
+ parent::__construct($message, $code, $previous);
+ $this->setData($data);
+ }
+
+ /**
+ * Attach additional information
+ *
+ * @access public
+ * @param mixed $data [optional] A value that contains additional information about the error.
+ * @return \JsonRPC\Exception\ResponseException
+ */
+ public function setData($data = null)
+ {
+ $this->data = $data;
+ return $this;
+ }
+
+ /**
+ * Get additional information
+ *
+ * @access public
+ * @return mixed|null
+ */
+ public function getData()
+ {
+ return $this->data;
+ }
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ServerErrorException.php b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ServerErrorException.php
new file mode 100644
index 00000000..ab3ea584
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Exception/ServerErrorException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace JsonRPC\Exception;
+
+use Exception;
+
+/**
+ * Class ServerErrorException
+ *
+ * @package JsonRPC\Exception
+ * @author Frederic Guillot
+ */
+class ServerErrorException extends Exception
+{
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/HttpClient.php b/vendor/fguillot/json-rpc/src/JsonRPC/HttpClient.php
new file mode 100644
index 00000000..90ce705a
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/HttpClient.php
@@ -0,0 +1,365 @@
+<?php
+
+namespace JsonRPC;
+
+use Closure;
+use JsonRPC\Exception\AccessDeniedException;
+use JsonRPC\Exception\ConnectionFailureException;
+use JsonRPC\Exception\ServerErrorException;
+
+/**
+ * Class HttpClient
+ *
+ * @package JsonRPC
+ * @author Frederic Guillot
+ */
+class HttpClient
+{
+ /**
+ * URL of the server
+ *
+ * @access private
+ * @var string
+ */
+ private $url;
+
+ /**
+ * HTTP client timeout
+ *
+ * @access private
+ * @var integer
+ */
+ private $timeout = 5;
+
+ /**
+ * Default HTTP headers to send to the server
+ *
+ * @access private
+ * @var array
+ */
+ private $headers = array(
+ 'User-Agent: JSON-RPC PHP Client <https://github.com/fguillot/JsonRPC>',
+ 'Content-Type: application/json',
+ 'Accept: application/json',
+ 'Connection: close',
+ );
+
+ /**
+ * Username for authentication
+ *
+ * @access private
+ * @var string
+ */
+ private $username;
+
+ /**
+ * Password for authentication
+ *
+ * @access private
+ * @var string
+ */
+ private $password;
+
+ /**
+ * Enable debug output to the php error log
+ *
+ * @access private
+ * @var boolean
+ */
+ private $debug = false;
+
+ /**
+ * Cookies
+ *
+ * @access private
+ * @var array
+ */
+ private $cookies = array();
+
+ /**
+ * SSL certificates verification
+ *
+ * @access private
+ * @var boolean
+ */
+ private $verifySslCertificate = true;
+
+ /**
+ * Callback called before the doing the request
+ *
+ * @access private
+ * @var Closure
+ */
+ private $beforeRequest;
+
+ /**
+ * HttpClient constructor
+ *
+ * @access public
+ * @param string $url
+ */
+ public function __construct($url = '')
+ {
+ $this->url = $url;
+ }
+
+ /**
+ * Set URL
+ *
+ * @access public
+ * @param string $url
+ * @return $this
+ */
+ public function withUrl($url)
+ {
+ $this->url = $url;
+ return $this;
+ }
+
+ /**
+ * Set username
+ *
+ * @access public
+ * @param string $username
+ * @return $this
+ */
+ public function withUsername($username)
+ {
+ $this->username = $username;
+ return $this;
+ }
+
+ /**
+ * Set password
+ *
+ * @access public
+ * @param string $password
+ * @return $this
+ */
+ public function withPassword($password)
+ {
+ $this->password = $password;
+ return $this;
+ }
+
+ /**
+ * Set timeout
+ *
+ * @access public
+ * @param integer $timeout
+ * @return $this
+ */
+ public function withTimeout($timeout)
+ {
+ $this->timeout = $timeout;
+ return $this;
+ }
+
+ /**
+ * Set timeout
+ *
+ * @access public
+ * @param array $headers
+ * @return $this
+ */
+ public function withHeaders(array $headers)
+ {
+ $this->headers = array_merge($this->headers, $headers);
+ return $this;
+ }
+
+ /**
+ * Set cookies
+ *
+ * @access public
+ * @param array $cookies
+ * @param boolean $replace
+ */
+ public function withCookies(array $cookies, $replace = false)
+ {
+ if ($replace) {
+ $this->cookies = $cookies;
+ } else {
+ $this->cookies = array_merge($this->cookies, $cookies);
+ }
+ }
+
+ /**
+ * Enable debug mode
+ *
+ * @access public
+ * @return $this
+ */
+ public function withDebug()
+ {
+ $this->debug = true;
+ return $this;
+ }
+
+ /**
+ * Disable SSL verification
+ *
+ * @access public
+ * @return $this
+ */
+ public function withoutSslVerification()
+ {
+ $this->verifySslCertificate = false;
+ return $this;
+ }
+
+ /**
+ * Assign a callback before the request
+ *
+ * @access public
+ * @param Closure $closure
+ * @return $this
+ */
+ public function withBeforeRequestCallback(Closure $closure)
+ {
+ $this->beforeRequest = $closure;
+ return $this;
+ }
+
+ /**
+ * Get cookies
+ *
+ * @access public
+ * @return array
+ */
+ public function getCookies()
+ {
+ return $this->cookies;
+ }
+
+ /**
+ * Do the HTTP request
+ *
+ * @access public
+ * @throws ConnectionFailureException
+ * @param string $payload
+ * @return array
+ */
+ public function execute($payload)
+ {
+ if (is_callable($this->beforeRequest)) {
+ call_user_func_array($this->beforeRequest, array($this, $payload));
+ }
+
+ $stream = fopen(trim($this->url), 'r', false, $this->buildContext($payload));
+
+ if (! is_resource($stream)) {
+ throw new ConnectionFailureException('Unable to establish a connection');
+ }
+
+ $metadata = stream_get_meta_data($stream);
+ $headers = $metadata['wrapper_data'];
+ $response = json_decode(stream_get_contents($stream), true);
+
+ if ($this->debug) {
+ error_log('==> Request: '.PHP_EOL.(is_string($payload) ? $payload : json_encode($payload, JSON_PRETTY_PRINT)));
+ error_log('==> Headers: '.PHP_EOL.var_export($headers, true));
+ error_log('==> Response: '.PHP_EOL.json_encode($response, JSON_PRETTY_PRINT));
+ }
+
+ $this->handleExceptions($headers);
+ $this->parseCookies($headers);
+
+ return $response;
+ }
+
+ /**
+ * Prepare stream context
+ *
+ * @access private
+ * @param string $payload
+ * @return resource
+ */
+ private function buildContext($payload)
+ {
+ $headers = $this->headers;
+
+ if (! empty($this->username) && ! empty($this->password)) {
+ $headers[] = 'Authorization: Basic '.base64_encode($this->username.':'.$this->password);
+ }
+
+ if (! empty($this->cookies)){
+ $cookies = array();
+
+ foreach ($this->cookies as $key => $value) {
+ $cookies[] = $key.'='.$value;
+ }
+
+ $headers[] = 'Cookie: '.implode('; ', $cookies);
+ }
+
+ return stream_context_create(array(
+ 'http' => array(
+ 'method' => 'POST',
+ 'protocol_version' => 1.1,
+ 'timeout' => $this->timeout,
+ 'max_redirects' => 2,
+ 'header' => implode("\r\n", $headers),
+ 'content' => $payload,
+ 'ignore_errors' => true,
+ ),
+ 'ssl' => array(
+ 'verify_peer' => $this->verifySslCertificate,
+ 'verify_peer_name' => $this->verifySslCertificate,
+ )
+ ));
+ }
+
+ /**
+ * Parse cookies from response
+ *
+ * @access private
+ * @param array $headers
+ */
+ private function parseCookies(array $headers)
+ {
+ foreach ($headers as $header) {
+ $pos = stripos($header, 'Set-Cookie:');
+
+ if ($pos !== false) {
+ $cookies = explode(';', substr($header, $pos + 11));
+
+ foreach ($cookies as $cookie) {
+ $item = explode('=', $cookie);
+
+ if (count($item) === 2) {
+ $name = trim($item[0]);
+ $value = $item[1];
+ $this->cookies[$name] = $value;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Throw an exception according the HTTP response
+ *
+ * @access public
+ * @param array $headers
+ * @throws AccessDeniedException
+ * @throws ServerErrorException
+ */
+ public function handleExceptions(array $headers)
+ {
+ $exceptions = array(
+ '401' => '\JsonRPC\Exception\AccessDeniedException',
+ '403' => '\JsonRPC\Exception\AccessDeniedException',
+ '404' => '\JsonRPC\Exception\ConnectionFailureException',
+ '500' => '\JsonRPC\Exception\ServerErrorException',
+ );
+
+ foreach ($headers as $header) {
+ foreach ($exceptions as $code => $exception) {
+ if (strpos($header, 'HTTP/1.0 '.$code) !== false || strpos($header, 'HTTP/1.1 '.$code) !== false) {
+ throw new $exception('Response: '.$header);
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareHandler.php b/vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareHandler.php
new file mode 100644
index 00000000..61d5a2d2
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareHandler.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace JsonRPC;
+
+/**
+ * Class MiddlewareHandler
+ *
+ * @package JsonRPC
+ * @author Frederic Guillot
+ */
+class MiddlewareHandler
+{
+ /**
+ * Procedure Name
+ *
+ * @access protected
+ * @var string
+ */
+ protected $procedureName = '';
+
+ /**
+ * Username
+ *
+ * @access protected
+ * @var string
+ */
+ protected $username = '';
+
+ /**
+ * Password
+ *
+ * @access protected
+ * @var string
+ */
+ protected $password = '';
+
+ /**
+ * List of middleware to execute before to call the method
+ *
+ * @access protected
+ * @var MiddlewareInterface[]
+ */
+ protected $middleware = array();
+
+ /**
+ * Set username
+ *
+ * @access public
+ * @param string $username
+ * @return $this
+ */
+ public function withUsername($username)
+ {
+ if (! empty($username)) {
+ $this->username = $username;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set password
+ *
+ * @access public
+ * @param string $password
+ * @return $this
+ */
+ public function withPassword($password)
+ {
+ if (! empty($password)) {
+ $this->password = $password;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set procedure name
+ *
+ * @access public
+ * @param string $procedureName
+ * @return $this
+ */
+ public function withProcedure($procedureName)
+ {
+ $this->procedureName = $procedureName;
+ return $this;
+ }
+
+ /**
+ * Add a new middleware
+ *
+ * @access public
+ * @param MiddlewareInterface $middleware
+ * @return MiddlewareHandler
+ */
+ public function withMiddleware(MiddlewareInterface $middleware)
+ {
+ $this->middleware[] = $middleware;
+ return $this;
+ }
+
+ /**
+ * Execute all middleware
+ *
+ * @access public
+ */
+ public function execute()
+ {
+ foreach ($this->middleware as $middleware) {
+ $middleware->execute($this->username, $this->password, $this->procedureName);
+ }
+ }
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareInterface.php b/vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareInterface.php
new file mode 100644
index 00000000..ab55261d
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/MiddlewareInterface.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace JsonRPC;
+
+use JsonRPC\Exception\AccessDeniedException;
+use JsonRPC\Exception\AuthenticationFailureException;
+
+/**
+ * Interface MiddlewareInterface
+ *
+ * @package JsonRPC
+ * @author Frederic Guillot
+ */
+interface MiddlewareInterface
+{
+ /**
+ * Execute Middleware
+ *
+ * @access public
+ * @param string $username
+ * @param string $password
+ * @param string $procedureName
+ * @throws AccessDeniedException
+ * @throws AuthenticationFailureException
+ */
+ public function execute($username, $password, $procedureName);
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/ProcedureHandler.php b/vendor/fguillot/json-rpc/src/JsonRPC/ProcedureHandler.php
new file mode 100644
index 00000000..1e4fd518
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/ProcedureHandler.php
@@ -0,0 +1,264 @@
+<?php
+
+namespace JsonRPC;
+
+use BadFunctionCallException;
+use Closure;
+use InvalidArgumentException;
+use ReflectionFunction;
+use ReflectionMethod;
+
+/**
+ * Class ProcedureHandler
+ *
+ * @package JsonRPC
+ * @author Frederic Guillot
+ */
+class ProcedureHandler
+{
+ /**
+ * List of procedures
+ *
+ * @access protected
+ * @var array
+ */
+ protected $callbacks = array();
+
+ /**
+ * List of classes
+ *
+ * @access protected
+ * @var array
+ */
+ protected $classes = array();
+
+ /**
+ * List of instances
+ *
+ * @access protected
+ * @var array
+ */
+ protected $instances = array();
+
+ /**
+ * Before method name to call
+ *
+ * @access protected
+ * @var string
+ */
+ protected $beforeMethodName = '';
+
+ /**
+ * Register a new procedure
+ *
+ * @access public
+ * @param string $procedure Procedure name
+ * @param closure $callback Callback
+ * @return $this
+ */
+ public function withCallback($procedure, Closure $callback)
+ {
+ $this->callbacks[$procedure] = $callback;
+ return $this;
+ }
+
+ /**
+ * Bind a procedure to a class
+ *
+ * @access public
+ * @param string $procedure Procedure name
+ * @param mixed $class Class name or instance
+ * @param string $method Procedure name
+ * @return $this
+ */
+ public function withClassAndMethod($procedure, $class, $method = '')
+ {
+ if ($method === '') {
+ $method = $procedure;
+ }
+
+ $this->classes[$procedure] = array($class, $method);
+ return $this;
+ }
+
+ /**
+ * Bind a class instance
+ *
+ * @access public
+ * @param mixed $instance
+ * @return $this
+ */
+ public function withObject($instance)
+ {
+ $this->instances[] = $instance;
+ return $this;
+ }
+
+ /**
+ * Set a before method to call
+ *
+ * @access public
+ * @param string $methodName
+ * @return $this
+ */
+ public function withBeforeMethod($methodName)
+ {
+ $this->beforeMethodName = $methodName;
+ return $this;
+ }
+
+ /**
+ * Execute the procedure
+ *
+ * @access public
+ * @param string $procedure Procedure name
+ * @param array $params Procedure params
+ * @return mixed
+ */
+ public function executeProcedure($procedure, array $params = array())
+ {
+ if (isset($this->callbacks[$procedure])) {
+ return $this->executeCallback($this->callbacks[$procedure], $params);
+ } elseif (isset($this->classes[$procedure]) && method_exists($this->classes[$procedure][0], $this->classes[$procedure][1])) {
+ return $this->executeMethod($this->classes[$procedure][0], $this->classes[$procedure][1], $params);
+ }
+
+ foreach ($this->instances as $instance) {
+ if (method_exists($instance, $procedure)) {
+ return $this->executeMethod($instance, $procedure, $params);
+ }
+ }
+
+ throw new BadFunctionCallException('Unable to find the procedure');
+ }
+
+ /**
+ * Execute a callback
+ *
+ * @access public
+ * @param Closure $callback Callback
+ * @param array $params Procedure params
+ * @return mixed
+ */
+ public function executeCallback(Closure $callback, $params)
+ {
+ $reflection = new ReflectionFunction($callback);
+
+ $arguments = $this->getArguments(
+ $params,
+ $reflection->getParameters(),
+ $reflection->getNumberOfRequiredParameters(),
+ $reflection->getNumberOfParameters()
+ );
+
+ return $reflection->invokeArgs($arguments);
+ }
+
+ /**
+ * Execute a method
+ *
+ * @access public
+ * @param mixed $class Class name or instance
+ * @param string $method Method name
+ * @param array $params Procedure params
+ * @return mixed
+ */
+ public function executeMethod($class, $method, $params)
+ {
+ $instance = is_string($class) ? new $class : $class;
+ $reflection = new ReflectionMethod($class, $method);
+
+ $this->executeBeforeMethod($instance, $method);
+
+ $arguments = $this->getArguments(
+ $params,
+ $reflection->getParameters(),
+ $reflection->getNumberOfRequiredParameters(),
+ $reflection->getNumberOfParameters()
+ );
+
+ return $reflection->invokeArgs($instance, $arguments);
+ }
+
+ /**
+ * Execute before method if defined
+ *
+ * @access public
+ * @param mixed $object
+ * @param string $method
+ */
+ public function executeBeforeMethod($object, $method)
+ {
+ if ($this->beforeMethodName !== '' && method_exists($object, $this->beforeMethodName)) {
+ call_user_func_array(array($object, $this->beforeMethodName), array($method));
+ }
+ }
+
+ /**
+ * Get procedure arguments
+ *
+ * @access public
+ * @param array $requestParams Incoming arguments
+ * @param array $methodParams Procedure arguments
+ * @param integer $nbRequiredParams Number of required parameters
+ * @param integer $nbMaxParams Maximum number of parameters
+ * @return array
+ */
+ public function getArguments(array $requestParams, array $methodParams, $nbRequiredParams, $nbMaxParams)
+ {
+ $nbParams = count($requestParams);
+
+ if ($nbParams < $nbRequiredParams) {
+ throw new InvalidArgumentException('Wrong number of arguments');
+ }
+
+ if ($nbParams > $nbMaxParams) {
+ throw new InvalidArgumentException('Too many arguments');
+ }
+
+ if ($this->isPositionalArguments($requestParams)) {
+ return $requestParams;
+ }
+
+ return $this->getNamedArguments($requestParams, $methodParams);
+ }
+
+ /**
+ * Return true if we have positional parameters
+ *
+ * @access public
+ * @param array $request_params Incoming arguments
+ * @return bool
+ */
+ public function isPositionalArguments(array $request_params)
+ {
+ return array_keys($request_params) === range(0, count($request_params) - 1);
+ }
+
+ /**
+ * Get named arguments
+ *
+ * @access public
+ * @param array $requestParams Incoming arguments
+ * @param array $methodParams Procedure arguments
+ * @return array
+ */
+ public function getNamedArguments(array $requestParams, array $methodParams)
+ {
+ $params = array();
+
+ foreach ($methodParams as $p) {
+ $name = $p->getName();
+
+ if (isset($requestParams[$name])) {
+ $params[$name] = $requestParams[$name];
+ } elseif ($p->isDefaultValueAvailable()) {
+ $params[$name] = $p->getDefaultValue();
+ } else {
+ throw new InvalidArgumentException('Missing argument: '.$name);
+ }
+ }
+
+ return $params;
+ }
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Request/BatchRequestParser.php b/vendor/fguillot/json-rpc/src/JsonRPC/Request/BatchRequestParser.php
new file mode 100644
index 00000000..c0fc776e
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Request/BatchRequestParser.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace JsonRPC\Request;
+
+/**
+ * Class BatchRequestParser
+ *
+ * @package JsonRPC\Request
+ * @author Frederic Guillot
+ */
+class BatchRequestParser extends RequestParser
+{
+ /**
+ * Parse incoming request
+ *
+ * @access public
+ * @return string
+ */
+ public function parse()
+ {
+ $responses = array();
+
+ foreach ($this->payload as $payload) {
+ $responses[] = RequestParser::create()
+ ->withPayload($payload)
+ ->withProcedureHandler($this->procedureHandler)
+ ->withMiddlewareHandler($this->middlewareHandler)
+ ->withLocalException($this->localExceptions)
+ ->parse();
+ }
+
+ $responses = array_filter($responses);
+ return empty($responses) ? '' : '['.implode(',', $responses).']';
+ }
+
+ /**
+ * Return true if we have a batch request
+ *
+ * ex : [
+ * 0 => '...',
+ * 1 => '...',
+ * 2 => '...',
+ * 3 => '...',
+ * ]
+ *
+ * @static
+ * @access public
+ * @param array $payload
+ * @return bool
+ */
+ public static function isBatchRequest(array $payload)
+ {
+ return array_keys($payload) === range(0, count($payload) - 1);
+ }
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestBuilder.php b/vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestBuilder.php
new file mode 100644
index 00000000..145d21c1
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestBuilder.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace JsonRPC\Request;
+
+/**
+ * Class RequestBuilder
+ *
+ * @package JsonRPC\Request
+ * @author Frederic Guillot
+ */
+class RequestBuilder
+{
+ /**
+ * Request ID
+ *
+ * @access private
+ * @var mixed
+ */
+ private $id = null;
+
+ /**
+ * Method name
+ *
+ * @access private
+ * @var string
+ */
+ private $procedure = '';
+
+ /**
+ * Method arguments
+ *
+ * @access private
+ * @var array
+ */
+ private $params = array();
+
+ /**
+ * Additional request attributes
+ *
+ * @access private
+ * @var array
+ */
+ private $reqattrs = array();
+
+ /**
+ * Get new object instance
+ *
+ * @static
+ * @access public
+ * @return RequestBuilder
+ */
+ public static function create()
+ {
+ return new static();
+ }
+
+ /**
+ * Set id
+ *
+ * @access public
+ * @param null $id
+ * @return RequestBuilder
+ */
+ public function withId($id)
+ {
+ $this->id = $id;
+ return $this;
+ }
+
+ /**
+ * Set method
+ *
+ * @access public
+ * @param string $procedure
+ * @return RequestBuilder
+ */
+ public function withProcedure($procedure)
+ {
+ $this->procedure = $procedure;
+ return $this;
+ }
+
+ /**
+ * Set parameters
+ *
+ * @access public
+ * @param array $params
+ * @return RequestBuilder
+ */
+ public function withParams(array $params)
+ {
+ $this->params = $params;
+ return $this;
+ }
+
+ /**
+ * Set additional request attributes
+ *
+ * @access public
+ * @param array $reqattrs
+ * @return RequestBuilder
+ */
+ public function withRequestAttributes(array $reqattrs)
+ {
+ $this->reqattrs = $reqattrs;
+ return $this;
+ }
+
+ /**
+ * Build the payload
+ *
+ * @access public
+ * @return string
+ */
+ public function build()
+ {
+ $payload = array_merge_recursive($this->reqattrs, array(
+ 'jsonrpc' => '2.0',
+ 'method' => $this->procedure,
+ 'id' => $this->id ?: mt_rand(),
+ ));
+
+ if (! empty($this->params)) {
+ $payload['params'] = $this->params;
+ }
+
+ return json_encode($payload);
+ }
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestParser.php b/vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestParser.php
new file mode 100644
index 00000000..ea1b7d43
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Request/RequestParser.php
@@ -0,0 +1,200 @@
+<?php
+
+namespace JsonRPC\Request;
+
+use Exception;
+use JsonRPC\Exception\AccessDeniedException;
+use JsonRPC\Exception\AuthenticationFailureException;
+use JsonRPC\Exception\InvalidJsonRpcFormatException;
+use JsonRPC\MiddlewareHandler;
+use JsonRPC\ProcedureHandler;
+use JsonRPC\Response\ResponseBuilder;
+use JsonRPC\Validator\JsonFormatValidator;
+use JsonRPC\Validator\RpcFormatValidator;
+
+/**
+ * Class RequestParser
+ *
+ * @package JsonRPC
+ * @author Frederic Guillot
+ */
+class RequestParser
+{
+ /**
+ * Request payload
+ *
+ * @access protected
+ * @var mixed
+ */
+ protected $payload;
+
+ /**
+ * List of exceptions that should not be relayed to the client
+ *
+ * @access protected
+ * @var array()
+ */
+ protected $localExceptions = array(
+ 'JsonRPC\Exception\AuthenticationFailureException',
+ 'JsonRPC\Exception\AccessDeniedException',
+ );
+
+ /**
+ * ProcedureHandler
+ *
+ * @access protected
+ * @var ProcedureHandler
+ */
+ protected $procedureHandler;
+
+ /**
+ * MiddlewareHandler
+ *
+ * @access protected
+ * @var MiddlewareHandler
+ */
+ protected $middlewareHandler;
+
+ /**
+ * Get new object instance
+ *
+ * @static
+ * @access public
+ * @return RequestParser
+ */
+ public static function create()
+ {
+ return new static();
+ }
+
+ /**
+ * Set payload
+ *
+ * @access public
+ * @param mixed $payload
+ * @return $this
+ */
+ public function withPayload($payload)
+ {
+ $this->payload = $payload;
+ return $this;
+ }
+
+ /**
+ * Exception classes that should not be relayed to the client
+ *
+ * @access public
+ * @param mixed $exception
+ * @return $this
+ */
+ public function withLocalException($exception)
+ {
+ if (is_array($exception)) {
+ $this->localExceptions = array_merge($this->localExceptions, $exception);
+ } else {
+ $this->localExceptions[] = $exception;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set procedure handler
+ *
+ * @access public
+ * @param ProcedureHandler $procedureHandler
+ * @return $this
+ */
+ public function withProcedureHandler(ProcedureHandler $procedureHandler)
+ {
+ $this->procedureHandler = $procedureHandler;
+ return $this;
+ }
+
+ /**
+ * Set middleware handler
+ *
+ * @access public
+ * @param MiddlewareHandler $middlewareHandler
+ * @return $this
+ */
+ public function withMiddlewareHandler(MiddlewareHandler $middlewareHandler)
+ {
+ $this->middlewareHandler = $middlewareHandler;
+ return $this;
+ }
+
+ /**
+ * Parse incoming request
+ *
+ * @access public
+ * @return string
+ * @throws AccessDeniedException
+ * @throws AuthenticationFailureException
+ */
+ public function parse()
+ {
+ try {
+
+ JsonFormatValidator::validate($this->payload);
+ RpcFormatValidator::validate($this->payload);
+
+ $this->middlewareHandler
+ ->withProcedure($this->payload['method'])
+ ->execute();
+
+ $result = $this->procedureHandler->executeProcedure(
+ $this->payload['method'],
+ empty($this->payload['params']) ? array() : $this->payload['params']
+ );
+
+ if (! $this->isNotification()) {
+ return ResponseBuilder::create()
+ ->withId($this->payload['id'])
+ ->withResult($result)
+ ->build();
+ }
+ } catch (Exception $e) {
+ return $this->handleExceptions($e);
+ }
+
+ return '';
+ }
+
+ /**
+ * Handle exceptions
+ *
+ * @access protected
+ * @param Exception $e
+ * @return string
+ * @throws Exception
+ */
+ protected function handleExceptions(Exception $e)
+ {
+ foreach ($this->localExceptions as $exception) {
+ if ($e instanceof $exception) {
+ throw $e;
+ }
+ }
+
+ if ($e instanceof InvalidJsonRpcFormatException || ! $this->isNotification()) {
+ return ResponseBuilder::create()
+ ->withId(isset($this->payload['id']) ? $this->payload['id'] : null)
+ ->withException($e)
+ ->build();
+ }
+
+ return '';
+ }
+
+ /**
+ * Return true if the message is a notification
+ *
+ * @access protected
+ * @return bool
+ */
+ protected function isNotification()
+ {
+ return is_array($this->payload) && !isset($this->payload['id']);
+ }
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseBuilder.php b/vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseBuilder.php
new file mode 100644
index 00000000..c1caff92
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseBuilder.php
@@ -0,0 +1,324 @@
+<?php
+
+namespace JsonRPC\Response;
+
+use BadFunctionCallException;
+use Exception;
+use InvalidArgumentException;
+use JsonRPC\Exception\AccessDeniedException;
+use JsonRPC\Exception\AuthenticationFailureException;
+use JsonRPC\Exception\InvalidJsonFormatException;
+use JsonRPC\Exception\InvalidJsonRpcFormatException;
+use JsonRPC\Exception\ResponseEncodingFailureException;
+use JsonRPC\Exception\ResponseException;
+use JsonRPC\Validator\JsonEncodingValidator;
+
+/**
+ * Class ResponseBuilder
+ *
+ * @package JsonRPC
+ * @author Frederic Guillot
+ */
+class ResponseBuilder
+{
+ /**
+ * Payload ID
+ *
+ * @access private
+ * @var mixed
+ */
+ private $id;
+
+ /**
+ * Payload ID
+ *
+ * @access private
+ * @var mixed
+ */
+ private $result;
+
+ /**
+ * Payload error code
+ *
+ * @access private
+ * @var integer
+ */
+ private $errorCode;
+
+ /**
+ * Payload error message
+ *
+ * @access private
+ * @var string
+ */
+ private $errorMessage;
+
+ /**
+ * Payload error data
+ *
+ * @access private
+ * @var mixed
+ */
+ private $errorData;
+
+ /**
+ * HTTP Headers
+ *
+ * @access private
+ * @var array
+ */
+ private $headers = array(
+ 'Content-Type' => 'application/json',
+ );
+
+ /**
+ * HTTP status
+ *
+ * @access private
+ * @var string
+ */
+ private $status;
+
+ /**
+ * Exception
+ *
+ * @access private
+ * @var ResponseException
+ */
+ private $exception;
+
+ /**
+ * Get new object instance
+ *
+ * @static
+ * @access public
+ * @return ResponseBuilder
+ */
+ public static function create()
+ {
+ return new static();
+ }
+
+ /**
+ * Set id
+ *
+ * @access public
+ * @param mixed $id
+ * @return $this
+ */
+ public function withId($id)
+ {
+ $this->id = $id;
+ return $this;
+ }
+
+ /**
+ * Set result
+ *
+ * @access public
+ * @param mixed $result
+ * @return $this
+ */
+ public function withResult($result)
+ {
+ $this->result = $result;
+ return $this;
+ }
+
+ /**
+ * Set error
+ *
+ * @access public
+ * @param integer $code
+ * @param string $message
+ * @param string $data
+ * @return $this
+ */
+ public function withError($code, $message, $data = '')
+ {
+ $this->errorCode = $code;
+ $this->errorMessage = $message;
+ $this->errorData = $data;
+ return $this;
+ }
+
+ /**
+ * Set exception
+ *
+ * @access public
+ * @param Exception $exception
+ * @return $this
+ */
+ public function withException(Exception $exception)
+ {
+ $this->exception = $exception;
+ return $this;
+ }
+
+ /**
+ * Add HTTP header
+ *
+ * @access public
+ * @param string $name
+ * @param string $value
+ * @return $this
+ */
+ public function withHeader($name, $value)
+ {
+ $this->headers[$name] = $value;
+ return $this;
+ }
+
+ /**
+ * Add HTTP Status
+ *
+ * @access public
+ * @param string $status
+ * @return $this
+ */
+ public function withStatus($status)
+ {
+ $this->status = $status;
+ return $this;
+ }
+
+ /**
+ * Get status
+ *
+ * @access public
+ * @return string
+ */
+ public function getStatus()
+ {
+ return $this->status;
+ }
+
+ /**
+ * Get headers
+ *
+ * @access public
+ * @return string[]
+ */
+ public function getHeaders()
+ {
+ return $this->headers;
+ }
+
+ /**
+ * Build response
+ *
+ * @access public
+ * @return string
+ */
+ public function build()
+ {
+ $encodedResponse = json_encode($this->buildResponse());
+ JsonEncodingValidator::validate();
+
+ return $encodedResponse;
+ }
+
+ /**
+ * Send HTTP headers
+ *
+ * @access public
+ * @return $this
+ */
+ public function sendHeaders()
+ {
+ if (! empty($this->status)) {
+ header($this->status);
+ }
+
+ foreach ($this->headers as $name => $value) {
+ header($name.': '.$value);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Build response payload
+ *
+ * @access private
+ * @return array
+ */
+ private function buildResponse()
+ {
+ $response = array('jsonrpc' => '2.0');
+ $this->handleExceptions();
+
+ if (! empty($this->errorMessage)) {
+ $response['error'] = $this->buildErrorResponse();
+ } else {
+ $response['result'] = $this->result;
+ }
+
+ $response['id'] = $this->id;
+ return $response;
+ }
+
+ /**
+ * Build response error payload
+ *
+ * @access private
+ * @return array
+ */
+ private function buildErrorResponse()
+ {
+ $response = array(
+ 'code' => $this->errorCode,
+ 'message' => $this->errorMessage,
+ );
+
+ if (! empty($this->errorData)) {
+ $response['data'] = $this->errorData;
+ }
+
+ return $response;
+ }
+
+ /**
+ * Transform exceptions to JSON-RPC errors
+ *
+ * @access private
+ */
+ private function handleExceptions()
+ {
+ if ($this->exception instanceof InvalidJsonFormatException) {
+ $this->errorCode = -32700;
+ $this->errorMessage = 'Parse error';
+ $this->id = null;
+ } elseif ($this->exception instanceof InvalidJsonRpcFormatException) {
+ $this->errorCode = -32600;
+ $this->errorMessage = 'Invalid Request';
+ $this->id = null;
+ } elseif ($this->exception instanceof BadFunctionCallException) {
+ $this->errorCode = -32601;
+ $this->errorMessage = 'Method not found';
+ } elseif ($this->exception instanceof InvalidArgumentException) {
+ $this->errorCode = -32602;
+ $this->errorMessage = 'Invalid params';
+ } elseif ($this->exception instanceof ResponseEncodingFailureException) {
+ $this->errorCode = -32603;
+ $this->errorMessage = 'Internal error';
+ $this->errorData = $this->exception->getMessage();
+ } elseif ($this->exception instanceof AuthenticationFailureException) {
+ $this->errorCode = 401;
+ $this->errorMessage = 'Unauthorized';
+ $this->status = 'HTTP/1.0 401 Unauthorized';
+ $this->withHeader('WWW-Authenticate', 'Basic realm="JsonRPC"');
+ } elseif ($this->exception instanceof AccessDeniedException) {
+ $this->errorCode = 403;
+ $this->errorMessage = 'Forbidden';
+ $this->status = 'HTTP/1.0 403 Forbidden';
+ } elseif ($this->exception instanceof ResponseException) {
+ $this->errorCode = $this->exception->getCode();
+ $this->errorMessage = $this->exception->getMessage();
+ $this->errorData = $this->exception->getData();
+ } elseif ($this->exception instanceof Exception) {
+ $this->errorCode = $this->exception->getCode();
+ $this->errorMessage = $this->exception->getMessage();
+ }
+ }
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseParser.php b/vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseParser.php
new file mode 100644
index 00000000..02d449ba
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Response/ResponseParser.php
@@ -0,0 +1,154 @@
+<?php
+
+namespace JsonRPC\Response;
+
+use BadFunctionCallException;
+use InvalidArgumentException;
+use Exception;
+use JsonRPC\Exception\InvalidJsonFormatException;
+use JsonRPC\Exception\InvalidJsonRpcFormatException;
+use JsonRPC\Exception\ResponseException;
+use JsonRPC\Validator\JsonFormatValidator;
+
+/**
+ * Class ResponseParser
+ *
+ * @package JsonRPC\Request
+ * @author Frederic Guillot
+ */
+class ResponseParser
+{
+ /**
+ * Payload
+ *
+ * @access private
+ * @var mixed
+ */
+ private $payload;
+
+ /**
+ * Do not immediately throw an exception on error. Return it instead.
+ *
+ * @var bool
+ */
+ private $returnException = false;
+
+ /**
+ * Get new object instance
+ *
+ * @static
+ * @access public
+ * @return ResponseParser
+ */
+ public static function create()
+ {
+ return new static();
+ }
+
+ /**
+ * Set Return Exception Or Throw It
+ *
+ * @param $returnException
+ * @return ResponseParser
+ */
+ public function withReturnException($returnException)
+ {
+ $this->returnException = $returnException;
+ return $this;
+ }
+
+ /**
+ * Set payload
+ *
+ * @access public
+ * @param mixed $payload
+ * @return $this
+ */
+ public function withPayload($payload)
+ {
+ $this->payload = $payload;
+ return $this;
+ }
+
+ /**
+ * Parse response
+ *
+ * @return array|Exception|null
+ * @throws InvalidJsonFormatException
+ * @throws BadFunctionCallException
+ * @throws InvalidJsonRpcFormatException
+ * @throws InvalidArgumentException
+ * @throws Exception
+ * @throws ResponseException
+ */
+ public function parse()
+ {
+ JsonFormatValidator::validate($this->payload);
+
+ if ($this->isBatchResponse()) {
+ $results = array();
+
+ foreach ($this->payload as $response) {
+ $results[] = self::create()
+ ->withReturnException($this->returnException)
+ ->withPayload($response)
+ ->parse();
+ }
+
+ return $results;
+ }
+
+ if (isset($this->payload['error']['code'])) {
+ try {
+ $this->handleExceptions();
+ } catch (Exception $e) {
+ if ($this->returnException) {
+ return $e;
+ }
+ throw $e;
+ }
+ }
+
+ return isset($this->payload['result']) ? $this->payload['result'] : null;
+ }
+
+ /**
+ * Handle exceptions
+ *
+ * @access private
+ * @throws InvalidJsonFormatException
+ * @throws InvalidJsonRpcFormatException
+ * @throws ResponseException
+ */
+ private function handleExceptions()
+ {
+ switch ($this->payload['error']['code']) {
+ case -32700:
+ throw new InvalidJsonFormatException('Parse error: '.$this->payload['error']['message']);
+ case -32600:
+ throw new InvalidJsonRpcFormatException('Invalid Request: '.$this->payload['error']['message']);
+ case -32601:
+ throw new BadFunctionCallException('Procedure not found: '.$this->payload['error']['message']);
+ case -32602:
+ throw new InvalidArgumentException('Invalid arguments: '.$this->payload['error']['message']);
+ default:
+ throw new ResponseException(
+ $this->payload['error']['message'],
+ $this->payload['error']['code'],
+ null,
+ isset($this->payload['error']['data']) ? $this->payload['error']['data'] : null
+ );
+ }
+ }
+
+ /**
+ * Return true if we have a batch response
+ *
+ * @access private
+ * @return boolean
+ */
+ private function isBatchResponse()
+ {
+ return array_keys($this->payload) === range(0, count($this->payload) - 1);
+ }
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Server.php b/vendor/fguillot/json-rpc/src/JsonRPC/Server.php
new file mode 100644
index 00000000..1ed075a4
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Server.php
@@ -0,0 +1,386 @@
+<?php
+
+namespace JsonRPC;
+
+use Closure;
+use Exception;
+use JsonRPC\Request\BatchRequestParser;
+use JsonRPC\Request\RequestParser;
+use JsonRPC\Response\ResponseBuilder;
+use JsonRPC\Validator\HostValidator;
+use JsonRPC\Validator\JsonFormatValidator;
+use JsonRPC\Validator\UserValidator;
+
+/**
+ * JsonRPC server class
+ *
+ * @package JsonRPC
+ * @author Frederic Guillot
+ */
+class Server
+{
+ /**
+ * Allowed hosts
+ *
+ * @access protected
+ * @var array
+ */
+ protected $hosts = array();
+
+ /**
+ * Data received from the client
+ *
+ * @access protected
+ * @var array
+ */
+ protected $payload = array();
+
+ /**
+ * List of exceptions that should not be relayed to the client
+ *
+ * @access protected
+ * @var array()
+ */
+ protected $localExceptions = array();
+
+ /**
+ * Username
+ *
+ * @access protected
+ * @var string
+ */
+ protected $username = '';
+
+ /**
+ * Password
+ *
+ * @access protected
+ * @var string
+ */
+ protected $password = '';
+
+ /**
+ * Allowed users
+ *
+ * @access protected
+ * @var array
+ */
+ protected $users = array();
+
+ /**
+ * $_SERVER
+ *
+ * @access protected
+ * @var array
+ */
+ protected $serverVariable;
+
+ /**
+ * ProcedureHandler object
+ *
+ * @access protected
+ * @var ProcedureHandler
+ */
+ protected $procedureHandler;
+
+ /**
+ * MiddlewareHandler object
+ *
+ * @access protected
+ * @var MiddlewareHandler
+ */
+ protected $middlewareHandler;
+
+ /**
+ * Response builder
+ *
+ * @access protected
+ * @var ResponseBuilder
+ */
+ protected $responseBuilder;
+
+ /**
+ * Response builder
+ *
+ * @access protected
+ * @var RequestParser
+ */
+ protected $requestParser;
+
+ /**
+ *
+ * Batch request parser
+ *
+ * @access protected
+ * @var BatchRequestParser
+ */
+ protected $batchRequestParser;
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param string $request
+ * @param array $server
+ * @param ResponseBuilder $responseBuilder
+ * @param RequestParser $requestParser
+ * @param BatchRequestParser $batchRequestParser
+ * @param ProcedureHandler $procedureHandler
+ * @param MiddlewareHandler $middlewareHandler
+ */
+ public function __construct(
+ $request = '',
+ array $server = array(),
+ ResponseBuilder $responseBuilder = null,
+ RequestParser $requestParser = null,
+ BatchRequestParser $batchRequestParser = null,
+ ProcedureHandler $procedureHandler = null,
+ MiddlewareHandler $middlewareHandler = null
+ ) {
+ if ($request !== '') {
+ $this->payload = json_decode($request, true);
+ } else {
+ $this->payload = json_decode(file_get_contents('php://input'), true);
+ }
+
+ $this->serverVariable = $server ?: $_SERVER;
+ $this->responseBuilder = $responseBuilder ?: ResponseBuilder::create();
+ $this->requestParser = $requestParser ?: RequestParser::create();
+ $this->batchRequestParser = $batchRequestParser ?: BatchRequestParser::create();
+ $this->procedureHandler = $procedureHandler ?: new ProcedureHandler();
+ $this->middlewareHandler = $middlewareHandler ?: new MiddlewareHandler();
+ }
+
+ /**
+ * Define alternative authentication header
+ *
+ * @access public
+ * @param string $header Header name
+ * @return $this
+ */
+ public function setAuthenticationHeader($header)
+ {
+ if (! empty($header)) {
+ $header = 'HTTP_'.str_replace('-', '_', strtoupper($header));
+ $value = $this->getServerVariable($header);
+
+ if (! empty($value)) {
+ list($this->username, $this->password) = explode(':', base64_decode($value));
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get ProcedureHandler
+ *
+ * @access public
+ * @return ProcedureHandler
+ */
+ public function getProcedureHandler()
+ {
+ return $this->procedureHandler;
+ }
+
+ /**
+ * Get MiddlewareHandler
+ *
+ * @access public
+ * @return MiddlewareHandler
+ */
+ public function getMiddlewareHandler()
+ {
+ return $this->middlewareHandler;
+ }
+
+ /**
+ * Get username
+ *
+ * @access public
+ * @return string
+ */
+ public function getUsername()
+ {
+ return $this->username ?: $this->getServerVariable('PHP_AUTH_USER');
+ }
+
+ /**
+ * Get password
+ *
+ * @access public
+ * @return string
+ */
+ public function getPassword()
+ {
+ return $this->password ?: $this->getServerVariable('PHP_AUTH_PW');
+ }
+
+ /**
+ * IP based client restrictions
+ *
+ * @access public
+ * @param array $hosts List of hosts
+ * @return $this
+ */
+ public function allowHosts(array $hosts)
+ {
+ $this->hosts = $hosts;
+ return $this;
+ }
+
+ /**
+ * HTTP Basic authentication
+ *
+ * @access public
+ * @param array $users Dictionary of username/password
+ * @return $this
+ */
+ public function authentication(array $users)
+ {
+ $this->users = $users;
+ return $this;
+ }
+
+ /**
+ * Register a new procedure
+ *
+ * @access public
+ * @deprecated Use $server->getProcedureHandler()->withCallback($procedure, $callback)
+ * @param string $procedure Procedure name
+ * @param closure $callback Callback
+ * @return $this
+ */
+ public function register($procedure, Closure $callback)
+ {
+ $this->procedureHandler->withCallback($procedure, $callback);
+ return $this;
+ }
+
+ /**
+ * Bind a procedure to a class
+ *
+ * @access public
+ * @deprecated Use $server->getProcedureHandler()->withClassAndMethod($procedure, $class, $method);
+ * @param string $procedure Procedure name
+ * @param mixed $class Class name or instance
+ * @param string $method Procedure name
+ * @return $this
+ */
+ public function bind($procedure, $class, $method = '')
+ {
+ $this->procedureHandler->withClassAndMethod($procedure, $class, $method);
+ return $this;
+ }
+
+ /**
+ * Bind a class instance
+ *
+ * @access public
+ * @deprecated Use $server->getProcedureHandler()->withObject($instance);
+ * @param mixed $instance Instance name
+ * @return $this
+ */
+ public function attach($instance)
+ {
+ $this->procedureHandler->withObject($instance);
+ return $this;
+ }
+
+ /**
+ * Exception classes that should not be relayed to the client
+ *
+ * @access public
+ * @param Exception|string $exception
+ * @return $this
+ */
+ public function withLocalException($exception)
+ {
+ $this->localExceptions[] = $exception;
+ return $this;
+ }
+
+ /**
+ * Parse incoming requests
+ *
+ * @access public
+ * @return string
+ */
+ public function execute()
+ {
+ try {
+ JsonFormatValidator::validate($this->payload);
+ HostValidator::validate($this->hosts, $this->getServerVariable('REMOTE_ADDR'));
+ UserValidator::validate($this->users, $this->getUsername(), $this->getPassword());
+
+ $this->middlewareHandler
+ ->withUsername($this->getUsername())
+ ->withPassword($this->getPassword())
+ ;
+
+ $response = $this->parseRequest();
+
+ } catch (Exception $e) {
+ $response = $this->handleExceptions($e);
+ }
+
+ $this->responseBuilder->sendHeaders();
+ return $response;
+ }
+
+ /**
+ * Handle exceptions
+ *
+ * @access protected
+ * @param Exception $e
+ * @return string
+ * @throws Exception
+ */
+ protected function handleExceptions(Exception $e)
+ {
+ foreach ($this->localExceptions as $exception) {
+ if ($e instanceof $exception) {
+ throw $e;
+ }
+ }
+
+ return $this->responseBuilder->withException($e)->build();
+ }
+
+ /**
+ * Parse incoming request
+ *
+ * @access protected
+ * @return string
+ */
+ protected function parseRequest()
+ {
+ if (BatchRequestParser::isBatchRequest($this->payload)) {
+ return $this->batchRequestParser
+ ->withPayload($this->payload)
+ ->withProcedureHandler($this->procedureHandler)
+ ->withMiddlewareHandler($this->middlewareHandler)
+ ->withLocalException($this->localExceptions)
+ ->parse();
+ }
+
+ return $this->requestParser
+ ->withPayload($this->payload)
+ ->withProcedureHandler($this->procedureHandler)
+ ->withMiddlewareHandler($this->middlewareHandler)
+ ->withLocalException($this->localExceptions)
+ ->parse();
+ }
+
+ /**
+ * Check existence and get value of server variable
+ *
+ * @access protected
+ * @param string $variable
+ * @return string|null
+ */
+ protected function getServerVariable($variable)
+ {
+ return isset($this->serverVariable[$variable]) ? $this->serverVariable[$variable] : null;
+ }
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Validator/HostValidator.php b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/HostValidator.php
new file mode 100644
index 00000000..7f8c0a04
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/HostValidator.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace JsonRPC\Validator;
+
+use JsonRPC\Exception\AccessDeniedException;
+
+/**
+ * Class HostValidator
+ *
+ * @package JsonRPC\Validator
+ * @author Frederic Guillot
+ */
+class HostValidator
+{
+ /**
+ * Validate
+ *
+ * @static
+ * @access public
+ * @param array $hosts
+ * @param string $remoteAddress
+ * @throws AccessDeniedException
+ */
+ public static function validate(array $hosts, $remoteAddress)
+ {
+ if (! empty($hosts) && ! in_array($remoteAddress, $hosts)) {
+ throw new AccessDeniedException('Access Forbidden');
+ }
+ }
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Validator/JsonEncodingValidator.php b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/JsonEncodingValidator.php
new file mode 100644
index 00000000..0bbc4abd
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/JsonEncodingValidator.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace JsonRPC\Validator;
+
+use JsonRPC\Exception\ResponseEncodingFailureException;
+
+/**
+ * Class JsonEncodingValidator
+ *
+ * @package JsonRPC\Validator
+ * @author Frederic Guillot
+ */
+class JsonEncodingValidator
+{
+ public static function validate()
+ {
+ $jsonError = json_last_error();
+
+ if ($jsonError !== JSON_ERROR_NONE) {
+ switch ($jsonError) {
+ case JSON_ERROR_DEPTH:
+ $errorMessage = 'Maximum stack depth exceeded';
+ break;
+ case JSON_ERROR_STATE_MISMATCH:
+ $errorMessage = 'Underflow or the modes mismatch';
+ break;
+ case JSON_ERROR_CTRL_CHAR:
+ $errorMessage = 'Unexpected control character found';
+ break;
+ case JSON_ERROR_SYNTAX:
+ $errorMessage = 'Syntax error, malformed JSON';
+ break;
+ case JSON_ERROR_UTF8:
+ $errorMessage = 'Malformed UTF-8 characters, possibly incorrectly encoded';
+ break;
+ default:
+ $errorMessage = 'Unknown error';
+ break;
+ }
+
+ throw new ResponseEncodingFailureException($errorMessage, $jsonError);
+ }
+ }
+}
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Validator/JsonFormatValidator.php b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/JsonFormatValidator.php
new file mode 100644
index 00000000..ca8e7a69
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/JsonFormatValidator.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace JsonRPC\Validator;
+
+use JsonRPC\Exception\InvalidJsonFormatException;
+
+/**
+ * Class JsonFormatValidator
+ *
+ * @package JsonRPC\Validator
+ * @author Frederic Guillot
+ */
+class JsonFormatValidator
+{
+ /**
+ * Validate
+ *
+ * @static
+ * @access public
+ * @param mixed $payload
+ * @throws InvalidJsonFormatException
+ */
+ public static function validate($payload)
+ {
+ if (! is_array($payload)) {
+ throw new InvalidJsonFormatException('Malformed payload');
+ }
+ }
+}
+
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Validator/RpcFormatValidator.php b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/RpcFormatValidator.php
new file mode 100644
index 00000000..f253a5a1
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/RpcFormatValidator.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace JsonRPC\Validator;
+
+use JsonRPC\Exception\InvalidJsonRpcFormatException;
+
+/**
+ * Class RpcFormatValidator
+ *
+ * @package JsonRPC\Validator
+ * @author Frederic Guillot
+ */
+class RpcFormatValidator
+{
+ /**
+ * Validate
+ *
+ * @static
+ * @access public
+ * @param array $payload
+ * @throws InvalidJsonRpcFormatException
+ */
+ public static function validate(array $payload)
+ {
+ if (! isset($payload['jsonrpc']) ||
+ ! isset($payload['method']) ||
+ ! is_string($payload['method']) ||
+ $payload['jsonrpc'] !== '2.0' ||
+ (isset($payload['params']) && ! is_array($payload['params']))) {
+
+ throw new InvalidJsonRpcFormatException('Invalid JSON RPC payload');
+ }
+ }
+}
+
diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Validator/UserValidator.php b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/UserValidator.php
new file mode 100644
index 00000000..4f889719
--- /dev/null
+++ b/vendor/fguillot/json-rpc/src/JsonRPC/Validator/UserValidator.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace JsonRPC\Validator;
+
+use JsonRPC\Exception\AuthenticationFailureException;
+
+/**
+ * Class UserValidator
+ *
+ * @package JsonRPC\Validator
+ * @author Frederic Guillot
+ */
+class UserValidator
+{
+ public static function validate(array $users, $username, $password)
+ {
+ if (! empty($users) && (! isset($users[$username]) || $users[$username] !== $password)) {
+ throw new AuthenticationFailureException('Access not allowed');
+ }
+ }
+}
diff --git a/vendor/fguillot/picodb/LICENSE b/vendor/fguillot/picodb/LICENSE
new file mode 100644
index 00000000..6a362bc1
--- /dev/null
+++ b/vendor/fguillot/picodb/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Frederic Guillot
+
+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/fguillot/picodb/lib/PicoDb/Builder/BaseBuilder.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/BaseBuilder.php
new file mode 100644
index 00000000..e075ae3c
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/BaseBuilder.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace PicoDb\Builder;
+
+use PicoDb\Database;
+
+/**
+ * Class InsertBuilder
+ *
+ * @package PicoDb\Builder
+ * @author Frederic Guillot
+ */
+abstract class BaseBuilder
+{
+ /**
+ * @var Database
+ */
+ protected $db;
+
+ /**
+ * @var ConditionBuilder
+ */
+ protected $conditionBuilder;
+
+ /**
+ * @var string
+ */
+ protected $table = '';
+
+ /**
+ * @var string[]
+ */
+ protected $columns = array();
+
+ /**
+ * InsertBuilder constructor
+ *
+ * @param Database $db
+ * @param ConditionBuilder $condition
+ */
+ public function __construct(Database $db, ConditionBuilder $condition)
+ {
+ $this->db = $db;
+ $this->conditionBuilder = $condition;
+ }
+
+ /**
+ * Get object instance
+ *
+ * @static
+ * @access public
+ * @param Database $db
+ * @param ConditionBuilder $condition
+ * @return static
+ */
+ public static function getInstance(Database $db, ConditionBuilder $condition)
+ {
+ return new static($db, $condition);
+ }
+
+ /**
+ * Set table name
+ *
+ * @access public
+ * @param string $table
+ * @return $this
+ */
+ public function withTable($table)
+ {
+ $this->table = $table;
+ return $this;
+ }
+
+ /**
+ * Set columns name
+ *
+ * @access public
+ * @param string[] $columns
+ * @return $this
+ */
+ public function withColumns(array $columns)
+ {
+ $this->columns = $columns;
+ return $this;
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php
new file mode 100644
index 00000000..b0465b6e
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/ConditionBuilder.php
@@ -0,0 +1,377 @@
+<?php
+
+namespace PicoDb\Builder;
+
+use PicoDb\Database;
+use PicoDb\Table;
+
+/**
+ * Handle SQL conditions
+ *
+ * @package PicoDb\Builder
+ * @author Frederic Guillot
+ */
+class ConditionBuilder
+{
+ /**
+ * Database instance
+ *
+ * @access private
+ * @var Database
+ */
+ private $db;
+
+ /**
+ * Condition values
+ *
+ * @access private
+ * @var array
+ */
+ private $values = array();
+
+ /**
+ * SQL AND conditions
+ *
+ * @access private
+ * @var string[]
+ */
+ private $conditions = array();
+
+ /**
+ * SQL OR conditions
+ *
+ * @access private
+ * @var OrConditionBuilder[]
+ */
+ private $orConditions = array();
+
+ /**
+ * SQL condition offset
+ *
+ * @access private
+ * @var int
+ */
+ private $orConditionOffset = 0;
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param Database $db
+ */
+ public function __construct(Database $db)
+ {
+ $this->db = $db;
+ }
+
+ /**
+ * Build the SQL condition
+ *
+ * @access public
+ * @return string
+ */
+ public function build()
+ {
+ return empty($this->conditions) ? '' : ' WHERE '.implode(' AND ', $this->conditions);
+ }
+
+ /**
+ * Get condition values
+ *
+ * @access public
+ * @return array
+ */
+ public function getValues()
+ {
+ return $this->values;
+ }
+
+ /**
+ * Returns true if there is some conditions
+ *
+ * @access public
+ * @return boolean
+ */
+ public function hasCondition()
+ {
+ return ! empty($this->conditions);
+ }
+
+ /**
+ * Add custom condition
+ *
+ * @access public
+ * @param string $sql
+ */
+ public function addCondition($sql)
+ {
+ if ($this->orConditionOffset > 0) {
+ $this->orConditions[$this->orConditionOffset]->withCondition($sql);
+ }
+ else {
+ $this->conditions[] = $sql;
+ }
+ }
+
+ /**
+ * Start OR condition
+ *
+ * @access public
+ */
+ public function beginOr()
+ {
+ $this->orConditionOffset++;
+ $this->orConditions[$this->orConditionOffset] = new OrConditionBuilder();
+ }
+
+ /**
+ * Close OR condition
+ *
+ * @access public
+ */
+ public function closeOr()
+ {
+ $condition = $this->orConditions[$this->orConditionOffset]->build();
+ $this->orConditionOffset--;
+
+ if ($this->orConditionOffset > 0) {
+ $this->orConditions[$this->orConditionOffset]->withCondition($condition);
+ } else {
+ $this->conditions[] = $condition;
+ }
+ }
+
+ /**
+ * Equal condition
+ *
+ * @access public
+ * @param string $column
+ * @param mixed $value
+ */
+ public function eq($column, $value)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' = ?');
+ $this->values[] = $value;
+ }
+
+ /**
+ * Not equal condition
+ *
+ * @access public
+ * @param string $column
+ * @param mixed $value
+ */
+ public function neq($column, $value)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' != ?');
+ $this->values[] = $value;
+ }
+
+ /**
+ * IN condition
+ *
+ * @access public
+ * @param string $column
+ * @param array $values
+ */
+ public function in($column, array $values)
+ {
+ if (! empty($values)) {
+ $this->addCondition($this->db->escapeIdentifier($column).' IN ('.implode(', ', array_fill(0, count($values), '?')).')');
+ $this->values = array_merge($this->values, $values);
+ }
+ }
+
+ /**
+ * IN condition with a subquery
+ *
+ * @access public
+ * @param string $column
+ * @param Table $subquery
+ */
+ public function inSubquery($column, Table $subquery)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' IN ('.$subquery->buildSelectQuery().')');
+ $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues());
+ }
+
+ /**
+ * NOT IN condition
+ *
+ * @access public
+ * @param string $column
+ * @param array $values
+ */
+ public function notIn($column, array $values)
+ {
+ if (! empty($values)) {
+ $this->addCondition($this->db->escapeIdentifier($column).' NOT IN ('.implode(', ', array_fill(0, count($values), '?')).')');
+ $this->values = array_merge($this->values, $values);
+ }
+ }
+
+ /**
+ * NOT IN condition with a subquery
+ *
+ * @access public
+ * @param string $column
+ * @param Table $subquery
+ */
+ public function notInSubquery($column, Table $subquery)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' NOT IN ('.$subquery->buildSelectQuery().')');
+ $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues());
+ }
+
+ /**
+ * LIKE condition
+ *
+ * @access public
+ * @param string $column
+ * @param mixed $value
+ */
+ public function like($column, $value)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' '.$this->db->getDriver()->getOperator('LIKE').' ?');
+ $this->values[] = $value;
+ }
+
+ /**
+ * ILIKE condition
+ *
+ * @access public
+ * @param string $column
+ * @param mixed $value
+ */
+ public function ilike($column, $value)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' '.$this->db->getDriver()->getOperator('ILIKE').' ?');
+ $this->values[] = $value;
+ }
+
+ /**
+ * Greater than condition
+ *
+ * @access public
+ * @param string $column
+ * @param mixed $value
+ */
+ public function gt($column, $value)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' > ?');
+ $this->values[] = $value;
+ }
+
+ /**
+ * Greater than condition with subquery
+ *
+ * @access public
+ * @param string $column
+ * @param Table $subquery
+ */
+ public function gtSubquery($column, Table $subquery)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' > ('.$subquery->buildSelectQuery().')');
+ $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues());
+ }
+
+ /**
+ * Lower than condition
+ *
+ * @access public
+ * @param string $column
+ * @param mixed $value
+ */
+ public function lt($column, $value)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' < ?');
+ $this->values[] = $value;
+ }
+
+ /**
+ * Lower than condition with subquery
+ *
+ * @access public
+ * @param string $column
+ * @param Table $subquery
+ */
+ public function ltSubquery($column, Table $subquery)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' < ('.$subquery->buildSelectQuery().')');
+ $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues());
+ }
+
+ /**
+ * Greater than or equals condition
+ *
+ * @access public
+ * @param string $column
+ * @param mixed $value
+ */
+ public function gte($column, $value)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' >= ?');
+ $this->values[] = $value;
+ }
+
+ /**
+ * Greater than or equal condition with subquery
+ *
+ * @access public
+ * @param string $column
+ * @param Table $subquery
+ */
+ public function gteSubquery($column, Table $subquery)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' >= ('.$subquery->buildSelectQuery().')');
+ $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues());
+ }
+
+ /**
+ * Lower than or equals condition
+ *
+ * @access public
+ * @param string $column
+ * @param mixed $value
+ */
+ public function lte($column, $value)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' <= ?');
+ $this->values[] = $value;
+ }
+
+ /**
+ * Lower than or equal condition with subquery
+ *
+ * @access public
+ * @param string $column
+ * @param Table $subquery
+ */
+ public function lteSubquery($column, Table $subquery)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' <= ('.$subquery->buildSelectQuery().')');
+ $this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues());
+ }
+
+ /**
+ * IS NULL condition
+ *
+ * @access public
+ * @param string $column
+ */
+ public function isNull($column)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' IS NULL');
+ }
+
+ /**
+ * IS NOT NULL condition
+ *
+ * @access public
+ * @param string $column
+ */
+ public function notNull($column)
+ {
+ $this->addCondition($this->db->escapeIdentifier($column).' IS NOT NULL');
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php
new file mode 100644
index 00000000..9d06c405
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/InsertBuilder.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace PicoDb\Builder;
+
+/**
+ * Class InsertBuilder
+ *
+ * @package PicoDb\Builder
+ * @author Frederic Guillot
+ */
+class InsertBuilder extends BaseBuilder
+{
+ /**
+ * Build SQL
+ *
+ * @access public
+ * @return string
+ */
+ public function build()
+ {
+ $columns = array();
+ $placeholders = array();
+
+ foreach ($this->columns as $column) {
+ $columns[] = $this->db->escapeIdentifier($column);
+ $placeholders[] = ':'.$column;
+ }
+
+ return sprintf(
+ 'INSERT INTO %s (%s) VALUES (%s)',
+ $this->db->escapeIdentifier($this->table),
+ implode(', ', $columns),
+ implode(', ', $placeholders)
+ );
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php
new file mode 100644
index 00000000..0defeaf4
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/OrConditionBuilder.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace PicoDb\Builder;
+
+/**
+ * Class OrConditionBuilder
+ *
+ * @package PicoDb\Builder
+ * @author Frederic Guillot
+ */
+class OrConditionBuilder
+{
+ /**
+ * List of SQL conditions
+ *
+ * @access protected
+ * @var string[]
+ */
+ protected $conditions = array();
+
+ /**
+ * Add new condition
+ *
+ * @access public
+ * @param string $condition
+ * @return $this
+ */
+ public function withCondition($condition) {
+ $this->conditions[] = $condition;
+ return $this;
+ }
+
+ /**
+ * Build SQL
+ *
+ * @access public
+ * @return string
+ */
+ public function build()
+ {
+ return '('.implode(' OR ', $this->conditions).')';
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php b/vendor/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php
new file mode 100644
index 00000000..300ea9b0
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Builder/UpdateBuilder.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace PicoDb\Builder;
+
+/**
+ * Class UpdateBuilder
+ *
+ * @package PicoDb\Builder
+ * @author Frederic Guillot
+ */
+class UpdateBuilder extends BaseBuilder
+{
+ /**
+ * @var string[]
+ */
+ protected $sumColumns = array();
+
+ /**
+ * Set columns name
+ *
+ * @access public
+ * @param string[] $columns
+ * @return $this
+ */
+ public function withSumColumns(array $columns)
+ {
+ $this->sumColumns = $columns;
+ return $this;
+ }
+
+ /**
+ * Build SQL
+ *
+ * @access public
+ * @return string
+ */
+ public function build()
+ {
+ $columns = array();
+
+ foreach ($this->columns as $column) {
+ $columns[] = $this->db->escapeIdentifier($column).'=?';
+ }
+
+ foreach ($this->sumColumns as $column) {
+ $columns[] = $this->db->escapeIdentifier($column).'='.$this->db->escapeIdentifier($column).' + ?';
+ }
+
+ return sprintf(
+ 'UPDATE %s SET %s %s',
+ $this->db->escapeIdentifier($this->table),
+ implode(', ', $columns),
+ $this->conditionBuilder->build()
+ );
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Database.php b/vendor/fguillot/picodb/lib/PicoDb/Database.php
new file mode 100644
index 00000000..22c9d2fb
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Database.php
@@ -0,0 +1,370 @@
+<?php
+
+namespace PicoDb;
+
+use Closure;
+use PDOException;
+use LogicException;
+use PicoDb\Driver\Mssql;
+use PicoDb\Driver\Sqlite;
+use PicoDb\Driver\Mysql;
+use PicoDb\Driver\Postgres;
+
+/**
+ * Database
+ *
+ * @package PicoDb
+ * @author Frederic Guillot
+ */
+class Database
+{
+ /**
+ * Database instances
+ *
+ * @static
+ * @access private
+ * @var array
+ */
+ private static $instances = array();
+
+ /**
+ * Statement object
+ *
+ * @access protected
+ * @var StatementHandler
+ */
+ protected $statementHandler;
+
+ /**
+ * Queries logs
+ *
+ * @access private
+ * @var array
+ */
+ private $logs = array();
+
+ /**
+ * Driver instance
+ *
+ * @access private
+ */
+ private $driver;
+
+ /**
+ * Initialize the driver
+ *
+ * @access public
+ * @param array $settings
+ */
+ public function __construct(array $settings = array())
+ {
+ $this->driver = DriverFactory::getDriver($settings);
+ $this->statementHandler = new StatementHandler($this);
+ }
+
+ /**
+ * Destructor
+ *
+ * @access public
+ */
+ public function __destruct()
+ {
+ $this->closeConnection();
+ }
+
+ /**
+ * Register a new database instance
+ *
+ * @static
+ * @access public
+ * @param string $name Instance name
+ * @param Closure $callback Callback
+ */
+ public static function setInstance($name, Closure $callback)
+ {
+ self::$instances[$name] = $callback;
+ }
+
+ /**
+ * Get a database instance
+ *
+ * @static
+ * @access public
+ * @param string $name Instance name
+ * @return Database
+ */
+ public static function getInstance($name)
+ {
+ if (! isset(self::$instances[$name])) {
+ throw new LogicException('No database instance created with that name');
+ }
+
+ if (is_callable(self::$instances[$name])) {
+ self::$instances[$name] = call_user_func(self::$instances[$name]);
+ }
+
+ return self::$instances[$name];
+ }
+
+ /**
+ * Add a log message
+ *
+ * @access public
+ * @param mixed $message
+ * @return Database
+ */
+ public function setLogMessage($message)
+ {
+ $this->logs[] = is_array($message) ? var_export($message, true) : $message;
+ return $this;
+ }
+
+ /**
+ * Add many log messages
+ *
+ * @access public
+ * @param array $messages
+ * @return Database
+ */
+ public function setLogMessages(array $messages)
+ {
+ foreach ($messages as $message) {
+ $this->setLogMessage($message);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get all queries logs
+ *
+ * @access public
+ * @return array
+ */
+ public function getLogMessages()
+ {
+ return $this->logs;
+ }
+
+ /**
+ * Get the PDO connection
+ *
+ * @access public
+ * @return \PDO
+ */
+ public function getConnection()
+ {
+ return $this->driver->getConnection();
+ }
+
+ /**
+ * Get the Driver instance
+ *
+ * @access public
+ * @return Mssql|Sqlite|Postgres|Mysql
+ */
+ public function getDriver()
+ {
+ return $this->driver;
+ }
+
+ /**
+ * Get the last inserted id
+ *
+ * @access public
+ * @return integer
+ */
+ public function getLastId()
+ {
+ return (int) $this->driver->getLastId();
+ }
+
+ /**
+ * Get statement object
+ *
+ * @access public
+ * @return StatementHandler
+ */
+ public function getStatementHandler()
+ {
+ return $this->statementHandler;
+ }
+
+ /**
+ * Release the PDO connection
+ *
+ * @access public
+ */
+ public function closeConnection()
+ {
+ $this->driver->closeConnection();
+ }
+
+ /**
+ * Escape an identifier (column, table name...)
+ *
+ * @access public
+ * @param string $value Value
+ * @param string $table Table name
+ * @return string
+ */
+ public function escapeIdentifier($value, $table = '')
+ {
+ // Do not escape custom query
+ if (strpos($value, '.') !== false || strpos($value, ' ') !== false) {
+ return $value;
+ }
+
+ if (! empty($table)) {
+ return $this->driver->escape($table).'.'.$this->driver->escape($value);
+ }
+
+ return $this->driver->escape($value);
+ }
+
+ /**
+ * Escape an identifier list
+ *
+ * @access public
+ * @param array $identifiers List of identifiers
+ * @param string $table Table name
+ * @return string[]
+ */
+ public function escapeIdentifierList(array $identifiers, $table = '')
+ {
+ foreach ($identifiers as $key => $value) {
+ $identifiers[$key] = $this->escapeIdentifier($value, $table);
+ }
+
+ return $identifiers;
+ }
+
+ /**
+ * Execute a prepared statement
+ *
+ * Note: returns false on duplicate keys instead of SQLException
+ *
+ * @access public
+ * @param string $sql SQL query
+ * @param array $values Values
+ * @return \PDOStatement|false
+ */
+ public function execute($sql, array $values = array())
+ {
+ return $this->statementHandler
+ ->withSql($sql)
+ ->withPositionalParams($values)
+ ->execute();
+ }
+
+ /**
+ * Run a transaction
+ *
+ * @access public
+ * @param Closure $callback Callback
+ * @return mixed
+ */
+ public function transaction(Closure $callback)
+ {
+ try {
+
+ $this->startTransaction();
+ $result = $callback($this);
+ $this->closeTransaction();
+
+ return $result === null ? true : $result;
+ } catch (PDOException $e) {
+ return $this->statementHandler->handleSqlError($e);
+ }
+ }
+
+ /**
+ * Begin a transaction
+ *
+ * @access public
+ */
+ public function startTransaction()
+ {
+ if (! $this->getConnection()->inTransaction()) {
+ $this->getConnection()->beginTransaction();
+ }
+ }
+
+ /**
+ * Commit a transaction
+ *
+ * @access public
+ */
+ public function closeTransaction()
+ {
+ if ($this->getConnection()->inTransaction()) {
+ $this->getConnection()->commit();
+ }
+ }
+
+ /**
+ * Rollback a transaction
+ *
+ * @access public
+ */
+ public function cancelTransaction()
+ {
+ if ($this->getConnection()->inTransaction()) {
+ $this->getConnection()->rollBack();
+ }
+ }
+
+ /**
+ * Get a table object
+ *
+ * @access public
+ * @param string $table
+ * @return Table
+ */
+ public function table($table)
+ {
+ return new Table($this, $table);
+ }
+
+ /**
+ * Get a hashtable object
+ *
+ * @access public
+ * @param string $table
+ * @return Hashtable
+ */
+ public function hashtable($table)
+ {
+ return new Hashtable($this, $table);
+ }
+
+ /**
+ * Get a LOB object
+ *
+ * @access public
+ * @param string $table
+ * @return LargeObject
+ */
+ public function largeObject($table)
+ {
+ return new LargeObject($this, $table);
+ }
+
+ /**
+ * Get a schema object
+ *
+ * @access public
+ * @param string $namespace
+ * @return Schema
+ */
+ public function schema($namespace = null)
+ {
+ $schema = new Schema($this);
+
+ if ($namespace !== null) {
+ $schema->setNamespace($namespace);
+ }
+
+ return $schema;
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php
new file mode 100644
index 00000000..790cd623
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php
@@ -0,0 +1,234 @@
+<?php
+
+namespace PicoDb\Driver;
+
+use PDO;
+use LogicException;
+use PDOException;
+
+/**
+ * Base Driver class
+ *
+ * @package PicoDb\Driver
+ * @author Frederic Guillot
+ */
+abstract class Base
+{
+ /**
+ * List of required settings options
+ *
+ * @access protected
+ * @var array
+ */
+ protected $requiredAttributes = array();
+
+ /**
+ * PDO connection
+ *
+ * @access protected
+ * @var PDO
+ */
+ protected $pdo = null;
+
+ /**
+ * Create a new PDO connection
+ *
+ * @abstract
+ * @access public
+ * @param array $settings
+ */
+ abstract public function createConnection(array $settings);
+
+ /**
+ * Enable foreign keys
+ *
+ * @abstract
+ * @access public
+ */
+ abstract public function enableForeignKeys();
+
+ /**
+ * Disable foreign keys
+ *
+ * @abstract
+ * @access public
+ */
+ abstract public function disableForeignKeys();
+
+ /**
+ * Return true if the error code is a duplicate key
+ *
+ * @abstract
+ * @access public
+ * @param integer $code
+ * @return boolean
+ */
+ abstract public function isDuplicateKeyError($code);
+
+ /**
+ * Escape identifier
+ *
+ * @abstract
+ * @access public
+ * @param string $identifier
+ * @return string
+ */
+ abstract public function escape($identifier);
+
+ /**
+ * Get non standard operator
+ *
+ * @abstract
+ * @access public
+ * @param string $operator
+ * @return string
+ */
+ abstract public function getOperator($operator);
+
+ /**
+ * Get last inserted id
+ *
+ * @abstract
+ * @access public
+ * @return integer
+ */
+ abstract public function getLastId();
+
+ /**
+ * Get current schema version
+ *
+ * @abstract
+ * @access public
+ * @return integer
+ */
+ abstract public function getSchemaVersion();
+
+ /**
+ * Set current schema version
+ *
+ * @abstract
+ * @access public
+ * @param integer $version
+ */
+ abstract public function setSchemaVersion($version);
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param array $settings
+ */
+ public function __construct(array $settings)
+ {
+ foreach ($this->requiredAttributes as $attribute) {
+ if (! isset($settings[$attribute])) {
+ throw new LogicException('This configuration parameter is missing: "'.$attribute.'"');
+ }
+ }
+
+ $this->createConnection($settings);
+ $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ }
+
+ /**
+ * Get the PDO connection
+ *
+ * @access public
+ * @return PDO
+ */
+ public function getConnection()
+ {
+ return $this->pdo;
+ }
+
+ /**
+ * Release the PDO connection
+ *
+ * @access public
+ */
+ public function closeConnection()
+ {
+ $this->pdo = null;
+ }
+
+ /**
+ * Upsert for a key/value variable
+ *
+ * @access public
+ * @param string $table
+ * @param string $keyColumn
+ * @param string $valueColumn
+ * @param array $dictionary
+ * @return bool False on failure
+ */
+ public function upsert($table, $keyColumn, $valueColumn, array $dictionary)
+ {
+ try {
+ $this->pdo->beginTransaction();
+
+ foreach ($dictionary as $key => $value) {
+
+ $rq = $this->pdo->prepare('SELECT 1 FROM '.$this->escape($table).' WHERE '.$this->escape($keyColumn).'=?');
+ $rq->execute(array($key));
+
+ if ($rq->fetchColumn()) {
+ $rq = $this->pdo->prepare('UPDATE '.$this->escape($table).' SET '.$this->escape($valueColumn).'=? WHERE '.$this->escape($keyColumn).'=?');
+ $rq->execute(array($value, $key));
+ }
+ else {
+ $rq = $this->pdo->prepare('INSERT INTO '.$this->escape($table).' ('.$this->escape($keyColumn).', '.$this->escape($valueColumn).') VALUES (?, ?)');
+ $rq->execute(array($key, $value));
+ }
+ }
+
+ $this->pdo->commit();
+
+ return true;
+ }
+ catch (PDOException $e) {
+ $this->pdo->rollBack();
+ return false;
+ }
+ }
+
+ /**
+ * Run EXPLAIN command
+ *
+ * @access public
+ * @param string $sql
+ * @param array $values
+ * @return array
+ */
+ public function explain($sql, array $values)
+ {
+ return $this->getConnection()->query('EXPLAIN '.$this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+ /**
+ * Replace placeholder with values in prepared statement
+ *
+ * @access protected
+ * @param string $sql
+ * @param array $values
+ * @return string
+ */
+ protected function getSqlFromPreparedStatement($sql, array $values)
+ {
+ foreach ($values as $value) {
+ $sql = substr_replace($sql, "'$value'", strpos($sql, '?'), 1);
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Get database version
+ *
+ * @access public
+ * @return array
+ */
+ public function getDatabaseVersion()
+ {
+ return $this->getConnection()->query('SELECT VERSION()')->fetchColumn();
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Mssql.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Mssql.php
new file mode 100644
index 00000000..83e75af2
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Mssql.php
@@ -0,0 +1,178 @@
+<?php
+
+namespace PicoDb\Driver;
+
+use PDO;
+
+/**
+ * Microsoft SQL Server Driver
+ *
+ * @package PicoDb\Driver
+ * @author Algy Taylor <thomas.taylor@cmft.nhs.uk>
+ */
+class Mssql extends Base
+{
+ /**
+ * List of required settings options
+ *
+ * @access protected
+ * @var array
+ */
+ protected $requiredAttributes = array(
+ 'hostname',
+ 'username',
+ 'password',
+ 'database',
+ );
+
+ /**
+ * Table to store the schema version
+ *
+ * @access private
+ * @var array
+ */
+ private $schemaTable = 'schema_version';
+
+ /**
+ * Create a new PDO connection
+ *
+ * @access public
+ * @param array $settings
+ */
+ public function createConnection(array $settings)
+ {
+ $dsn = 'sqlsrv:Server=' . $settings['hostname'] . ';Database=' . $settings['database'];
+
+ if (! empty($settings['port'])) {
+ $dsn .= ';port=' . $settings['port'];
+ }
+
+ $this->pdo = new PDO($dsn, $settings['username'], $settings['password']);
+
+ if (isset($settings['schema_table'])) {
+ $this->schemaTable = $settings['schema_table'];
+ }
+ }
+
+ /**
+ * Enable foreign keys
+ *
+ * @access public
+ */
+ public function enableForeignKeys()
+ {
+ $this->pdo->exec('EXEC sp_MSforeachtable @command1="ALTER TABLE ? CHECK CONSTRAINT ALL"; GO;');
+ }
+
+ /**
+ * Disable foreign keys
+ *
+ * @access public
+ */
+ public function disableForeignKeys()
+ {
+ $this->pdo->exec('EXEC sp_MSforeachtable @command1="ALTER TABLE ? NOCHECK CONSTRAINT ALL"; GO;');
+ }
+
+ /**
+ * Return true if the error code is a duplicate key
+ *
+ * @access public
+ * @param integer $code
+ * @return boolean
+ */
+ public function isDuplicateKeyError($code)
+ {
+ return $code == 2601;
+ }
+
+ /**
+ * Escape identifier
+ *
+ * https://msdn.microsoft.com/en-us/library/ms175874.aspx
+ *
+ * @access public
+ * @param string $identifier
+ * @return string
+ */
+ public function escape($identifier)
+ {
+ return '['.$identifier.']';
+ }
+
+ /**
+ * Get non standard operator
+ *
+ * @access public
+ * @param string $operator
+ * @return string
+ */
+ public function getOperator($operator)
+ {
+ if ($operator === 'LIKE' || $operator === 'ILIKE') {
+ return 'LIKE';
+ }
+
+ return '';
+ }
+
+ /**
+ * Get last inserted id
+ *
+ * @access public
+ * @return integer
+ */
+ public function getLastId()
+ {
+ return $this->pdo->lastInsertId();
+ }
+
+ /**
+ * Get current schema version
+ *
+ * @access public
+ * @return integer
+ */
+ public function getSchemaVersion()
+ {
+ $this->pdo->exec("CREATE TABLE IF NOT EXISTS [".$this->schemaTable."] ([version] INT DEFAULT '0')");
+
+ $rq = $this->pdo->prepare('SELECT [version] FROM ['.$this->schemaTable.']');
+ $rq->execute();
+ $result = $rq->fetchColumn();
+
+ if ($result !== false) {
+ return (int) $result;
+ }
+ else {
+ $this->pdo->exec('INSERT INTO ['.$this->schemaTable.'] VALUES(0)');
+ }
+
+ return 0;
+ }
+
+ /**
+ * Set current schema version
+ *
+ * @access public
+ * @param integer $version
+ */
+ public function setSchemaVersion($version)
+ {
+ $rq = $this->pdo->prepare('UPDATE ['.$this->schemaTable.'] SET [version]=?');
+ $rq->execute(array($version));
+ }
+
+ /**
+ * Run EXPLAIN command
+ *
+ * @param string $sql
+ * @param array $values
+ * @return array
+ */
+ public function explain($sql, array $values)
+ {
+ $this->getConnection()->exec('SET SHOWPLAN_ALL ON');
+ return $this->getConnection()->query($this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC);
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php
new file mode 100644
index 00000000..4f3ca64e
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php
@@ -0,0 +1,252 @@
+<?php
+
+namespace PicoDb\Driver;
+
+use PDO;
+use PDOException;
+
+/**
+ * Mysql Driver
+ *
+ * @package PicoDb\Driver
+ * @author Frederic Guillot
+ */
+class Mysql extends Base
+{
+ /**
+ * List of required settings options
+ *
+ * @access protected
+ * @var array
+ */
+ protected $requiredAttributes = array(
+ 'hostname',
+ 'username',
+ 'password',
+ 'database',
+ );
+
+ /**
+ * Table to store the schema version
+ *
+ * @access private
+ * @var array
+ */
+ private $schemaTable = 'schema_version';
+
+ /**
+ * Create a new PDO connection
+ *
+ * @access public
+ * @param array $settings
+ */
+ public function createConnection(array $settings)
+ {
+ $this->pdo = new PDO(
+ $this->buildDsn($settings),
+ $settings['username'],
+ $settings['password'],
+ $this->buildOptions($settings)
+ );
+
+ if (isset($settings['schema_table'])) {
+ $this->schemaTable = $settings['schema_table'];
+ }
+ }
+
+ /**
+ * Build connection DSN
+ *
+ * @access protected
+ * @param array $settings
+ * @return string
+ */
+ protected function buildDsn(array $settings)
+ {
+ $charset = empty($settings['charset']) ? 'utf8' : $settings['charset'];
+ $dsn = 'mysql:host='.$settings['hostname'].';dbname='.$settings['database'].';charset='.$charset;
+
+ if (! empty($settings['port'])) {
+ $dsn .= ';port='.$settings['port'];
+ }
+
+ return $dsn;
+ }
+
+ /**
+ * Build connection options
+ *
+ * @access protected
+ * @param array $settings
+ * @return array
+ */
+ protected function buildOptions(array $settings)
+ {
+ $options = array(
+ PDO::MYSQL_ATTR_INIT_COMMAND => 'SET sql_mode = STRICT_ALL_TABLES',
+ );
+
+ if (! empty($settings['ssl_key'])) {
+ $options[PDO::MYSQL_ATTR_SSL_KEY] = $settings['ssl_key'];
+ }
+
+ if (! empty($settings['ssl_cert'])) {
+ $options[PDO::MYSQL_ATTR_SSL_CERT] = $settings['ssl_cert'];
+ }
+
+ if (! empty($settings['ssl_ca'])) {
+ $options[PDO::MYSQL_ATTR_SSL_CA] = $settings['ssl_ca'];
+ }
+
+ return $options;
+ }
+
+ /**
+ * Enable foreign keys
+ *
+ * @access public
+ */
+ public function enableForeignKeys()
+ {
+ $this->pdo->exec('SET FOREIGN_KEY_CHECKS=1');
+ }
+
+ /**
+ * Disable foreign keys
+ *
+ * @access public
+ */
+ public function disableForeignKeys()
+ {
+ $this->pdo->exec('SET FOREIGN_KEY_CHECKS=0');
+ }
+
+ /**
+ * Return true if the error code is a duplicate key
+ *
+ * @access public
+ * @param integer $code
+ * @return boolean
+ */
+ public function isDuplicateKeyError($code)
+ {
+ return $code == 23000;
+ }
+
+ /**
+ * Escape identifier
+ *
+ * @access public
+ * @param string $identifier
+ * @return string
+ */
+ public function escape($identifier)
+ {
+ return '`'.$identifier.'`';
+ }
+
+ /**
+ * Get non standard operator
+ *
+ * @access public
+ * @param string $operator
+ * @return string
+ */
+ public function getOperator($operator)
+ {
+ if ($operator === 'LIKE') {
+ return 'LIKE BINARY';
+ }
+ else if ($operator === 'ILIKE') {
+ return 'LIKE';
+ }
+
+ return '';
+ }
+
+ /**
+ * Get last inserted id
+ *
+ * @access public
+ * @return integer
+ */
+ public function getLastId()
+ {
+ return $this->pdo->lastInsertId();
+ }
+
+ /**
+ * Get current schema version
+ *
+ * @access public
+ * @return integer
+ */
+ public function getSchemaVersion()
+ {
+ $this->pdo->exec("CREATE TABLE IF NOT EXISTS `".$this->schemaTable."` (`version` INT DEFAULT '0') ENGINE=InnoDB CHARSET=utf8");
+
+ $rq = $this->pdo->prepare('SELECT `version` FROM `'.$this->schemaTable.'`');
+ $rq->execute();
+ $result = $rq->fetchColumn();
+
+ if ($result !== false) {
+ return (int) $result;
+ }
+ else {
+ $this->pdo->exec('INSERT INTO `'.$this->schemaTable.'` VALUES(0)');
+ }
+
+ return 0;
+ }
+
+ /**
+ * Set current schema version
+ *
+ * @access public
+ * @param integer $version
+ */
+ public function setSchemaVersion($version)
+ {
+ $rq = $this->pdo->prepare('UPDATE `'.$this->schemaTable.'` SET `version`=?');
+ $rq->execute(array($version));
+ }
+
+ /**
+ * Upsert for a key/value variable
+ *
+ * @access public
+ * @param string $table
+ * @param string $keyColumn
+ * @param string $valueColumn
+ * @param array $dictionary
+ * @return bool False on failure
+ */
+ public function upsert($table, $keyColumn, $valueColumn, array $dictionary)
+ {
+ try {
+
+ $sql = sprintf(
+ 'REPLACE INTO %s (%s, %s) VALUES %s',
+ $this->escape($table),
+ $this->escape($keyColumn),
+ $this->escape($valueColumn),
+ implode(', ', array_fill(0, count($dictionary), '(?, ?)'))
+ );
+
+ $values = array();
+
+ foreach ($dictionary as $key => $value) {
+ $values[] = $key;
+ $values[] = $value;
+ }
+
+ $rq = $this->pdo->prepare($sql);
+ $rq->execute($values);
+
+ return true;
+ }
+ catch (PDOException $e) {
+ return false;
+ }
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php
new file mode 100644
index 00000000..a494cdca
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php
@@ -0,0 +1,196 @@
+<?php
+
+namespace PicoDb\Driver;
+
+use PDO;
+use PDOException;
+
+/**
+ * Postgres Driver
+ *
+ * @package PicoDb\Driver
+ * @author Frederic Guillot
+ */
+class Postgres extends Base
+{
+ /**
+ * List of required settings options
+ *
+ * @access protected
+ * @var array
+ */
+ protected $requiredAttributes = array(
+ 'hostname',
+ 'username',
+ 'password',
+ 'database',
+ );
+
+ /**
+ * Table to store the schema version
+ *
+ * @access private
+ * @var array
+ */
+ private $schemaTable = 'schema_version';
+
+ /**
+ * Create a new PDO connection
+ *
+ * @access public
+ * @param array $settings
+ */
+ public function createConnection(array $settings)
+ {
+ $dsn = 'pgsql:host='.$settings['hostname'].';dbname='.$settings['database'];
+
+ if (! empty($settings['port'])) {
+ $dsn .= ';port='.$settings['port'];
+ }
+
+ $this->pdo = new PDO($dsn, $settings['username'], $settings['password']);
+
+ if (isset($settings['schema_table'])) {
+ $this->schemaTable = $settings['schema_table'];
+ }
+ }
+
+ /**
+ * Enable foreign keys
+ *
+ * @access public
+ */
+ public function enableForeignKeys()
+ {
+ }
+
+ /**
+ * Disable foreign keys
+ *
+ * @access public
+ */
+ public function disableForeignKeys()
+ {
+ }
+
+ /**
+ * Return true if the error code is a duplicate key
+ *
+ * @access public
+ * @param integer $code
+ * @return boolean
+ */
+ public function isDuplicateKeyError($code)
+ {
+ return $code == 23505 || $code == 23503;
+ }
+
+ /**
+ * Escape identifier
+ *
+ * @access public
+ * @param string $identifier
+ * @return string
+ */
+ public function escape($identifier)
+ {
+ return '"'.$identifier.'"';
+ }
+
+ /**
+ * Get non standard operator
+ *
+ * @access public
+ * @param string $operator
+ * @return string
+ */
+ public function getOperator($operator)
+ {
+ if ($operator === 'LIKE') {
+ return 'LIKE';
+ }
+ else if ($operator === 'ILIKE') {
+ return 'ILIKE';
+ }
+
+ return '';
+ }
+
+ /**
+ * Get last inserted id
+ *
+ * @access public
+ * @return integer
+ */
+ public function getLastId()
+ {
+ try {
+ $rq = $this->pdo->prepare('SELECT LASTVAL()');
+ $rq->execute();
+
+ return $rq->fetchColumn();
+ }
+ catch (PDOException $e) {
+ return 0;
+ }
+ }
+
+ /**
+ * Get current schema version
+ *
+ * @access public
+ * @return integer
+ */
+ public function getSchemaVersion()
+ {
+ $this->pdo->exec("CREATE TABLE IF NOT EXISTS ".$this->schemaTable." (version INTEGER DEFAULT 0)");
+
+ $rq = $this->pdo->prepare('SELECT "version" FROM "'.$this->schemaTable.'"');
+ $rq->execute();
+ $result = $rq->fetchColumn();
+
+ if ($result !== false) {
+ return (int) $result;
+ }
+ else {
+ $this->pdo->exec('INSERT INTO '.$this->schemaTable.' VALUES(0)');
+ }
+
+ return 0;
+ }
+
+ /**
+ * Set current schema version
+ *
+ * @access public
+ * @param integer $version
+ */
+ public function setSchemaVersion($version)
+ {
+ $rq = $this->pdo->prepare('UPDATE '.$this->schemaTable.' SET version=?');
+ $rq->execute(array($version));
+ }
+
+ /**
+ * Run EXPLAIN command
+ *
+ * @param string $sql
+ * @param array $values
+ * @return array
+ */
+ public function explain($sql, array $values)
+ {
+ return $this->getConnection()->query('EXPLAIN (FORMAT YAML) '.$this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+ /**
+ * Get database version
+ *
+ * @access public
+ * @return array
+ */
+ public function getDatabaseVersion()
+ {
+ return $this->getConnection()->query('SHOW server_version')->fetchColumn();
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php
new file mode 100644
index 00000000..ea39f007
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php
@@ -0,0 +1,193 @@
+<?php
+
+namespace PicoDb\Driver;
+
+use PDO;
+use PDOException;
+
+/**
+ * Sqlite Driver
+ *
+ * @package PicoDb\Driver
+ * @author Frederic Guillot
+ */
+class Sqlite extends Base
+{
+ /**
+ * List of required settings options
+ *
+ * @access protected
+ * @var array
+ */
+ protected $requiredAttributes = array('filename');
+
+ /**
+ * Create a new PDO connection
+ *
+ * @access public
+ * @param array $settings
+ */
+ public function createConnection(array $settings)
+ {
+ $this->pdo = new PDO('sqlite:'.$settings['filename']);
+ $this->enableForeignKeys();
+ }
+
+ /**
+ * Enable foreign keys
+ *
+ * @access public
+ */
+ public function enableForeignKeys()
+ {
+ $this->pdo->exec('PRAGMA foreign_keys = ON');
+ }
+
+ /**
+ * Disable foreign keys
+ *
+ * @access public
+ */
+ public function disableForeignKeys()
+ {
+ $this->pdo->exec('PRAGMA foreign_keys = OFF');
+ }
+
+ /**
+ * Return true if the error code is a duplicate key
+ *
+ * @access public
+ * @param integer $code
+ * @return boolean
+ */
+ public function isDuplicateKeyError($code)
+ {
+ return $code == 23000;
+ }
+
+ /**
+ * Escape identifier
+ *
+ * @access public
+ * @param string $identifier
+ * @return string
+ */
+ public function escape($identifier)
+ {
+ return '"'.$identifier.'"';
+ }
+
+ /**
+ * Get non standard operator
+ *
+ * @access public
+ * @param string $operator
+ * @return string
+ */
+ public function getOperator($operator)
+ {
+ if ($operator === 'LIKE' || $operator === 'ILIKE') {
+ return 'LIKE';
+ }
+
+ return '';
+ }
+
+ /**
+ * Get last inserted id
+ *
+ * @access public
+ * @return integer
+ */
+ public function getLastId()
+ {
+ return $this->pdo->lastInsertId();
+ }
+
+ /**
+ * Get current schema version
+ *
+ * @access public
+ * @return integer
+ */
+ public function getSchemaVersion()
+ {
+ $rq = $this->pdo->prepare('PRAGMA user_version');
+ $rq->execute();
+
+ return (int) $rq->fetchColumn();
+ }
+
+ /**
+ * Set current schema version
+ *
+ * @access public
+ * @param integer $version
+ */
+ public function setSchemaVersion($version)
+ {
+ $this->pdo->exec('PRAGMA user_version='.$version);
+ }
+
+ /**
+ * Upsert for a key/value variable
+ *
+ * @access public
+ * @param string $table
+ * @param string $keyColumn
+ * @param string $valueColumn
+ * @param array $dictionary
+ * @return bool False on failure
+ */
+ public function upsert($table, $keyColumn, $valueColumn, array $dictionary)
+ {
+ try {
+ $this->pdo->beginTransaction();
+
+ foreach ($dictionary as $key => $value) {
+
+ $sql = sprintf(
+ 'INSERT OR REPLACE INTO %s (%s, %s) VALUES (?, ?)',
+ $this->escape($table),
+ $this->escape($keyColumn),
+ $this->escape($valueColumn)
+ );
+
+ $rq = $this->pdo->prepare($sql);
+ $rq->execute(array($key, $value));
+ }
+
+ $this->pdo->commit();
+
+ return true;
+ }
+ catch (PDOException $e) {
+ $this->pdo->rollBack();
+ return false;
+ }
+ }
+
+ /**
+ * Run EXPLAIN command
+ *
+ * @access public
+ * @param string $sql
+ * @param array $values
+ * @return array
+ */
+ public function explain($sql, array $values)
+ {
+ return $this->getConnection()->query('EXPLAIN QUERY PLAN '.$this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+ /**
+ * Get database version
+ *
+ * @access public
+ * @return array
+ */
+ public function getDatabaseVersion()
+ {
+ return $this->getConnection()->query('SELECT sqlite_version()')->fetchColumn();
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/DriverFactory.php b/vendor/fguillot/picodb/lib/PicoDb/DriverFactory.php
new file mode 100644
index 00000000..13151ba7
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/DriverFactory.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace PicoDb;
+
+use LogicException;
+use PicoDb\Driver\Mssql;
+use PicoDb\Driver\Mysql;
+use PicoDb\Driver\Postgres;
+use PicoDb\Driver\Sqlite;
+
+/**
+ * Class DriverFactory
+ *
+ * @package PicoDb
+ * @author Frederic Guillot
+ */
+class DriverFactory
+{
+ /**
+ * Get database driver from settings or environment URL
+ *
+ * @access public
+ * @param array $settings
+ * @return Mssql|Mysql|Postgres|Sqlite
+ */
+ public static function getDriver(array $settings)
+ {
+ if (! isset($settings['driver'])) {
+ throw new LogicException('You must define a database driver');
+ }
+
+ switch ($settings['driver']) {
+ case 'sqlite':
+ return new Sqlite($settings);
+ case 'mssql':
+ return new Mssql($settings);
+ case 'mysql':
+ return new Mysql($settings);
+ case 'postgres':
+ return new Postgres($settings);
+ default:
+ throw new LogicException('This database driver is not supported');
+ }
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Hashtable.php b/vendor/fguillot/picodb/lib/PicoDb/Hashtable.php
new file mode 100644
index 00000000..17afd0e6
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Hashtable.php
@@ -0,0 +1,112 @@
+<?php
+
+namespace PicoDb;
+
+use PDO;
+
+/**
+ * HashTable (key/value)
+ *
+ * @package PicoDb
+ * @author Frederic Guillot
+ * @author Mathias Kresin
+ */
+class Hashtable extends Table
+{
+ /**
+ * Column for the key
+ *
+ * @access private
+ * @var string
+ */
+ private $keyColumn = 'key';
+
+ /**
+ * Column for the value
+ *
+ * @access private
+ * @var string
+ */
+ private $valueColumn = 'value';
+
+ /**
+ * Set the key column
+ *
+ * @access public
+ * @param string $column
+ * @return $this
+ */
+ public function columnKey($column)
+ {
+ $this->keyColumn = $column;
+ return $this;
+ }
+
+ /**
+ * Set the value column
+ *
+ * @access public
+ * @param string $column
+ * @return $this
+ */
+ public function columnValue($column)
+ {
+ $this->valueColumn = $column;
+ return $this;
+ }
+
+ /**
+ * Insert or update
+ *
+ * @access public
+ * @param array $hashmap
+ * @return boolean
+ */
+ public function put(array $hashmap)
+ {
+ return $this->db->getDriver()->upsert($this->getName(), $this->keyColumn, $this->valueColumn, $hashmap);
+ }
+
+ /**
+ * Hashmap result [ [column1 => column2], [], ...]
+ *
+ * @access public
+ * @return array
+ */
+ public function get()
+ {
+ $hashmap = array();
+
+ // setup where condition
+ if (func_num_args() > 0) {
+ $this->in($this->keyColumn, func_get_args());
+ }
+
+ // setup to select columns in case that there are more than two
+ $this->columns($this->keyColumn, $this->valueColumn);
+
+ $rq = $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues());
+ $rows = $rq->fetchAll(PDO::FETCH_NUM);
+
+ foreach ($rows as $row) {
+ $hashmap[$row[0]] = $row[1];
+ }
+
+ return $hashmap;
+ }
+
+ /**
+ * Shortcut method to get a hashmap result
+ *
+ * @access public
+ * @param string $key Key column
+ * @param string $value Value column
+ * @return array
+ */
+ public function getAll($key, $value)
+ {
+ $this->keyColumn = $key;
+ $this->valueColumn = $value;
+ return $this->get();
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/LargeObject.php b/vendor/fguillot/picodb/lib/PicoDb/LargeObject.php
new file mode 100644
index 00000000..ba5e3b92
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/LargeObject.php
@@ -0,0 +1,167 @@
+<?php
+
+namespace PicoDb;
+
+use PDO;
+use PicoDb\Builder\InsertBuilder;
+use PicoDb\Builder\UpdateBuilder;
+
+/**
+ * Handle Large Objects (LOBs)
+ *
+ * @package PicoDb
+ * @author Frederic Guillot
+ */
+class LargeObject extends Table
+{
+ /**
+ * Fetch large object as file descriptor
+ *
+ * This method is not compatible with Sqlite and Mysql (return a string instead of resource)
+ *
+ * @access public
+ * @param string $column
+ * @return resource
+ */
+ public function findOneColumnAsStream($column)
+ {
+ $this->limit(1);
+ $this->columns($column);
+
+ $rq = $this->db->getStatementHandler()
+ ->withSql($this->buildSelectQuery())
+ ->withPositionalParams($this->conditionBuilder->getValues())
+ ->execute();
+
+ $rq->bindColumn($column, $fd, PDO::PARAM_LOB);
+ $rq->fetch(PDO::FETCH_BOUND);
+
+ return $fd;
+ }
+
+ /**
+ * Fetch large object as string
+ *
+ * @access public
+ * @param string $column
+ * @return string
+ */
+ public function findOneColumnAsString($column)
+ {
+ $fd = $this->findOneColumnAsStream($column);
+
+ if (is_string($fd)) {
+ return $fd;
+ }
+
+ return stream_get_contents($fd);
+ }
+
+ /**
+ * Insert large object from stream
+ *
+ * @access public
+ * @param string $blobColumn
+ * @param resource|string $blobDescriptor
+ * @param array $data
+ * @return bool
+ */
+ public function insertFromStream($blobColumn, &$blobDescriptor, array $data = array())
+ {
+ $columns = array_merge(array($blobColumn), array_keys($data));
+ $this->db->startTransaction();
+
+ $result = $this->db->getStatementHandler()
+ ->withSql(InsertBuilder::getInstance($this->db, $this->conditionBuilder)
+ ->withTable($this->name)
+ ->withColumns($columns)
+ ->build()
+ )
+ ->withNamedParams($data)
+ ->withLobParam($blobColumn, $blobDescriptor)
+ ->execute();
+
+ $this->db->closeTransaction();
+
+ return $result !== false;
+ }
+
+ /**
+ * Insert large object from file
+ *
+ * @access public
+ * @param string $blobColumn
+ * @param string $filename
+ * @param array $data
+ * @return bool
+ */
+ public function insertFromFile($blobColumn, $filename, array $data = array())
+ {
+ $fp = fopen($filename, 'rb');
+ $result = $this->insertFromStream($blobColumn, $fp, $data);
+ fclose($fp);
+ return $result;
+ }
+
+ /**
+ * Insert large object from string
+ *
+ * @access public
+ * @param string $blobColumn
+ * @param string $blobData
+ * @param array $data
+ * @return bool
+ */
+ public function insertFromString($blobColumn, &$blobData, array $data = array())
+ {
+ return $this->insertFromStream($blobColumn, $blobData, $data);
+ }
+
+ /**
+ * Update large object from stream
+ *
+ * @access public
+ * @param string $blobColumn
+ * @param resource $blobDescriptor
+ * @param array $data
+ * @return bool
+ */
+ public function updateFromStream($blobColumn, &$blobDescriptor, array $data = array())
+ {
+ $values = array_merge(array_values($data), $this->conditionBuilder->getValues());
+ $columns = array_merge(array($blobColumn), array_keys($data));
+
+ $this->db->startTransaction();
+
+ $result = $this->db->getStatementHandler()
+ ->withSql(UpdateBuilder::getInstance($this->db, $this->conditionBuilder)
+ ->withTable($this->name)
+ ->withColumns($columns)
+ ->build()
+ )
+ ->withPositionalParams($values)
+ ->withLobParam($blobColumn, $blobDescriptor)
+ ->execute();
+
+ $this->db->closeTransaction();
+
+ return $result !== false;
+ }
+
+ /**
+ * Update large object from file
+ *
+ * @access public
+ * @param string $blobColumn
+ * @param string $filename
+ * @param array $data
+ * @return bool
+ */
+ public function updateFromFile($blobColumn, $filename, array $data = array())
+ {
+ $fp = fopen($filename, 'r');
+ $result = $this->updateFromStream($blobColumn, $fp, $data);
+ fclose($fp);
+ return $result;
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/SQLException.php b/vendor/fguillot/picodb/lib/PicoDb/SQLException.php
new file mode 100644
index 00000000..7e570834
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/SQLException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace PicoDb;
+
+use Exception;
+
+/**
+ * SQLException
+ *
+ * @package PicoDb
+ * @author Frederic Guillot
+ */
+class SQLException extends Exception
+{
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Schema.php b/vendor/fguillot/picodb/lib/PicoDb/Schema.php
new file mode 100644
index 00000000..a0280368
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Schema.php
@@ -0,0 +1,119 @@
+<?php
+
+namespace PicoDb;
+
+use PDOException;
+
+/**
+ * Schema migration class
+ *
+ * @package PicoDb
+ * @author Frederic Guillot
+ */
+class Schema
+{
+ /**
+ * Database instance
+ *
+ * @access protected
+ * @var Database
+ */
+ protected $db = null;
+
+ /**
+ * Schema namespace
+ *
+ * @access protected
+ * @var string
+ */
+ protected $namespace = '\Schema';
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param Database $db
+ */
+ public function __construct(Database $db)
+ {
+ $this->db = $db;
+ }
+
+ /**
+ * Set another namespace
+ *
+ * @access public
+ * @param string $namespace
+ * @return Schema
+ */
+ public function setNamespace($namespace)
+ {
+ $this->namespace = $namespace;
+ return $this;
+ }
+
+ /**
+ * Get schema namespace
+ *
+ * @access public
+ * @return string
+ */
+ public function getNamespace()
+ {
+ return $this->namespace;
+ }
+
+ /**
+ * Check the schema version and run the migrations
+ *
+ * @access public
+ * @param integer $last_version
+ * @return boolean
+ */
+ public function check($last_version = 1)
+ {
+ $current_version = $this->db->getDriver()->getSchemaVersion();
+
+ if ($current_version < $last_version) {
+ return $this->migrateTo($current_version, $last_version);
+ }
+
+ return true;
+ }
+
+ /**
+ * Migrate the schema to one version to another
+ *
+ * @access public
+ * @param integer $current_version
+ * @param integer $next_version
+ * @return boolean
+ */
+ public function migrateTo($current_version, $next_version)
+ {
+ try {
+ for ($i = $current_version + 1; $i <= $next_version; $i++) {
+ $this->db->startTransaction();
+ $this->db->getDriver()->disableForeignKeys();
+
+ $function_name = $this->getNamespace().'\version_'.$i;
+
+ if (function_exists($function_name)) {
+ $this->db->setLogMessage('Running migration '.$function_name);
+ call_user_func($function_name, $this->db->getConnection());
+ }
+
+ $this->db->getDriver()->setSchemaVersion($i);
+ $this->db->getDriver()->enableForeignKeys();
+ $this->db->closeTransaction();
+ }
+ } catch (PDOException $e) {
+ $this->db->setLogMessage($e->getMessage());
+ $this->db->cancelTransaction();
+ $this->db->getDriver()->enableForeignKeys();
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/StatementHandler.php b/vendor/fguillot/picodb/lib/PicoDb/StatementHandler.php
new file mode 100644
index 00000000..a7021b36
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/StatementHandler.php
@@ -0,0 +1,353 @@
+<?php
+
+namespace PicoDb;
+
+use PDO;
+use PDOException;
+use PDOStatement;
+
+/**
+ * Statement Handler
+ *
+ * @package PicoDb
+ * @author Frederic Guillot
+ */
+class StatementHandler
+{
+ /**
+ * Database instance
+ *
+ * @access protected
+ * @var Database
+ */
+ protected $db = null;
+
+ /**
+ * Flag to calculate query time
+ *
+ * @access protected
+ * @var boolean
+ */
+ protected $stopwatch = false;
+
+ /**
+ * Start time
+ *
+ * @access protected
+ * @var float
+ */
+ protected $startTime = 0;
+
+ /**
+ * Execution time of all queries
+ *
+ * @access protected
+ * @var float
+ */
+ protected $executionTime = 0;
+
+ /**
+ * Flag to log generated SQL queries
+ *
+ * @access protected
+ * @var boolean
+ */
+ protected $logQueries = false;
+
+ /**
+ * Run explain command on each query
+ *
+ * @access protected
+ * @var boolean
+ */
+ protected $explain = false;
+
+ /**
+ * Number of SQL queries executed
+ *
+ * @access protected
+ * @var integer
+ */
+ protected $nbQueries = 0;
+
+ /**
+ * SQL query
+ *
+ * @access protected
+ * @var string
+ */
+ protected $sql = '';
+
+ /**
+ * Positional SQL parameters
+ *
+ * @access protected
+ * @var array
+ */
+ protected $positionalParams = array();
+
+ /**
+ * Named SQL parameters
+ *
+ * @access protected
+ * @var array
+ */
+ protected $namedParams = array();
+
+ /**
+ * Flag to use named params
+ *
+ * @access protected
+ * @var boolean
+ */
+ protected $useNamedParams = false;
+
+ /**
+ * LOB params
+ *
+ * @access protected
+ * @var array
+ */
+ protected $lobParams = array();
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param Database $db
+ */
+ public function __construct(Database $db)
+ {
+ $this->db = $db;
+ }
+
+ /**
+ * Enable query logging
+ *
+ * @access public
+ * @return $this
+ */
+ public function withLogging()
+ {
+ $this->logQueries = true;
+ return $this;
+ }
+
+ /**
+ * Record query execution time
+ *
+ * @access public
+ * @return $this
+ */
+ public function withStopWatch()
+ {
+ $this->stopwatch = true;
+ return $this;
+ }
+
+ /**
+ * Execute explain command on query
+ *
+ * @access public
+ * @return $this
+ */
+ public function withExplain()
+ {
+ $this->explain = true;
+ return $this;
+ }
+
+ /**
+ * Set SQL query
+ *
+ * @access public
+ * @param string $sql
+ * @return $this
+ */
+ public function withSql($sql)
+ {
+ $this->sql = $sql;
+ return $this;
+ }
+
+ /**
+ * Set positional parameters
+ *
+ * @access public
+ * @param array $params
+ * @return $this
+ */
+ public function withPositionalParams(array $params)
+ {
+ $this->positionalParams = $params;
+ return $this;
+ }
+
+ /**
+ * Set named parameters
+ *
+ * @access public
+ * @param array $params
+ * @return $this
+ */
+ public function withNamedParams(array $params)
+ {
+ $this->namedParams = $params;
+ $this->useNamedParams = true;
+ return $this;
+ }
+
+ /**
+ * Bind large object parameter
+ *
+ * @access public
+ * @param $name
+ * @param $fp
+ * @return $this
+ */
+ public function withLobParam($name, &$fp)
+ {
+ $this->lobParams[$name] =& $fp;
+ return $this;
+ }
+
+ /**
+ * Get number of queries executed
+ *
+ * @access public
+ * @return int
+ */
+ public function getNbQueries()
+ {
+ return $this->nbQueries;
+ }
+
+ /**
+ * Execute a prepared statement
+ *
+ * Note: returns false on duplicate keys instead of SQLException
+ *
+ * @access public
+ * @return PDOStatement|false
+ */
+ public function execute()
+ {
+ try {
+ $this->beforeExecute();
+
+ $pdoStatement = $this->db->getConnection()->prepare($this->sql);
+ $this->bindParams($pdoStatement);
+ $pdoStatement->execute();
+
+ $this->afterExecute();
+ return $pdoStatement;
+ } catch (PDOException $e) {
+ return $this->handleSqlError($e);
+ }
+ }
+
+ /**
+ * Bind parameters to PDOStatement
+ *
+ * @access protected
+ * @param PDOStatement $pdoStatement
+ */
+ protected function bindParams(PDOStatement $pdoStatement)
+ {
+ $i = 1;
+
+ foreach ($this->lobParams as $name => $variable) {
+ if (! $this->useNamedParams) {
+ $parameter = $i;
+ $i++;
+ } else {
+ $parameter = $name;
+ }
+
+ $pdoStatement->bindParam($parameter, $variable, PDO::PARAM_LOB);
+ }
+
+ foreach ($this->positionalParams as $value) {
+ $pdoStatement->bindValue($i, $value, PDO::PARAM_STR);
+ $i++;
+ }
+
+ foreach ($this->namedParams as $name => $value) {
+ $pdoStatement->bindValue($name, $value, PDO::PARAM_STR);
+ }
+ }
+
+ /**
+ * Method executed before query execution
+ *
+ * @access protected
+ */
+ protected function beforeExecute()
+ {
+ if ($this->logQueries) {
+ $this->db->setLogMessage($this->sql);
+ }
+
+ if ($this->stopwatch) {
+ $this->startTime = microtime(true);
+ }
+ }
+
+ /**
+ * Method executed after query execution
+ *
+ * @access protected
+ */
+ protected function afterExecute()
+ {
+ if ($this->stopwatch) {
+ $duration = microtime(true) - $this->startTime;
+ $this->executionTime += $duration;
+ $this->db->setLogMessage('query_duration='.$duration);
+ $this->db->setLogMessage('total_execution_time='.$this->executionTime);
+ }
+
+ if ($this->explain) {
+ $this->db->setLogMessages($this->db->getDriver()->explain($this->sql, $this->positionalParams));
+ }
+
+ $this->nbQueries++;
+ $this->cleanup();
+ }
+
+ /**
+ * Reset internal properties after execution
+ * The same object instance is used
+ *
+ * @access protected
+ */
+ protected function cleanup()
+ {
+ $this->sql = '';
+ $this->useNamedParams = false;
+ $this->positionalParams = array();
+ $this->namedParams = array();
+ $this->lobParams = array();
+ }
+
+ /**
+ * Handle PDOException
+ *
+ * @access public
+ * @param PDOException $e
+ * @return bool
+ * @throws SQLException
+ */
+ public function handleSqlError(PDOException $e)
+ {
+ $this->cleanup();
+ $this->db->cancelTransaction();
+ $this->db->setLogMessage($e->getMessage());
+
+ if ($this->db->getDriver()->isDuplicateKeyError($e->getCode())) {
+ return false;
+ }
+
+ throw new SQLException('SQL error'.($this->logQueries ? ': '.$e->getMessage() : ''));
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/Table.php b/vendor/fguillot/picodb/lib/PicoDb/Table.php
new file mode 100644
index 00000000..09eb928e
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/Table.php
@@ -0,0 +1,721 @@
+<?php
+
+namespace PicoDb;
+
+use PDO;
+use Closure;
+use PicoDb\Builder\ConditionBuilder;
+use PicoDb\Builder\InsertBuilder;
+use PicoDb\Builder\UpdateBuilder;
+
+/**
+ * Table
+ *
+ * @package PicoDb
+ * @author Frederic Guillot
+ *
+ * @method $this addCondition($sql)
+ * @method $this beginOr()
+ * @method $this closeOr()
+ * @method $this eq($column, $value)
+ * @method $this neq($column, $value)
+ * @method $this in($column, array $values)
+ * @method $this inSubquery($column, Table $subquery)
+ * @method $this notIn($column, array $values)
+ * @method $this notInSubquery($column, Table $subquery)
+ * @method $this like($column, $value)
+ * @method $this ilike($column, $value)
+ * @method $this gt($column, $value)
+ * @method $this gtSubquery($column, Table $subquery)
+ * @method $this lt($column, $value)
+ * @method $this ltSubquery($column, Table $subquery)
+ * @method $this gte($column, $value)
+ * @method $this gteSubquery($column, Table $subquery)
+ * @method $this lte($column, $value)
+ * @method $this lteSubquery($column, Table $subquery)
+ * @method $this isNull($column)
+ * @method $this notNull($column)
+ */
+class Table
+{
+ /**
+ * Sorting direction
+ *
+ * @access public
+ * @var string
+ */
+ const SORT_ASC = 'ASC';
+ const SORT_DESC = 'DESC';
+
+ /**
+ * Condition instance
+ *
+ * @access protected
+ * @var ConditionBuilder
+ */
+ protected $conditionBuilder;
+
+ /**
+ * Database instance
+ *
+ * @access protected
+ * @var Database
+ */
+ protected $db;
+
+ /**
+ * Table name
+ *
+ * @access protected
+ * @var string
+ */
+ protected $name = '';
+
+ /**
+ * Columns list for SELECT query
+ *
+ * @access private
+ * @var array
+ */
+ private $columns = array();
+
+ /**
+ * Columns to sum during update
+ *
+ * @access private
+ * @var array
+ */
+ private $sumColumns = array();
+
+ /**
+ * SQL limit
+ *
+ * @access private
+ * @var string
+ */
+ private $sqlLimit = '';
+
+ /**
+ * SQL offset
+ *
+ * @access private
+ * @var string
+ */
+ private $sqlOffset = '';
+
+ /**
+ * SQL order
+ *
+ * @access private
+ * @var string
+ */
+ private $sqlOrder = '';
+
+ /**
+ * SQL custom SELECT value
+ *
+ * @access private
+ * @var string
+ */
+ private $sqlSelect = '';
+
+ /**
+ * SQL joins
+ *
+ * @access private
+ * @var array
+ */
+ private $joins = array();
+
+ /**
+ * Use DISTINCT or not?
+ *
+ * @access private
+ * @var boolean
+ */
+ private $distinct = false;
+
+ /**
+ * Group by those columns
+ *
+ * @access private
+ * @var array
+ */
+ private $groupBy = array();
+
+ /**
+ * Callback for result filtering
+ *
+ * @access private
+ * @var Closure
+ */
+ private $callback = null;
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param Database $db
+ * @param string $name
+ */
+ public function __construct(Database $db, $name)
+ {
+ $this->db = $db;
+ $this->name = $name;
+ $this->conditionBuilder = new ConditionBuilder($db);
+ }
+
+ /**
+ * Return the table name
+ *
+ * @access public
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Return ConditionBuilder object
+ *
+ * @access public
+ * @return ConditionBuilder
+ */
+ public function getConditionBuilder()
+ {
+ return $this->conditionBuilder;
+ }
+
+ /**
+ * Insert or update
+ *
+ * @access public
+ * @param array $data
+ * @return boolean
+ */
+ public function save(array $data)
+ {
+ return $this->conditionBuilder->hasCondition() ? $this->update($data) : $this->insert($data);
+ }
+
+ /**
+ * Update
+ *
+ * @access public
+ * @param array $data
+ * @return boolean
+ */
+ public function update(array $data = array())
+ {
+ $values = array_merge(array_values($data), array_values($this->sumColumns), $this->conditionBuilder->getValues());
+ $sql = UpdateBuilder::getInstance($this->db, $this->conditionBuilder)
+ ->withTable($this->name)
+ ->withColumns(array_keys($data))
+ ->withSumColumns(array_keys($this->sumColumns))
+ ->build();
+
+ return $this->db->execute($sql, $values) !== false;
+ }
+
+ /**
+ * Insert
+ *
+ * @access public
+ * @param array $data
+ * @return boolean
+ */
+ public function insert(array $data)
+ {
+ return $this->db->getStatementHandler()
+ ->withSql(InsertBuilder::getInstance($this->db, $this->conditionBuilder)
+ ->withTable($this->name)
+ ->withColumns(array_keys($data))
+ ->build()
+ )
+ ->withNamedParams($data)
+ ->execute() !== false;
+ }
+
+ /**
+ * Insert a new row and return the ID of the primary key
+ *
+ * @access public
+ * @param array $data
+ * @return bool|int
+ */
+ public function persist(array $data)
+ {
+ if ($this->insert($data)) {
+ return $this->db->getLastId();
+ }
+
+ return false;
+ }
+
+ /**
+ * Remove
+ *
+ * @access public
+ * @return boolean
+ */
+ public function remove()
+ {
+ $sql = sprintf(
+ 'DELETE FROM %s %s',
+ $this->db->escapeIdentifier($this->name),
+ $this->conditionBuilder->build()
+ );
+
+ $result = $this->db->execute($sql, $this->conditionBuilder->getValues());
+ return $result->rowCount() > 0;
+ }
+
+ /**
+ * Fetch all rows
+ *
+ * @access public
+ * @return array
+ */
+ public function findAll()
+ {
+ $rq = $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues());
+ $results = $rq->fetchAll(PDO::FETCH_ASSOC);
+
+ if (is_callable($this->callback) && ! empty($results)) {
+ return call_user_func($this->callback, $results);
+ }
+
+ return $results;
+ }
+
+ /**
+ * Find all with a single column
+ *
+ * @access public
+ * @param string $column
+ * @return mixed
+ */
+ public function findAllByColumn($column)
+ {
+ $this->columns = array($column);
+ $rq = $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues());
+
+ return $rq->fetchAll(PDO::FETCH_COLUMN, 0);
+ }
+
+ /**
+ * Fetch one row
+ *
+ * @access public
+ * @return array|null
+ */
+ public function findOne()
+ {
+ $this->limit(1);
+ $result = $this->findAll();
+
+ return isset($result[0]) ? $result[0] : null;
+ }
+
+ /**
+ * Fetch one column, first row
+ *
+ * @access public
+ * @param string $column
+ * @return string
+ */
+ public function findOneColumn($column)
+ {
+ $this->limit(1);
+ $this->columns = array($column);
+
+ return $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues())->fetchColumn();
+ }
+
+ /**
+ * Build a subquery with an alias
+ *
+ * @access public
+ * @param string $sql
+ * @param string $alias
+ * @return $this
+ */
+ public function subquery($sql, $alias)
+ {
+ $this->columns[] = '('.$sql.') AS '.$this->db->escapeIdentifier($alias);
+ return $this;
+ }
+
+ /**
+ * Exists
+ *
+ * @access public
+ * @return integer
+ */
+ public function exists()
+ {
+ $sql = sprintf(
+ 'SELECT 1 FROM %s '.implode(' ', $this->joins).$this->conditionBuilder->build(),
+ $this->db->escapeIdentifier($this->name)
+ );
+
+ $rq = $this->db->execute($sql, $this->conditionBuilder->getValues());
+ $result = $rq->fetchColumn();
+
+ return $result ? true : false;
+ }
+
+ /**
+ * Count
+ *
+ * @access public
+ * @return integer
+ */
+ public function count()
+ {
+ $sql = sprintf(
+ 'SELECT COUNT(*) FROM %s '.implode(' ', $this->joins).$this->conditionBuilder->build().$this->sqlOrder.$this->sqlLimit.$this->sqlOffset,
+ $this->db->escapeIdentifier($this->name)
+ );
+
+ $rq = $this->db->execute($sql, $this->conditionBuilder->getValues());
+ $result = $rq->fetchColumn();
+
+ return $result ? (int) $result : 0;
+ }
+
+ /**
+ * Sum
+ *
+ * @access public
+ * @param string $column
+ * @return float
+ */
+ public function sum($column)
+ {
+ $sql = sprintf(
+ 'SELECT SUM(%s) FROM %s '.implode(' ', $this->joins).$this->conditionBuilder->build().$this->sqlOrder.$this->sqlLimit.$this->sqlOffset,
+ $this->db->escapeIdentifier($column),
+ $this->db->escapeIdentifier($this->name)
+ );
+
+ $rq = $this->db->execute($sql, $this->conditionBuilder->getValues());
+ $result = $rq->fetchColumn();
+
+ return $result ? (float) $result : 0;
+ }
+
+ /**
+ * Increment column value
+ *
+ * @access public
+ * @param string $column
+ * @param string $value
+ * @return boolean
+ */
+ public function increment($column, $value)
+ {
+ $sql = sprintf(
+ 'UPDATE %s SET %s=%s+%d '.$this->conditionBuilder->build(),
+ $this->db->escapeIdentifier($this->name),
+ $this->db->escapeIdentifier($column),
+ $this->db->escapeIdentifier($column),
+ $value
+ );
+
+ return $this->db->execute($sql, $this->conditionBuilder->getValues()) !== false;
+ }
+
+ /**
+ * Decrement column value
+ *
+ * @access public
+ * @param string $column
+ * @param string $value
+ * @return boolean
+ */
+ public function decrement($column, $value)
+ {
+ $sql = sprintf(
+ 'UPDATE %s SET %s=%s-%d '.$this->conditionBuilder->build(),
+ $this->db->escapeIdentifier($this->name),
+ $this->db->escapeIdentifier($column),
+ $this->db->escapeIdentifier($column),
+ $value
+ );
+
+ return $this->db->execute($sql, $this->conditionBuilder->getValues()) !== false;
+ }
+
+ /**
+ * Left join
+ *
+ * @access public
+ * @param string $table Join table
+ * @param string $foreign_column Foreign key on the join table
+ * @param string $local_column Local column
+ * @param string $local_table Local table
+ * @param string $alias Join table alias
+ * @return $this
+ */
+ public function join($table, $foreign_column, $local_column, $local_table = '', $alias = '')
+ {
+ $this->joins[] = sprintf(
+ 'LEFT JOIN %s ON %s=%s',
+ $this->db->escapeIdentifier($table),
+ $this->db->escapeIdentifier($alias ?: $table).'.'.$this->db->escapeIdentifier($foreign_column),
+ $this->db->escapeIdentifier($local_table ?: $this->name).'.'.$this->db->escapeIdentifier($local_column)
+ );
+
+ return $this;
+ }
+
+ /**
+ * Left join
+ *
+ * @access public
+ * @param string $table1
+ * @param string $alias1
+ * @param string $column1
+ * @param string $table2
+ * @param string $column2
+ * @return $this
+ */
+ public function left($table1, $alias1, $column1, $table2, $column2)
+ {
+ $this->joins[] = sprintf(
+ 'LEFT JOIN %s AS %s ON %s=%s',
+ $this->db->escapeIdentifier($table1),
+ $this->db->escapeIdentifier($alias1),
+ $this->db->escapeIdentifier($alias1).'.'.$this->db->escapeIdentifier($column1),
+ $this->db->escapeIdentifier($table2).'.'.$this->db->escapeIdentifier($column2)
+ );
+
+ return $this;
+ }
+
+ /**
+ * Inner join
+ *
+ * @access public
+ * @param string $table1
+ * @param string $alias1
+ * @param string $column1
+ * @param string $table2
+ * @param string $column2
+ * @return $this
+ */
+ public function inner($table1, $alias1, $column1, $table2, $column2)
+ {
+ $this->joins[] = sprintf(
+ 'JOIN %s AS %s ON %s=%s',
+ $this->db->escapeIdentifier($table1),
+ $this->db->escapeIdentifier($alias1),
+ $this->db->escapeIdentifier($alias1).'.'.$this->db->escapeIdentifier($column1),
+ $this->db->escapeIdentifier($table2).'.'.$this->db->escapeIdentifier($column2)
+ );
+
+ return $this;
+ }
+
+ /**
+ * Order by
+ *
+ * @access public
+ * @param string $column Column name
+ * @param string $order Direction ASC or DESC
+ * @return $this
+ */
+ public function orderBy($column, $order = self::SORT_ASC)
+ {
+ $order = strtoupper($order);
+ $order = $order === self::SORT_ASC || $order === self::SORT_DESC ? $order : self::SORT_ASC;
+
+ if ($this->sqlOrder === '') {
+ $this->sqlOrder = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.$order;
+ }
+ else {
+ $this->sqlOrder .= ', '.$this->db->escapeIdentifier($column).' '.$order;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Ascending sort
+ *
+ * @access public
+ * @param string $column
+ * @return $this
+ */
+ public function asc($column)
+ {
+ $this->orderBy($column, self::SORT_ASC);
+ return $this;
+ }
+
+ /**
+ * Descending sort
+ *
+ * @access public
+ * @param string $column
+ * @return $this
+ */
+ public function desc($column)
+ {
+ $this->orderBy($column, self::SORT_DESC);
+ return $this;
+ }
+
+ /**
+ * Limit
+ *
+ * @access public
+ * @param integer $value
+ * @return $this
+ */
+ public function limit($value)
+ {
+ if (! is_null($value)) {
+ $this->sqlLimit = ' LIMIT '.(int) $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Offset
+ *
+ * @access public
+ * @param integer $value
+ * @return $this
+ */
+ public function offset($value)
+ {
+ if (! is_null($value)) {
+ $this->sqlOffset = ' OFFSET '.(int) $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Group by
+ *
+ * @access public
+ * @return $this
+ */
+ public function groupBy()
+ {
+ $this->groupBy = func_get_args();
+ return $this;
+ }
+
+ /**
+ * Custom select
+ *
+ * @access public
+ * @param string $select
+ * @return $this
+ */
+ public function select($select)
+ {
+ $this->sqlSelect = $select;
+ return $this;
+ }
+
+ /**
+ * Define the columns for the select
+ *
+ * @access public
+ * @return $this
+ */
+ public function columns()
+ {
+ $this->columns = func_get_args();
+ return $this;
+ }
+
+ /**
+ * Sum column
+ *
+ * @access public
+ * @param string $column
+ * @param mixed $value
+ * @return $this
+ */
+ public function sumColumn($column, $value)
+ {
+ $this->sumColumns[$column] = $value;
+ return $this;
+ }
+
+ /**
+ * Distinct
+ *
+ * @access public
+ * @return $this
+ */
+ public function distinct()
+ {
+ $this->columns = func_get_args();
+ $this->distinct = true;
+ return $this;
+ }
+
+ /**
+ * Add callback to alter the resultset
+ *
+ * @access public
+ * @param Closure|array $callback
+ * @return $this
+ */
+ public function callback($callback)
+ {
+ $this->callback = $callback;
+ return $this;
+ }
+
+ /**
+ * Build a select query
+ *
+ * @access public
+ * @return string
+ */
+ public function buildSelectQuery()
+ {
+ if (empty($this->sqlSelect)) {
+ $this->columns = $this->db->escapeIdentifierList($this->columns);
+ $this->sqlSelect = ($this->distinct ? 'DISTINCT ' : '').(empty($this->columns) ? '*' : implode(', ', $this->columns));
+ }
+
+ $this->groupBy = $this->db->escapeIdentifierList($this->groupBy);
+
+ return trim(sprintf(
+ 'SELECT %s FROM %s %s %s %s %s %s %s',
+ $this->sqlSelect,
+ $this->db->escapeIdentifier($this->name),
+ implode(' ', $this->joins),
+ $this->conditionBuilder->build(),
+ empty($this->groupBy) ? '' : 'GROUP BY '.implode(', ', $this->groupBy),
+ $this->sqlOrder,
+ $this->sqlLimit,
+ $this->sqlOffset
+ ));
+ }
+
+ /**
+ * Magic method for sql conditions
+ *
+ * @access public
+ * @param string $name
+ * @param array $arguments
+ * @return $this
+ */
+ public function __call($name, array $arguments)
+ {
+ call_user_func_array(array($this->conditionBuilder, $name), $arguments);
+ return $this;
+ }
+}
diff --git a/vendor/fguillot/picodb/lib/PicoDb/UrlParser.php b/vendor/fguillot/picodb/lib/PicoDb/UrlParser.php
new file mode 100644
index 00000000..d8fcaf00
--- /dev/null
+++ b/vendor/fguillot/picodb/lib/PicoDb/UrlParser.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace PicoDb;
+
+/**
+ * Class UrlParser
+ *
+ * @package PicoDb
+ * @author Frederic Guillot
+ */
+class UrlParser
+{
+ /**
+ * URL
+ *
+ * @access private
+ * @var string
+ */
+ private $url;
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param string $environmentVariable
+ */
+ public function __construct($environmentVariable = 'DATABASE_URL')
+ {
+ $this->url = getenv($environmentVariable);
+ }
+
+ /**
+ * Get object instance
+ *
+ * @access public
+ * @param string $environmentVariable
+ * @return static
+ */
+ public static function getInstance($environmentVariable = 'DATABASE_URL')
+ {
+ return new static($environmentVariable);
+ }
+
+ /**
+ * Return true if the variable is defined
+ *
+ * @access public
+ * @return bool
+ */
+ public function isEnvironmentVariableDefined()
+ {
+ return ! empty($this->url);
+ }
+
+ /**
+ * Get settings from URL
+ *
+ * @access public
+ * @param string $url
+ * @return array
+ */
+ public function getSettings($url = '')
+ {
+ $url = $url ?: $this->url;
+ $components = parse_url($url);
+
+ if ($components === false) {
+ return array();
+ }
+
+ return array(
+ 'driver' => $this->getUrlComponent($components, 'scheme'),
+ 'username' => $this->getUrlComponent($components, 'user'),
+ 'password' => $this->getUrlComponent($components, 'pass'),
+ 'hostname' => $this->getUrlComponent($components, 'host'),
+ 'port' => $this->getUrlComponent($components, 'port'),
+ 'database' => ltrim($this->getUrlComponent($components, 'path'), '/'),
+ );
+ }
+
+ /**
+ * Get URL component
+ *
+ * @access private
+ * @param array $components
+ * @param string $component
+ * @return mixed|null
+ */
+ private function getUrlComponent(array $components, $component)
+ {
+ return ! empty($components[$component]) ? $components[$component] : null;
+ }
+}
diff --git a/vendor/fguillot/simple-queue/LICENSE b/vendor/fguillot/simple-queue/LICENSE
new file mode 100644
index 00000000..a19d63a3
--- /dev/null
+++ b/vendor/fguillot/simple-queue/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Frédéric Guillot
+
+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/fguillot/simple-queue/src/Adapter/AmqpQueueAdapter.php b/vendor/fguillot/simple-queue/src/Adapter/AmqpQueueAdapter.php
new file mode 100644
index 00000000..379dd9b8
--- /dev/null
+++ b/vendor/fguillot/simple-queue/src/Adapter/AmqpQueueAdapter.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace SimpleQueue\Adapter;
+
+use DateTime;
+use PhpAmqpLib\Channel\AMQPChannel;
+use PhpAmqpLib\Message\AMQPMessage;
+use PhpAmqpLib\Wire\AMQPTable;
+use SimpleQueue\Job;
+use SimpleQueue\QueueAdapterInterface;
+
+/**
+ * Class AmqpQueueAdapter
+ *
+ * @package SimpleQueue\Adapter
+ */
+class AmqpQueueAdapter implements QueueAdapterInterface
+{
+ /**
+ * @var AMQPChannel
+ */
+ protected $channel;
+
+ /**
+ * @var string
+ */
+ protected $exchange = '';
+
+ /**
+ * @var string
+ */
+ protected $queue = '';
+
+ /**
+ * AmqpQueueAdapter constructor.
+ *
+ * @param AMQPChannel $channel
+ * @param string $queue
+ * @param string $exchange
+ */
+ public function __construct(AMQPChannel $channel, $queue, $exchange)
+ {
+ $this->channel = $channel;
+ $this->exchange = $exchange;
+ $this->queue = $queue;
+ }
+
+ /**
+ * Send a job
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function push(Job $job)
+ {
+ $message = new AMQPMessage($job->serialize(), array('content_type' => 'text/plain'));
+ $this->channel->basic_publish($message, $this->exchange);
+ return $this;
+ }
+
+ /**
+ * Schedule a job in the future
+ *
+ * @access public
+ * @param Job $job
+ * @param DateTime $dateTime
+ * @return $this
+ */
+ public function schedule(Job $job, DateTime $dateTime)
+ {
+ $now = new DateTime();
+ $when = clone($dateTime);
+ $delay = $when->getTimestamp() - $now->getTimestamp();
+
+ $message = new AMQPMessage($job->serialize(), array('delivery_mode' => 2));
+ $message->set('application_headers', new AMQPTable(array('x-delay' => $delay)));
+
+ $this->channel->basic_publish($message, $this->exchange);
+ return $this;
+ }
+
+ /**
+ * Wait and get job from a queue
+ *
+ * @access public
+ * @return Job|null
+ */
+ public function pull()
+ {
+ $message = null;
+
+ $this->channel->basic_consume($this->queue, 'test', false, false, false, false, function ($msg) use (&$message) {
+ $message = $msg;
+ $message->delivery_info['channel']->basic_cancel($message->delivery_info['consumer_tag']);
+ });
+
+ while (count($this->channel->callbacks)) {
+ $this->channel->wait();
+ }
+
+ if ($message === null) {
+ return null;
+ }
+
+ $job = new Job();
+ $job->setId($message->get('delivery_tag'));
+ $job->unserialize($message->getBody());
+
+ return $job;
+ }
+
+ /**
+ * Acknowledge a job
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function completed(Job $job)
+ {
+ $this->channel->basic_ack($job->getId());
+ return $this;
+ }
+
+ /**
+ * Mark a job as failed
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function failed(Job $job)
+ {
+ $this->channel->basic_nack($job->getId());
+ return $this;
+ }
+}
diff --git a/vendor/fguillot/simple-queue/src/Adapter/AwsSqsQueueAdapter.php b/vendor/fguillot/simple-queue/src/Adapter/AwsSqsQueueAdapter.php
new file mode 100644
index 00000000..01377317
--- /dev/null
+++ b/vendor/fguillot/simple-queue/src/Adapter/AwsSqsQueueAdapter.php
@@ -0,0 +1,150 @@
+<?php
+
+namespace SimpleQueue\Adapter;
+
+use Aws\Sqs\SqsClient;
+use SimpleQueue\Job;
+use SimpleQueue\QueueAdapterInterface;
+use DateTime;
+
+/**
+ * Class AwsSqsQueueAdapter
+ *
+ * @package SimpleQueue\Adapter
+ * @author George Webb <george@webb.uno>
+ */
+class AwsSqsQueueAdapter implements QueueAdapterInterface
+{
+ /**
+ * @var string
+ */
+ private $queueName;
+
+ /**
+ * @var SqsClient
+ */
+ private $sqsClient;
+
+ /**
+ * @var string
+ */
+ private $sqsUrl;
+
+ /**
+ * @var array
+ */
+ private $config;
+
+ /**
+ * AwsSqsQueueAdapter constructor.
+ *
+ * @param string $queueName The name of the SQS queue
+ * @param SqsClient $sqsClient An SQS client
+ * @param array $config Array of config values
+ */
+ public function __construct($queueName, SqsClient $sqsClient, $config = array())
+ {
+ $this->queueName = $queueName;
+ $this->sqsClient = $sqsClient;
+ $this->sqsUrl = $this->sqsClient->getQueueUrl(array('QueueName' => $this->queueName))->get('QueueUrl');
+ $this->config = $config;
+ }
+
+ /**
+ * Send a job
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function push(Job $job)
+ {
+ $this->sqsClient->sendMessage(array(
+ 'QueueUrl' => $this->sqsUrl,
+ 'MessageBody' => $job->serialize()
+ ));
+ return $this;
+ }
+
+ /**
+ * Schedule a job in the future
+ *
+ * @access public
+ * @param Job $job
+ * @param DateTime $dateTime
+ * @return $this
+ */
+ public function schedule(Job $job, DateTime $dateTime)
+ {
+ $now = new DateTime();
+ $when = clone($dateTime);
+ $delay = $when->getTimestamp() - $now->getTimestamp();
+
+ $this->sqsClient->sendMessage(array(
+ 'QueueUrl' => $this->sqsUrl,
+ 'MessageBody' => $job->serialize(),
+ 'VisibilityTimeout' => $delay
+ ));
+
+ return $this;
+ }
+
+ /**
+ * Wait and get job from a queue
+ *
+ * @access public
+ * @return Job|null
+ */
+ public function pull()
+ {
+ $result = $this->sqsClient->receiveMessage(array(
+ 'QueueUrl' => $this->sqsUrl,
+ 'WaitTimeSeconds' => empty($this->config['LongPollingTime']) ? 0 : (int) $this->config['LongPollingTime']
+ ));
+
+ if ($result['Messages'] == null) {
+ return null;
+ }
+
+ $resultMessage = array_pop($result['Messages']);
+
+ $job = new Job();
+ $job->setId($resultMessage['ReceiptHandle']);
+ $job->unserialize($resultMessage['Body']);
+
+ return $job;
+ }
+
+ /**
+ * Acknowledge a job
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function completed(Job $job)
+ {
+ $this->sqsClient->deleteMessage(array(
+ 'QueueUrl' => $this->sqsUrl,
+ 'ReceiptHandle' => $job->getId()
+ ));
+ return $this;
+ }
+
+ /**
+ * Mark a job as failed
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function failed(Job $job)
+ {
+ $this->sqsClient->changeMessageVisibility(array(
+ 'QueueUrl' => $this->sqsUrl,
+ 'ReceiptHandle' => $job->getId(),
+ 'VisibilityTimeout' => 0
+ ));
+ return $this;
+ }
+}
diff --git a/vendor/fguillot/simple-queue/src/Adapter/BeanstalkQueueAdapter.php b/vendor/fguillot/simple-queue/src/Adapter/BeanstalkQueueAdapter.php
new file mode 100644
index 00000000..407f60e2
--- /dev/null
+++ b/vendor/fguillot/simple-queue/src/Adapter/BeanstalkQueueAdapter.php
@@ -0,0 +1,120 @@
+<?php
+
+namespace SimpleQueue\Adapter;
+
+use DateTime;
+use Pheanstalk\Job as BeanstalkJob;
+use Pheanstalk\Pheanstalk;
+use Pheanstalk\PheanstalkInterface;
+use SimpleQueue\Job;
+use SimpleQueue\QueueAdapterInterface;
+
+/**
+ * Class BeanstalkQueueAdapter
+ *
+ * @package SimpleQueue\Adapter
+ */
+class BeanstalkQueueAdapter implements QueueAdapterInterface
+{
+ /**
+ * @var PheanstalkInterface
+ */
+ protected $beanstalk;
+
+ /**
+ * @var string
+ */
+ protected $queueName = '';
+
+ /**
+ * BeanstalkQueueAdapter constructor.
+ *
+ * @param PheanstalkInterface $beanstalk
+ * @param string $queueName
+ */
+ public function __construct(PheanstalkInterface $beanstalk, $queueName)
+ {
+ $this->beanstalk = $beanstalk;
+ $this->queueName = $queueName;
+ }
+
+ /**
+ * Send a job
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function push(Job $job)
+ {
+ $this->beanstalk->putInTube($this->queueName, $job->serialize());
+ return $this;
+ }
+
+ /**
+ * Schedule a job in the future
+ *
+ * @access public
+ * @param Job $job
+ * @param DateTime $dateTime
+ * @return $this
+ */
+ public function schedule(Job $job, DateTime $dateTime)
+ {
+ $now = new DateTime();
+ $when = clone($dateTime);
+ $delay = $when->getTimestamp() - $now->getTimestamp();
+
+ $this->beanstalk->putInTube($this->queueName, $job->serialize(), Pheanstalk::DEFAULT_PRIORITY, $delay);
+ return $this;
+ }
+
+ /**
+ * Wait and get job from a queue
+ *
+ * @access public
+ * @return Job|null
+ */
+ public function pull()
+ {
+ $beanstalkJob = $this->beanstalk->reserveFromTube($this->queueName);
+
+ if ($beanstalkJob === false) {
+ return null;
+ }
+
+ $job = new Job();
+ $job->setId($beanstalkJob->getId());
+ $job->unserialize($beanstalkJob->getData());
+
+ return $job;
+ }
+
+ /**
+ * Acknowledge a job
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function completed(Job $job)
+ {
+ $beanstalkJob = new BeanstalkJob($job->getId(), $job->serialize());
+ $this->beanstalk->delete($beanstalkJob);
+ return $this;
+ }
+
+ /**
+ * Mark a job as failed
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function failed(Job $job)
+ {
+ $beanstalkJob = new BeanstalkJob($job->getId(), $job->serialize());
+ $this->beanstalk->bury($beanstalkJob);
+ return $this;
+ }
+}
diff --git a/vendor/fguillot/simple-queue/src/Adapter/DisqueQueueAdapter.php b/vendor/fguillot/simple-queue/src/Adapter/DisqueQueueAdapter.php
new file mode 100644
index 00000000..047658d7
--- /dev/null
+++ b/vendor/fguillot/simple-queue/src/Adapter/DisqueQueueAdapter.php
@@ -0,0 +1,109 @@
+<?php
+
+namespace SimpleQueue\Adapter;
+
+use DateTime;
+use Disque\Client as DisqueClient;
+use Disque\Queue\Job as DisqueJob;
+use SimpleQueue\Job;
+use SimpleQueue\QueueAdapterInterface;
+
+/**
+ * Class DisqueQueueAdapter
+ *
+ * @package SimpleQueue\Adapter
+ */
+class DisqueQueueAdapter implements QueueAdapterInterface
+{
+ /**
+ * @var DisqueClient
+ */
+ protected $disque;
+
+ /**
+ * @var string
+ */
+ protected $queueName;
+
+ /**
+ * DisqueQueueAdapter constructor.
+ *
+ * @param DisqueClient $disque
+ * @param string $queueName
+ */
+ public function __construct(DisqueClient $disque, $queueName)
+ {
+ $this->disque = $disque;
+ $this->queueName = $queueName;
+ }
+
+ /**
+ * Send a job
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function push(Job $job)
+ {
+ $this->disque->queue($this->queueName)->push(new DisqueJob($job->getBody()));
+ return $this;
+ }
+
+ /**
+ * Schedule a job in the future
+ *
+ * @access public
+ * @param Job $job
+ * @param DateTime $dateTime
+ * @return $this
+ */
+ public function schedule(Job $job, DateTime $dateTime)
+ {
+ $this->disque->queue($this->queueName)->schedule(new DisqueJob($job->serialize()), $dateTime);
+ return $this;
+ }
+
+ /**
+ * Wait and get job from a queue
+ *
+ * @access public
+ * @return Job|null
+ */
+ public function pull()
+ {
+ $disqueJob = $this->disque->queue($this->queueName)->pull();
+
+ if ($disqueJob === null) {
+ return null;
+ }
+
+ return new Job($disqueJob->getBody(), $disqueJob->getId());
+ }
+
+ /**
+ * Acknowledge a job
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function completed(Job $job)
+ {
+ $this->disque->queue($this->queueName)->processed(new DisqueJob($job->getBody(), $job->getId()));
+ return $this;
+ }
+
+ /**
+ * Mark a job as failed
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function failed(Job $job)
+ {
+ $this->disque->queue($this->queueName)->failed(new DisqueJob($job->getBody(), $job->getId()));
+ return $this;
+ }
+}
diff --git a/vendor/fguillot/simple-queue/src/Adapter/MemoryQueueAdapter.php b/vendor/fguillot/simple-queue/src/Adapter/MemoryQueueAdapter.php
new file mode 100644
index 00000000..36375d5b
--- /dev/null
+++ b/vendor/fguillot/simple-queue/src/Adapter/MemoryQueueAdapter.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace SimpleQueue\Adapter;
+
+use DateTime;
+use Exception;
+use SimpleQueue\Exception\NotSupportedException;
+use SimpleQueue\QueueAdapterInterface;
+use SimpleQueue\Job;
+use SplQueue;
+
+/**
+ * Class MemoryAdapter
+ *
+ * @package SimpleQueue\Adapter
+ */
+class MemoryQueueAdapter implements QueueAdapterInterface
+{
+ /**
+ * @var SplQueue
+ */
+ protected $queue;
+
+ /**
+ * MemoryAdapter constructor.
+ */
+ public function __construct()
+ {
+ $this->queue = new SplQueue();
+ }
+
+ /**
+ * Send a job
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function push(Job $job)
+ {
+ $this->queue->enqueue($job->serialize());
+ return $this;
+ }
+
+ /**
+ * Schedule a job in the future
+ *
+ * @access public
+ * @param Job $job
+ * @param DateTime $dateTime
+ * @return bool
+ * @throws NotSupportedException
+ */
+ public function schedule(Job $job, DateTime $dateTime)
+ {
+ throw new NotSupportedException('Job delay is not supported by MemoryQueue');
+ }
+
+ /**
+ * Wait and get job from a queue
+ *
+ * @access public
+ * @return Job|null
+ */
+ public function pull()
+ {
+ try {
+ $job = new Job();
+ $payload = $this->queue->dequeue();
+ return $job->unserialize($payload);
+ } catch (Exception $e) {
+ return null;
+ }
+ }
+
+ /**
+ * Acknowledge a job
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function completed(Job $job)
+ {
+ return $this;
+ }
+
+ /**
+ * Mark a job as failed
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function failed(Job $job)
+ {
+ $this->queue->enqueue($job->serialize());
+ return $this;
+ }
+}
diff --git a/vendor/fguillot/simple-queue/src/Exception/NotSupportedException.php b/vendor/fguillot/simple-queue/src/Exception/NotSupportedException.php
new file mode 100644
index 00000000..36106659
--- /dev/null
+++ b/vendor/fguillot/simple-queue/src/Exception/NotSupportedException.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace SimpleQueue\Exception;
+
+use Exception;
+
+/**
+ * Class NotSupportedException
+ *
+ * @package SimpleQueue\Exception
+ */
+class NotSupportedException extends Exception
+{
+}
diff --git a/vendor/fguillot/simple-queue/src/Job.php b/vendor/fguillot/simple-queue/src/Job.php
new file mode 100644
index 00000000..799bbba8
--- /dev/null
+++ b/vendor/fguillot/simple-queue/src/Job.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace SimpleQueue;
+
+/**
+ * Class Job
+ *
+ * @package SimpleQueue
+ */
+class Job
+{
+ protected $id;
+ protected $body;
+
+ /**
+ * Job constructor.
+ *
+ * @param null $body
+ * @param null $id
+ */
+ public function __construct($body = null, $id = null)
+ {
+ $this->body = $body;
+ $this->id = $id;
+ }
+
+ /**
+ * Unserialize a payload
+ *
+ * @param string $payload
+ * @return $this
+ */
+ public function unserialize($payload)
+ {
+ $this->body = json_decode($payload, true);
+ return $this;
+ }
+
+ /**
+ * Serialize the body
+ *
+ * @return string
+ */
+ public function serialize()
+ {
+ return json_encode($this->body);
+ }
+
+ /**
+ * Set body
+ *
+ * @param mixed $body
+ * @return Job
+ */
+ public function setBody($body)
+ {
+ $this->body = $body;
+ return $this;
+ }
+
+ /**
+ * Get body
+ *
+ * @return mixed
+ */
+ public function getBody()
+ {
+ return $this->body;
+ }
+
+ /**
+ * Set job ID
+ *
+ * @param mixed $jobId
+ * @return Job
+ */
+ public function setId($jobId)
+ {
+ $this->id = $jobId;
+ return $this;
+ }
+
+ /**
+ * Get job ID
+ * @return mixed
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * Execute job
+ */
+ public function execute()
+ {
+ }
+}
diff --git a/vendor/fguillot/simple-queue/src/Queue.php b/vendor/fguillot/simple-queue/src/Queue.php
new file mode 100644
index 00000000..a88b55cb
--- /dev/null
+++ b/vendor/fguillot/simple-queue/src/Queue.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace SimpleQueue;
+
+use DateTime;
+
+/**
+ * Class Queue
+ *
+ * @package SimpleQueue
+ */
+class Queue implements QueueAdapterInterface
+{
+ /**
+ * @var QueueAdapterInterface
+ */
+ protected $queueAdapter;
+
+ /**
+ * Queue constructor.
+ *
+ * @param QueueAdapterInterface $queueAdapter
+ */
+ public function __construct(QueueAdapterInterface $queueAdapter)
+ {
+ $this->queueAdapter = $queueAdapter;
+ }
+
+ /**
+ * Send a job
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function push(Job $job)
+ {
+ $this->queueAdapter->push($job);
+ return $this;
+ }
+
+ /**
+ * Schedule a job in the future
+ *
+ * @access public
+ * @param Job $job
+ * @param DateTime $dateTime
+ * @return $this
+ */
+ public function schedule(Job $job, DateTime $dateTime)
+ {
+ $this->queueAdapter->schedule($job, $dateTime);
+ return $this;
+ }
+
+ /**
+ * Wait and get job from a queue
+ *
+ * @access public
+ * @return Job|null
+ */
+ public function pull()
+ {
+ return $this->queueAdapter->pull();
+ }
+
+ /**
+ * Acknowledge a job
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function completed(Job $job)
+ {
+ $this->queueAdapter->completed($job);
+ return $this;
+ }
+
+ /**
+ * Mark a job as failed
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function failed(Job $job)
+ {
+ $this->queueAdapter->failed($job);
+ return $this;
+ }
+}
diff --git a/vendor/fguillot/simple-queue/src/QueueAdapterInterface.php b/vendor/fguillot/simple-queue/src/QueueAdapterInterface.php
new file mode 100644
index 00000000..9bda3070
--- /dev/null
+++ b/vendor/fguillot/simple-queue/src/QueueAdapterInterface.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace SimpleQueue;
+
+use DateTime;
+
+/**
+ * Interface AdapterInterface
+ *
+ * @package SimpleQueue\Adapter
+ */
+interface QueueAdapterInterface
+{
+ /**
+ * Send a job
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function push(Job $job);
+
+ /**
+ * Schedule a job in the future
+ *
+ * @access public
+ * @param Job $job
+ * @param DateTime $dateTime
+ * @return $this
+ */
+ public function schedule(Job $job, DateTime $dateTime);
+
+ /**
+ * Wait and get job from a queue
+ *
+ * @access public
+ * @return Job|null
+ */
+ public function pull();
+
+ /**
+ * Acknowledge a job
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function completed(Job $job);
+
+ /**
+ * Mark a job as failed
+ *
+ * @access public
+ * @param Job $job
+ * @return $this
+ */
+ public function failed(Job $job);
+}
diff --git a/vendor/fguillot/simple-validator/LICENSE b/vendor/fguillot/simple-validator/LICENSE
new file mode 100644
index 00000000..6a362bc1
--- /dev/null
+++ b/vendor/fguillot/simple-validator/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Frederic Guillot
+
+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/fguillot/simple-validator/src/SimpleValidator/Validator.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validator.php
new file mode 100644
index 00000000..30015dc6
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validator.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace SimpleValidator;
+
+class Validator
+{
+ private $data = array();
+ private $errors = array();
+ private $validators = array();
+
+ public function __construct(array $data, array $validators)
+ {
+ $this->data = $data;
+ $this->validators = $validators;
+ }
+
+ public function execute()
+ {
+ $result = true;
+
+ foreach ($this->validators as $validator) {
+ if (! $validator->execute($this->data)) {
+ $this->addError($validator->getField(), $validator->getErrorMessage());
+ $result = false;
+ }
+ }
+
+ return $result;
+ }
+
+ public function addError($field, $message)
+ {
+ if (! isset($this->errors[$field])) {
+ $this->errors[$field] = array();
+ }
+
+ $this->errors[$field][] = $message;
+ }
+
+ public function getErrors()
+ {
+ return $this->errors;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Alpha.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Alpha.php
new file mode 100644
index 00000000..c29ba481
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Alpha.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+class Alpha extends Base
+{
+ public function execute(array $data)
+ {
+ if ($this->isFieldNotEmpty($data)) {
+ return ctype_alpha($data[$this->field]);
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/AlphaNumeric.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/AlphaNumeric.php
new file mode 100644
index 00000000..8d5000b4
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/AlphaNumeric.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+class AlphaNumeric extends Base
+{
+ public function execute(array $data)
+ {
+ if ($this->isFieldNotEmpty($data)) {
+ return ctype_alnum($data[$this->field]);
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Base.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Base.php
new file mode 100644
index 00000000..8157ae50
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Base.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+abstract class Base
+{
+ protected $field = '';
+ protected $error_message = '';
+ protected $data = array();
+
+ abstract public function execute(array $data);
+
+ public function __construct($field, $error_message)
+ {
+ $this->field = $field;
+ $this->error_message = $error_message;
+ }
+
+ public function getErrorMessage()
+ {
+ return $this->error_message;
+ }
+
+ public function getField()
+ {
+ if (is_array($this->field)) {
+ return $this->field[0];
+ }
+
+ return $this->field;
+ }
+
+ public function isFieldNotEmpty(array $data)
+ {
+ return isset($data[$this->field]) && $data[$this->field] !== '';
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Date.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Date.php
new file mode 100644
index 00000000..4ec4b7fd
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Date.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+use DateTime;
+
+class Date extends Base
+{
+ private $formats = array();
+
+ public function __construct($field, $error_message, array $formats)
+ {
+ parent::__construct($field, $error_message);
+ $this->formats = $formats;
+ }
+
+ public function execute(array $data)
+ {
+ if ($this->isFieldNotEmpty($data)) {
+ foreach ($this->formats as $format) {
+ if ($this->isValidDate($data[$this->field], $format)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ public function isValidDate($value, $format)
+ {
+ $date = DateTime::createFromFormat($format, $value);
+
+ if ($date !== false) {
+ $errors = DateTime::getLastErrors();
+ if ($errors['error_count'] === 0 && $errors['warning_count'] === 0) {
+ return $date->getTimestamp() > 0;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Email.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Email.php
new file mode 100644
index 00000000..f3977042
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Email.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+class Email extends Base
+{
+ public function execute(array $data)
+ {
+ if ($this->isFieldNotEmpty($data)) {
+
+ // I use the same validation method as Firefox
+ // http://hg.mozilla.org/mozilla-central/file/cf5da681d577/content/html/content/src/nsHTMLInputElement.cpp#l3967
+
+ $value = $data[$this->field];
+ $length = strlen($value);
+
+ // If the email address begins with a '@' or ends with a '.',
+ // we know it's invalid.
+ if ($value[0] === '@' || $value[$length - 1] === '.') {
+
+ return false;
+ }
+
+ // Check the username
+ for ($i = 0; $i < $length && $value[$i] !== '@'; ++$i) {
+
+ $c = $value[$i];
+
+ if (! (ctype_alnum($c) || $c === '.' || $c === '!' || $c === '#' || $c === '$' ||
+ $c === '%' || $c === '&' || $c === '\'' || $c === '*' || $c === '+' ||
+ $c === '-' || $c === '/' || $c === '=' || $c === '?' || $c === '^' ||
+ $c === '_' || $c === '`' || $c === '{' || $c === '|' || $c === '}' ||
+ $c === '~')) {
+
+ return false;
+ }
+ }
+
+ // There is no domain name (or it's one-character long),
+ // that's not a valid email address.
+ if (++$i >= $length) return false;
+ if (($i + 1) === $length) return false;
+
+ // The domain name can't begin with a dot.
+ if ($value[$i] === '.') return false;
+
+ // Parsing the domain name.
+ for (; $i < $length; ++$i) {
+
+ $c = $value[$i];
+
+ if ($c === '.') {
+
+ // A dot can't follow a dot.
+ if ($value[$i - 1] === '.') return false;
+ }
+ elseif (! (ctype_alnum($c) || $c === '-')) {
+
+ // The domain characters have to be in this list to be valid.
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Equals.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Equals.php
new file mode 100644
index 00000000..6b69dd80
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Equals.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+class Equals extends Base
+{
+ private $field2;
+
+ public function __construct($field1, $field2, $error_message)
+ {
+ parent::__construct($field1, $error_message);
+ $this->field2 = $field2;
+ }
+
+ public function execute(array $data)
+ {
+ if ($this->isFieldNotEmpty($data)) {
+ if (! isset($data[$this->field2])) {
+ return false;
+ }
+
+ return $data[$this->field] === $data[$this->field2];
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Exists.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Exists.php
new file mode 100644
index 00000000..1998e673
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Exists.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+use PDO;
+
+class Exists extends Base
+{
+ private $pdo;
+ private $key;
+ private $table;
+
+ public function __construct($field, $error_message, PDO $pdo, $table, $key = '')
+ {
+ parent::__construct($field, $error_message);
+
+ $this->pdo = $pdo;
+ $this->table = $table;
+ $this->key = $key;
+ }
+
+
+ public function execute(array $data)
+ {
+ if (! $this->isFieldNotEmpty($data)) {
+ return true;
+ }
+
+ if ($this->key === '') {
+ $this->key = $this->field;
+ }
+
+ $rq = $this->pdo->prepare('SELECT 1 FROM '.$this->table.' WHERE '.$this->key.'=?');
+ $rq->execute(array($data[$this->field]));
+
+ return $rq->fetchColumn() == 1;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/GreaterThan.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/GreaterThan.php
new file mode 100644
index 00000000..6e560319
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/GreaterThan.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+class GreaterThan extends Base
+{
+ private $min;
+
+ public function __construct($field, $error_message, $min)
+ {
+ parent::__construct($field, $error_message);
+ $this->min = $min;
+ }
+
+ public function execute(array $data)
+ {
+ if ($this->isFieldNotEmpty($data)) {
+ return $data[$this->field] > $this->min;
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/InArray.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/InArray.php
new file mode 100644
index 00000000..f2f8c134
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/InArray.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+class InArray extends Base
+{
+ protected $array;
+
+ public function __construct($field, array $array, $error_message)
+ {
+ parent::__construct($field, $error_message);
+ $this->array = $array;
+ }
+
+ public function execute(array $data)
+ {
+ if ($this->isFieldNotEmpty($data)) {
+ return in_array($data[$this->field], $this->array);
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Integer.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Integer.php
new file mode 100644
index 00000000..5afdc1e0
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Integer.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+class Integer extends Base
+{
+ public function execute(array $data)
+ {
+ if ($this->isFieldNotEmpty($data)) {
+ if (is_string($data[$this->field])) {
+
+ if ($data[$this->field][0] === '-') {
+ return ctype_digit(substr($data[$this->field], 1));
+ }
+
+ return ctype_digit($data[$this->field]);
+ }
+ else {
+ return is_int($data[$this->field]);
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Ip.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Ip.php
new file mode 100644
index 00000000..754f4f3e
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Ip.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+class Ip extends Base
+{
+ public function execute(array $data)
+ {
+ if ($this->isFieldNotEmpty($data)) {
+ return filter_var($data[$this->field], FILTER_VALIDATE_IP) !== false;
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Length.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Length.php
new file mode 100644
index 00000000..7ef241c4
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Length.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+class Length extends Base
+{
+ private $min;
+ private $max;
+
+ public function __construct($field, $error_message, $min, $max)
+ {
+ parent::__construct($field, $error_message);
+ $this->min = $min;
+ $this->max = $max;
+ }
+
+ public function execute(array $data)
+ {
+ if ($this->isFieldNotEmpty($data)) {
+ $length = mb_strlen($data[$this->field], 'UTF-8');
+ return $length >= $this->min && $length <= $this->max;
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MaxLength.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MaxLength.php
new file mode 100644
index 00000000..6c4e7771
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MaxLength.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+class MaxLength extends Base
+{
+ private $max;
+
+ public function __construct($field, $error_message, $max)
+ {
+ parent::__construct($field, $error_message);
+ $this->max = $max;
+ }
+
+ public function execute(array $data)
+ {
+ if ($this->isFieldNotEmpty($data)) {
+ $length = mb_strlen($data[$this->field], 'UTF-8');
+ return $length <= $this->max;
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MinLength.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MinLength.php
new file mode 100644
index 00000000..0ac4217a
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/MinLength.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+class MinLength extends Base
+{
+ private $min;
+
+ public function __construct($field, $error_message, $min)
+ {
+ parent::__construct($field, $error_message);
+ $this->min = $min;
+ }
+
+ public function execute(array $data)
+ {
+ if ($this->isFieldNotEmpty($data)) {
+ $length = mb_strlen($data[$this->field], 'UTF-8');
+ return $length >= $this->min;
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEmpty.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEmpty.php
new file mode 100644
index 00000000..bbb14b5b
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEmpty.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+class NotEmpty extends Base
+{
+ public function execute(array $data)
+ {
+ if (array_key_exists($this->field, $data)) {
+ return $data[$this->field] !== null && $data[$this->field] !== '';
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEquals.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEquals.php
new file mode 100644
index 00000000..d1d949ea
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotEquals.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+class NotEquals extends Base
+{
+ private $field2;
+
+ public function __construct($field1, $field2, $error_message)
+ {
+ parent::__construct($field1, $error_message);
+ $this->field2 = $field2;
+ }
+
+ public function execute(array $data)
+ {
+ if ($this->isFieldNotEmpty($data)) {
+
+ if (! isset($data[$this->field2])) {
+ return true;
+ }
+
+ return $data[$this->field] !== $data[$this->field2];
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotInArray.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotInArray.php
new file mode 100644
index 00000000..98974c9c
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/NotInArray.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+class NotInArray extends InArray
+{
+ public function execute(array $data)
+ {
+ if ($this->isFieldNotEmpty($data)) {
+ return ! in_array($data[$this->field], $this->array);
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Numeric.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Numeric.php
new file mode 100644
index 00000000..31226866
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Numeric.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+class Numeric extends Base
+{
+ public function execute(array $data)
+ {
+ if ($this->isFieldNotEmpty($data)) {
+ return is_numeric($data[$this->field]);
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Range.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Range.php
new file mode 100644
index 00000000..065b2b9d
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Range.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+class Range extends Base
+{
+ private $min;
+ private $max;
+
+ public function __construct($field, $error_message, $min, $max)
+ {
+ parent::__construct($field, $error_message);
+
+ $this->min = $min;
+ $this->max = $max;
+ }
+
+ public function execute(array $data)
+ {
+ if ($this->isFieldNotEmpty($data)) {
+
+ if (! is_numeric($data[$this->field])) {
+ return false;
+ }
+
+ if ($data[$this->field] < $this->min || $data[$this->field] > $this->max) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Required.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Required.php
new file mode 100644
index 00000000..f5e65616
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Required.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+class Required extends Base
+{
+ public function execute(array $data)
+ {
+ return $this->isFieldNotEmpty($data);
+ }
+}
diff --git a/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Unique.php b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Unique.php
new file mode 100644
index 00000000..00caeb54
--- /dev/null
+++ b/vendor/fguillot/simple-validator/src/SimpleValidator/Validators/Unique.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+use PDO;
+
+class Unique extends Base
+{
+ private $pdo;
+ private $primary_key;
+ private $table;
+
+ public function __construct($field, $error_message, PDO $pdo, $table, $primary_key = 'id')
+ {
+ parent::__construct($field, $error_message);
+
+ $this->pdo = $pdo;
+ $this->primary_key = $primary_key;
+ $this->table = $table;
+ }
+
+ public function execute(array $data)
+ {
+ if ($this->isFieldNotEmpty($data)) {
+ if (! isset($data[$this->primary_key])) {
+ $rq = $this->pdo->prepare('SELECT 1 FROM '.$this->table.' WHERE '.$this->field.'=?');
+ $rq->execute(array($data[$this->field]));
+ }
+ else {
+
+ $rq = $this->pdo->prepare(
+ 'SELECT 1 FROM '.$this->table.'
+ WHERE '.$this->field.'=? AND '.$this->primary_key.' != ?'
+ );
+
+ $rq->execute(array($data[$this->field], $data[$this->primary_key]));
+ }
+
+ $result = $rq->fetchColumn();
+
+ if ($result == 1) { // Postgresql returns an integer but other database returns a string '1'
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/fguillot/simpleLogger/LICENSE b/vendor/fguillot/simpleLogger/LICENSE
new file mode 100644
index 00000000..6a362bc1
--- /dev/null
+++ b/vendor/fguillot/simpleLogger/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Frederic Guillot
+
+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/fguillot/simpleLogger/src/SimpleLogger/Base.php b/vendor/fguillot/simpleLogger/src/SimpleLogger/Base.php
new file mode 100644
index 00000000..c662a1a3
--- /dev/null
+++ b/vendor/fguillot/simpleLogger/src/SimpleLogger/Base.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace SimpleLogger;
+
+use Psr\Log\AbstractLogger;
+use Psr\Log\LogLevel;
+
+/**
+ * Base class for loggers
+ *
+ * @package SimpleLogger
+ * @author Frédéric Guillot
+ */
+abstract class Base extends AbstractLogger
+{
+ /**
+ * Minimum log level for the logger
+ *
+ * @access private
+ * @var string
+ */
+ private $level = LogLevel::DEBUG;
+
+ /**
+ * Set minimum log level
+ *
+ * @access public
+ * @param string $level
+ */
+ public function setLevel($level)
+ {
+ $this->level = $level;
+ }
+
+ /**
+ * Get minimum log level
+ *
+ * @access public
+ * @return string
+ */
+ public function getLevel()
+ {
+ return $this->level;
+ }
+
+ /**
+ * Dump to log a variable (by example an array)
+ *
+ * @param mixed $variable
+ */
+ public function dump($variable)
+ {
+ $this->log(LogLevel::DEBUG, var_export($variable, true));
+ }
+
+ /**
+ * Interpolates context values into the message placeholders.
+ *
+ * @access protected
+ * @param string $message
+ * @param array $context
+ * @return string
+ */
+ protected function interpolate($message, array $context = array())
+ {
+ // build a replacement array with braces around the context keys
+ $replace = array();
+
+ foreach ($context as $key => $val) {
+ $replace['{' . $key . '}'] = $val;
+ }
+
+ // interpolate replacement values into the message and return
+ return strtr($message, $replace);
+ }
+
+ /**
+ * Format log message
+ *
+ * @param mixed $level
+ * @param string $message
+ * @param array $context
+ * @return string
+ */
+ protected function formatMessage($level, $message, array $context = array())
+ {
+ return '['.date('Y-m-d H:i:s').'] ['.$level.'] '.$this->interpolate($message, $context).PHP_EOL;
+ }
+}
diff --git a/vendor/fguillot/simpleLogger/src/SimpleLogger/File.php b/vendor/fguillot/simpleLogger/src/SimpleLogger/File.php
new file mode 100644
index 00000000..be3bfa85
--- /dev/null
+++ b/vendor/fguillot/simpleLogger/src/SimpleLogger/File.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace SimpleLogger;
+
+use RuntimeException;
+
+/**
+ * File logger
+ *
+ * @package SimpleLogger
+ * @author Frédéric Guillot
+ */
+class File extends Base
+{
+ /**
+ * Filename
+ *
+ * @access protected
+ * @var string
+ */
+ protected $filename = '';
+
+ /**
+ * Setup logger configuration
+ *
+ * @param string $filename Output file
+ */
+ public function __construct($filename)
+ {
+ $this->filename = $filename;
+ }
+
+ /**
+ * Logs with an arbitrary level.
+ *
+ * @param mixed $level
+ * @param string $message
+ * @param array $context
+ */
+ public function log($level, $message, array $context = array())
+ {
+ $line = $this->formatMessage($level, $message, $context);
+
+ if (file_put_contents($this->filename, $line, FILE_APPEND | LOCK_EX) === false) {
+ throw new RuntimeException('Unable to write to the log file.');
+ }
+ }
+}
diff --git a/vendor/fguillot/simpleLogger/src/SimpleLogger/Logger.php b/vendor/fguillot/simpleLogger/src/SimpleLogger/Logger.php
new file mode 100644
index 00000000..dc340cde
--- /dev/null
+++ b/vendor/fguillot/simpleLogger/src/SimpleLogger/Logger.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace SimpleLogger;
+
+use Psr\Log\AbstractLogger;
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+use Psr\Log\LogLevel;
+
+/**
+ * Handler for multiple loggers
+ *
+ * @package SimpleLogger
+ * @author Frédéric Guillot
+ */
+class Logger extends AbstractLogger implements LoggerAwareInterface
+{
+ /**
+ * Logger instances
+ *
+ * @access private
+ */
+ private $loggers = array();
+
+ /**
+ * Get level priority
+ *
+ * @param mixed $level
+ * @return integer
+ */
+ public function getLevelPriority($level)
+ {
+ switch ($level) {
+ case LogLevel::EMERGENCY:
+ return 600;
+ case LogLevel::ALERT:
+ return 550;
+ case LogLevel::CRITICAL:
+ return 500;
+ case LogLevel::ERROR:
+ return 400;
+ case LogLevel::WARNING:
+ return 300;
+ case LogLevel::NOTICE:
+ return 250;
+ case LogLevel::INFO:
+ return 200;
+ }
+
+ return 100;
+ }
+
+ /**
+ * Sets a logger instance on the object
+ *
+ * @param LoggerInterface $logger
+ * @return null
+ */
+ public function setLogger(LoggerInterface $logger)
+ {
+ $this->loggers[] = $logger;
+ }
+
+ /**
+ * Proxy method to the real loggers
+ *
+ * @param mixed $level
+ * @param string $message
+ * @param array $context
+ * @return null
+ */
+ public function log($level, $message, array $context = array())
+ {
+ foreach ($this->loggers as $logger) {
+ if ($this->getLevelPriority($level) >= $this->getLevelPriority($logger->getLevel())) {
+ $logger->log($level, $message, $context);
+ }
+ }
+ }
+
+ /**
+ * Dump variables for debugging
+ *
+ * @param mixed $variable
+ */
+ public function dump($variable)
+ {
+ foreach ($this->loggers as $logger) {
+ if ($this->getLevelPriority(LogLevel::DEBUG) >= $this->getLevelPriority($logger->getLevel())) {
+ $logger->dump($variable);
+ }
+ }
+ }
+}
diff --git a/vendor/fguillot/simpleLogger/src/SimpleLogger/Stderr.php b/vendor/fguillot/simpleLogger/src/SimpleLogger/Stderr.php
new file mode 100644
index 00000000..2573177e
--- /dev/null
+++ b/vendor/fguillot/simpleLogger/src/SimpleLogger/Stderr.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace SimpleLogger;
+
+/**
+ * Stderr logger
+ *
+ * @package SimpleLogger
+ * @author Frédéric Guillot
+ */
+class Stderr extends Base
+{
+ /**
+ * Logs with an arbitrary level.
+ *
+ * @param mixed $level
+ * @param string $message
+ * @param array $context
+ * @return null
+ */
+ public function log($level, $message, array $context = array())
+ {
+ file_put_contents('php://stderr', $this->formatMessage($level, $message, $context), FILE_APPEND);
+ }
+}
diff --git a/vendor/fguillot/simpleLogger/src/SimpleLogger/Stdout.php b/vendor/fguillot/simpleLogger/src/SimpleLogger/Stdout.php
new file mode 100644
index 00000000..82d181b2
--- /dev/null
+++ b/vendor/fguillot/simpleLogger/src/SimpleLogger/Stdout.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace SimpleLogger;
+
+/**
+ * Stdout logger
+ *
+ * @package SimpleLogger
+ * @author Frédéric Guillot
+ */
+class Stdout extends Base
+{
+ /**
+ * Logs with an arbitrary level.
+ *
+ * @param mixed $level
+ * @param string $message
+ * @param array $context
+ * @return null
+ */
+ public function log($level, $message, array $context = array())
+ {
+ file_put_contents('php://stdout', $this->formatMessage($level, $message, $context), FILE_APPEND);
+ }
+}
diff --git a/vendor/fguillot/simpleLogger/src/SimpleLogger/Syslog.php b/vendor/fguillot/simpleLogger/src/SimpleLogger/Syslog.php
new file mode 100644
index 00000000..c4e26a7a
--- /dev/null
+++ b/vendor/fguillot/simpleLogger/src/SimpleLogger/Syslog.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace SimpleLogger;
+
+use RuntimeException;
+use Psr\Log\LogLevel;
+
+/**
+ * Syslog Logger
+ *
+ * @package SimpleLogger
+ * @author Frédéric Guillot
+ */
+class Syslog extends Base
+{
+ /**
+ * Setup Syslog configuration
+ *
+ * @param string $ident Application name
+ * @param int $facility See http://php.net/manual/en/function.openlog.php
+ */
+ public function __construct($ident = 'PHP', $facility = LOG_USER)
+ {
+ if (! openlog($ident, LOG_ODELAY | LOG_PID, $facility)) {
+ throw new RuntimeException('Unable to connect to syslog.');
+ }
+ }
+
+ /**
+ * Get syslog priority according to Psr\LogLevel
+ *
+ * @param mixed $level
+ * @return integer
+ */
+ public function getSyslogPriority($level)
+ {
+ switch ($level) {
+ case LogLevel::EMERGENCY:
+ return LOG_EMERG;
+ case LogLevel::ALERT:
+ return LOG_ALERT;
+ case LogLevel::CRITICAL:
+ return LOG_CRIT;
+ case LogLevel::ERROR:
+ return LOG_ERR;
+ case LogLevel::WARNING:
+ return LOG_WARNING;
+ case LogLevel::NOTICE:
+ return LOG_NOTICE;
+ case LogLevel::INFO:
+ return LOG_INFO;
+ }
+
+ return LOG_DEBUG;
+ }
+
+ /**
+ * Logs with an arbitrary level.
+ *
+ * @param mixed $level
+ * @param string $message
+ * @param array $context
+ * @return null
+ */
+ public function log($level, $message, array $context = array())
+ {
+ $syslogPriority = $this->getSyslogPriority($level);
+ $syslogMessage = $this->interpolate($message, $context);
+
+ syslog($syslogPriority, $syslogMessage);
+ }
+}