<commit_before><?php namespace fenrir\system;

#
# [!!] Mysqli support requires mysqlnd extentions
#

/**
 * @copyright (c) 2016, freia Team
 * @license BSD-2 <http://freialib.github.io/license.txt>
 * @package freia Library
 */
class MysqliDatabase implements MysqlDatabaseSignature {

	/**
	 * @var array
	 */
	protected static $instances = [];

	/**
	 * @var \PDO
	 */
	protected $dbh = null;

	/**
	 * @return static
	 */
	static function instance(array $conf) {

		if ( ! isset($conf['dsn'], $conf['username'], $conf['password'])) {
			throw new Panic('Required database configuration value missing.');
		}

		// Parse DSN
		// ---------
		
		if ( ! preg_match('/host=(?<host>[^;]+)(;|$)/i', $conf['dsn'], $matches)) {
			throw new Panic('Unable to parse host from DSN');
		}

		$conf['host'] = $matches['host'];

		if ( ! preg_match('/dbname=(?<dbname>[^;]+)(;|$)/i', $conf['dsn'], $matches)) {
			throw new Panic('Unable to parse dbname from DSN');
		}

		$conf['dbname'] = $matches['dbname'];

		$conf['charset'] = 'utf8';
		if (preg_match('/charset=(?<charset>[^;]+)(;|$)/i', $conf['dsn'], $matches)) {
			$conf['charset'] = $matches['charset'];
		}

		// Normalize Options Configuration
		// -------------------------------

		if ( ! isset($conf['options'])) {
			$conf['options'] = [];
		}
		else { // options set
			$conf['options'] = $conf['options'];
		}

		// Normalize Attributes Configuration
		// ----------------------------------

		$default_attributes = [
			'error-mode' => [ 'key' => \PDO::ATTR_ERRMODE, 'value' => \PDO::ERRMODE_EXCEPTION ],
			'default-fetch-mode' => [ 'key' => \PDO::ATTR_DEFAULT_FETCH_MODE, 'value' => \PDO::FETCH_ASSOC ]
		];

		if ( ! isset($conf['attributes'])) {
			$conf['attributes'] = $default_attributes;
		}
		else { // attributes set
			$conf['attributes'] = array_merge($default_attributes, $conf['attributes']);
		}

		// Ensure Timezone Configuration
		// -----------------------------

		if ( ! isset($conf['timezone'])) {
			$conf['timezone'] = date_default_timezone_get();
		}

		$name = $conf['dsn'];
		if (isset(static::$instances[$name])) {
			return static::$instances[$name];
		}

		$i = new static;
		$i->conf = $conf;

		return static::$instances[$name] = $i;
	}

	/**
	 * Cleanup
	 */
	function __destruct() {
		mysqli_close($this->dbh);
		$this->dbh = null;
	}

// ---- Private ---------------------------------------------------------------

	/**
	 * Performs database initialization.
	 */
	protected function setup() {
		$conf = $this->conf;

		$dbh = $this->dbh = \mysqli_init();

		foreach ($this->conf['options'] as $key => $setting) {
			if ($setting != null && is_array($setting)) {
				mysqli_options($dbh, $setting['key'], $setting['value']);
			}
		}

		\mysqli_real_connect($dbh, $conf['host'], $conf['username'], $conf['password'], $conf['dbname']);

		/* check connection */
		if (mysqli_connect_errno()) {
			throw new Panic("Connect failed: " . \mysqli_connect_error());
		}

		\mysqli_set_charset($dbh, $conf['charset']);

		$offset = \hlin\Time::timezoneOffset($conf['timezone']);
		\mysqli_real_query($dbh, "SET time_zone='$offset';");
	}

	/**
	 * ...
	 */
	protected function reportFailure($statement) {
		throw new Panic($statement);
	}

// ---- Interface -------------------------------------------------------------

	/**
	 * @return string quoted version
	 */
	function quote($value) {
		$this->dbh or $this->setup();<commit_msg>feat: mysqli configuration hooks<commit_after><?php namespace fenrir\system;

#
# [!!] Mysqli support requires mysqlnd extentions
#

/**
 * @copyright (c) 2016, freia Team
 * @license BSD-2 <http://freialib.github.io/license.txt>
 * @package freia Library
 */
class MysqliDatabase implements MysqlDatabaseSignature {

	/**
	 * @var array
	 */
	protected static $instances = [];

	/**
	 * @var \PDO
	 */
	protected $dbh = null;

	/**
	 * @return static
	 */
	static function instance(array $conf) {

		if ( ! isset($conf['dsn'], $conf['username'], $conf['password'])) {
			throw new Panic('Required database configuration value missing.');
		}

		// Parse DSN
		// ---------
		
		if ( ! preg_match('/host=(?<host>[^;]+)(;|$)/i', $conf['dsn'], $matches)) {
			throw new Panic('Unable to parse host from DSN');
		}

		$conf['host'] = $matches['host'];

		if ( ! preg_match('/dbname=(?<dbname>[^;]+)(;|$)/i', $conf['dsn'], $matches)) {
			throw new Panic('Unable to parse dbname from DSN');
		}

		$conf['dbname'] = $matches['dbname'];

		$conf['charset'] = 'utf8';
		if (preg_match('/charset=(?<charset>[^;]+)(;|$)/i', $conf['dsn'], $matches)) {
			$conf['charset'] = $matches['charset'];
		}

		// Normalize Options Configuration
		// -------------------------------

		if ( ! isset($conf['options'])) {
			$conf['options'] = [];
		}
		else { // options set
			$conf['options'] = $conf['options'];
		}

		// Normalize Attributes Configuration
		// ----------------------------------

		$default_attributes = [
			'error-mode' => [ 'key' => \PDO::ATTR_ERRMODE, 'value' => \PDO::ERRMODE_EXCEPTION ],
			'default-fetch-mode' => [ 'key' => \PDO::ATTR_DEFAULT_FETCH_MODE, 'value' => \PDO::FETCH_ASSOC ]
		];

		if ( ! isset($conf['attributes'])) {
			$conf['attributes'] = $default_attributes;
		}
		else { // attributes set
			$conf['attributes'] = array_merge($default_attributes, $conf['attributes']);
		}

		// Ensure Timezone Configuration
		// -----------------------------

		if ( ! isset($conf['timezone'])) {
			$conf['timezone'] = date_default_timezone_get();
		}

		$name = $conf['dsn'];
		if (isset(static::$instances[$name])) {
			return static::$instances[$name];
		}

		$i = new static;
		$i->conf = $conf;

		return static::$instances[$name] = $i;
	}

	/**
	 * Cleanup
	 */
	function __destruct() {
		mysqli_close($this->dbh);
		$this->dbh = null;
	}

// ---- Private ---------------------------------------------------------------

	/**
	 * Overwrite to add options.
	 */
	protected function preconnectConf($dbh) {
		// hook
	}

	/**
	 * Overwrite to add options.
	 */
	protected function postconnectConf($dbh) {
		// hook
	}

	/**
	 * Performs database initialization.
	 */
	protected function setup() {
		$conf = $this->conf;

		$dbh = $this->dbh = \mysqli_init();

		foreach ($this->conf['options'] as $key => $setting) {
			if ($setting != null && is_array($setting)) {
				mysqli_options($dbh, $setting['key'], $setting['value']);
			}
		}

		$this->preconnectConf($dbh);

		\mysqli_real_connect($dbh, $conf['host'], $conf['username'], $conf['password'], $conf['dbname']);

		$this->postconnectConf($dbh);

		/* check connection */
		if (mysqli_connect_errno()) {
			throw new Panic("Connect failed: " . \mysqli_connect_error());
		}

		\mysqli_set_charset($dbh, $conf['charset']);

		$offset = \hlin\Time::timezoneOffset($conf['timezone']);
		\mysqli_real_query($dbh, "SET time_zone='$offset';");
	}

	/**
	 * ...
	 */
	protected function reportFailure($statement) {
		throw new Panic($statement);
	}

// ---- Interface -------------------------------------------------------------

	/**
	 * @return string quoted version
	 */
	function quote($value) {
		$this->dbh or $this->setup();
{'fraction_non_alphanumeric': 0.2666760286476618, 'fraction_numerical': 0.0016851565791321443, 'mean_word_length': 2.7642706131078225, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2608026', 'n_tokens_mistral': 2633, 'n_tokens_neox': 2323, 'n_words': 442}