diff --git a/composer.json b/composer.json
index ececb30..a9ea690 100644
--- a/composer.json
+++ b/composer.json
@@ -7,8 +7,13 @@
"email": "vintsukevich@gmail.com"
}
],
+ "require": {
+ "php": "^8.1",
+ "ext-zlib": "*",
+ "ext-mbstring": "*"
+ },
"require-dev": {
- "phpunit/phpunit": "~4.0"
+ "phpunit/phpunit": "^11.0"
},
"autoload": {
"psr-4": {
@@ -20,4 +25,4 @@
"Vvval\\Binary\\Tests\\": "tests"
}
}
-}
\ No newline at end of file
+}
diff --git a/phpunit.xml b/phpunit.xml
index 94dc2bd..7d079ed 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,18 +1,12 @@
+ stopOnError="false">
./tests/
-
\ No newline at end of file
+
diff --git a/src/Binary.php b/src/Binary.php
new file mode 100644
index 0000000..70cecda
--- /dev/null
+++ b/src/Binary.php
@@ -0,0 +1,34 @@
+handler = $handler;
+ }
+
+ public function finish(): void
+ {
+ unset($this->handler);
+ }
+
+ /**
+ * @return iterable
+ * @throws ReadDataException
+ */
+ public function readAll(): iterable
+ {
+ while (true) {
+ $line = $this->readLine();
+ if ($line === null) {
+ $this->finish();
+ return;
+ }
+
+ yield $line;
+ }
+ }
+
+ /**
+ * @throws ReadDataException
+ */
+ public function readLine(): ?string
+ {
+ return $this->read();
+ }
+
+ public function __destruct()
+ {
+ $this->finish();
+ }
+
+ /**
+ * @throws ReadDataException
+ */
+ private function read(): ?string
+ {
+ if (empty($this->handler)) {
+ throw ReadDataException::handlerAlreadyClosed($this->filename);
+ }
+
+ $length = $this->handler->read(self::FORMAT_LENGTH);
+ if (empty($length)) {
+ return null;
+ }
+
+ if (mb_strlen($length) !== self::FORMAT_LENGTH) {
+ throw ReadDataException::dataCorrupted($this->filename);
+ }
+
+ $data = unpack(self::FORMAT . 'len', $length);
+ $line = $this->handler->read((int)$data['len']);
+
+ return $line !== false ? $line : null;
+ }
+}
diff --git a/src/Binary/Writer.php b/src/Binary/Writer.php
new file mode 100644
index 0000000..acb6129
--- /dev/null
+++ b/src/Binary/Writer.php
@@ -0,0 +1,50 @@
+handler = $handler;
+ }
+
+ public function finish(): void
+ {
+ unset($this->handler);
+ }
+
+ public function writeLine(string $data): int|false
+ {
+ if (empty($this->handler)) {
+ throw WriteDataException::handlerAlreadyClosed($this->filename);
+ }
+
+ $length = pack(self::FORMAT, mb_strlen($data));
+
+ return $this->handler->write($length, $data);
+ }
+
+ /**
+ * @throws BinaryException
+ */
+ public function __destruct()
+ {
+ $this->finish();
+ }
+}
diff --git a/src/BinaryGenerator.php b/src/BinaryGenerator.php
deleted file mode 100644
index 5ae94c5..0000000
--- a/src/BinaryGenerator.php
+++ /dev/null
@@ -1,207 +0,0 @@
-handler)) {
- throw new HandlerException("Binary generator has already been started, please finish it before next usage.");
- }
-
- $this->mode = $mode;
- $this->filename = $filename;
- $this->compression = $compression;
-
- $this->handler = $this->open();
-
- if (empty($this->handler)) {
- throw new HandlerException("Error during opening \"$this->filename\" file.");
- }
- }
-
- /**
- * Finish process.
- */
- public function finish()
- {
- if (!empty($this->handler)) {
- $this->close();
- $this->handler = null;
- }
- }
-
- /**
- * Write part of data into file.
- *
- * @param string $data
- * @return int
- * @throws HandlerException
- * @throws WriteDataException
- */
- public function writeData($data)
- {
- if (!$this->isWriteMode()) {
- throw new WriteDataException("Unable to write data into \"$this->filename\" file, read mode is set.");
- }
-
- if (empty($this->handler)) {
- throw new HandlerException("Unable to write data into \"$this->filename\" file, no resource is available.");
- }
-
- $packedLength = pack('L', mb_strlen($data));
-
- return $this->write($packedLength, $data);
- }
-
- /**
- * Write part of data.
- *
- * @return null|string
- * @throws HandlerException
- * @throws ReadDataException
- */
- public function readData()
- {
- if (!$this->isReadMode()) {
- throw new ReadDataException("Unable to read data from \"$this->filename\" file, write mode is set.");
- }
-
- if (empty($this->handler)) {
- throw new HandlerException("Unable to read data from \"$this->filename\" file, no resource is available.");
- }
-
- $length = $this->read(4);
-
- if (empty($length)) {
- return null;
- }
-
- if (mb_strlen($length) != 4) {
- throw new ReadDataException("Unable to read data from \"$this->filename\" file, data is corrupted.");
- }
-
- $length = unpack('L', $length);
-
- return $this->read((int)$length);
- }
-
- /**
- * Open file and get handler.
- *
- * @return resource
- */
- private function open()
- {
- if (!empty($this->compression)) {
- return gzopen($this->filename, $this->mode);
- } else {
- return fopen($this->filename, $this->mode);
- }
- }
-
- /**
- * Close handler file.
- */
- private function close()
- {
- if (!empty($this->compression)) {
- gzclose($this->handler);
- } else {
- fclose($this->handler);
- }
- }
-
- /**
- * Read piece of data with the defined length.
- *
- * @param int $length
- * @return string
- */
- private function read($length)
- {
- if (!empty($this->compression)) {
- return gzread($this->handler, $length);
- } else {
- return fread($this->handler, $length);
- }
- }
-
- /**
- * Write data into file.
- *
- * @param int $length
- * @param string $data
- * @return int
- */
- private function write($length, $data)
- {
- if (!empty($this->compression)) {
- return gzwrite($this->handler, $length . $data);
- } else {
- return fwrite($this->handler, $length . $data);
- }
- }
-
- /**
- * If current file in read mode.
- *
- * @return bool
- */
- private function isReadMode()
- {
- return $this->mode === self::READ;
- }
-
- /**
- * If current file in write mode.
- *
- * @return bool
- */
- private function isWriteMode()
- {
- return in_array($this->mode, [self::WRITE, self::APPEND]);
- }
-
- /**
- * Check if file was closed.
- */
- public function __destruct()
- {
- if (!empty($this->handler)) {
- throw new HandlerException("Binary generator hasn't finished \"$this->filename\" file yet.");
- }
- }
-}
\ No newline at end of file
diff --git a/src/Exceptions/BinaryException.php b/src/Exceptions/BinaryException.php
new file mode 100644
index 0000000..82891e2
--- /dev/null
+++ b/src/Exceptions/BinaryException.php
@@ -0,0 +1,15 @@
+getMessage(), $e->getCode(), $e);
+ }
+}
diff --git a/src/Exceptions/ClosedHandlerException.php b/src/Exceptions/ClosedHandlerException.php
new file mode 100644
index 0000000..7d51564
--- /dev/null
+++ b/src/Exceptions/ClosedHandlerException.php
@@ -0,0 +1,13 @@
+status = Status::PENDING;
+ }
+
+ public function read(int $length): string|false
+ {
+ try {
+ $this->open();
+ } catch (ClosedHandlerException) {
+ throw ReadDataException::handlerAlreadyClosed($this->filename);
+ } catch (OpenHandlerException $e) {
+ throw ReadDataException::wrap($e);
+ }
+
+ return $this->compression
+ ? gzread($this->handler, $length)
+ : fread($this->handler, $length);
+ }
+
+ public function write(string $length, string $data): int|false
+ {
+ try {
+ $this->open();
+ } catch (ClosedHandlerException) {
+ throw WriteDataException::handlerAlreadyClosed($this->filename);
+ } catch (OpenHandlerException $e) {
+ throw WriteDataException::wrap($e);
+ }
+
+ return $this->compression
+ ? gzwrite($this->handler, $length . $data)
+ : fwrite($this->handler, $length . $data);
+ }
+
+ public function __destruct()
+ {
+ $this->close();
+ }
+
+ private function open(): void
+ {
+ if ($this->status === Status::CLOSED) {
+ throw new ClosedHandlerException($this->filename);
+ }
+
+ if ($this->status === Status::OPENED) {
+ return;
+ }
+
+ $handler = $this->compression
+ ? gzopen($this->filename, $this->mode->value)
+ : fopen($this->filename, $this->mode->value);
+
+ if (!$handler) {
+ throw new OpenHandlerException($this->filename);
+ }
+
+ $this->handler = $handler;
+ $this->status = Status::OPENED;
+ }
+
+ private function close(): void
+ {
+ if ($this->status === Status::CLOSED) {
+ return;
+ }
+
+ if ($this->status === Status::OPENED) {
+ if (!empty($this->compression)) {
+ gzclose($this->handler);
+ } else {
+ fclose($this->handler);
+ }
+ }
+
+ $this->handler = null;
+ $this->status = Status::CLOSED;
+ }
+}
diff --git a/src/Handler/HandlerInterface.php b/src/Handler/HandlerInterface.php
new file mode 100644
index 0000000..28cb96f
--- /dev/null
+++ b/src/Handler/HandlerInterface.php
@@ -0,0 +1,9 @@
+assertSame('hello', $reader->readLine());
+ $this->assertSame('world', $reader->readLine());
+ $this->assertNull($reader->readLine());
+ }
+
+ public function testReadAllLines(): void
+ {
+ $reader = Binary::reader(self::FILENAME);
+ $lines = [...$reader->readAll()];
+
+ $this->assertSame(['hello', 'world'], $lines);
+ }
+
+ public function testCantReadClosedFile(): void
+ {
+ $this->expectException(ReadDataException::class);
- $binary->start($this->writeFile, BinaryGenerator::WRITE);
- $binary->writeData('some data.1');
- $binary->writeData('some data.2');
- $binary->finish();
+ $reader = Binary::reader(self::FILENAME);
+ $reader->finish();
+ $reader->readLine();
+ }
+
+ public function testWriteSingleLine(): void
+ {
+ $writer = Binary::writer(self::FILENAME);
+ $writer->writeLine('world');
+ $writer->finish();
+
+ $reader = Binary::reader(self::FILENAME);
+
+ $this->assertSame('world', $reader->readLine());
+ $this->assertNull($reader->readLine());
+ }
+
+ public function testAppendSingleLine(): void
+ {
+ $writer = Binary::appender(self::FILENAME);
+ $writer->writeLine('!');
+ $writer->finish();
+
+ $reader = Binary::reader(self::FILENAME);
+
+ $this->assertSame('hello', $reader->readLine());
+ $this->assertSame('world', $reader->readLine());
+ $this->assertSame('!', $reader->readLine());
+ $this->assertNull($reader->readLine());
+ }
+
+
+ public function testCantWriteToClosedFile(): void
+ {
+ $this->expectException(WriteDataException::class);
- $this->assertFileExists($this->writeFile);
+ $writer = Binary::writer(self::FILENAME);
+ $writer->finish();
+ $writer->writeLine('!');
}
-}
\ No newline at end of file
+}
diff --git a/tests/assets/.empty b/tests/assets/.empty
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/assets/stub.txt b/tests/assets/stub.txt
new file mode 100644
index 0000000..a3176ca
Binary files /dev/null and b/tests/assets/stub.txt differ