21 Commits

Author SHA1 Message Date
8d14445786 Moved to tabs for indent and add phpcs.xml file, update phpunit to 12 and update tests 2026-01-13 10:36:11 +09:00
979ec79fc0 Fix .gitignore 2025-02-28 12:58:10 +09:00
dfe8607934 Turn off phpunit verbose 2025-01-21 09:57:13 +09:00
a7ade86485 phpstan 2.0 upgrade and fixes 2024-11-18 20:10:09 +09:00
b9feacaded Add Block settings to dotenv reader
Now blocks can be set as prefix names for variables via [Block Name] type
grouping.
2024-08-21 11:41:15 +09:00
646456b86b Github actions cache version update 2024-05-22 17:59:16 +09:00
4b0550b8d2 github action cache folder change 2024-05-22 17:54:04 +09:00
efb99f259e Github action phpstan temp folder location 2024-05-22 17:52:33 +09:00
72f4827985 Github actions tmp cache 2024-05-22 17:48:46 +09:00
d5bf24c8cf Github action cache path change 2024-05-22 17:43:14 +09:00
773f40e2d1 Github actions 2024-05-22 17:42:01 +09:00
0bb137dff6 phpunit xml layout update 2024-05-22 17:06:13 +09:00
7e1a19b86b Note about PHPunit test setup 2024-05-22 17:00:40 +09:00
2524092cd8 phpunit tests 2024-05-22 16:55:55 +09:00
f692ca41b1 phpunit test 2024-05-22 16:52:56 +09:00
b9620704bc Github actions update 2024-05-22 16:38:19 +09:00
5004e3c9d8 Github actions add phpunit tests 2024-05-22 16:33:54 +09:00
e86528a366 git actions update 2024-05-22 15:49:10 +09:00
e99a995a2e CI github work flow added 2024-05-22 15:40:20 +09:00
e29f9fcd88 Add another psr-4 prefix, update composer packages
Previously it was just "gullevek\dotEnv" but now also "gullevek\dotenv"
will work.

Update phan/phpstan composer dev requirement verisons
2023-03-03 09:32:02 +09:00
ae0eb1f939 Convert phan config to PSR12 2023-01-19 12:45:21 +09:00
13 changed files with 483 additions and 363 deletions

35
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: CI
run-name: ${{ github.actor}} runs CI
on: [push]
jobs:
ci-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: php-actions/composer@v6
env:
COMPOSER_ROOT_VERSION: dev-master
- name: "Restore result cache"
uses: actions/cache/restore@v4
with:
path: ./tmp
key: "result-cache-v1-${{ matrix.php-version }}-${{ github.run_id }}"
restore-keys: |
result-cache-v1-${{ matrix.php-version }}-
- name: PHPStan Static Analysis
uses: php-actions/phpstan@v3
with:
path: src/
configuration: phpstan.neon
- name: "Save result cache"
uses: actions/cache/save@v4
if: always()
with:
path: ./tmp
key: "result-cache-v1-${{ matrix.php-version }}-${{ github.run_id }}"
# We need to use phpunit from the self install to get the class paths
- name: PHPunit Tests
run: |
vendor/bin/phpunit

3
.gitignore vendored
View File

@@ -2,3 +2,6 @@ vendor
.phpunit.result.cache .phpunit.result.cache
.phplint-cache .phplint-cache
composer.lock composer.lock
**/.env
**/.target
.phpunit.cache

View File

@@ -26,72 +26,73 @@
// use Phan\Config; // use Phan\Config;
return [ return [
// If true, missing properties will be created when // If true, missing properties will be created when
// they are first seen. If false, we'll report an // they are first seen. If false, we'll report an
// error message. // error message.
"allow_missing_properties" => false, "allow_missing_properties" => false,
// Allow null to be cast as any type and for any // Allow null to be cast as any type and for any
// type to be cast to null. // type to be cast to null.
"null_casts_as_any_type" => false, "null_casts_as_any_type" => false,
// Backwards Compatibility Checking // Backwards Compatibility Checking
'backward_compatibility_checks' => true, 'backward_compatibility_checks' => true,
// Run a quick version of checks that takes less // Run a quick version of checks that takes less
// time // time
"quick_mode" => false, "quick_mode" => false,
// Only emit critical issues to start with // Only emit critical issues to start with
// (0 is low severity, 5 is normal severity, 10 is critical) // (0 is low severity, 5 is normal severity, 10 is critical)
"minimum_severity" => 10, "minimum_severity" => 10,
// default false for include path check // default false for include path check
"enable_include_path_checks" => true, "enable_include_path_checks" => true,
"include_paths" => [ "include_paths" => [
], ],
'ignore_undeclared_variables_in_global_scope' => true, 'ignore_undeclared_variables_in_global_scope' => true,
"file_list" => [ "file_list" => [
], ],
// A list of directories that should be parsed for class and // A list of directories that should be parsed for class and
// method information. After excluding the directories // method information. After excluding the directories
// defined in exclude_analysis_directory_list, the remaining // defined in exclude_analysis_directory_list, the remaining
// files will be statically analyzed for errors. // files will be statically analyzed for errors.
// //
// Thus, both first-party and third-party code being used by // Thus, both first-party and third-party code being used by
// your application should be included in this list. // your application should be included in this list.
'directory_list' => [ 'directory_list' => [
// Change this to include the folders you wish to analyze // Change this to include the folders you wish to analyze
// (and the folders of their dependencies) // (and the folders of their dependencies)
'.' '.'
// 'www', // 'www',
// To speed up analysis, we recommend going back later and // To speed up analysis, we recommend going back later and
// limiting this to only the vendor/ subdirectories your // limiting this to only the vendor/ subdirectories your
// project depends on. // project depends on.
// `phan --init` will generate a list of folders for you // `phan --init` will generate a list of folders for you
], ],
// A list of directories holding code that we want // A list of directories holding code that we want
// to parse, but not analyze // to parse, but not analyze
"exclude_analysis_directory_list" => [ "exclude_analysis_directory_list" => [
'vendor', 'vendor',
'test' 'test',
], 'tmp'
'exclude_file_list' => [ ],
], 'exclude_file_list' => [
],
// what not to show as problem // what not to show as problem
'suppress_issue_types' => [ 'suppress_issue_types' => [
// 'PhanUndeclaredMethod', // 'PhanUndeclaredMethod',
'PhanEmptyFile', 'PhanEmptyFile',
], ],
// Override to hardcode existence and types of (non-builtin) globals in the global scope. // Override to hardcode existence and types of (non-builtin) globals in the global scope.
// Class names should be prefixed with `\`. // Class names should be prefixed with `\`.
// //
// (E.g. `['_FOO' => '\FooClass', 'page' => '\PageClass', 'userId' => 'int']`) // (E.g. `['_FOO' => '\FooClass', 'page' => '\PageClass', 'userId' => 'int']`)
'globals_type_map' => [], 'globals_type_map' => [],
]; ];

View File

@@ -61,6 +61,29 @@ DOUBLE="I will be used"
DOUBLE="This will be ignored" DOUBLE="This will be ignored"
``` ```
A prefix name can be set with `[PrefixName]`. Tne name rules are like for variables, but spaces
are allowed, but will be converted to "_".
The prefix is valid from the time set until the next prefix block appears or the file ends.
Example
```ini
FOO="bar"
FOOBAR="bar bar"
[SecitonA]
FOO="other bar"
FOOBAR="other bar bar"
```
Will have environmen variables as
```php
$_ENV["FOO"];
$_ENV["FOOBAR"];
$_ENV["SecitonA.FOO"];
$_ENV["SecitonA.FOOBAR"];
```
## Development ## Development
### Phan ### Phan

View File

@@ -6,7 +6,8 @@
"license": "MIT", "license": "MIT",
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"gullevek\\dotEnv\\": "src/" "gullevek\\dotEnv\\": "src/",
"gullevek\\dotenv\\": "src/"
} }
}, },
"authors": [ "authors": [
@@ -25,8 +26,10 @@
"exclude": ["/test/", "/test/*", "/phpstan.neon", "/psalm.xml", "/.phan/", "/.vscode/", "/phpunit.xml"] "exclude": ["/test/", "/test/*", "/phpstan.neon", "/psalm.xml", "/.phan/", "/.vscode/", "/phpunit.xml"]
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^9", "phan/phan": "^5.4",
"phpstan/phpstan": "1.10.x-dev", "phpstan/phpdoc-parser": "^2.0",
"phan/phan": "v5.x-dev" "phpstan/phpstan-deprecation-rules": "^2.0",
"phpstan/phpstan": "2.1.x-dev",
"phpunit/phpunit": "^12"
} }
} }

18
phpcs.xml Normal file
View File

@@ -0,0 +1,18 @@
<?xml version="1.0"?>
<ruleset name="MyStandard">
<description>PSR12 override rules (strict, standard). Switch spaces indent to tab.</description>
<arg name="tab-width" value="4"/>
<rule ref="PSR1"/>
<rule ref="PSR12">
<!-- turn off white space check for tab -->
<exclude name="Generic.WhiteSpace.DisallowTabIndent"/>
</rule>
<!-- no space indent, must be tab, 4 is tab iwdth -->
<rule ref="Generic.WhiteSpace.DisallowSpaceIndent"/>
<rule ref="Generic.WhiteSpace.ScopeIndent">
<properties>
<property name="indent" value="4"/>
<property name="tabIndent" value="true"/>
</properties>
</rule>
</ruleset>

View File

@@ -1,10 +1,10 @@
# PHP Stan Config # PHP Stan Config
parameters: parameters:
tmpDir: /tmp/phpstan-codeblocks-dotenv tmpDir: %currentWorkingDirectory%/tmp/phpstan-codeblocks-dotenv
level: max level: max
paths: paths:
- %currentWorkingDirectory% - %currentWorkingDirectory%/src
excludePaths: excludePaths:
- vendor - vendor
- test - test

View File

@@ -1,5 +1,8 @@
<phpunit <?xml version="1.0"?>
colors="true" <phpunit colors="true" cacheDirectory=".phpunit.cache">
verbose="true" <testsuites>
> <testsuite name="unit">
<directory>test/phpUnitTests/</directory>
</testsuite>
</testsuites>
</phpunit> </phpunit>

View File

@@ -6,105 +6,115 @@ namespace gullevek\dotEnv;
class DotEnv class DotEnv
{ {
/** @var string constant comment char, set to # */ /** @var string constant comment char, set to # */
private const COMMENT_CHAR = '#'; private const COMMENT_CHAR = '#';
/** /**
* parses .env file * parses .env file
* *
* Rules for .env file * Rules for .env file
* variable is any alphanumeric string followed by = on the same line * variable is any alphanumeric string followed by = on the same line
* content starts with the first non space part * content starts with the first non space part
* strings can be contained in " * strings can be contained in "
* strings MUST be contained in " if they are multiline * strings MUST be contained in " if they are multiline
* if string starts with " it will match until another " is found * if string starts with " it will match until another " is found
* anything AFTER " is ignored * anything AFTER " is ignored
* if there are two variables with the same name only the first is used * if there are two variables with the same name only the first is used
* variables are case sensitive * variables are case sensitive
* *
* @param string $path Folder to file, default is __DIR__ * [] Grouping Block Name as prefix until next or end if set,
* @param string $env_file What file to load, default is .env * space replaced by _, all other var rules apply
* @return int -1 other error *
* 0 for success full load * @param string $path Folder to file, default is __DIR__
* 1 for file loadable, no data or data already loaded * @param string $env_file What file to load, default is .env
* 2 for file not readable or open failed * @return int -1 other error
* 3 for file not found * 0 for success full load
*/ * 1 for file loadable, no data or data already loaded
public static function readEnvFile( * 2 for file not readable or open failed
string $path = __DIR__, * 3 for file not found
string $env_file = '.env' */
): int { public static function readEnvFile(
// default -1; string $path = __DIR__,
$status = -1; string $env_file = '.env'
$env_file_target = $path . DIRECTORY_SEPARATOR . $env_file; ): int {
// this is not a file -> abort // default -1;
if (!is_file($env_file_target)) { $status = -1;
$status = 3; $env_file_target = $path . DIRECTORY_SEPARATOR . $env_file;
return $status; // this is not a file -> abort
} if (!is_file($env_file_target)) {
// cannot open file -> abort $status = 3;
if (!is_readable($env_file_target)) { return $status;
$status = 2; }
return $status; // cannot open file -> abort
} if (!is_readable($env_file_target)) {
// open file $status = 2;
if (($fp = fopen($env_file_target, 'r')) === false) { return $status;
$status = 2; }
return $status; // open file
} if (($fp = fopen($env_file_target, 'r')) === false) {
// set to readable but not yet any data loaded $status = 2;
$status = 1; return $status;
$block = false; }
$var = ''; // set to readable but not yet any data loaded
while ($line = fgets($fp)) { $status = 1;
// main match for variable = value part $block = false;
if (preg_match("/^\s*([\w_.]+)\s*=\s*((\"?).*)/", $line, $matches)) { $var = '';
$var = $matches[1]; $prefix_name = '';
$value = $matches[2]; while (($line = fgets($fp)) !== false) {
$quotes = $matches[3]; // [] block must be a single line, or it will be ignored
// write only if env is not set yet, and write only the first time if (preg_match("/^\s*\[([\w_.\s]+)\]/", $line, $matches)) {
if (empty($_ENV[$var])) { $prefix_name = preg_replace("/\s+/", "_", $matches[1]) . ".";
if (!empty($quotes)) { } elseif (preg_match("/^\s*([\w_.]+)\s*=\s*((\"?).*)/", $line, $matches)) {
// match greedy for first to last so we move any " if there are // main match for variable = value part
if (preg_match('/^"(.*[^\\\])"/U', $value, $matches)) { $var = $prefix_name . $matches[1];
$value = $matches[1]; $value = $matches[2];
} else { $quotes = $matches[3];
// this is a multi line // write only if env is not set yet, and write only the first time
$block = true; if (empty($_ENV[$var])) {
// first " in string remove if (!empty($quotes)) {
// add removed new line back because this is a multi line // match greedy for first to last so we move any " if there are
$value = ltrim($value, '"') . PHP_EOL; if (preg_match('/^"(.*[^\\\])"/U', $value, $matches)) {
} $value = $matches[1];
} else { } else {
// strip any quotes at end for unquoted single line // this is a multi line
// an right hand spaces are removed too $block = true;
$value = false !== ($pos = strpos($value, self::COMMENT_CHAR)) ? // first " in string remove
rtrim(substr($value, 0, $pos)) : $value; // add removed new line back because this is a multi line
} $value = ltrim($value, '"') . PHP_EOL;
// if block is set, we strip line of slashes }
$_ENV[$var] = $block === true ? stripslashes($value) : $value; } else {
// set successful load // strip any quotes at end for unquoted single line
$status = 0; // an right hand spaces are removed too
} $value = false !== ($pos = strpos($value, self::COMMENT_CHAR)) ?
} elseif ($block === true) { rtrim(substr($value, 0, $pos)) : $value;
// read line until there is a unescaped " }
// this also strips everything after the last " // if block is set, we strip line of slashes
if (preg_match("/(.*[^\\\])\"/", $line, $matches)) { $_ENV[$var] = $block === true ? stripslashes($value) : $value;
$block = false; // set successful load
// strip ending " and EVERYTHING that follows after that $status = 0;
$line = $matches[1]; }
} } elseif ($block === true) {
// just be sure it is init before we fill // read line until there is a unescaped "
if (!isset($_ENV[$var])) { // this also strips everything after the last "
$_ENV[$var] = ''; if (preg_match("/(.*[^\\\])\"/", $line, $matches)) {
} $block = false;
// strip line of slashes // strip ending " and EVERYTHING that follows after that
$_ENV[$var] .= stripslashes($line); $line = $matches[1];
} }
} // just be sure it is init before we fill
fclose($fp); if (!isset($_ENV[$var])) {
return $status; $_ENV[$var] = '';
} } elseif (!is_string($_ENV[$var])) {
// if this is not string, skip
continue;
}
// strip line of slashes
$_ENV[$var] .= stripslashes($line);
}
}
fclose($fp);
return $status;
}
} }
// __END__ // __END__

View File

@@ -5,205 +5,214 @@ declare(strict_types=1);
namespace tests; namespace tests;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\CoversMethod;
use PHPUnit\Framework\Attributes\DataProvider;
/** /**
* Test class for DotEnv * Test class for DotEnv
* @coversDefaultClass \gullevek\DotEnv
* @testdox \gullevek\DotEnv method tests
*/ */
#[TestDox("\gullevek\DotEnv method tests")]
#[CoversClass(\gullevek\dotEnv\DotEnv::class)]
#[CoversMethod(\gullevek\dotEnv\DotEnv::class, 'readEnvFile')]
final class DotEnvTest extends TestCase final class DotEnvTest extends TestCase
{ {
/** /**
* setup the .env files before test run * setup the .env files before test run
* *
* @return void * @return void
*/ */
public static function setUpBeforeClass(): void public static function setUpBeforeClass(): void
{ {
// create .env files // create .env files
$file_content = __DIR__ . DIRECTORY_SEPARATOR $file_content = __DIR__ . DIRECTORY_SEPARATOR
. 'dotenv' . DIRECTORY_SEPARATOR . 'dotenv' . DIRECTORY_SEPARATOR
. 'test.env'; . 'test.env';
// copy to all folder levels // copy to all folder levels
$env_files = [ $env_files = [
__DIR__ . DIRECTORY_SEPARATOR __DIR__ . DIRECTORY_SEPARATOR
. 'dotenv' . DIRECTORY_SEPARATOR . 'dotenv' . DIRECTORY_SEPARATOR
. '.env', . '.env',
__DIR__ . DIRECTORY_SEPARATOR __DIR__ . DIRECTORY_SEPARATOR
. '.env', . '.env',
__DIR__ . DIRECTORY_SEPARATOR __DIR__ . DIRECTORY_SEPARATOR
. '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR
. '.env', . '.env',
]; ];
// if not found, skip -> all will fail // if not found, skip -> all will fail
if (is_file($file_content)) { if (is_file($file_content)) {
foreach ($env_files as $env_file) { foreach ($env_files as $env_file) {
copy($file_content, $env_file); copy($file_content, $env_file);
} }
} }
} }
/** /**
* Undocumented function * Undocumented function
* *
* @return array * @return array
*/ */
public function envFileProvider(): array public static function envFileProvider(): array
{ {
$dot_env_content = [ $dot_env_content = [
'SOMETHING' => 'A', 'SOMETHING' => 'A',
'OTHER' => 'B IS B', 'OTHER' => 'B IS B',
'Complex' => 'A B \"D is F', 'Complex' => 'A B \"D is F',
'HAS_SPACE' => 'ABC', 'HAS_SPACE' => 'ABC',
'HAS_COMMENT_QUOTES_SPACE' => 'Comment at end with quotes and space', '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_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_SPACE' => 'Comment at end no quotes and space',
'HAS_COMMENT_NO_QUOTES_NO_SPACE' => 'Comment at end no quotes no space', 'HAS_COMMENT_NO_QUOTES_NO_SPACE' => 'Comment at end no quotes no space',
'COMMENT_IN_TEXT_QUOTES' => 'Foo bar # comment in here', 'COMMENT_IN_TEXT_QUOTES' => 'Foo bar # comment in here',
'FAILURE' => 'ABC', 'HAS_EQUAL_NO_QUITES' => 'Is This = Valid',
'SIMPLEBOX' => 'A B C', 'HAS_EQUAL_QUITES' => 'Is This = Valid',
'TITLE' => '1', 'FAILURE' => 'ABC',
'FOO' => '1.2', 'SIMPLEBOX' => 'A B C',
'SOME.TEST' => 'Test Var', 'TITLE' => '1',
'SOME.LIVE' => 'Live Var', 'FOO' => '1.2',
'A_TEST1' => 'foo', 'SOME.TEST' => 'Test Var',
'A_TEST2' => '${TEST1:-bar}', 'SOME.LIVE' => 'Live Var',
'A_TEST3' => '${TEST4:-bar}', 'A_TEST1' => 'foo',
'A_TEST5' => 'null', 'A_TEST2' => '${TEST1:-bar}',
'A_TEST6' => '${TEST5-bar}', 'A_TEST3' => '${TEST4:-bar}',
'A_TEST7' => '${TEST6:-bar}', 'A_TEST5' => 'null',
'B_TEST1' => 'foo', 'A_TEST6' => '${TEST5-bar}',
'B_TEST2' => '${TEST1:=bar}', 'A_TEST7' => '${TEST6:-bar}',
'B_TEST3' => '${TEST4:=bar}', 'B_TEST1' => 'foo',
'B_TEST5' => 'null', 'B_TEST2' => '${TEST1:=bar}',
'B_TEST6' => '${TEST5=bar}', 'B_TEST3' => '${TEST4:=bar}',
'B_TEST7' => '${TEST6=bar}', 'B_TEST5' => 'null',
'Test' => 'A', 'B_TEST6' => '${TEST5=bar}',
'TEST' => 'B', 'B_TEST7' => '${TEST6=bar}',
'LINE' => "ABC\nDEF", 'Test' => 'A',
'OTHERLINE' => "ABC\nAF\"ASFASDF\nMORESHIT", 'TEST' => 'B',
'SUPERLINE' => '', 'LINE' => "ABC\nDEF",
'__FOO_BAR_1' => 'b', 'OTHERLINE' => "ABC\nAF\"ASFASDF\nMORESHIT",
'__FOOFOO' => 'f ', 'SUPERLINE' => '',
123123 => 'number', '__FOO_BAR_1' => 'b',
'EMPTY' => '', '__FOOFOO' => 'f ',
]; 123123 => 'number',
// 0: folder relative to test folder, if unset __DIR__ 'EMPTY' => '',
// 1: file, if unset .env 'Var_Test.TEST' => 'Block 1 D',
// 2: status to be returned 'OtherSet.TEST' => 'Block 2 D',
// 3: _ENV file content to be set ];
// 4: override chmod as octect in string // 0: folder relative to test folder, if unset __DIR__
return [ // 1: file, if unset .env
'default' => [ // 2: status to be returned
'folder' => null, // 3: _ENV file content to be set
'file' => null, // 4: override chmod as octect in string
'status' => 3, return [
'content' => [], 'default' => [
'chmod' => null, 'folder' => null,
], 'file' => null,
'cannot open file' => [ 'expected_status' => 3,
'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv', 'expected_env' => [],
'file' => 'cannot_read.env', 'chmod' => null,
'status' => 2, ],
'content' => [], 'cannot open file' => [
// 0000 'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv',
'chmod' => '100000', 'file' => 'cannot_read.env',
], 'expected_status' => 2,
'empty file' => [ 'expected_env' => [],
'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv', // 0000
'file' => 'empty.env', 'chmod' => '100000',
'status' => 1, ],
'content' => [], 'empty file' => [
// 0664 'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv',
'chmod' => '100664', 'file' => 'empty.env',
], 'expected_status' => 1,
'override all' => [ 'expected_env' => [],
'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv', // 0664
'file' => 'test.env', 'chmod' => '100664',
'status' => 0, ],
'content' => $dot_env_content, 'override all' => [
// 0664 'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv',
'chmod' => '100664', 'file' => 'test.env',
], 'expected_status' => 0,
'override directory' => [ 'expected_env' => $dot_env_content,
'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv', // 0664
'file' => null, 'chmod' => '100664',
'status' => 0, ],
'content' => $dot_env_content, 'override directory' => [
'chmod' => null, 'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv',
], 'file' => null,
]; 'expected_status' => 0,
} 'expected_env' => $dot_env_content,
'chmod' => null,
],
];
}
/** /**
* test read .env file * test read .env file
* *
* @covers ::readEnvFile * @param string|null $folder
* @dataProvider envFileProvider * @param string|null $file
* @testdox Read _ENV file from $folder / $file with expected status: $expected_status [$_dataName] * @param int $expected_status
* * @param array $expected_env
* @param string|null $folder * @param string|null $chmod
* @param string|null $file * @return void
* @param int $expected_status */
* @param array $expected_env #[Test]
* @param string|null $chmod #[TestDox('Read _ENV file from $folder / $file with expected status: $expected_status [$_dataName]')]
* @return void #[DataProvider('envFileProvider')]
*/ public function testReadEnvFile(
public function testReadEnvFile( ?string $folder,
?string $folder, ?string $file,
?string $file, int $expected_status,
int $expected_status, array $expected_env,
array $expected_env, ?string $chmod
?string $chmod ): void {
): void { // skip if chmod is set to 10000 (000 no rights) if we are root
// skip if chmod is set to 10000 (000 no rights) if we are root // as root there is no stop reading a file
// as root there is no stop reading a file if (
if ( !empty($chmod) &&
!empty($chmod) && $chmod == '100000' &&
$chmod == '100000' && getmyuid() == 0
getmyuid() == 0 ) {
) { $this->markTestSkipped(
$this->markTestSkipped( "Skip cannot read file test because run user is root"
"Skip cannot read file test because run user is root" );
); return;
return; }
} // reset $_ENV for clean compare
// reset $_ENV for clean compare $_ENV = [];
$_ENV = []; // previous file perm
// previous file perm $old_chmod = null;
$old_chmod = null; // if we have change permission for file
// if we have change permission for file if (
if ( is_file($folder . DIRECTORY_SEPARATOR . $file) &&
is_file($folder . DIRECTORY_SEPARATOR . $file) && !empty($chmod)
!empty($chmod) ) {
) { // get the old permissions
// get the old permissions $old_chmod = fileperms($folder . DIRECTORY_SEPARATOR . $file);
$old_chmod = fileperms($folder . DIRECTORY_SEPARATOR . $file); chmod($folder . DIRECTORY_SEPARATOR . $file, octdec($chmod));
chmod($folder . DIRECTORY_SEPARATOR . $file, octdec($chmod)); }
} if ($folder !== null && $file !== null) {
if ($folder !== null && $file !== null) { $status = \gullevek\dotEnv\DotEnv::readEnvFile($folder, $file);
$status = \gullevek\dotEnv\DotEnv::readEnvFile($folder, $file); } elseif ($folder !== null) {
} elseif ($folder !== null) { $status = \gullevek\dotEnv\DotEnv::readEnvFile($folder);
$status = \gullevek\dotEnv\DotEnv::readEnvFile($folder); } else {
} else { $status = \gullevek\dotEnv\DotEnv::readEnvFile();
$status = \gullevek\dotEnv\DotEnv::readEnvFile(); }
} $this->assertEquals(
$this->assertEquals( $expected_status,
$status, $status,
$expected_status, 'Assert returned status equal'
'Assert returned status equal' );
); // now assert read data
// now assert read data $this->assertEquals(
$this->assertEquals( $expected_env,
$_ENV, $_ENV,
$expected_env, 'Assert _ENV correct'
'Assert _ENV correct' );
); // if we have file and chmod unset
// if we have file and chmod unset if ($old_chmod !== null) {
if ($old_chmod !== null) { chmod($folder . DIRECTORY_SEPARATOR . $file, $old_chmod);
chmod($folder . DIRECTORY_SEPARATOR . $file, $old_chmod); }
} }
}
} }
// __END__ // __END__

View File

@@ -10,6 +10,8 @@ HAS_COMMENT_QUOTES_NO_SPACE="Comment at end with quotes no space"# Comment QES
HAS_COMMENT_NO_QUOTES_SPACE=Comment at end no quotes and space # Comment NQE HAS_COMMENT_NO_QUOTES_SPACE=Comment at end no quotes and space # Comment NQE
HAS_COMMENT_NO_QUOTES_NO_SPACE=Comment at end no quotes no space# Comment NQES HAS_COMMENT_NO_QUOTES_NO_SPACE=Comment at end no quotes no space# Comment NQES
COMMENT_IN_TEXT_QUOTES="Foo bar # comment in here" COMMENT_IN_TEXT_QUOTES="Foo bar # comment in here"
HAS_EQUAL_NO_QUITES=Is This = Valid
HAS_EQUAL_QUITES="Is This = Valid"
FAILURE = ABC FAILURE = ABC
SIMPLEBOX= A B C SIMPLEBOX= A B C
TITLE=1 TITLE=1
@@ -47,3 +49,10 @@ SUPERLINE=
EMPTY= EMPTY=
= flase = flase
asfasdf asfasdf
# BLOCK TESTS
[Var Test]
TEST="Block 1 D"
[OtherSet]
TEST="Block 2 D"
[Ignore-Invalid-Block]
TEST="Block 3 D"

View File

@@ -8,18 +8,18 @@ use gullevek\dotEnv\DotEnv;
// copy test file to .env file in env folder // copy test file to .env file in env folder
$file_content = __DIR__ . DIRECTORY_SEPARATOR $file_content = __DIR__ . DIRECTORY_SEPARATOR
. 'phpUnitTests' . DIRECTORY_SEPARATOR . 'phpUnitTests' . DIRECTORY_SEPARATOR
. 'dotenv' . DIRECTORY_SEPARATOR . 'dotenv' . DIRECTORY_SEPARATOR
. 'test.env'; . 'test.env';
// env folder // env folder
$env_file = __DIR__ . DIRECTORY_SEPARATOR $env_file = __DIR__ . DIRECTORY_SEPARATOR
. 'env' . DIRECTORY_SEPARATOR . 'env' . DIRECTORY_SEPARATOR
. '.env'; . '.env';
if (!is_file($file_content)) { if (!is_file($file_content)) {
die("Cannot read $file_content"); die("Cannot read $file_content");
} }
if (copy($file_content, $env_file) === false) { 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__ . "<br>"; print "BASE: " . __DIR__ . "<br>";
@@ -31,4 +31,8 @@ $status = DotEnv::readEnvFile(__DIR__ . DIRECTORY_SEPARATOR . 'env');
print "STATUS: " . (string)$status . "<br>"; print "STATUS: " . (string)$status . "<br>";
print "ENV: <pre>" . print_r($_ENV, true) . "</pre><br>"; print "ENV: <pre>" . print_r($_ENV, true) . "</pre><br>";
$status = gullevek\dotenv\DotEnv::readEnvFile(__DIR__ . DIRECTORY_SEPARATOR . 'env');
print "STATUS B: " . (string)$status . "<br>";
print "ENV B: <pre>" . print_r($_ENV, true) . "</pre><br>";
// __END__ // __END__

2
tmp/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore