* @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 . */ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBackend { // Load traits use CompressorChannelTrait; use IoHandlerTrait; /** * 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 = []; /** * Whether the "connection is already up */ private $alreadyConnected = false; /** * Table information array */ private $tableInfo = []; /** * Element for index */ private $indexKey = '__idx'; /** * Cached file names based on table name to avoid "expensive" invocations * of FrameworkConfiguration->getConfigEntry(). */ private $pathNames = []; /** * The protected constructor. Do never instance from outside! You need to * set a local file path. The class will then validate it. * * @return void */ private 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(); // Set the compressor channel $databaseInstance->setCompressorChannelInstance(ObjectFactory::createObjectByConfiguredName('compressor_channel_class')); // Get a file IO handler and set it $databaseInstance->setFileIoInstance(ObjectFactory::createObjectByConfiguredName('file_io_class')); // "Connect" to the database $databaseInstance->connectToDatabase(); // Return database instance return $databaseInstance; } /** * Setter for the last read file * * @param $infoInstance The FQFN of the last read file * @return void */ private final function setLastFile (SplFileInfo $infoInstance) { // Cast string and set it $this->lastFile = $infoInstance; } /** * 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 $infoInstance An instance of a SplFileInfo class * @return $dataArray */ private function getDataArrayFromFile (SplFileInfo $infoInstance) { // Init compressed data //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: Reading elements from database file ' . $infoInstance . ' ...'); $compressedData = $this->getFileIoInstance()->loadFileContents($infoInstance); $compressedData = $compressedData['data']; // Decompress it $serializedData = $this->getCompressorChannelInstance()->getCompressor()->decompressStream($compressedData); // Unserialize it $dataArray = json_decode($serializedData, true); // Finally return it //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: Read ' . count($dataArray) . ' elements from database file ' . $infoInstance . '.'); //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: dataArray=' . print_r($dataArray, true)); return $dataArray; } /** * Writes data array to local file * * @param $infoInstance An instance of a SplFileInfo class * @param $dataArray An array with all the data we shall write * @return void */ private function writeDataArrayToFqfn (SplFileInfo $infoInstance, array $dataArray) { // Serialize and compress it //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: Flushing ' . count($dataArray) . ' elements to database file ' . $infoInstance . ' ...'); //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: dataArray=' . print_r($dataArray, true)); $compressedData = $this->getCompressorChannelInstance()->getCompressor()->compressStream(json_encode($dataArray)); // Write this data BASE64 encoded to the file //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: Writing ' . strlen($compressedData) . ' bytes ...'); $this->getFileIoInstance()->saveStreamToFile($infoInstance, $compressedData, $this); // Debug message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: 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 = []; // Create FQFN for getting the table information file $infoInstance = $this->generateFileFromDataSet($dataSetInstance, 'info'); // Get the file contents try { $infoArray = $this->getDataArrayFromFile($infoInstance); } catch (FileNotFoundException $e) { // Not found, so ignore it here } // ... and return it return $infoArray; } /** * Generates a file info class from given dataset instance and string * * @param $dataSetInstance An instance of a database set class * @param $rowName Name of the row * @return $infoInstance An instance of a SplFileInfo class */ private function generateFileFromDataSet (Criteria $dataSetInstance, string $rowName) { // Instanciate new file object $infoInstance = new SplFileInfo(FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('local_database_path') . $dataSetInstance->getTableName() . DIRECTORY_SEPARATOR . $rowName . '.' . $this->getFileExtension()); // Return it return $infoInstance; } /** * 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 $infoInstance = $this->generateFileFromDataSet($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($infoInstance, $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) { // Get table name from criteria $tableName = $dataSetInstance->getTableName(); // Create FQFN for creating the table information file $infoInstance = $this->generateFileFromDataSet($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($infoInstance, $this->tableInfo[$tableName]); } /** * 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) { // Get table name from criteria //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: dataSetInstance=%s - CALLED!', $dataSetInstance->__toString())); $tableName = $dataSetInstance->getTableName(); // Get the information array from lower method //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: tableName=%s', $tableName)); $infoArray = $this->getContentsFromTableInfoFile($dataSetInstance); // Is the primary key there? //* PRINTR-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: tableInfo=' . print_r($this->tableInfo, true)); if (!isset($this->tableInfo[$tableName]['primary'])) { // Then create the info file //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: Creating info table for tableName=%s ...', $tableName)); $this->createTableInfoFile($dataSetInstance); } elseif ((FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('db_update_primary_forced') == 'Y') && ($dataSetInstance->getPrimaryKey() != $this->tableInfo[$tableName]['primary'])) { // Set the array element //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: Setting primaryKey=%s for tableName=%s ...', $dataSetInstance->getPrimaryKey(), $tableName)); $this->tableInfo[$tableName]['primary'] = $dataSetInstance->getPrimaryKey(); // Update the entry //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: Updating info table for tableName=%s ...', $tableName)); $this->updateTableInfoFile($dataSetInstance); } // Trace message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: EXIT!'); } /** * 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 InvalidArgumentException If a parameter is not valid * @throws UnsupportedCriteriaException If the criteria is unsupported * @throws SqlException If an 'SQL error' occurs */ public function querySelect (string $tableName, LocalSearchCriteria $searchInstance) { // Validate parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: tableName=%s,searchInstance=%s - CALLED!', $tableName, $searchInstance->__toString())); if (empty($tableName)) { // Throw IAE throw new InvalidArgumentException('Parameter "tableName" is empty'); } elseif (!isset($this->pathNames[$tableName])) { // "Cache" is not present, so create and assign it $this->pathNames[$tableName] = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('local_database_path') . $tableName . DIRECTORY_SEPARATOR; } // The result is null by any errors $resultData = NULL; /* * 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 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: Getting directory_class for this->pathNames[%s]=%s ...', $tableName, $this->pathNames[$tableName])); $directoryInstance = ObjectFactory::createObjectByConfiguredName('directory_class', [$this->pathNames[$tableName]]); // Initialize the result data, this need to be rewritten e.g. if a local file cannot be read $resultData = [ BaseDatabaseResult::RESULT_NAME_STATUS => self::RESULT_OKAY, BaseDatabaseResult::RESULT_NAME_ROWS => [] ]; // Initialize limit/skip $limitFound = 0; $skipFound = 0; $idx = 1; // Read the directory with some exceptions while (($fileInfoInstance = $directoryInstance->readDirectoryExcept(['.gitkeep', 'info.' . $this->getFileExtension()])) && (($limitFound < $searchInstance->getLimit()) || ($searchInstance->getLimit() == 0))) { // Does the extension match? //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: fileInfoInstance->extension=%s,this->fileExtension=%s', $fileInfoInstance->getExtension(), $this->getFileExtension())); if ($fileInfoInstance->getExtension() !== $this->getFileExtension()) { // Skip this file! //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: Skipping fileInfoInstance->filename=%s ...', $fileInfoInstance->getFilename())); $directoryInstance->getDirectoryIteratorInstance()->next(); continue; } // Read the file $dataArray = $this->getDataArrayFromFile($fileInfoInstance); // Is this an array? //* PRINTR-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: fileInfoInstance.pathname=' . $fileInfoInstance->getPathname() . ',dataArray='.print_r($dataArray, true)); if (is_array($dataArray)) { // Default is nothing found $isFound = true; // Search in the criteria with FMFW (First Matches, First Wins) //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: data[]=%d', count($dataArray))); foreach ($dataArray as $key => $value) { // Found one entry? $isFound = ($isFound && $searchInstance->isCriteriaMatching($key, $value)); //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: key=%s,value[%s]=%s,isFound=%s', $key, gettype($value), $value, intval($isFound))); } // Is all found? //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: isFound=%d,limitFound=%d,limit=%d', intval($isFound), $limitFound, $searchInstance->getLimit())); if ($isFound === true) { // Shall we skip this entry? //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: searchInstance->skip=%d,skipFound=%d', $searchInstance->getSkip(), $skipFound)); if ($searchInstance->getSkip() > 0 && $skipFound < $searchInstance->getSkip()) { // Skip this entry $skipFound++; break; } // Set id number //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: Setting dataArray[%s]=%d', $this->getIndexKey(), $idx)); $dataArray[$this->getIndexKey()] = $idx; // Entry found! array_push($resultData[BaseDatabaseResult::RESULT_NAME_ROWS], $dataArray); // Count found entries up //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: resultData[%s]()=%d', BaseDatabaseResult::RESULT_NAME_ROWS, count($resultData[BaseDatabaseResult::RESULT_NAME_ROWS]))); $limitFound++; } } else { // Throw an exception here throw new SqlException(array($this, sprintf('File '%s' contains invalid data.', $fileInfoInstance->getPathname()), self::DB_CODE_DATA_FILE_CORRUPT), self::EXCEPTION_SQL_QUERY); } // Count entry up $idx++; // Advance to next entry $directoryInstance->getDirectoryIteratorInstance()->next(); } // 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 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: dataSetInstance=%s - CALLED!', $dataSetInstance->__toString())); try { // Create full path name $infoInstance = $this->generateFileFromDataSet($dataSetInstance, md5($dataSetInstance->getUniqueValue())); // Write the data away $this->writeDataArrayToFqfn($infoInstance, $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([ $this, sprintf('Cannot write data to table '%s', is the table created? e=%s,e->message=%s', $dataSetInstance->getTableName(), $e->__toString(), $e->getMessage() ), self::DB_CODE_TABLE_UNWRITEABLE ], self::EXCEPTION_SQL_QUERY ); } // Trace message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: EXIT!'); } /** * "Updates" a data set instance with a database layer * * @param $dataSetInstance An instance of a StorableCriteria class * @return void * @throws UnexpectedValueException If $tableName is empty * @throws SqlException If an SQL error occurs */ public function queryUpdateDataSet (StoreableCriteria $dataSetInstance) { // Get table name $tableName = $dataSetInstance->getTableName(); // Is "cache" there? if (empty($tableName)) { // Should never be an empty string throw new UnexpectedValueException('Class field dataSetInstance->tableName is empty'); } elseif (!isset($this->pathNames[$tableName])) { // "Cache" is not present, so create and assign it $this->pathNames[$tableName] = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('local_database_path') . $tableName . DIRECTORY_SEPARATOR; } // Try all the requests try { // Get a file pointer instance $directoryInstance = ObjectFactory::createObjectByConfiguredName('directory_class', [$this->pathNames[$tableName]]); // 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 (($fileInfoInstance = $directoryInstance->readDirectoryExcept(['.gitkeep', 'info.' . $this->getFileExtension()])) && (($limitFound < $searchInstance->getLimit()) || ($searchInstance->getLimit() == 0))) { // Does the extension match? //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: fileInfoInstance->extension=' . $fileInfoInstance->getExtension() . ',this->getFileExtension()=' . $this->getFileExtension()); if ($fileInfoInstance->getExtension() !== $this->getFileExtension()) { // Skip this file! //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: fileInfoInstance.extension=' . $fileInfoInstance->getExtension() . ',getFileExtension()=' . $this->getFileExtension() . ' - SKIPPED!'); $directoryInstance->getDirectoryIteratorInstance()->next(); continue; } // Open this file for reading $dataArray = $this->getDataArrayFromFile($fileInfoInstance); //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: fileInfoInstance.pathname=' . $fileInfoInstance->getPathname() . ',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) { // Found one entry? $isFound = (($isFound === true) && ($searchInstance->isCriteriaMatching($key, $value))); } // Is all found? //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: 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__, __LINE__)->debugOutput('CACHED-LFDB: Found entry, but skipping ...'); $skipFound++; break; } } // 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__, __LINE__)->debugOutput('CACHED-LFDB: criteriaKey=' . $searchKey . ',criteriaValue=' . $searchValue); $dataArray[$searchKey] = $searchValue; } // Write the data to a local file //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: Writing data[]=' . count($dataArray) . ' to ' . $fileInfoInstance->getPathname() . ' ...'); $this->writeDataArrayToFqfn($fileInfoInstance, $dataArray); // Count found entries up $limitFound++; } } // Advance to next entry $directoryInstance->getDirectoryIteratorInstance()->next(); } // 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 * @throws InvalidArgumentException If a parameter is not valid * @todo Rename method to getPrimaryKeyFromTableInfo() */ public function getPrimaryKeyOfTable (string $tableName) { // Validate parameter if (empty($tableName)) { // Throw IAE throw new InvalidArgumentException('Parameter "tableName" is empty'); } // 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']; } // 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__, __LINE__)->debugOutput('CACHED-LFDB: 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 * @throws InvalidArgumentException If a parameter is not valid */ public function countTotalRows (string $tableName) { // Validate parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: tableName=' . $tableName . ' - CALLED!'); if (empty($tableName)) { // Throw IAE throw new InvalidArgumentException('Parameter "tableName" is empty'); } elseif (!isset($this->pathNames[$tableName])) { // "Cache" is not present, so create and assign it $this->pathNames[$tableName] = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('local_database_path') . $tableName . DIRECTORY_SEPARATOR; } // Try all the requests //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: this->pathNames[%s]=%s', $tableName, $this->pathNames[$tableName])); try { // Get a file pointer instance $directoryInstance = ObjectFactory::createObjectByConfiguredName('directory_class', [$this->pathNames[$tableName]]); // Initialize counter $count = 0; // Read the directory with some exceptions while ($fileInfoInstance = $directoryInstance->readDirectoryExcept(['.gitkeep', 'info.' . $this->getFileExtension()])) { // Does the extension match? //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: fileInfoInstance->extension=' . $fileInfoInstance->getExtension() . ',this->getFileExtension()=' . $this->getFileExtension()); if ($fileInfoInstance->getExtension() !== $this->getFileExtension()) { // Debug message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: fileInfoInstance.extension=' . $fileInfoInstance->getExtension() . ',getFileExtension()=' . $this->getFileExtension() . ' - SKIPPED!'); // Skip this file! continue; } // Count this row up //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: fileInfoInstance.pathname=' . $fileInfoInstance->getPathname() . ',getFileExtension()=' . $this->getFileExtension() . ' - COUNTED!'); $count++; } } 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__, __LINE__)->debugOutput('CACHED-LFDB: tableName=' . $tableName . ',count=' . $count . ' - EXIT!'); return $count; } }