diff --git a/composer.json b/composer.json index 964d4d1..c103fba 100644 --- a/composer.json +++ b/composer.json @@ -37,5 +37,10 @@ } } }, - "minimum-stability": "stable" + "minimum-stability": "stable", + "require-dev": { + "phpunit/phpunit": "^12.4", + "mockery/mockery": "^1.6", + "fakerphp/faker": "^1.24" + } } diff --git a/src/Config/config.php b/src/Config/config.php index 74bc3e0..0758784 100644 --- a/src/Config/config.php +++ b/src/Config/config.php @@ -34,4 +34,33 @@ */ 'alwaysCreateBackupFolder' => false, + + + /* + |---------------------------------------------------------------------- + | Writer settings + |---------------------------------------------------------------------- + */ + 'writer' => [ + /* + |---------------------------------------------------------------------- + | End of Line mode + |---------------------------------------------------------------------- + | + | 'os' (default) - use PHP_EOL as line separator + | 'windows' - use '\r\n' as line separator + | 'unix' - use '\n' as line separator + */ + 'EOLMode' => 'os', + + /* + |---------------------------------------------------------------------- + | End file with line-break + |---------------------------------------------------------------------- + | + | true (default) - add EOL symbol at end of file + | false - don't add EOL symbol at end of file + */ + 'endsWithLinebreak' => true, + ], ]; diff --git a/src/DotenvEditor.php b/src/DotenvEditor.php index f359a29..bdc1c08 100644 --- a/src/DotenvEditor.php +++ b/src/DotenvEditor.php @@ -119,6 +119,11 @@ public function __construct(Container $app, Config $config) $this->reader = new DotenvReader(new $parser); $this->writer = new DotenvWriter(new Formatter); + $eolMode = $this->config->get('dotenv-editor.writer.EOLMode', 'auto'); + $this->writer->setEOLMode($eolMode); + $endWithEOL = $this->config->get('dotenv-editor.writer.endsWithLinebreak', true); + $this->writer->setEndsWithLinebreak($endWithEOL); + self::configBackuping(); $this->load(); } diff --git a/src/DotenvWriter.php b/src/DotenvWriter.php index 7580474..4a34810 100644 --- a/src/DotenvWriter.php +++ b/src/DotenvWriter.php @@ -15,6 +15,10 @@ */ class DotenvWriter implements WriterInterface { + const EOL_MODE_OS = 'os'; + const EOL_MODE_WINDOWS = 'windows'; + const EOL_MODE_UNIX = 'unix'; + /** * The content buffer. * @@ -35,14 +39,34 @@ class DotenvWriter implements WriterInterface * @var array */ protected $entryTemplate = [ - 'line' => null, - 'type' => 'empty', - 'export' => false, - 'key' => '', - 'value' => '', + 'line' => null, + 'type' => 'empty', + 'export' => false, + 'key' => '', + 'value' => '', 'comment' => '', ]; + /** + * EOL mode + * 'os' (default) - use PHP_EOL as line separator + * 'windows' - use '\r\n' as line separator + * 'unix' - use '\n' as line separator + * + * @var self::STATUS_* + */ + protected $eolMode = self::EOL_MODE_OS; + + /* + |---------------------------------------------------------------------- + | End file with line-break + |---------------------------------------------------------------------- + | + | true (default) - add EOL symbol at end of file + | false - don't add EOL symbol at end of file + */ + protected $endsWithLinebreak = true; + /** * Create a new writer instance. * @@ -103,48 +127,48 @@ public function appendEmpty() public function appendComment(string $comment) { return $this->appendEntry([ - 'type' => 'comment', - 'comment' => (string) $comment, + 'type' => 'comment', + 'comment' => (string)$comment, ]); } /** * Append one setter to buffer. * - * @param string $key + * @param string $key * @param null|string $value * @param null|string $comment - * @param bool $export + * @param bool $export * * @return DotenvWriter */ public function appendSetter(string $key, ?string $value = null, ?string $comment = null, bool $export = false) { return $this->appendEntry([ - 'type' => 'setter', - 'export' => $export, - 'key' => (string) $key, - 'value' => (string) $value, - 'comment' => (string) $comment, + 'type' => 'setter', + 'export' => $export, + 'key' => (string)$key, + 'value' => (string)$value, + 'comment' => (string)$comment, ]); } /** * Update the setter data in buffer. * - * @param string $key + * @param string $key * @param null|string $value * @param null|string $comment - * @param bool $export + * @param bool $export * * @return DotenvWriter */ public function updateSetter(string $key, ?string $value = null, ?string $comment = null, bool $export = false) { $data = [ - 'export' => $export, - 'value' => (string) $value, - 'comment' => (string) $comment, + 'export' => $export, + 'value' => (string)$value, + 'comment' => (string)$comment, ]; array_walk($this->buffer, function (&$entry, $index) use ($key, $data) { @@ -159,7 +183,7 @@ public function updateSetter(string $key, ?string $value = null, ?string $commen /** * Update comment for the setter in buffer. * - * @param string $key + * @param string $key * @param null|string $comment * * @return DotenvWriter @@ -167,7 +191,7 @@ public function updateSetter(string $key, ?string $value = null, ?string $commen public function updateSetterComment(string $key, ?string $comment = null) { $data = [ - 'comment' => (string) $comment, + 'comment' => (string)$comment, ]; array_walk($this->buffer, function (&$entry, $index) use ($key, $data) { @@ -183,7 +207,7 @@ public function updateSetterComment(string $key, ?string $comment = null) * Update export status for the setter in buffer. * * @param string $key - * @param bool $state + * @param bool $state * * @return DotenvWriter */ @@ -233,6 +257,31 @@ public function saveTo(string $filePath) return $this; } + public function getEOLMode(): string + { + return $this->eolMode; + } + + /** + * @param self::STATUS_* $eolMode + * @return void + */ + public function setEOLMode(string $eolMode): void + { + $this->eolMode = $eolMode; + } + + public function isEndsWithLinebreak(): bool + { + return $this->endsWithLinebreak; + } + + + public function setEndsWithLinebreak(bool $endsWithLinebreak): void + { + $this->endsWithLinebreak = $endsWithLinebreak; + } + /** * Append new line to buffer. * @@ -283,6 +332,17 @@ protected function buildTextContent() return ''; }, $this->buffer); - return implode(PHP_EOL, $data) . PHP_EOL; + $eol = PHP_EOL; + switch ($this->eolMode) { + case 'windows': + $eol = "\r\n"; + break; + case 'unix': + $eol = "\n"; + break; + + } + + return implode($eol, $data) . ($this->endsWithLinebreak ? $eol : ''); } } diff --git a/src/DotenvWriterTest.php b/src/DotenvWriterTest.php new file mode 100644 index 0000000..545b3ca --- /dev/null +++ b/src/DotenvWriterTest.php @@ -0,0 +1,103 @@ +setEndsWithLinebreak(false); + + $keys = $this->faker->words(); + $values = array_map(fn($key) => $this->faker->sentence(), $keys); + $lines = array_combine($keys, $values); + foreach ($lines as $key => $value) { + $instance->appendSetter($key, $value); + } + + $result = $instance->getBuffer(false); + $separator = PHP_EOL; + $result = explode($separator, $result); + $expected = array_map(fn($key, $value) => $formatter->formatSetter($key, $value), array_keys($lines), array_values($lines)); + self::assertSame($expected, $result); + } + + public function test_getBuffer_should_return_lines_joined_via_unix_line_separator() + { + $formatter = new Formatter(); + $instance = new DotenvWriter($formatter); + $instance->setEOLMode('unix'); + $instance->setEndsWithLinebreak(false); + + $keys = $this->faker->words(); + $values = array_map(fn($key) => $this->faker->sentence(), $keys); + $lines = array_combine($keys, $values); + foreach ($lines as $key => $value) { + $instance->appendSetter($key, $value); + } + + $result = $instance->getBuffer(false); + $resultInvalid = explode("\r\n", $result); + $result = explode("\n", $result); + $expected = array_map(fn($key, $value) => $formatter->formatSetter($key, $value), array_keys($lines), array_values($lines)); + self::assertSame($expected, $result); + self::assertNotSame($expected, $resultInvalid); + } + + public function test_getBuffer_should_return_lines_joined_via_windows_line_separator() + { + $formatter = new Formatter(); + $instance = new DotenvWriter($formatter); + $instance->setEOLMode('windows'); + $instance->setEndsWithLinebreak(false); + + $keys = $this->faker->words(); + $values = array_map(fn($key) => $this->faker->sentence(), $keys); + $lines = array_combine($keys, $values); + foreach ($lines as $key => $value) { + $instance->appendSetter($key, $value); + } + + $result = $instance->getBuffer(false); + $resultInvalid = explode("\n", $result); + $result = explode("\r\n", $result); + $expected = array_map(fn($key, $value) => $formatter->formatSetter($key, $value), array_keys($lines), array_values($lines)); + self::assertSame($expected, $result); + self::assertNotSame($expected, $resultInvalid); + } + + public function test_getBuffer_should_return_plus_one_lines_when_ends_with_linebreak() + { + $formatter = new Formatter(); + $instance = new DotenvWriter($formatter); + $instance->setEndsWithLinebreak(true); + + $keys = $this->faker->words(); + $values = array_map(fn($key) => $this->faker->sentence(), $keys); + $lines = array_combine($keys, $values); + foreach ($lines as $key => $value) { + $instance->appendSetter($key, $value); + } + + $result = $instance->getBuffer(false); + $separator = PHP_EOL; + $result = explode($separator, $result); + self::assertCount(count($lines) + 1, $result); + } + + protected function setUp(): void + { + parent::setUp(); + $this->faker = Factory::create(); + } + + +}