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