* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontendInterface {
+
+ // Constants for MySQL backward-compatiblity (PLEASE FIX THEM!)
+ const DB_CODE_TABLE_MISSING = 0x000;
+ const DB_CODE_TABLE_UNWRITEABLE = 0x001;
+
/**
* Save path for "file database"
*/
private $alreadyConnected = false;
/**
- * The private constructor. Do never instance from outside!
- * You need to set a local file path. The class will then validate it.
+ * Last error message
+ */
+ private $lastError = "";
+
+ /**
+ * Last exception
+ */
+ private $lastException = null;
+
+ /**
+ * The protected constructor. Do never instance from outside! You need to
+ * set a local file path. The class will then validate it.
*
* @return void
*/
$this->setObjectDescription("Class for local file databases");
// Create unique ID
- $this->createUniqueID();
+ $this->generateUniqueId();
// Clean up a little
$this->removeSystemArray();
return $this->savePath;
}
+ /**
+ * Getter for last error message
+ *
+ * @return $lastError Last error message
+ */
+ public final function getLastError () {
+ return $this->lastError;
+ }
+
+ /**
+ * Getter for last exception
+ *
+ * @return $lastException Last thrown exception
+ */
+ public final function getLastException () {
+ return $this->lastException;
+ }
+
/**
* Saves a given object to the local file system by serializing and
* transparently compressing it
*
- * @param $object The object we shall save to the local file system
+ * @param $object The object we shall save to the local file system
* @return void
- * @throws NullPointerException If the object instance is null
- * @throws NoObjectException If the parameter $object is not
- * an object
*/
- public final function saveObject ($object) {
- // Some tests on the parameter...
- if (is_null($object)) {
- // Is null, throw exception
- throw new NullPointerException($object, self::EXCEPTION_IS_NULL_POINTER);
- } elseif (!is_object($object)) {
- // Is not an object, throw exception
- throw new NoObjectException($object, self::EXCEPTION_IS_NO_OBJECT);
- } elseif (!method_exists($object, '__toString')) {
- // A highly required method was not found... :-(
- throw new MissingMethodException(array($object, '__toString'), self::EXCEPTION_MISSING_METHOD);
- }
-
+ public final function saveObject (FrameworkInterface $object) {
// Get a string containing the serialized object. We cannot exchange
// $this and $object here because $object does not need to worry
// about it's limitations... ;-)
/**
* Get a serialized string from the given object
*
- * @param $object The object we want to serialize and transparently
- * compress
- * @return $serialized A string containing the serialzed/compressed object
+ * @param $object The object we want to serialize and transparently
+ * compress
+ * @return $serialized A string containing the serialzed/compressed object
* @see ObjectLimits An object holding limition information
* @see SerializationContainer A special container class for e.g.
- * attributes from limited objects
+ * attributes from limited objects
*/
- private function serializeObject ($object) {
+ private function serializeObject (FrameworkInterface $object) {
// If there is no limiter instance we serialize the whole object
// otherwise only in the limiter object (ObjectLimits) specified
// attributes summarized in a special container class
* Analyses if a unique ID has already been used or not by search in the
* local database folder.
*
- * @param $uniqueID A unique ID number which shall be checked
- * before it will be used
- * @param $inConstructor If we got called in a de/con-structor or
- * from somewhere else
+ * @param $uniqueID A unique ID number which shall be checked
+ * before it will be used
+ * @param $inConstructor If we got called in a de/con-structor or
+ * from somewhere else
* @return $isUnused true = The unique ID was not found in the database,
- * false = It is already in use by an other object
- * @throws NoArrayCreatedException If explode() fails to create an array
+ * false = It is already in use by an other object
+ * @throws NoArrayCreatedException If explode() fails to create an array
* @throws InvalidArrayCountException If the array contains less or
* more than two elements
*/
// Initialize the search loop
$isValid = false;
- while ($dataFile = $dirInstance->readDirectoryExcept(array(".", ".."))) {
+ while ($dataFile = $dirInstance->readDirectoryExcept(array(".", "..", ".htaccess", ".svn"))) {
// Generate FQFN for testing
$fqfn = sprintf("%s/%s", $pathName, $dataFile);
$this->setLastFile($fqfn);
$this->lastFile = $fqfn;
}
+ /**
+ * Reset the last error and exception instance. This should be done after
+ * a successfull "query"
+ *
+ * @return void
+ */
+ private final function resetLastError () {
+ $this->lastError = "";
+ $this->lastException = null;
+ }
+
/**
* Getter for last read file
*
* @param $criteria 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 ($resultType, $tableName, Criteria $criteriaInstance) {
+ // The result is null by any errors
+ $resultData = null;
+
// Is this criteria supported?
- if (!$criteriaInstance instanceof LocalCriteria) {
+ if (!$criteriaInstance instanceof LocalSearchCriteria) {
// Not supported by this database layer
throw new UnsupportedCriteriaException(array($this, $criteriaInstance), self::EXCEPTION_REQUIRED_INTERFACE_MISSING);
}
- // A "select" query on local files is not that easy, so begin slowly with it...
- $this->partialStub(sprintf("type=%s,table=%s,criteria=%s", $resultType, $tableName, $criteriaInstance));
+ // Create full path name
+ $pathName = $this->getSavePath() . $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 = FrameworkDirectoryPointer::createFrameworkDirectoryPointer($pathName);
+
+ // Initialize the result data, this need to be rewritten e.g. if a local file cannot be read
+ $resultData = array(
+ 'status' => "ok",
+ 'rows' => array()
+ );
+
+ // Initialize limit/skip
+ $limitFound = 0; $skipFound = 0;
+
+ // Read the directory with some exceptions
+ while (($dataFile = $directoryInstance->readDirectoryExcept(array(".", "..", ".htaccess", ".svn"))) && ($limitFound < $criteriaInstance->getLimit())) {
+ // Open this file for reading
+ $filePointer = FrameworkFileInputPointer::createFrameworkFileInputPointer($pathName . $dataFile);
+
+ // Get the raw data and BASE64-decode it
+ $compressedData = base64_decode($filePointer->readLinesFromFile());
+
+ // Close the file and throw the instance away
+ $filePointer->closeFile();
+ unset($filePointer);
+
+ // Decompress it
+ $serializedData = $this->getCompressorChannel()->getCompressor()->decompressStream($compressedData);
+
+ // Unserialize it
+ $dataArray = unserialize($serializedData);
+
+ // Is this an array?
+ if (is_array($dataArray)) {
+ // Search in the criteria with FMFW (First Matches, First Wins)
+ foreach ($dataArray as $key=>$value) {
+ // Get criteria element
+ $criteria = $criteriaInstance->getCriteriaElemnent($key);
+
+ // Is the criteria met?
+ if ((!is_null($criteria)) && ($criteria == $value)) {
+
+ // Shall we skip this entry?
+ if ($criteriaInstance->getSkip() > 0) {
+ // We shall skip some entries
+ if ($skipFound < $criteriaInstance->getSkip()) {
+ // Skip this entry
+ $skipFound++;
+ break;
+ } // END - if
+ } // END - if
+
+ // Entry found!
+ $resultData['rows'][] = $dataArray;
+ $limitFound++;
+ break;
+ } // END - if
+ } // END - foreach
+ } // END - if
+ } // END - while
+
+ // Close directory and throw the instance away
+ $directoryInstance->closeDirectory();
+ unset($directoryInstance);
+
+ // Reset last error message and exception
+ $this->resetLastError();
+ } catch (PathIsNoDirectoryException $e) {
+ // Path not found means "table not found" for real databases...
+ $this->lastException = $e;
+ $this->lastError = $e->getMessage();
+
+ // 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->lastException = $e;
+ $this->lastError = $e->getMessage();
+ }
+
+ // 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 insertDataSet (StoreableCriteria $dataSetInstance) {
+ // Create full path name
+ $fqfn = sprintf("%s%s/%s.%s",
+ $this->getSavePath(),
+ $dataSetInstance->getTableName(),
+ md5($dataSetInstance->getUniqueValue()),
+ $this->getFileExtension()
+ );
+
+ // Try to save the request away
+ try {
+ // Get a file pointer instance
+ $filePointer = FrameworkFileOutputPointer::createFrameworkFileOutputPointer($fqfn, 'w');
+
+ // Get the criteria array from the dataset
+ $criteriaArray = $dataSetInstance->getCriteriaArray();
+
+ // Serialize and compress it
+ $compressedData = $this->getCompressorChannel()->getCompressor()->compressStream(serialize($criteriaArray));
+
+ // Write this data BASE64 encoded to the file
+ $filePointer->writeToFile(base64_encode($compressedData));
+
+ // Close the file pointer
+ $filePointer->closeFile();
+
+ // Reset last error message and exception
+ $this->resetLastError();
+ } catch (FrameworkException $e) {
+ // Catch all exceptions and store them in last error
+ $this->lastException = $e;
+ $this->lastError = $e->getMessage();
+
+ // Throw an SQL exception
+ throw new SqlException (array($this, sprintf("Cannot write data to table '%s'", $tableName), self::DB_CODE_TABLE_UNWRITEABLE), self::EXCEPTION_SQL_QUERY);
+ }
}
}