]> git.mxchange.org Git - core.git/blob - framework/main/classes/database/frontend/class_BaseDatabaseFrontend.php
Continued:
[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          * Protected constructor
55          *
56          * @param       $className      Name of the class
57          * @return      void
58          */
59         protected function __construct (string $className) {
60                 // Call parent constructor
61                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-DATABASE-FRONTEND: CONSTRUCTED!');
62                 parent::__construct($className);
63
64                 // Initialize the cache instance
65                 $this->initCacheInstance();
66
67                 // Trace message
68                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-DATABASE-FRONTEND: EXIT!');
69         }
70
71         /**
72          * Initializes the cache instance with a new object
73          *
74          * @return      void
75          */
76         private final function initCacheInstance () {
77                 // Set "cache" attributes
78                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-DATABASE-FRONTEND: CALLED!');
79                 $this->databaseCacheEnabled = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('database_cache_enabled');
80
81                 // Is the cache enabled?
82                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: this->databaseCacheEnabled=%d', intval($this->databaseCacheEnabled)));
83                 if ($this->databaseCacheEnabled === true) {
84                         // Set the new instance
85                         $this->setCacheInstance(ObjectFactory::createObjectByConfiguredName('cache_class'));
86                 }
87
88                 // Trace message
89                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-DATABASE-FRONTEND: EXIT!');
90         }
91
92         /**
93          * Setter for table name
94          *
95          * @param       $tableName      Name of table name to set
96          * @return      void
97          */
98         protected final function setTableName (string $tableName) {
99                 $this->tableName = $tableName;
100         }
101
102         /**
103          * Getter for table name
104          *
105          * @return      $tableName      Name of table name to set
106          */
107         protected final function getTableName () {
108                 return $this->tableName;
109         }
110
111         /**
112          * Gets a cache key from Criteria instance
113          *
114          * @param       $criteriaInstance       An instance of a Criteria class
115          * @param       $onlyKeys                       Only use these keys for a cache key
116          * @return      $cacheKey                       A cache key suitable for lookup/storage purposes
117          */
118         protected function getCacheKeyByCriteria (Criteria $criteriaInstance, array $onlyKeys = []) {
119                 // Generate it
120                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: criteriaInstance=%s,onlyKeys()=%d - CALLED!', $criteriaInstance->__toString(), count($onlyKeys)));
121                 $cacheKey = sprintf('%s@%s',
122                         $this->__toString(),
123                         $criteriaInstance->getCacheKey($onlyKeys)
124                 );
125
126                 // And return it
127                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FRAMEWORK-SYSTEM: cacheKey=' . $cacheKey . ' - EXIT!');
128                 return $cacheKey;
129         }
130
131         /**
132          * 'Inserts' a data set instance into a local file database folder
133          *
134          * @param       $dataSetInstance        A storeable data set
135          * @param       $onlyKeys                       Only use these keys for a cache key
136          * @return      void
137          */
138         protected function queryInsertDataSet (StoreableCriteria $dataSetInstance, array $onlyKeys = []) {
139                 // Default cache key is NULL
140                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: dataSetInstance=%s,onlyKeys()=%d - CALLED!', $dataSetInstance->__toString(), count($onlyKeys)));
141                 $cacheKey = NULL;
142
143                 // Is cache enabled?
144                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: this->databaseCacheEnabled=%d', intval($this->databaseCacheEnabled)));
145                 if ($this->databaseCacheEnabled === true) {
146                         // First get a key suitable for our cache and extend it with this class name
147                         $cacheKey = $this->getCacheKeyByCriteria($dataSetInstance, $onlyKeys);
148                         //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: Using cache key %s for purging ...', $cacheKey));
149                 }
150
151                 // Does this key exists in cache?
152                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: this->databaseCacheEnabled=%d,cacheKey[%s]=%s', intval($this->databaseCacheEnabled), gettype($cacheKey), $cacheKey));
153                 if (($this->databaseCacheEnabled === true) && ($this->getCacheInstance()->offsetExists($cacheKey))) {
154                         // Purge the cache
155                         //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: Invoking this->cacheInstance->purgeOffset(%s) ...', $cacheKey));
156                         $this->getCacheInstance()->purgeOffset($cacheKey);
157                 }
158
159                 // Handle it over to the middleware
160                 FrameworkBootstrap::getDatabaseInstance()->queryInsertDataSet($dataSetInstance);
161
162                 // Trace message
163                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-DATABASE-FRONTEND: EXIT!');
164         }
165
166         /**
167          * 'Updates' a data set instance with a database layer
168          *
169          * @param       $dataSetInstance        A storeable data set
170          * @param       $onlyKeys                       Only use these keys for a cache key
171          * @return      void
172          */
173         protected function queryUpdateDataSet (StoreableCriteria $dataSetInstance, array $onlyKeys = []) {
174                 // Init cache key
175                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: dataSetInstance=%s,onlyKeys()=%d - CALLED!', $dataSetInstance->__toString(), count($onlyKeys)));
176                 $cacheKey = NULL;
177
178                 // Is cache enabled?
179                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: this->databaseCacheEnabled=%d', intval($this->databaseCacheEnabled)));
180                 if ($this->databaseCacheEnabled === true) {
181                         // First get a key suitable for our cache and extend it with this class name
182                         $cacheKey = $this->getCacheKeyByCriteria($dataSetInstance, $onlyKeys);
183                         //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-DATABASE-FRONTEND: Using cache key ' . $cacheKey . ' for purging ...');
184                 }
185
186                 // Does this key exists in cache?
187                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: this->databaseCacheEnabled=%d,cacheKey=%s', intval($this->databaseCacheEnabled), $cacheKey));
188                 if (($this->databaseCacheEnabled === true) && ($this->getCacheInstance()->offsetExists($cacheKey))) {
189                         // Purge the cache
190                         $this->getCacheInstance()->purgeOffset($cacheKey);
191                 }
192
193                 // Handle it over to the middleware
194                 FrameworkBootstrap::getDatabaseInstance()->queryUpdateDataSet($dataSetInstance);
195
196                 // Trace message
197                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-DATABASE-FRONTEND: EXIT!');
198         }
199
200         /**
201          * Getter for index key
202          *
203          * @return      $indexKey       Index key
204          */
205         public final function getIndexKey () {
206                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-DATABASE-FRONTEND: CALLED!');
207                 return FrameworkBootstrap::getDatabaseInstance()->getIndexKey();
208         }
209
210         /**
211          * Getter for last exception
212          *
213          * @return      $lastException  Last exception or NULL if none occured
214          */
215         public final function getLastException () {
216                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-DATABASE-FRONTEND: CALLED!');
217                 return FrameworkBootstrap::getDatabaseInstance()->getLastException();
218         }
219
220         /**
221          * Do a "select" query on the current table with the given search criteria and
222          * store it in cache for later usage
223          *
224          * @param       $criteriaInstance       An instance of a Criteria class
225          * @param       $onlyKeys                       Only use these keys for a cache key
226          * @return      $resultInstance         An instance of a database result class
227          */
228         public function doSelectByCriteria (Criteria $criteriaInstance, array $onlyKeys = []) {
229                 // Default cache key if cache is not enabled
230                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: criteriaInstance=%s,onlyKeys()=%d - CALLED!', $criteriaInstance->__toString(), count($onlyKeys)));
231                 $cacheKey = NULL;
232                 $result = [];
233
234                 // Is the cache enabled?
235                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: this->databaseCacheEnabled=%d', intval($this->databaseCacheEnabled)));
236                 if ($this->databaseCacheEnabled === true) {
237                         // First get a key suitable for our cache and extend it with this class name
238                         $cacheKey = $this->getCacheKeyByCriteria($criteriaInstance, $onlyKeys);
239                 }
240
241                 // Does this key exists in cache?
242                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: this->databaseCacheEnabled=%d,cacheKey[%s]=%s', intval($this->databaseCacheEnabled), gettype($cacheKey), $cacheKey));
243                 if (($this->databaseCacheEnabled === true) && ($this->getCacheInstance()->offsetExists($cacheKey, BaseDatabaseResult::RESULT_NAME_ROWS, 1))) {
244                         // Then use this result
245                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: Cache used for cacheKey=%s', $cacheKey));
246                         $result = $this->getCacheInstance()->offsetGet($cacheKey);
247                 } else {
248                         // Now it's time to ask the database layer for this select statement
249                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: Quering database, cacheKey=%s ...', $cacheKey));
250                         $result = FrameworkBootstrap::getDatabaseInstance()->doSelectByTableCriteria($this->getTableName(), $criteriaInstance);
251
252                         // Cache the result if not null
253                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: result[]=%s', gettype($result)));
254                         if (!is_null($result)) {
255                                 // Is cache enabled?
256                                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: this->databaseCacheEnabled=%d', intval($this->databaseCacheEnabled)));
257                                 if ($this->databaseCacheEnabled === true) {
258                                         // A valid result has returned from the database layer
259                                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: Setting cacheKey=%s with result()=%d entries', $cacheKey, count($result)));
260                                         $this->getCacheInstance()->offsetSet($cacheKey, $result);
261                                 }
262                         } else {
263                                 // This invalid result must be wrapped
264                                 $result = array(
265                                         BaseDatabaseResult::RESULT_NAME_STATUS    => 'invalid',
266                                         BaseDatabaseResult::RESULT_NAME_EXCEPTION => FrameworkBootstrap::getDatabaseInstance()->getLastException()
267                                 );
268                         }
269                 }
270
271                 // Create an instance of a CachedDatabaseResult class with the given result
272                 // @TODO Minor: Update above comment to e.g. BaseDatabaseResult
273                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(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])));
274                 $resultInstance = ObjectFactory::createObjectByConfiguredName('database_result_class', array($result));
275
276                 // And return the instance
277                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: resultInstance=%s - EXIT!', $resultInstance->__toString()));
278                 return $resultInstance;
279         }
280
281         /**
282          * Count the numbers of rows we shall receive
283          *
284          * @param       $criteriaInstance       An instance of a Criteria class
285          * @param       $onlyKeys                       Only use these keys for a cache key
286          * @return      $numRows                        Numbers of rows of database entries
287          */
288         public function doSelectCountByCriteria (Criteria $criteriaInstance, array $onlyKeys = []) {
289                 // Total numbers is -1 so we can distinglish between failed and valid queries
290                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: criteriaInstance=%s,onlyKeys()=%d - CALLED!', $criteriaInstance->__toString(), count($onlyKeys)));
291                 $numRows = 0;
292
293                 // Get the result from above method
294                 $resultInstance = $this->doSelectByCriteria($criteriaInstance, $onlyKeys);
295
296                 // Was that query fine?
297                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: resultInstance->ifStatusIsOkay()=%d', $resultInstance->ifStatusIsOkay()));
298                 if ($resultInstance->ifStatusIsOkay()) {
299                         // Then get the number of rows
300                         $numRows = $resultInstance->getAffectedRows();
301                 }
302
303                 // Return the result
304                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: numRows=%d - EXIT!', $numRows));
305                 return $numRows;
306         }
307
308         /**
309          * Generates a primary key for this database frontend
310          *
311          * @return      $primaryKey             Primary key used in wrapped table
312          */
313         public final function generatePrimaryKey () {
314                 // Get the table name and a database instance and ask for it
315                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-DATABASE-FRONTEND: CALLED!');
316                 $primaryKey = FrameworkBootstrap::getDatabaseInstance()->getPrimaryKeyOfTable($this->getTableName());
317
318                 // Return value
319                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: primaryKey=%s - EXIT!', $primaryKey));
320                 return $primaryKey;
321         }
322
323         /**
324          * Count rows of this table
325          *
326          * @return      $count  Count of total rows in this table
327          */
328         public final function countTotalRows () {
329                 // Get the table name and a database instance and ask for it
330                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-DATABASE-FRONTEND: CALLED!');
331                 $count = FrameworkBootstrap::getDatabaseInstance()->countTotalRows($this->getTableName());
332
333                 // Return value
334                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: count=%d - EXIT!', $count));
335                 return $count;
336         }
337
338         /**
339          * Removes non-public data from given array.
340          *
341          * @param       $data   An array with possible non-public data that needs to be removed.
342          * @return      $data   A cleaned up array with only public data.
343          * @throws      InvalidArgumentException        If a parameter has an invalid value
344          */
345         public function removeNonPublicDataFromArray (array $data) {
346                 // Check parameter
347                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: data()=%d - CALLED!', count($data)));
348                 if (count($data) == 0) {
349                         // Throw IAE
350                         throw new InvalidArgumentException('Parameter "data" is an empty array', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
351                 }
352
353                 // Remove non-public data (aka. sensitive)
354                 $data = FrameworkBootstrap::getDatabaseInstance()->removeNonPublicDataFromArray($data);
355
356                 // Return cleaned array
357                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-DATABASE-FRONTEND: data()=%d - EXIT!', count($data)));
358                 return $data;
359         }
360
361 }