3 namespace Org\Mxchange\CoreFramework\Filesystem\Pointer;
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Bootstrap\FrameworkBootstrap;
7 use Org\Mxchange\CoreFramework\Filesystem\BaseFileIo;
8 use Org\Mxchange\CoreFramework\Filesystem\FileReadProtectedException;
9 use Org\Mxchange\CoreFramework\Filesystem\FileWriteProtectedException;
10 use Org\Mxchange\CoreFramework\Filesystem\PathWriteProtectedException;
11 use Org\Mxchange\CoreFramework\Generic\NullPointerException;
12 use Org\Mxchange\CoreFramework\Generic\UnsupportedOperationException;
13 use Org\Mxchange\CoreFramework\Object\BaseFrameworkSystem;
16 use \InvalidArgumentException;
18 use \OutOfBoundsException;
19 use \UnexpectedValueException;
22 * A class for reading files
24 * @author Roland Haeder <webmaster@shipsimu.org>
26 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2020 Core Developer Team
27 * @license GNU GPL 3.0 or any newer version
28 * @link http://www.shipsimu.org
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.
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.
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/>.
43 class FrameworkFileInputOutputPointer extends BaseFileIo implements InputOutputPointer {
45 * Protected constructor
49 protected function __construct () {
50 // Call parent constructor
51 parent::__construct(__CLASS__);
55 * Create a file pointer based on the given file. The file will also
58 * @param $fileInstance An instance of a SplFileInfo class
60 * @throws FileReadProtectedException If PHP cannot read an existing file
61 * @throws FileWriteProtectedException If PHP cannot write an existing file
62 * @throws PathWriteProtectedException If PHP cannot write to an existing path
63 * @throws FileIoException If fopen() returns not a file resource
65 public static final function createFrameworkFileInputOutputPointer (SplFileInfo $fileInstance) {
66 // Some pre-sanity checks...
67 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-INPUT-OUTPUT-POINTER: fileInstance[%s]=%s - CALLED!', get_class($fileInstance), $fileInstance));
68 if (!FrameworkBootstrap::isReachableFilePath($fileInstance)) {
69 // File exists but cannot be read
70 throw new FileIoException($fileInstance, self::EXCEPTION_FILE_NOT_REACHABLE);
71 } elseif ((!FrameworkBootstrap::isReadableFile($fileInstance)) && (file_exists($fileInstance))) {
72 // File exists but cannot be read
73 throw new FileReadProtectedException($fileInstance, self::EXCEPTION_FILE_CANNOT_BE_READ);
74 } elseif (!is_writable($fileInstance->getPath())) {
75 // Path is not writable
76 throw new PathWriteProtectedException($fileInstance, self::EXCEPTION_PATH_CANNOT_BE_WRITTEN);
77 } elseif (($fileInstance->isFile()) && (!$fileInstance->isWritable())) {
78 // File exists but cannot be written
79 throw new FileWriteProtectedException($fileInstance, self::EXCEPTION_FILE_CANNOT_BE_WRITTEN);
82 // Try to open a handler
83 $fileObject = $fileInstance->openFile('c+b');
86 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-INPUT-OUTPUT-POINTER: fileObject[]=%s', gettype($fileObject)));
87 if ((is_null($fileObject)) || ($fileObject === false)) {
88 // Something bad happend
89 throw new FileIoException($fileInstance->getPathname(), self::EXCEPTION_FILE_POINTER_INVALID);
92 // Create new instance
93 $pointerInstance = new FrameworkFileInputOutputPointer();
95 // Set file object and file name
96 $pointerInstance->setFileObject($fileObject);
98 // Return the instance
99 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-INPUT-OUTPUT-POINTER: pointerInstance=%s - EXIT!', $pointerInstance->__toString()));
100 return $pointerInstance;
104 * Read 1024 bytes data from a file pointer
106 * @return mixed The result of fread()
108 public function readFromFile () {
109 // Read data from the file pointer and return it
110 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-INPUT-OUTPUT-POINTER: CALLED!');
111 $data = $this->read(1024);
114 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-INPUT-OUTPUT-POINTER: data[%s]=%d - EXIT!', gettype($data), $data));
119 * Write data to a file pointer
121 * @param $dataStream The data stream we shall write to the file
122 * @return mixed Number of writes bytes or false on error
124 public function writeToFile (string $dataStream) {
125 // Validate parameter
126 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-INPUT-OUTPUT-POINTER: dataStream(%d)=%s - CALLED!', strlen($dataStream), $dataStream));
127 if (empty($dataStream)) {
129 throw new InvalidArgumentException('Parameter "dataStream" is empty');
133 $length = strlen($dataStream);
135 // Write data to the file pointer and return written bytes
136 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-INPUT-OUTPUT-POINTER: Calling this->fileObject->fwrite(%s,%d) ...', $dataStream, $length));
137 $status = $this->getFileObject()->fwrite($dataStream, $length);
140 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-INPUT-OUTPUT-POINTER: status[%s]=%d - EXIT!', gettype($status), $status));
145 * Writes at given position by seeking to it.
147 * @param $seekPosition Seek position in file
148 * @param $dataStream Data to be written
149 * @return mixed Number of writes bytes or false on error
150 * @throws OutOfBoundsException If the position is not seekable
151 * @throws InvalidArgumentException If a parameter is not valid
153 public function writeAtPosition (int $seekPosition, string $dataStream) {
154 // Validate parameter
155 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-INPUT-OUTPUT-POINTER: seekPosition=%d,dataStream(%d)=%s - CALLED!', $seekPosition, strlen($dataStream), $dataStream));
156 if ($seekPosition < 0) {
157 // Invalid seek position
158 throw new OutOfBoundsException(sprintf('seekPosition=%d is not valid.', $seekPosition));
159 } elseif (empty($dataStream)) {
161 throw new InvalidArgumentException('Parameter "dataStream" is empty');
162 } elseif (($this->getFileSize() > 0 || $seekPosition > 0) && $this->seek($seekPosition) === -1) {
164 throw new InvalidArgumentException(sprintf('Could not seek to seekPosition=%d', $seekPosition));
167 // Then write the data at that position
168 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-INPUT-OUTPUT-POINTER: Calling this->writeToFile(%s) ...', $dataStream));
169 $status = $this->writeToFile($dataStream);
172 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-INPUT-OUTPUT-POINTER: status[%s]=%d - EXIT!', gettype($status), $status));
177 * Rewinds to the beginning of the file
181 public function rewind () {
182 /// Rewind the pointer
183 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-INPUT-OUTPUT-POINTER: CALLED!');
184 $this->getFileObject()->rewind();
187 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-INPUT-OUTPUT-POINTER: EXIT!');
191 * Seeks to given position
193 * @param $seekPosition Seek position in file
194 * @param $whence "Seek mode" (see http://de.php.net/fseek)
195 * @return $status Status of this operation
196 * @throws OutOfBoundsException If the position is not seekable
198 public function seek (int $seekPosition, int $whence = SEEK_SET) {
199 // Validate parameter
200 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-INPUT-OUTPUT-POINTER: seekPosition=%d,whence=%d - CALLED!', $seekPosition, $whence));
201 if ($seekPosition < 0) {
202 // Invalid seek position
203 throw new OutOfBoundsException(sprintf('seekPosition=%d is not valid.', $seekPosition));
206 // Move the file pointer
207 $status = $this->getFileObject()->fseek($seekPosition, $whence);
210 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-INPUT-OUTPUT-POINTER: status[%s]=%d - EXIT!', gettype($status), $status));
215 * Reads a line, maximum 4096 Bytes from current file pointer
217 * @return $data Read data from file
219 public function readLine () {
221 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-INPUT-OUTPUT-POINTER: CALLED!');
222 $data = $this->read();
225 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-INPUT-OUTPUT-POINTER: data[%s]=%s - EXIT!', gettype($data), $data));
230 * Reads given amount of bytes from file.
232 * @param $bytes Amount of bytes to read
233 * @return $data Data read from file
234 * @throws OutOfBoundsException If the position is not seekable
236 public function read (int $bytes = 0) {
237 // Validatre parameter
238 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-INPUT-OUTPUT-POINTER: bytes=%d - CALLED!', $bytes));
240 // Bytes cannot be lesser than zero
241 throw new OutOfBoundsException(sprintf('bytes=%d is not valid', $bytes));
244 // Is $bytes bigger than zero?
246 // Try to read given characters
247 $data = $this->getFileObject()->fread($bytes);
249 // Try to read whole line
250 $data = $this->getFileObject()->fgets();
254 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-INPUT-OUTPUT-POINTER: data[%s]=%s - EXIT!', gettype($data), $data));
259 * Analyzes entries in index file. This will count all found (and valid)
260 * entries, mark invalid as damaged and count gaps ("fragmentation"). If
261 * only gaps are found, the file is considered as "virgin" (no entries).
264 * @throws UnsupportedOperationException If this method is called
266 public function analyzeFileStructure () {
267 throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION);
271 * Advances to next "block" of bytes
274 * @throws UnsupportedOperationException If this method is called
276 public function next () {
277 throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION);
281 * Checks wether the current entry is valid (not at the end of the file).
282 * This method will return true if an emptied (nulled) entry has been found.
284 * @return $isValid Whether the next entry is valid
285 * @throws UnsupportedOperationException If this method is called
287 public function valid () {
288 throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION);
292 * Gets current seek position ("key").
294 * @return $key Current key in iteration
295 * @throws UnsupportedOperationException If this method is called
297 public function key () {
298 throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION);
302 * "Getter" for file size
304 * @return $fileSize Size of currently loaded file
305 * @throws UnexpectedValueException If $fileData does not contain "size"
307 public function getFileSize () {
309 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('FILE-INPUT-OUTPUT-POINTER: CALLED!');
310 $fileData = $this->getFileObject()->fstat();
312 // Make sure the required array key is there
313 if (!isset($fileData['size'])) {
315 throw new UnexpectedValueException(sprintf('fileData=%s has no element "size"', print_r($fileData, TRUE)));
319 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('FILE-INPUT-OUTPUT-POINTER: fileData[size]=%d - EXIT!', $fileData['size']));
320 return $fileData['size'];