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