From c608201de1a87693c3c42747a9aca6b37696ff12 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Thu, 19 Jan 2023 12:38:37 +0900 Subject: [PATCH] Switch to standard PSR-12 with spaces instead of tab --- src/DotEnv.php | 196 ++++++++-------- test/phpUnitTests/DotEnvTest.php | 378 +++++++++++++++---------------- test/read_env_file.php | 14 +- 3 files changed, 294 insertions(+), 294 deletions(-) diff --git a/src/DotEnv.php b/src/DotEnv.php index 9a02ec0..6dd878c 100644 --- a/src/DotEnv.php +++ b/src/DotEnv.php @@ -6,105 +6,105 @@ namespace gullevek\dotEnv; class DotEnv { - /** @var string constant comment char, set to # */ - private const COMMENT_CHAR = '#'; + /** @var string constant comment char, set to # */ + private const COMMENT_CHAR = '#'; - /** - * parses .env file - * - * Rules for .env file - * variable is any alphanumeric string followed by = on the same line - * content starts with the first non space part - * strings can be contained in " - * strings MUST be contained in " if they are multiline - * if string starts with " it will match until another " is found - * anything AFTER " is ignored - * if there are two variables with the same name only the first is used - * variables are case sensitive - * - * @param string $path Folder to file, default is __DIR__ - * @param string $env_file What file to load, default is .env - * @return int -1 other error - * 0 for success full load - * 1 for file loadable, no data or data already loaded - * 2 for file not readable or open failed - * 3 for file not found - */ - public static function readEnvFile( - string $path = __DIR__, - string $env_file = '.env' - ): int { - // default -1; - $status = -1; - $env_file_target = $path . DIRECTORY_SEPARATOR . $env_file; - // this is not a file -> abort - if (!is_file($env_file_target)) { - $status = 3; - return $status; - } - // cannot open file -> abort - if (!is_readable($env_file_target)) { - $status = 2; - return $status; - } - // open file - if (($fp = fopen($env_file_target, 'r')) === false) { - $status = 2; - return $status; - } - // set to readable but not yet any data loaded - $status = 1; - $block = false; - $var = ''; - while ($line = fgets($fp)) { - // main match for variable = value part - if (preg_match("/^\s*([\w_.]+)\s*=\s*((\"?).*)/", $line, $matches)) { - $var = $matches[1]; - $value = $matches[2]; - $quotes = $matches[3]; - // write only if env is not set yet, and write only the first time - if (empty($_ENV[$var])) { - if (!empty($quotes)) { - // match greedy for first to last so we move any " if there are - if (preg_match('/^"(.*[^\\\])"/U', $value, $matches)) { - $value = $matches[1]; - } else { - // this is a multi line - $block = true; - // first " in string remove - // add removed new line back because this is a multi line - $value = ltrim($value, '"') . PHP_EOL; - } - } else { - // strip any quotes at end for unquoted single line - // an right hand spaces are removed too - $value = false !== ($pos = strpos($value, self::COMMENT_CHAR)) ? - rtrim(substr($value, 0, $pos)) : $value; - } - // if block is set, we strip line of slashes - $_ENV[$var] = $block === true ? stripslashes($value) : $value; - // set successful load - $status = 0; - } - } elseif ($block === true) { - // read line until there is a unescaped " - // this also strips everything after the last " - if (preg_match("/(.*[^\\\])\"/", $line, $matches)) { - $block = false; - // strip ending " and EVERYTHING that follows after that - $line = $matches[1]; - } - // just be sure it is init before we fill - if (!isset($_ENV[$var])) { - $_ENV[$var] = ''; - } - // strip line of slashes - $_ENV[$var] .= stripslashes($line); - } - } - fclose($fp); - return $status; - } + /** + * parses .env file + * + * Rules for .env file + * variable is any alphanumeric string followed by = on the same line + * content starts with the first non space part + * strings can be contained in " + * strings MUST be contained in " if they are multiline + * if string starts with " it will match until another " is found + * anything AFTER " is ignored + * if there are two variables with the same name only the first is used + * variables are case sensitive + * + * @param string $path Folder to file, default is __DIR__ + * @param string $env_file What file to load, default is .env + * @return int -1 other error + * 0 for success full load + * 1 for file loadable, no data or data already loaded + * 2 for file not readable or open failed + * 3 for file not found + */ + public static function readEnvFile( + string $path = __DIR__, + string $env_file = '.env' + ): int { + // default -1; + $status = -1; + $env_file_target = $path . DIRECTORY_SEPARATOR . $env_file; + // this is not a file -> abort + if (!is_file($env_file_target)) { + $status = 3; + return $status; + } + // cannot open file -> abort + if (!is_readable($env_file_target)) { + $status = 2; + return $status; + } + // open file + if (($fp = fopen($env_file_target, 'r')) === false) { + $status = 2; + return $status; + } + // set to readable but not yet any data loaded + $status = 1; + $block = false; + $var = ''; + while ($line = fgets($fp)) { + // main match for variable = value part + if (preg_match("/^\s*([\w_.]+)\s*=\s*((\"?).*)/", $line, $matches)) { + $var = $matches[1]; + $value = $matches[2]; + $quotes = $matches[3]; + // write only if env is not set yet, and write only the first time + if (empty($_ENV[$var])) { + if (!empty($quotes)) { + // match greedy for first to last so we move any " if there are + if (preg_match('/^"(.*[^\\\])"/U', $value, $matches)) { + $value = $matches[1]; + } else { + // this is a multi line + $block = true; + // first " in string remove + // add removed new line back because this is a multi line + $value = ltrim($value, '"') . PHP_EOL; + } + } else { + // strip any quotes at end for unquoted single line + // an right hand spaces are removed too + $value = false !== ($pos = strpos($value, self::COMMENT_CHAR)) ? + rtrim(substr($value, 0, $pos)) : $value; + } + // if block is set, we strip line of slashes + $_ENV[$var] = $block === true ? stripslashes($value) : $value; + // set successful load + $status = 0; + } + } elseif ($block === true) { + // read line until there is a unescaped " + // this also strips everything after the last " + if (preg_match("/(.*[^\\\])\"/", $line, $matches)) { + $block = false; + // strip ending " and EVERYTHING that follows after that + $line = $matches[1]; + } + // just be sure it is init before we fill + if (!isset($_ENV[$var])) { + $_ENV[$var] = ''; + } + // strip line of slashes + $_ENV[$var] .= stripslashes($line); + } + } + fclose($fp); + return $status; + } } // __END__ diff --git a/test/phpUnitTests/DotEnvTest.php b/test/phpUnitTests/DotEnvTest.php index ce538a6..77d4385 100644 --- a/test/phpUnitTests/DotEnvTest.php +++ b/test/phpUnitTests/DotEnvTest.php @@ -13,197 +13,197 @@ use PHPUnit\Framework\TestCase; */ final class DotEnvTest extends TestCase { - /** - * setup the .env files before test run - * - * @return void - */ - public static function setUpBeforeClass(): void - { - // create .env files - $file_content = __DIR__ . DIRECTORY_SEPARATOR - . 'dotenv' . DIRECTORY_SEPARATOR - . 'test.env'; - // copy to all folder levels - $env_files = [ - __DIR__ . DIRECTORY_SEPARATOR - . 'dotenv' . DIRECTORY_SEPARATOR - . '.env', - __DIR__ . DIRECTORY_SEPARATOR - . '.env', - __DIR__ . DIRECTORY_SEPARATOR - . '..' . DIRECTORY_SEPARATOR - . '.env', - ]; - // if not found, skip -> all will fail - if (is_file($file_content)) { - foreach ($env_files as $env_file) { - copy($file_content, $env_file); - } - } - } + /** + * setup the .env files before test run + * + * @return void + */ + public static function setUpBeforeClass(): void + { + // create .env files + $file_content = __DIR__ . DIRECTORY_SEPARATOR + . 'dotenv' . DIRECTORY_SEPARATOR + . 'test.env'; + // copy to all folder levels + $env_files = [ + __DIR__ . DIRECTORY_SEPARATOR + . 'dotenv' . DIRECTORY_SEPARATOR + . '.env', + __DIR__ . DIRECTORY_SEPARATOR + . '.env', + __DIR__ . DIRECTORY_SEPARATOR + . '..' . DIRECTORY_SEPARATOR + . '.env', + ]; + // if not found, skip -> all will fail + if (is_file($file_content)) { + foreach ($env_files as $env_file) { + copy($file_content, $env_file); + } + } + } - /** - * Undocumented function - * - * @return array - */ - public function envFileProvider(): array - { - $dot_env_content = [ - 'SOMETHING' => 'A', - 'OTHER' => 'B IS B', - 'Complex' => 'A B \"D is F', - 'HAS_SPACE' => 'ABC', - 'HAS_COMMENT_QUOTES_SPACE' => 'Comment at end with quotes and space', - 'HAS_COMMENT_QUOTES_NO_SPACE' => 'Comment at end with quotes no space', - 'HAS_COMMENT_NO_QUOTES_SPACE' => 'Comment at end no quotes and space', - 'HAS_COMMENT_NO_QUOTES_NO_SPACE' => 'Comment at end no quotes no space', - 'COMMENT_IN_TEXT_QUOTES' => 'Foo bar # comment in here', - 'FAILURE' => 'ABC', - 'SIMPLEBOX' => 'A B C', - 'TITLE' => '1', - 'FOO' => '1.2', - 'SOME.TEST' => 'Test Var', - 'SOME.LIVE' => 'Live Var', - 'A_TEST1' => 'foo', - 'A_TEST2' => '${TEST1:-bar}', - 'A_TEST3' => '${TEST4:-bar}', - 'A_TEST5' => 'null', - 'A_TEST6' => '${TEST5-bar}', - 'A_TEST7' => '${TEST6:-bar}', - 'B_TEST1' => 'foo', - 'B_TEST2' => '${TEST1:=bar}', - 'B_TEST3' => '${TEST4:=bar}', - 'B_TEST5' => 'null', - 'B_TEST6' => '${TEST5=bar}', - 'B_TEST7' => '${TEST6=bar}', - 'Test' => 'A', - 'TEST' => 'B', - 'LINE' => "ABC\nDEF", - 'OTHERLINE' => "ABC\nAF\"ASFASDF\nMORESHIT", - 'SUPERLINE' => '', - '__FOO_BAR_1' => 'b', - '__FOOFOO' => 'f ', - 123123 => 'number', - 'EMPTY' => '', - ]; - // 0: folder relative to test folder, if unset __DIR__ - // 1: file, if unset .env - // 2: status to be returned - // 3: _ENV file content to be set - // 4: override chmod as octect in string - return [ - 'default' => [ - 'folder' => null, - 'file' => null, - 'status' => 3, - 'content' => [], - 'chmod' => null, - ], - 'cannot open file' => [ - 'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv', - 'file' => 'cannot_read.env', - 'status' => 2, - 'content' => [], - // 0000 - 'chmod' => '100000', - ], - 'empty file' => [ - 'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv', - 'file' => 'empty.env', - 'status' => 1, - 'content' => [], - // 0664 - 'chmod' => '100664', - ], - 'override all' => [ - 'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv', - 'file' => 'test.env', - 'status' => 0, - 'content' => $dot_env_content, - // 0664 - 'chmod' => '100664', - ], - 'override directory' => [ - 'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv', - 'file' => null, - 'status' => 0, - 'content' => $dot_env_content, - 'chmod' => null, - ], - ]; - } + /** + * Undocumented function + * + * @return array + */ + public function envFileProvider(): array + { + $dot_env_content = [ + 'SOMETHING' => 'A', + 'OTHER' => 'B IS B', + 'Complex' => 'A B \"D is F', + 'HAS_SPACE' => 'ABC', + 'HAS_COMMENT_QUOTES_SPACE' => 'Comment at end with quotes and space', + 'HAS_COMMENT_QUOTES_NO_SPACE' => 'Comment at end with quotes no space', + 'HAS_COMMENT_NO_QUOTES_SPACE' => 'Comment at end no quotes and space', + 'HAS_COMMENT_NO_QUOTES_NO_SPACE' => 'Comment at end no quotes no space', + 'COMMENT_IN_TEXT_QUOTES' => 'Foo bar # comment in here', + 'FAILURE' => 'ABC', + 'SIMPLEBOX' => 'A B C', + 'TITLE' => '1', + 'FOO' => '1.2', + 'SOME.TEST' => 'Test Var', + 'SOME.LIVE' => 'Live Var', + 'A_TEST1' => 'foo', + 'A_TEST2' => '${TEST1:-bar}', + 'A_TEST3' => '${TEST4:-bar}', + 'A_TEST5' => 'null', + 'A_TEST6' => '${TEST5-bar}', + 'A_TEST7' => '${TEST6:-bar}', + 'B_TEST1' => 'foo', + 'B_TEST2' => '${TEST1:=bar}', + 'B_TEST3' => '${TEST4:=bar}', + 'B_TEST5' => 'null', + 'B_TEST6' => '${TEST5=bar}', + 'B_TEST7' => '${TEST6=bar}', + 'Test' => 'A', + 'TEST' => 'B', + 'LINE' => "ABC\nDEF", + 'OTHERLINE' => "ABC\nAF\"ASFASDF\nMORESHIT", + 'SUPERLINE' => '', + '__FOO_BAR_1' => 'b', + '__FOOFOO' => 'f ', + 123123 => 'number', + 'EMPTY' => '', + ]; + // 0: folder relative to test folder, if unset __DIR__ + // 1: file, if unset .env + // 2: status to be returned + // 3: _ENV file content to be set + // 4: override chmod as octect in string + return [ + 'default' => [ + 'folder' => null, + 'file' => null, + 'status' => 3, + 'content' => [], + 'chmod' => null, + ], + 'cannot open file' => [ + 'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv', + 'file' => 'cannot_read.env', + 'status' => 2, + 'content' => [], + // 0000 + 'chmod' => '100000', + ], + 'empty file' => [ + 'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv', + 'file' => 'empty.env', + 'status' => 1, + 'content' => [], + // 0664 + 'chmod' => '100664', + ], + 'override all' => [ + 'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv', + 'file' => 'test.env', + 'status' => 0, + 'content' => $dot_env_content, + // 0664 + 'chmod' => '100664', + ], + 'override directory' => [ + 'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv', + 'file' => null, + 'status' => 0, + 'content' => $dot_env_content, + 'chmod' => null, + ], + ]; + } - /** - * test read .env file - * - * @covers ::readEnvFile - * @dataProvider envFileProvider - * @testdox Read _ENV file from $folder / $file with expected status: $expected_status [$_dataName] - * - * @param string|null $folder - * @param string|null $file - * @param int $expected_status - * @param array $expected_env - * @param string|null $chmod - * @return void - */ - public function testReadEnvFile( - ?string $folder, - ?string $file, - int $expected_status, - array $expected_env, - ?string $chmod - ): void { - // skip if chmod is set to 10000 (000 no rights) if we are root - // as root there is no stop reading a file - if ( - !empty($chmod) && - $chmod == '100000' && - getmyuid() == 0 - ) { - $this->markTestSkipped( - "Skip cannot read file test because run user is root" - ); - return; - } - // reset $_ENV for clean compare - $_ENV = []; - // previous file perm - $old_chmod = null; - // if we have change permission for file - if ( - is_file($folder . DIRECTORY_SEPARATOR . $file) && - !empty($chmod) - ) { - // get the old permissions - $old_chmod = fileperms($folder . DIRECTORY_SEPARATOR . $file); - chmod($folder . DIRECTORY_SEPARATOR . $file, octdec($chmod)); - } - if ($folder !== null && $file !== null) { - $status = \gullevek\dotEnv\DotEnv::readEnvFile($folder, $file); - } elseif ($folder !== null) { - $status = \gullevek\dotEnv\DotEnv::readEnvFile($folder); - } else { - $status = \gullevek\dotEnv\DotEnv::readEnvFile(); - } - $this->assertEquals( - $status, - $expected_status, - 'Assert returned status equal' - ); - // now assert read data - $this->assertEquals( - $_ENV, - $expected_env, - 'Assert _ENV correct' - ); - // if we have file and chmod unset - if ($old_chmod !== null) { - chmod($folder . DIRECTORY_SEPARATOR . $file, $old_chmod); - } - } + /** + * test read .env file + * + * @covers ::readEnvFile + * @dataProvider envFileProvider + * @testdox Read _ENV file from $folder / $file with expected status: $expected_status [$_dataName] + * + * @param string|null $folder + * @param string|null $file + * @param int $expected_status + * @param array $expected_env + * @param string|null $chmod + * @return void + */ + public function testReadEnvFile( + ?string $folder, + ?string $file, + int $expected_status, + array $expected_env, + ?string $chmod + ): void { + // skip if chmod is set to 10000 (000 no rights) if we are root + // as root there is no stop reading a file + if ( + !empty($chmod) && + $chmod == '100000' && + getmyuid() == 0 + ) { + $this->markTestSkipped( + "Skip cannot read file test because run user is root" + ); + return; + } + // reset $_ENV for clean compare + $_ENV = []; + // previous file perm + $old_chmod = null; + // if we have change permission for file + if ( + is_file($folder . DIRECTORY_SEPARATOR . $file) && + !empty($chmod) + ) { + // get the old permissions + $old_chmod = fileperms($folder . DIRECTORY_SEPARATOR . $file); + chmod($folder . DIRECTORY_SEPARATOR . $file, octdec($chmod)); + } + if ($folder !== null && $file !== null) { + $status = \gullevek\dotEnv\DotEnv::readEnvFile($folder, $file); + } elseif ($folder !== null) { + $status = \gullevek\dotEnv\DotEnv::readEnvFile($folder); + } else { + $status = \gullevek\dotEnv\DotEnv::readEnvFile(); + } + $this->assertEquals( + $status, + $expected_status, + 'Assert returned status equal' + ); + // now assert read data + $this->assertEquals( + $_ENV, + $expected_env, + 'Assert _ENV correct' + ); + // if we have file and chmod unset + if ($old_chmod !== null) { + chmod($folder . DIRECTORY_SEPARATOR . $file, $old_chmod); + } + } } // __END__ diff --git a/test/read_env_file.php b/test/read_env_file.php index 97b511d..d421ca8 100644 --- a/test/read_env_file.php +++ b/test/read_env_file.php @@ -8,18 +8,18 @@ use gullevek\dotEnv\DotEnv; // copy test file to .env file in env folder $file_content = __DIR__ . DIRECTORY_SEPARATOR - . 'phpUnitTests' . DIRECTORY_SEPARATOR - . 'dotenv' . DIRECTORY_SEPARATOR - . 'test.env'; + . 'phpUnitTests' . DIRECTORY_SEPARATOR + . 'dotenv' . DIRECTORY_SEPARATOR + . 'test.env'; // env folder $env_file = __DIR__ . DIRECTORY_SEPARATOR - . 'env' . DIRECTORY_SEPARATOR - . '.env'; + . 'env' . DIRECTORY_SEPARATOR + . '.env'; if (!is_file($file_content)) { - die("Cannot read $file_content"); + die("Cannot read $file_content"); } if (copy($file_content, $env_file) === false) { - die("Cannot copy $file_content to $env_file"); + die("Cannot copy $file_content to $env_file"); } print "BASE: " . __DIR__ . "
";