diff --git a/CHANGELOG.md b/CHANGELOG.md index 185aa262..c3293bc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ All Notable changes to `Csv` will be documented in this file - `TabulatDataReader::firstOrFailMatching` - `FragmentFinder` to implement [RFC7111](https://www.rfc-editor.org/rfc/rfc7111) - `ResultSet::fromRecords` +- `Stream::setMaxLineLen` +- `Stream::getMaxLineLen` ### Deprecated @@ -26,6 +28,7 @@ It's usage will trigger a `E_USER_DEPRECATED` call. - `ResultSet` constructor now allows the records to be an `array`. - to the internal `Stream` object it will throw a `RuntimeException` if the rewind action fails - if calls to `fseek` fails (returns `-1` ) a new `RuntimeException` will be thrown too. +- `Stream` can iterate and return the full line respecting `SplFielObject` flags. Previously it only returned the CSV records. ### Removed diff --git a/src/Stream.php b/src/Stream.php index a85816b6..69dbd7fc 100644 --- a/src/Stream.php +++ b/src/Stream.php @@ -18,6 +18,7 @@ use SplFileObject; use Stringable; use TypeError; +use ValueError; use function array_keys; use function array_walk_recursive; @@ -67,6 +68,7 @@ final class Stream implements SeekableIterator private string $escape = '\\'; /** @var array> Attached filters. */ private array $filters = []; + private int $maxLength = 0; /** * @param resource $stream stream type resource @@ -295,7 +297,7 @@ public function rewind(): void $this->offset = 0; $this->value = false; - if (0 !== ($this->flags & SplFileObject::READ_AHEAD)) { + if (SplFileObject::READ_AHEAD === ($this->flags & SplFileObject::READ_AHEAD)) { $this->current(); } } @@ -308,7 +310,7 @@ public function rewind(): void public function valid(): bool { return match (true) { - 0 !== ($this->flags & SplFileObject::READ_AHEAD) => false !== $this->current(), + SplFileObject::READ_AHEAD === ($this->flags & SplFileObject::READ_AHEAD) => false !== $this->current(), default => !feof($this->stream), }; } @@ -324,17 +326,63 @@ public function current(): mixed return $this->value; } - $this->value = $this->getCurrentRecord(); + $this->value = match (true) { + SplFileObject::READ_CSV === ($this->flags & SplFileObject::READ_CSV) => $this->getCurrentRecord(), + default => $this->getCurrentLine(), + }; return $this->value; } + public function fgets(): string|false + { + $arg = [$this->stream]; + if (0 < $this->maxLength) { + $arg[] = $this->maxLength; + } + return fgets(...$arg); + } + + /** + * Sets the maximum length of a line to be read. + * + * @see https://www.php.net/manual/en/splfileobject.setmaxlinelen.php + */ + public function setMaxLineLen(int $maxLength): void + { + if (0 > $maxLength) { + throw new ValueError(' Argument #1 ($maxLength) must be greater than or equal to 0'); + } + + $this->maxLength = $maxLength; + } + + /** + * Gets the maximum line length as set by setMaxLineLen. + * + * @see https://www.php.net/manual/en/splfileobject.getmaxlinelen.php + */ + public function getMaxLineLen(): int + { + return $this->maxLength; + } + + /** + * Tells whether the end of file has been reached. + * + * @see https://www.php.net/manual/en/splfileobject.eof.php + */ + public function eof(): bool + { + return feof($this->stream); + } + /** * Retrieves the current line as a CSV Record. */ private function getCurrentRecord(): array|false { - $flag = 0 !== ($this->flags & SplFileObject::SKIP_EMPTY); + $flag = SplFileObject::SKIP_EMPTY === ($this->flags & SplFileObject::SKIP_EMPTY); do { $ret = fgetcsv($this->stream, 0, $this->delimiter, $this->enclosure, $this->escape); } while ($flag && is_array($ret) && null === $ret[0]); @@ -342,6 +390,33 @@ private function getCurrentRecord(): array|false return $ret; } + /** + * Retrieves the current line. + */ + public function getCurrentLine(): string|false + { + $isEmptyLine = SplFileObject::SKIP_EMPTY === ($this->flags & SplFileObject::SKIP_EMPTY); + $dropNewLine = SplFileObject::DROP_NEW_LINE === ($this->flags & SplFileObject::DROP_NEW_LINE); + + $arguments = [$this->stream]; + if (0 < $this->maxLength) { + $arguments[] = $this->maxLength + 1; + } + + do { + $line = fgets(...$arguments); + } while ( + (($isEmptyLine || $dropNewLine) && (false !== $line && '' === rtrim($line, "\r\n"))) + ); + + if (false !== $line && $dropNewLine) { + $line = rtrim($line, "\r\n"); + } + + return $line; + } + + /** * Seeks to specified line. *