Update of last activitity and action in user added, refresh of auth cookies added
[shipsimu.git] / inc / classes / main / database / databases / class_LocalFileDatabase.php
1 <?php
2 /**
3  * Database backend class for storing objects in locally created files.
4  *
5  * This class serializes objects and saves them to local files.
6  *
7  * @author              Roland Haeder <webmaster@ship-simu.org>
8  * @version             0.0.0
9  * @copyright   Copyright(c) 2007, 2008 Roland Haeder, this is free software
10  * @license             GNU GPL 3.0 or any newer version
11  * @link                http://www.ship-simu.org
12  *
13  * This program is free software: you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation, either version 3 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program. If not, see <http://www.gnu.org/licenses/>.
25  */
26 class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontendInterface {
27
28         // Constants for MySQL backward-compatiblity (PLEASE FIX THEM!)
29         const DB_CODE_TABLE_MISSING     = 0x010;
30         const DB_CODE_TABLE_UNWRITEABLE = 0x011;
31         const DB_CODE_DATA_FILE_CORRUPT = 0x012;
32
33         /**
34          * Save path for "file database"
35          */
36         private $savePath = "";
37
38         /**
39          * The file's extension
40          */
41         private $fileExtension = "serialized";
42
43         /**
44          * The last read file's name
45          */
46         private $lastFile = "";
47
48         /**
49          * The last read file's content including header information
50          */
51         private $lastContents = array();
52
53         /**
54          * Wether the "connection is already up
55          */
56         private $alreadyConnected = false;
57
58         /**
59          * Last error message
60          */
61         private $lastError = "";
62
63         /**
64          * Last exception
65          */
66         private $lastException = null;
67
68         /**
69          * The protected constructor. Do never instance from outside! You need to
70          * set a local file path. The class will then validate it.
71          *
72          * @return      void
73          */
74         protected function __construct() {
75                 // Call parent constructor
76                 parent::__construct(__CLASS__);
77
78                 // Set description
79                 $this->setObjectDescription("Class for local file databases");
80
81                 // Create unique ID
82                 $this->generateUniqueId();
83
84                 // Clean up a little
85                 $this->removeSystemArray();
86         }
87
88         /**
89          * Create an object of LocalFileDatabase and set the save path for local files.
90          * This method also validates the given file path.
91          *
92          * @param               $savePath                                       The local file path string
93          * @param               $ioInstance                             The input/output handler. This
94          *                                                                      should be FileIoHandler
95          * @return      $dbInstance                             An instance of LocalFileDatabase
96          */
97         public final static function createLocalFileDatabase ($savePath, FileIoHandler $ioInstance) {
98                 // Get an instance
99                 $dbInstance = new LocalFileDatabase();
100
101                 // Set save path and IO instance
102                 $dbInstance->setSavePath($savePath);
103                 $dbInstance->setFileIoInstance($ioInstance);
104
105                 // "Connect" to the database
106                 $dbInstance->connectToDatabase();
107
108                 // Return database instance
109                 return $dbInstance;
110         }
111
112         /**
113          * Setter for save path
114          *
115          * @param               $savePath               The local save path where we shall put our serialized classes
116          * @return      void
117          */
118         public final function setSavePath ($savePath) {
119                 // Secure string
120                 $savePath = (string) $savePath;
121
122                 // Set save path
123                 $this->savePath = $savePath;
124         }
125
126         /**
127          * Getter for save path
128          *
129          * @return      $savePath               The local save path where we shall put our serialized classes
130          */
131         public final function getSavePath () {
132                 return $this->savePath;
133         }
134
135         /**
136          * Getter for last error message
137          *
138          * @return      $lastError      Last error message
139          */
140         public final function getLastError () {
141                 return $this->lastError;
142         }
143
144         /**
145          * Getter for last exception
146          *
147          * @return      $lastException  Last thrown exception
148          */
149         public final function getLastException () {
150                 return $this->lastException;
151         }
152
153         /**
154          * Saves a given object to the local file system by serializing and
155          * transparently compressing it
156          *
157          * @param       $object                                 The object we shall save to the local file system
158          * @return      void
159          * @deprecated
160          */
161         public final function saveObject (FrameworkInterface $object) {
162                 // Get a string containing the serialized object. We cannot exchange
163                 // $this and $object here because $object does not need to worry
164                 // about it's limitations... ;-)
165                 $serialized = $this->serializeObject($object);
166
167                 // Get a path name plus file name and append the extension
168                 $fqfn = $this->getSavePath() . $object->getPathFileNameFromObject() . "." . $this->getFileExtension();
169
170                 // Save the file to disc we don't care here if the path is there,
171                 // this must be done in later methods.
172                 $this->getFileIoInstance()->saveFile($fqfn, array($this->getCompressorChannel()->getCompressorExtension(), $serialized));
173         }
174
175         /**
176          * Get a serialized string from the given object
177          *
178          * @param       $object                 The object we want to serialize and transparently
179          *                                                      compress
180          * @return      $serialized             A string containing the serialzed/compressed object
181          * @see         ObjectLimits    An object holding limition information
182          * @see         SerializationContainer  A special container class for e.g.
183          *                                                                      attributes from limited objects
184          * @deprecated
185          */
186         private function serializeObject (FrameworkInterface $object) {
187                 // If there is no limiter instance we serialize the whole object
188                 // otherwise only in the limiter object (ObjectLimits) specified
189                 // attributes summarized in a special container class
190                 if ($this->getLimitInstance() === null) {
191                         // Serialize the whole object. This tribble call is the reason
192                         // why we need a fall-back implementation in CompressorChannel
193                         // of the methods compressStream() and decompressStream().
194                         $serialized = $this->getCompressorChannel()->getCompressor()->compressStream(serialize($object));
195                 } else {
196                         // Serialize only given attributes in a special container
197                         $container = SerializationContainer::createSerializationContainer($this->getLimitInstance(), $object);
198
199                         // Serialize the container
200                         $serialized = $this->getCompressorChannel()->getCompressor()->compressStream(serialize($container));
201                 }
202
203                 // Return the serialized object string
204                 return $serialized;
205         }
206
207         /**
208          * Analyses if a unique ID has already been used or not by search in the
209          * local database folder.
210          *
211          * @param       $uniqueID               A unique ID number which shall be checked
212          *                                                      before it will be used
213          * @param       $inConstructor  If we got called in a de/con-structor or
214          *                                                      from somewhere else
215          * @return      $isUnused               true    = The unique ID was not found in the database,
216          *                                                      false = It is already in use by an other object
217          * @throws      NoArrayCreatedException         If explode() fails to create an array
218          * @throws      InvalidArrayCountException      If the array contains less or
219          *                                                                      more than two elements
220          */
221         public function isUniqueIdUsed ($uniqueID, $inConstructor = false) {
222                 // Currently not used... ;-)
223                 $isUsed = false;
224
225                 // Split the unique ID up in path and file name
226                 $pathFile = explode("@", $uniqueID);
227
228                 // Are there two elements? Index 0 is the path, 1 the file name + global extension
229                 if (!is_array($pathFile)) {
230                         // No array found
231                         if ($inConstructor) {
232                                 return false;
233                         } else {
234                                 throw new NoArrayCreatedException(array($this, "pathFile"), self::EXCEPTION_ARRAY_EXPECTED);
235                         }
236                 } elseif (count($pathFile) != 2) {
237                         // Invalid ID returned!
238                         if ($inConstructor) {
239                                 return false;
240                         } else {
241                                 throw new InvalidArrayCountException(array($this, "pathFile", count($pathFile), 2), self::EXCEPTION_ARRAY_HAS_INVALID_COUNT);
242                         }
243                 }
244
245                 // Create full path name
246                 $pathName = $this->getSavePath() . $pathFile[0];
247
248                 // Check if the file is there with a file handler
249                 if ($inConstructor) {
250                         // No exceptions in constructors and destructors!
251                         $dirInstance = FrameworkDirectoryPointer::createFrameworkDirectoryPointer($pathName, true);
252
253                         // Has an object being created?
254                         if (!is_object($dirInstance)) return false;
255                 } else {
256                         // Outside a constructor
257                         try {
258                                 $dirInstance = FrameworkDirectoryPointer::createFrameworkDirectoryPointer($pathName);
259                         } catch (PathIsNoDirectoryException $e) {
260                                 // Okay, path not found
261                                 return false;
262                         }
263                 }
264
265                 // Initialize the search loop
266                 $isValid = false;
267                 while ($dataFile = $dirInstance->readDirectoryExcept(array(".", "..", ".htaccess", ".svn"))) {
268                         // Generate FQFN for testing
269                         $fqfn = sprintf("%s/%s", $pathName, $dataFile);
270                         $this->setLastFile($fqfn);
271
272                         // Get instance for file handler
273                         $inputHandler = $this->getFileIoInstance();
274
275                         // Try to read from it. This makes it sure that the file is
276                         // readable and a valid database file
277                         $this->setLastFileContents($inputHandler->loadFileContents($fqfn));
278
279                         // Extract filename (= unique ID) from it
280                         $ID = substr(basename($fqfn), 0, -(strlen($this->getFileExtension()) + 1));
281
282                         // Is this the required unique ID?
283                         if ($ID == $pathFile[1]) {
284                                 // Okay, already in use!
285                                 $isUsed = true;
286                         }
287                 }
288
289                 // Close the directory handler
290                 $dirInstance->closeDirectory();
291
292                 // Now the same for the file...
293                 return $isUsed;
294         }
295
296         /**
297          * Setter for the last read file
298          *
299          * @param               $fqfn   The FQFN of the last read file
300          * @return      void
301          */
302         private final function setLastFile ($fqfn) {
303                 // Cast string
304                 $fqfn = (string) $fqfn;
305                 $this->lastFile = $fqfn;
306         }
307
308         /**
309          * Reset the last error and exception instance. This should be done after
310          * a successfull "query"
311          *
312          * @return      void
313          */
314         private final function resetLastError () {
315                 $this->lastError = "";
316                 $this->lastException = null;
317         }
318
319         /**
320          * Getter for last read file
321          *
322          * @return      $lastFile               The last read file's name with full path
323          */
324         public final function getLastFile () {
325                 return $this->lastFile;
326         }
327
328         /**
329          * Setter for contents of the last read file
330          *
331          * @param               $contents               An array with header and data elements
332          * @return      void
333          */
334         private final function setLastFileContents ($contents) {
335                 // Cast array
336                 $contents = (array) $contents;
337                 $this->lastContents = $contents;
338         }
339
340         /**
341          * Getter for last read file's content as an array
342          *
343          * @return      $lastContent    The array with elements 'header' and 'data'.
344          */
345         public final function getLastContents () {
346                 return $this->lastContents;
347         }
348
349         /**
350          * Getter for file extension
351          *
352          * @return      $fileExtension  The array with elements 'header' and 'data'.
353          */
354         public final function getFileExtension () {
355                 return $this->fileExtension;
356         }
357
358         /**
359          * Get cached (last fetched) data from the local file database
360          *
361          * @param       $uniqueID       The ID number for looking up the data
362          * @return      $object         The restored object from the maybe compressed
363          *                                              serialized data
364          * @throws      MismatchingCompressorsException         If the compressor from
365          *                                                                      the loaded file
366          *                                                                      mismatches with the
367          *                                                                      current used one.
368          * @throws      NullPointerException    If the restored object
369          *                                                                      is null
370          * @throws      NoObjectException               If the restored "object"
371          *                                                                      is not an object instance
372          * @throws      MissingMethodException  If the required method
373          *                                                                      toString() is missing
374          */
375         public final function getObjectFromCachedData ($uniqueID) {
376                 // Get instance for file handler
377                 $inputHandler = $this->getFileIoInstance();
378
379                 // Get last file's name and contents
380                 $fqfn = $this->repairFQFN($this->getLastFile(), $uniqueID);
381                 $contents = $this->repairContents($this->getLastContents(), $fqfn);
382
383                 // Let's decompress it. First we need the instance
384                 $compressInstance = $this->getCompressorChannel();
385
386                 // Is the compressor's extension the same as the one from the data?
387                 if ($compressInstance->getCompressorExtension() != $contents['header'][0]) {
388                         /**
389                          * @todo        For now we abort here but later we need to make this a little more dynamic.
390                          */
391                         throw new MismatchingCompressorsException(array($this, $contents['header'][0], $fqfn, $compressInstance->getCompressorExtension()), self::EXCEPTION_MISMATCHING_COMPRESSORS);
392                 }
393
394                 // Decompress the data now
395                 $serialized = $compressInstance->getCompressor()->decompressStream($contents['data']);
396
397                 // And unserialize it...
398                 $object = unserialize($serialized);
399
400                 // This must become a valid object, so let's check it...
401                 if (is_null($object)) {
402                         // Is null, throw exception
403                         throw new NullPointerException($object, self::EXCEPTION_IS_NULL_POINTER);
404                 } elseif (!is_object($object)) {
405                         // Is not an object, throw exception
406                         throw new NoObjectException($object, self::EXCEPTION_IS_NO_OBJECT);
407                 } elseif (!$object instanceof FrameworkInterface) {
408                         // A highly required method was not found... :-(
409                         throw new MissingMethodException(array($object, '__toString'), self::EXCEPTION_MISSING_METHOD);
410                 }
411
412                 // And return the object
413                 return $object;
414         }
415
416         /**
417          * Private method for re-gathering (repairing) the FQFN
418          *
419          * @param               $fqfn           The current FQFN we shall validate
420          * @param               $uniqueID               The unique ID number
421          * @return      $fqfn           The repaired FQFN when it is empty
422          * @throws      NoArrayCreatedException         If explode() has not
423          *                                                                      created an array
424          * @throws      InvalidArrayCountException      If the array count is not
425          *                                                                      as the expected
426          */
427         private function repairFQFN ($fqfn, $uniqueID) {
428                 // Cast both strings
429                 $fqfn     = (string) $fqfn;
430                 $uniqueID = (string) $uniqueID;
431
432                 // Is there pre-cached data available?
433                 if (empty($fqfn)) {
434                         // Split the unique ID up in path and file name
435                         $pathFile = explode("@", $uniqueID);
436
437                         // Are there two elements? Index 0 is the path, 1 the file name + global extension
438                         if (!is_array($pathFile)) {
439                                 // No array found
440                                 throw new NoArrayCreatedException(array($this, "pathFile"), self::EXCEPTION_ARRAY_EXPECTED);
441                         } elseif (count($pathFile) != 2) {
442                                 // Invalid ID returned!
443                                 throw new InvalidArrayCountException(array($this, "pathFile", count($pathFile), 2), self::EXCEPTION_ARRAY_HAS_INVALID_COUNT);
444                         }
445
446                         // Create full path name
447                         $pathName = $this->getSavePath() . $pathFile[0];
448
449                         // Nothing cached, so let's create a FQFN first
450                         $fqfn = sprintf("%s/%s.%s", $pathName, $pathFile[1], $this->getFileExtension());
451                         $this->setLastFile($fqfn);
452                 }
453
454                 // Return repaired FQFN
455                 return $fqfn;
456         }
457
458         /**
459          * Private method for re-gathering the contents of a given file
460          *
461          * @param               $contents               The (maybe) already cached contents as an array
462          * @param               $fqfn           The current FQFN we shall validate
463          * @return      $contents               The repaired contents from the given file
464          */
465         private function repairContents ($contents, $fqfn) {
466                 // Is there some content and header (2 indexes) in?
467                 if ((!is_array($contents)) || (count($contents) != 2) || (!isset($contents['header'])) || (!isset($contents['data']))) {
468                         // No content found so load the file again
469                         $contents = $inputHandler->loadFileContents($fqfn);
470
471                         // And remember all data for later usage
472                         $this->setLastContents($contents);
473                 }
474
475                 // Return the repaired contents
476                 return $contents;
477         }
478
479         /**
480          * Reads a local data file  and returns it's contents in an array
481          *
482          * @param       $fqfn   The FQFN for the requested file
483          * @return      $dataArray
484          */
485         private function getDataArrayFromFile ($fqfn) {
486                 // Get a file pointer
487                 $fileInstance = FrameworkFileInputPointer::createFrameworkFileInputPointer($fqfn);
488
489                 // Get the raw data and BASE64-decode it
490                 $compressedData = base64_decode($fileInstance->readLinesFromFile());
491
492                 // Close the file and throw the instance away
493                 $fileInstance->closeFile();
494                 unset($fileInstance);
495
496                 // Decompress it
497                 $serializedData = $this->getCompressorChannel()->getCompressor()->decompressStream($compressedData);
498
499                 // Unserialize it
500                 $dataArray = unserialize($serializedData);
501
502                 // Finally return it
503                 return $dataArray;
504         }
505
506         /**
507          * Writes data array to local file
508          *
509          * @param       $fqfn           The FQFN of the local file
510          * @param       $dataArray      An array with all the data we shall write
511          * @return      void
512          */
513         private function writeDataArrayToFqfn ($fqfn, array $dataArray) {
514                 // Get a file pointer instance
515                 $fileInstance = FrameworkFileOutputPointer::createFrameworkFileOutputPointer($fqfn, 'w');
516
517                 // Serialize and compress it
518                 $compressedData = $this->getCompressorChannel()->getCompressor()->compressStream(serialize($dataArray));
519
520                 // Write this data BASE64 encoded to the file
521                 $fileInstance->writeToFile(base64_encode($compressedData));
522
523                 // Close the file pointer
524                 $fileInstance->closeFile();
525         }
526
527         /**
528          * Makes sure that the database connection is alive
529          *
530          * @return      void
531          */
532         public function connectToDatabase () {
533                 /* @TODO Do some checks on the database directory and files here */
534         }
535
536         /**
537          * Loads data saved with saveObject from the database and re-creates a
538          * full object from it.
539          * If limitObject() was called before a new object ObjectContainer with
540          * all requested attributes will be returned instead.
541          *
542          * @return      Object  The fully re-created object or instance to
543          *                                      ObjectContainer
544          * @throws      SavePathIsEmptyException                If the given save path is an
545          *                                                                                      empty string
546          * @throws      SavePathIsNoDirectoryException  If the save path is no
547          *                                                                                      path (e.g. a file)
548          * @throws      SavePathReadProtectedException  If the save path is read-
549          *                                                                                      protected
550          * @throws      SavePathWriteProtectedException If the save path is write-
551          *                                                                                      protected
552          */
553         public function loadObject () {
554                 // Already connected? Then abort here
555                 if ($this->alreadyConnected === true) return true;
556
557                 // Get the save path
558                 $savePath = $this->getSavePath();
559
560                 if (empty($savePath)) {
561                         // Empty string
562                         throw new SavePathIsEmptyException($dbInstance, self::EXCEPTION_UNEXPECTED_EMPTY_STRING);
563                 } elseif (!is_dir($savePath)) {
564                         // Is not a dir
565                         throw new SavePathIsNoDirectoryException($savePath, self::EXCEPTION_INVALID_PATH_NAME);
566                 } elseif (!is_readable($savePath)) {
567                         // Path not readable
568                         throw new SavePathReadProtectedException($savePath, self::EXCEPTION_READ_PROTECED_PATH);
569                 } elseif (!is_writeable($savePath)) {
570                         // Path not writeable
571                         throw new SavePathWriteProtectedException($savePath, self::EXCEPTION_WRITE_PROTECED_PATH);
572                 }
573
574                 // "Connection" established... ;-)
575                 $this->alreadyConnected = true;
576         }
577
578         /**
579          * Starts a SELECT query on the database by given return type, table name
580          * and search criteria
581          *
582          * @param       $resultType             Result type ("array", "object" and "indexed" are valid)
583          * @param       $tableName              Name of the database table
584          * @param       $criteria               Local search criteria class
585          * @return      $resultData             Result data of the query
586          * @throws      UnsupportedCriteriaException    If the criteria is unsupported
587          * @throws      SqlException                                    If an "SQL error" occurs
588          */
589         public function querySelect ($resultType, $tableName, LocalSearchCriteria $criteriaInstance) {
590                 // The result is null by any errors
591                 $resultData = null;
592
593                 // Create full path name
594                 $pathName = $this->getSavePath() . $tableName . '/';
595
596                 // A "select" query is not that easy on local files, so first try to
597                 // find the "table" which is in fact a directory on the server
598                 try {
599                         // Get a directory pointer instance
600                         $directoryInstance = FrameworkDirectoryPointer::createFrameworkDirectoryPointer($pathName);
601
602                         // Initialize the result data, this need to be rewritten e.g. if a local file cannot be read
603                         $resultData = array(
604                                 'status'        => "ok",
605                                 'rows'          => array()
606                         );
607
608                         // Initialize limit/skip
609                         $limitFound = 0; $skipFound = 0;
610
611                         // Read the directory with some exceptions
612                         while (($dataFile = $directoryInstance->readDirectoryExcept(array(".", "..", ".htaccess", ".svn"))) && ($limitFound < $criteriaInstance->getLimit())) {
613                                 // Read the file
614                                 $dataArray = $this->getDataArrayFromFile($pathName . $dataFile);
615
616                                 // Is this an array?
617                                 if (is_array($dataArray)) {
618                                         // Search in the criteria with FMFW (First Matches, First Wins)
619                                         foreach ($dataArray as $key=>$value) {
620                                                 // Get criteria element
621                                                 $criteria = $criteriaInstance->getCriteriaElemnent($key);
622
623                                                 // Is the criteria met?
624                                                 if ((!is_null($criteria)) && ($criteria == $value))  {
625
626                                                         // Shall we skip this entry?
627                                                         if ($criteriaInstance->getSkip() > 0) {
628                                                                 // We shall skip some entries
629                                                                 if ($skipFound < $criteriaInstance->getSkip()) {
630                                                                         // Skip this entry
631                                                                         $skipFound++;
632                                                                         break;
633                                                                 } // END - if
634                                                         } // END - if
635
636                                                         // Entry found!
637                                                         $resultData['rows'][] = $dataArray;
638                                                         $limitFound++;
639                                                         break;
640                                                 } // END - if
641                                         } // END - foreach
642                                 } else {
643                                         // Throw an exception here
644                                         throw new SqlException(sprintf("File &#39;%s&#39; contains invalid data.", $dataFile), self::DB_CODE_DATA_FILE_CORRUPT);
645                                 }
646                         } // END - while
647
648                         // Close directory and throw the instance away
649                         $directoryInstance->closeDirectory();
650                         unset($directoryInstance);
651
652                         // Reset last error message and exception
653                         $this->resetLastError();
654                 } catch (PathIsNoDirectoryException $e) {
655                         // Path not found means "table not found" for real databases...
656                         $this->lastException = $e;
657                         $this->lastError = $e->getMessage();
658
659                         // So throw an SqlException here with faked error message
660                         throw new SqlException (array($this, sprintf("Table &#39;%s&#39; not found", $tableName), self::DB_CODE_TABLE_MISSING), self::EXCEPTION_SQL_QUERY);
661                 } catch (FrameworkException $e) {
662                         // Catch all exceptions and store them in last error
663                         $this->lastException = $e;
664                         $this->lastError = $e->getMessage();
665                 }
666
667                 // Return the gathered result
668                 return $resultData;
669         }
670
671         /**
672          * "Inserts" a data set instance into a local file database folder
673          *
674          * @param       $dataSetInstance        A storeable data set
675          * @return      void
676          * @throws      SqlException    If an SQL error occurs
677          */
678         public function queryInsertDataSet (StoreableCriteria $dataSetInstance) {
679                 // Create full path name
680                 $fqfn = sprintf("%s%s/%s.%s",
681                         $this->getSavePath(),
682                         $dataSetInstance->getTableName(),
683                         md5($dataSetInstance->getUniqueValue()),
684                         $this->getFileExtension()
685                 );
686
687                 // Try to save the request away
688                 try {
689                         // Write the data away
690                         $this->writeDataArrayToFqfn($fqfn, $dataSetInstance->getCriteriaArray());
691
692                         // Reset last error message and exception
693                         $this->resetLastError();
694                 } catch (FrameworkException $e) {
695                         // Catch all exceptions and store them in last error
696                         $this->lastException = $e;
697                         $this->lastError = $e->getMessage();
698
699                         // Throw an SQL exception
700                         throw new SqlException (array($this, sprintf("Cannot write data to table &#39;%s&#39;", $tableName), self::DB_CODE_TABLE_UNWRITEABLE), self::EXCEPTION_SQL_QUERY);
701                 }
702         }
703
704         /**
705          * "Updates" a data set instance with a database layer
706          *
707          * @param       $dataSetInstance        A storeable data set
708          * @return      void
709          * @throws      SqlException    If an SQL error occurs
710          */
711         public function queryUpdateDataSet (StoreableCriteria $dataSetInstance) {
712                 // Create full path name
713                 $pathName = $this->getSavePath() . $dataSetInstance->getTableName() . '/';
714
715                 // Try all the requests
716                 try {
717                         // Get a file pointer instance
718                         $directoryInstance = FrameworkDirectoryPointer::createFrameworkDirectoryPointer($pathName);
719
720                         // Initialize limit/skip
721                         $limitFound = 0; $skipFound = 0;
722
723                         // Get the criteria array from the dataset
724                         $criteriaArray = $dataSetInstance->getCriteriaArray();
725
726                         // Get search criteria
727                         $searchInstance = $dataSetInstance->getSearchInstance();
728
729                         // Read the directory with some exceptions
730                         while (($dataFile = $directoryInstance->readDirectoryExcept(array(".", "..", ".htaccess", ".svn"))) && ($limitFound < $searchInstance->getLimit())) {
731                                 // Open this file for reading
732                                 $dataArray = $this->getDataArrayFromFile($pathName . $dataFile);
733
734                                 // Is this an array?
735                                 if (is_array($dataArray)) {
736                                         // Search in the criteria with FMFW (First Matches, First Wins)
737                                         foreach ($dataArray as $key=>$value) {
738                                                 // Get criteria element
739                                                 $criteria = $searchInstance->getCriteriaElemnent($key);
740
741                                                 // Is the criteria met?
742                                                 if ((!is_null($criteria)) && ($criteria == $value))  {
743
744                                                         // Shall we skip this entry?
745                                                         if ($searchInstance->getSkip() > 0) {
746                                                                 // We shall skip some entries
747                                                                 if ($skipFound < $searchInstance->getSkip()) {
748                                                                         // Skip this entry
749                                                                         $skipFound++;
750                                                                         break;
751                                                                 } // END - if
752                                                         } // END - if
753
754                                                         // Entry found, so update it
755                                                         foreach ($criteriaArray as $criteriaKey=>$criteriaValue) {
756                                                                 $dataArray[$criteriaKey] = $criteriaValue;
757                                                         } // END - foreach
758
759                                                         // Write the data to a local file
760                                                         $this->writeDataArrayToFqfn($pathName . $dataFile, $dataArray);
761
762                                                         // Count it
763                                                         $limitFound++;
764                                                         break;
765                                                 } // END - if
766                                         } // END - foreach
767                                 } // END - if
768                         } // END - while
769
770                         // Close the file pointer
771                         $directoryInstance->closeDirectory();
772
773                         // Reset last error message and exception
774                         $this->resetLastError();
775                 } catch (FrameworkException $e) {
776                         // Catch all exceptions and store them in last error
777                         $this->lastException = $e;
778                         $this->lastError = $e->getMessage();
779
780                         // Throw an SQL exception
781                         throw new SqlException (array($this, sprintf("Cannot write data to table &#39;%s&#39;", $tableName), self::DB_CODE_TABLE_UNWRITEABLE), self::EXCEPTION_SQL_QUERY);
782                 }
783         }
784 }
785
786 // [EOF]
787 ?>