getConfig = $getConfig; $this->requiredVars = $requiredVars; } /** * set "silent" mode (no error logging) * -> optional clear $this->errors * @param bool $silent * @param bool $clearErrors */ public function setSilent(bool $silent, bool $clearErrors = false){ $this->silent = $silent; if($clearErrors){ $this->errors = []; } } /** * @return bool */ public function isSilent() : bool { return $this->silent; } /** * connect to the DB server itself -> NO database is used * -> can be used to check if a certain DB exists without connecting to it directly * @param string $alias * @return Sql|null */ public function connectToServer(string $alias) : ?Sql { $config = ($this->getConfig)($alias); $config['NAME'] = ''; return $this->newDB($config); } /** * tries to create a database if not exists * -> DB user needs rights to create a DB * @param string $alias * @return Sql|null */ public function createDB(string $alias) : ?Sql { $db = null; $config = ($this->getConfig)($alias); // remove database from $dsn (we want to crate it) $newDbName = $config['NAME']; if(!empty($newDbName)){ $config['NAME'] = ''; $db = $this->newDB($config); if(!is_null($db)){ $schema = new Schema($db); if(!in_array($newDbName, $schema->getDatabases())){ $db->exec("CREATE DATABASE IF NOT EXISTS `" . $newDbName . "` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"); $db->exec("USE `" . $newDbName . "`"); // check if DB create was successful $dbCheck = $db->exec("SELECT DATABASE()"); if( !empty($dbCheck[0]) && !empty($checkDbName = reset($dbCheck[0])) && $checkDbName == $newDbName ){ // prepare new created DB $requiredVars = ($this->requiredVars)($db->driver()); $db->prepareDatabase($requiredVars['CHARACTER_SET_DATABASE'], $requiredVars['COLLATION_DATABASE']); } } } } return $db; } /** * get active connection from store or init new connection * @param string $alias * @return Sql|null */ public function getDB(string $alias) : ?Sql { if(!isset($this->connectionStore[$alias])){ $db = $this->newDB(($this->getConfig)($alias)); if(!is_null($db)){ $this->connectionStore[$alias] = $db; } return $db; }else{ return $this->connectionStore[$alias]; } } /** * get last recent Exceptions from error history * @param string $alias * @param int $limit * @return \Exception[] */ public function getErrors(string $alias, int $limit = 1) : array { return array_slice((array)$this->errors[$alias] , 0, $limit); } /** * build PDO DNS connect string from DB config array * -> Hint: dbName is not part of the DNS we need -> passed as extra parameter * @param array $config * @return string */ protected function buildDnsFromConfig(array $config) : string { $dns = $config['SCHEME'] . ':'; $dns .= $config['SOCKET'] ? 'unix_socket=' . $config['SOCKET'] : 'host=' . $config['HOST']; $dns .= $config['PORT'] && !$config['SOCKET'] ? ';port=' . $config['PORT'] : ''; $dns .= $config['NAME'] ? ';dbname=' . $config['NAME'] : ''; return $dns; } /** * @param array $config * @return Sql|null */ protected function newDB(array $config) : ?Sql { $db = null; if($config['SCHEME'] == 'mysql'){ try{ $db = new Sql($this->buildDnsFromConfig($config), $config['USER'], $config['PASS'], $config['OPTIONS']); }catch(\PDOException $e){ $this->pushError($config['ALIAS'], $e); if(!$this->isSilent()){ self::getLogger()->write($e); } } }else{ // unsupported DB type $this->pushError($config['ALIAS'], new ConfigException( sprintf(self::ERROR_SCHEME, $config['SCHEME'], $config['ALIAS'])) ); } return $db; } /** * push new Exception into static error history * @param string $alias * @param \Exception $e */ protected function pushError(string $alias, \Exception $e){ if(!is_array($this->errors[$alias])){ $this->errors[$alias] = []; } // prevent adding same errors twice if(!empty($this->errors[$alias])){ /** * @var $lastError \Exception */ $lastError = array_values($this->errors[$alias])[0]; if($lastError->getMessage() === $e->getMessage()){ return; } } array_unshift($this->errors[$alias], $e); if(count($this->errors[$alias]) > 5){ $this->errors[$alias] = array_pop($this->errors[$alias]); } } /** * @return \Log */ static function getLogger() : \Log { return LogController::getLogger('ERROR'); } }