37 Commits

Author SHA1 Message Date
ade724b3bb Set color flag for phan in config 2026-01-13 18:37:06 +09:00
532c66ac27 Rename Readme.md to README.md 2026-01-13 11:05:10 +09:00
609b89641e add phive as tool installer 2026-01-13 10:41:07 +09:00
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
c608201de1 Switch to standard PSR-12 with spaces instead of tab 2023-01-19 12:38:37 +09:00
8e062ff114 Add comment to unit test skip condition 2023-01-12 15:07:12 +09:00
225e3e7929 Unit test fixes with permissions
- in case the unit test is run as root, skip test for cannot read (0000)
- set read (0664) for all must read files
- write .env file int all folders for test so that __DIR__ base will
  always find one
2023-01-12 11:42:15 +09:00
44bcb39e51 Delete .gitlab-ci.yml 2023-01-11 08:53:12 +00:00
5729a0c977 Update .gitlab-ci.yml file 2023-01-11 08:49:23 +00:00
9af7790b61 Fix Readme file 2023-01-11 17:15:37 +09:00
0579a075dc Remove bitbucket pipeline, update readme with test tools 2023-01-11 17:14:47 +09:00
58a6d994ca bitbucket pipeline test 2023-01-11 17:06:24 +09:00
233f9fbf81 Dev install phpstan, phan and phplint 2023-01-11 17:00:57 +09:00
8ae06efe4e Remove symlinked .env files 2023-01-11 16:48:28 +09:00
4079d7c66e Merge bitbucket.org:egplusww/code-blocks-dotenv 2023-01-11 16:31:30 +09:00
dd2274f3b1 Auto create .env files 2023-01-11 16:30:51 +09:00
5742314581 Initial Bitbucket Pipelines configuration 2023-01-11 06:46:28 +00:00
17 changed files with 255 additions and 41 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

5
.gitignore vendored
View File

@@ -1,3 +1,8 @@
vendor
.phpunit.result.cache
.phplint-cache
composer.lock
**/.env
**/.target
.phpunit.cache
tools/

View File

@@ -26,6 +26,8 @@
// use Phan\Config;
return [
// turn color on (-C)
"color_issue_messages_if_supported" => true,
// If true, missing properties will be created when
// they are first seen. If false, we'll report an
// error message.
@@ -78,7 +80,8 @@ return [
// to parse, but not analyze
"exclude_analysis_directory_list" => [
'vendor',
'test'
'test',
'tmp'
],
'exclude_file_list' => [
],

9
.phive/phars.xml Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive">
<phar name="phpcs" version="^4.0.1" installed="4.0.1" location="./tools/phpcs" copy="false"/>
<phar name="phpcbf" version="^4.0.1" installed="4.0.1" location="./tools/phpcbf" copy="false"/>
<phar name="phan" version="^5.5.2" installed="5.5.2" location="./tools/phan" copy="false"/>
<phar name="psalm" version="^5.26.1" installed="5.26.1" location="./tools/psalm" copy="false"/>
<phar name="phpstan" version="^2.1.33" installed="2.1.33" location="./tools/phpstan" copy="false"/>
<phar name="phpunit" version="^12.5.4" installed="12.5.4" location="./tools/phpunit" copy="false"/>
</phive>

View File

@@ -60,3 +60,42 @@ ESCAPE="String \" inside \" other "
DOUBLE="I will be used"
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
### Phan
`vendor/bin/phan --analyze-twice`
### PHPstan
`vendor/bin/phpstan`
### PHPUnit
Unit tests have to be run from base folder with
`vendor/bin/phpunit test/phpUnitTests/`

View File

@@ -6,7 +6,8 @@
"license": "MIT",
"autoload": {
"psr-4": {
"gullevek\\dotEnv\\": "src/"
"gullevek\\dotEnv\\": "src/",
"gullevek\\dotenv\\": "src/"
}
},
"authors": [
@@ -25,6 +26,10 @@
"exclude": ["/test/", "/test/*", "/phpstan.neon", "/psalm.xml", "/.phan/", "/.vscode/", "/phpunit.xml"]
},
"require-dev": {
"phpunit/phpunit": "^9"
"phan/phan": "^5.4",
"phpstan/phpdoc-parser": "^2.0",
"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
parameters:
tmpDir: /tmp/phpstan-codeblocks-dotenv
tmpDir: %currentWorkingDirectory%/tmp/phpstan-codeblocks-dotenv
level: max
paths:
- %currentWorkingDirectory%
- %currentWorkingDirectory%/src
excludePaths:
- vendor
- test

View File

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

View File

@@ -22,6 +22,9 @@ class DotEnv
* if there are two variables with the same name only the first is used
* variables are case sensitive
*
* [] Grouping Block Name as prefix until next or end if set,
* space replaced by _, all other var rules apply
*
* @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
@@ -56,10 +59,14 @@ class DotEnv
$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];
$prefix_name = '';
while (($line = fgets($fp)) !== false) {
// [] block must be a single line, or it will be ignored
if (preg_match("/^\s*\[([\w_.\s]+)\]/", $line, $matches)) {
$prefix_name = preg_replace("/\s+/", "_", $matches[1]) . ".";
} elseif (preg_match("/^\s*([\w_.]+)\s*=\s*((\"?).*)/", $line, $matches)) {
// main match for variable = value part
$var = $prefix_name . $matches[1];
$value = $matches[2];
$quotes = $matches[3];
// write only if env is not set yet, and write only the first time
@@ -97,6 +104,9 @@ class DotEnv
// just be sure it is init before we fill
if (!isset($_ENV[$var])) {
$_ENV[$var] = '';
} elseif (!is_string($_ENV[$var])) {
// if this is not string, skip
continue;
}
// strip line of slashes
$_ENV[$var] .= stripslashes($line);

View File

@@ -1 +0,0 @@
phpUnitTests/dotenv/test.env

0
test/env/.gitignore vendored Normal file
View File

View File

@@ -5,20 +5,56 @@ declare(strict_types=1);
namespace tests;
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
* @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
{
/**
* 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
public static function envFileProvider(): array
{
$dot_env_content = [
'SOMETHING' => 'A',
@@ -30,6 +66,8 @@ final class DotEnvTest extends TestCase
'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',
'HAS_EQUAL_NO_QUITES' => 'Is This = Valid',
'HAS_EQUAL_QUITES' => 'Is This = Valid',
'FAILURE' => 'ABC',
'SIMPLEBOX' => 'A B C',
'TITLE' => '1',
@@ -57,6 +95,8 @@ final class DotEnvTest extends TestCase
'__FOOFOO' => 'f ',
123123 => 'number',
'EMPTY' => '',
'Var_Test.TEST' => 'Block 1 D',
'OtherSet.TEST' => 'Block 2 D',
];
// 0: folder relative to test folder, if unset __DIR__
// 1: file, if unset .env
@@ -67,36 +107,39 @@ final class DotEnvTest extends TestCase
'default' => [
'folder' => null,
'file' => null,
'status' => 3,
'content' => [],
'expected_status' => 3,
'expected_env' => [],
'chmod' => null,
],
'cannot open file' => [
'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv',
'file' => 'cannot_read.env',
'status' => 2,
'content' => [],
'chmod' => '000',
'expected_status' => 2,
'expected_env' => [],
// 0000
'chmod' => '100000',
],
'empty file' => [
'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv',
'file' => 'empty.env',
'status' => 1,
'content' => [],
'chmod' => null,
'expected_status' => 1,
'expected_env' => [],
// 0664
'chmod' => '100664',
],
'override all' => [
'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv',
'file' => 'test.env',
'status' => 0,
'content' => $dot_env_content,
'chmod' => null,
'expected_status' => 0,
'expected_env' => $dot_env_content,
// 0664
'chmod' => '100664',
],
'override directory' => [
'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv',
'file' => null,
'status' => 0,
'content' => $dot_env_content,
'expected_status' => 0,
'expected_env' => $dot_env_content,
'chmod' => null,
],
];
@@ -105,10 +148,6 @@ final class DotEnvTest extends TestCase
/**
* 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
@@ -116,6 +155,9 @@ final class DotEnvTest extends TestCase
* @param string|null $chmod
* @return void
*/
#[Test]
#[TestDox('Read _ENV file from $folder / $file with expected status: $expected_status [$_dataName]')]
#[DataProvider('envFileProvider')]
public function testReadEnvFile(
?string $folder,
?string $file,
@@ -123,8 +165,23 @@ final class DotEnvTest extends TestCase
array $expected_env,
?string $chmod
): void {
// if we have file + chmod set
// 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)
@@ -141,14 +198,14 @@ final class DotEnvTest extends TestCase
$status = \gullevek\dotEnv\DotEnv::readEnvFile();
}
$this->assertEquals(
$status,
$expected_status,
$status,
'Assert returned status equal'
);
// now assert read data
$this->assertEquals(
$_ENV,
$expected_env,
$_ENV,
'Assert _ENV correct'
);
// if we have file and chmod unset

View File

@@ -1 +0,0 @@
test.env

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_NO_SPACE=Comment at end no quotes no space# Comment NQES
COMMENT_IN_TEXT_QUOTES="Foo bar # comment in here"
HAS_EQUAL_NO_QUITES=Is This = Valid
HAS_EQUAL_QUITES="Is This = Valid"
FAILURE = ABC
SIMPLEBOX= A B C
TITLE=1
@@ -47,3 +49,10 @@ SUPERLINE=
EMPTY=
= flase
asfasdf
# BLOCK TESTS
[Var Test]
TEST="Block 1 D"
[OtherSet]
TEST="Block 2 D"
[Ignore-Invalid-Block]
TEST="Block 3 D"

View File

@@ -6,12 +6,33 @@ $loader = require '../vendor/autoload.php';
$loader->addPsr4('gullevek\\', __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'src');
use gullevek\dotEnv\DotEnv;
print "BASE: " . __DIR__ . "<br>";
print "ORIG: <pre>" . file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . '.env') . "</pre>";
// copy test file to .env file in env folder
$file_content = __DIR__ . DIRECTORY_SEPARATOR
. 'phpUnitTests' . DIRECTORY_SEPARATOR
. 'dotenv' . DIRECTORY_SEPARATOR
. 'test.env';
// env folder
$env_file = __DIR__ . DIRECTORY_SEPARATOR
. 'env' . DIRECTORY_SEPARATOR
. '.env';
if (!is_file($file_content)) {
die("Cannot read $file_content");
}
if (copy($file_content, $env_file) === false) {
die("Cannot copy $file_content to $env_file");
}
$status = DotEnv::readEnvFile(__DIR__);
print "BASE: " . __DIR__ . "<br>";
print "ENV: " . $env_file . "<br>";
print "ORIG: <pre>" . file_get_contents($env_file) . "</pre>";
$status = DotEnv::readEnvFile(__DIR__ . DIRECTORY_SEPARATOR . 'env');
print "STATUS: " . (string)$status . "<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__

2
tmp/.gitignore vendored Normal file
View File

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