* @version 0.0.0 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2023 Core Developer Team * @license GNU GPL 3.0 or any newer version * @link http://www.shipsimu.org * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ class DatabaseConnection extends BaseMiddleware implements DatabaseConnector, Registerable { /** * Array for connection data */ private $connectData = [ 'login' => '', 'pass' => '', 'dbase' => '', 'host' => '', ]; /** * The real database layer */ private $backendInstance = NULL; /** * An instance of this class */ private static $selfInstance = NULL; /** * Protected constructor */ private function __construct () { // Call parent constructor parent::__construct(__CLASS__); } /** * Creates a new database connection layer * * @param $backendInstance An instance of a DatabaseBackend class * @todo $debugInstance is currently not used */ public static final function createDatabaseConnection (DatabaseBackend $backendInstance) { // Get instance //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: backendInstance=%s - CALLED!', $backendInstance->__toString())); $databaseInstance = new DatabaseConnection(); // Set database layer $databaseInstance->setBackendInstance($backendInstance); // Set db instance self::$selfInstance = $databaseInstance; // Return instance //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: databaseInstance=%s - EXIT!', $databaseInstance->__toString())); return $databaseInstance; } /** * Getter for this class * * @return $selfInstance An instance of this class */ public static final function getSelfInstance () { return self::$selfInstance; } /** * Setter for the real database layer * @param $backendInstance An instance of a DatabaseBackend class * @return void */ private final function setBackendInstance (DatabaseBackend $backendInstance) { $this->backendInstance = $backendInstance; } /** * Setter for all database connection data. All these parameters may be * supported by the underlaying backend. * * @param $login Login name to database * @param $pass Passwort for above login * @param $dbase Name of used database * @param $host Host to connect to (default: 127.0.0.1) * @return void * @throws InvalidArgumentException If a parameter is empty */ public final function setConnectionData (string $login, string $pass, string $dbase, string $host = 'localhost') { // Check parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: login=%s,pass=%s,dbase=%s,host=%s CALLED!', $login, $pass, $dbase, $host)); if (empty($login)) { // Throw IAE throw new InvalidArgumentException('Parameter "login" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } elseif (empty($dbase)) { // Throw IAE throw new InvalidArgumentException('Parameter "dbase" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } elseif (empty($host)) { // Throw IAE throw new InvalidArgumentException('Parameter "host" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } // Transfer connection data //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage('DATABASE-CONNECTION: Setting data ...'); $this->connectData['login'] = $login; $this->connectData['pass'] = $pass; $this->connectData['dbase'] = $dbase; $this->connectData['host'] = $host; // Trace message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: EXIT!'); } /** * Getter for connection data * * @return $connectData Connection data stored with this clas */ public final function getConnectionData () { return $this->connectData; } /** * Getter for index key * * @return $indexKey Index key */ public final function getIndexKey () { return $this->backendInstance->getIndexKey(); } /** * Runs a 'select' statement on the database layer with given table name * and criteria. If this doesn't fail the result will be returned * * @param $tableName Name of the 'table' we shall query * @param $criteriaInstance An instance of a Criteria class * @return $result The result as an array * @throws InvalidArgumentException If a parameter is empty * @throws OutOfBoundsException If important array elements are not present * @throws UnexpectedValueException If $result['status'] is not 'ok' */ public function doSelectByTableCriteria (string $tableName, Criteria $criteriaInstance) { // Validate parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: tableName=%s,criteriaInstance=%s - CALLED!', $tableName, $criteriaInstance->__toString())); if (empty($tableName)) { // Throw IAE throw new InvalidArgumentException('Parameter "tableName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } // Connect to the database //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: Invoking this->backendInstance->connectToDatabase() ...'); $this->backendInstance->connectToDatabase(); // Get result from query //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: Invoking this->backendInstance->querySelect(%s,%s) ...', $tableName, $criteriaInstance->__toString())); $result = $this->backendInstance->querySelect($tableName, $criteriaInstance); // Is a valid array returned? if (!isset($result[BaseDatabaseResult::RESULT_NAME_STATUS])) { // Missing element throw new OutOfBoundsException(sprintf('result()=%d does not have element "%s"', count($result), BaseDatabaseResult::RESULT_NAME_STATUS), FrameworkInterface::EXCEPTION_OUT_OF_BOUNDS); } elseif ($result[BaseDatabaseResult::RESULT_NAME_STATUS] != 'ok') { // Is exception given? if (isset($result[BaseDatabaseResult::RESULT_NAME_EXCEPTION])) { // Attach it throw new UnexpectedValueException(sprintf('result[%s]=%s is not "ok" with cause', BaseDatabaseResult::RESULT_NAME_STATUS), FrameworkInterface::EXCEPTION_INVALID_ARGUMENT, BaseDatabaseResult::RESULT_NAME_EXCEPTION); } else { // No exception attached throw new UnexpectedValueException(sprintf('result[%s]=%s is not "ok"', BaseDatabaseResult::RESULT_NAME_STATUS, $result[BaseDatabaseResult::RESULT_NAME_STATUS]), FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } } // Return the result //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: result[]=%s - EXIT!', gettype($result))); return $result; } /** * Getter for last exception * * @return $exceptionInstance Last thrown exception */ public final function getLastException () { // Get instance //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: CALLED!'); $exceptionInstance = $this->backendInstance->getLastException(); // Return it //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: exceptionInstance=%s - EXIT!', $exceptionInstance->__toString())); return $exceptionInstance; } /** * 'Inserts' a data set instance into a local file database folder * * @param $dataSetInstance A storeable data set * @return void */ public function queryInsertDataSet (StoreableCriteria $dataSetInstance) { // Connect to the database //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: dataSetInstance=%s - CALLED!', $dataSetInstance->__toString())); $this->backendInstance->connectToDatabase(); // Ask the database layer //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: Invoking this->backendInstance->queryInsertDataSet(%s) ...', $dataSetInstance->__toString())); $this->backendInstance->queryInsertDataSet($dataSetInstance); // Trace message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: EXIT!'); } /** * 'Updates' a data set instance with a database layer * * @param $dataSetInstance A storeable data set * @return void */ public function queryUpdateDataSet (StoreableCriteria $dataSetInstance) { // Connect to the database //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: dataSetInstance=%s - CALLED!', $dataSetInstance->__toString())); $this->backendInstance->connectToDatabase(); // Ask the database layer //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: Invoking this->backendInstance->queryUpdateDataSet(%s) ...', $dataSetInstance->__toString())); $this->backendInstance->queryUpdateDataSet($dataSetInstance); // Trace message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: EXIT!'); } /** * Getter for primary key column of specified table name * * @param $tableName Name of table we need the primary key column from * @return $primaryKey Primary key column of requested table * @throws InvalidArgumentException If a parameter is empty */ public function getPrimaryKeyOfTable (string $tableName) { // Validate parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: tableName=%s - CALLED!', $tableName)); if (empty($tableName)) { // Throw IAE throw new InvalidArgumentException('Parameter "tableName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } // Connect to the database //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: Invoking this->backendInstance->connectToDatabase() ...'); $this->backendInstance->connectToDatabase(); // Ask the database layer //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: Invoking this->backendInstance->getPrimaryKeyOfTable(%s) ...', $tableName)); $primaryKey = $this->backendInstance->getPrimaryKeyOfTable($tableName); // Return the value //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: primaryKey=%s - CALLED!', $primaryKey)); return $primaryKey; } /** * Removes non-public data from given array. * * @param $data An array with possible non-public data that needs to be removed. * @return $data A cleaned up array with only public data. * @throws InvalidArgumentException If a parameter has an invalid value */ public function removeNonPublicDataFromArray (array $data) { // Check parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: data()=%d - CALLED!', count($data))); if (count($data) == 0) { // Throw IAE throw new InvalidArgumentException('Parameter "data" is an empty array', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } // Connect to the database //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: Invoking this->backendInstance->connectToDatabase() ...'); $this->backendInstance->connectToDatabase(); // Call database backend //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: Invoking this->backendInstance->removeNonPublicDataFromArray(data()=%d) ...', count($data))); $data = $this->backendInstance->removeNonPublicDataFromArray($data); // Trace message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: data()=%d - EXIT!', count($data))); return $data; } /** * Count total table rows * * @param $tableName Table name * @return $count Total row count * @throws InvalidArgumentException If a parameter is empty */ public function countTotalRows (string $tableName) { // Validate parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: tableName=%s - CALLED!', $tableName)); if (empty($tableName)) { // Throw IAE throw new InvalidArgumentException('Parameter "tableName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } // Connect to the database //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: Invoking this->backendInstance->connectToDatabase() ...'); $this->backendInstance->connectToDatabase(); // Ask the database layer $count = $this->backendInstance->countTotalRows($tableName); // Return the value //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: count=%d - CALLED!', $count)); return $count; } }