Fixed: Fatal error: Access to undeclared static property: BaseFrameworkSystem::$hashL...
[core.git] / inc / classes / main / file_directories / class_BaseFile.php
1 <?php
2 /**
3  * A general file class
4  *
5  * @author              Roland Haeder <webmaster@ship-simu.org>
6  * @version             0.0.0
7  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2012 Core Developer Team
8  * @license             GNU GPL 3.0 or any newer version
9  * @link                http://www.ship-simu.org
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 class BaseFile extends BaseFrameworkSystem {
25         /**
26          * Separator for header data
27          */
28         const SEPARATOR_HEADER_DATA = 0x01;
29
30         /**
31          * Separator header->entries
32          */
33         const SEPARATOR_HEADER_ENTRIES = 0x02;
34
35         /**
36          * Separator hash->name
37          */
38         const SEPARATOR_HASH_NAME = 0x03;
39
40         /**
41          * Separator entry->entry
42          */
43         const SEPARATOR_ENTRIES = 0x04;
44
45         /**
46          * Separator type->position
47          */
48         const SEPARATOR_TYPE_POSITION = 0x05;
49
50         /**
51          * Length of count
52          */
53         const LENGTH_COUNT = 20;
54
55         /**
56          * Length of position
57          */
58         const LENGTH_POSITION = 20;
59
60         /**
61          * Length of name
62          */
63         const LENGTH_NAME = 10;
64
65         /**
66          * Maximum length of entry type
67          */
68         const LENGTH_TYPE = 20;
69
70         //***** Array elements for 'gaps' array *****
71
72         /**
73          * Start of gap
74          */
75         const GAPS_INDEX_START = 'start';
76
77         /**
78          * End of gap
79          */
80         const GAPS_INDEX_END = 'end';
81
82         /**
83          * Counter for total entries
84          */
85         private $totalEntries = 0;
86
87         /**
88          * Current seek position
89          */
90         private $seekPosition = 0;
91
92         /**
93          * Size of header
94          */
95         private $headerSize = 0;
96
97         /**
98          * File header
99          */
100         private $header = array();
101
102         /**
103          * Seek positions for gaps ("fragmentation")
104          */
105         private $gaps = array();
106
107         /**
108          * Seek positions for damaged entries (e.g. mismatching hash sum, ...)
109          */
110         private $damagedEntries = array();
111
112         /**
113          * The current file we are working in
114          */
115         private $fileName = '';
116
117         /**
118          * Back-buffer
119          */
120         private $backBuffer = '';
121
122         /**
123          * Currently loaded block (will be returned by current())
124          */
125         private $currentBlock = '';
126
127         /**
128          * Protected constructor
129          *
130          * @param       $className      Name of the class
131 y        * @return      void
132          */
133         protected function __construct ($className) {
134                 // Call parent constructor
135                 parent::__construct($className);
136
137                 // Init counters and gaps array
138                 $this->initCountersGapsArray();
139         }
140
141         /**
142          * Destructor for cleaning purposes, etc
143          *
144          * @return      void
145          */
146         public final function __destruct() {
147                 // Try to close a file
148                 $this->closeFile();
149
150                 // Call the parent destructor
151                 parent::__destruct();
152         }
153
154         /**
155          * Initializes counter for valid entries, arrays for damaged entries and
156          * an array for gap seek positions. If you call this method on your own,
157          * please re-analyze the file structure. So you are better to call
158          * analyzeFile() instead of this method.
159          *
160          * @return      void
161          */
162         public function initCountersGapsArray () {
163                 // Init counter and seek position
164                 $this->setCounter(0);
165                 $this->setSeekPosition(0);
166
167                 // Init arrays
168                 $this->gaps = array();
169                 $this->damagedEntries = array();
170         }
171
172         /**
173          * Getter for total entries
174          *
175          * @return      $totalEntries   Total entries in this file
176          */
177         protected final function getCounter () {
178                 // Get it
179                 return $this->totalEntries;
180         }
181
182         /**
183          * Setter for total entries
184          *
185          * @param       $totalEntries   Total entries in this file
186          * @return      void
187          */
188         protected final function setCounter ($counter) {
189                 // Set it
190                 $this->totalEntries = $counter;
191         }
192
193         /**
194          * Increment counter
195          *
196          * @return      void
197          */
198         protected final function incrementCounter () {
199                 // Get it
200                 $this->totalEntries++;
201         }
202
203         /**
204          * Getter for header size
205          *
206          * @return      $totalEntries   Size of file header
207          */
208         public final function getHeaderSize () {
209                 // Get it
210                 return $this->headerSize;
211         }
212
213         /**
214          * Setter for header size
215          *
216          * @param       $headerSize             Size of file header
217          * @return      void
218          */
219         public final function setHeaderSize ($headerSize) {
220                 // Set it
221                 $this->headerSize = $headerSize;
222         }
223
224         /**
225          * Getter for header array
226          *
227          * @return      $totalEntries   Size of file header
228          */
229         public final function getHeader () {
230                 // Get it
231                 return $this->header;
232         }
233
234         /**
235          * Setter for header
236          *
237          * @param       $header         Array for a file header
238          * @return      void
239          */
240         public final function setHeader (array $header) {
241                 // Set it
242                 $this->header = $header;
243         }
244
245         /**
246          * Getter for seek position
247          *
248          * @return      $seekPosition   Current seek position (stored here in object)
249          */
250         protected final function getSeekPosition () {
251                 // Get it
252                 return $this->seekPosition;
253         }
254
255         /**
256          * Setter for seek position
257          *
258          * @param       $seekPosition   Current seek position (stored here in object)
259          * @return      void
260          */
261         protected final function setSeekPosition ($seekPosition) {
262                 // And set it
263                 $this->seekPosition = $seekPosition;
264         }
265
266         /**
267          * Updates seekPosition attribute from file to avoid to much access on file.
268          *
269          * @return      void
270          */
271         public function updateSeekPosition () {
272                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
273
274                 // Get key (= seek position)
275                 $seekPosition = $this->key();
276                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] Setting seekPosition=%s', __METHOD__, __LINE__, $seekPosition));
277
278                 // And set it here
279                 $this->setSeekPosition($seekPosition);
280
281                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__));
282         }
283
284         /**
285          * Seeks to beginning of file, updates seek position in this object and
286          * flushes the header.
287          *
288          * @return      void
289          */
290         protected function rewindUpdateSeekPosition () {
291                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
292
293                 // flushFileHeader must be callable
294                 assert(is_callable(array($this, 'flushFileHeader')));
295
296                 // Seek to beginning of file
297                 $this->rewind();
298
299                 // And update seek position ...
300                 $this->updateSeekPosition();
301
302                 // ... to write it back into the file
303                 $this->flushFileHeader();
304
305                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!!', __METHOD__, __LINE__));
306         }
307
308         /**
309          * Seeks to old position
310          *
311          * @return      void
312          */
313         protected function seekToOldPosition () {
314                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
315
316                 // Seek to currently ("old") saved position
317                 $this->seek($this->getSeekPosition());
318
319                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!!', __METHOD__, __LINE__));
320         }
321
322         /**
323          * Checks whether the block separator has been found
324          *
325          * @param       $str            String to look in
326          * @return      $isFound        Whether the block separator has been found
327          */
328         public static function isBlockSeparatorFound ($str) {
329                 // Determine it
330                 $isFound = (strpos($str, chr(self::SEPARATOR_ENTRIES)) !== FALSE);
331
332                 // Return result
333                 return $isFound;
334         }
335
336         /**
337          * Getter for the file pointer
338          *
339          * @return      $filePointer    The file pointer which shall be a valid file resource
340          * @throws      UnsupportedOperationException   If this method is called
341          */
342         public final function getPointer () {
343                 throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION);
344         }
345
346         /**
347          * Setter for file name
348          *
349          * @param       $fileName       The new file name
350          * @return      void
351          */
352         protected final function setFileName ($fileName) {
353                 $fileName = (string) $fileName;
354                 $this->fileName = $fileName;
355         }
356
357         /**
358          * Getter for file name
359          *
360          * @return      $fileName       The current file name
361          */
362         public final function getFileName () {
363                 return $this->fileName;
364         }
365
366         /**
367          * Initializes the back-buffer by setting it to an empty string.
368          *
369          * @return      void
370          */
371         private function initBackBuffer () {
372                 // Simply call the setter
373                 $this->setBackBuffer('');
374         }
375
376         /**
377          * Setter for backBuffer field
378          *
379          * @param       $backBuffer             Characters to "store" in back-buffer
380          * @return      void
381          */
382         private function setBackBuffer ($backBuffer) {
383                 // Cast to string (so no arrays or objects)
384                 $backBuffer = (string) $backBuffer;
385
386                 // ... and set it
387                 $this->backBuffer = $backBuffer;
388         }
389
390         /**
391          * Getter for backBuffer field
392          *
393          * @return      $backBuffer             Characters "stored" in back-buffer
394          */
395         private function getBackBuffer () {
396                 return $this->backBuffer;
397         }
398
399         /**
400          * Setter for currentBlock field
401          *
402          * @param       $currentBlock           Characters to set a currently loaded block
403          * @return      void
404          */
405         private function setCurrentBlock ($currentBlock) {
406                 // Cast to string (so no arrays or objects)
407                 $currentBlock = (string) $currentBlock;
408
409                 // ... and set it
410                 $this->currentBlock = $currentBlock;
411         }
412
413         /**
414          * Gets currently read data
415          *
416          * @return      $current        Currently read data
417          */
418         public function getCurrentBlock () {
419                 // Return it
420                 return $this->currentBlock;
421         }
422
423         /**
424          * Initializes this file class
425          *
426          * @param       $fileName       Name of this abstract file
427          * @return      void
428          */
429         protected function initFile ($fileName) {
430                 // Get a file i/o pointer instance
431                 $pointerInstance = ObjectFactory::createObjectByConfiguredName('file_raw_input_output_class', array($fileName));
432
433                 // ... and set it here
434                 $this->setPointerInstance($pointerInstance);
435         }
436
437         /**
438          * Writes data at given position
439          *
440          * @param       $seekPosition   Seek position
441          * @param       $data                   Data to be written
442          * @param       $flushHeader    Whether to flush the header (default: flush)
443          * @return      void
444          */
445         protected function writeData ($seekPosition, $data, $flushHeader = TRUE) {
446                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] seekPosition=%s,data()=%s - CALLED!', __METHOD__, __LINE__, $seekPosition, strlen($data)));
447
448                 // Write data at given position
449                 $this->getPointerInstance()->writeAtPosition($seekPosition, $data);
450
451                 // Update seek position
452                 $this->updateSeekPosition();
453
454                 // Flush the header?
455                 if ($flushHeader === TRUE) {
456                         // Flush header
457                         $this->flushFileHeader();
458
459                         // Seek to old position
460                         $this->seekToOldPosition();
461                 } // END - if
462
463                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__));
464         }
465
466         /**
467          * Marks the currently loaded block as empty (with length of the block)
468          *
469          * @param       $length         Length of the block
470          * @return      void
471          */
472         protected function markCurrentBlockAsEmpty ($length) {
473                 // Get current seek position
474                 $currentPosition = $this->key();
475
476                 // Now add it as gap entry
477                 array_push($this->gaps, array(
478                         self::GAPS_INDEX_START  => ($currentPosition - $length),
479                         self::GAPS_INDEX_END    => $currentPosition,
480                 ));
481         }
482
483         /**
484          * Checks whether the file header is initialized
485          *
486          * @return      $isInitialized  Whether the file header is initialized
487          */
488         public function isFileHeaderInitialized () {
489                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
490
491                 // Default is not initialized
492                 $isInitialized = FALSE;
493
494                 // Is the file initialized?
495                 if ($this->isFileInitialized()) {
496                         // Some bytes has been written, so rewind to start of it.
497                         $rewindStatus = $this->rewind();
498                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] rewindStatus=%s', __METHOD__, __LINE__, $rewindStatus));
499
500                         // Is the rewind() call successfull?
501                         if ($rewindStatus != 1) {
502                                 // Something bad happened
503                                 self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] Could not rewind().', __METHOD__, __LINE__));
504                         } // END - if
505
506                         // Read file header
507                         $this->readFileHeader();
508
509                         // The above method does already check the header
510                         $isInitialized = TRUE;
511                 } // END - if
512
513                 // Return result
514                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] isInitialized=%d - EXIT!', __METHOD__, __LINE__, intval($isInitialized)));
515                 return $isInitialized;
516         }
517
518         /**
519          * Checks whether the assigned file has been initialized
520          *
521          * @return      $isInitialized          Whether the file's size is zero
522          */
523         public function isFileInitialized () {
524                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
525
526                 // Get it from iterator which holds the pointer instance. If FALSE is returned
527                 $fileSize = $this->size();
528                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] fileSize=%s', __METHOD__, __LINE__, $fileSize));
529
530                 /*
531                  * The returned file size should not be FALSE or NULL as this means
532                  * that the pointer class does not work correctly.
533                  */
534                 assert(is_int($fileSize));
535
536                 // Is more than 0 returned?
537                 $isInitialized = ($fileSize > 0);
538
539                 // Return result
540                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] isInitialized=%d - EXIT!', __METHOD__, __LINE__, intval($isInitialized)));
541                 return $isInitialized;
542         }
543
544         /**
545          * Creates the assigned file
546          *
547          * @return      void
548          */
549         public function createFileHeader () {
550                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
551
552                 // The file's header should not be initialized here
553                 assert(!$this->isFileHeaderInitialized());
554
555                 // Simple flush file header which will create it.
556                 $this->flushFileHeader();
557
558                 // Rewind seek position (to beginning of file) and update/flush file header
559                 $this->rewindUpdateSeekPosition();
560
561                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!!', __METHOD__, __LINE__));
562         }
563
564         /**
565          * Pre-allocates file (if enabled) with some space for later faster write access.
566          *
567          * @param       $type   Type of the file
568          * @return      void
569          */
570         public function preAllocateFile ($type) {
571                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
572
573                 // Is it enabled?
574                 if ($this->getConfigInstance()->getConfigEntry($type . '_pre_allocate_enabled') != 'Y') {
575                         // Not enabled
576                         self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] Not pre-allocating file.', __METHOD__, __LINE__));
577
578                         // Don't continue here.
579                         return;
580                 } // END - if
581
582                 // Message to user
583                 self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] Pre-allocating file ...', __METHOD__, __LINE__));
584
585                 // Calculate minimum length for one entry
586                 $minLengthEntry = $this->getBlockInstance()->calculateMinimumBlockLength();
587                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] minLengthEntry=%s', __METHOD__, __LINE__, $minLengthEntry));
588
589                 // Calulcate seek position
590                 $seekPosition = $minLengthEntry * $this->getConfigInstance()->getConfigEntry($type . '_pre_allocate_count');
591                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] seekPosition=%s', __METHOD__, __LINE__, $seekPosition));
592
593                 // Now simply write a NUL there. This will pre-allocate the file.
594                 $this->writeData($seekPosition, chr(0));
595
596                 // Rewind seek position (to beginning of file) and update/flush file header
597                 $this->rewindUpdateSeekPosition();
598
599                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__));
600         }
601
602         /**
603          * Close a file source and set it's instance to null and the file name
604          * to empty
605          *
606          * @return      void
607          * @todo        ~10% done?
608          */
609         public function closeFile () {
610                 $this->partialStub('Unfinished method.');
611
612                 // Remove file name
613                 $this->setFileName('');
614         }
615
616         /**
617          * Determines seek position
618          *
619          * @return      $seekPosition   Current seek position
620          */
621         public function determineSeekPosition () {
622                 // Call pointer instance
623                 return $this->getPointerInstance()->determineSeekPosition();
624         }
625
626         /**
627          * Seek to given offset (default) or other possibilities as fseek() gives.
628          *
629          * @param       $offset         Offset to seek to (or used as "base" for other seeks)
630          * @param       $whence         Added to offset (default: only use offset to seek to)
631          * @return      $status         Status of file seek: 0 = success, -1 = failed
632          */
633         public function seek ($offset, $whence = SEEK_SET) {
634                 // Call pointer instance
635                 return $this->getPointerInstance()->seek($offset, $whence);
636         }
637
638         /**
639          * Size of this file
640          *
641          * @return      $size   Size (in bytes) of file
642          * @todo        Handle seekStatus
643          */
644         public function size () {
645                 // Call pointer instance
646                 return $this->getPointerInstance()->size();
647         }
648
649         /**
650          * Read data a file pointer
651          *
652          * @return      mixed   The result of fread()
653          * @throws      NullPointerException    If the file pointer instance
654          *                                                                      is not set by setPointer()
655          * @throws      InvalidResourceException        If there is being set
656          */
657         public function readFromFile () {
658                 // Call pointer instance
659                 return $this->getPointerInstance()->readFromFile();
660         }
661
662         /**
663          * Reads given amount of bytes from file.
664          *
665          * @param       $bytes  Amount of bytes to read
666          * @return      $data   Data read from file
667          */
668         public function read ($bytes) {
669                 // Call pointer instance
670                 return $this->getPointerInstance()->read($bytes);
671         }
672
673         /**
674          * Write data to a file pointer
675          *
676          * @param       $dataStream             The data stream we shall write to the file
677          * @return      mixed                   Number of writes bytes or FALSE on error
678          * @throws      NullPointerException    If the file pointer instance
679          *                                                                      is not set by setPointer()
680          * @throws      InvalidResourceException        If there is being set
681          *                                                                                      an invalid file resource
682          */
683         public function writeToFile ($dataStream) {
684                 // Call pointer instance
685                 return $this->getPointerInstance()->writeToFile($dataStream);
686         }
687
688         /**
689          * Rewinds to the beginning of the file
690          *
691          * @return      $status         Status of this operation
692          */
693         public function rewind () {
694                 // Call pointer instance
695                 return $this->getPointerInstance()->rewind();
696         }
697
698         /**
699          * Determines whether the EOF has been reached
700          *
701          * @return      $isEndOfFileReached             Whether the EOF has been reached
702          */
703         public final function isEndOfFileReached () {
704                 // Call pointer instance
705                 return $this->getPointerInstance()->isEndOfFileReached();
706         }
707
708         /**
709          * Analyzes entries in index file. This will count all found (and valid)
710          * entries, mark invalid as damaged and count gaps ("fragmentation"). If
711          * only gaps are found, the file is considered as "virgin" (no entries).
712          *
713          * @return      void
714          */
715         public function analyzeFile () {
716                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] CALLED!', __METHOD__, __LINE__));
717
718                 // Make sure the file is initialized
719                 assert($this->isFileInitialized());
720
721                 // Init counters and gaps array
722                 $this->initCountersGapsArray();
723
724                 // Output message (as this may take some time)
725                 self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] Analyzing file structure ... (this may take some time)', __METHOD__, __LINE__));
726
727                 // First rewind to the begining
728                 $this->rewind();
729
730                 // Then try to load all entries
731                 while ($this->valid()) {
732                         // Go to next entry
733                         $this->next();
734
735                         // Get current entry
736                         $current = $this->getCurrentBlock();
737
738                         // Debug message
739                         /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] current()=%s', __METHOD__, __LINE__, strlen($current)));
740                 } // END - while
741
742                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d:] EXIT!', __METHOD__, __LINE__));
743         }
744
745         /**
746          * Advances to next "block" of bytes
747          *
748          * @return      void
749          */
750         public function next () {
751                 // Is there nothing to read?
752                 if (!$this->valid()) {
753                         // Nothing to read
754                         return;
755                 } // END - if
756
757                 // Make sure the block instance is set
758                 assert($this->getBlockInstance() instanceof CalculatableBlock);
759
760                 // First calculate minimum block length
761                 $length = $this->getBlockInstance()->calculateMinimumBlockLength();
762
763                 // Short be more than zero!
764                 assert($length > 0);
765
766                 // Read possibly back-buffered bytes from previous call of next().
767                 $data = $this->getBackBuffer();
768
769                 /*
770                  * Read until a entry/block separator has been found. The next read
771                  * "block" may not fit, so this loop will continue until the EOB or EOF
772                  * has been reached whatever comes first.
773                  */
774                 while ((!$this->isEndOfFileReached()) && (!self::isBlockSeparatorFound($data))) {
775                         // Then read the next possible block
776                         $block = $this->read($length);
777
778                         // Is it all empty?
779                         if (strlen(trim($block)) == 0) {
780                                 // Mark this block as empty
781                                 $this->markCurrentBlockAsEmpty($length);
782
783                                 // Skip to next block
784                                 continue;
785                         } // END - if
786
787                         // At this block then
788                         $data .= $block;
789
790                         // A debug message
791                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(sprintf('[%s:%d] data()=%s', __FUNCTION__, __LINE__, strlen($data)));
792                 } // END - if
793
794                 // EOF reached?
795                 if ($this->isEndOfFileReached()) {
796                         // Set whole data as current read block
797                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('Calling setCurrentBlock(' . strlen($data) . ') ...');
798                         $this->setCurrentBlock($data);
799
800                         // Then abort here silently
801                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('EOF reached.');
802                         return;
803                 } // END - if
804
805                 /*
806                  * Init back-buffer which is the data that has been found beyond the
807                  * separator.
808                  */
809                 $this->initBackBuffer();
810
811                 // Separate data
812                 $dataArray = explode(chr(self::SEPARATOR_ENTRIES), $data);
813
814                 // This array must contain two elements
815                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('dataArray=' . print_r($dataArray, TRUE));
816                 assert(count($dataArray) == 2);
817
818                 // Left part is the actual block, right one the back-buffer data
819                 $this->setCurrentBlock($dataArray[0]);
820                 $this->setBackBuffer($dataArray[1]);
821         }
822
823         /**
824          * Checks wether the current entry is valid (not at the end of the file).
825          * This method will return TRUE if an emptied (nulled) entry has been found.
826          *
827          * @return      $isValid        Whether the next entry is valid
828          */
829         public function valid () {
830                 // Make sure the block instance is set
831                 assert($this->getBlockInstance() instanceof Block);
832
833                 // First calculate minimum block length
834                 $length = $this->getBlockInstance()->calculateMinimumBlockLength();
835
836                 // Short be more than zero!
837                 assert($length > 0);
838
839                 // Get current seek position
840                 $seekPosition = $this->key();
841
842                 // Then try to read it
843                 $data = $this->read($length);
844
845                 // If some bytes could be read, all is fine
846                 $isValid = ((is_string($data)) && (strlen($data) > 0));
847
848                 // Get header size
849                 $headerSize = $this->getBlockInstance()->getHeaderSize();
850
851                 // Is the seek position at or beyond the header?
852                 if ($seekPosition >= $headerSize) {
853                         // Seek back to old position
854                         $this->seek($seekPosition);
855                 } else {
856                         // Seek directly behind the header
857                         $this->seek($headerSize);
858                 }
859
860                 // Return result
861                 return $isValid;
862         }
863
864         /**
865          * Gets current seek position ("key").
866          *
867          * @return      $key    Current key in iteration
868          */
869         public function key () {
870                 // Call pointer instance
871                 return $this->getPointerInstance()->determineSeekPosition();
872         }
873
874         /**
875          * Reads the file header
876          *
877          * @return      void
878          */
879         public function readFileHeader () {
880                 // Make sure the block instance is set
881                 assert($this->getBlockInstance() instanceof Block);
882
883                 // Call block instance
884                 $this->getBlockInstance()->readFileHeader();
885         }
886
887         /**
888          * Flushes the file header
889          *
890          * @return      void
891          */
892         public function flushFileHeader () {
893                 // Make sure the block instance is set
894                 assert($this->getBlockInstance() instanceof Block);
895
896                 // Call block instance
897                 $this->getBlockInstance()->flushFileHeader();
898         }
899 }
900
901 // [EOF]
902 ?>