X-Git-Url: https://git.mxchange.org/?p=core.git;a=blobdiff_plain;f=inc%2Fclasses%2Fmain%2Fdatabase%2Fbackend%2Fclass_CachedLocalFileDatabase.php;fp=inc%2Fclasses%2Fmain%2Fdatabase%2Fbackend%2Fclass_CachedLocalFileDatabase.php;h=0000000000000000000000000000000000000000;hp=825a70e416eeab30e01c38ec50b4a417ba6b83e1;hb=751f9e6c51f00dba27757b72fc85490e51fd3797;hpb=5203f9bd014ad46fbc7ee54e7223dcd46e14e3b4 diff --git a/inc/classes/main/database/backend/class_CachedLocalFileDatabase.php b/inc/classes/main/database/backend/class_CachedLocalFileDatabase.php deleted file mode 100644 index 825a70e4..00000000 --- a/inc/classes/main/database/backend/class_CachedLocalFileDatabase.php +++ /dev/null @@ -1,673 +0,0 @@ - - * @version 0.0.0 - * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2015 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 . - */ -class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBackend { - /** - * The file's extension - */ - private $fileExtension = 'serialized'; - - /** - * The last read file's name - */ - private $lastFile = ''; - - /** - * The last read file's content including header information - */ - private $lastContents = array(); - - /** - * Whether the "connection is already up - */ - private $alreadyConnected = FALSE; - - /** - * Table information array - */ - private $tableInfo = array(); - - /** - * Element for index - */ - private $indexKey = '__idx'; - - /** - * The protected constructor. Do never instance from outside! You need to - * set a local file path. The class will then validate it. - * - * @return void - */ - protected function __construct () { - // Call parent constructor - parent::__construct(__CLASS__); - } - - /** - * Create an object of CachedLocalFileDatabase and set the save path from - * configuration for local files. - * - * @return $databaseInstance An instance of CachedLocalFileDatabase - */ - public static final function createCachedLocalFileDatabase () { - // Get an instance - $databaseInstance = new CachedLocalFileDatabase(); - - // Get a new compressor channel instance - $compressorInstance = ObjectFactory::createObjectByConfiguredName('compressor_channel_class'); - - // Set the compressor channel - $databaseInstance->setCompressorChannel($compressorInstance); - - // Get a file IO handler - $fileIoInstance = ObjectFactory::createObjectByConfiguredName('file_io_class'); - - // ... and set it - $databaseInstance->setFileIoInstance($fileIoInstance); - - // "Connect" to the database - $databaseInstance->connectToDatabase(); - - // Return database instance - return $databaseInstance; - } - - /** - * Setter for the last read file - * - * @param $fqfn The FQFN of the last read file - * @return void - */ - private final function setLastFile ($fqfn) { - // Cast string and set it - $this->lastFile = (string) $fqfn; - } - - /** - * Getter for last read file - * - * @return $lastFile The last read file's name with full path - */ - public final function getLastFile () { - return $this->lastFile; - } - - /** - * Setter for contents of the last read file - * - * @param $contents An array with header and data elements - * @return void - */ - private final function setLastFileContents (array $contents) { - // Set array - $this->lastContents = $contents; - } - - /** - * Getter for last read file's content as an array - * - * @return $lastContent The array with elements 'header' and 'data'. - */ - public final function getLastContents () { - return $this->lastContents; - } - - /** - * Getter for file extension - * - * @return $fileExtension The array with elements 'header' and 'data'. - */ - public final function getFileExtension () { - return $this->fileExtension; - } - - /** - * Getter for index key - * - * @return $indexKey Index key - */ - public final function getIndexKey () { - return $this->indexKey; - } - - /** - * Reads a local data file and returns it's contents in an array - * - * @param $fqfn The FQFN for the requested file - * @return $dataArray - */ - private function getDataArrayFromFile ($fqfn) { - // Debug message - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: Reading elements from database file ' . $fqfn . ' ...'); - - // Init compressed data - $compressedData = $this->getFileIoInstance()->loadFileContents($fqfn); - $compressedData = $compressedData['data']; - - // Close the file and throw the instance away - unset($fileInstance); - - // Decompress it - $serializedData = $this->getCompressorChannel()->getCompressor()->decompressStream($compressedData); - - // Unserialize it - $dataArray = json_decode($serializedData, TRUE); - - // Debug message - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: Read ' . count($dataArray) . ' elements from database file ' . $fqfn . '.'); - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: dataArray=' . print_r($dataArray, TRUE)); - - // Finally return it - return $dataArray; - } - - /** - * Writes data array to local file - * - * @param $fqfn The FQFN of the local file - * @param $dataArray An array with all the data we shall write - * @return void - */ - private function writeDataArrayToFqfn ($fqfn, array $dataArray) { - // Debug message - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: Flushing ' . count($dataArray) . ' elements to database file ' . $fqfn . ' ...'); - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: dataArray=' . print_r($dataArray, TRUE)); - - // Serialize and compress it - $compressedData = $this->getCompressorChannel()->getCompressor()->compressStream(json_encode($dataArray)); - - // Write data - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: Writing ' . strlen($compressedData) . ' bytes ...'); - - // Write this data BASE64 encoded to the file - $this->getFileIoInstance()->saveStreamToFile($fqfn, $compressedData, $this); - - // Debug message - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: Flushing ' . count($dataArray) . ' elements to database file completed.'); - } - - /** - * Getter for table information file contents or an empty if info file was not created - * - * @param $dataSetInstance An instance of a database set class - * @return $infoArray An array with all table informations - */ - private function getContentsFromTableInfoFile (StoreableCriteria $dataSetInstance) { - // Default content is no data - $infoArray = array(); - - // Create FQFN for getting the table information file - $fqfn = $this->generateFqfnFromDataSet($dataSetInstance, 'info'); - - // Get the file contents - try { - $infoArray = $this->getDataArrayFromFile($fqfn); - } catch (FileNotFoundException $e) { - // Not found, so ignore it here - } - - // ... and return it - return $infoArray; - } - - /** - * Generates an FQFN from given dataset instance and string - * - * @param $dataSetInstance An instance of a database set class - * @param $rowName Name of the row - * @return $fqfn The FQFN for this row - */ - private function generateFqfnFromDataSet (Criteria $dataSetInstance, $rowName) { - // This is the FQFN - $fqfn = $this->getConfigInstance()->getConfigEntry('local_db_path') . $dataSetInstance->getTableName() . '/' . $rowName . '.' . $this->getFileExtension(); - - // Return it - return $fqfn; - } - - /** - * Creates the table info file from given dataset instance - * - * @param $dataSetInstance An instance of a database set class - * @return void - */ - private function createTableInfoFile (StoreableCriteria $dataSetInstance) { - // Create FQFN for creating the table information file - $fqfn = $this->generateFqfnFromDataSet($dataSetInstance, 'info'); - - // Get the data out from dataset in a local array - $this->tableInfo[$dataSetInstance->getTableName()] = array( - 'primary' => $dataSetInstance->getPrimaryKey(), - 'created' => time(), - 'last_updated' => time() - ); - - // Write the data to the file - $this->writeDataArrayToFqfn($fqfn, $this->tableInfo[$dataSetInstance->getTableName()]); - } - - /** - * Updates the table info file from given dataset instance - * - * @param $dataSetInstance An instance of a database set class - * @return void - */ - private function updateTableInfoFile (StoreableCriteria $dataSetInstance) { - // Create FQFN for creating the table information file - $fqfn = $this->generateFqfnFromDataSet($dataSetInstance, 'info'); - - // Get the data out from dataset in a local array - $this->tableInfo[$tableName]['primary'] = $dataSetInstance->getPrimaryKey(); - $this->tableInfo[$tableName]['last_updated'] = time(); - - // Write the data to the file - $this->writeDataArrayToFqfn($fqfn, $this->tableInfo[$dataSetInstance->getTableName()]); - } - - /** - * Updates the primary key information or creates the table info file if not found - * - * @param $dataSetInstance An instance of a database set class - * @return void - */ - private function updatePrimaryKey (StoreableCriteria $dataSetInstance) { - // "Cache" table name - $tableName = $dataSetInstance->getTableName(); - - // Get the information array from lower method - $infoArray = $this->getContentsFromTableInfoFile($dataSetInstance); - - // Is the primary key there? - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: tableInfo=' . print_r($this->tableInfo, TRUE)); - if (!isset($this->tableInfo[$tableName]['primary'])) { - // Then create the info file - $this->createTableInfoFile($dataSetInstance); - } elseif (($this->getConfigInstance()->getConfigEntry('db_update_primary_forced') == 'Y') && ($dataSetInstance->getPrimaryKey() != $this->tableInfo[$tableName]['primary'])) { - // Set the array element - $this->tableInfo[$tableName]['primary'] = $dataSetInstance->getPrimaryKey(); - - // Update the entry - $this->updateTableInfoFile($dataSetInstance); - } - } - - /** - * Makes sure that the database connection is alive - * - * @return void - * @todo Do some checks on the database directory and files here - */ - public function connectToDatabase () { - } - - /** - * Starts a SELECT query on the database by given return type, table name - * and search criteria - * - * @param $tableName Name of the database table - * @param $searchInstance Local search criteria class - * @return $resultData Result data of the query - * @throws UnsupportedCriteriaException If the criteria is unsupported - * @throws SqlException If an 'SQL error' occurs - */ - public function querySelect ($tableName, LocalSearchCriteria $searchInstance) { - // Debug message - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: tableName=' . $tableName . ' - CALLED!'); - - // The result is null by any errors - $resultData = NULL; - - // Create full path name - $pathName = $this->getConfigInstance()->getConfigEntry('local_db_path') . $tableName . '/'; - - /* - * A 'select' query is not that easy on local files, so first try to - * find the 'table' which is in fact a directory on the server - */ - try { - // Get a directory pointer instance - $directoryInstance = ObjectFactory::createObjectByConfiguredName('directory_class', array($pathName)); - - // Initialize the result data, this need to be rewritten e.g. if a local file cannot be read - $resultData = array( - BaseDatabaseBackend::RESULT_INDEX_STATUS => self::RESULT_OKAY, - BaseDatabaseBackend::RESULT_INDEX_ROWS => array() - ); - - // Initialize limit/skip - $limitFound = 0; - $skipFound = 0; - $idx = 1; - - // Read the directory with some exceptions - while (($dataFile = $directoryInstance->readDirectoryExcept(array('.htaccess', 'info.' . $this->getFileExtension()))) && (($limitFound < $searchInstance->getLimit()) || ($searchInstance->getLimit() == 0))) { - // Debug message - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: dataFile=' . $dataFile . ',this->getFileExtension()=' . $this->getFileExtension()); - - // Does the extension match? - if (substr($dataFile, -(strlen($this->getFileExtension()))) !== $this->getFileExtension()) { - // Skip this file! - continue; - } // END - if - - // Read the file - $dataArray = $this->getDataArrayFromFile($pathName . $dataFile); - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: dataFile=' . $dataFile . ',dataArray='.print_r($dataArray, TRUE)); - - // Is this an array? - if (is_array($dataArray)) { - // Default is nothing found - $isFound = TRUE; - - // Search in the criteria with FMFW (First Matches, First Wins) - foreach ($dataArray as $key => $value) { - // Make sure value is not bool - assert(!is_bool($value)); - - // Found one entry? - $isFound = (($isFound === TRUE) && ($searchInstance->isCriteriaMatching($key, $value))); - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: key=' . $key . ',value=' . $value . ',isFound=' . intval($isFound)); - } // END - foreach - - // Is all found? - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: isFound=' . intval($isFound) . ',limitFound=' . $limitFound . ',limit=' . $searchInstance->getLimit()); - if ($isFound === TRUE) { - // Shall we skip this entry? - if ($searchInstance->getSkip() > 0) { - // We shall skip some entries - if ($skipFound < $searchInstance->getSkip()) { - // Skip this entry - $skipFound++; - break; - } // END - if - } // END - if - - // Set id number - $dataArray[$this->getIndexKey()] = $idx; - - // Entry found! - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: indexKey=' . $this->getIndexKey() . ',idx=' . $idx . ',dataArray=' . print_r($dataArray, TRUE)); - array_push($resultData[BaseDatabaseBackend::RESULT_INDEX_ROWS], $dataArray); - - // Count found entries up - $limitFound++; - } // END - if - } else { - // Throw an exception here - throw new SqlException(array($this, sprintf('File '%s' contains invalid data.', $dataFile), self::DB_CODE_DATA_FILE_CORRUPT), self::EXCEPTION_SQL_QUERY); - } - - // Count entry up - $idx++; - } // END - while - - // Close directory and throw the instance away - $directoryInstance->closeDirectory(); - unset($directoryInstance); - - // Reset last exception - $this->resetLastException(); - } catch (PathIsNoDirectoryException $e) { - // Path not found means "table not found" for real databases... - $this->setLastException($e); - - // So throw an SqlException here with faked error message - throw new SqlException (array($this, sprintf('Table '%s' not found', $tableName), self::DB_CODE_TABLE_MISSING), self::EXCEPTION_SQL_QUERY); - } catch (FrameworkException $e) { - // Catch all exceptions and store them in last error - $this->setLastException($e); - } - - // Return the gathered result - return $resultData; - } - - /** - * "Inserts" a data set instance into a local file database folder - * - * @param $dataSetInstance A storeable data set - * @return void - * @throws SqlException If an SQL error occurs - */ - public function queryInsertDataSet (StoreableCriteria $dataSetInstance) { - // Try to save the request away - try { - // Create full path name - $fqfn = $this->generateFqfnFromDataSet($dataSetInstance, md5($dataSetInstance->getUniqueValue())); - - // Write the data away - $this->writeDataArrayToFqfn($fqfn, $dataSetInstance->getCriteriaArray()); - - // Update the primary key - $this->updatePrimaryKey($dataSetInstance); - - // Reset last exception - $this->resetLastException(); - } catch (FrameworkException $e) { - // Catch all exceptions and store them in last error - $this->setLastException($e); - - // Throw an SQL exception - throw new SqlException(array($this, sprintf('Cannot write data to table '%s', is the table created?', $dataSetInstance->getTableName()), self::DB_CODE_TABLE_UNWRITEABLE), self::EXCEPTION_SQL_QUERY); - } - } - - /** - * "Updates" a data set instance with a database layer - * - * @param $dataSetInstance A storeable data set - * @return void - * @throws SqlException If an SQL error occurs - */ - public function queryUpdateDataSet (StoreableCriteria $dataSetInstance) { - // Create full path name - $pathName = $this->getConfigInstance()->getConfigEntry('local_db_path') . $dataSetInstance->getTableName() . '/'; - - // Try all the requests - try { - // Get a file pointer instance - $directoryInstance = ObjectFactory::createObjectByConfiguredName('directory_class', array($pathName)); - - // Initialize limit/skip - $limitFound = 0; - $skipFound = 0; - - // Get the criteria array from the dataset - $searchArray = $dataSetInstance->getCriteriaArray(); - - // Get search criteria - $searchInstance = $dataSetInstance->getSearchInstance(); - - // Read the directory with some exceptions - while (($dataFile = $directoryInstance->readDirectoryExcept(array('.htaccess', 'info.' . $this->getFileExtension()))) && (($limitFound < $searchInstance->getLimit()) || ($searchInstance->getLimit() == 0))) { - // Does the extension match? - if (substr($dataFile, -(strlen($this->getFileExtension()))) !== $this->getFileExtension()) { - // Debug message - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: dataFile=' . $dataFile . ',getFileExtension()=' . $this->getFileExtension() . ' - SKIPPED!'); - // Skip this file! - continue; - } // END - if - - // Open this file for reading - $dataArray = $this->getDataArrayFromFile($pathName . $dataFile); - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: dataFile=' . $dataFile . ',dataArray='.print_r($dataArray, TRUE)); - - // Is this an array? - if (is_array($dataArray)) { - // Default is nothing found - $isFound = TRUE; - - // Search in the criteria with FMFW (First Matches, First Wins) - foreach ($dataArray as $key => $value) { - // Make sure value is not bool - assert(!is_bool($value)); - - // Found one entry? - $isFound = (($isFound === TRUE) && ($searchInstance->isCriteriaMatching($key, $value))); - } // END - foreach - - // Is all found? - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: isFound=' . intval($isFound)); - if ($isFound === TRUE) { - // Shall we skip this entry? - if ($searchInstance->getSkip() > 0) { - // We shall skip some entries - if ($skipFound < $searchInstance->getSkip()) { - // Skip this entry - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: Found entry, but skipping ...'); - $skipFound++; - break; - } // END - if - } // END - if - - // Entry found, so update it - foreach ($searchArray as $searchKey => $searchValue) { - // Make sure the value is not bool again - assert(!is_bool($searchValue)); - assert($searchKey != $this->indexKey); - - // Debug message + add/update it - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: criteriaKey=' . $searchKey . ',criteriaValue=' . $searchValue); - $dataArray[$searchKey] = $searchValue; - } // END - foreach - - // Write the data to a local file - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: Writing dataArray()=' . count($dataArray) . ' to ' . $dataFile . ' ...'); - $this->writeDataArrayToFqfn($pathName . $dataFile, $dataArray); - - // Count found entries up - $limitFound++; - } // END - if - } // END - if - } // END - while - - // Close the file pointer - $directoryInstance->closeDirectory(); - - // Update the primary key - $this->updatePrimaryKey($dataSetInstance); - - // Reset last exception - $this->resetLastException(); - } catch (FrameworkException $e) { - // Catch all exceptions and store them in last error - $this->setLastException($e); - - // Throw an SQL exception - throw new SqlException(array($this, sprintf('Cannot write data to table '%s', is the table created? Exception: %s, message:%s', $dataSetInstance->getTableName(), $e->__toString(), $e->getMessage()), self::DB_CODE_TABLE_UNWRITEABLE), self::EXCEPTION_SQL_QUERY); - } - } - - /** - * Getter for primary key of specified table or if not found null will be - * returned. This must be database-specific. - * - * @param $tableName Name of the table we need the primary key from - * @return $primaryKey Primary key column of the given table - */ - public function getPrimaryKeyOfTable ($tableName) { - // Default key is null - $primaryKey = NULL; - - // Does the table information exist? - if (isset($this->tableInfo[$tableName])) { - // Then return the primary key - $primaryKey = $this->tableInfo[$tableName]['primary']; - } // END - if - - // Return the column - return $primaryKey; - } - - /** - * 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. - * @todo Add more generic non-public data for removal - */ - public function removeNonPublicDataFromArray (array $data) { - // Remove '__idx' - unset($data[$this->indexKey]); - - // Return it - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: data[' . gettype($data) . ']='.print_r($data, TRUE)); - return $data; - } - - /** - * Counts total rows of given table - * - * @param $tableName Table name - * @return $count Total rows of given table - */ - public function countTotalRows($tableName) { - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: tableName=' . $tableName . ' - CALLED!'); - - // Create full path name - $pathName = $this->getConfigInstance()->getConfigEntry('local_db_path') . $tableName . '/'; - - // Try all the requests - try { - // Get a file pointer instance - $directoryInstance = ObjectFactory::createObjectByConfiguredName('directory_class', array($pathName)); - - // Initialize counter - $count = 0; - - // Read the directory with some exceptions - while ($dataFile = $directoryInstance->readDirectoryExcept(array('.htaccess', 'info.' . $this->getFileExtension()))) { - // Does the extension match? - if (substr($dataFile, -(strlen($this->getFileExtension()))) !== $this->getFileExtension()) { - // Debug message - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: dataFile=' . $dataFile . ',getFileExtension()=' . $this->getFileExtension() . ' - SKIPPED!'); - // Skip this file! - continue; - } // END - if - - // Count this row up - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: dataFile=' . $dataFile . ',getFileExtension()=' . $this->getFileExtension() . ' - COUNTED!'); - $count++; - } // END - while - } catch (FrameworkException $e) { - // Catch all exceptions and store them in last error - $this->setLastException($e); - - // Throw an SQL exception - throw new SqlException(array($this, sprintf('Cannot count on table '%s', is the table created?', $dataSetInstance->getTableName()), self::DB_CODE_TABLE_NOT_FOUND), self::EXCEPTION_SQL_QUERY); - } - - // Return count - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: tableName=' . $tableName . ',count=' . $count . ' - EXIT!'); - return $count; - } -} - -// [EOF] -?>