Continued:
[core.git] / framework / main / middleware / database / class_DatabaseConnection.php
1 <?php
2 // Own namespace
3 namespace Org\Mxchange\CoreFramework\Connection\Database;
4
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;
14
15 // Import SPL stuff
16 use \InvalidArgumentException;
17 use \OutOfBoundsException;
18 use \UnexpectedValueException;
19
20 /**
21  * Database connectivity class
22  *
23  * @author              Roland Haeder <webmaster@shipsimu.org>
24  * @version             0.0.0
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
28  *
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.
33  *
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.
38  *
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/>.
41  */
42 class DatabaseConnection extends BaseMiddleware implements DatabaseConnector, Registerable {
43         /**
44          * Array for connection data
45          */
46         private $connectData = [
47                 'login' => '',
48                 'pass'  => '',
49                 'dbase' => '',
50                 'host'  => '',
51         ];
52
53         /**
54          * The real database layer
55          */
56         private $backendInstance = NULL;
57
58         /**
59          * An instance of this class
60          */
61         private static $selfInstance = NULL;
62
63         /**
64          * Protected constructor
65          */
66         private function __construct () {
67                 // Call parent constructor
68                 parent::__construct(__CLASS__);
69         }
70
71         /**
72          * Creates a new database connection layer
73          *
74          * @param       $backendInstance        An instance of a DatabaseBackend class
75          * @todo        $debugInstance is currently not used
76          */
77         public static final function createDatabaseConnection (DatabaseBackend $backendInstance) {
78                 // Get instance
79                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: backendInstance=%s - CALLED!', $backendInstance->__toString()));
80                 $databaseInstance = new DatabaseConnection();
81
82                 // Set database layer
83                 $databaseInstance->setBackendInstance($backendInstance);
84
85                 // Set db instance
86                 self::$selfInstance = $databaseInstance;
87
88                 // Return instance
89                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: databaseInstance=%s - EXIT!', $databaseInstance->__toString()));
90                 return $databaseInstance;
91         }
92
93         /**
94          * Getter for this class
95          *
96          * @return      $selfInstance   An instance of this class
97          */
98         public static final function getSelfInstance () {
99                 return self::$selfInstance;
100         }
101
102         /**
103          * Setter for the real database layer
104          * @param       $backendInstance        An instance of a DatabaseBackend class
105          * @return      void
106          */
107         private final function setBackendInstance (DatabaseBackend $backendInstance) {
108                 $this->backendInstance = $backendInstance;
109         }
110
111         /**
112          * Setter for all database connection data. All these parameters may be
113          * supported by the underlaying backend.
114          *
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)
119          * @return      void
120          * @throws      InvalidArgumentException        If a parameter is empty
121          */
122         public final function setConnectionData (string $login, string $pass, string $dbase, string $host = 'localhost') {
123                 // Check parameter
124                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: login=%s,pass=%s,dbase=%s,host=%s CALLED!', $login, $pass, $dbase, $host));
125                 if (empty($login)) {
126                         // Throw IAE
127                         throw new InvalidArgumentException('Parameter "login" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
128                 } elseif (empty($dbase)) {
129                         // Throw IAE
130                         throw new InvalidArgumentException('Parameter "dbase" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
131                 } elseif (empty($host)) {
132                         // Throw IAE
133                         throw new InvalidArgumentException('Parameter "host" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
134                 }
135
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;
142
143                 // Trace message
144                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: EXIT!');
145         }
146
147         /**
148          * Getter for connection data
149          *
150          * @return      $connectData    Connection data stored with this clas
151          */
152         public final function getConnectionData () {
153                 return $this->connectData;
154         }
155
156         /**
157          * Getter for index key
158          *
159          * @return      $indexKey       Index key
160          */
161         public final function getIndexKey () {
162                 return $this->backendInstance->getIndexKey();
163         }
164
165         /**
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
168          *
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'
175          */
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)) {
180                         // Throw IAE
181                         throw new InvalidArgumentException('Parameter "tableName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
182                 }
183
184                 // Connect to the database
185                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: Invoking this->backendInstance->connectToDatabase() ...');
186                 $this->backendInstance->connectToDatabase();
187
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);
191
192                 // Is a valid array returned?
193                 if (!isset($result[BaseDatabaseResult::RESULT_NAME_STATUS])) {
194                         // Missing element
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])) {
199                                 // Attach it
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);
201                         } else {
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);
204                         }
205                 }
206
207                 // Return the result
208                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: result[]=%s - EXIT!', gettype($result)));
209                 return $result;
210         }
211
212         /**
213          * Getter for last exception
214          *
215          * @return      $exceptionInstance      Last thrown exception
216          */
217         public final function getLastException () {
218                 // Get instance
219                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: CALLED!');
220                 $exceptionInstance = $this->backendInstance->getLastException();
221
222                 // Return it
223                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: exceptionInstance=%s - EXIT!', $exceptionInstance->__toString()));
224                 return $exceptionInstance;
225         }
226
227         /**
228          * 'Inserts' a data set instance into a local file database folder
229          *
230          * @param       $dataSetInstance        A storeable data set
231          * @return      void
232          */
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();
237
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);
241
242                 // Trace message
243                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: EXIT!');
244         }
245
246         /**
247          * 'Updates' a data set instance with a database layer
248          *
249          * @param       $dataSetInstance        A storeable data set
250          * @return      void
251          */
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();
256
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);
260
261                 // Trace message
262                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: EXIT!');
263         }
264
265         /**
266          * Getter for primary key column of specified table name
267          *
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
271          */
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)) {
276                         // Throw IAE
277                         throw new InvalidArgumentException('Parameter "tableName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
278                 }
279
280                 // Connect to the database
281                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: Invoking this->backendInstance->connectToDatabase() ...');
282                 $this->backendInstance->connectToDatabase();
283
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);
287
288                 // Return the value
289                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: primaryKey=%s - CALLED!', $primaryKey));
290                 return $primaryKey;
291         }
292
293         /**
294          * Removes non-public data from given array.
295          *
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
299          */
300         public function removeNonPublicDataFromArray (array $data) {
301                 // Check parameter
302                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: data()=%d - CALLED!', count($data)));
303                 if (count($data) == 0) {
304                         // Throw IAE
305                         throw new InvalidArgumentException('Parameter "data" is an empty array', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
306                 }
307
308                 // Connect to the database
309                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: Invoking this->backendInstance->connectToDatabase() ...');
310                 $this->backendInstance->connectToDatabase();
311
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);
315
316                 // Trace message
317                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: data()=%d - EXIT!', count($data)));
318                 return $data;
319         }
320
321         /**
322          * Count total table rows
323          *
324          * @param       $tableName      Table name
325          * @return      $count          Total row count
326          * @throws      InvalidArgumentException        If a parameter is empty
327          */
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)) {
332                         // Throw IAE
333                         throw new InvalidArgumentException('Parameter "tableName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
334                 }
335
336                 // Connect to the database
337                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('DATABASE-CONNECTION: Invoking this->backendInstance->connectToDatabase() ...');
338                 $this->backendInstance->connectToDatabase();
339
340                 // Ask the database layer
341                 $count = $this->backendInstance->countTotalRows($tableName);
342
343                 // Return the value
344                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('DATABASE-CONNECTION: count=%d - CALLED!', $count));
345                 return $count;
346         }
347
348 }