Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8d14445786 | |||
| 979ec79fc0 | |||
| dfe8607934 | |||
| a7ade86485 | |||
| b9feacaded | |||
| 646456b86b | |||
| 4b0550b8d2 | |||
| efb99f259e | |||
| 72f4827985 | |||
| d5bf24c8cf | |||
| 773f40e2d1 | |||
| 0bb137dff6 | |||
| 7e1a19b86b | |||
| 2524092cd8 | |||
| f692ca41b1 | |||
| b9620704bc | |||
| 5004e3c9d8 | |||
| e86528a366 | |||
| e99a995a2e | |||
| e29f9fcd88 | |||
| ae0eb1f939 | |||
| c608201de1 | |||
| 8e062ff114 | |||
| 225e3e7929 | |||
| 44bcb39e51 | |||
| 5729a0c977 | |||
| 9af7790b61 | |||
| 0579a075dc | |||
| 58a6d994ca | |||
| 233f9fbf81 | |||
| 8ae06efe4e | |||
| 4079d7c66e | |||
| dd2274f3b1 | |||
| 5742314581 | |||
| 3668e6552c | |||
| 404f6713f8 | |||
| 97b5a669aa | |||
| 899586ac83 | |||
| e95e0db218 | |||
| 74b0d02a7c | |||
| 0d3e10fe26 | |||
| f8e5cb64ad | |||
| 1389d5c768 | |||
| 2ec6650c99 | |||
| df23a7a1b1 | |||
| a7bca09bf4 | |||
| 652c3554f8 | |||
| 2c592f0289 | |||
| 6ee4358579 | |||
| 135d3d2dd4 | |||
| c0f1d9daf7 | |||
| 1f3ec93fb9 | |||
| 2c7931b7a5 | |||
| 914b6c1da8 | |||
| 69a386d00f | |||
| cdb275ef87 | |||
| c1e35c910c | |||
| c6d4c8d3d0 | |||
| ad59120f52 | |||
| 06479cadea | |||
| b73a24b447 | |||
| 784788e035 | |||
| bded6105ad | |||
| 6cc09fb089 |
6
.gitattributes
vendored
Normal file
6
.gitattributes
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
test/ export-ignore
|
||||||
|
phpstan.neon export-ignore
|
||||||
|
phpunit.xml export-ignore
|
||||||
|
psalm.xml export-ignore
|
||||||
|
.phan/ export-ignore
|
||||||
|
.* export-ignore
|
||||||
35
.github/workflows/ci.yml
vendored
Normal file
35
.github/workflows/ci.yml
vendored
Normal 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
|
||||||
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
vendor
|
||||||
|
.phpunit.result.cache
|
||||||
|
.phplint-cache
|
||||||
|
composer.lock
|
||||||
|
**/.env
|
||||||
|
**/.target
|
||||||
|
.phpunit.cache
|
||||||
98
.phan/config.php
Normal file
98
.phan/config.php
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This configuration will be read and overlaid on top of the
|
||||||
|
* default configuration. Command line arguments will be applied
|
||||||
|
* after this file is read.
|
||||||
|
*
|
||||||
|
* @see src/Phan/Config.php
|
||||||
|
* See Config for all configurable options.
|
||||||
|
*
|
||||||
|
* A Note About Paths
|
||||||
|
* ==================
|
||||||
|
*
|
||||||
|
* Files referenced from this file should be defined as
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* Config::projectPath('relative_path/to/file')
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* where the relative path is relative to the root of the
|
||||||
|
* project which is defined as either the working directory
|
||||||
|
* of the phan executable or a path passed in via the CLI
|
||||||
|
* '-d' flag.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// use Phan\Config;
|
||||||
|
|
||||||
|
return [
|
||||||
|
// If true, missing properties will be created when
|
||||||
|
// they are first seen. If false, we'll report an
|
||||||
|
// error message.
|
||||||
|
"allow_missing_properties" => false,
|
||||||
|
|
||||||
|
// Allow null to be cast as any type and for any
|
||||||
|
// type to be cast to null.
|
||||||
|
"null_casts_as_any_type" => false,
|
||||||
|
|
||||||
|
// Backwards Compatibility Checking
|
||||||
|
'backward_compatibility_checks' => true,
|
||||||
|
|
||||||
|
// Run a quick version of checks that takes less
|
||||||
|
// time
|
||||||
|
"quick_mode" => false,
|
||||||
|
|
||||||
|
// Only emit critical issues to start with
|
||||||
|
// (0 is low severity, 5 is normal severity, 10 is critical)
|
||||||
|
"minimum_severity" => 10,
|
||||||
|
|
||||||
|
// default false for include path check
|
||||||
|
"enable_include_path_checks" => true,
|
||||||
|
"include_paths" => [
|
||||||
|
],
|
||||||
|
'ignore_undeclared_variables_in_global_scope' => true,
|
||||||
|
|
||||||
|
"file_list" => [
|
||||||
|
],
|
||||||
|
|
||||||
|
// A list of directories that should be parsed for class and
|
||||||
|
// method information. After excluding the directories
|
||||||
|
// defined in exclude_analysis_directory_list, the remaining
|
||||||
|
// files will be statically analyzed for errors.
|
||||||
|
//
|
||||||
|
// Thus, both first-party and third-party code being used by
|
||||||
|
// your application should be included in this list.
|
||||||
|
'directory_list' => [
|
||||||
|
// Change this to include the folders you wish to analyze
|
||||||
|
// (and the folders of their dependencies)
|
||||||
|
'.'
|
||||||
|
// 'www',
|
||||||
|
// To speed up analysis, we recommend going back later and
|
||||||
|
// limiting this to only the vendor/ subdirectories your
|
||||||
|
// project depends on.
|
||||||
|
// `phan --init` will generate a list of folders for you
|
||||||
|
],
|
||||||
|
|
||||||
|
|
||||||
|
// A list of directories holding code that we want
|
||||||
|
// to parse, but not analyze
|
||||||
|
"exclude_analysis_directory_list" => [
|
||||||
|
'vendor',
|
||||||
|
'test',
|
||||||
|
'tmp'
|
||||||
|
],
|
||||||
|
'exclude_file_list' => [
|
||||||
|
],
|
||||||
|
|
||||||
|
// what not to show as problem
|
||||||
|
'suppress_issue_types' => [
|
||||||
|
// 'PhanUndeclaredMethod',
|
||||||
|
'PhanEmptyFile',
|
||||||
|
],
|
||||||
|
|
||||||
|
// Override to hardcode existence and types of (non-builtin) globals in the global scope.
|
||||||
|
// Class names should be prefixed with `\`.
|
||||||
|
//
|
||||||
|
// (E.g. `['_FOO' => '\FooClass', 'page' => '\PageClass', 'userId' => 'int']`)
|
||||||
|
'globals_type_map' => [],
|
||||||
|
];
|
||||||
57
Readme.md
57
Readme.md
@@ -1,12 +1,28 @@
|
|||||||
# dotenv: readEnvFile()
|
# dotenv: readEnvFile()
|
||||||
|
|
||||||
A single function implementation of <https://github.com/vlucas/phpdotenv>
|
A simple implementation of <https://github.com/vlucas/phpdotenv>
|
||||||
|
|
||||||
This is not a functional replacement, but a very simple implementation of the basic functions.
|
This is not a functional replacement, but a very simple implementation of the basic functions.
|
||||||
|
|
||||||
It is recommended to create a `.env.example` example file that is checked into the
|
It is recommended to create a `.env.example` example file that is checked into the
|
||||||
repository. The `.env` should *NEVER* be checked into anything
|
repository. The `.env` should *NEVER* be checked into anything
|
||||||
|
|
||||||
|
## How to install
|
||||||
|
|
||||||
|
`composer require gullevek/dotEnv`
|
||||||
|
|
||||||
|
## Run it
|
||||||
|
|
||||||
|
Create a `.env` file in the current folder.
|
||||||
|
Create a file like below
|
||||||
|
|
||||||
|
```php
|
||||||
|
require '../vendor/autoload.php';
|
||||||
|
gullevek\dotEnv\DotEnv::readEnvFile(__DIR__);
|
||||||
|
```
|
||||||
|
|
||||||
|
All data will be in the `$_ENV` array
|
||||||
|
|
||||||
## How it works
|
## How it works
|
||||||
|
|
||||||
Put the function where it is needed or put it in a file and load it.
|
Put the function where it is needed or put it in a file and load it.
|
||||||
@@ -44,3 +60,42 @@ ESCAPE="String \" inside \" other "
|
|||||||
DOUBLE="I will be used"
|
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
|
||||||
|
|
||||||
|
### 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/`
|
||||||
|
|||||||
35
composer.json
Normal file
35
composer.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "gullevek/dotenv",
|
||||||
|
"description": "Simple .env file processing and storing in _ENV array",
|
||||||
|
"keywords": [".env", "dotenv", "_ENV", "environment variables"],
|
||||||
|
"type": "library",
|
||||||
|
"license": "MIT",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"gullevek\\dotEnv\\": "src/",
|
||||||
|
"gullevek\\dotenv\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Clemens Schwaighofer",
|
||||||
|
"email": "gullevek@gullevek.org",
|
||||||
|
"homepage": "http://gullevek.org"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/gullevek/dotEnv",
|
||||||
|
"minimum-stability": "dev",
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.4.0"
|
||||||
|
},
|
||||||
|
"archive": {
|
||||||
|
"exclude": ["/test/", "/test/*", "/phpstan.neon", "/psalm.xml", "/.phan/", "/.vscode/", "/phpunit.xml"]
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"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
18
phpcs.xml
Normal 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>
|
||||||
10
phpstan.neon
Normal file
10
phpstan.neon
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# PHP Stan Config
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
tmpDir: %currentWorkingDirectory%/tmp/phpstan-codeblocks-dotenv
|
||||||
|
level: max
|
||||||
|
paths:
|
||||||
|
- %currentWorkingDirectory%/src
|
||||||
|
excludePaths:
|
||||||
|
- vendor
|
||||||
|
- test
|
||||||
8
phpunit.xml
Normal file
8
phpunit.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<phpunit colors="true" cacheDirectory=".phpunit.cache">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="unit">
|
||||||
|
<directory>test/phpUnitTests/</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
</phpunit>
|
||||||
16
psalm.xml
Normal file
16
psalm.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<psalm
|
||||||
|
errorLevel="8"
|
||||||
|
resolveFromConfigFile="true"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="https://getpsalm.org/schema/config"
|
||||||
|
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||||
|
>
|
||||||
|
<projectFiles>
|
||||||
|
<directory name="." />
|
||||||
|
<ignoreFiles>
|
||||||
|
<directory name="vendor" />
|
||||||
|
<directory name="test" />
|
||||||
|
</ignoreFiles>
|
||||||
|
</projectFiles>
|
||||||
|
</psalm>
|
||||||
120
src/DotEnv.php
Normal file
120
src/DotEnv.php
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace gullevek\dotEnv;
|
||||||
|
|
||||||
|
class DotEnv
|
||||||
|
{
|
||||||
|
/** @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
|
||||||
|
*
|
||||||
|
* [] 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
|
||||||
|
* 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 = '';
|
||||||
|
$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
|
||||||
|
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] = '';
|
||||||
|
} 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__
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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, but no data inside
|
|
||||||
* 2 for file not readable
|
|
||||||
* 3 for file not found
|
|
||||||
*/
|
|
||||||
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 (($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];
|
|
||||||
// wirte 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 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];
|
|
||||||
}
|
|
||||||
// strip line of slashes
|
|
||||||
$_ENV[$var] .= stripslashes($line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose($fp);
|
|
||||||
return $status;
|
|
||||||
}
|
|
||||||
|
|
||||||
// __END__
|
|
||||||
26
test/.env
26
test/.env
@@ -1,26 +0,0 @@
|
|||||||
# enviroment file
|
|
||||||
SOMETHING=A
|
|
||||||
OTHER="B IS B"
|
|
||||||
Complex="A B \"D is F"
|
|
||||||
# COMMENT
|
|
||||||
HAS_SPACE= "ABC";
|
|
||||||
FAILURE = ABC
|
|
||||||
SIMPLEBOX= A B C
|
|
||||||
TITLE=1
|
|
||||||
FOO=1.2
|
|
||||||
Test="A"
|
|
||||||
TEST="B"
|
|
||||||
TEST="D"
|
|
||||||
LINE="ABC
|
|
||||||
DEF"
|
|
||||||
OTHERLINE="ABC
|
|
||||||
AF\"ASFASDF
|
|
||||||
MORESHIT"
|
|
||||||
SUPERLINE=
|
|
||||||
"asfasfasf"
|
|
||||||
__FOO_BAR_1 = b
|
|
||||||
__FOOFOO = f
|
|
||||||
123123=number
|
|
||||||
EMPTY=
|
|
||||||
= flase
|
|
||||||
asfasdf
|
|
||||||
0
test/env/.gitignore
vendored
Normal file
0
test/env/.gitignore
vendored
Normal file
218
test/phpUnitTests/DotEnvTest.php
Normal file
218
test/phpUnitTests/DotEnvTest.php
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
#[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 static 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',
|
||||||
|
'HAS_EQUAL_NO_QUITES' => 'Is This = Valid',
|
||||||
|
'HAS_EQUAL_QUITES' => 'Is This = Valid',
|
||||||
|
'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' => '',
|
||||||
|
'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
|
||||||
|
// 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,
|
||||||
|
'expected_status' => 3,
|
||||||
|
'expected_env' => [],
|
||||||
|
'chmod' => null,
|
||||||
|
],
|
||||||
|
'cannot open file' => [
|
||||||
|
'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv',
|
||||||
|
'file' => 'cannot_read.env',
|
||||||
|
'expected_status' => 2,
|
||||||
|
'expected_env' => [],
|
||||||
|
// 0000
|
||||||
|
'chmod' => '100000',
|
||||||
|
],
|
||||||
|
'empty file' => [
|
||||||
|
'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv',
|
||||||
|
'file' => 'empty.env',
|
||||||
|
'expected_status' => 1,
|
||||||
|
'expected_env' => [],
|
||||||
|
// 0664
|
||||||
|
'chmod' => '100664',
|
||||||
|
],
|
||||||
|
'override all' => [
|
||||||
|
'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv',
|
||||||
|
'file' => 'test.env',
|
||||||
|
'expected_status' => 0,
|
||||||
|
'expected_env' => $dot_env_content,
|
||||||
|
// 0664
|
||||||
|
'chmod' => '100664',
|
||||||
|
],
|
||||||
|
'override directory' => [
|
||||||
|
'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv',
|
||||||
|
'file' => null,
|
||||||
|
'expected_status' => 0,
|
||||||
|
'expected_env' => $dot_env_content,
|
||||||
|
'chmod' => null,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test read .env file
|
||||||
|
*
|
||||||
|
* @param string|null $folder
|
||||||
|
* @param string|null $file
|
||||||
|
* @param int $expected_status
|
||||||
|
* @param array $expected_env
|
||||||
|
* @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,
|
||||||
|
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(
|
||||||
|
$expected_status,
|
||||||
|
$status,
|
||||||
|
'Assert returned status equal'
|
||||||
|
);
|
||||||
|
// now assert read data
|
||||||
|
$this->assertEquals(
|
||||||
|
$expected_env,
|
||||||
|
$_ENV,
|
||||||
|
'Assert _ENV correct'
|
||||||
|
);
|
||||||
|
// if we have file and chmod unset
|
||||||
|
if ($old_chmod !== null) {
|
||||||
|
chmod($folder . DIRECTORY_SEPARATOR . $file, $old_chmod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// __END__
|
||||||
0
test/phpUnitTests/dotenv/cannot_read.env
Normal file
0
test/phpUnitTests/dotenv/cannot_read.env
Normal file
0
test/phpUnitTests/dotenv/empty.env
Normal file
0
test/phpUnitTests/dotenv/empty.env
Normal file
58
test/phpUnitTests/dotenv/test.env
Normal file
58
test/phpUnitTests/dotenv/test.env
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# enviroment file
|
||||||
|
SOMETHING=A
|
||||||
|
OTHER="B IS B"
|
||||||
|
Complex="A B \"D is F"
|
||||||
|
# COMMENT
|
||||||
|
HAS_SPACE= "ABC";
|
||||||
|
# COMMENT AT END
|
||||||
|
HAS_COMMENT_QUOTES_SPACE="Comment at end with quotes and space" # Comment QE
|
||||||
|
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
|
||||||
|
FOO=1.2
|
||||||
|
SOME.TEST=Test Var
|
||||||
|
SOME.LIVE=Live Var
|
||||||
|
# VAR TESTS -
|
||||||
|
A_TEST1 = foo
|
||||||
|
A_TEST2 = ${TEST1:-bar} # TEST1 is set so the value of TEST2 = foo
|
||||||
|
A_TEST3 = ${TEST4:-bar} # TEST4 is not set so the value of TEST3 = bar
|
||||||
|
A_TEST5 = null
|
||||||
|
A_TEST6 = ${TEST5-bar} # TEST5 is set but empty so the value of TEST6 = null
|
||||||
|
A_TEST7 = ${TEST6:-bar} # TEST5 is set and empty so the value of TEST7 = bar
|
||||||
|
# VAR TESTS =
|
||||||
|
B_TEST1 = foo
|
||||||
|
B_TEST2 = ${TEST1:=bar} # TEST1 is set so the value of TEST2 = foo
|
||||||
|
B_TEST3 = ${TEST4:=bar} # TEST4 is not set so the value of TEST3 = bar and TEST4 = bar
|
||||||
|
B_TEST5 = null
|
||||||
|
B_TEST6 = ${TEST5=bar} # TEST5 is set but emtpy so the value of TEST6 = null
|
||||||
|
B_TEST7 = ${TEST6=bar} # TEST5 is set and empty so the value of TEST7 = bar and TEST5 = bar
|
||||||
|
# VAR TEST END
|
||||||
|
Test="A"
|
||||||
|
TEST="B"
|
||||||
|
TEST="D"
|
||||||
|
LINE="ABC
|
||||||
|
DEF"
|
||||||
|
OTHERLINE="ABC
|
||||||
|
AF\"ASFASDF
|
||||||
|
MORESHIT"
|
||||||
|
SUPERLINE=
|
||||||
|
"asfasfasf"
|
||||||
|
__FOO_BAR_1 = b
|
||||||
|
__FOOFOO = f
|
||||||
|
123123=number
|
||||||
|
EMPTY=
|
||||||
|
= flase
|
||||||
|
asfasdf
|
||||||
|
# BLOCK TESTS
|
||||||
|
[Var Test]
|
||||||
|
TEST="Block 1 D"
|
||||||
|
[OtherSet]
|
||||||
|
TEST="Block 2 D"
|
||||||
|
[Ignore-Invalid-Block]
|
||||||
|
TEST="Block 3 D"
|
||||||
@@ -1,15 +1,38 @@
|
|||||||
<?php // phpcs:ignore PSR1.Files.SideEffects
|
<?php
|
||||||
|
|
||||||
// test read .env file
|
// composer auto loader
|
||||||
|
$loader = require '../vendor/autoload.php';
|
||||||
|
// need to add this or it will not load here
|
||||||
|
$loader->addPsr4('gullevek\\', __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'src');
|
||||||
|
use gullevek\dotEnv\DotEnv;
|
||||||
|
|
||||||
require '../src/read_env_file.php';
|
// 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");
|
||||||
|
}
|
||||||
|
|
||||||
print "BASE: " . __DIR__ . "<br>";
|
print "BASE: " . __DIR__ . "<br>";
|
||||||
print "ORIG: <pre>" . file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . '.env') . "</pre>";
|
print "ENV: " . $env_file . "<br>";
|
||||||
|
print "ORIG: <pre>" . file_get_contents($env_file) . "</pre>";
|
||||||
|
|
||||||
$status = readEnvFile(__DIR__);
|
$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
2
tmp/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
Reference in New Issue
Block a user