Rewritten a lot double-quotes to single-requotes, removed deprecated exception, some...
[core.git] / 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.0
7  * @copyright   Copyright (c) 2007 - 2009 Roland Haeder, this is free software
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 FileIoStream extends BaseFrameworkSystem implements FileInputStreamer, FileOutputStreamer {
25         /**
26          * File header indicator
27          */
28         private $fileHeader = '@head';
29
30         /**
31          * Data block indicator
32          */
33         private $dataBlock = '@data';
34
35         /**
36          * Seperator #1
37          */
38         private $chunker = ':';
39
40         /**
41          * Seperator #2
42          */
43         private $seperator = '^';
44
45         /**
46          * Protected constructor
47          */
48         protected function __construct () {
49                 // Call parent constructor
50                 parent::__construct(__CLASS__);
51
52                 // Clean-up a little
53                 $this->removeNumberFormaters();
54                 $this->removeSystemArray();
55         }
56
57         /**
58          * Create a file IO stream. This is a class for performing all actions
59          * on files like creating, deleting and loading them.
60          *
61          * @return      $ioInstance     An instance of FileIoStream
62          */
63         public final static function createFileIoStream () {
64                 // Create new instance
65                 $ioInstance = new FileIoStream();
66
67                 // Return the instance
68                 return $ioInstance;
69         }
70
71         /**
72          * Saves data to a given local file and create missing directory structures
73          *
74          * @param       $fileName       The file name for the to be saved file
75          * @param       $dataArray      The data we shall store to the file
76          * @return      void
77          * @see         FileOutputStreamer
78          * @todo        This method needs heavy rewrite
79          */
80         public final function saveFile ($fileName, $dataArray) {
81                 // Try it five times
82                 $dirName = ''; $fileInstance = null;
83                 for ($idx = 0; $idx < 5; $idx++) {
84                         // Get a file output pointer
85                         try {
86                                 $fileInstance = FrameworkFileOutputPointer::createFrameworkFileOutputPointer($fileName, 'w');
87                         } catch (FilePointerNotOpenedException $e) {
88                                 // Create missing directory
89                                 $dirName = dirname($fileName);
90                                 for ($idx2 = 0; $idx2 < (2 - $idx); $idx2++) {
91                                         $dirName = dirname($dirName);
92                                 } // END - for
93
94                                 // Try to create it
95                                 @mkdir($dirName);
96                         }
97                 } // END - for
98
99                 // Write a header information for validation purposes
100                 $fileInstance->writeToFile(sprintf("%s%s%s%s%s%s%s%s%s\n",
101                         $this->fileHeader,
102                         $this->seperator,
103                         $dataArray[0],
104                         $this->chunker,
105                         time(),
106                         $this->chunker,
107                         strlen($dataArray[1]),
108                         $this->chunker,
109                         md5($dataArray[1])
110                 ));
111
112                 // Encode the (maybe) binary stream with Base64
113                 $b64Stream = base64_encode($dataArray[1]);
114
115                 // write the data line by line
116                 $line = str_repeat(' ', 50); $idx = 0;
117                 while (strlen($line) == 50) {
118                         // Get 50 chars or less
119                         $line = substr($b64Stream, $idx, 50);
120
121                         // Save it to the stream
122                         $fileInstance->writeToFile(sprintf("%s%s%s%s%s\n",
123                                 $this->dataBlock,
124                                 $this->seperator,
125                                 $line,
126                                 $this->chunker,
127                                 md5($line)
128                         ));
129
130                         // Advance to the next 50-chars block
131                         $idx += 50;
132                 } // END - while
133
134                 // Close the file
135                 $fileInstance->closeFile();
136         }
137
138         /**
139          * Reads from a local file
140          *
141          * @param               $fqfn   The full-qualified file-name which we shall load
142          * @return      $array  An array with the element 'header' and 'data'
143          * @see         FileInputStreamer
144          */
145         public final function loadFileContents ($fqfn) {
146                 // Initialize some variables and arrays
147                 $inputBuffer = '';
148                 $lastBuffer = '';
149                 $header = array();
150                 $data = array();
151                 $readData = ''; // This will contain our read data
152
153                 // Get a file input handler
154                 $fileInstance = FrameworkFileInputPointer::createFrameworkFileInputPointer($fqfn);
155
156                 // Read all it's contents (we very and transparently decompress it below)
157                 while ($readRawLine = $fileInstance->readFromFile()) {
158                         // Add the read line to the buffer
159                         $inputBuffer .= $readRawLine;
160
161                         // Break infinite loop maybe caused by the input handler
162                         if ($lastBuffer == $inputBuffer) break;
163
164                         // Remember last read line for avoiding possible infinite loops
165                         $lastBuffer = $inputBuffer;
166                 }
167
168                 // Close directory handle
169                 $fileInstance->closeFile();
170
171                 // Convert it into an array
172                 $inputBuffer = explode("\n", $inputBuffer);
173
174                 // Now process the read lines and verify it's content
175                 foreach ($inputBuffer as $rawLine) {
176                         // Trim it a little but not the leading spaces/tab-stops
177                         $rawLine = rtrim($rawLine);
178
179                         // Analyze this line
180                         if (substr($rawLine, 0, 5) == $this->fileHeader) {
181                                 // Header found, so let's extract it
182                                 $header = explode($this->seperator, $rawLine);
183                                 $header = trim($header[1]);
184
185                                 // Now we must convert it again into an array
186                                 $header = explode($this->chunker, $header);
187
188                                 // Is the header (maybe) valid?
189                                 if (count($header) != 4) {
190                                         // Throw an exception
191                                         throw new InvalidArrayCountException(array($this, 'header', count($header), 4), self::EXCEPTION_ARRAY_HAS_INVALID_COUNT);
192                                 }
193                         } elseif (substr($rawLine, 0, 5) == $this->dataBlock) {
194                                 // Is a data line!
195                                 $data = explode($this->seperator, $rawLine);
196                                 $data = $data[1];
197
198                                 // First element is the data, second the MD5 checksum
199                                 $data = explode($this->chunker, $data);
200
201                                 // Validate the read line
202                                 if (count($data) == 2) {
203                                         if (md5($data[0]) != $data[1]) {
204                                                 // MD5 hash did not match!
205                                                 throw new InvalidMD5ChecksumException(array($this, md5($data[0]), $data[1]), self::EXCEPTION_MD5_CHECKSUMS_MISMATCH);
206                                         }
207                                 } else {
208                                         // Invalid count!
209                                         throw new InvalidArrayCountException(array($this, "data", count($data), 2), self::EXCEPTION_ARRAY_HAS_INVALID_COUNT);
210                                 }
211
212                                 // Add this to the readData string
213                                 $readData .= $data[0];
214                         } else {
215                                 // Other raw lines than header/data tagged lines and re-add the new-line char
216                                 $readData .= $rawLine . "\n";
217                         }
218                 }
219
220                 // Was raw lines read and no header/data?
221                 if ((!empty($readData)) && (count($header) == 0) && (count($data) == 0)) {
222                         // Return raw lines back
223                         return $readData;
224                 }
225
226                 // Was a header found?
227                 if (count($header) != 4) {
228                         // Throw an exception
229                         throw new InvalidArrayCountException(array($this, 'header', count($header), 4), self::EXCEPTION_ARRAY_HAS_INVALID_COUNT);
230                 }
231
232                 // Decode all from Base64
233                 $readData = @base64_decode($readData);
234
235                 // Does the size match?
236                 if (strlen($readData) != $header[2]) {
237                         // Size did not match
238                         throw new InvalidDataLengthException(array($this, strlen($readData), $header[2]), self::EXCEPTION_UNEXPECTED_STRING_SIZE);
239                 }
240
241                 // Validate the decoded data with the final MD5 hash
242                 if (md5($readData) != $header[3]) {
243                         // MD5 hash did not match!
244                         throw new InvalidMD5ChecksumException(array($this, md5($readData), $header[3]), self::EXCEPTION_MD5_CHECKSUMS_MISMATCH);
245                 }
246
247                 // Return all in an array
248                 return array(
249                         'header' => $header,
250                         'data'   => $readData
251                 );
252         }
253 }
254
255 // [EOF]
256 ?>