]> git.mxchange.org Git - core.git/blob - framework/main/classes/database/frontend/class_BaseDatabaseFrontend.php
78665d33715e5b278c642266ba10edaa117a4890
[core.git] / framework / main / classes / database / frontend / class_BaseDatabaseFrontend.php
1 <?php
2 // Own namespace
3 namespace Org\Mxchange\CoreFramework\Database\Frontend;
4
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Bootstrap\FrameworkBootstrap;
7 use Org\Mxchange\CoreFramework\Criteria\Criteria;
8 use Org\Mxchange\CoreFramework\Criteria\Storing\StoreableCriteria;
9 use Org\Mxchange\CoreFramework\Database\Backend\BaseDatabaseBackend;
10 use Org\Mxchange\CoreFramework\Factory\Object\ObjectFactory;
11 use Org\Mxchange\CoreFramework\Object\BaseFrameworkSystem;
12 use Org\Mxchange\CoreFramework\Result\Database\BaseDatabaseResult;
13 use Org\Mxchange\CoreFramework\Traits\Cache\CacheableTrait;
14
15 // Import SPL stuff
16 use \InvalidArgumentException;
17
18 /**
19  * A generic database frontend
20  *
21  * @author              Roland Haeder <webmaster@shipsimu.org>
22  * @version             0.0.0
23  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2023 Core Developer Team
24  * @license             GNU GPL 3.0 or any newer version
25  * @link                http://www.shipsimu.org
26  *
27  * This program is free software: you can redistribute it and/or modify
28  * it under the terms of the GNU General Public License as published by
29  * the Free Software Foundation, either version 3 of the License, or
30  * (at your option) any later version.
31  *
32  * This program is distributed in the hope that it will be useful,
33  * but WITHOUT ANY WARRANTY; without even the implied warranty of
34  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
35  * GNU General Public License for more details.
36  *
37  * You should have received a copy of the GNU General Public License
38  * along with this program. If not, see <http://www.gnu.org/licenses/>.
39  */
40 abstract class BaseDatabaseFrontend extends BaseFrameworkSystem {
41         // Load traits
42         use CacheableTrait;
43
44         /**
45          * Current table name to use
46          */
47         private $tableName = 'unknown';
48
49         /**
50          * "Cached" value 'database_cache_enabled' from configuration
51          */
52         private $databaseCacheEnabled = false;
53
54         /**
55          * Protected constructor
56          *
57          * @param       $className      Name of the class
58          * @return      void
59          */
60         protected function __construct (string $className) {
61                 // Call parent constructor
62                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-DATABASE-FRONTEND: className=%s - CONSTRUCTED!', $className));
63                 parent::__construct($className);
64
65                 // Initialize the cache instance
66                 $this->initCacheInstance();
67
68                 // Trace message
69                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-DATABASE-FRONTEND: EXIT!');
70         }
71
72         /**
73          * Initializes the cache instance with a new object
74          *
75          * @return      void
76          */
77         private final function initCacheInstance () {
78                 // Set "cache" attributes
79                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-DATABASE-FRONTEND: CALLED!');
80                 $this->databaseCacheEnabled = FrameworkBootstrap::getConfigurationInstance()->isEnabled('database_cache');
81
82                 // Is the cache enabled?
83                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-DATABASE-FRONTEND: this->databaseCacheEnabled=%d', intval($this->databaseCacheEnabled)));
84                 if ($this->databaseCacheEnabled === true) {
85                         // Set the new instance
86                         $this->setCacheInstance(ObjectFactory::createObjectByConfiguredName('cache_class'));
87                 }
88
89                 // Trace message
90                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-DATABASE-FRONTEND: EXIT!');
91         }
92
93         /**
94          * Setter for table name
95          *
96          * @param       $tableName      Name of table name to set
97          * @return      void
98          */
99         protected final function setTableName (string $tableName) {
100                 $this->tableName = $tableName;
101         }
102
103         /**
104          * Getter for table name
105          *
106          * @return      $tableName      Name of table name to set
107          */
108         protected final function getTableName () {
109                 return $this->tableName;
110         }
111
112         /**
113          * Gets a cache key from Criteria instance
114          *
115          * @param       $criteriaInstance       An instance of a Criteria class
116          * @param       $onlyKeys                       Only use these keys for a cache key
117          * @return      $cacheKey                       A cache key suitable for lookup/storage purposes
118          */
119         protected function getCacheKeyByCriteria (Criteria $criteriaInstance, array $onlyKeys = []) {
120                 // Generate it
121                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-DATABASE-FRONTEND: criteriaInstance=%s,onlyKeys()=%d - CALLED!', $criteriaInstance->__toString(), count($onlyKeys)));
122                 $cacheKey = sprintf('%s@%s',
123                         $this->__toString(),
124                         $criteriaInstance->getCacheKey($onlyKeys)
125                 );
126
127                 // And return it
128                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-FRAMEWORK-SYSTEM: cacheKey=' . $cacheKey . ' - EXIT!');
129                 return $cacheKey;
130         }
131
132         /**
133          * 'Inserts' a data set instance into a local file database folder
134          *
135          * @param       $dataSetInstance        A storeable data set
136          * @param       $onlyKeys                       Only use these keys for a cache key
137          * @return      void
138          */
139         protected function queryInsertDataSet (StoreableCriteria $dataSetInstance, array $onlyKeys = []) {
140                 // Default cache key is NULL
141                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-DATABASE-FRONTEND: dataSetInstance=%s,onlyKeys()=%d - CALLED!', $dataSetInstance->__toString(), count($onlyKeys)));
142                 $cacheKey = NULL;
143
144                 // Is cache enabled?
145                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-DATABASE-FRONTEND: this->databaseCacheEnabled=%d', intval($this->databaseCacheEnabled)));
146                 if ($this->databaseCacheEnabled === true) {
147                         // First get a key suitable for our cache and extend it with this class name
148                         $cacheKey = $this->getCacheKeyByCriteria($dataSetInstance, $onlyKeys);
149                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-DATABASE-FRONTEND: Using cache key %s for purging ...', $cacheKey));
150                 }
151
152                 // Does this key exists in cache?
153                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-DATABASE-FRONTEND: this->databaseCacheEnabled=%d,cacheKey[%s]=%s', intval($this->databaseCacheEnabled), gettype($cacheKey), $cacheKey));
154                 if (($this->databaseCacheEnabled === true) && ($this->getCacheInstance()->offsetExists($cacheKey))) {
155                         // Purge the cache
156                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-DATABASE-FRONTEND: Invoking this->cacheInstance->purgeOffset(%s) ...', $cacheKey));
157                         $this->getCacheInstance()->purgeOffset($cacheKey);
158                 }
159
160                 // Handle it over to the middleware
161                 FrameworkBootstrap::getDatabaseInstance()->queryInsertDataSet($dataSetInstance);
162
163                 // Trace message
164                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-DATABASE-FRONTEND: EXIT!');
165         }
166
167         /**
168          * 'Updates' a data set instance with a database layer
169          *
170          * @param       $dataSetInstance        A storeable data set
171          * @param       $onlyKeys                       Only use these keys for a cache key
172          * @return      void
173          */
174         protected function queryUpdateDataSet (StoreableCriteria $dataSetInstance, array $onlyKeys = []) {
175                 // Init cache key
176                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-DATABASE-FRONTEND: dataSetInstance=%s,onlyKeys()=%d - CALLED!', $dataSetInstance->__toString(), count($onlyKeys)));
177                 $cacheKey = NULL;
178
179                 // Is cache enabled?
180                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-DATABASE-FRONTEND: this->databaseCacheEnabled=%d', intval($this->databaseCacheEnabled)));
181                 if ($this->databaseCacheEnabled === true) {
182                         // First get a key suitable for our cache and extend it with this class name
183                         $cacheKey = $this->getCacheKeyByCriteria($dataSetInstance, $onlyKeys);
184                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage('BASE-DATABASE-FRONTEND: Using cache key ' . $cacheKey . ' for purging ...');
185                 }
186
187                 // Does this key exists in cache?
188                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-DATABASE-FRONTEND: this->databaseCacheEnabled=%d,cacheKey=%s', intval($this->databaseCacheEnabled), $cacheKey));
189                 if (($this->databaseCacheEnabled === true) && ($this->getCacheInstance()->offsetExists($cacheKey))) {
190                         // Purge the cache
191                         $this->getCacheInstance()->purgeOffset($cacheKey);
192                 }
193
194                 // Handle it over to the middleware
195                 FrameworkBootstrap::getDatabaseInstance()->queryUpdateDataSet($dataSetInstance);
196
197                 // Trace message
198                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-DATABASE-FRONTEND: EXIT!');
199         }
200
201         /**
202          * Getter for index key
203          *
204          * @return      $indexKey       Index key
205          */
206         public final function getIndexKey () {
207                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-DATABASE-FRONTEND: CALLED!');
208                 return FrameworkBootstrap::getDatabaseInstance()->getIndexKey();
209         }
210
211         /**
212          * Getter for last exception
213          *
214          * @return      $lastException  Last exception or NULL if none occured
215          */
216         public final function getLastException () {
217                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-DATABASE-FRONTEND: CALLED!');
218                 return FrameworkBootstrap::getDatabaseInstance()->getLastException();
219         }
220
221         /**
222          * Do a "select" query on the current table with the given search criteria and
223          * store it in cache for later usage
224          *
225          * @param       $criteriaInstance       An instance of a Criteria class
226          * @param       $onlyKeys                       Only use these keys for a cache key
227          * @return      $resultInstance         An instance of a database result class
228          */
229         public function doSelectByCriteria (Criteria $criteriaInstance, array $onlyKeys = []) {
230                 // Default cache key if cache is not enabled
231                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-DATABASE-FRONTEND: criteriaInstance=%s,onlyKeys()=%d - CALLED!', $criteriaInstance->__toString(), count($onlyKeys)));
232                 $cacheKey = NULL;
233                 $result = [];
234
235                 // Is the cache enabled?
236                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-DATABASE-FRONTEND: this->databaseCacheEnabled=%d', intval($this->databaseCacheEnabled)));
237                 if ($this->databaseCacheEnabled === true) {
238                         // First get a key suitable for our cache and extend it with this class name
239                         $cacheKey = $this->getCacheKeyByCriteria($criteriaInstance, $onlyKeys);
240                 }
241
242                 // Does this key exists in cache?
243                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-DATABASE-FRONTEND: this->databaseCacheEnabled=%d,cacheKey[%s]=%s', intval($this->databaseCacheEnabled), gettype($cacheKey), $cacheKey));
244                 if (($this->databaseCacheEnabled === true) && ($this->getCacheInstance()->offsetExists($cacheKey, BaseDatabaseResult::RESULT_NAME_ROWS, 1))) {
245                         // Then use this result
246                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-DATABASE-FRONTEND: Cache used for cacheKey=%s', $cacheKey));
247                         $result = $this->getCacheInstance()->offsetGet($cacheKey);
248                 } else {
249                         // Now it's time to ask the database layer for this select statement
250                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-DATABASE-FRONTEND: Quering database, cacheKey=%s ...', $cacheKey));
251                         $result = FrameworkBootstrap::getDatabaseInstance()->doSelectByTableCriteria($this->getTableName(), $criteriaInstance);
252
253                         // Cache the result if not null
254                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-DATABASE-FRONTEND: result[]=%s', gettype($result)));
255                         if (!is_null($result)) {
256                                 // Is cache enabled?
257                                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-DATABASE-FRONTEND: this->databaseCacheEnabled=%d', intval($this->databaseCacheEnabled)));
258                                 if ($this->databaseCacheEnabled === true) {
259                                         // A valid result has returned from the database layer
260                                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-DATABASE-FRONTEND: Setting cacheKey=%s with result()=%d entries', $cacheKey, count($result)));
261                                         $this->getCacheInstance()->offsetSet($cacheKey, $result);
262                                 }
263                         } else {
264                                 // This invalid result must be wrapped
265                                 $result = [
266                                         BaseDatabaseResult::RESULT_NAME_STATUS    => 'invalid',
267                                         BaseDatabaseResult::RESULT_NAME_EXCEPTION => FrameworkBootstrap::getDatabaseInstance()->getLastException(),
268                                 ];
269                         }
270                 }
271
272                 // Create an instance of a CachedDatabaseResult class with the given result
273                 // @TODO Minor: Update above comment to e.g. BaseDatabaseResult
274                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-DATABASE-FRONTEND: result[%s]=%s,result[%s]?=%d,result[%s]?=%d', BaseDatabaseResult::RESULT_NAME_STATUS, $result[BaseDatabaseResult::RESULT_NAME_STATUS], BaseDatabaseResult::RESULT_NAME_ROWS, isset($result[BaseDatabaseResult::RESULT_NAME_ROWS]), BaseDatabaseResult::RESULT_NAME_EXCEPTION, isset($result[BaseDatabaseResult::RESULT_NAME_EXCEPTION])));
275                 $resultInstance = ObjectFactory::createObjectByConfiguredName('database_result_class', array($result));
276
277                 // And return the instance
278                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-DATABASE-FRONTEND: resultInstance=%s - EXIT!', $resultInstance->__toString()));
279                 return $resultInstance;
280         }
281
282         /**
283          * Count the numbers of rows we shall receive
284          *
285          * @param       $criteriaInstance       An instance of a Criteria class
286          * @param       $onlyKeys                       Only use these keys for a cache key
287          * @return      $numRows                        Numbers of rows of database entries
288          */
289         public function doSelectCountByCriteria (Criteria $criteriaInstance, array $onlyKeys = []) {
290                 // Total numbers is -1 so we can distinglish between failed and valid queries
291                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-DATABASE-FRONTEND: criteriaInstance=%s,onlyKeys()=%d - CALLED!', $criteriaInstance->__toString(), count($onlyKeys)));
292                 $numRows = 0;
293
294                 // Get the result from above method
295                 $resultInstance = $this->doSelectByCriteria($criteriaInstance, $onlyKeys);
296
297                 // Was that query fine?
298                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-DATABASE-FRONTEND: resultInstance->ifStatusIsOkay()=%d', $resultInstance->ifStatusIsOkay()));
299                 if ($resultInstance->ifStatusIsOkay()) {
300                         // Then get the number of rows
301                         $numRows = $resultInstance->getAffectedRows();
302                 }
303
304                 // Return the result
305                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-DATABASE-FRONTEND: numRows=%d - EXIT!', $numRows));
306                 return $numRows;
307         }
308
309         /**
310          * Generates a primary key for this database frontend
311          *
312          * @return      $primaryKey             Primary key used in wrapped table
313          */
314         public final function generatePrimaryKey () {
315                 // Get the table name and a database instance and ask for it
316                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-DATABASE-FRONTEND: CALLED!');
317                 $primaryKey = FrameworkBootstrap::getDatabaseInstance()->getPrimaryKeyOfTable($this->getTableName());
318
319                 // Return value
320                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-DATABASE-FRONTEND: primaryKey=%s - EXIT!', $primaryKey));
321                 return $primaryKey;
322         }
323
324         /**
325          * Count rows of this table
326          *
327          * @return      $count  Count of total rows in this table
328          */
329         public final function countTotalRows () {
330                 // Get the table name and a database instance and ask for it
331                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-DATABASE-FRONTEND: CALLED!');
332                 $count = FrameworkBootstrap::getDatabaseInstance()->countTotalRows($this->getTableName());
333
334                 // Return value
335                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-DATABASE-FRONTEND: count=%d - EXIT!', $count));
336                 return $count;
337         }
338
339         /**
340          * Removes non-public data from given array.
341          *
342          * @param       $data   An array with possible non-public data that needs to be removed.
343          * @return      $data   A cleaned up array with only public data.
344          * @throws      InvalidArgumentException        If a parameter has an invalid value
345          */
346         public function removeNonPublicDataFromArray (array $data) {
347                 // Check parameter
348                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-DATABASE-FRONTEND: data()=%d - CALLED!', count($data)));
349                 if (count($data) == 0) {
350                         // Throw IAE
351                         throw new InvalidArgumentException('Parameter "data" is an empty array', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
352                 }
353
354                 // Remove non-public data (aka. sensitive)
355                 $data = FrameworkBootstrap::getDatabaseInstance()->removeNonPublicDataFromArray($data);
356
357                 // Return cleaned array
358                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-DATABASE-FRONTEND: data()=%d - EXIT!', count($data)));
359                 return $data;
360         }
361
362 }