header comments added
[shipsimu.git] / ship-simu / inc / classes / main / io / class_FileIOStream.php
1 <?php
2 /**
3  * An universal class for file input/output streams.
4  *
5  * @author              Roland Haeder <webmaster@ship-simu.org>
6  * @version             0.0
7  * @copyright   Copyright(c) 2007, 2008 Roland Haeder, this is free software
8  * @license             GNU GPL 3.0 or any newer version
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23 class FileIOStream extends BaseFrameworkSystem implements FileInputStreamer, FileOutputStreamer {
24         /**
25          * Private constructor
26          */
27         private function __construct () {
28                 // Call parent constructor
29                 parent::constructor(__CLASS__);
30
31                 // Set part description
32                 $this->setPartDescr("Universal Datei-Ein-/Ausgabesystem");
33
34                 // Create unique ID
35                 $this->createUniqueID();
36
37                 // Clean-up a little
38                 $this->removeNumberFormaters();
39                 $this->removeSystemArray();
40         }
41
42         /**
43          * Create a file IO stream. This is a class for performing all actions
44          * on files like creating, deleting and loading them.
45          *
46          * @return      $ioInstance     An instance of FileIOStream
47          */
48         public final static function createFileIOStream () {
49                 // Create new instance
50                 $ioInstance = new FileIOStream();
51
52                 // Return the instance
53                 return $ioInstance;
54         }
55
56         /**
57          * Saves data to a given local file
58          *
59          * @param               $fileName               The file name for the to be saved file
60          * @param               $dataArray      The data we shall store to the file
61          * @return      void
62          * @see         FileOutputStreamer
63          */
64         public final function saveFile ($fileName, $dataArray) {
65                 // Try it five times
66                 $dirName = ""; $fileInstance = null;
67                 for ($idx = 0; $idx < 5; $idx++) {
68                         // Get a file output pointer
69                         try {
70                                 $fileInstance = FrameworkFileOutputPointer::createFrameworkFileOutputPointer($fileName, 'w');
71                         } catch (FilePointerNotOpenedException $e) {
72                                 // Create missing directory
73                                 $dirName = dirname($fileName);
74                                 for ($idx2 = 0; $idx2 < (2 - $idx); $idx2++) {
75                                         $dirName = dirname($dirName);
76                                 }
77                                 // Try to create it
78                                 @mkdir($dirName);
79                         }
80                 }
81
82                 // Write a header information for validation purposes
83                 $fileInstance->writeToFile(sprintf("@head^%s:%s:%s:%s\n",
84                         $dataArray[0],
85                         time(),
86                         strlen($dataArray[1]),
87                         md5($dataArray[1])
88                 ));
89
90                 // Encode the (maybe) binary stream with Base64
91                 $b64Stream = base64_encode($dataArray[1]);
92
93                 // write the data line by line
94                 $line = str_repeat(" ", 50); $idx = 0;
95                 while (strlen($line) == 50) {
96                         // Get 50 chars or less
97                         $line = substr($b64Stream, $idx, 50);
98
99                         // Save it to the stream
100                         $fileInstance->writeToFile(sprintf("@data^%s:%s\n",
101                                 $line,
102                                 md5($line)
103                         ));
104
105                         // Advance to the next 50-chars block
106                         $idx += 50;
107                 }
108
109                 // Close the file
110                 $fileInstance->closeFile();
111         }
112
113         /**
114          * Reads from a local file
115          *
116          * @param               $fqfn   The full-qualified file-name which we shall load
117          * @return      $array  An array with the element 'header' and 'data'
118          * @see         FileInputStreamer
119          */
120         public final function loadFileContents ($fqfn) {
121                 // Initialize some variables and arrays
122                 $inputBuffer = "";
123                 $lastBuffer = "";
124                 $header = array();
125                 $data = array();
126                 $readData = ""; // This will contain our read data
127
128                 // Get a file input handler
129                 $fileInstance = FrameworkFileInputPointer::createFrameworkFileInputPointer($fqfn);
130
131                 // Read all it's contents (we very and transparently decompress it below)
132                 while ($readRawLine = $fileInstance->readFromFile()) {
133                         // Add the read line to the buffer
134                         $inputBuffer .= $readRawLine;
135
136                         // Break infinite loop maybe caused by the input handler
137                         if ($lastBuffer == $inputBuffer) break;
138
139                         // Remember last read line for avoiding possible infinite loops
140                         $lastBuffer = $inputBuffer;
141                 }
142
143                 // Close directory handle
144                 $fileInstance->closeFile();
145
146                 // Convert it into an array
147                 $inputBuffer = explode("\n", $inputBuffer);
148
149                 // Now process the read lines and verify it's content
150                 foreach ($inputBuffer as $rawLine) {
151                         // Trim it a little but not the leading spaces/tab-stops
152                         $rawLine = rtrim($rawLine);
153
154                         // Analyze this line
155                         if (substr($rawLine, 0, 5) == "@head") {
156                                 // Header found, so let's extract it
157                                 $header = explode("^", $rawLine);
158                                 $header = trim($header[1]);
159
160                                 // Now we must convert it again into an array
161                                 $header = explode(":", $header);
162
163                                 // Is the header (maybe) valid?
164                                 if (count($header) != 4) {
165                                         // Throw an exception
166                                         throw new InvalidArrayCountException(array($this, "header", count($header), 4), self::EXCEPTION_ARRAY_HAS_INVALID_COUNT);
167                                 }
168                         } elseif (substr($rawLine, 0, 5) == "@data") {
169                                 // Is a data line!
170                                 $data = explode("^", $rawLine);
171                                 $data = $data[1];
172
173                                 // First element is the data, second the MD5 checksum
174                                 $data = explode(":", $data);
175
176                                 // Validate the read line
177                                 if (count($data) == 2) {
178                                         if (md5($data[0]) != $data[1]) {
179                                                 // MD5 hash did not match!
180                                                 throw new InvalidMD5ChecksumException(array($this, md5($data[0]), $data[1]), self::EXCEPTION_MD5_CHECKSUMS_MISMATCH);
181                                         }
182                                 } else {
183                                         // Invalid count!
184                                         throw new InvalidArrayCountException(array($this, "data", count($data), 2), self::EXCEPTION_ARRAY_HAS_INVALID_COUNT);
185                                 }
186
187                                 // Add this to the readData string
188                                 $readData .= $data[0];
189                         } else {
190                                 // Other raw lines than header/data tagged lines and re-add the new-line char
191                                 $readData .= $rawLine."\n";
192                         }
193                 }
194
195                 // Was raw lines read and no header/data?
196                 if ((!empty($readData)) && (count($header) == 0) && (count($data) == 0)) {
197                         // Return raw lines back
198                         return $readData;
199                 }
200
201                 // Was a header found?
202                 if (count($header) != 4) {
203                         // Throw an exception
204                         throw new InvalidArrayCountException(array($this, "header", count($header), 4), self::EXCEPTION_ARRAY_HAS_INVALID_COUNT);
205                 }
206
207                 // Decode all from Base64
208                 $readData = @base64_decode($readData);
209
210                 // Does the size match?
211                 if (strlen($readData) != $header[2]) {
212                         // Size did not match
213                         throw new InvalidDataLengthException(array($this, strlen($readData), $header[2]), self::EXCEPTION_UNEXPECTED_STRING_SIZE);
214                 }
215
216                 // Validate the decoded data with the final MD5 hash
217                 if (md5($readData) != $header[3]) {
218                         // MD5 hash did not match!
219                         throw new InvalidMD5ChecksumException(array($this, md5($readData), $header[3]), self::EXCEPTION_MD5_CHECKSUMS_MISMATCH);
220                 }
221
222                 // Return all in an array
223                 return array(
224                         'header' => $header,
225                         'data'   => $readData
226                 );
227         }
228 }
229
230 // [EOF]
231 ?>