<?php declare(strict_types = 1);

/**
 * This file is part of the Nextras\Dbal library.
 * @license    MIT
 * @link       https://github.com/nextras/dbal
 */

namespace Nextras\Dbal\Utils;

use Nextras\Dbal\IConnection;
use Nextras\Dbal\IOException;

class FileImporter
{
	/**
	 * Imports & executes queries from sql file.
	 * Code taken from Adminer (http://www.adminer.org) & modified,
	 * @author Jakub Vrána
	 * @author Jan Tvrdík
	 * @author Michael Moravec
	 * @author Jan Škrášek
	 * @license Apache License
	 *
	 * @return int number of executed queries
	 */
	public static function executeFile(IConnection $connection, string $file): int
	{
		$query = @file_get_contents($file);
		if ($query === false) {
			throw new IOException("Cannot open file '$file'.");
		}

		$delimiter = ';';
		$offset = $queries = 0;
		$space = "(?:\\s|/\\*.*\\*/|(?:#|-- )[^\\n]*\\n|--\\n)";

		$platformName = $connection->getPlatform()->getName();
		if ($platformName === 'mysql') {
			$parse = '[\'"]|/\*|-- |$';
		} elseif ($platformName === 'pgsql') {
			$parse = '[\'"]|/\*|-- |$|\$[^$]*\$';
		} elseif ($platformName === 'mssql') {
			$parse = '[\'"[]|/\*|-- |$';
		} else { // general
			$parse = '[\'"`#]|/\*|-- |$';
		}

		while ($query != '') {
			if (!$offset && preg_match("~^{$space}*DELIMITER\\s+(\\S+)~i", $query, $match)) {
				$delimiter = $match[1];
				$query = substr($query, strlen($match[0]));

			} else {
				preg_match('(' . preg_quote($delimiter) . "\\s*|$parse)", $query, $match, PREG_OFFSET_CAPTURE, $offset); // should always match
				$found = $match[0][0];
				/** @var int $offset */
				$offset = $match[0][1] + strlen($found);

				if (!$found && rtrim($query) === '') {
					break;
				}

				if (!$found || rtrim($found) == $delimiter) { // end of a query
					$q = substr($query, 0, $match[0][1]);

					$queries++;
					$connection->query('%raw', $q);

					$query = substr($query, $offset);
					/** @var int $offset */
					$offset = 0;

				} else { // find matching quote or comment end
					while (preg_match('(' . ($found == '/*' ? '\*/' : ($found == '[' ? ']' : (preg_match('~^-- |^#~', $found) ? "\n" : preg_quote($found) . "|\\\\."))) . '|$)s', $query, $match, PREG_OFFSET_CAPTURE, $offset)) { //! respect sql_mode NO_BACKSLASH_ESCAPES
						$s = $match[0][0];
						/** @var int $offset */
						$offset = $match[0][1] + strlen($s);
						if ($s[0] !== '\\') {
							break;
						}
					}
				}
			}
		}

		return $queries;
	}
}


<?php declare(strict_types = 1);

/**
 * This file is part of the Nextras\Dbal library.
 * @license    MIT
 * @link       https://github.com/nextras/dbal
 */

namespace Nextras\Dbal\Drivers\Mysqli;

use DateInterval;
use DateTimeZone;
use mysqli;
use Nextras\Dbal\Connection;
use Nextras\Dbal\ConnectionException;
use Nextras\Dbal\DriverException;
use Nextras\Dbal\Drivers\IDriver;
use Nextras\Dbal\ForeignKeyConstraintViolationException;
use Nextras\Dbal\InvalidArgumentException;
use Nextras\Dbal\NotNullConstraintViolationException;
use Nextras\Dbal\NotSupportedException;
use Nextras\Dbal\Platforms\IPlatform;
use Nextras\Dbal\Platforms\MySqlPlatform;
use Nextras\Dbal\QueryException;
use Nextras\Dbal\Result\Result;
use Nextras\Dbal\UniqueConstraintViolationException;

class MysqliDriver implements IDriver
{
	/** @var mysqli|null */
	private $connection;

	/** @var DateTimeZone Timezone for database connection. */
	private $connectionTz;

	/** @var callable */
	private $onQueryCallback;

	/** @var float */
	private $timeTaken = 0.0;

	public function __destruct()
	{
		$this->disconnect();
	}

	public function connect(array $params, callable $onQueryCallback)
	{
		$this->onQueryCallback = $onQueryCallback;

		$host = $params['host'] ?? ini_get('mysqli.default_host');
		$port = $params['port'] ?? (int) (ini_get('mysqli.default_port') ?: 3306);
		$dbname = $params['database'] ?? '';
		$socket = $params['unix_socket'] ?? ini_get('mysqli.default_socket') ?? '';
		$flags = $params['flags'] ?? 0;

		$this->connection = new mysqli();

		if (!@$this->connection->real_connect($host, $params['username'], (string) $params['password'], $dbname, $port, $socket, $flags)) {
			throw $this->createException(
				$this->connection->connect_error,
				$this->connection->connect_errno,
				@$this->connection->sqlstate ?: 'HY000'
			);
		}

		$this->processInitialSettings($params);
	}

	public function disconnect()
	{
		if ($this->connection) {
			$this->connection->close();
			$this->connection = null;
		}
	}

	public function isConnected(): bool
	{
		return $this->connection !== null;
	}

	/**
	 * @return mysqli|null
	 */
	public function getResourceHandle()
	{
		return $this->connection;
	}

	public function query(string $query): Result
	{
		assert($this->connection !== null);

		$time = microtime(true);
		$result = @$this->connection->query($query);
		$this->timeTaken = microtime(true) - $time;

		if ($result === false) {
			throw $this->createException(
				$this->connection->error,
				$this->connection->errno,
				$this->connection->sqlstate,
				$query
			);
		}

		if ($result === true) {
			return new Result(new MysqliEmptyResultAdapter(), $this);
		}

		return new Result(new MysqliResultAdapter($result), $this);
	}

	public function getLastInsertedId(string $sequenceName = null)
	{
		assert($this->connection !== null);
		return $this->connection->insert_id;
	}

	public function getAffectedRows(): int
	{
		assert($this->connection !== null);
		return $this->connection->affected_rows;
	}

	public function getQueryElapsedTime(): float
	{
		return $this->timeTaken;
	}

	public function createPlatform(Connection $connection): IPlatform
	{
		return new MySqlPlatform($connection);
	}

	public function getServerVersion(): string
	{
		assert($this->connection !== null);
		$version = $this->connection->server_version;
		$majorVersion = floor($version / 10000);
		$minorVersion = floor(($version - $majorVersion * 10000) / 100);
		$patchVersion = floor($version - $majorVersion * 10000 - $minorVersion * 100);
		return $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
	}

	public function ping(): bool
	{
		assert($this->connection !== null);
		return $this->connection->ping();
	}

	public function setTransactionIsolationLevel(int $level)
	{
		static $levels = [
			Connection::TRANSACTION_READ_UNCOMMITTED => 'READ UNCOMMITTED',
			Connection::TRANSACTION_READ_COMMITTED => 'READ COMMITTED',
			Connection::TRANSACTION_REPEATABLE_READ => 'REPEATABLE READ',
			Connection::TRANSACTION_SERIALIZABLE => 'SERIALIZABLE',
		];
		if (isset($levels[$level])) {
			throw new NotSupportedException("Unsupported transation level $level");
		}
		$this->loggedQuery("SET SESSION TRANSACTION ISOLATION LEVEL {$levels[$level]}");
	}

	public function beginTransaction(): void
	{
		$this->loggedQuery('START TRANSACTION');
	}

	public function commitTransaction(): void
	{
		$this->loggedQuery('COMMIT');
	}

	public function rollbackTransaction(): void
	{
		$this->loggedQuery('ROLLBACK');
	}

	public function createSavepoint(string $name): void
	{
		$this->loggedQuery('SAVEPOINT ' . $this->convertIdentifierToSql($name));
	}

	public function releaseSavepoint(string $name): void
	{
		$this->loggedQuery('RELEASE SAVEPOINT ' . $this->convertIdentifierToSql($name));
	}

	public function rollbackSavepoint(string $name): void
	{
		$this->loggedQuery('ROLLBACK TO SAVEPOINT ' . $this->convertIdentifierToSql($name));
	}

	protected function processInitialSettings(array $params)
	{
		assert($this->connection !== null);

		if (isset($params['charset'])) {
			$charset = $params['charset'];
		} elseif (($version = $this->getServerVersion()) && version_compare($version, '5.5.3', '>=')) {
			$charset = 'utf8mb4';
		} else {
			$charset = 'utf8';
		}

		$this->connection->set_charset($charset);

		if (!array_key_exists('sqlMode', $params)) {
			$params['sqlMode'] = 'TRADITIONAL';
		}
		if ($params['sqlMode'] !== null) {
			$this->loggedQuery('SET sql_mode = ' . $this->convertStringToSql($params['sqlMode']));
		}

		if (!isset($params['connectionTz']) || $params['connectionTz'] === IDriver::TIMEZONE_AUTO_PHP_NAME) {
			$params['connectionTz'] = date_default_timezone_get();
		} elseif ($params['connectionTz'] === IDriver::TIMEZONE_AUTO_PHP_OFFSET) {
			$params['connectionTz'] = date('P');
		}

		$this->connectionTz = new DateTimeZone($params['connectionTz']);
		$this->loggedQuery('SET time_zone = ' . $this->convertStringToSql($this->connectionTz->getName()));
	}

	public function convertToPhp(string $value, $nativeType)
	{
		if ($nativeType === MYSQLI_TYPE_TIMESTAMP) {
			return $value . ' ' . $this->connectionTz->getName();

		} elseif ($nativeType === MYSQLI_TYPE_LONGLONG) {
			// called only on 32bit
			// hack for phpstan
			/** @var int|float $numeric */
			$numeric = $value;
			return is_float($tmp = $numeric * 1) ? $numeric : $tmp;

		} elseif ($nativeType === MYSQLI_TYPE_TIME) {
			preg_match('#^(-?)(\d+):(\d+):(\d+)#', $value, $m);
			$value = new DateInterval("PT{$m[2]}H{$m[3]}M{$m[4]}S");
			$value->invert = $m[1] ? 1 : 0;
			return $value;

		} else {
			throw new NotSupportedException("MysqliDriver does not support '{$nativeType}' type conversion.");
		}
	}

	public function convertStringToSql(string $value): string
	{
		assert($this->connection !== null);
		return "'" . $this->connection->escape_string($value) . "'";
	}

	public function convertJsonToSql($value): string
	{
		$encoded = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION);
		if (json_last_error()) {
			throw new InvalidArgumentException('JSON Encode Error: ' . json_last_error_msg());
		}
		assert(is_string($encoded));
		return $this->convertStringToSql($encoded);
	}

	public function convertLikeToSql(string $value, int $mode): string
	{
		$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
		return ($mode <= 0 ? "'%" : "'") . $value . ($mode >= 0 ? "%'" : "'");
	}

	public function convertBoolToSql(bool $value): string
	{
		return $value ? '1' : '0';
	}

	public function convertIdentifierToSql(string $value): string
	{
		return str_replace('`*`', '*', '`' . str_replace(['`', '.'], ['``', '`.`'], $value) . '`');
	}

	public function convertDatetimeToSql(\DateTimeInterface $value): string
	{
		assert($value instanceof \DateTime || $value instanceof \DateTimeImmutable);
		if ($value->getTimezone()->getName() !== $this->connectionTz->getName()) {
			if ($value instanceof \DateTimeImmutable) {
				$value = $value->setTimezone($this->connectionTz);
			} else {
				$value = clone $value;
				$value->setTimezone($this->connectionTz);
			}
		}
		return "'" . $value->format('Y-m-d H:i:s.u') . "'";
	}

	public function convertDatetimeSimpleToSql(\DateTimeInterface $value): string
	{
		return "'" . $value->format('Y-m-d H:i:s.u') . "'";
	}

	public function convertDateIntervalToSql(\DateInterval $value): string
	{
		$totalHours = ((int) $value->format('%a')) * 24 + $value->h;
		if ($totalHours >= 839) {
			// see https://dev.mysql.com/doc/refman/5.0/en/time.html
			throw new InvalidArgumentException('Mysql cannot store interval bigger than 839h:59m:59s.');
		}
		return "'" . $value->format("%r{$totalHours}:%I:%S") . "'";
	}

	public function convertBlobToSql(string $value): string
	{
		assert($this->connection !== null);
		return "_binary'" . $this->connection->escape_string($value) . "'";
	}

	public function modifyLimitQuery(string $query, ?int $limit, ?int $offset): string
	{
		if ($limit !== null || $offset !== null) {
			// 18446744073709551615 is maximum of unsigned BIGINT
			// see http://dev.mysql.com/doc/refman/5.0/en/select.html
			$query .= ' LIMIT ' . ($limit !== null ? (string) $limit : '18446744073709551615');
		}

		if ($offset !== null) {
			$query .= ' OFFSET ' . $offset;
		}

		return $query;
	}

	/**
	 * This method is based on Doctrine\DBAL project.
	 * @link www.doctrine-project.org
	 */
	protected function createException($error, $errorNo, $sqlState, $query = null)
	{
		if (in_array($errorNo, [1216, 1217, 1451, 1452, 1701], true)) {
			return new ForeignKeyConstraintViolationException($error, $errorNo, $sqlState, null, $query);

		} elseif (in_array($errorNo, [1062, 1557, 1569, 1586], true)) {
			return new UniqueConstraintViolationException($error, $errorNo, $sqlState, null, $query);

		} elseif (in_array($errorNo, [1044, 1045, 1046, 1049, 1095, 1142, 1143, 1227, 1370, 2002, 2005], true)) {
			return new ConnectionException($error, $errorNo, $sqlState);

		} elseif (in_array($errorNo, [1048, 1121, 1138, 1171, 1252, 1263, 1566], true)) {
			return new NotNullConstraintViolationException($error, $errorNo, $sqlState, null, $query);

		} elseif ($query !== null) {
			return new QueryException($error, $errorNo, $sqlState, null, $query);

		} else {
			return new DriverException($error, $errorNo, $sqlState);
		}
	}

	protected function loggedQuery(string $sql)
	{
		try {
			$result = $this->query($sql);
			($this->onQueryCallback)($sql, $this->getQueryElapsedTime(), $result, null);
			return $result;
		} catch (DriverException $exception) {
			($this->onQueryCallback)($sql, $this->getQueryElapsedTime(), null, $exception);
			throw $exception;
		}
	}
}


<?php declare(strict_types = 1);

/**
 * This file is part of the Nextras\Dbal library.
 * @license    MIT
 * @link       https://github.com/nextras/dbal
 */

namespace Nextras\Dbal\Drivers\Pgsql;

use DateInterval;
use DateTimeZone;
use Nextras\Dbal\Connection;
use Nextras\Dbal\ConnectionException;
use Nextras\Dbal\DriverException;
use Nextras\Dbal\Drivers\IDriver;
use Nextras\Dbal\ForeignKeyConstraintViolationException;
use Nextras\Dbal\InvalidArgumentException;
use Nextras\Dbal\NotNullConstraintViolationException;
use Nextras\Dbal\NotSupportedException;
use Nextras\Dbal\Platforms\IPlatform;
use Nextras\Dbal\Platforms\PostgreSqlPlatform;
use Nextras\Dbal\QueryException;
use Nextras\Dbal\Result\Result;
use Nextras\Dbal\UniqueConstraintViolationException;
use Tester\Assert;

class PgsqlDriver implements IDriver
{
	/** @var resource|null */
	private $connection;

	/** @var DateTimeZone Timezone for database connection. */
	private $connectionTz;

	/** @var callable */
	private $onQueryCallback;

	/** @var int */
	private $affectedRows = 0;

	/** @var float */
	private $timeTaken = 0.0;

	public function __destruct()
	{
		$this->disconnect();
	}

	public function connect(array $params, callable $onQueryCallback)
	{
		static $knownKeys = [
			'host', 'hostaddr', 'port', 'dbname', 'user', 'password',
			'connect_timeout', 'options', 'sslmode', 'service',
		];

		$this->onQueryCallback = $onQueryCallback;

		$params = $this->processConfig($params);
		$connectionString = '';
		foreach ($knownKeys as $key) {
			if (isset($params[$key])) {
				$connectionString .= $key . '=' . $params[$key] . ' ';
			}
		}

		set_error_handler(function($code, $message) {
			restore_error_handler();
			throw $this->createException($message, $code, null);
		}, E_ALL);

		$connection = pg_connect($connectionString, PGSQL_CONNECT_FORCE_NEW);
		assert($connection !== false); // connection error is handled in error_handler
		$this->connection = $connection;

		restore_error_handler();

		$this->connectionTz = new DateTimeZone($params['connectionTz']);
		if (strpos($this->connectionTz->getName(), ':') !== false) {
			$this->loggedQuery('SET TIME ZONE INTERVAL ' . pg_escape_literal($this->connectionTz->getName()) . ' HOUR TO MINUTE');
		} else {
			$this->loggedQuery('SET TIME ZONE ' . pg_escape_literal($this->connectionTz->getName()));
		}

		if (isset($params['searchPath'])) {
			$this->loggedQuery('SET search_path TO ' . implode(', ', array_map([$this, 'convertIdentifierToSql'], (array) $params['searchPath'])));
		}
	}

	public function disconnect()
	{
		if ($this->connection) {
			pg_close($this->connection);
			$this->connection = null;
		}
	}

	public function isConnected(): bool
	{
		return $this->connection !== null;
	}

	public function getResourceHandle()
	{
		return $this->connection;
	}

	public function query(string $query): Result
	{
		assert($this->connection !== null);
		if (!pg_send_query($this->connection, $query)) {
			throw $this->createException(pg_last_error($this->connection), 0, null);
		}

		$time = microtime(true);
		$resource = pg_get_result($this->connection);
		$this->timeTaken = microtime(true) - $time;

		if ($resource === false) {
			throw $this->createException(pg_last_error($this->connection), 0, null);
		}

		$state = pg_result_error_field($resource, PGSQL_DIAG_SQLSTATE);
		if ($state !== null) {
			throw $this->createException(pg_result_error($resource), 0, $state, $query);
		}

		$this->affectedRows = pg_affected_rows($resource);
		return new Result(new PgsqlResultAdapter($resource), $this);
	}

	public function getLastInsertedId(string $sequenceName = null)
	{
		if (empty($sequenceName)) {
			throw new InvalidArgumentException('PgsqlDriver require to pass sequence name for getLastInsertedId() method.');
		}
		assert($this->connection !== null);
		$sql = 'SELECT CURRVAL(' . pg_escape_literal($this->connection, $sequenceName) . ')';
		return $this->loggedQuery($sql)->fetchField();
	}

	public function getAffectedRows(): int
	{
		return $this->affectedRows;
	}

	public function getQueryElapsedTime(): float
	{
		return $this->timeTaken;
	}

	public function createPlatform(Connection $connection): IPlatform
	{
		return new PostgreSqlPlatform($connection);
	}

	public function getServerVersion(): string
	{
		assert($this->connection !== null);
		return pg_version($this->connection)['server'];
	}

	public function ping(): bool
	{
		assert($this->connection !== null);
		return pg_ping($this->connection);
	}

	public function setTransactionIsolationLevel(int $level)
	{
		static $levels = [
			Connection::TRANSACTION_READ_UNCOMMITTED => 'READ UNCOMMITTED',
			Connection::TRANSACTION_READ_COMMITTED => 'READ COMMITTED',
			Connection::TRANSACTION_REPEATABLE_READ => 'REPEATABLE READ',
			Connection::TRANSACTION_SERIALIZABLE => 'SERIALIZABLE',
		];
		if (isset($levels[$level])) {
			throw new NotSupportedException("Unsupported transation level $level");
		}
		$this->loggedQuery("SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL {$levels[$level]}");
	}

	public function beginTransaction(): void
	{
		$this->loggedQuery('START TRANSACTION');
	}

	public function commitTransaction(): void
	{
		$this->loggedQuery('COMMIT');
	}

	public function rollbackTransaction(): void
	{
		$this->loggedQuery('ROLLBACK');
	}

	public function createSavepoint(string $name): void
	{
		$this->loggedQuery('SAVEPOINT ' . $this->convertIdentifierToSql($name));
	}

	public function releaseSavepoint(string $name): void
	{
		$this->loggedQuery('RELEASE SAVEPOINT ' . $this->convertIdentifierToSql($name));
	}

	public function rollbackSavepoint(string $name): void
	{
		$this->loggedQuery('ROLLBACK TO SAVEPOINT ' . $this->convertIdentifierToSql($name));
	}

	public function convertToPhp(string $value, $nativeType)
	{
		static $trues = ['true', 't', 'yes', 'y', 'on', '1'];

		if ($nativeType === 'bool') {
			return in_array(strtolower($value), $trues, true);

		} elseif ($nativeType === 'int8') {
			// called only on 32bit
			// hack for phpstan
			/** @var int|float $numeric */
			$numeric = $value;
			return is_float($tmp = $numeric * 1) ? $numeric : $tmp;

		} elseif ($nativeType === 'interval') {
			return DateInterval::createFromDateString($value);

		} elseif ($nativeType === 'bit' || $nativeType === 'varbit') {
			return bindec($value);

		} elseif ($nativeType === 'bytea') {
			return pg_unescape_bytea($value);

		} else {
			throw new NotSupportedException("PgsqlDriver does not support '{$nativeType}' type conversion.");
		}
	}

	public function convertStringToSql(string $value): string
	{
		assert($this->connection !== null);
		return pg_escape_literal($this->connection, $value);
	}

	public function convertJsonToSql($value): string
	{
		$encoded = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION);
		if (json_last_error()) {
			throw new InvalidArgumentException('JSON Encode Error: ' . json_last_error_msg());
		}
		assert(is_string($encoded));
		return $this->convertStringToSql($encoded);
	}

	public function convertLikeToSql(string $value, int $mode)
	{
		$value = strtr($value, [
			"'" => "''",
			'\\' => '\\\\',
			'%' => '\\%',
			'_' => '\\_',
		]);
		return ($mode <= 0 ? "'%" : "'") . $value . ($mode >= 0 ? "%'" : "'");
	}

	public function convertBoolToSql(bool $value): string
	{
		return $value ? 'TRUE' : 'FALSE';
	}

	public function convertIdentifierToSql(string $value): string
	{
		assert($this->connection !== null);
		$parts = explode('.', $value);
		foreach ($parts as &$part) {
			if ($part !== '*') {
				$part = pg_escape_identifier($this->connection, $part);
			}
		}
		return implode('.', $parts);
	}

	public function convertDateTimeToSql(\DateTimeInterface $value): string
	{
		assert($value instanceof \DateTime || $value instanceof \DateTimeImmutable);
		if ($value->getTimezone()->getName() !== $this->connectionTz->getName()) {
			if ($value instanceof \DateTimeImmutable) {
				$value = $value->setTimezone($this->connectionTz);
			} else {
				$value = clone $value;
				$value->setTimezone($this->connectionTz);
			}
		}
		return "'" . $value->format('Y-m-d H:i:s.u') . "'::timestamptz";
	}

	public function convertDateTimeSimpleToSql(\DateTimeInterface $value): string
	{
		return "'" . $value->format('Y-m-d H:i:s.u') . "'::timestamp";
	}

	public function convertDateIntervalToSql(\DateInterval $value): string
	{
		return $value->format('P%yY%mM%dDT%hH%iM%sS');
	}

	public function convertBlobToSql(string $value): string
	{
		assert($this->connection !== null);
		return "'" . pg_escape_bytea($this->connection, $value) . "'";
	}

	public function modifyLimitQuery(string $query, ?int $limit, ?int $offset): string
	{
		if ($limit !== null) {
			$query .= ' LIMIT ' . $limit;
		}
		if ($offset !== null) {
			$query .= ' OFFSET ' . $offset;
		}
		return $query;
	}

	/**
	 * This method is based on Doctrine\DBAL project.
	 * @link www.doctrine-project.org
	 */
	protected function createException($error, $errorNo, $sqlState, $query = null)
	{
		// see codes at http://www.postgresql.org/docs/9.4/static/errcodes-appendix.html
		if ($sqlState === '0A000' && strpos($error, 'truncate') !== false) {
			// Foreign key constraint violations during a TRUNCATE operation
			// are considered "feature not supported" in PostgreSQL.
			return new ForeignKeyConstraintViolationException($error, $errorNo, $sqlState, null, $query);

		} elseif ($sqlState === '23502') {
			return new NotNullConstraintViolationException($error, $errorNo, $sqlState, null, $query);

		} elseif ($sqlState === '23503') {
			return new ForeignKeyConstraintViolationException($error, $errorNo, $sqlState, null, $query);

		} elseif ($sqlState === '23505') {
			return new UniqueConstraintViolationException($error, $errorNo, $sqlState, null, $query);

		} elseif ($sqlState === null && stripos($error, 'pg_connect()') !== false) {
			return new ConnectionException($error, $errorNo, $sqlState);

		} elseif ($query !== null) {
			return new QueryException($error, $errorNo, $sqlState, null, $query);

		} else {
			return new DriverException($error, $errorNo, $sqlState);
		}
	}

	protected function loggedQuery(string $sql)
	{
		try {
			$result = $this->query($sql);
			($this->onQueryCallback)($sql, $this->getQueryElapsedTime(), $result, null);
			return $result;
		} catch (DriverException $exception) {
			($this->onQueryCallback)($sql, $this->getQueryElapsedTime(), null, $exception);
			throw $exception;
		}
	}

	private function processConfig(array $params): array
	{
		if (!isset($params['database']) && isset($params['dbname'])) {
			throw new InvalidArgumentException("You have passed 'dbname' key, did you mean 'database' key?");
		}
		$params['dbname'] = $params['database'] ?? null;
		$params['user'] = $params['username'] ?? null;
		unset($params['database'], $params['username']);
		if (!isset($params['connectionTz']) || $params['connectionTz'] === IDriver::TIMEZONE_AUTO_PHP_NAME) {
			$params['connectionTz'] = date_default_timezone_get();
		} elseif ($params['connectionTz'] === IDriver::TIMEZONE_AUTO_PHP_OFFSET) {
			$params['connectionTz'] = date('P');
		}
		return $params;
	}
}


<?php
/**
 * User: nvolgas
 * Date: 2/13/15
 * Time: 3:28 PM
 */

namespace Volnix\CSRF;

class CSRF {

	/**
	 * The default token name
	 */
	const TOKEN_NAME = "_csrf_token_645a83a41868941e4692aa31e7235f2";

	/**
	 * (Re-)Generate a token and write it to session
	 *
	 * @param string $token_name - defaults to the default token name
	 * @return void
	 */
	public static function generateToken($token_name = self::TOKEN_NAME)
	{
		// generate as random of a token as possible
		$salt   = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : uniqid();
		$_SESSION[$token_name]  = sha1(uniqid(sha1($salt), true));
	}

	/**
	 * Get the token.  If it's not defined, this will go ahead and generate one.
	 *
	 * @param string $token_name - defaults to the default token name
	 * @return string
	 */
	public static function getToken($token_name = self::TOKEN_NAME)
	{
		if (empty($_SESSION[$token_name])) {
			static::generateToken($token_name);
		}

		return $_SESSION[$token_name];
	}

	/**
	 * Get the token name.  This is just a CRUD method to make your code cleaner.
	 *
	 * @param string $token_name
	 * @return string
	 */
	public static function getTokenName($token_name = self::TOKEN_NAME)
	{
		return $token_name;
	}

	/**
	 * Validate the token.  If there's not one yet, it will set one and return false.
	 *
	 * @param array $request_data - your whole POST/GET array - will index in with the token name to get the token.
	 * @param string $token_name - defaults to the default token name
	 * @return bool
	 */
	public static function validate($request_data = array(), $token_name = self::TOKEN_NAME)
	{
		if (empty($_SESSION[$token_name])) {
			static::generateToken($token_name);
			return false;
		} elseif (empty($request_data[$token_name])) {
			return false;
		} else {
			if(static::compare($request_data[$token_name], static::getToken($token_name))){
				static::generateToken($token_name);
				return true;
			} else {
				return false;
			}
		}
	}

	/**
	 * Get a hidden input string with the token/token name in it.
	 *
	 * @param string $token_name - defaults to the default token name
	 * @return string
	 */
	public static function getHiddenInputString($token_name = self::TOKEN_NAME)
	{
		return sprintf('<input type="hidden" name="%s" value="%s"/>', $token_name, static::getToken($token_name));
	}

	/**
	 * Get a query string mark-up with the token/token name in it.
	 *
	 * @param string $token_name - defaults to the default token name
	 * @return string
	 */
	public static function getQueryString($token_name = self::TOKEN_NAME)
	{
		return sprintf('%s=%s', $token_name, static::getToken($token_name));
	}

	/**
	 * Get an array with the token (useful for form libraries, etc.)
	 *
	 * @param string $token_name
	 * @return array
	 */
	public static function getTokenAsArray($token_name = self::TOKEN_NAME)
	{
		return array(
			$token_name => self::getToken($token_name)
		);
	}

	/**
	 * Constant-time string comparison.  This comparison function is timing-attack safe
	 *
	 * @param string $hasha
	 * @param string $hashb
	 * @return bool
	 */
	public static function compare($hasha = "", $hashb = "")
	{
		// we want hashes_are_not_equal to be false by the end of this if the strings are identical

		// if the strings are NOT equal length this will return true, else false
		$hashes_are_not_equal = strlen($hasha) ^ strlen($hashb);

		// compare the shortest of the two strings (the above line will still kick back a failure if the lengths weren't equal.  this just keeps us from over-flowing our strings when comparing
		$length = min(strlen($hasha), strlen($hashb));
		$hasha = substr($hasha, 0, $length);
		$hashb = substr($hashb, 0, $length);

		// iterate through the hashes comparing them character by character
		// if a character does not match, then return true, so the hashes are not equal
		for ($i = 0; $i < strlen($hasha); $i++) {
			$hashes_are_not_equal += !(ord($hasha[$i]) === ord($hashb[$i]));
		}

		// if not hashes are not equal, then hashes are equal
		return !$hashes_are_not_equal;
	}

}


<?php
/**
 * Matomo - free/libre analytics platform
 *
 * @link https://matomo.org
 * @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL v3 or later
 */

namespace Matomo\Decompress;

/**
 * Unzip implementation for .gz files.
 */
class Gzip implements DecompressInterface
{
    /**
     * Name of .gz file.
     *
     * @var string
     */
    private $filename = null;

    /**
     * Error string.
     *
     * @var string
     */
    private $error = null;

    /**
     * Constructor.
     *
     * @param string $filename Name of .gz file.
     */
    public function __construct($filename)
    {
        $this->filename = $filename;
    }

    /**
     * Extracts the contents of the .gz file to $pathExtracted.
     *
     * @param string $pathExtracted Must be file, not directory.
     * @return bool true if successful, false if otherwise.
     */
    public function extract($pathExtracted)
    {
        $file = @gzopen($this->filename, 'r');

        if ($file === false) {
            $this->error = "gzopen failed";
            return false;
        }

        $output = fopen($pathExtracted, 'w');
        while (!feof($file)) {
            fwrite($output, fread($file, 1024 * 1024));
        }
        fclose($output);

        $success = gzclose($file);
        if (false === $success) {
            $this->error = "gzclose failed";
            return false;
        }

        return true;
    }

    /**
     * Get error status string for the latest error.
     *
     * @return string
     */
    public function errorInfo()
    {
        return $this->error;
    }
}