3 namespace Org\Mxchange\CoreFramework\Connection\Database;
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Connector\Database\DatabaseConnector;
7 use Org\Mxchange\CoreFramework\Criteria\Criteria;
8 use Org\Mxchange\CoreFramework\Criteria\Storing\StoreableCriteria;
9 use Org\Mxchange\CoreFramework\Database\Backend\DatabaseBackend;
10 use Org\Mxchange\CoreFramework\Generic\FrameworkInterface;
11 use Org\Mxchange\CoreFramework\Middleware\BaseMiddleware;
12 use Org\Mxchange\CoreFramework\Registry\Registerable;
13 use Org\Mxchange\CoreFramework\Result\Database\BaseDatabaseResult;
16 use \InvalidArgumentException;
17 use \OutOfBoundsException;
18 use \UnexpectedValueException;
21 * Database selector class
23 * @author Roland Haeder <webmaster@shipsimu.org>
25 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2023 Core Developer Team
26 * @license GNU GPL 3.0 or any newer version
27 * @link http://www.shipsimu.org
29 * This program is free software: you can redistribute it and/or modify
30 * it under the terms of the GNU General Public License as published by
31 * the Free Software Foundation, either version 3 of the License, or
32 * (at your option) any later version.
34 * This program is distributed in the hope that it will be useful,
35 * but WITHOUT ANY WARRANTY; without even the implied warranty of
36 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37 * GNU General Public License for more details.
39 * You should have received a copy of the GNU General Public License
40 * along with this program. If not, see <http://www.gnu.org/licenses/>.
42 class DatabaseConnection extends BaseMiddleware implements DatabaseConnector, Registerable {
44 * Array for connection data
46 private $connectData = [
54 * The real database layer
56 private $backendInstance = NULL;
59 * An instance of this class
61 private static $selfInstance = NULL;
64 * Protected constructor
66 private function __construct () {
67 // Call parent constructor
68 parent::__construct(__CLASS__);
72 * Creates a new database connection layer
74 * @param $backendInstance An instance of a DatabaseBackend class
75 * @todo $debugInstance is currently not used
77 public static final function createDatabaseConnection (DatabaseBackend $backendInstance) {
79 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: backendInstance=%s - CALLED!', $backendInstance->__toString()));
80 $databaseInstance = new DatabaseConnection();
83 $databaseInstance->setBackendInstance($backendInstance);
86 self::$selfInstance = $databaseInstance;
89 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: databaseInstance=%s - EXIT!', $databaseInstance->__toString()));
90 return $databaseInstance;
94 * Getter for this class
96 * @return $selfInstance An instance of this class
98 public static final function getSelfInstance () {
99 return self::$selfInstance;
103 * Setter for the real database layer
104 * @param $backendInstance An instance of a DatabaseBackend class
107 private final function setBackendInstance (DatabaseBackend $backendInstance) {
108 $this->backendInstance = $backendInstance;
112 * Setter for all database connection data. All these parameters may be
113 * supported by the underlaying backend.
115 * @param $login Login name to database
116 * @param $pass Passwort for above login
117 * @param $dbase Name of used database
118 * @param $host Host to connect to (default: 127.0.0.1)
120 * @throws InvalidArgumentException If a parameter is empty
122 public final function setConnectionData (string $login, string $pass, string $dbase, string $host = 'localhost') {
124 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: login=%s,pass=%s,dbase=%s,host=%s CALLED!', $login, $pass, $dbase, $host));
127 throw new InvalidArgumentException('Parameter "login" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
128 } elseif (empty($dbase)) {
130 throw new InvalidArgumentException('Parameter "dbase" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
131 } elseif (empty($host)) {
133 throw new InvalidArgumentException('Parameter "host" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
136 // Transfer connection data
137 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage('DATABASE-CONNECTION: Setting data ...');
138 $this->connectData['login'] = $login;
139 $this->connectData['pass'] = $pass;
140 $this->connectData['dbase'] = $dbase;
141 $this->connectData['host'] = $host;
144 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: EXIT!');
148 * Getter for connection data
150 * @return $connectData Connection data stored with this clas
152 public final function getConnectionData () {
153 return $this->connectData;
157 * Getter for index key
159 * @return $indexKey Index key
161 public final function getIndexKey () {
162 return $this->backendInstance->getIndexKey();
166 * Runs a 'select' statement on the database layer with given table name
167 * and criteria. If this doesn't fail the result will be returned
169 * @param $tableName Name of the 'table' we shall query
170 * @param $criteriaInstance An instance of a Criteria class
171 * @return $result The result as an array
172 * @throws InvalidArgumentException If a parameter is empty
173 * @throws OutOfBoundsException If important array elements are not present
174 * @throws UnexpectedValueException If $result['status'] is not 'ok'
176 public function doSelectByTableCriteria (string $tableName, Criteria $criteriaInstance) {
177 // Validate parameter
178 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: tableName=%s,criteriaInstance=%s - CALLED!', $tableName, $criteriaInstance->__toString()));
179 if (empty($tableName)) {
181 throw new InvalidArgumentException('Parameter "tableName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
184 // Connect to the database
185 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: Invoking this->backendInstance->connectToDatabase() ...');
186 $this->backendInstance->connectToDatabase();
188 // Get result from query
189 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: Invoking this->backendInstance->querySelect(%s,%s) ...', $tableName, $criteriaInstance->__toString()));
190 $result = $this->backendInstance->querySelect($tableName, $criteriaInstance);
192 // Is a valid array returned?
193 if (!isset($result[BaseDatabaseResult::RESULT_NAME_STATUS])) {
195 throw new OutOfBoundsException(sprintf('result()=%d does not have element "%s"', count($result), BaseDatabaseResult::RESULT_NAME_STATUS), FrameworkInterface::EXCEPTION_OUT_OF_BOUNDS);
196 } elseif ($result[BaseDatabaseResult::RESULT_NAME_STATUS] != 'ok') {
197 // Is exception given?
198 if (isset($result[BaseDatabaseResult::RESULT_NAME_EXCEPTION])) {
200 throw new UnexpectedValueException(sprintf('result[%s]=%s is not "ok" with cause', BaseDatabaseResult::RESULT_NAME_STATUS), FrameworkInterface::EXCEPTION_INVALID_ARGUMENT, BaseDatabaseResult::RESULT_NAME_EXCEPTION);
202 // No exception attached
203 throw new UnexpectedValueException(sprintf('result[%s]=%s is not "ok"', BaseDatabaseResult::RESULT_NAME_STATUS, $result[BaseDatabaseResult::RESULT_NAME_STATUS]), FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
208 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: result[]=%s - EXIT!', gettype($result)));
213 * Getter for last exception
215 * @return $exceptionInstance Last thrown exception
217 public final function getLastException () {
219 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: CALLED!');
220 $exceptionInstance = $this->backendInstance->getLastException();
223 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: exceptionInstance=%s - EXIT!', $exceptionInstance->__toString()));
224 return $exceptionInstance;
228 * 'Inserts' a data set instance into a local file database folder
230 * @param $dataSetInstance A storeable data set
233 public function queryInsertDataSet (StoreableCriteria $dataSetInstance) {
234 // Connect to the database
235 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: dataSetInstance=%s - CALLED!', $dataSetInstance->__toString()));
236 $this->backendInstance->connectToDatabase();
238 // Ask the database layer
239 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: Invoking this->backendInstance->queryInsertDataSet(%s) ...', $dataSetInstance->__toString()));
240 $this->backendInstance->queryInsertDataSet($dataSetInstance);
243 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: EXIT!');
247 * 'Updates' a data set instance with a database layer
249 * @param $dataSetInstance A storeable data set
252 public function queryUpdateDataSet (StoreableCriteria $dataSetInstance) {
253 // Connect to the database
254 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: dataSetInstance=%s - CALLED!', $dataSetInstance->__toString()));
255 $this->backendInstance->connectToDatabase();
257 // Ask the database layer
258 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: Invoking this->backendInstance->queryUpdateDataSet(%s) ...', $dataSetInstance->__toString()));
259 $this->backendInstance->queryUpdateDataSet($dataSetInstance);
262 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: EXIT!');
266 * Getter for primary key column of specified table name
268 * @param $tableName Name of table we need the primary key column from
269 * @return $primaryKey Primary key column of requested table
270 * @throws InvalidArgumentException If a parameter is empty
272 public function getPrimaryKeyOfTable (string $tableName) {
273 // Validate parameter
274 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: tableName=%s - CALLED!', $tableName));
275 if (empty($tableName)) {
277 throw new InvalidArgumentException('Parameter "tableName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
280 // Connect to the database
281 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: Invoking this->backendInstance->connectToDatabase() ...');
282 $this->backendInstance->connectToDatabase();
284 // Ask the database layer
285 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: Invoking this->backendInstance->getPrimaryKeyOfTable(%s) ...', $tableName));
286 $primaryKey = $this->backendInstance->getPrimaryKeyOfTable($tableName);
289 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: primaryKey=%s - CALLED!', $primaryKey));
294 * Removes non-public data from given array.
296 * @param $data An array with possible non-public data that needs to be removed.
297 * @return $data A cleaned up array with only public data.
298 * @throws InvalidArgumentException If a parameter has an invalid value
300 public function removeNonPublicDataFromArray (array $data) {
302 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: data()=%d - CALLED!', count($data)));
303 if (count($data) == 0) {
305 throw new InvalidArgumentException('Parameter "data" is an empty array', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
308 // Connect to the database
309 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: Invoking this->backendInstance->connectToDatabase() ...');
310 $this->backendInstance->connectToDatabase();
312 // Call database backend
313 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: Invoking this->backendInstance->removeNonPublicDataFromArray(data()=%d) ...', count($data)));
314 $data = $this->backendInstance->removeNonPublicDataFromArray($data);
317 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: data()=%d - EXIT!', count($data)));
322 * Count total table rows
324 * @param $tableName Table name
325 * @return $count Total row count
326 * @throws InvalidArgumentException If a parameter is empty
328 public function countTotalRows (string $tableName) {
329 // Validate parameter
330 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: tableName=%s - CALLED!', $tableName));
331 if (empty($tableName)) {
333 throw new InvalidArgumentException('Parameter "tableName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
336 // Connect to the database
337 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: Invoking this->backendInstance->connectToDatabase() ...');
338 $this->backendInstance->connectToDatabase();
340 // Ask the database layer
341 $count = $this->backendInstance->countTotalRows($tableName);
344 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: count=%d - CALLED!', $count));