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\FileIoException;
9 use Org\Mxchange\CoreFramework\Filesystem\FileReadProtectedException;
10 use Org\Mxchange\CoreFramework\Filesystem\FileWriteProtectedException;
11 use Org\Mxchange\CoreFramework\Filesystem\PathWriteProtectedException;
12 use Org\Mxchange\CoreFramework\Generic\FrameworkInterface;
13 use Org\Mxchange\CoreFramework\Generic\NullPointerException;
14 use Org\Mxchange\CoreFramework\Generic\UnsupportedOperationException;
15 use Org\Mxchange\CoreFramework\Object\BaseFrameworkSystem;
18 use \InvalidArgumentException;
21 use \OutOfBoundsException;
22 use \UnexpectedValueException;
25 * A class for reading files
27 * @author Roland Haeder <webmaster@shipsimu.org>
29 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2023 Core Developer Team
30 * @license GNU GPL 3.0 or any newer version
31 * @link http://www.shipsimu.org
33 * This program is free software: you can redistribute it and/or modify
34 * it under the terms of the GNU General Public License as published by
35 * the Free Software Foundation, either version 3 of the License, or
36 * (at your option) any later version.
38 * This program is distributed in the hope that it will be useful,
39 * but WITHOUT ANY WARRANTY; without even the implied warranty of
40 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
41 * GNU General Public License for more details.
43 * You should have received a copy of the GNU General Public License
44 * along with this program. If not, see <http://www.gnu.org/licenses/>.
46 class FrameworkFileInputOutputPointer extends BaseFileIo implements InputOutputPointer {
48 * Protected constructor
52 private function __construct () {
53 // Call parent constructor
54 parent::__construct(__CLASS__);
58 * Create a file pointer based on the given file. The file will also
61 * @param $fileInstance An instance of a SplFileInfo class
63 * @throws FileReadProtectedException If PHP cannot read an existing file
64 * @throws FileWriteProtectedException If PHP cannot write an existing file
65 * @throws PathWriteProtectedException If PHP cannot write to an existing path
66 * @throws FileIoException If fopen() returns not a file resource
68 public static final function createFrameworkFileInputOutputPointer (SplFileInfo $fileInstance) {
69 // Some pre-sanity checks...
70 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('FILE-INPUT-OUTPUT-POINTER: fileInstance[%s]=%s - CALLED!', get_class($fileInstance), $fileInstance));
71 if (!FrameworkBootstrap::isReachableFilePath($fileInstance)) {
72 // File exists but cannot be read
73 throw new FileIoException($fileInstance, self::EXCEPTION_FILE_NOT_REACHABLE);
74 } elseif ((!FrameworkBootstrap::isReadableFile($fileInstance)) && (file_exists($fileInstance))) {
75 // File exists but cannot be read
76 throw new FileReadProtectedException($fileInstance, self::EXCEPTION_FILE_CANNOT_BE_READ);
77 } elseif (!is_writable($fileInstance->getPath())) {
78 // Path is not writable
79 throw new PathWriteProtectedException($fileInstance, self::EXCEPTION_PATH_CANNOT_BE_WRITTEN);
80 } elseif (($fileInstance->isFile()) && (!$fileInstance->isWritable())) {
81 // File exists but cannot be written
82 throw new FileWriteProtectedException($fileInstance, self::EXCEPTION_FILE_CANNOT_BE_WRITTEN);
85 // Try to open a handler
86 $fileObject = $fileInstance->openFile('c+b');
89 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('FILE-INPUT-OUTPUT-POINTER: fileObject[]=%s', gettype($fileObject)));
90 if (!($fileObject instanceof SplFileObject)) {
91 // Something bad happend
92 throw new FileIoException($fileInstance->getPathname(), self::EXCEPTION_FILE_POINTER_INVALID);
95 // Create new instance
96 $pointerInstance = new FrameworkFileInputOutputPointer();
98 // Set file object and file name
99 $pointerInstance->setFileObject($fileObject);
101 // Return the instance
102 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('FILE-INPUT-OUTPUT-POINTER: pointerInstance=%s - EXIT!', $pointerInstance->__toString()));
103 return $pointerInstance;
107 * Read 1024 bytes data from a file pointer
109 * @return mixed The result of fread()
111 public function readFromFile () {
112 // Read data from the file pointer and return it
113 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('FILE-INPUT-OUTPUT-POINTER: CALLED!');
114 $data = $this->read(1024);
117 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('FILE-INPUT-OUTPUT-POINTER: data[%s]=%d - EXIT!', gettype($data), $data));
122 * Write data to a file pointer
124 * @param $dataStream The data stream we shall write to the file
125 * @return mixed Number of writes bytes or false on error
127 public function writeToFile (string $dataStream) {
128 // Validate parameter
129 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('FILE-INPUT-OUTPUT-POINTER: dataStream(%d)=%s - CALLED!', strlen($dataStream), $dataStream));
130 if (empty($dataStream)) {
132 throw new InvalidArgumentException('Parameter "dataStream" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
136 $length = strlen($dataStream);
138 // Write data to the file pointer and return written bytes
139 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('FILE-INPUT-OUTPUT-POINTER: Invoking this->fileObject->fwrite(%s,%d) ...', $dataStream, $length));
140 $status = $this->getFileObject()->fwrite($dataStream, $length);
143 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('FILE-INPUT-OUTPUT-POINTER: status[%s]=%d - EXIT!', gettype($status), $status));
148 * Writes at given position by seeking to it.
150 * @param $seekPosition Seek position in file
151 * @param $dataStream Data to be written
152 * @return mixed Number of writes bytes or false on error
153 * @throws OutOfBoundsException If the position is not seekable
154 * @throws InvalidArgumentException If a parameter is not valid
156 public function writeAtPosition (int $seekPosition, string $dataStream) {
157 // Validate parameter
158 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('FILE-INPUT-OUTPUT-POINTER: seekPosition=%d,dataStream(%d)=%s - CALLED!', $seekPosition, strlen($dataStream), $dataStream));
159 if ($seekPosition < 0) {
160 // Invalid seek position
161 throw new OutOfBoundsException(sprintf('seekPosition=%d is not valid.', $seekPosition));
162 } elseif (empty($dataStream)) {
164 throw new InvalidArgumentException('Parameter "dataStream" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
165 } elseif (($this->getFileSize() > 0 || $seekPosition > 0) && $this->seek($seekPosition) === -1) {
167 throw new InvalidArgumentException(sprintf('Could not seek to seekPosition=%d', $seekPosition));
170 // Then write the data at that position
171 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('FILE-INPUT-OUTPUT-POINTER: Invoking this->writeToFile(%s) ...', $dataStream));
172 $status = $this->writeToFile($dataStream);
175 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('FILE-INPUT-OUTPUT-POINTER: status[%s]=%d - EXIT!', gettype($status), $status));
180 * Rewinds to the beginning of the file
184 public function rewind () {
185 /// Rewind the pointer
186 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('FILE-INPUT-OUTPUT-POINTER: CALLED!');
187 $this->getFileObject()->rewind();
190 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('FILE-INPUT-OUTPUT-POINTER: EXIT!');
194 * Seeks to given position
196 * @param $seekPosition Seek position in file
197 * @param $whence "Seek mode" (see http://de.php.net/fseek)
198 * @return $status Status of this operation
199 * @throws OutOfBoundsException If the position is not seekable
201 public function seek (int $seekPosition, int $whence = SEEK_SET) {
202 // Validate parameter
203 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('FILE-INPUT-OUTPUT-POINTER: seekPosition=%d,whence=%d - CALLED!', $seekPosition, $whence));
204 if ($seekPosition < 0) {
205 // Invalid seek position
206 throw new OutOfBoundsException(sprintf('seekPosition=%d is not valid.', $seekPosition));
207 } elseif ($whence < 0) {
208 // Invalid seek position
209 throw new OutOfBoundsException(sprintf('whence=%d is not valid.', $whence));
212 // Move the file pointer
213 $status = $this->getFileObject()->fseek($seekPosition, $whence);
216 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('FILE-INPUT-OUTPUT-POINTER: status[%s]=%d - EXIT!', gettype($status), $status));
221 * Reads a line, maximum 4096 Bytes from current file pointer
223 * @return $data Read data from file
225 public function readLine () {
227 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('FILE-INPUT-OUTPUT-POINTER: CALLED!');
228 $data = $this->read();
231 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('FILE-INPUT-OUTPUT-POINTER: data[%s]=%s - EXIT!', gettype($data), $data));
236 * Reads given amount of bytes from file.
238 * @param $bytes Amount of bytes to read
239 * @return $data Data read from file
240 * @throws OutOfBoundsException If the position is not seekable
242 public function read (int $bytes = 0) {
243 // Validatre parameter
244 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('FILE-INPUT-OUTPUT-POINTER: bytes=%d - CALLED!', $bytes));
246 // Bytes cannot be lesser than zero
247 throw new OutOfBoundsException(sprintf('bytes=%d is not valid', $bytes));
250 // Is $bytes bigger than zero?
252 // Try to read given characters
253 $data = $this->getFileObject()->fread($bytes);
255 // Try to read whole line
256 $data = $this->getFileObject()->fgets();
260 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('FILE-INPUT-OUTPUT-POINTER: data[%s]=%s - EXIT!', gettype($data), $data));
265 * Analyzes entries in index file. This will count all found (and valid)
266 * entries, mark invalid as damaged and count gaps ("fragmentation"). If
267 * only gaps are found, the file is considered as "virgin" (no entries).
270 * @throws UnsupportedOperationException If this method is called
272 public function analyzeFileStructure () {
273 throw new UnsupportedOperationException([$this, __FUNCTION__], FrameworkInterface::EXCEPTION_UNSPPORTED_OPERATION);
277 * Advances to next "block" of bytes
280 * @throws UnsupportedOperationException If this method is called
282 public function next () {
283 throw new UnsupportedOperationException([$this, __FUNCTION__], FrameworkInterface::EXCEPTION_UNSPPORTED_OPERATION);
287 * Checks wether the current entry is valid (not at the end of the file).
288 * This method will return true if an emptied (nulled) entry has been found.
290 * @return $isValid Whether the next entry is valid
291 * @throws UnsupportedOperationException If this method is called
293 public function valid () {
294 throw new UnsupportedOperationException([$this, __FUNCTION__], FrameworkInterface::EXCEPTION_UNSPPORTED_OPERATION);
298 * Gets current seek position ("key").
300 * @return $key Current key in iteration
301 * @throws UnsupportedOperationException If this method is called
303 public function key () {
304 throw new UnsupportedOperationException([$this, __FUNCTION__], FrameworkInterface::EXCEPTION_UNSPPORTED_OPERATION);
308 * "Getter" for file size
310 * @return $fileSize Size of currently loaded file
311 * @throws UnexpectedValueException If $fileData does not contain "size"
313 public function getFileSize () {
315 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('FILE-INPUT-OUTPUT-POINTER: CALLED!');
316 $fileData = $this->getFileObject()->fstat();
318 // Make sure the required array key is there
319 if (!isset($fileData['size'])) {
321 throw new UnexpectedValueException(sprintf('fileData=%s has no element "size"', print_r($fileData, TRUE)), FrameworkInterface::EXCEPTION_UNEXPECTED_VALUE);
325 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('FILE-INPUT-OUTPUT-POINTER: fileData[size]=%d - EXIT!', $fileData['size']));
326 return $fileData['size'];