* @version 0.0.0 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2021 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 . */ abstract class BaseDatabaseFrontend extends BaseFrameworkSystem { // Load traits use CacheableTrait; /** * Current table name to use */ private $tableName = 'unknown'; /** * "Cached" value 'database_cache_enabled' from configuration */ private $databaseCacheEnabled = false; /** * Protected constructor * * @param $className Name of the class * @return void */ protected function __construct (string $className) { // Call parent constructor parent::__construct($className); // Initialize the cache instance $this->initCacheInstance(); } /** * Initializes the cache instance with a new object * * @return void */ private final function initCacheInstance () { // Set "cache" attributes $this->databaseCacheEnabled = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('database_cache_enabled'); // Is the cache enabled? if ($this->databaseCacheEnabled === true) { // Set the new instance $this->setCacheInstance(ObjectFactory::createObjectByConfiguredName('cache_class')); } } /** * Setter for table name * * @param $tableName Name of table name to set * @return void */ protected final function setTableName (string $tableName) { $this->tableName = $tableName; } /** * Getter for table name * * @return $tableName Name of table name to set */ protected final function getTableName () { return $this->tableName; } /** * Gets a cache key from Criteria instance * * @param $criteriaInstance An instance of a Criteria class * @param $onlyKeys Only use these keys for a cache key * @return $cacheKey A cache key suitable for lookup/storage purposes */ protected function getCacheKeyByCriteria (Criteria $criteriaInstance, array $onlyKeys = []) { // Generate it //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FRAMEWORK-SYSTEM: criteriaInstance=' . $criteriaInstance->__toString() . ',onlyKeys()=' . count($onlyKeys) . ' - CALLED!'); $cacheKey = sprintf('%s@%s', $this->__toString(), $criteriaInstance->getCacheKey($onlyKeys) ); // And return it //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FRAMEWORK-SYSTEM: cacheKey=' . $cacheKey . ' - EXIT!'); return $cacheKey; } /** * 'Inserts' a data set instance into a local file database folder * * @param $dataSetInstance A storeable data set * @param $onlyKeys Only use these keys for a cache key * @return void */ protected function queryInsertDataSet (StoreableCriteria $dataSetInstance, array $onlyKeys = []) { // Default cache key is NULL //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: dataSetInstance=%s,onlyKeys()=%d - CALLED!', $dataSetInstance->__toString(), count($onlyKeys))); $cacheKey = NULL; // Is cache enabled? if ($this->databaseCacheEnabled === true) { // First get a key suitable for our cache and extend it with this class name $cacheKey = $this->getCacheKeyByCriteria($dataSetInstance, $onlyKeys); //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FRONTEND: Using cache key ' . $cacheKey . ' for purging ...'); } // Does this key exists in cache? //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: cacheKey[%s]=%s', gettype($cacheKey), $cacheKey)); if (($this->databaseCacheEnabled === true) && ($this->getCacheInstance()->offsetExists($cacheKey))) { // Purge the cache //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: Calling this->cacheInstance->purgeOffset(%s) ...', $cacheKey)); $this->getCacheInstance()->purgeOffset($cacheKey); } // Handle it over to the middleware FrameworkBootstrap::getDatabaseInstance()->queryInsertDataSet($dataSetInstance); // Trace message //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FRONTEND: EXIT!'); } /** * 'Updates' a data set instance with a database layer * * @param $dataSetInstance A storeable data set * @param $onlyKeys Only use these keys for a cache key * @return void */ protected function queryUpdateDataSet (StoreableCriteria $dataSetInstance, array $onlyKeys = []) { // Init cache key $cacheKey = NULL; // Is cache enabled? if ($this->databaseCacheEnabled === true) { // First get a key suitable for our cache and extend it with this class name $cacheKey = $this->getCacheKeyByCriteria($dataSetInstance, $onlyKeys); //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FRONTEND: Using cache key ' . $cacheKey . ' for purging ...'); } // Does this key exists in cache? if (($this->databaseCacheEnabled === true) && ($this->getCacheInstance()->offsetExists($cacheKey))) { // Purge the cache $this->getCacheInstance()->purgeOffset($cacheKey); } // Handle it over to the middleware FrameworkBootstrap::getDatabaseInstance()->queryUpdateDataSet($dataSetInstance); } /** * Getter for index key * * @return $indexKey Index key */ public final function getIndexKey () { return FrameworkBootstrap::getDatabaseInstance()->getIndexKey(); } /** * Getter for last exception * * @return $lastException Last exception or NULL if none occured */ public final function getLastException () { return FrameworkBootstrap::getDatabaseInstance()->getLastException(); } /** * Do a "select" query on the current table with the given search criteria and * store it in cache for later usage * * @param $criteriaInstance An instance of a Criteria class * @param $onlyKeys Only use these keys for a cache key * @return $resultInstance An instance of a database result class */ public function doSelectByCriteria (Criteria $criteriaInstance, array $onlyKeys = []) { // Default cache key if cache is not enabled //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: criteriaInstance=%s,onlyKeys()=%d - CALLED!', $criteriaInstance->__toString(), count($onlyKeys))); $cacheKey = NULL; $result = []; // Is the cache enabled? if ($this->databaseCacheEnabled === true) { // First get a key suitable for our cache and extend it with this class name $cacheKey = $this->getCacheKeyByCriteria($criteriaInstance, $onlyKeys); } // Does this key exists in cache? //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: cacheKey[%s]=%s', gettype($cacheKey), $cacheKey)); if (($this->databaseCacheEnabled === true) && ($this->getCacheInstance()->offsetExists($cacheKey, BaseDatabaseResult::RESULT_NAME_ROWS, 1))) { // Then use this result //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: Cache used for cacheKey=%s', $cacheKey)); $result = $this->getCacheInstance()->offsetGet($cacheKey); } else { // Now it's time to ask the database layer for this select statement //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: Quering database, cacheKey=%s ...', $cacheKey)); $result = FrameworkBootstrap::getDatabaseInstance()->doSelectByTableCriteria($this->getTableName(), $criteriaInstance); // Cache the result if not null //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: result[]=%s', gettype($result))); if (!is_null($result)) { // Is cache enabled? if ($this->databaseCacheEnabled === true) { // A valid result has returned from the database layer //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: Setting cacheKey=%s with result()=%d entries', $cacheKey, count($result))); $this->getCacheInstance()->offsetSet($cacheKey, $result); } } else { // This invalid result must be wrapped $result = array( BaseDatabaseResult::RESULT_NAME_STATUS => 'invalid', BaseDatabaseResult::RESULT_NAME_EXCEPTION => FrameworkBootstrap::getDatabaseInstance()->getLastException() ); } } // Create an instance of a CachedDatabaseResult class with the given result // @TODO Minor: Update above comment to e.g. BaseDatabaseResult //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-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]))); $resultInstance = ObjectFactory::createObjectByConfiguredName('database_result_class', array($result)); // And return the instance //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-FRONTEND: resultInstance=%s - EXIT!', $resultInstance->__toString())); return $resultInstance; } /** * Count the numbers of rows we shall receive * * @param $criteriaInstance An instance of a Criteria class * @param $onlyKeys Only use these keys for a cache key * @return $numRows Numbers of rows of database entries */ public function doSelectCountByCriteria (Criteria $criteriaInstance, array $onlyKeys = []) { // Total numbers is -1 so we can distinglish between failed and valid queries $numRows = 0; // Get the result from above method $resultInstance = $this->doSelectByCriteria($criteriaInstance, $onlyKeys); // Was that query fine? if ($resultInstance->ifStatusIsOkay()) { // Then get the number of rows $numRows = $resultInstance->getAffectedRows(); // Debug message //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-FRONTEND: numRows=' . $numRows); } // Return the result return $numRows; } /** * Getter for primary key used in wrapped table * * @return $primaryKey Primary key used in wrapped table */ public final function getPrimaryKeyValue () { // Get the table name and a database instance and ask for it $primaryKey = FrameworkBootstrap::getDatabaseInstance()->getPrimaryKeyOfTable($this->getTableName()); // Return value return $primaryKey; } /** * Count rows of this table * * @return $count Count of total rows in this table */ public final function countTotalRows () { // Get the table name and a database instance and ask for it $count = FrameworkBootstrap::getDatabaseInstance()->countTotalRows($this->getTableName()); // Return value return $count; } /** * 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. */ public function removeNonPublicDataFromArray (array $data) { //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('WRAPPER[' . $this->__toString() . ']: Calling FrameworkBootstrap::getDatabaseInstance()->removeNonPublicDataFromArray(data) ...'); $data = FrameworkBootstrap::getDatabaseInstance()->removeNonPublicDataFromArray($data); //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('WRAPPER[' . $this->__toString() . ']: data[]=' . gettype($data)); return $data; } }