dcd34e4201fc4e26ebeff6bc4d47e484b76e4af0
[core.git] / framework / main / classes / file_directories / io_stream / class_FileIoStream.php
1 <?php
2 // Own namespace
3 namespace Org\Mxchange\CoreFramework\Stream\Filesystem;
4
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\EntryPoint\ApplicationEntryPoint;
7 use Org\Mxchange\CoreFramework\Factory\ObjectFactory;
8 use Org\Mxchange\CoreFramework\Filesystem\FileNotFoundException;
9 use Org\Mxchange\CoreFramework\Generic\UnsupportedOperationException;
10 use Org\Mxchange\CoreFramework\Object\BaseFrameworkSystem;
11 use Org\Mxchange\CoreFramework\Stream\Filesystem\FileInputStreamer;
12 use Org\Mxchange\CoreFramework\Stream\Filesystem\FileOutputStreamer;
13
14 // Import SPL stuff
15 use \SplFileInfo;
16
17 /**
18  * An universal class for file input/output streams.
19  *
20  * @author              Roland Haeder <webmaster@shipsimu.org>
21  * @version             0.0.0
22 <<<<<<< HEAD:framework/main/classes/file_directories/io_stream/class_FileIoStream.php
23  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Core Developer Team
24 =======
25  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2016 Core Developer Team
26 >>>>>>> Some updates::inc/main/classes/file_directories/io_stream/class_FileIoStream.php
27  * @license             GNU GPL 3.0 or any newer version
28  * @link                http://www.shipsimu.org
29  *
30  * This program is free software: you can redistribute it and/or modify
31  * it under the terms of the GNU General Public License as published by
32  * the Free Software Foundation, either version 3 of the License, or
33  * (at your option) any later version.
34  *
35  * This program is distributed in the hope that it will be useful,
36  * but WITHOUT ANY WARRANTY; without even the implied warranty of
37  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
38  * GNU General Public License for more details.
39  *
40  * You should have received a copy of the GNU General Public License
41  * along with this program. If not, see <http://www.gnu.org/licenses/>.
42  */
43 class FileIoStream extends BaseFrameworkSystem implements FileInputStreamer, FileOutputStreamer {
44         /**
45          * File header indicator
46          */
47         const FILE_IO_FILE_HEADER_ID = '@head';
48
49         /**
50          * Data block indicator
51          */
52         const FILE_IO_DATA_BLOCK_ID = '@data';
53
54         /**
55          * Separator #1
56          */
57         const FILE_IO_CHUNKER = ':';
58
59         /**
60          * Separator #2
61          */
62         const FILE_IO_SEPARATOR = '^';
63
64         /**
65          * Protected constructor
66          */
67         protected function __construct () {
68                 // Call parent constructor
69                 parent::__construct(__CLASS__);
70         }
71
72         /**
73          * Create a file IO stream. This is a class for performing all actions
74          * on files like creating, deleting and loading them.
75          *
76          * @return      $ioInstance     An instance of a FileIoStream class
77          */
78         public static final function createFileIoStream () {
79                 // Create new instance
80                 $ioInstance = new FileIoStream();
81
82                 // Return the instance
83                 return $ioInstance;
84         }
85
86         /**
87          * Saves data to a given local file and create missing directory structures
88          *
89          * @param       $fileInfoInstance       An instance of a SplFileInfo class
90          * @param       $dataArray      The data we shall store to the file
91          * @return      void
92          * @see         FileOutputStreamer
93          * @todo        This method needs heavy rewrite
94          */
95         public final function saveFile (SplFileInfo $fileInfoInstance, array $dataArray) {
96                 // Try it five times
97                 $dirName = '';
98                 $fileInstance = NULL;
99
100                 for ($idx = 0; $idx < 5; $idx++) {
101                         // Get a file output pointer
102                         try {
103                                 $fileInstance = ObjectFactory::createObjectByConfiguredName('file_raw_output_class', array($fileInfoInstance, 'wb'));
104                         } catch (FileNotFoundException $e) {
105                                 // Bail out
106                                 ApplicationEntryPoint::exitApplication('The application has made a fatal error. Exception: ' . $e->__toString() . ' with message: ' . $e->getMessage());
107                         }
108                 } // END - for
109
110                 // Write a header information for validation purposes
111                 $fileInstance->writeToFile(sprintf('%s%s%s%s%s%s%s%s%s' . PHP_EOL,
112                         self::FILE_IO_FILE_HEADER_ID,
113                         self::FILE_IO_SEPARATOR,
114                         $dataArray[0],
115                         self::FILE_IO_CHUNKER,
116                         time(),
117                         self::FILE_IO_CHUNKER,
118                         strlen($dataArray[1]),
119                         self::FILE_IO_CHUNKER,
120                         md5($dataArray[1])
121                 ));
122
123                 // Encode the (maybe) binary stream with Base64
124                 $b64Stream = base64_encode($dataArray[1]);
125
126                 // write the data line by line
127                 $line = str_repeat(' ', 50); $idx = 0;
128                 while (strlen($line) == 50) {
129                         // Get 50 chars or less
130                         $line = substr($b64Stream, $idx, 50);
131
132                         // Save it to the stream
133                         $fileInstance->writeToFile(sprintf('%s%s%s%s%s' . PHP_EOL,
134                                 self::FILE_IO_DATA_BLOCK_ID,
135                                 self::FILE_IO_SEPARATOR,
136                                 $line,
137                                 self::FILE_IO_CHUNKER,
138                                 md5($line)
139                         ));
140
141                         // Advance to the next 50-chars block
142                         $idx += 50;
143                 } // END - while
144
145                 // Close the file
146                 unset($fileInstance);
147         }
148
149         /**
150          * Reads from a local file
151          *
152          * @param       $infoInstance   An instance of a SplFileInfo class
153          * @return      $array  An array with the element 'header' and 'data'
154          * @see         FileInputStreamer
155          */
156         public final function loadFileContents (SplFileInfo $infoInstance) {
157                 // Initialize some variables and arrays
158                 $inputBuffer = '';
159                 $lastBuffer = '';
160                 $header = array();
161                 $data = array();
162                 $readData = ''; // This will contain our read data
163
164                 // Get a file input handler
165                 $fileInstance = ObjectFactory::createObjectByConfiguredName('file_raw_input_class', array($infoInstance));
166
167                 // Read all it's contents (we very and transparently decompress it below)
168                 while ($readRawLine = $fileInstance->readFromFile()) {
169                         // Add the read line to the buffer
170                         $inputBuffer .= $readRawLine;
171
172                         // Break infinite loop maybe caused by the input handler
173                         if ($lastBuffer == $inputBuffer) {
174                                 break;
175                         } // END - if
176
177                         // Remember last read line for avoiding possible infinite loops
178                         $lastBuffer = $inputBuffer;
179                 } // END - while
180
181                 // Close directory handle
182                 unset($fileInstance);
183
184                 // Convert it into an array
185                 $inputBuffer = explode(chr(10), $inputBuffer);
186
187                 // Now process the read lines and verify it's content
188                 foreach ($inputBuffer as $rawLine) {
189                         // Trim it a little but not the leading spaces/tab-stops
190                         $rawLine = rtrim($rawLine);
191
192                         // Analyze this line
193                         if (substr($rawLine, 0, 5) == self::FILE_IO_FILE_HEADER_ID) {
194                                 // Header found, so let's extract it
195                                 $header = explode(self::FILE_IO_SEPARATOR, $rawLine);
196                                 $header = trim($header[1]);
197
198                                 // Now we must convert it again into an array
199                                 $header = explode(self::FILE_IO_CHUNKER, $header);
200
201                                 // Is the header (maybe) valid?
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                                 } // END - if
206                         } elseif (substr($rawLine, 0, 5) == self::FILE_IO_DATA_BLOCK_ID) {
207                                 // Is a data line!
208                                 $data = explode(self::FILE_IO_SEPARATOR, $rawLine);
209                                 $data = $data[1];
210
211                                 // First element is the data, second the MD5 checksum
212                                 $data = explode(self::FILE_IO_CHUNKER, $data);
213
214                                 // Validate the read line
215                                 if (count($data) == 2) {
216                                         if (md5($data[0]) != $data[1]) {
217                                                 // MD5 hash did not match!
218                                                 throw new InvalidMD5ChecksumException(array($this, md5($data[0]), $data[1]), self::EXCEPTION_MD5_CHECKSUMS_MISMATCH);
219                                         } // END - if
220                                 } else {
221                                         // Invalid count!
222                                         throw new InvalidArrayCountException(array($this, 'data', count($data), 2), self::EXCEPTION_ARRAY_HAS_INVALID_COUNT);
223                                 }
224
225                                 // Add this to the readData string
226                                 $readData .= $data[0];
227                         } else {
228                                 // Other raw lines than header/data tagged lines and re-add the new-line char
229                                 $readData .= $rawLine . PHP_EOL;
230                         }
231                 } // END - foreach
232
233                 // Was raw lines read and no header/data?
234                 if ((!empty($readData)) && (count($header) == 0) && (count($data) == 0)) {
235                         // Return raw lines back
236                         return $readData;
237                 } // END - if
238
239                 // Was a header found?
240                 if (count($header) != 4) {
241                         // Throw an exception
242                         throw new InvalidArrayCountException(array($this, 'header', count($header), 4), self::EXCEPTION_ARRAY_HAS_INVALID_COUNT);
243                 } // END - if
244
245                 // Decode all from Base64
246                 $readData = @base64_decode($readData);
247
248                 // Does the size match?
249                 if (strlen($readData) != $header[2]) {
250                         // Size did not match
251                         throw new InvalidDataLengthException(array($this, strlen($readData), $header[2]), self::EXCEPTION_UNEXPECTED_STRING_SIZE);
252                 } // END - if
253
254                 // Validate the decoded data with the final MD5 hash
255                 if (md5($readData) != $header[3]) {
256                         // MD5 hash did not match!
257                         throw new InvalidMD5ChecksumException(array($this, md5($readData), $header[3]), self::EXCEPTION_MD5_CHECKSUMS_MISMATCH);
258                 } // END - if
259
260                 // Return all in an array
261                 return array(
262                         'header' => $header,
263                         'data'   => $readData
264                 );
265         }
266
267         /**
268          * Streams the data and maybe does something to it
269          *
270          * @param       $data   The data (string mostly) to "stream"
271          * @return      $data   The data (string mostly) to "stream"
272          * @throws      UnsupportedOperationException   If this method is called
273          */
274         public function streamData ($data) {
275                 self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('Unhandled ' . strlen($data) . ' bytes in this stream.');
276                 throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION);
277         }
278
279         /**
280          * Determines seek position
281          *
282          * @return      $seekPosition   Current seek position
283          * @todo        0% done
284          */
285         public function determineSeekPosition () {
286                 $this->partialStub();
287         }
288
289         /**
290          * Seek to given offset (default) or other possibilities as fseek() gives.
291          *
292          * @param       $offset         Offset to seek to (or used as "base" for other seeks)
293          * @param       $whence         Added to offset (default: only use offset to seek to)
294          * @return      $status         Status of file seek: 0 = success, -1 = failed
295          */
296         public function seek ($offset, $whence = SEEK_SET) {
297                 $this->partialStub('offset=' . $offset . ',whence=' . $whence);
298         }
299
300         /**
301          * Size of file stack
302          *
303          * @return      $size   Size (in bytes) of file
304          */
305         public function size () {
306                 $this->partialStub();
307         }
308
309 }