diff options
Diffstat (limited to 'vendor/fguillot')
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); + } +} |