diff --git a/src/JsonlParser.php b/src/JsonlParser.php index 3303f0a..a58bf6d 100644 --- a/src/JsonlParser.php +++ b/src/JsonlParser.php @@ -13,6 +13,15 @@ public function __construct(protected $stream) { } + /** + * Initialize the parser from the given file. + */ + public static function fromFile(string $filename, string $mode = 'a+t'): self + { + $stream = fopen($filename, $mode); + return new self($stream); + } + public function push(array|string $item): void { $encoded = json_encode($item); @@ -65,9 +74,10 @@ public function pop(): null|array|string $buffer = strrev($buffer); - // truncate the stream and remove the trailing newline + // truncate the stream + // remove the trailing newline if the stream is now empty $pos = ftell($this->stream); - ftruncate($this->stream, $pos < 1 ? 0 : $pos-1); + ftruncate($this->stream, $pos <= strlen(self::LINES_SEPARATOR) ? 0 : $pos); return json_decode($buffer, associative: true); } @@ -103,4 +113,12 @@ public function count(): int return $count; } + + /** + * Checks if there are no items in the stream. + */ + public function empty(): bool + { + return count($this) === 0; + } } diff --git a/tests/JsonParserTest.php b/tests/JsonParserTest.php index f17b0dd..8708666 100644 --- a/tests/JsonParserTest.php +++ b/tests/JsonParserTest.php @@ -126,4 +126,44 @@ public function testPushItemsToFile(): void fclose($stream); // this removes the file } + + public function testFromFile(): void + { + $tmpFilename = tempnam(directory: sys_get_temp_dir(), prefix: 'jsonld'); + + $parser = JsonlParser::fromFile($tmpFilename); + $this->assertCount(0, $parser); + $this->assertTrue($parser->empty()); + + $parser->push(self::ITEM_ONE); + $parser->push(self::ITEM_TWO); + $this->assertCount(2, $parser); + $this->assertFalse($parser->empty()); + + // now get one and push the next item + $this->assertSame(self::ITEM_TWO, $parser->pop()); + $this->assertStringEqualsFile( + expectedFile: $tmpFilename, + actualString: json_encode(self::ITEM_ONE) . JsonlParser::LINES_SEPARATOR + ); + $this->assertCount(1, $parser); + $parser->push(self::ITEM); + $this->assertStringEqualsFile( + expectedFile: $tmpFilename, + actualString: json_encode(self::ITEM_ONE) . JsonlParser::LINES_SEPARATOR . json_encode(self::ITEM) . JsonlParser::LINES_SEPARATOR + ); + $this->assertCount(2, $parser); + + // now, empty the file + $parser->pop(); + $parser->pop(); + $this->assertCount(0, $parser); + $this->assertTrue($parser->empty()); + $this->assertStringEqualsFile( + expectedFile: $tmpFilename, + actualString: '' + ); + + unlink($tmpFilename); + } }