Full Code of vlucas/phpdotenv for AI

master 2af27192fc6c cached
91 files
208.1 KB
53.6k tokens
391 symbols
1 requests
Download .txt
Showing preview only (229K chars total). Download the full file or copy to clipboard to get everything.
Repository: vlucas/phpdotenv
Branch: master
Commit: 2af27192fc6c
Files: 91
Total size: 208.1 KB

Directory structure:
gitextract_7d43qxaz/

├── .editorconfig
├── .gitattributes
├── .github/
│   ├── CODE_OF_CONDUCT.md
│   ├── CONTRIBUTING.md
│   ├── FUNDING.yml
│   ├── SECURITY.md
│   └── workflows/
│       ├── static.yml
│       └── tests.yml
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── UPGRADING.md
├── composer.json
├── phpstan-baseline.neon
├── phpstan.neon.dist
├── phpunit.xml.dist
├── src/
│   ├── Dotenv.php
│   ├── Exception/
│   │   ├── ExceptionInterface.php
│   │   ├── InvalidEncodingException.php
│   │   ├── InvalidFileException.php
│   │   ├── InvalidPathException.php
│   │   └── ValidationException.php
│   ├── Loader/
│   │   ├── Loader.php
│   │   ├── LoaderInterface.php
│   │   └── Resolver.php
│   ├── Parser/
│   │   ├── Entry.php
│   │   ├── EntryParser.php
│   │   ├── Lexer.php
│   │   ├── Lines.php
│   │   ├── Parser.php
│   │   ├── ParserInterface.php
│   │   └── Value.php
│   ├── Repository/
│   │   ├── Adapter/
│   │   │   ├── AdapterInterface.php
│   │   │   ├── ApacheAdapter.php
│   │   │   ├── ArrayAdapter.php
│   │   │   ├── EnvConstAdapter.php
│   │   │   ├── GuardedWriter.php
│   │   │   ├── ImmutableWriter.php
│   │   │   ├── MultiReader.php
│   │   │   ├── MultiWriter.php
│   │   │   ├── PutenvAdapter.php
│   │   │   ├── ReaderInterface.php
│   │   │   ├── ReplacingWriter.php
│   │   │   ├── ServerConstAdapter.php
│   │   │   └── WriterInterface.php
│   │   ├── AdapterRepository.php
│   │   ├── RepositoryBuilder.php
│   │   └── RepositoryInterface.php
│   ├── Store/
│   │   ├── File/
│   │   │   ├── Paths.php
│   │   │   └── Reader.php
│   │   ├── FileStore.php
│   │   ├── StoreBuilder.php
│   │   ├── StoreInterface.php
│   │   └── StringStore.php
│   ├── Util/
│   │   ├── Regex.php
│   │   └── Str.php
│   └── Validator.php
├── tests/
│   ├── Dotenv/
│   │   ├── DotenvTest.php
│   │   ├── Loader/
│   │   │   └── LoaderTest.php
│   │   ├── Parser/
│   │   │   ├── EntryParserTest.php
│   │   │   ├── LexerTest.php
│   │   │   ├── LinesTest.php
│   │   │   └── ParserTest.php
│   │   ├── Repository/
│   │   │   ├── Adapter/
│   │   │   │   ├── ArrayAdapterTest.php
│   │   │   │   ├── EnvConstAdapterTest.php
│   │   │   │   ├── PutenvAdapterTest.php
│   │   │   │   └── ServerConstAdapterTest.php
│   │   │   └── RepositoryTest.php
│   │   ├── Store/
│   │   │   └── StoreTest.php
│   │   └── ValidatorTest.php
│   └── fixtures/
│       └── env/
│           ├── assertions.env
│           ├── booleans.env
│           ├── commented.env
│           ├── empty.env
│           ├── example.env
│           ├── exported.env
│           ├── immutable.env
│           ├── integers.env
│           ├── large.env
│           ├── multibyte.env
│           ├── multiline.env
│           ├── multiple.env
│           ├── mutable.env
│           ├── nested.env
│           ├── quoted.env
│           ├── specialchars.env
│           ├── unicodevarnames.env
│           ├── utf8-with-bom-encoding.env
│           └── windows.env
└── vendor-bin/
    └── phpstan/
        └── composer.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

[*.{yml,yaml}]
indent_size = 2


================================================
FILE: .gitattributes
================================================
* text=auto

/tests export-ignore
/.editorconfig export-ignore
/.gitattributes export-ignore
/.github export-ignore
/.gitignore export-ignore
/.github export-ignore
/Makefile export-ignore
/phpstan-baseline.neon export-ignore
/phpstan.neon.dist export-ignore
/phpunit.xml.dist export-ignore
/README.md export-ignore
/UPGRADING.md export-ignore
/vendor-bin export-ignore


================================================
FILE: .github/CODE_OF_CONDUCT.md
================================================
# CONTRIBUTOR COVENANT CODE OF CONDUCT

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual identity
and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
  and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
  overall community

Examples of unacceptable behavior include:

* The use of sexualized language or imagery, and sexual attention or
  advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
  address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.

Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
hello@gjcampbell.co.uk.
All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

### 2. Warning

**Community Impact**: A violation through a single incident or series
of actions.

**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.

### 3. Temporary Ban

**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.

**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.

### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior,  harassment of an
individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within
the community.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].

Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].

For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
at [https://www.contributor-covenant.org/translations][translations].

[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations


================================================
FILE: .github/CONTRIBUTING.md
================================================
# CONTRIBUTION GUIDELINES

Contributions are **welcome** and will be fully **credited**.

We accept contributions via pull requests on Github. Please review these guidelines before continuing.

## Guidelines

* Please follow the [PSR-12 Coding Style Guide](https://www.php-fig.org/psr/psr-12/).
* Ensure that the current tests pass, and if you've added something new, add the tests where relevant.
* Send a coherent commit history, making sure each individual commit in your pull request is meaningful.
* You may need to [rebase](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) to avoid merge conflicts.
* If you are changing or adding to the behaviour or public api, you may need to update the docs.
* Please remember that we follow [SemVer](https://semver.org/).

## Running Tests

First, install the dependencies using [Composer](https://getcomposer.org/):

```bash
$ make install
```

Then run [PHPUnit](https://phpunit.de/) and the static analyzers:

```bash
$ make test
```

These will also be automatically run by [GitHub Actions](https://github.com/features/actions) against pull requests.


================================================
FILE: .github/FUNDING.yml
================================================
github: GrahamCampbell
tidelift: "packagist/vlucas/phpdotenv"


================================================
FILE: .github/SECURITY.md
================================================
# SECURITY POLICY

## Supported Versions

After each new major release, the previous release will be supported for no
less than 2 years, unless explicitly stated otherwise. This may mean that there
are multiple supported versions at any given time.

## Reporting a Vulnerability

If you discover a security vulnerability within this package, please send an
email to security@tidelift.com. All security vulnerabilities will be promptly
addressed. Please do not disclose security-related issues publicly until a fix
has been announced.


================================================
FILE: .github/workflows/static.yml
================================================
name: Static Analysis

on:
  push:
  pull_request:

jobs:
  phpstan:
    name: PHPStan
    runs-on: ubuntu-24.04

    steps:
      - name: Checkout code
        uses: actions/checkout@v6

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.5'
          tools: composer:v2
          coverage: none
        env:
          update: true

      - name: Install Dependencies
        uses: nick-fields/retry@v3
        with:
          timeout_minutes: 5
          max_attempts: 5
          command: composer update --no-interaction --no-progress

      - name: Install PHPStan
        uses: nick-fields/retry@v3
        with:
          timeout_minutes: 5
          max_attempts: 5
          command: composer bin phpstan update --no-interaction --no-progress

      - name: Execute PHPStan
        run: vendor/bin/phpstan analyze --no-progress


================================================
FILE: .github/workflows/tests.yml
================================================
name: Tests

on:
  push:
  pull_request:

jobs:
  latest:
    name: PHP ${{ matrix.php }} Latest
    runs-on: ubuntu-24.04

    strategy:
      matrix:
        php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']

    steps:
      - name: Checkout Code
        uses: actions/checkout@v6

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php }}
          tools: composer:v2
          coverage: none

      - name: Setup Problem Matchers
        run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"

      - name: Install Latest Dependencies
        uses: nick-fields/retry@v3
        with:
          timeout_minutes: 5
          max_attempts: 5
          command: composer update --no-interaction --no-progress

      - name: Execute PHPUnit
        run: vendor/bin/phpunit

  lowest:
    name: PHP ${{ matrix.php }} Lowest
    runs-on: ubuntu-24.04

    strategy:
      matrix:
        php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']

    steps:
      - name: Checkout Code
        uses: actions/checkout@v6

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php }}
          tools: composer:v2
          coverage: none

      - name: Setup Problem Matchers
        run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"

      - name: Install Lowest Dependencies
        uses: nick-fields/retry@v3
        with:
          timeout_minutes: 5
          max_attempts: 5
          command: composer update --prefer-lowest --prefer-stable --no-interaction --no-progress

      - name: Execute PHPUnit
        run: vendor/bin/phpunit


================================================
FILE: .gitignore
================================================
.phpunit.result.cache
composer.lock
phpstan.neon
phpstan.tests.neon
phpunit.xml
vendor


================================================
FILE: LICENSE
================================================
BSD 3-Clause License

Copyright (c) 2014, Graham Campbell.
Copyright (c) 2013, Vance Lucas.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
   contributors may be used to endorse or promote products derived from
   this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: Makefile
================================================
install:
	@docker run -it -w /data -v ${PWD}:/data:delegated -v ~/.composer:/root/.composer:delegated --entrypoint composer --rm registry.gitlab.com/grahamcampbell/php:8.5-base update
	@docker run -it -w /data -v ${PWD}:/data:delegated -v ~/.composer:/root/.composer:delegated --entrypoint composer --rm registry.gitlab.com/grahamcampbell/php:8.5-base bin all update

phpunit:
	@docker run -it -w /data -v ${PWD}:/data:delegated --entrypoint vendor/bin/phpunit --rm registry.gitlab.com/grahamcampbell/php:8.5-cli

phpstan-analyze:
	@docker run -it -w /data -v ${PWD}:/data:delegated --entrypoint vendor/bin/phpstan --rm registry.gitlab.com/grahamcampbell/php:8.5-cli analyze

phpstan-baseline:
	@docker run -it -w /data -v ${PWD}:/data:delegated --entrypoint vendor/bin/phpstan --rm registry.gitlab.com/grahamcampbell/php:8.5-cli analyze --generate-baseline

test: phpunit phpstan-analyze

clean:
	@rm -rf .phpunit.result.cache composer.lock vendor vendor-bin/*/composer.lock vendor-bin/*/vendor


================================================
FILE: README.md
================================================
PHP dotenv
==========

Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.

![Banner](https://user-images.githubusercontent.com/2829600/71564012-31105580-2a91-11ea-9ad7-ef1278411b35.png)

<p align="center">
<a href="LICENSE"><img src="https://img.shields.io/badge/license-BSD%203--Clause-brightgreen.svg?style=flat-square" alt="Software License"></img></a>
<a href="https://packagist.org/packages/vlucas/phpdotenv"><img src="https://img.shields.io/packagist/dt/vlucas/phpdotenv.svg?style=flat-square" alt="Total Downloads"></img></a>
<a href="https://github.com/vlucas/phpdotenv/releases"><img src="https://img.shields.io/github/release/vlucas/phpdotenv.svg?style=flat-square" alt="Latest Version"></img></a>
</p>

<div align="center">

**Special thanks to [our sponsors](https://github.com/sponsors/GrahamCampbell)**

<hr>
</div>


## Why .env?

**You should never store sensitive credentials in your code**. Storing
[configuration in the environment](https://www.12factor.net/config) is one of
the tenets of a [twelve-factor app](https://www.12factor.net/). Anything that
is likely to change between deployment environments – such as database
credentials or credentials for 3rd party services – should be extracted from
the code into environment variables.

Basically, a `.env` file is an easy way to load custom configuration variables
that your application needs without having to modify .htaccess files or
Apache/nginx virtual hosts. This means you won't have to edit any files outside
the project, and all the environment variables are always set no matter how you
run your project - Apache, Nginx, CLI, and even PHP's built-in webserver. It's
WAY easier than all the other ways you know of to set environment variables,
and you're going to love it!

* NO editing virtual hosts in Apache or Nginx
* NO adding `php_value` flags to .htaccess files
* EASY portability and sharing of required ENV values
* COMPATIBLE with PHP's built-in web server and CLI runner

PHP dotenv is a PHP version of the original [Ruby
dotenv](https://github.com/bkeepers/dotenv).


## Installation

Installation is super-easy via [Composer](https://getcomposer.org/):

```bash
$ composer require vlucas/phpdotenv
```

or add it by hand to your `composer.json` file.


## Upgrading

We follow [semantic versioning](https://semver.org/), which means breaking
changes may occur between major releases. We have upgrading guides available
for V2 to V3, V3 to V4 and V4 to V5 available [here](UPGRADING.md).


## Usage

The `.env` file is generally kept out of version control since it can contain
sensitive API keys and passwords. A separate `.env.example` file is created
with all the required environment variables defined except for the sensitive
ones, which are either user-supplied for their own development environments or
are communicated elsewhere to project collaborators. The project collaborators
then independently copy the `.env.example` file to a local `.env` and ensure
all the settings are correct for their local environment, filling in the secret
keys or providing their own values when necessary. In this usage, the `.env`
file should be added to the project's `.gitignore` file so that it will never
be committed by collaborators.  This usage ensures that no sensitive passwords
or API keys will ever be in the version control history so there is less risk
of a security breach, and production values will never have to be shared with
all project collaborators.

Add your application configuration to a `.env` file in the root of your
project. **Make sure the `.env` file is added to your `.gitignore` so it is not
checked-in the code**

```shell
S3_BUCKET="dotenv"
SECRET_KEY="souper_seekret_key"
```

Now create a file named `.env.example` and check this into the project. This
should have the ENV variables you need to have set, but the values should
either be blank or filled with dummy data. The idea is to let people know what
variables are required, but not give them the sensitive production values.

```shell
S3_BUCKET="devbucket"
SECRET_KEY="abc123"
```

You can then load `.env` in your application with:

```php
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
```

To suppress the exception that is thrown when there is no `.env` file, you can:

```php
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->safeLoad();
```

Optionally you can pass in a filename as the second parameter, if you would
like to use something other than `.env`:

```php
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__, 'myconfig');
$dotenv->load();
```

All of the defined variables are now available in the `$_ENV` and `$_SERVER`
super-globals.

```php
$s3_bucket = $_ENV['S3_BUCKET'];
$s3_bucket = $_SERVER['S3_BUCKET'];
```


### Putenv and Getenv

Using `getenv()` and `putenv()` is strongly discouraged due to the fact that
these functions are not thread safe, however it is still possible to instruct
PHP dotenv to use these functions. Instead of calling
`Dotenv::createImmutable`, one can call `Dotenv::createUnsafeImmutable`, which
will add the `PutenvAdapter` behind the scenes. Your environment variables will
now be available using the `getenv` method, as well as the super-globals:

```php
$s3_bucket = getenv('S3_BUCKET');
$s3_bucket = $_ENV['S3_BUCKET'];
$s3_bucket = $_SERVER['S3_BUCKET'];
```


### Nesting Variables

It's possible to nest an environment variable within another, useful to cut
down on repetition.

This is done by wrapping an existing environment variable in `${…}` e.g.

```shell
BASE_DIR="/var/webroot/project-root"
CACHE_DIR="${BASE_DIR}/cache"
TMP_DIR="${BASE_DIR}/tmp"
```


### Immutability and Repository Customization

Immutability refers to if Dotenv is allowed to overwrite existing environment
variables. If you want Dotenv to overwrite existing environment variables,
use `createMutable` instead of `createImmutable`:

```php
$dotenv = Dotenv\Dotenv::createMutable(__DIR__);
$dotenv->load();
```

Behind the scenes, this is instructing the "repository" to allow immutability
or not. By default, the repository is configured to allow overwriting existing
values by default, which is relevant if one is calling the "create" method
using the `RepositoryBuilder` to construct a more custom repository:

```php
$repository = Dotenv\Repository\RepositoryBuilder::createWithNoAdapters()
    ->addAdapter(Dotenv\Repository\Adapter\EnvConstAdapter::class)
    ->addWriter(Dotenv\Repository\Adapter\PutenvAdapter::class)
    ->immutable()
    ->make();

$dotenv = Dotenv\Dotenv::create($repository, __DIR__);
$dotenv->load();
```

The above example will write loaded values to `$_ENV` and `putenv`, but when
interpolating environment variables, we'll only read from `$_ENV`. Moreover, it
will never replace any variables already set before loading the file.

By means of another example, one can also specify a set of variables to be
allow listed. That is, only the variables in the allow list will be loaded:

```php
$repository = Dotenv\Repository\RepositoryBuilder::createWithDefaultAdapters()
    ->allowList(['FOO', 'BAR'])
    ->make();

$dotenv = Dotenv\Dotenv::create($repository, __DIR__);
$dotenv->load();
```


### Requiring Variables to be Set

PHP dotenv has built in validation functionality, including for enforcing the
presence of an environment variable. This is particularly useful to let people
know any explicit required variables that your app will not work without.

You can use a single string:

```php
$dotenv->required('DATABASE_DSN');
```

Or an array of strings:

```php
$dotenv->required(['DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASS']);
```

If any ENV vars are missing, Dotenv will throw a `RuntimeException` like this:

```
One or more environment variables failed assertions: DATABASE_DSN is missing
```


### Empty Variables

Beyond simply requiring a variable to be set, you might also need to ensure the
variable is not empty:

```php
$dotenv->required('DATABASE_DSN')->notEmpty();
```

If the environment variable is empty, you'd get an Exception:

```
One or more environment variables failed assertions: DATABASE_DSN is empty
```


### Integer Variables

You might also need to ensure that the variable is of an integer value. You may
do the following:

```php
$dotenv->required('FOO')->isInteger();
```

If the environment variable is not an integer, you'd get an Exception:

```
One or more environment variables failed assertions: FOO is not an integer.
```

One may only want to enforce validation rules when a variable is set. We
support this too:

```php
$dotenv->ifPresent('FOO')->isInteger();
```


### Boolean Variables

You may need to ensure a variable is in the form of a boolean, accepting
"true", "false", "On", "1", "Yes", "Off", "0" and "No". You may do the
following:

```php
$dotenv->required('FOO')->isBoolean();
```

If the environment variable is not a boolean, you'd get an Exception:

```
One or more environment variables failed assertions: FOO is not a boolean.
```

Similarly, one may write:

```php
$dotenv->ifPresent('FOO')->isBoolean();
```


### Allowed Values

It is also possible to define a set of values that your environment variable
should be. This is especially useful in situations where only a handful of
options or drivers are actually supported by your code:

```php
$dotenv->required('SESSION_STORE')->allowedValues(['Filesystem', 'Memcached']);
```

If the environment variable wasn't in this list of allowed values, you'd get a
similar Exception:

```
One or more environment variables failed assertions: SESSION_STORE is not an allowed value.
```

It is also possible to define a regex that your environment variable should be.
```php
$dotenv->required('FOO')->allowedRegexValues('([[:lower:]]{3})');
```


### Comments

You can comment your `.env` file using the `#` character. E.g.

```shell
# this is a comment
VAR="value" # comment
VAR=value # comment
```


### Parsing Without Loading

Sometimes you just wanna parse the file and resolve the nested environment variables, by giving us a string, and have an array returned back to you. While this is already possible, it is a little fiddly, so we have provided a direct way to do this:

```php
// ['FOO' => 'Bar', 'BAZ' => 'Hello Bar']
Dotenv\Dotenv::parse("FOO=Bar\nBAZ=\"Hello \${FOO}\"");
```

This is exactly the same as:

```php
Dotenv\Dotenv::createArrayBacked(__DIR__)->load();
```

only, instead of providing the directory to find the file, you have directly provided the file contents.


### Usage Notes

When a new developer clones your codebase, they will have an additional
one-time step to manually copy the `.env.example` file to `.env` and fill-in
their own values (or get any sensitive values from a project co-worker).


### Troubleshooting

In certain server setups (most commonly found in shared hosting), PHP might deactivate superglobals like `$_ENV` or `$_SERVER`. If these variables are not set, review the `variables_order` in the `php.ini` file. See [php.net/manual/en/ini.core.php#ini.variables-order](https://www.php.net/manual/en/ini.core.php#ini.variables-order).

## Security

If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. You may view our full security policy [here](https://github.com/vlucas/phpdotenv/security/policy).


## License

PHP dotenv is licensed under [The BSD 3-Clause License](LICENSE).


## For Enterprise

Available as part of the Tidelift Subscription

The maintainers of `vlucas/phpdotenv` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-vlucas-phpdotenv?utm_source=packagist-vlucas-phpdotenv&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)


================================================
FILE: UPGRADING.md
================================================
# Upgrading Guide

## V5.5 to V5.6

Bumping the minimum required PHP version is not a breaking change, however it is notable. Since version 5.6.0, we now require PHP 7.2.5 or higher. Installation metrics show that for some time, PHP 7.1 has represented only around 0.1% of installs of V5.

Release notes for 5.6.0 are available [here](https://github.com/vlucas/phpdotenv/releases/tag/v5.6.0).

## V4 to V5

### Introduction

Version 5 bumps to PHP 7.1+, and adds some additional parameter typing. There have been some internal changes and refactorings too, but nothing that changes the overall feel and usage of the package. The Dotenv class itself is largely unchanged from V4.

Release notes for 5.0.0 are available [here](https://github.com/vlucas/phpdotenv/releases/tag/v5.0.0).

### Details

1. The `Dotenv\Dotenv::createImmutable` and `Dotenv\Dotenv::createMutable` methods no longer call will result in `getenv` and `putenv` being called. One should instead use `Dotenv\Dotenv::createUnsafeImmutable` and `Dotenv\Dotenv::createUnsafeMutable` methods, if one really needs these functions.
2. The `Dotenv\Dotenv` constructor has been modified to expect exactly 4 parameters: a store, a parser, a loader, and a repository. This likely won't affect many people, since it is more common to construct this class via the public static create methods. Those methods have not changed.
3. Scalar typehints have been added to the public interface.
4. The parser now returns a result type instead of raising an exception. This change is strictly internal, and most users won't notice a difference. The responsibility for raising an exception has simply been shifted up to the caller.
5. Adapters have been refactored again, with changes to the repositories. In particular, the repository builder has been tweaked. It now expects to be explicitly told if you want to use the default adapters or not, and expects individual readers and writers to be added, one by one. Similar changes have been applied to the store factory. Moreover, the `ApacheAdapter` has been changed so that it behaves much like the other adapters. The old behaviour can be simulated by composing it with the new `ReplacingWriter` (see below). We will no longer include this adapter in our default setup, so that people can enable exactly what they need. Finally, by default, we will no longer be using the `PutenvAdapter`. It can be added, as required.
6. Variable whitelisting has been replaced with allow listing, and the responsibility has moved from the loader to a new adapter `GuardedWriter`.
7. The parser has been moved to its own namespace and parses entire files now. This change is expected to have little impact when upgrading. The `Lines` class has also moved to the parser namespace.
8. The loader now only returns the variables that were actually loaded into the repository, and not all the variables from the file. Moreover, it now expects as input the result of running the new parser (an array of entries), rather than raw file content.

The changes listed in (4) mean that instead of:

```php
$repository = Dotenv\Repository\RepositoryBuilder::create()
    ->withReaders([
        new Dotenv\Repository\Adapter\EnvConstAdapter(),
    ])
    ->withWriters([
        new Dotenv\Repository\Adapter\EnvConstAdapter(),
        new Dotenv\Repository\Adapter\PutenvAdapter(),
    ])
    ->make();
```

one would now write:

```php
$repository = Dotenv\Repository\RepositoryBuilder::createWithNoAdapters()
    ->addAdapter(Dotenv\Repository\Adapter\EnvConstAdapter::class)
    ->addWriter(Dotenv\Repository\Adapter\PutenvAdapter::class)
    ->make();
```

Instead of passing class names, one can also pass actual adapter instances. Note that it is not possible to directly construct any of the adapters. One has to go via their static `create` method which returns an optional. This is to strictly encapsulate the fact that not all adapters are capable of running on all systems, and so those that cannot be run, cannot be created. For example, the apache adapter can only be run within an apache web server context. Passing the class names as in the above example will handle this for you, by adding the adapter only if it can be created (the optional has a value set).

To add an apache environment variable writer that only writes to existing apache environment variables, as was the default in v4, one should do the following:

```php
$builder = Dotenv\Repository\RepositoryBuilder::createWithDefaultAdapters();

Dotenv\Repository\Adapter\ApacheAdapter::create()->map(function ($adapter) {
    return new Dotenv\Repository\Adapter\ReplacingWriter($adapter, $adapter);
})->map([$builder, 'addWriter'])->getOrElse($builder);

$repository = $builder->make();
```

The use of optionals handles the case where the apache environment functions are not available (such as in a CLI environment).

## V4.0 to V4.1

### Introduction

Version 4.1 is a minor release, and as such, there are no breaking changes. There is, however a deprecation to be noted.

### Details

The `Dotenv\Dotenv` constructor now expects either an array of file paths as the third parameter, or an instance of `Dotenv\Store\StoreInterface`. Passing an array is deprecated, and will be removed in V5.

## V3 to V4

### Introduction

Version 4 sees some refactoring, and support for escaping dollars in values (https://github.com/vlucas/phpdotenv/pull/380). It is no longer possible to change immutability on the fly, and the `Loader` no longer is responsible for tracking immutability. It is now the responsibility of "repositories" to track this. One must explicitly decide if they want (im)mutability when constructing an instance of `Dotenv\Dotenv`.

Release notes for 4.0.0 are available [here](https://github.com/vlucas/phpdotenv/releases/tag/v4.0.0).

### Details

V4 has again changed the way you initialize the `Dotenv` class. If you want immutable loading of environment variables, then replace `Dotenv::create` with `Dotenv::createImmutable`, and if you want mutable loading, replace `Dotenv::create` with `Dotenv::createMutable` and `->overload()` with `->load()`. The `overload` method has been removed in favour of specifying mutability at object construction.

The behaviour when parsing single quoted strings has now changed, to mimic the behaviour of bash. It is no longer possible to escape characters in single quoted strings, and everything is treated literally. As soon as the first single quote character is read, after the initial one, then the variable is treated as ending immediately at that point. When parsing unquoted or double quoted strings, it is now possible to escape dollar signs, to forcefully avoid variable interpolation. Escaping dollars is not mandated, in the sense that if a dollar is present, and not following by variable interpolation syntax, this is allowed, and the dollar will be treated as a literal dollar. Finally, interpolation of variables is now performed right to left, instead of left to right, so it is possible to nest interpolations to allow using the value of a variable as the name of another for further interpolation.

The `getEnvironmentVariableNames` method is no longer available. This is because calls to `load()` (since v3.0.0) return an associative array of what was loaded, so `$dotenv->getEnvironmentVariableNames()` can be replaced with `array_keys($dotenv->load())`.

There have been various internal refactorings. Apart from what has already been mentioned, the only other changes likely to affect developers is:

1. The `Dotenv\Environment` namespace has been moved to `Dotenv\Repository`, the `Dotenv\Environment\Adapter\AdapterInterface` interface has been replaced by `Dotenv\Repository\Adapter\ReaderInterface` and `Dotenv\Repository\Adapter\WriterInterface`.
2. The `Dotenv\Environment\DotenvFactory` has been (roughly) replaced by `Dotenv\Repository\RepositoryBuilder`, and `Dotenv\Environment\FactoryInterface` has been deleted.
3. `Dotenv\Environment\AbstractVariables` has been replaced by `Dotenv\Repository\AbstractRepository`, `Dotenv\Environment\DotenvVariables` has been replaced by `Dotenv\Repository\AdapterRepository`, and `Dotenv\Environment\VariablesInterface` has been replaced by `Dotenv\Repository\RepositoryInterface`.
4. The `Dotenv\Loader` class has been moved to `Dotenv\Loader\Loader`, and now has a different public interface. It no longer expects any parameters at construction, and implements only the new interface `Dotenv\Loader\LoaderInterface`. Its responsibility has changed to purely taking raw env file content, and handing it off to the parser, dealing with variable interpolation, and sending off instructions to the repository to set variables. No longer can it be used as a way to read the environment by callers, and nor does it track immutability.
5. The `Dotenv\Parser` and `Dotenv\Lines` classes have moved to `Dotenv\Loader\Parser` and `Dotenv\Loader\Lines`, respectively. `Dotenv\Loader\Parser::parse` now return has either `null` or `Dotenv\Loader\Value` objects as values, instead of `string`s. This is to support the new variable interpolation and dollar escaping features.
6. The `Dotenv\Validator` constructor has changed from `__construct(array $variables, Loader $loader, $required = true)` to `__construct(RepositoryInterface $repository, array $variables, $required = true)`.

The example at the bottom of the below upgrading guide, in V4 now looks like:

```php
<?php

use Dotenv\Dotenv;
use Dotenv\Repository\Adapter\EnvConstAdapter;
use Dotenv\Repository\Adapter\ServerConstAdapter;
use Dotenv\Repository\RepositoryBuilder;

$adapters = [
	new EnvConstAdapter(),
	new ServerConstAdapter(),
];

$repository = RepositoryBuilder::create()
    ->withReaders($adapters)
    ->withWriters($adapters)
    ->immutable()
    ->make();

Dotenv::create($repository, $path, null)->load();
```

Since v3.2.0, it was easily possible to read a file and process variable interpolations, without actually "loading" the variables. This is still possible in v4.0.0. Example code that does this is as follows:

```php
<?php

use Dotenv\Repository\Adapter\ArrayAdapter;
use Dotenv\Repository\RepositoryBuilder;
use Dotenv\Loader\Loader;

$adapters = [new ArrayAdapter()];

$repository = RepositoryBuilder::create()
    ->withReaders($adapters)
    ->withWriters($adapters)
    ->make();

$variables = (new Loader())->load($repository, $content);
```

Notice, that compared to v3, the loader no longer expects file paths in the constructor. Reading of the files is now managed by the `Dotenv\Dotenv` class. The loader is genuinely just loading the content into the repository.

Finally, we note that the minimum supported version of PHP has increased to 5.5.9, up from 5.4.0 in V3 and 5.3.9 in V2.

## V2 to V3

### Introduction

New in Version 3 is first-class support for multiline variables ([#301](https://github.com/vlucas/phpdotenv/pull/301)) and much more flexibility in terms of which parts of the environment we try to read and modify ([#300](https://github.com/vlucas/phpdotenv/pull/300)). Consequently, you will need to replace any occurrences of `new Dotenv(...)` with `Dotenv::create(...)`, since our new native constructor takes a `Loader` instance now, so that it can be truly customized if required. Finally, one should note that the loader will no longer be trimming values ([#302](https://github.com/vlucas/phpdotenv/pull/302)), moreover `Loader::load()` and its callers now return an associative array of the variables loaded with their values, rather than an array of raw lines from the environment file ([#306](https://github.com/vlucas/phpdotenv/pull/306)).

Release notes for 3.0.0 are available [here](https://github.com/vlucas/phpdotenv/releases/tag/v3.0.0).

### Details

V3 has changed the way you initialize the `Dotenv` class. Consequently, you will need to replace any occurrences of new Dotenv(...) with Dotenv::create(...), since our new native constructor takes a `Loader` instance now.

`Loader::load()` and its callers now return an associative array of the variables loaded with their values.

Value parsing has been modified in the following ways:

1. For unquoted strings, as soon as there's a hash, it's treated as a comment start.
2. We're being stricter about invalid escape sequences within quoted strings.
3. We're no longer trimming the parsed values of quoted strings.
4. Multiline quoted values are now permitted, and will be parsed by V3.

| input value | V2.5.2 | V2.6.1 | V3.3.1 |
|-|-|-|-|
| `foo#bar` | `foo#bar` | `foo#bar` | `foo` |
| `foo # bar` | `foo` | `foo` | `foo` |
| `"iiiiviiiixiiiiviiii\n"` | silent failure | `iiiviiiixiiiiviiii\n` | fails with invalid escape sequence exception |
| `"iiiiviiiixiiiiviiii\\n"` | `iiiiviiiixiiiiviiii\n` | `iiiiviiiixiiiiviiii\n` | `iiiiviiiixiiiiviiii\n` |
| `"foo\"bar"` | `foo"bar` | `foo"bar` | `foo"bar` |
| `"  foo "` | `foo` with whitespace trimmed | `foo` with whitespace trimmed | `foo` with 2 spaces in front and one after |

In double quoted strings, double quotes and backslashes need escaping with a backslash, and in single quoted strings, single quote and backslashes need escaping with a backslash. In v2.5.2, forgetting an escape can lead to odd results due to the regex running out of stack, but this was fixed in 2.6 and 3.3, with 2.6 allowing you to continue after an unescaped backslash, but 3.3 not.

It's possible to use phpdotenv V3 in a threaded environment, instructing it to not call any functions that are not tread-safe:

```php
<?php

use Dotenv\Dotenv;
use Dotenv\Environment\Adapter\EnvConstAdapter;
use Dotenv\Environment\Adapter\ServerConstAdapter;
use Dotenv\Environment\DotenvFactory;

$factory = new DotenvFactory([new EnvConstAdapter(), new ServerConstAdapter()]);

Dotenv::create($path, null, $factory)->load();
```

Finally, we note that the minimum supported version of PHP has increased from 5.3.9 to 5.4.0.


================================================
FILE: composer.json
================================================
{
    "name": "vlucas/phpdotenv",
    "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
    "keywords": ["env", "dotenv", "environment"],
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Graham Campbell",
            "email": "hello@gjcampbell.co.uk",
            "homepage": "https://github.com/GrahamCampbell"
        },
        {
            "name": "Vance Lucas",
            "email": "vance@vancelucas.com",
            "homepage": "https://github.com/vlucas"
        }
    ],
    "require": {
        "php": "^7.2.5 || ^8.0",
        "ext-pcre": "*",
        "graham-campbell/result-type": "^1.1.4",
        "phpoption/phpoption": "^1.9.5",
        "symfony/polyfill-ctype": "^1.26",
        "symfony/polyfill-mbstring": "^1.26",
        "symfony/polyfill-php80": "^1.26"
    },
    "require-dev": {
        "ext-filter": "*",
        "bamarni/composer-bin-plugin": "^1.8.2",
        "phpunit/phpunit":"^8.5.34 || ^9.6.13 || ^10.4.2"
    },
    "autoload": {
        "psr-4": {
            "Dotenv\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Dotenv\\Tests\\": "tests/Dotenv/"
        }
    },
    "suggest": {
        "ext-filter": "Required to use the boolean validator."
    },
    "config": {
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        },
        "preferred-install": "dist"
    },
    "extra": {
        "bamarni-bin": {
            "bin-links": true,
            "forward-command": false
        },
        "branch-alias": {
            "dev-master": "5.6-dev"
        }
    }
}


================================================
FILE: phpstan-baseline.neon
================================================
parameters:
	ignoreErrors:
		-
			message: '#^PHPDoc tag @var with type PhpOption\\Option\<Dotenv\\Parser\\Value\> is not subtype of type PhpOption\\Option\<Dotenv\\Parser\\Value\|null\>\.$#'
			identifier: varTag.type
			count: 1
			path: src/Parser/Entry.php

		-
			message: '#^Anonymous function should return GrahamCampbell\\ResultType\\Result\<mixed, string\> but returns GrahamCampbell\\ResultType\\Result\<Dotenv\\Parser\\Entry, string\>\.$#'
			identifier: return.type
			count: 1
			path: src/Parser/EntryParser.php

		-
			message: '#^Method Dotenv\\Parser\\EntryParser\:\:parse\(\) should return GrahamCampbell\\ResultType\\Result\<Dotenv\\Parser\\Entry, string\> but returns GrahamCampbell\\ResultType\\Result\<mixed, string\>\.$#'
			identifier: return.type
			count: 1
			path: src/Parser/EntryParser.php

		-
			message: '#^PHPDoc tag @var with type GrahamCampbell\\ResultType\\Result\<Dotenv\\Parser\\Value\|null, string\> is not subtype of type GrahamCampbell\\ResultType\\Result\<Dotenv\\Parser\\Value, string\>\|GrahamCampbell\\ResultType\\Result\<null, mixed\>\.$#'
			identifier: varTag.type
			count: 1
			path: src/Parser/EntryParser.php

		-
			message: '#^Parameter \#2 \$callback of function array_reduce expects callable\(GrahamCampbell\\ResultType\\Result\<array\{Dotenv\\Parser\\Value, int\}, mixed\>\|GrahamCampbell\\ResultType\\Result\<array\{mixed, int\}, string\>, string\)\: \(GrahamCampbell\\ResultType\\Result\<array\{Dotenv\\Parser\\Value, int\}, mixed\>\|GrahamCampbell\\ResultType\\Result\<array\{mixed, int\}, string\>\), Closure\(GrahamCampbell\\ResultType\\Result, string\)\: GrahamCampbell\\ResultType\\Result\<array\{Dotenv\\Parser\\Value, int\}, string\> given\.$#'
			identifier: argument.type
			count: 1
			path: src/Parser/EntryParser.php

		-
			message: '#^Only booleans are allowed in a negated boolean, int\|false given\.$#'
			identifier: booleanNot.exprNotBoolean
			count: 1
			path: src/Parser/Lexer.php

		-
			message: '#^Parameter \#1 \$pattern of function preg_match expects string, mixed given\.$#'
			identifier: argument.type
			count: 1
			path: src/Parser/Lexer.php

		-
			message: '#^PHPDoc tag @var with type PhpOption\\Option\<Dotenv\\Repository\\Adapter\\AdapterInterface\> is not subtype of type PhpOption\\Some\<Dotenv\\Repository\\Adapter\\ApacheAdapter\>\.$#'
			identifier: varTag.type
			count: 1
			path: src/Repository/Adapter/ApacheAdapter.php

		-
			message: '#^PHPDoc tag @var with type PhpOption\\Option\<string\> is not subtype of type PhpOption\\Option\<string\|false\|null\>\.$#'
			identifier: varTag.type
			count: 1
			path: src/Repository/Adapter/ApacheAdapter.php

		-
			message: '#^PHPDoc tag @var with type PhpOption\\Option\<Dotenv\\Repository\\Adapter\\AdapterInterface\> is not subtype of type PhpOption\\Some\<Dotenv\\Repository\\Adapter\\ArrayAdapter\>\.$#'
			identifier: varTag.type
			count: 1
			path: src/Repository/Adapter/ArrayAdapter.php

		-
			message: '#^Cannot cast mixed to string\.$#'
			identifier: cast.string
			count: 1
			path: src/Repository/Adapter/EnvConstAdapter.php

		-
			message: '#^PHPDoc tag @var with type PhpOption\\Option\<Dotenv\\Repository\\Adapter\\AdapterInterface\> is not subtype of type PhpOption\\Some\<Dotenv\\Repository\\Adapter\\EnvConstAdapter\>\.$#'
			identifier: varTag.type
			count: 1
			path: src/Repository/Adapter/EnvConstAdapter.php

		-
			message: '#^PHPDoc tag @var with type PhpOption\\Option\<Dotenv\\Repository\\Adapter\\AdapterInterface\> is not subtype of type PhpOption\\Some\<Dotenv\\Repository\\Adapter\\PutenvAdapter\>\.$#'
			identifier: varTag.type
			count: 1
			path: src/Repository/Adapter/PutenvAdapter.php

		-
			message: '#^PHPDoc tag @var with type PhpOption\\Option\<string\> is not subtype of type PhpOption\\Option\<string\|false\>\.$#'
			identifier: varTag.type
			count: 1
			path: src/Repository/Adapter/PutenvAdapter.php

		-
			message: '#^Cannot cast mixed to string\.$#'
			identifier: cast.string
			count: 1
			path: src/Repository/Adapter/ServerConstAdapter.php

		-
			message: '#^PHPDoc tag @var with type PhpOption\\Option\<Dotenv\\Repository\\Adapter\\AdapterInterface\> is not subtype of type PhpOption\\Some\<Dotenv\\Repository\\Adapter\\ServerConstAdapter\>\.$#'
			identifier: varTag.type
			count: 1
			path: src/Repository/Adapter/ServerConstAdapter.php

		-
			message: '#^Parameter \#1 \$callable of method PhpOption\\Some\<Dotenv\\Repository\\Adapter\\AdapterInterface\|string\>\:\:flatMap\(\) expects callable\(Dotenv\\Repository\\Adapter\\AdapterInterface\|string\)\: PhpOption\\Option\<S\>, Closure\(mixed\)\: mixed given\.$#'
			identifier: argument.type
			count: 1
			path: src/Repository/RepositoryBuilder.php

		-
			message: '#^Parameter \#1 \$callable of method PhpOption\\Some\<Dotenv\\Repository\\Adapter\\ReaderInterface\|string\>\:\:flatMap\(\) expects callable\(Dotenv\\Repository\\Adapter\\ReaderInterface\|string\)\: PhpOption\\Option\<S\>, Closure\(mixed\)\: mixed given\.$#'
			identifier: argument.type
			count: 1
			path: src/Repository/RepositoryBuilder.php

		-
			message: '#^Parameter \#1 \$callable of method PhpOption\\Some\<Dotenv\\Repository\\Adapter\\WriterInterface\|string\>\:\:flatMap\(\) expects callable\(Dotenv\\Repository\\Adapter\\WriterInterface\|string\)\: PhpOption\\Option\<S\>, Closure\(mixed\)\: mixed given\.$#'
			identifier: argument.type
			count: 1
			path: src/Repository/RepositoryBuilder.php

		-
			message: '#^Parameter \#1 \$readers of class Dotenv\\Repository\\RepositoryBuilder constructor expects array\<Dotenv\\Repository\\Adapter\\ReaderInterface\>, array\<Dotenv\\Repository\\Adapter\\ReaderInterface\|S\> given\.$#'
			identifier: argument.type
			count: 2
			path: src/Repository/RepositoryBuilder.php

		-
			message: '#^Parameter \#2 \$writers of class Dotenv\\Repository\\RepositoryBuilder constructor expects array\<Dotenv\\Repository\\Adapter\\WriterInterface\>, array\<Dotenv\\Repository\\Adapter\\WriterInterface\|S\> given\.$#'
			identifier: argument.type
			count: 2
			path: src/Repository/RepositoryBuilder.php

		-
			message: '#^PHPDoc tag @var with type PhpOption\\Option\<string\> is not subtype of type PhpOption\\Option\<string\|false\>\.$#'
			identifier: varTag.type
			count: 1
			path: src/Store/File/Reader.php

		-
			message: '#^Method Dotenv\\Util\\Regex\:\:occurrences\(\) should return GrahamCampbell\\ResultType\\Result\<int, string\> but returns GrahamCampbell\\ResultType\\Result\<int\<0, max\>, string\>\.$#'
			identifier: return.type
			count: 1
			path: src/Util/Regex.php

		-
			message: '#^Loose comparison via "\=\=" is not allowed\.$#'
			identifier: equal.notAllowed
			count: 1
			path: src/Util/Str.php

		-
			message: '#^PHPDoc tag @var with type GrahamCampbell\\ResultType\\Result\<string, string\> is not subtype of type GrahamCampbell\\ResultType\\Result\<mixed, non\-falsy\-string\>\.$#'
			identifier: varTag.type
			count: 2
			path: src/Util/Str.php

		-
			message: '#^PHPDoc tag @var with type PhpOption\\Option\<int\> is not subtype of type PhpOption\\Option\<int\<0, max\>\|false\>\.$#'
			identifier: varTag.type
			count: 1
			path: src/Util/Str.php


================================================
FILE: phpstan.neon.dist
================================================
includes:
    - phpstan-baseline.neon

parameters:
    level: max
    paths:
        - src


================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" beStrictAboutTestsThatDoNotTestAnything="true" beStrictAboutOutputDuringTests="true" bootstrap="vendor/autoload.php" colors="true" failOnRisky="true" failOnWarning="true" processIsolation="false" stopOnError="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.4/phpunit.xsd">
  <testsuites>
    <testsuite name="PHP Dotenv Test Suite">
      <directory suffix="Test.php">./tests</directory>
    </testsuite>
  </testsuites>
  <source>
    <include>
      <directory suffix=".php">./src</directory>
    </include>
  </source>
</phpunit>


================================================
FILE: src/Dotenv.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv;

use Dotenv\Exception\InvalidPathException;
use Dotenv\Loader\Loader;
use Dotenv\Loader\LoaderInterface;
use Dotenv\Parser\Parser;
use Dotenv\Parser\ParserInterface;
use Dotenv\Repository\Adapter\ArrayAdapter;
use Dotenv\Repository\Adapter\PutenvAdapter;
use Dotenv\Repository\RepositoryBuilder;
use Dotenv\Repository\RepositoryInterface;
use Dotenv\Store\StoreBuilder;
use Dotenv\Store\StoreInterface;
use Dotenv\Store\StringStore;

class Dotenv
{
    /**
     * The store instance.
     *
     * @var \Dotenv\Store\StoreInterface
     */
    private $store;

    /**
     * The parser instance.
     *
     * @var \Dotenv\Parser\ParserInterface
     */
    private $parser;

    /**
     * The loader instance.
     *
     * @var \Dotenv\Loader\LoaderInterface
     */
    private $loader;

    /**
     * The repository instance.
     *
     * @var \Dotenv\Repository\RepositoryInterface
     */
    private $repository;

    /**
     * Create a new dotenv instance.
     *
     * @param \Dotenv\Store\StoreInterface           $store
     * @param \Dotenv\Parser\ParserInterface         $parser
     * @param \Dotenv\Loader\LoaderInterface         $loader
     * @param \Dotenv\Repository\RepositoryInterface $repository
     *
     * @return void
     */
    public function __construct(
        StoreInterface $store,
        ParserInterface $parser,
        LoaderInterface $loader,
        RepositoryInterface $repository
    ) {
        $this->store = $store;
        $this->parser = $parser;
        $this->loader = $loader;
        $this->repository = $repository;
    }

    /**
     * Create a new dotenv instance.
     *
     * @param \Dotenv\Repository\RepositoryInterface $repository
     * @param string|string[]                        $paths
     * @param string|string[]|null                   $names
     * @param bool                                   $shortCircuit
     * @param string|null                            $fileEncoding
     *
     * @return \Dotenv\Dotenv
     */
    public static function create(RepositoryInterface $repository, $paths, $names = null, bool $shortCircuit = true, ?string $fileEncoding = null)
    {
        $builder = $names === null ? StoreBuilder::createWithDefaultName() : StoreBuilder::createWithNoNames();

        foreach ((array) $paths as $path) {
            $builder = $builder->addPath($path);
        }

        foreach ((array) $names as $name) {
            $builder = $builder->addName($name);
        }

        if ($shortCircuit) {
            $builder = $builder->shortCircuit();
        }

        return new self($builder->fileEncoding($fileEncoding)->make(), new Parser(), new Loader(), $repository);
    }

    /**
     * Create a new mutable dotenv instance with default repository.
     *
     * @param string|string[]      $paths
     * @param string|string[]|null $names
     * @param bool                 $shortCircuit
     * @param string|null          $fileEncoding
     *
     * @return \Dotenv\Dotenv
     */
    public static function createMutable($paths, $names = null, bool $shortCircuit = true, ?string $fileEncoding = null)
    {
        $repository = RepositoryBuilder::createWithDefaultAdapters()->make();

        return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding);
    }

    /**
     * Create a new mutable dotenv instance with default repository with the putenv adapter.
     *
     * @param string|string[]      $paths
     * @param string|string[]|null $names
     * @param bool                 $shortCircuit
     * @param string|null          $fileEncoding
     *
     * @return \Dotenv\Dotenv
     */
    public static function createUnsafeMutable($paths, $names = null, bool $shortCircuit = true, ?string $fileEncoding = null)
    {
        $repository = RepositoryBuilder::createWithDefaultAdapters()
            ->addAdapter(PutenvAdapter::class)
            ->make();

        return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding);
    }

    /**
     * Create a new immutable dotenv instance with default repository.
     *
     * @param string|string[]      $paths
     * @param string|string[]|null $names
     * @param bool                 $shortCircuit
     * @param string|null          $fileEncoding
     *
     * @return \Dotenv\Dotenv
     */
    public static function createImmutable($paths, $names = null, bool $shortCircuit = true, ?string $fileEncoding = null)
    {
        $repository = RepositoryBuilder::createWithDefaultAdapters()->immutable()->make();

        return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding);
    }

    /**
     * Create a new immutable dotenv instance with default repository with the putenv adapter.
     *
     * @param string|string[]      $paths
     * @param string|string[]|null $names
     * @param bool                 $shortCircuit
     * @param string|null          $fileEncoding
     *
     * @return \Dotenv\Dotenv
     */
    public static function createUnsafeImmutable($paths, $names = null, bool $shortCircuit = true, ?string $fileEncoding = null)
    {
        $repository = RepositoryBuilder::createWithDefaultAdapters()
            ->addAdapter(PutenvAdapter::class)
            ->immutable()
            ->make();

        return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding);
    }

    /**
     * Create a new dotenv instance with an array backed repository.
     *
     * @param string|string[]      $paths
     * @param string|string[]|null $names
     * @param bool                 $shortCircuit
     * @param string|null          $fileEncoding
     *
     * @return \Dotenv\Dotenv
     */
    public static function createArrayBacked($paths, $names = null, bool $shortCircuit = true, ?string $fileEncoding = null)
    {
        $repository = RepositoryBuilder::createWithNoAdapters()->addAdapter(ArrayAdapter::class)->make();

        return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding);
    }

    /**
     * Parse the given content and resolve nested variables.
     *
     * This method behaves just like load(), only without mutating your actual
     * environment. We do this by using an array backed repository.
     *
     * @param string $content
     *
     * @throws \Dotenv\Exception\InvalidFileException
     *
     * @return array<string, string|null>
     */
    public static function parse(string $content)
    {
        $repository = RepositoryBuilder::createWithNoAdapters()->addAdapter(ArrayAdapter::class)->make();

        $phpdotenv = new self(new StringStore($content), new Parser(), new Loader(), $repository);

        return $phpdotenv->load();
    }

    /**
     * Read and load environment file(s).
     *
     * @throws \Dotenv\Exception\InvalidPathException|\Dotenv\Exception\InvalidEncodingException|\Dotenv\Exception\InvalidFileException
     *
     * @return array<string, string|null>
     */
    public function load()
    {
        $entries = $this->parser->parse($this->store->read());

        return $this->loader->load($this->repository, $entries);
    }

    /**
     * Read and load environment file(s), silently failing if no files can be read.
     *
     * @throws \Dotenv\Exception\InvalidEncodingException|\Dotenv\Exception\InvalidFileException
     *
     * @return array<string, string|null>
     */
    public function safeLoad()
    {
        try {
            return $this->load();
        } catch (InvalidPathException $e) {
            // suppressing exception
            return [];
        }
    }

    /**
     * Required ensures that the specified variables exist, and returns a new validator object.
     *
     * @param string|string[] $variables
     *
     * @return \Dotenv\Validator
     */
    public function required($variables)
    {
        return (new Validator($this->repository, (array) $variables))->required();
    }

    /**
     * Returns a new validator object that won't check if the specified variables exist.
     *
     * @param string|string[] $variables
     *
     * @return \Dotenv\Validator
     */
    public function ifPresent($variables)
    {
        return new Validator($this->repository, (array) $variables);
    }
}


================================================
FILE: src/Exception/ExceptionInterface.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Exception;

use Throwable;

interface ExceptionInterface extends Throwable
{
    //
}


================================================
FILE: src/Exception/InvalidEncodingException.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Exception;

use InvalidArgumentException;

final class InvalidEncodingException extends InvalidArgumentException implements ExceptionInterface
{
    //
}


================================================
FILE: src/Exception/InvalidFileException.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Exception;

use InvalidArgumentException;

final class InvalidFileException extends InvalidArgumentException implements ExceptionInterface
{
    //
}


================================================
FILE: src/Exception/InvalidPathException.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Exception;

use InvalidArgumentException;

final class InvalidPathException extends InvalidArgumentException implements ExceptionInterface
{
    //
}


================================================
FILE: src/Exception/ValidationException.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Exception;

use RuntimeException;

final class ValidationException extends RuntimeException implements ExceptionInterface
{
    //
}


================================================
FILE: src/Loader/Loader.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Loader;

use Dotenv\Parser\Entry;
use Dotenv\Parser\Value;
use Dotenv\Repository\RepositoryInterface;

final class Loader implements LoaderInterface
{
    /**
     * Load the given entries into the repository.
     *
     * We'll substitute any nested variables, and send each variable to the
     * repository, with the effect of actually mutating the environment.
     *
     * @param \Dotenv\Repository\RepositoryInterface $repository
     * @param \Dotenv\Parser\Entry[]                 $entries
     *
     * @return array<string, string|null>
     */
    public function load(RepositoryInterface $repository, array $entries)
    {
        /** @var array<string, string|null> */
        return \array_reduce($entries, static function (array $vars, Entry $entry) use ($repository) {
            $name = $entry->getName();

            $value = $entry->getValue()->map(static function (Value $value) use ($repository) {
                return Resolver::resolve($repository, $value);
            });

            if ($value->isDefined()) {
                $inner = $value->get();
                if ($repository->set($name, $inner)) {
                    return \array_merge($vars, [$name => $inner]);
                }
            } else {
                if ($repository->clear($name)) {
                    return \array_merge($vars, [$name => null]);
                }
            }

            return $vars;
        }, []);
    }
}


================================================
FILE: src/Loader/LoaderInterface.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Loader;

use Dotenv\Repository\RepositoryInterface;

interface LoaderInterface
{
    /**
     * Load the given entries into the repository.
     *
     * @param \Dotenv\Repository\RepositoryInterface $repository
     * @param \Dotenv\Parser\Entry[]                 $entries
     *
     * @return array<string, string|null>
     */
    public function load(RepositoryInterface $repository, array $entries);
}


================================================
FILE: src/Loader/Resolver.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Loader;

use Dotenv\Parser\Value;
use Dotenv\Repository\RepositoryInterface;
use Dotenv\Util\Regex;
use Dotenv\Util\Str;
use PhpOption\Option;

final class Resolver
{
    /**
     * This class is a singleton.
     *
     * @codeCoverageIgnore
     *
     * @return void
     */
    private function __construct()
    {
        //
    }

    /**
     * Resolve the nested variables in the given value.
     *
     * Replaces ${varname} patterns in the allowed positions in the variable
     * value by an existing environment variable.
     *
     * @param \Dotenv\Repository\RepositoryInterface $repository
     * @param \Dotenv\Parser\Value                   $value
     *
     * @return string
     */
    public static function resolve(RepositoryInterface $repository, Value $value)
    {
        return \array_reduce($value->getVars(), static function (string $s, int $i) use ($repository) {
            return Str::substr($s, 0, $i).self::resolveVariable($repository, Str::substr($s, $i));
        }, $value->getChars());
    }

    /**
     * Resolve a single nested variable.
     *
     * @param \Dotenv\Repository\RepositoryInterface $repository
     * @param string                                 $str
     *
     * @return string
     */
    private static function resolveVariable(RepositoryInterface $repository, string $str)
    {
        return Regex::replaceCallback(
            '/\A\${([a-zA-Z0-9_.]+)}/',
            static function (array $matches) use ($repository) {
                /** @var string */
                return Option::fromValue($repository->get($matches[1]))->getOrElse($matches[0]);
            },
            $str,
            1
        )->success()->getOrElse($str);
    }
}


================================================
FILE: src/Parser/Entry.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Parser;

use PhpOption\Option;

final class Entry
{
    /**
     * The entry name.
     *
     * @var string
     */
    private $name;

    /**
     * The entry value.
     *
     * @var \Dotenv\Parser\Value|null
     */
    private $value;

    /**
     * Create a new entry instance.
     *
     * @param string                    $name
     * @param \Dotenv\Parser\Value|null $value
     *
     * @return void
     */
    public function __construct(string $name, ?Value $value = null)
    {
        $this->name = $name;
        $this->value = $value;
    }

    /**
     * Get the entry name.
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Get the entry value.
     *
     * @return \PhpOption\Option<\Dotenv\Parser\Value>
     */
    public function getValue()
    {
        /** @var \PhpOption\Option<\Dotenv\Parser\Value> */
        return Option::fromValue($this->value);
    }
}


================================================
FILE: src/Parser/EntryParser.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Parser;

use Dotenv\Util\Regex;
use Dotenv\Util\Str;
use GrahamCampbell\ResultType\Error;
use GrahamCampbell\ResultType\Result;
use GrahamCampbell\ResultType\Success;

final class EntryParser
{
    private const INITIAL_STATE = 0;
    private const UNQUOTED_STATE = 1;
    private const SINGLE_QUOTED_STATE = 2;
    private const DOUBLE_QUOTED_STATE = 3;
    private const ESCAPE_SEQUENCE_STATE = 4;
    private const WHITESPACE_STATE = 5;
    private const COMMENT_STATE = 6;
    private const REJECT_STATES = [self::SINGLE_QUOTED_STATE, self::DOUBLE_QUOTED_STATE, self::ESCAPE_SEQUENCE_STATE];

    /**
     * This class is a singleton.
     *
     * @codeCoverageIgnore
     *
     * @return void
     */
    private function __construct()
    {
        //
    }

    /**
     * Parse a raw entry into a proper entry.
     *
     * That is, turn a raw environment variable entry into a name and possibly
     * a value. We wrap the answer in a result type.
     *
     * @param string $entry
     *
     * @return \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Entry, string>
     */
    public static function parse(string $entry)
    {
        return self::splitStringIntoParts($entry)->flatMap(static function (array $parts) {
            [$name, $value] = $parts;

            return self::parseName($name)->flatMap(static function (string $name) use ($value) {
                /** @var Result<Value|null, string> */
                $parsedValue = $value === null ? Success::create(null) : self::parseValue($value);

                return $parsedValue->map(static function (?Value $value) use ($name) {
                    return new Entry($name, $value);
                });
            });
        });
    }

    /**
     * Split the compound string into parts.
     *
     * @param string $line
     *
     * @return \GrahamCampbell\ResultType\Result<array{string, string|null},string>
     */
    private static function splitStringIntoParts(string $line)
    {
        /** @var array{string, string|null} */
        $result = Str::pos($line, '=')->map(static function () use ($line) {
            return \array_map('trim', \explode('=', $line, 2));
        })->getOrElse([$line, null]);

        if ($result[0] === '') {
            /** @var \GrahamCampbell\ResultType\Result<array{string, string|null},string> */
            return Error::create(self::getErrorMessage('an unexpected equals', $line));
        }

        /** @var \GrahamCampbell\ResultType\Result<array{string, string|null},string> */
        return Success::create($result);
    }

    /**
     * Parse the given variable name.
     *
     * That is, strip the optional quotes and leading "export" from the
     * variable name. We wrap the answer in a result type.
     *
     * @param string $name
     *
     * @return \GrahamCampbell\ResultType\Result<string, string>
     */
    private static function parseName(string $name)
    {
        if (Str::len($name) > 8 && Str::substr($name, 0, 6) === 'export' && \ctype_space(Str::substr($name, 6, 1))) {
            $name = \ltrim(Str::substr($name, 6));
        }

        if (self::isQuotedName($name)) {
            $name = Str::substr($name, 1, -1);
        }

        if (!self::isValidName($name)) {
            /** @var \GrahamCampbell\ResultType\Result<string, string> */
            return Error::create(self::getErrorMessage('an invalid name', $name));
        }

        /** @var \GrahamCampbell\ResultType\Result<string, string> */
        return Success::create($name);
    }

    /**
     * Is the given variable name quoted?
     *
     * @param string $name
     *
     * @return bool
     */
    private static function isQuotedName(string $name)
    {
        if (Str::len($name) < 3) {
            return false;
        }

        $first = Str::substr($name, 0, 1);
        $last = Str::substr($name, -1, 1);

        return ($first === '"' && $last === '"') || ($first === '\'' && $last === '\'');
    }

    /**
     * Is the given variable name valid?
     *
     * @param string $name
     *
     * @return bool
     */
    private static function isValidName(string $name)
    {
        return Regex::matches('~(*UTF8)\A[\p{Ll}\p{Lu}\p{M}\p{N}_.]+\z~', $name)->success()->getOrElse(false);
    }

    /**
     * Parse the given variable value.
     *
     * This has the effect of stripping quotes and comments, dealing with
     * special characters, and locating nested variables, but not resolving
     * them. Formally, we run a finite state automaton with an output tape: a
     * transducer. We wrap the answer in a result type.
     *
     * @param string $value
     *
     * @return \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Value, string>
     */
    private static function parseValue(string $value)
    {
        if (\trim($value) === '') {
            /** @var \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Value, string> */
            return Success::create(Value::blank());
        }

        return \array_reduce(\iterator_to_array(Lexer::lex($value)), static function (Result $data, string $token) {
            return $data->flatMap(static function (array $data) use ($token) {
                return self::processToken($data[1], $token)->map(static function (array $val) use ($data) {
                    return [$data[0]->append($val[0], $val[1]), $val[2]];
                });
            });
        }, Success::create([Value::blank(), self::INITIAL_STATE]))->flatMap(static function (array $result) {
            if (in_array($result[1], self::REJECT_STATES, true)) {
                /** @var \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Value, string> */
                return Error::create('a missing closing quote');
            }

            /** @var \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Value, string> */
            return Success::create($result[0]);
        })->mapError(static function (string $err) use ($value) {
            return self::getErrorMessage($err, $value);
        });
    }

    /**
     * Process the given token.
     *
     * @param int    $state
     * @param string $token
     *
     * @return \GrahamCampbell\ResultType\Result<array{string, bool, int}, string>
     */
    private static function processToken(int $state, string $token)
    {
        switch ($state) {
            case self::INITIAL_STATE:
                if ($token === '\'') {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create(['', false, self::SINGLE_QUOTED_STATE]);
                } elseif ($token === '"') {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create(['', false, self::DOUBLE_QUOTED_STATE]);
                } elseif ($token === '#') {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create(['', false, self::COMMENT_STATE]);
                } elseif ($token === '$') {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create([$token, true, self::UNQUOTED_STATE]);
                } else {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create([$token, false, self::UNQUOTED_STATE]);
                }
            case self::UNQUOTED_STATE:
                if ($token === '#') {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create(['', false, self::COMMENT_STATE]);
                } elseif (\ctype_space($token)) {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create(['', false, self::WHITESPACE_STATE]);
                } elseif ($token === '$') {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create([$token, true, self::UNQUOTED_STATE]);
                } else {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create([$token, false, self::UNQUOTED_STATE]);
                }
            case self::SINGLE_QUOTED_STATE:
                if ($token === '\'') {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create(['', false, self::WHITESPACE_STATE]);
                } else {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create([$token, false, self::SINGLE_QUOTED_STATE]);
                }
            case self::DOUBLE_QUOTED_STATE:
                if ($token === '"') {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create(['', false, self::WHITESPACE_STATE]);
                } elseif ($token === '\\') {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create(['', false, self::ESCAPE_SEQUENCE_STATE]);
                } elseif ($token === '$') {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create([$token, true, self::DOUBLE_QUOTED_STATE]);
                } else {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create([$token, false, self::DOUBLE_QUOTED_STATE]);
                }
            case self::ESCAPE_SEQUENCE_STATE:
                if ($token === '"' || $token === '\\') {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create([$token, false, self::DOUBLE_QUOTED_STATE]);
                } elseif ($token === '$') {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create([$token, false, self::DOUBLE_QUOTED_STATE]);
                } else {
                    $first = Str::substr($token, 0, 1);
                    if (\in_array($first, ['f', 'n', 'r', 't', 'v'], true)) {
                        /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                        return Success::create([\stripcslashes('\\'.$first).Str::substr($token, 1), false, self::DOUBLE_QUOTED_STATE]);
                    } else {
                        /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                        return Error::create('an unexpected escape sequence');
                    }
                }
            case self::WHITESPACE_STATE:
                if ($token === '#') {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create(['', false, self::COMMENT_STATE]);
                } elseif (!\ctype_space($token)) {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Error::create('unexpected whitespace');
                } else {
                    /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                    return Success::create(['', false, self::WHITESPACE_STATE]);
                }
            case self::COMMENT_STATE:
                /** @var \GrahamCampbell\ResultType\Result<array{string, bool, int}, string> */
                return Success::create(['', false, self::COMMENT_STATE]);
            default:
                throw new \Error('Parser entered invalid state.');
        }
    }

    /**
     * Generate a friendly error message.
     *
     * @param string $cause
     * @param string $subject
     *
     * @return string
     */
    private static function getErrorMessage(string $cause, string $subject)
    {
        return \sprintf(
            'Encountered %s at [%s].',
            $cause,
            \strtok($subject, "\n")
        );
    }
}


================================================
FILE: src/Parser/Lexer.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Parser;

final class Lexer
{
    /**
     * The regex for each type of token.
     */
    private const PATTERNS = [
        '[\r\n]{1,1000}', '[^\S\r\n]{1,1000}', '\\\\', '\'', '"', '\\#', '\\$', '([^(\s\\\\\'"\\#\\$)]|\\(|\\)){1,1000}',
    ];

    /**
     * This class is a singleton.
     *
     * @codeCoverageIgnore
     *
     * @return void
     */
    private function __construct()
    {
        //
    }

    /**
     * Convert content into a token stream.
     *
     * Multibyte string processing is not needed here, and nether is error
     * handling, for performance reasons.
     *
     * @param string $content
     *
     * @return \Generator<string>
     */
    public static function lex(string $content)
    {
        static $regex;

        if ($regex === null) {
            $regex = '(('.\implode(')|(', self::PATTERNS).'))A';
        }

        $offset = 0;

        while (isset($content[$offset])) {
            if (!\preg_match($regex, $content, $matches, 0, $offset)) {
                throw new \Error(\sprintf('Lexer encountered unexpected character [%s].', $content[$offset]));
            }

            $offset += \strlen($matches[0]);

            yield $matches[0];
        }
    }
}


================================================
FILE: src/Parser/Lines.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Parser;

use Dotenv\Util\Regex;
use Dotenv\Util\Str;

final class Lines
{
    /**
     * This class is a singleton.
     *
     * @codeCoverageIgnore
     *
     * @return void
     */
    private function __construct()
    {
        //
    }

    /**
     * Process the array of lines of environment variables.
     *
     * This will produce an array of raw entries, one per variable.
     *
     * @param string[] $lines
     *
     * @return string[]
     */
    public static function process(array $lines)
    {
        $output = [];
        $multiline = false;
        $multilineBuffer = [];

        foreach ($lines as $line) {
            [$multiline, $line, $multilineBuffer] = self::multilineProcess($multiline, $line, $multilineBuffer);

            if (!$multiline && !self::isCommentOrWhitespace($line)) {
                $output[] = $line;
            }
        }

        return $output;
    }

    /**
     * Used to make all multiline variable process.
     *
     * @param bool     $multiline
     * @param string   $line
     * @param string[] $buffer
     *
     * @return array{bool,string, string[]}
     */
    private static function multilineProcess(bool $multiline, string $line, array $buffer)
    {
        $startsOnCurrentLine = $multiline ? false : self::looksLikeMultilineStart($line);

        // check if $line can be multiline variable
        if ($startsOnCurrentLine) {
            $multiline = true;
        }

        if ($multiline) {
            \array_push($buffer, $line);

            if (self::looksLikeMultilineStop($line, $startsOnCurrentLine)) {
                $multiline = false;
                $line = \implode("\n", $buffer);
                $buffer = [];
            }
        }

        return [$multiline, $line, $buffer];
    }

    /**
     * Determine if the given line can be the start of a multiline variable.
     *
     * @param string $line
     *
     * @return bool
     */
    private static function looksLikeMultilineStart(string $line)
    {
        return Str::pos($line, '="')->map(static function () use ($line) {
            return self::looksLikeMultilineStop($line, true) === false;
        })->getOrElse(false);
    }

    /**
     * Determine if the given line can be the start of a multiline variable.
     *
     * @param string $line
     * @param bool   $started
     *
     * @return bool
     */
    private static function looksLikeMultilineStop(string $line, bool $started)
    {
        if ($line === '"') {
            return true;
        }

        return Regex::occurrences('/(?=([^\\\\]"))/', \str_replace('\\\\', '', $line))->map(static function (int $count) use ($started) {
            return $started ? $count > 1 : $count >= 1;
        })->success()->getOrElse(false);
    }

    /**
     * Determine if the line in the file is a comment or whitespace.
     *
     * @param string $line
     *
     * @return bool
     */
    private static function isCommentOrWhitespace(string $line)
    {
        $line = \trim($line);

        return $line === '' || (isset($line[0]) && $line[0] === '#');
    }
}


================================================
FILE: src/Parser/Parser.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Parser;

use Dotenv\Exception\InvalidFileException;
use Dotenv\Util\Regex;
use GrahamCampbell\ResultType\Result;
use GrahamCampbell\ResultType\Success;

final class Parser implements ParserInterface
{
    /**
     * Parse content into an entry array.
     *
     * @param string $content
     *
     * @throws \Dotenv\Exception\InvalidFileException
     *
     * @return \Dotenv\Parser\Entry[]
     */
    public function parse(string $content)
    {
        return Regex::split("/(\r\n|\n|\r)/", $content)->mapError(static function () {
            return 'Could not split into separate lines.';
        })->flatMap(static function (array $lines) {
            return self::process(Lines::process($lines));
        })->mapError(static function (string $error) {
            throw new InvalidFileException(\sprintf('Failed to parse dotenv file. %s', $error));
        })->success()->get();
    }

    /**
     * Convert the raw entries into proper entries.
     *
     * @param string[] $entries
     *
     * @return \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Entry[], string>
     */
    private static function process(array $entries)
    {
        /** @var \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Entry[], string> */
        return \array_reduce($entries, static function (Result $result, string $raw) {
            return $result->flatMap(static function (array $entries) use ($raw) {
                return EntryParser::parse($raw)->map(static function (Entry $entry) use ($entries) {
                    /** @var \Dotenv\Parser\Entry[] */
                    return \array_merge($entries, [$entry]);
                });
            });
        }, Success::create([]));
    }
}


================================================
FILE: src/Parser/ParserInterface.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Parser;

interface ParserInterface
{
    /**
     * Parse content into an entry array.
     *
     * @param string $content
     *
     * @throws \Dotenv\Exception\InvalidFileException
     *
     * @return \Dotenv\Parser\Entry[]
     */
    public function parse(string $content);
}


================================================
FILE: src/Parser/Value.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Parser;

use Dotenv\Util\Str;

final class Value
{
    /**
     * The string representation of the parsed value.
     *
     * @var string
     */
    private $chars;

    /**
     * The locations of the variables in the value.
     *
     * @var int[]
     */
    private $vars;

    /**
     * Internal constructor for a value.
     *
     * @param string $chars
     * @param int[]  $vars
     *
     * @return void
     */
    private function __construct(string $chars, array $vars)
    {
        $this->chars = $chars;
        $this->vars = $vars;
    }

    /**
     * Create an empty value instance.
     *
     * @return \Dotenv\Parser\Value
     */
    public static function blank()
    {
        return new self('', []);
    }

    /**
     * Create a new value instance, appending the characters.
     *
     * @param string $chars
     * @param bool   $var
     *
     * @return \Dotenv\Parser\Value
     */
    public function append(string $chars, bool $var)
    {
        return new self(
            $this->chars.$chars,
            $var ? \array_merge($this->vars, [Str::len($this->chars)]) : $this->vars
        );
    }

    /**
     * Get the string representation of the parsed value.
     *
     * @return string
     */
    public function getChars()
    {
        return $this->chars;
    }

    /**
     * Get the locations of the variables in the value.
     *
     * @return int[]
     */
    public function getVars()
    {
        $vars = $this->vars;

        \rsort($vars);

        return $vars;
    }
}


================================================
FILE: src/Repository/Adapter/AdapterInterface.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Repository\Adapter;

interface AdapterInterface extends ReaderInterface, WriterInterface
{
    /**
     * Create a new instance of the adapter, if it is available.
     *
     * @return \PhpOption\Option<\Dotenv\Repository\Adapter\AdapterInterface>
     */
    public static function create();
}


================================================
FILE: src/Repository/Adapter/ApacheAdapter.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Repository\Adapter;

use PhpOption\None;
use PhpOption\Option;
use PhpOption\Some;

final class ApacheAdapter implements AdapterInterface
{
    /**
     * Create a new apache adapter instance.
     *
     * @return void
     */
    private function __construct()
    {
        //
    }

    /**
     * Create a new instance of the adapter, if it is available.
     *
     * @return \PhpOption\Option<\Dotenv\Repository\Adapter\AdapterInterface>
     */
    public static function create()
    {
        if (self::isSupported()) {
            /** @var \PhpOption\Option<AdapterInterface> */
            return Some::create(new self());
        }

        return None::create();
    }

    /**
     * Determines if the adapter is supported.
     *
     * This happens if PHP is running as an Apache module.
     *
     * @return bool
     */
    private static function isSupported()
    {
        return \function_exists('apache_getenv') && \function_exists('apache_setenv');
    }

    /**
     * Read an environment variable, if it exists.
     *
     * @param non-empty-string $name
     *
     * @return \PhpOption\Option<string>
     */
    public function read(string $name)
    {
        /** @var \PhpOption\Option<string> */
        return Option::fromValue(apache_getenv($name))->filter(static function ($value) {
            return \is_string($value) && $value !== '';
        });
    }

    /**
     * Write to an environment variable, if possible.
     *
     * @param non-empty-string $name
     * @param string           $value
     *
     * @return bool
     */
    public function write(string $name, string $value)
    {
        return apache_setenv($name, $value);
    }

    /**
     * Delete an environment variable, if possible.
     *
     * @param non-empty-string $name
     *
     * @return bool
     */
    public function delete(string $name)
    {
        return apache_setenv($name, '');
    }
}


================================================
FILE: src/Repository/Adapter/ArrayAdapter.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Repository\Adapter;

use PhpOption\Option;
use PhpOption\Some;

final class ArrayAdapter implements AdapterInterface
{
    /**
     * The variables and their values.
     *
     * @var array<string, string>
     */
    private $variables;

    /**
     * Create a new array adapter instance.
     *
     * @return void
     */
    private function __construct()
    {
        $this->variables = [];
    }

    /**
     * Create a new instance of the adapter, if it is available.
     *
     * @return \PhpOption\Option<\Dotenv\Repository\Adapter\AdapterInterface>
     */
    public static function create()
    {
        /** @var \PhpOption\Option<AdapterInterface> */
        return Some::create(new self());
    }

    /**
     * Read an environment variable, if it exists.
     *
     * @param non-empty-string $name
     *
     * @return \PhpOption\Option<string>
     */
    public function read(string $name)
    {
        return Option::fromArraysValue($this->variables, $name);
    }

    /**
     * Write to an environment variable, if possible.
     *
     * @param non-empty-string $name
     * @param string           $value
     *
     * @return bool
     */
    public function write(string $name, string $value)
    {
        $this->variables[$name] = $value;

        return true;
    }

    /**
     * Delete an environment variable, if possible.
     *
     * @param non-empty-string $name
     *
     * @return bool
     */
    public function delete(string $name)
    {
        unset($this->variables[$name]);

        return true;
    }
}


================================================
FILE: src/Repository/Adapter/EnvConstAdapter.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Repository\Adapter;

use PhpOption\Option;
use PhpOption\Some;

final class EnvConstAdapter implements AdapterInterface
{
    /**
     * Create a new env const adapter instance.
     *
     * @return void
     */
    private function __construct()
    {
        //
    }

    /**
     * Create a new instance of the adapter, if it is available.
     *
     * @return \PhpOption\Option<\Dotenv\Repository\Adapter\AdapterInterface>
     */
    public static function create()
    {
        /** @var \PhpOption\Option<AdapterInterface> */
        return Some::create(new self());
    }

    /**
     * Read an environment variable, if it exists.
     *
     * @param non-empty-string $name
     *
     * @return \PhpOption\Option<string>
     */
    public function read(string $name)
    {
        /** @var \PhpOption\Option<string> */
        return Option::fromArraysValue($_ENV, $name)
            ->filter(static function ($value) {
                return \is_scalar($value);
            })
            ->map(static function ($value) {
                if ($value === false) {
                    return 'false';
                }

                if ($value === true) {
                    return 'true';
                }

                return (string) $value;
            });
    }

    /**
     * Write to an environment variable, if possible.
     *
     * @param non-empty-string $name
     * @param string           $value
     *
     * @return bool
     */
    public function write(string $name, string $value)
    {
        $_ENV[$name] = $value;

        return true;
    }

    /**
     * Delete an environment variable, if possible.
     *
     * @param non-empty-string $name
     *
     * @return bool
     */
    public function delete(string $name)
    {
        unset($_ENV[$name]);

        return true;
    }
}


================================================
FILE: src/Repository/Adapter/GuardedWriter.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Repository\Adapter;

final class GuardedWriter implements WriterInterface
{
    /**
     * The inner writer to use.
     *
     * @var \Dotenv\Repository\Adapter\WriterInterface
     */
    private $writer;

    /**
     * The variable name allow list.
     *
     * @var string[]
     */
    private $allowList;

    /**
     * Create a new guarded writer instance.
     *
     * @param \Dotenv\Repository\Adapter\WriterInterface $writer
     * @param string[]                                   $allowList
     *
     * @return void
     */
    public function __construct(WriterInterface $writer, array $allowList)
    {
        $this->writer = $writer;
        $this->allowList = $allowList;
    }

    /**
     * Write to an environment variable, if possible.
     *
     * @param non-empty-string $name
     * @param string           $value
     *
     * @return bool
     */
    public function write(string $name, string $value)
    {
        // Don't set non-allowed variables
        if (!$this->isAllowed($name)) {
            return false;
        }

        // Set the value on the inner writer
        return $this->writer->write($name, $value);
    }

    /**
     * Delete an environment variable, if possible.
     *
     * @param non-empty-string $name
     *
     * @return bool
     */
    public function delete(string $name)
    {
        // Don't clear non-allowed variables
        if (!$this->isAllowed($name)) {
            return false;
        }

        // Set the value on the inner writer
        return $this->writer->delete($name);
    }

    /**
     * Determine if the given variable is allowed.
     *
     * @param non-empty-string $name
     *
     * @return bool
     */
    private function isAllowed(string $name)
    {
        return \in_array($name, $this->allowList, true);
    }
}


================================================
FILE: src/Repository/Adapter/ImmutableWriter.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Repository\Adapter;

final class ImmutableWriter implements WriterInterface
{
    /**
     * The inner writer to use.
     *
     * @var \Dotenv\Repository\Adapter\WriterInterface
     */
    private $writer;

    /**
     * The inner reader to use.
     *
     * @var \Dotenv\Repository\Adapter\ReaderInterface
     */
    private $reader;

    /**
     * The record of loaded variables.
     *
     * @var array<string, string>
     */
    private $loaded;

    /**
     * Create a new immutable writer instance.
     *
     * @param \Dotenv\Repository\Adapter\WriterInterface $writer
     * @param \Dotenv\Repository\Adapter\ReaderInterface $reader
     *
     * @return void
     */
    public function __construct(WriterInterface $writer, ReaderInterface $reader)
    {
        $this->writer = $writer;
        $this->reader = $reader;
        $this->loaded = [];
    }

    /**
     * Write to an environment variable, if possible.
     *
     * @param non-empty-string $name
     * @param string           $value
     *
     * @return bool
     */
    public function write(string $name, string $value)
    {
        // Don't overwrite existing environment variables
        // Ruby's dotenv does this with `ENV[key] ||= value`
        if ($this->isExternallyDefined($name)) {
            return false;
        }

        // Set the value on the inner writer
        if (!$this->writer->write($name, $value)) {
            return false;
        }

        // Record that we have loaded the variable
        $this->loaded[$name] = '';

        return true;
    }

    /**
     * Delete an environment variable, if possible.
     *
     * @param non-empty-string $name
     *
     * @return bool
     */
    public function delete(string $name)
    {
        // Don't clear existing environment variables
        if ($this->isExternallyDefined($name)) {
            return false;
        }

        // Clear the value on the inner writer
        if (!$this->writer->delete($name)) {
            return false;
        }

        // Leave the variable as fair game
        unset($this->loaded[$name]);

        return true;
    }

    /**
     * Determine if the given variable is externally defined.
     *
     * That is, is it an "existing" variable.
     *
     * @param non-empty-string $name
     *
     * @return bool
     */
    private function isExternallyDefined(string $name)
    {
        return $this->reader->read($name)->isDefined() && !isset($this->loaded[$name]);
    }
}


================================================
FILE: src/Repository/Adapter/MultiReader.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Repository\Adapter;

use PhpOption\None;

final class MultiReader implements ReaderInterface
{
    /**
     * The set of readers to use.
     *
     * @var \Dotenv\Repository\Adapter\ReaderInterface[]
     */
    private $readers;

    /**
     * Create a new multi-reader instance.
     *
     * @param \Dotenv\Repository\Adapter\ReaderInterface[] $readers
     *
     * @return void
     */
    public function __construct(array $readers)
    {
        $this->readers = $readers;
    }

    /**
     * Read an environment variable, if it exists.
     *
     * @param non-empty-string $name
     *
     * @return \PhpOption\Option<string>
     */
    public function read(string $name)
    {
        foreach ($this->readers as $reader) {
            $result = $reader->read($name);
            if ($result->isDefined()) {
                return $result;
            }
        }

        return None::create();
    }
}


================================================
FILE: src/Repository/Adapter/MultiWriter.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Repository\Adapter;

final class MultiWriter implements WriterInterface
{
    /**
     * The set of writers to use.
     *
     * @var \Dotenv\Repository\Adapter\WriterInterface[]
     */
    private $writers;

    /**
     * Create a new multi-writer instance.
     *
     * @param \Dotenv\Repository\Adapter\WriterInterface[] $writers
     *
     * @return void
     */
    public function __construct(array $writers)
    {
        $this->writers = $writers;
    }

    /**
     * Write to an environment variable, if possible.
     *
     * @param non-empty-string $name
     * @param string           $value
     *
     * @return bool
     */
    public function write(string $name, string $value)
    {
        foreach ($this->writers as $writers) {
            if (!$writers->write($name, $value)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Delete an environment variable, if possible.
     *
     * @param non-empty-string $name
     *
     * @return bool
     */
    public function delete(string $name)
    {
        foreach ($this->writers as $writers) {
            if (!$writers->delete($name)) {
                return false;
            }
        }

        return true;
    }
}


================================================
FILE: src/Repository/Adapter/PutenvAdapter.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Repository\Adapter;

use PhpOption\None;
use PhpOption\Option;
use PhpOption\Some;

final class PutenvAdapter implements AdapterInterface
{
    /**
     * Create a new putenv adapter instance.
     *
     * @return void
     */
    private function __construct()
    {
        //
    }

    /**
     * Create a new instance of the adapter, if it is available.
     *
     * @return \PhpOption\Option<\Dotenv\Repository\Adapter\AdapterInterface>
     */
    public static function create()
    {
        if (self::isSupported()) {
            /** @var \PhpOption\Option<AdapterInterface> */
            return Some::create(new self());
        }

        return None::create();
    }

    /**
     * Determines if the adapter is supported.
     *
     * @return bool
     */
    private static function isSupported()
    {
        return \function_exists('getenv') && \function_exists('putenv');
    }

    /**
     * Read an environment variable, if it exists.
     *
     * @param non-empty-string $name
     *
     * @return \PhpOption\Option<string>
     */
    public function read(string $name)
    {
        /** @var \PhpOption\Option<string> */
        return Option::fromValue(\getenv($name), false)->filter(static function ($value) {
            return \is_string($value);
        });
    }

    /**
     * Write to an environment variable, if possible.
     *
     * @param non-empty-string $name
     * @param string           $value
     *
     * @return bool
     */
    public function write(string $name, string $value)
    {
        \putenv("$name=$value");

        return true;
    }

    /**
     * Delete an environment variable, if possible.
     *
     * @param non-empty-string $name
     *
     * @return bool
     */
    public function delete(string $name)
    {
        \putenv($name);

        return true;
    }
}


================================================
FILE: src/Repository/Adapter/ReaderInterface.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Repository\Adapter;

interface ReaderInterface
{
    /**
     * Read an environment variable, if it exists.
     *
     * @param non-empty-string $name
     *
     * @return \PhpOption\Option<string>
     */
    public function read(string $name);
}


================================================
FILE: src/Repository/Adapter/ReplacingWriter.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Repository\Adapter;

final class ReplacingWriter implements WriterInterface
{
    /**
     * The inner writer to use.
     *
     * @var \Dotenv\Repository\Adapter\WriterInterface
     */
    private $writer;

    /**
     * The inner reader to use.
     *
     * @var \Dotenv\Repository\Adapter\ReaderInterface
     */
    private $reader;

    /**
     * The record of seen variables.
     *
     * @var array<string, string>
     */
    private $seen;

    /**
     * Create a new replacement writer instance.
     *
     * @param \Dotenv\Repository\Adapter\WriterInterface $writer
     * @param \Dotenv\Repository\Adapter\ReaderInterface $reader
     *
     * @return void
     */
    public function __construct(WriterInterface $writer, ReaderInterface $reader)
    {
        $this->writer = $writer;
        $this->reader = $reader;
        $this->seen = [];
    }

    /**
     * Write to an environment variable, if possible.
     *
     * @param non-empty-string $name
     * @param string           $value
     *
     * @return bool
     */
    public function write(string $name, string $value)
    {
        if ($this->exists($name)) {
            return $this->writer->write($name, $value);
        }

        // succeed if nothing to do
        return true;
    }

    /**
     * Delete an environment variable, if possible.
     *
     * @param non-empty-string $name
     *
     * @return bool
     */
    public function delete(string $name)
    {
        if ($this->exists($name)) {
            return $this->writer->delete($name);
        }

        // succeed if nothing to do
        return true;
    }

    /**
     * Does the given environment variable exist.
     *
     * Returns true if it currently exists, or existed at any point in the past
     * that we are aware of.
     *
     * @param non-empty-string $name
     *
     * @return bool
     */
    private function exists(string $name)
    {
        if (isset($this->seen[$name])) {
            return true;
        }

        if ($this->reader->read($name)->isDefined()) {
            $this->seen[$name] = '';

            return true;
        }

        return false;
    }
}


================================================
FILE: src/Repository/Adapter/ServerConstAdapter.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Repository\Adapter;

use PhpOption\Option;
use PhpOption\Some;

final class ServerConstAdapter implements AdapterInterface
{
    /**
     * Create a new server const adapter instance.
     *
     * @return void
     */
    private function __construct()
    {
        //
    }

    /**
     * Create a new instance of the adapter, if it is available.
     *
     * @return \PhpOption\Option<\Dotenv\Repository\Adapter\AdapterInterface>
     */
    public static function create()
    {
        /** @var \PhpOption\Option<AdapterInterface> */
        return Some::create(new self());
    }

    /**
     * Read an environment variable, if it exists.
     *
     * @param non-empty-string $name
     *
     * @return \PhpOption\Option<string>
     */
    public function read(string $name)
    {
        /** @var \PhpOption\Option<string> */
        return Option::fromArraysValue($_SERVER, $name)
            ->filter(static function ($value) {
                return \is_scalar($value);
            })
            ->map(static function ($value) {
                if ($value === false) {
                    return 'false';
                }

                if ($value === true) {
                    return 'true';
                }

                return (string) $value;
            });
    }

    /**
     * Write to an environment variable, if possible.
     *
     * @param non-empty-string $name
     * @param string           $value
     *
     * @return bool
     */
    public function write(string $name, string $value)
    {
        $_SERVER[$name] = $value;

        return true;
    }

    /**
     * Delete an environment variable, if possible.
     *
     * @param non-empty-string $name
     *
     * @return bool
     */
    public function delete(string $name)
    {
        unset($_SERVER[$name]);

        return true;
    }
}


================================================
FILE: src/Repository/Adapter/WriterInterface.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Repository\Adapter;

interface WriterInterface
{
    /**
     * Write to an environment variable, if possible.
     *
     * @param non-empty-string $name
     * @param string           $value
     *
     * @return bool
     */
    public function write(string $name, string $value);

    /**
     * Delete an environment variable, if possible.
     *
     * @param non-empty-string $name
     *
     * @return bool
     */
    public function delete(string $name);
}


================================================
FILE: src/Repository/AdapterRepository.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Repository;

use Dotenv\Repository\Adapter\ReaderInterface;
use Dotenv\Repository\Adapter\WriterInterface;
use InvalidArgumentException;

final class AdapterRepository implements RepositoryInterface
{
    /**
     * The reader to use.
     *
     * @var \Dotenv\Repository\Adapter\ReaderInterface
     */
    private $reader;

    /**
     * The writer to use.
     *
     * @var \Dotenv\Repository\Adapter\WriterInterface
     */
    private $writer;

    /**
     * Create a new adapter repository instance.
     *
     * @param \Dotenv\Repository\Adapter\ReaderInterface $reader
     * @param \Dotenv\Repository\Adapter\WriterInterface $writer
     *
     * @return void
     */
    public function __construct(ReaderInterface $reader, WriterInterface $writer)
    {
        $this->reader = $reader;
        $this->writer = $writer;
    }

    /**
     * Determine if the given environment variable is defined.
     *
     * @param string $name
     *
     * @return bool
     */
    public function has(string $name)
    {
        return '' !== $name && $this->reader->read($name)->isDefined();
    }

    /**
     * Get an environment variable.
     *
     * @param string $name
     *
     * @throws \InvalidArgumentException
     *
     * @return string|null
     */
    public function get(string $name)
    {
        if ('' === $name) {
            throw new InvalidArgumentException('Expected name to be a non-empty string.');
        }

        return $this->reader->read($name)->getOrElse(null);
    }

    /**
     * Set an environment variable.
     *
     * @param string $name
     * @param string $value
     *
     * @throws \InvalidArgumentException
     *
     * @return bool
     */
    public function set(string $name, string $value)
    {
        if ('' === $name) {
            throw new InvalidArgumentException('Expected name to be a non-empty string.');
        }

        return $this->writer->write($name, $value);
    }

    /**
     * Clear an environment variable.
     *
     * @param string $name
     *
     * @throws \InvalidArgumentException
     *
     * @return bool
     */
    public function clear(string $name)
    {
        if ('' === $name) {
            throw new InvalidArgumentException('Expected name to be a non-empty string.');
        }

        return $this->writer->delete($name);
    }
}


================================================
FILE: src/Repository/RepositoryBuilder.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Repository;

use Dotenv\Repository\Adapter\AdapterInterface;
use Dotenv\Repository\Adapter\EnvConstAdapter;
use Dotenv\Repository\Adapter\GuardedWriter;
use Dotenv\Repository\Adapter\ImmutableWriter;
use Dotenv\Repository\Adapter\MultiReader;
use Dotenv\Repository\Adapter\MultiWriter;
use Dotenv\Repository\Adapter\ReaderInterface;
use Dotenv\Repository\Adapter\ServerConstAdapter;
use Dotenv\Repository\Adapter\WriterInterface;
use InvalidArgumentException;
use PhpOption\Some;
use ReflectionClass;

final class RepositoryBuilder
{
    /**
     * The set of default adapters.
     */
    private const DEFAULT_ADAPTERS = [
        ServerConstAdapter::class,
        EnvConstAdapter::class,
    ];

    /**
     * The set of readers to use.
     *
     * @var \Dotenv\Repository\Adapter\ReaderInterface[]
     */
    private $readers;

    /**
     * The set of writers to use.
     *
     * @var \Dotenv\Repository\Adapter\WriterInterface[]
     */
    private $writers;

    /**
     * Are we immutable?
     *
     * @var bool
     */
    private $immutable;

    /**
     * The variable name allow list.
     *
     * @var string[]|null
     */
    private $allowList;

    /**
     * Create a new repository builder instance.
     *
     * @param \Dotenv\Repository\Adapter\ReaderInterface[] $readers
     * @param \Dotenv\Repository\Adapter\WriterInterface[] $writers
     * @param bool                                         $immutable
     * @param string[]|null                                $allowList
     *
     * @return void
     */
    private function __construct(array $readers = [], array $writers = [], bool $immutable = false, ?array $allowList = null)
    {
        $this->readers = $readers;
        $this->writers = $writers;
        $this->immutable = $immutable;
        $this->allowList = $allowList;
    }

    /**
     * Create a new repository builder instance with no adapters added.
     *
     * @return \Dotenv\Repository\RepositoryBuilder
     */
    public static function createWithNoAdapters()
    {
        return new self();
    }

    /**
     * Create a new repository builder instance with the default adapters added.
     *
     * @return \Dotenv\Repository\RepositoryBuilder
     */
    public static function createWithDefaultAdapters()
    {
        $adapters = \iterator_to_array(self::defaultAdapters());

        return new self($adapters, $adapters);
    }

    /**
     * Return the array of default adapters.
     *
     * @return \Generator<\Dotenv\Repository\Adapter\AdapterInterface>
     */
    private static function defaultAdapters()
    {
        foreach (self::DEFAULT_ADAPTERS as $adapter) {
            $instance = $adapter::create();
            if ($instance->isDefined()) {
                yield $instance->get();
            }
        }
    }

    /**
     * Determine if the given name if of an adapterclass.
     *
     * @param string $name
     *
     * @return bool
     */
    private static function isAnAdapterClass(string $name)
    {
        if (!\class_exists($name)) {
            return false;
        }

        return (new ReflectionClass($name))->implementsInterface(AdapterInterface::class);
    }

    /**
     * Creates a repository builder with the given reader added.
     *
     * Accepts either a reader instance, or a class-string for an adapter. If
     * the adapter is not supported, then we silently skip adding it.
     *
     * @param \Dotenv\Repository\Adapter\ReaderInterface|string $reader
     *
     * @throws \InvalidArgumentException
     *
     * @return \Dotenv\Repository\RepositoryBuilder
     */
    public function addReader($reader)
    {
        if (!(\is_string($reader) && self::isAnAdapterClass($reader)) && !($reader instanceof ReaderInterface)) {
            throw new InvalidArgumentException(
                \sprintf(
                    'Expected either an instance of %s or a class-string implementing %s',
                    ReaderInterface::class,
                    AdapterInterface::class
                )
            );
        }

        $optional = Some::create($reader)->flatMap(static function ($reader) {
            return \is_string($reader) ? $reader::create() : Some::create($reader);
        });

        $readers = \array_merge($this->readers, \iterator_to_array($optional));

        return new self($readers, $this->writers, $this->immutable, $this->allowList);
    }

    /**
     * Creates a repository builder with the given writer added.
     *
     * Accepts either a writer instance, or a class-string for an adapter. If
     * the adapter is not supported, then we silently skip adding it.
     *
     * @param \Dotenv\Repository\Adapter\WriterInterface|string $writer
     *
     * @throws \InvalidArgumentException
     *
     * @return \Dotenv\Repository\RepositoryBuilder
     */
    public function addWriter($writer)
    {
        if (!(\is_string($writer) && self::isAnAdapterClass($writer)) && !($writer instanceof WriterInterface)) {
            throw new InvalidArgumentException(
                \sprintf(
                    'Expected either an instance of %s or a class-string implementing %s',
                    WriterInterface::class,
                    AdapterInterface::class
                )
            );
        }

        $optional = Some::create($writer)->flatMap(static function ($writer) {
            return \is_string($writer) ? $writer::create() : Some::create($writer);
        });

        $writers = \array_merge($this->writers, \iterator_to_array($optional));

        return new self($this->readers, $writers, $this->immutable, $this->allowList);
    }

    /**
     * Creates a repository builder with the given adapter added.
     *
     * Accepts either an adapter instance, or a class-string for an adapter. If
     * the adapter is not supported, then we silently skip adding it. We will
     * add the adapter as both a reader and a writer.
     *
     * @param \Dotenv\Repository\Adapter\WriterInterface|string $adapter
     *
     * @throws \InvalidArgumentException
     *
     * @return \Dotenv\Repository\RepositoryBuilder
     */
    public function addAdapter($adapter)
    {
        if (!(\is_string($adapter) && self::isAnAdapterClass($adapter)) && !($adapter instanceof AdapterInterface)) {
            throw new InvalidArgumentException(
                \sprintf(
                    'Expected either an instance of %s or a class-string implementing %s',
                    WriterInterface::class,
                    AdapterInterface::class
                )
            );
        }

        $optional = Some::create($adapter)->flatMap(static function ($adapter) {
            return \is_string($adapter) ? $adapter::create() : Some::create($adapter);
        });

        $readers = \array_merge($this->readers, \iterator_to_array($optional));
        $writers = \array_merge($this->writers, \iterator_to_array($optional));

        return new self($readers, $writers, $this->immutable, $this->allowList);
    }

    /**
     * Creates a repository builder with mutability enabled.
     *
     * @return \Dotenv\Repository\RepositoryBuilder
     */
    public function immutable()
    {
        return new self($this->readers, $this->writers, true, $this->allowList);
    }

    /**
     * Creates a repository builder with the given allow list.
     *
     * @param string[]|null $allowList
     *
     * @return \Dotenv\Repository\RepositoryBuilder
     */
    public function allowList(?array $allowList = null)
    {
        return new self($this->readers, $this->writers, $this->immutable, $allowList);
    }

    /**
     * Creates a new repository instance.
     *
     * @return \Dotenv\Repository\RepositoryInterface
     */
    public function make()
    {
        $reader = new MultiReader($this->readers);
        $writer = new MultiWriter($this->writers);

        if ($this->immutable) {
            $writer = new ImmutableWriter($writer, $reader);
        }

        if ($this->allowList !== null) {
            $writer = new GuardedWriter($writer, $this->allowList);
        }

        return new AdapterRepository($reader, $writer);
    }
}


================================================
FILE: src/Repository/RepositoryInterface.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Repository;

interface RepositoryInterface
{
    /**
     * Determine if the given environment variable is defined.
     *
     * @param string $name
     *
     * @return bool
     */
    public function has(string $name);

    /**
     * Get an environment variable.
     *
     * @param string $name
     *
     * @throws \InvalidArgumentException
     *
     * @return string|null
     */
    public function get(string $name);

    /**
     * Set an environment variable.
     *
     * @param string $name
     * @param string $value
     *
     * @throws \InvalidArgumentException
     *
     * @return bool
     */
    public function set(string $name, string $value);

    /**
     * Clear an environment variable.
     *
     * @param string $name
     *
     * @throws \InvalidArgumentException
     *
     * @return bool
     */
    public function clear(string $name);
}


================================================
FILE: src/Store/File/Paths.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Store\File;

/**
 * @internal
 */
final class Paths
{
    /**
     * This class is a singleton.
     *
     * @codeCoverageIgnore
     *
     * @return void
     */
    private function __construct()
    {
        //
    }

    /**
     * Returns the full paths to the files.
     *
     * @param string[] $paths
     * @param string[] $names
     *
     * @return string[]
     */
    public static function filePaths(array $paths, array $names)
    {
        $files = [];

        foreach ($paths as $path) {
            foreach ($names as $name) {
                $files[] = \rtrim($path, \DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR.$name;
            }
        }

        return $files;
    }
}


================================================
FILE: src/Store/File/Reader.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Store\File;

use Dotenv\Exception\InvalidEncodingException;
use Dotenv\Util\Str;
use PhpOption\Option;

/**
 * @internal
 */
final class Reader
{
    /**
     * This class is a singleton.
     *
     * @codeCoverageIgnore
     *
     * @return void
     */
    private function __construct()
    {
        //
    }

    /**
     * Read the file(s), and return their raw content.
     *
     * We provide the file path as the key, and its content as the value. If
     * short circuit mode is enabled, then the returned array with have length
     * at most one. File paths that couldn't be read are omitted entirely.
     *
     * @param string[]    $filePaths
     * @param bool        $shortCircuit
     * @param string|null $fileEncoding
     *
     * @throws \Dotenv\Exception\InvalidEncodingException
     *
     * @return array<string, string>
     */
    public static function read(array $filePaths, bool $shortCircuit = true, ?string $fileEncoding = null)
    {
        $output = [];

        foreach ($filePaths as $filePath) {
            $content = self::readFromFile($filePath, $fileEncoding);
            if ($content->isDefined()) {
                $output[$filePath] = $content->get();
                if ($shortCircuit) {
                    break;
                }
            }
        }

        return $output;
    }

    /**
     * Read the given file.
     *
     * @param string      $path
     * @param string|null $encoding
     *
     * @throws \Dotenv\Exception\InvalidEncodingException
     *
     * @return \PhpOption\Option<string>
     */
    private static function readFromFile(string $path, ?string $encoding = null)
    {
        /** @var Option<string> */
        $content = Option::fromValue(@\file_get_contents($path), false);

        return $content->flatMap(static function (string $content) use ($encoding) {
            return Str::utf8($content, $encoding)->mapError(static function (string $error) {
                throw new InvalidEncodingException($error);
            })->success();
        });
    }
}


================================================
FILE: src/Store/FileStore.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Store;

use Dotenv\Exception\InvalidPathException;
use Dotenv\Store\File\Reader;

final class FileStore implements StoreInterface
{
    /**
     * The file paths.
     *
     * @var string[]
     */
    private $filePaths;

    /**
     * Should file loading short circuit?
     *
     * @var bool
     */
    private $shortCircuit;

    /**
     * The file encoding.
     *
     * @var string|null
     */
    private $fileEncoding;

    /**
     * Create a new file store instance.
     *
     * @param string[]    $filePaths
     * @param bool        $shortCircuit
     * @param string|null $fileEncoding
     *
     * @return void
     */
    public function __construct(array $filePaths, bool $shortCircuit, ?string $fileEncoding = null)
    {
        $this->filePaths = $filePaths;
        $this->shortCircuit = $shortCircuit;
        $this->fileEncoding = $fileEncoding;
    }

    /**
     * Read the content of the environment file(s).
     *
     * @throws \Dotenv\Exception\InvalidEncodingException|\Dotenv\Exception\InvalidPathException
     *
     * @return string
     */
    public function read()
    {
        if ($this->filePaths === []) {
            throw new InvalidPathException('At least one environment file path must be provided.');
        }

        $contents = Reader::read($this->filePaths, $this->shortCircuit, $this->fileEncoding);

        if (\count($contents) > 0) {
            return \implode("\n", $contents);
        }

        throw new InvalidPathException(
            \sprintf('Unable to read any of the environment file(s) at [%s].', \implode(', ', $this->filePaths))
        );
    }
}


================================================
FILE: src/Store/StoreBuilder.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Store;

use Dotenv\Store\File\Paths;

final class StoreBuilder
{
    /**
     * The of default name.
     */
    private const DEFAULT_NAME = '.env';

    /**
     * The paths to search within.
     *
     * @var string[]
     */
    private $paths;

    /**
     * The file names to search for.
     *
     * @var string[]
     */
    private $names;

    /**
     * Should file loading short circuit?
     *
     * @var bool
     */
    private $shortCircuit;

    /**
     * The file encoding.
     *
     * @var string|null
     */
    private $fileEncoding;

    /**
     * Create a new store builder instance.
     *
     * @param string[]    $paths
     * @param string[]    $names
     * @param bool        $shortCircuit
     * @param string|null $fileEncoding
     *
     * @return void
     */
    private function __construct(array $paths = [], array $names = [], bool $shortCircuit = false, ?string $fileEncoding = null)
    {
        $this->paths = $paths;
        $this->names = $names;
        $this->shortCircuit = $shortCircuit;
        $this->fileEncoding = $fileEncoding;
    }

    /**
     * Create a new store builder instance with no names.
     *
     * @return \Dotenv\Store\StoreBuilder
     */
    public static function createWithNoNames()
    {
        return new self();
    }

    /**
     * Create a new store builder instance with the default name.
     *
     * @return \Dotenv\Store\StoreBuilder
     */
    public static function createWithDefaultName()
    {
        return new self([], [self::DEFAULT_NAME]);
    }

    /**
     * Creates a store builder with the given path added.
     *
     * @param string $path
     *
     * @return \Dotenv\Store\StoreBuilder
     */
    public function addPath(string $path)
    {
        return new self(\array_merge($this->paths, [$path]), $this->names, $this->shortCircuit, $this->fileEncoding);
    }

    /**
     * Creates a store builder with the given name added.
     *
     * @param string $name
     *
     * @return \Dotenv\Store\StoreBuilder
     */
    public function addName(string $name)
    {
        return new self($this->paths, \array_merge($this->names, [$name]), $this->shortCircuit, $this->fileEncoding);
    }

    /**
     * Creates a store builder with short circuit mode enabled.
     *
     * @return \Dotenv\Store\StoreBuilder
     */
    public function shortCircuit()
    {
        return new self($this->paths, $this->names, true, $this->fileEncoding);
    }

    /**
     * Creates a store builder with the specified file encoding.
     *
     * @param string|null $fileEncoding
     *
     * @return \Dotenv\Store\StoreBuilder
     */
    public function fileEncoding(?string $fileEncoding = null)
    {
        return new self($this->paths, $this->names, $this->shortCircuit, $fileEncoding);
    }

    /**
     * Creates a new store instance.
     *
     * @return \Dotenv\Store\StoreInterface
     */
    public function make()
    {
        return new FileStore(
            Paths::filePaths($this->paths, $this->names),
            $this->shortCircuit,
            $this->fileEncoding
        );
    }
}


================================================
FILE: src/Store/StoreInterface.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Store;

interface StoreInterface
{
    /**
     * Read the content of the environment file(s).
     *
     * @throws \Dotenv\Exception\InvalidEncodingException|\Dotenv\Exception\InvalidPathException
     *
     * @return string
     */
    public function read();
}


================================================
FILE: src/Store/StringStore.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Store;

final class StringStore implements StoreInterface
{
    /**
     * The file content.
     *
     * @var string
     */
    private $content;

    /**
     * Create a new string store instance.
     *
     * @param string $content
     *
     * @return void
     */
    public function __construct(string $content)
    {
        $this->content = $content;
    }

    /**
     * Read the content of the environment file(s).
     *
     * @return string
     */
    public function read()
    {
        return $this->content;
    }
}


================================================
FILE: src/Util/Regex.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Util;

use GrahamCampbell\ResultType\Error;
use GrahamCampbell\ResultType\Success;

/**
 * @internal
 */
final class Regex
{
    /**
     * This class is a singleton.
     *
     * @codeCoverageIgnore
     *
     * @return void
     */
    private function __construct()
    {
        //
    }

    /**
     * Perform a preg match, wrapping up the result.
     *
     * @param string $pattern
     * @param string $subject
     *
     * @return \GrahamCampbell\ResultType\Result<bool, string>
     */
    public static function matches(string $pattern, string $subject)
    {
        return self::pregAndWrap(static function (string $subject) use ($pattern) {
            return @\preg_match($pattern, $subject) === 1;
        }, $subject);
    }

    /**
     * Perform a preg match all, wrapping up the result.
     *
     * @param string $pattern
     * @param string $subject
     *
     * @return \GrahamCampbell\ResultType\Result<int, string>
     */
    public static function occurrences(string $pattern, string $subject)
    {
        return self::pregAndWrap(static function (string $subject) use ($pattern) {
            return (int) @\preg_match_all($pattern, $subject);
        }, $subject);
    }

    /**
     * Perform a preg replace callback, wrapping up the result.
     *
     * @param string                     $pattern
     * @param callable(string[]): string $callback
     * @param string                     $subject
     * @param int|null                   $limit
     *
     * @return \GrahamCampbell\ResultType\Result<string, string>
     */
    public static function replaceCallback(string $pattern, callable $callback, string $subject, ?int $limit = null)
    {
        return self::pregAndWrap(static function (string $subject) use ($pattern, $callback, $limit) {
            return (string) @\preg_replace_callback($pattern, $callback, $subject, $limit ?? -1);
        }, $subject);
    }

    /**
     * Perform a preg split, wrapping up the result.
     *
     * @param string $pattern
     * @param string $subject
     *
     * @return \GrahamCampbell\ResultType\Result<string[], string>
     */
    public static function split(string $pattern, string $subject)
    {
        return self::pregAndWrap(static function (string $subject) use ($pattern) {
            /** @var string[] */
            return (array) @\preg_split($pattern, $subject);
        }, $subject);
    }

    /**
     * Perform a preg operation, wrapping up the result.
     *
     * @template V
     *
     * @param callable(string): V $operation
     * @param string              $subject
     *
     * @return \GrahamCampbell\ResultType\Result<V, string>
     */
    private static function pregAndWrap(callable $operation, string $subject)
    {
        $result = $operation($subject);

        if (\preg_last_error() !== \PREG_NO_ERROR) {
            /** @var \GrahamCampbell\ResultType\Result<V,string> */
            return Error::create(\preg_last_error_msg());
        }

        /** @var \GrahamCampbell\ResultType\Result<V,string> */
        return Success::create($result);
    }
}


================================================
FILE: src/Util/Str.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Util;

use GrahamCampbell\ResultType\Error;
use GrahamCampbell\ResultType\Success;
use PhpOption\Option;

/**
 * @internal
 */
final class Str
{
    /**
     * This class is a singleton.
     *
     * @codeCoverageIgnore
     *
     * @return void
     */
    private function __construct()
    {
        //
    }

    /**
     * Convert a string to UTF-8 from the given encoding.
     *
     * @param string      $input
     * @param string|null $encoding
     *
     * @return \GrahamCampbell\ResultType\Result<string, string>
     */
    public static function utf8(string $input, ?string $encoding = null)
    {
        if ($encoding !== null && !\in_array($encoding, \mb_list_encodings(), true)) {
            /** @var \GrahamCampbell\ResultType\Result<string, string> */
            return Error::create(
                \sprintf('Illegal character encoding [%s] specified.', $encoding)
            );
        }

        $converted = $encoding === null ?
            @\mb_convert_encoding($input, 'UTF-8') :
            @\mb_convert_encoding($input, 'UTF-8', $encoding);

        if (!is_string($converted)) {
            /** @var \GrahamCampbell\ResultType\Result<string, string> */
            return Error::create(
                \sprintf('Conversion from encoding [%s] failed.', $encoding ?? 'NULL')
            );
        }

        /**
         * this is for support UTF-8 with BOM encoding
         * @see https://en.wikipedia.org/wiki/Byte_order_mark
         * @see https://github.com/vlucas/phpdotenv/issues/500
         */
        if (\substr($converted, 0, 3) == "\xEF\xBB\xBF") {
            $converted = \substr($converted, 3);
        }

        /** @var \GrahamCampbell\ResultType\Result<string, string> */
        return Success::create($converted);
    }

    /**
     * Search for a given substring of the input.
     *
     * @param string $haystack
     * @param string $needle
     *
     * @return \PhpOption\Option<int>
     */
    public static function pos(string $haystack, string $needle)
    {
        /** @var \PhpOption\Option<int> */
        return Option::fromValue(\mb_strpos($haystack, $needle, 0, 'UTF-8'), false);
    }

    /**
     * Grab the specified substring of the input.
     *
     * @param string   $input
     * @param int      $start
     * @param int|null $length
     *
     * @return string
     */
    public static function substr(string $input, int $start, ?int $length = null)
    {
        return \mb_substr($input, $start, $length, 'UTF-8');
    }

    /**
     * Compute the length of the given string.
     *
     * @param string $input
     *
     * @return int
     */
    public static function len(string $input)
    {
        return \mb_strlen($input, 'UTF-8');
    }
}


================================================
FILE: src/Validator.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv;

use Dotenv\Exception\ValidationException;
use Dotenv\Repository\RepositoryInterface;
use Dotenv\Util\Regex;
use Dotenv\Util\Str;

class Validator
{
    /**
     * The environment repository instance.
     *
     * @var \Dotenv\Repository\RepositoryInterface
     */
    private $repository;

    /**
     * The variables to validate.
     *
     * @var string[]
     */
    private $variables;

    /**
     * Create a new validator instance.
     *
     * @param \Dotenv\Repository\RepositoryInterface $repository
     * @param string[]                               $variables
     *
     * @return void
     */
    public function __construct(RepositoryInterface $repository, array $variables)
    {
        $this->repository = $repository;
        $this->variables = $variables;
    }

    /**
     * Assert that each variable is present.
     *
     * @throws \Dotenv\Exception\ValidationException
     *
     * @return \Dotenv\Validator
     */
    public function required()
    {
        return $this->assert(
            static function (?string $value) {
                return $value !== null;
            },
            'is missing'
        );
    }

    /**
     * Assert that each variable is not empty.
     *
     * @throws \Dotenv\Exception\ValidationException
     *
     * @return \Dotenv\Validator
     */
    public function notEmpty()
    {
        return $this->assertNullable(
            static function (string $value) {
                return Str::len(\trim($value)) > 0;
            },
            'is empty'
        );
    }

    /**
     * Assert that each specified variable is an integer.
     *
     * @throws \Dotenv\Exception\ValidationException
     *
     * @return \Dotenv\Validator
     */
    public function isInteger()
    {
        return $this->assertNullable(
            static function (string $value) {
                return \ctype_digit($value);
            },
            'is not an integer'
        );
    }

    /**
     * Assert that each specified variable is a boolean.
     *
     * @throws \Dotenv\Exception\ValidationException
     *
     * @return \Dotenv\Validator
     */
    public function isBoolean()
    {
        return $this->assertNullable(
            static function (string $value) {
                if ($value === '') {
                    return false;
                }

                return \filter_var($value, \FILTER_VALIDATE_BOOLEAN, \FILTER_NULL_ON_FAILURE) !== null;
            },
            'is not a boolean'
        );
    }

    /**
     * Assert that each variable is amongst the given choices.
     *
     * @param string[] $choices
     *
     * @throws \Dotenv\Exception\ValidationException
     *
     * @return \Dotenv\Validator
     */
    public function allowedValues(array $choices)
    {
        return $this->assertNullable(
            static function (string $value) use ($choices) {
                return \in_array($value, $choices, true);
            },
            \sprintf('is not one of [%s]', \implode(', ', $choices))
        );
    }

    /**
     * Assert that each variable matches the given regular expression.
     *
     * @param string $regex
     *
     * @throws \Dotenv\Exception\ValidationException
     *
     * @return \Dotenv\Validator
     */
    public function allowedRegexValues(string $regex)
    {
        return $this->assertNullable(
            static function (string $value) use ($regex) {
                return Regex::matches($regex, $value)->success()->getOrElse(false);
            },
            \sprintf('does not match "%s"', $regex)
        );
    }

    /**
     * Assert that the callback returns true for each variable.
     *
     * @param callable(?string):bool $callback
     * @param string                 $message
     *
     * @throws \Dotenv\Exception\ValidationException
     *
     * @return \Dotenv\Validator
     */
    public function assert(callable $callback, string $message)
    {
        $failing = [];

        foreach ($this->variables as $variable) {
            if ($callback($this->repository->get($variable)) === false) {
                $failing[] = \sprintf('%s %s', $variable, $message);
            }
        }

        if (\count($failing) > 0) {
            throw new ValidationException(\sprintf(
                'One or more environment variables failed assertions: %s.',
                \implode(', ', $failing)
            ));
        }

        return $this;
    }

    /**
     * Assert that the callback returns true for each variable.
     *
     * Skip checking null variable values.
     *
     * @param callable(string):bool $callback
     * @param string                $message
     *
     * @throws \Dotenv\Exception\ValidationException
     *
     * @return \Dotenv\Validator
     */
    public function assertNullable(callable $callback, string $message)
    {
        return $this->assert(
            static function (?string $value) use ($callback) {
                if ($value === null) {
                    return true;
                }

                return $callback($value);
            },
            $message
        );
    }
}


================================================
FILE: tests/Dotenv/DotenvTest.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Tests;

use Dotenv\Dotenv;
use Dotenv\Exception\InvalidEncodingException;
use Dotenv\Exception\InvalidPathException;
use Dotenv\Loader\Loader;
use Dotenv\Parser\Parser;
use Dotenv\Repository\RepositoryBuilder;
use Dotenv\Store\StoreBuilder;
use PHPUnit\Framework\TestCase;

final class DotenvTest extends TestCase
{
    /**
     * @var string
     */
    private static $folder;

    /**
     * @beforeClass
     *
     * @return void
     */
    public static function setFolder()
    {
        self::$folder = \dirname(__DIR__).'/fixtures/env';
    }

    public function testDotenvThrowsExceptionIfUnableToLoadFile()
    {
        $dotenv = Dotenv::createMutable(__DIR__);

        $this->expectException(InvalidPathException::class);
        $this->expectExceptionMessage('Unable to read any of the environment file(s) at');

        $dotenv->load();
    }

    public function testDotenvThrowsExceptionIfUnableToLoadFiles()
    {
        $dotenv = Dotenv::createMutable([__DIR__, __DIR__.'/foo/bar']);

        $this->expectException(InvalidPathException::class);
        $this->expectExceptionMessage('Unable to read any of the environment file(s) at');

        $dotenv->load();
    }

    public function testDotenvThrowsExceptionWhenNoFiles()
    {
        $dotenv = Dotenv::createMutable([]);

        $this->expectException(InvalidPathException::class);
        $this->expectExceptionMessage('At least one environment file path must be provided.');

        $dotenv->load();
    }

    public function testDotenvTriesPathsToLoad()
    {
        $dotenv = Dotenv::createMutable([__DIR__, self::$folder]);
        self::assertCount(4, $dotenv->load());
    }

    public function testDotenvTriesPathsToLoadTwice()
    {
        $dotenv = Dotenv::createMutable([__DIR__, self::$folder]);
        self::assertCount(4, $dotenv->load());

        $dotenv = Dotenv::createImmutable([__DIR__, self::$folder]);
        self::assertCount(0, $dotenv->load());
    }

    public function testDotenvTriesPathsToSafeLoad()
    {
        $dotenv = Dotenv::createMutable([__DIR__, self::$folder]);
        self::assertCount(4, $dotenv->safeLoad());
    }

    public function testDotenvSkipsLoadingIfFileIsMissing()
    {
        $dotenv = Dotenv::createMutable(__DIR__);
        self::assertSame([], $dotenv->safeLoad());
    }

    public function testDotenvLoadsEnvironmentVars()
    {
        $dotenv = Dotenv::createMutable(self::$folder);
        self::assertSame(
            ['FOO' => 'bar', 'BAR' => 'baz', 'SPACED' => 'with spaces', 'NULL' => ''],
            $dotenv->load()
        );
        self::assertSame('bar', $_SERVER['FOO']);
        self::assertSame('baz', $_SERVER['BAR']);
        self::assertSame('with spaces', $_SERVER['SPACED']);
        self::assertEmpty($_SERVER['NULL']);
    }

    public function testDotenvLoadsEnvironmentVarsMultipleWithShortCircuitMode()
    {
        $dotenv = Dotenv::createMutable(self::$folder, ['.env', 'example.env']);

        self::assertSame(
            ['FOO' => 'bar', 'BAR' => 'baz', 'SPACED' => 'with spaces', 'NULL' => ''],
            $dotenv->load()
        );
    }

    public function testDotenvLoadsEnvironmentVarsMultipleWithoutShortCircuitMode()
    {
        $dotenv = Dotenv::createMutable(self::$folder, ['.env', 'example.env'], false);

        self::assertSame(
            ['FOO' => 'bar', 'BAR' => 'baz', 'SPACED' => 'with spaces', 'NULL' => '', 'EG' => 'example'],
            $dotenv->load()
        );
    }

    public function testCommentedDotenvLoadsEnvironmentVars()
    {
        $dotenv = Dotenv::createMutable(self::$folder, 'commented.env');
        $dotenv->load();
        self::assertSame('bar', $_SERVER['CFOO']);
        self::assertFalse(isset($_SERVER['CBAR']));
        self::assertFalse(isset($_SERVER['CZOO']));
        self::assertSame('with spaces', $_SERVER['CSPACED']);
        self::assertSame('a value with a # character', $_SERVER['CQUOTES']);
        self::assertSame('a value with a # character & a quote " character inside quotes', $_SERVER['CQUOTESWITHQUOTE']);
        self::assertEmpty($_SERVER['CNULL']);
        self::assertEmpty($_SERVER['EMPTY']);
        self::assertEmpty($_SERVER['EMPTY2']);
        self::assertSame('foo', $_SERVER['FOOO']);
    }

    public function testQuotedDotenvLoadsEnvironmentVars()
    {
        $dotenv = Dotenv::createMutable(self::$folder, 'quoted.env');
        $dotenv->load();
        self::assertSame('bar', $_SERVER['QFOO']);
        self::assertSame('baz', $_SERVER['QBAR']);
        self::assertSame('with spaces', $_SERVER['QSPACED']);
        self::assertEmpty(\getenv('QNULL'));

        self::assertSame('pgsql:host=localhost;dbname=test', $_SERVER['QEQUALS']);
        self::assertSame('test some escaped characters like a quote (") or maybe a backslash (\\)', $_SERVER['QESCAPED']);
        self::assertSame('iiiiviiiixiiiiviiii\\n', $_SERVER['QSLASH']);
        self::assertSame('iiiiviiiixiiiiviiii\\\\n', $_SERVER['SQSLASH']);
    }

    public function testLargeDotenvLoadsEnvironmentVars()
    {
        $dotenv = Dotenv::createMutable(self::$folder, 'large.env');
        $dotenv->load();
        self::assertSame(2730, \strlen($_SERVER['LARGE']));
        self::assertSame(8192, \strlen($_SERVER['HUGE']));
    }

    public function testDotenvLoadsMultibyteVars()
    {
        $dotenv = Dotenv::createMutable(self::$folder, 'multibyte.env');
        $dotenv->load();
        self::assertSame('Ā ā Ă ă Ą ą Ć ć Ĉ ĉ Ċ ċ Č č Ď ď Đ đ Ē ē Ĕ ĕ Ė ė Ę ę Ě ě', $_SERVER['MB1']);
        self::assertSame('行内支付', $_SERVER['MB2']);
        self::assertSame('🚀', $_SERVER['APP_ENV']);
    }

    public function testDotenvLoadsMultibyteUTF8Vars()
    {
        $dotenv = Dotenv::createMutable(self::$folder, 'multibyte.env', false, 'UTF-8');
        $dotenv->load();
        self::assertSame('Ā ā Ă ă Ą ą Ć ć Ĉ ĉ Ċ ċ Č č Ď ď Đ đ Ē ē Ĕ ĕ Ė ė Ę ę Ě ě', $_SERVER['MB1']);
        self::assertSame('行内支付', $_SERVER['MB2']);
        self::assertSame('🚀', $_SERVER['APP_ENV']);
    }

    public function testDotenvLoadWithInvalidEncoding()
    {
        $dotenv = Dotenv::createMutable(self::$folder, 'multibyte.env', false, 'UTF-88');

        $this->expectException(InvalidEncodingException::class);
        $this->expectExceptionMessage('Illegal character encoding [UTF-88] specified.');

        $dotenv->load();
    }

    public function testDotenvLoadsMultibyteWindowsVars()
    {
        $dotenv = Dotenv::createMutable(self::$folder, 'windows.env', false, 'Windows-1252');
        $dotenv->load();
        self::assertSame('ñá', $_SERVER['MBW']);
    }

    public function testMultipleDotenvLoadsEnvironmentVars()
    {
        $dotenv = Dotenv::createMutable(self::$folder, 'multiple.env');
        $dotenv->load();
        self::assertSame('bar', $_SERVER['MULTI1']);
        self::assertSame('foo', $_SERVER['MULTI2']);
    }

    public function testExportedDotenvLoadsEnvironmentVars()
    {
        $dotenv = Dotenv::createMutable(self::$folder, 'exported.env');
        $dotenv->load();
        self::assertSame('bar', $_SERVER['EFOO']);
        self::assertSame('baz', $_SERVER['EBAR']);
        self::assertSame('with spaces', $_SERVER['ESPACED']);
        self::assertSame('123', $_SERVER['EDQUOTED']);
        self::assertSame('456', $_SERVER['ESQUOTED']);
        self::assertEmpty($_SERVER['ENULL']);
    }

    public function testDotenvLoadsEnvGlobals()
    {
        $dotenv = Dotenv::createMutable(self::$folder);
        $dotenv->load();
        self::assertSame('bar', $_SERVER['FOO']);
        self::assertSame('baz', $_SERVER['BAR']);
        self::assertSame('with spaces', $_SERVER['SPACED']);
        self::assertEmpty($_SERVER['NULL']);
    }

    public function testDotenvLoadsServerGlobals()
    {
        $dotenv = Dotenv::createMutable(self::$folder);
        $dotenv->load();
        self::assertSame('bar', $_ENV['FOO']);
        self::assertSame('baz', $_ENV['BAR']);
        self::assertSame('with spaces', $_ENV['SPACED']);
        self::assertEmpty($_ENV['NULL']);
    }

    public function testDotenvNestedEnvironmentVars()
    {
        $dotenv = Dotenv::createMutable(self::$folder, 'nested.env');
        $dotenv->load();
        self::assertSame('{$NVAR1} {$NVAR2}', $_ENV['NVAR3']); // not resolved
        self::assertSame('Hellō World!', $_ENV['NVAR4']);
        self::assertSame('$NVAR1 {NVAR2}', $_ENV['NVAR5']); // not resolved
        self::assertSame('Special Value', $_ENV['N.VAR6']); // new '.' (dot) in var name
        self::assertSame('Special Value', $_ENV['NVAR7']);  // nested '.' (dot) variable
        self::assertSame('', $_ENV['NVAR8']); // nested variable is empty string
        self::assertSame('', $_ENV['NVAR9']); // nested variable is empty string
        self::assertSame('${NVAR888}', $_ENV['NVAR10']); // nested variable is not set
        self::assertSame('NVAR1', $_ENV['NVAR11']);
        self::assertSame('Hellō', $_ENV['NVAR12']);
        self::assertSame('${${NVAR11}}', $_ENV['NVAR13']); // single quotes
        self::assertSame('${NVAR1} ${NVAR2}', $_ENV['NVAR14']); // single quotes
        self::assertSame('${NVAR1} ${NVAR2}', $_ENV['NVAR15']); // escaped
    }

    public function testDotenvNullFileArgumentUsesDefault()
    {
        $dotenv = Dotenv::createMutable(self::$folder, null);
        $dotenv->load();
        self::assertSame('bar', $_SERVER['FOO']);
    }

    /**
     * The fixture data has whitespace between the key and in the value string.
     *
     * Test that these keys are trimmed down.
     */
    public function testDotenvTrimmedKeys()
    {
        $dotenv = Dotenv::createMutable(self::$folder, 'quoted.env');
        $dotenv->load();
        self::assertSame('no space', $_SERVER['QWHITESPACE']);
    }

    public function testDotenvLoadDoesNotOverwriteEnv()
    {
        \putenv('IMMUTABLE=true');
        $dotenv = Dotenv::createImmutable(self::$folder, 'immutable.env');
        $dotenv->load();
        self::assertSame('true', \getenv('IMMUTABLE'));
    }

    public function testDotenvLoadAfterOverload()
    {
        \putenv('IMMUTABLE=true');
        $dotenv = Dotenv::createUnsafeMutable(self::$folder, 'immutable.env');
        $dotenv->load();
        self::assertSame('false', \getenv('IMMUTABLE'));
    }

    public function testDotenvOverloadAfterLoad()
    {
        \putenv('IMMUTABLE=true');
        $dotenv = Dotenv::createUnsafeImmutable(self::$folder, 'immutable.env');
        $dotenv->load();
        self::assertSame('true', \getenv('IMMUTABLE'));
    }

    public function testDotenvOverloadDoesOverwriteEnv()
    {
        $dotenv = Dotenv::createUnsafeMutable(self::$folder, 'mutable.env');
        $dotenv->load();
        self::assertSame('true', \getenv('MUTABLE'));
    }

    public function testDotenvAllowsSpecialCharacters()
    {
        $dotenv = Dotenv::createUnsafeMutable(self::$folder, 'specialchars.env');
        $dotenv->load();
        self::assertSame('$a6^C7k%zs+e^.jvjXk', \getenv('SPVAR1'));
        self::assertSame('?BUty3koaV3%GA*hMAwH}B', \getenv('SPVAR2'));
        self::assertSame('jdgEB4{QgEC]HL))&GcXxokB+wqoN+j>xkV7K?m$r', \getenv('SPVAR3'));
        self::assertSame('22222:22#2^{', \getenv('SPVAR4'));
        self::assertSame('test some escaped characters like a quote " or maybe a backslash \\', \getenv('SPVAR5'));
        self::assertSame('secret!@', \getenv('SPVAR6'));
        self::assertSame('secret!@#', \getenv('SPVAR7'));
        self::assertSame('secret!@#', \getenv('SPVAR8'));
    }

    public function testMultilineLoading()
    {
        $dotenv = Dotenv::createUnsafeMutable(self::$folder, 'multiline.env');
        $dotenv->load();
        self::assertSame("test\n     test\"test\"\n     test", \getenv('TEST'));
        self::assertSame("test\ntest", \getenv('TEST_ND'));
        self::assertSame('test\\ntest', \getenv('TEST_NS'));

        self::assertSame('https://vision.googleapis.com/v1/images:annotate?key=', \getenv('TEST_EQD'));
        self::assertSame('https://vision.googleapis.com/v1/images:annotate?key=', \getenv('TEST_EQS'));
    }

    public function testEmptyLoading()
    {
        $dotenv = Dotenv::createImmutable(self::$folder, 'empty.env');
        self::assertSame(['EMPTY_VAR' => null], $dotenv->load());
    }

    public function testUnicodeVarNames()
    {
        $dotenv = Dotenv::createImmutable(self::$folder, 'unicodevarnames.env');
        $dotenv->load();
        self::assertSame('Skybert', $_SERVER['AlbertÅberg']);
        self::assertSame('2022-04-01T00:00', $_SERVER['ДатаЗакрытияРасчетногоПериода']);
    }

    public function testDirectConstructor()
    {
        $repository = RepositoryBuilder::createWithDefaultAdapters()->make();
        $store = StoreBuilder::createWithDefaultName()->addPath(self::$folder)->make();

        $dotenv = new Dotenv($store, new Parser(), new Loader(), $repository);

        self::assertSame([
            'FOO'    => 'bar',
            'BAR'    => 'baz',
            'SPACED' => 'with spaces',
            'NULL'   => '',
        ], $dotenv->load());
    }

    public function testDotenvParseExample1()
    {
        $output = Dotenv::parse(
            "BASE_DIR=\"/var/webroot/project-root\"\nCACHE_DIR=\"\${BASE_DIR}/cache\"\nTMP_DIR=\"\${BASE_DIR}/tmp\"\n"
        );

        self::assertSame($output, [
            'BASE_DIR'  => '/var/webroot/project-root',
            'CACHE_DIR' => '/var/webroot/project-root/cache',
            'TMP_DIR'   => '/var/webroot/project-root/tmp',
        ]);
    }

    public function testDotenvParseExample2()
    {
        $output = Dotenv::parse("FOO=Bar\nBAZ=\"Hello \${FOO}\"");

        self::assertSame($output, ['FOO' => 'Bar', 'BAZ' => 'Hello Bar']);
    }

    public function testDotenvParseEmptyCase()
    {
        $output = Dotenv::parse('');

        self::assertSame($output, []);
    }
}


================================================
FILE: tests/Dotenv/Loader/LoaderTest.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Tests\Loader;

use Dotenv\Exception\InvalidFileException;
use Dotenv\Loader\Loader;
use Dotenv\Loader\LoaderInterface;
use Dotenv\Parser\Parser;
use Dotenv\Repository\Adapter\ArrayAdapter;
use Dotenv\Repository\Adapter\EnvConstAdapter;
use Dotenv\Repository\Adapter\ServerConstAdapter;
use Dotenv\Repository\RepositoryBuilder;
use PHPUnit\Framework\TestCase;

final class LoaderTest extends TestCase
{
    public function testParserInstanceOf()
    {
        self::assertInstanceOf(LoaderInterface::class, new Loader());
    }

    public function testLoaderWithNoReaders()
    {
        $repository = RepositoryBuilder::createWithNoAdapters()->addWriter(ArrayAdapter::class)->make();
        $loader = new Loader();

        $content = "NVAR1=\"Hello\"\nNVAR2=\"World!\"\nNVAR3=\"{\$NVAR1} {\$NVAR2}\"\nNVAR4=\"\${NVAR1} \${NVAR2}\"";
        $expected = ['NVAR1' => 'Hello', 'NVAR2' => 'World!', 'NVAR3' => '{$NVAR1} {$NVAR2}', 'NVAR4' => '${NVAR1} ${NVAR2}'];

        self::assertSame($expected, $loader->load($repository, (new Parser())->parse($content)));
    }

    public function testLoaderWithAllowList()
    {
        $adapter = ArrayAdapter::create()->get();
        $repository = RepositoryBuilder::createWithNoAdapters()->addReader($adapter)->addWriter($adapter)->allowList(['FOO'])->make();
        $loader = new Loader();

        self::assertSame(['FOO' => 'Hello'], $loader->load($repository, (new Parser())->parse("FOO=\"Hello\"\nBAR=\"World!\"\n")));
        self::assertTrue($adapter->read('FOO')->isDefined());
        self::assertSame('Hello', $adapter->read('FOO')->get());
        self::assertFalse($adapter->read('BAR')->isDefined());
    }

    public function testLoaderWithGarbage()
    {
        $adapter = ArrayAdapter::create()->get();
        $repository = RepositoryBuilder::createWithNoAdapters()->make();
        $loader = new Loader();

        $this->expectException(InvalidFileException::class);
        $this->expectExceptionMessage('Failed to parse dotenv file. Encountered unexpected whitespace at ["""].');

        $loader->load($repository, (new Parser())->parse('FOO="""'));
    }

    /**
     * @return array<int,\Dotenv\Repository\Adapter\AdapterInterface|string>[]
     */
    public static function providesAdapters()
    {
        return [
            [ArrayAdapter::create()->get()],
            [EnvConstAdapter::class],
            [ServerConstAdapter::class],
        ];
    }

    /**
     * @dataProvider providesAdapters
     *
     * @param \Dotenv\Repository\Adapter\AdapterInterface|string $adapter
     */
    public function testLoaderWithSpecificAdapter($adapter)
    {
        $repository = RepositoryBuilder::createWithNoAdapters()->addReader($adapter)->addWriter($adapter)->make();
        $loader = new Loader();

        $content = "NVAR1=\"Hello\"\nNVAR2=\"World!\"\nNVAR3=\"{\$NVAR1} {\$NVAR2}\"\nNVAR4=\"\${NVAR1} \${NVAR2}\"";
        $expected = ['NVAR1' => 'Hello', 'NVAR2' => 'World!', 'NVAR3' => '{$NVAR1} {$NVAR2}', 'NVAR4' => 'Hello World!'];

        self::assertSame($expected, $loader->load($repository, (new Parser())->parse($content)));
    }
}


================================================
FILE: tests/Dotenv/Parser/EntryParserTest.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Tests\Parser;

use Dotenv\Parser\Entry;
use Dotenv\Parser\EntryParser;
use Dotenv\Parser\Value;
use GrahamCampbell\ResultType\Result;
use PHPUnit\Framework\TestCase;

final class EntryParserTest extends TestCase
{
    public function testBasicParse()
    {
        $result = EntryParser::parse('FOO=BAR');
        $this->checkPositiveResult($result, 'FOO', 'BAR');
    }

    public function testNullParse()
    {
        $result = EntryParser::parse('FOO');
        $this->checkEmptyResult($result, 'FOO');
    }

    public function testUnicodeNameParse()
    {
        $result = EntryParser::parse('FOOƱ=BAZ');
        $this->checkPositiveResult($result, 'FOOƱ', 'BAZ');
    }

    public function testQuotesParse()
    {
        $result = EntryParser::parse("FOO=\"BAR  \n\"");
        $this->checkPositiveResult($result, 'FOO', "BAR  \n");
    }

    public function testNewlineParse()
    {
        $result = EntryParser::parse('FOO="\n"');
        $this->checkPositiveResult($result, 'FOO', "\n");
    }

    public function testTabParseDouble()
    {
        $result = EntryParser::parse('FOO="\t"');
        $this->checkPositiveResult($result, 'FOO', "\t");
    }

    public function testTabParseSingle()
    {
        $result = EntryParser::parse('FOO=\'\t\'');
        $this->checkPositiveResult($result, 'FOO', '\t');
    }

    public function testNonEscapeParse1()
    {
        $result = EntryParser::parse('FOO=\n\v');
        $this->checkPositiveResult($result, 'FOO', '\n\v');
    }

    public function testNonEscapeParse2()
    {
        $result = EntryParser::parse('FOO=\q');
        $this->checkPositiveResult($result, 'FOO', '\q');
    }

    public function testBadEscapeParse()
    {
        $result = EntryParser::parse('FOO="\q"');
        $this->checkErrorResult($result, 'Encountered an unexpected escape sequence at ["\q"].');
    }

    public function testInlineVariable()
    {
        $result = EntryParser::parse('FOO=$BAR');
        $this->checkPositiveResult($result, 'FOO', '$BAR', [0]);
    }

    public function testInlineVariableOffset()
    {
        $result = EntryParser::parse('FOO=AAA$BAR');
        $this->checkPositiveResult($result, 'FOO', 'AAA$BAR', [3]);
    }

    public function testInlineVariables()
    {
        $result = EntryParser::parse('FOO="TEST $BAR $$BAZ"');
        $this->checkPositiveResult($result, 'FOO', 'TEST $BAR $$BAZ', [11, 10, 5]);
    }

    public function testNonInlineVariable()
    {
        $result = EntryParser::parse('FOO=\'TEST $BAR $$BAZ\'');
        $this->checkPositiveResult($result, 'FOO', 'TEST $BAR $$BAZ');
        self::assertTrue($result->success()->isDefined());
    }

    public function testWhitespaceParse()
    {
        $result = EntryParser::parse("FOO=\"\n\"");
        $this->checkPositiveResult($result, 'FOO', "\n");
    }

    public function testExportParse()
    {
        $result = EntryParser::parse('export FOO="bar baz"');
        $this->checkPositiveResult($result, 'FOO', 'bar baz');
    }

    public function testExportParseTab()
    {
        $result = EntryParser::parse("export\t\"FOO\"='bar baz'");
        $this->checkPositiveResult($result, 'FOO', 'bar baz');
    }

    public function testExportParseFail()
    {
        $result = EntryParser::parse('export "FOO="bar baz"');
        $this->checkErrorResult($result, 'Encountered an invalid name at ["FOO].');
    }

    public function testClosingSlashParse()
    {
        $result = EntryParser::parse('SPVAR5="test some escaped characters like a quote \\" or maybe a backslash \\\\" # not escaped');
        $this->checkPositiveResult($result, 'SPVAR5', 'test some escaped characters like a quote " or maybe a backslash \\');
    }

    public function testParseInvalidSpaces()
    {
        $result = EntryParser::parse('FOO=bar baz');
        $this->checkErrorResult($result, 'Encountered unexpected whitespace at [bar baz].');
    }

    public function testParseStrayEquals()
    {
        $result = EntryParser::parse('=');
        $this->checkErrorResult($result, 'Encountered an unexpected equals at [=].');
    }

    public function testParseInvalidName()
    {
        $result = EntryParser::parse('FOO_ASD!=BAZ');
        $this->checkErrorResult($result, 'Encountered an invalid name at [FOO_ASD!].');
    }

    public function testParserEscapingDouble()
    {
        $result = EntryParser::parse('FOO_BAD="iiiiviiiixiiiiviiii\\a"');
        $this->checkErrorResult($result, 'Encountered an unexpected escape sequence at ["iiiiviiiixiiiiviiii\a"].');
    }

    public function testParserEscapingSingle()
    {
        $result = EntryParser::parse('FOO_BAD=\'iiiiviiiixiiiiviiii\\a\'');
        $this->checkPositiveResult($result, 'FOO_BAD', 'iiiiviiiixiiiiviiii\\a');
    }

    public function testParserMissingClosingSingleQuote()
    {
        $result = EntryParser::parse('TEST=\'erert');
        $this->checkErrorResult($result, 'Encountered a missing closing quote at [\'erert].');
    }

    public function testParserMissingClosingDoubleQuote()
    {
        $result = EntryParser::parse('TEST="erert');
        $this->checkErrorResult($result, 'Encountered a missing closing quote at ["erert].');
    }

    public function testParserMissingClosingQuotes()
    {
        $result = EntryParser::parse("TEST=\"erert\nTEST='erert\n");
        $this->checkErrorResult($result, 'Encountered a missing closing quote at ["erert].');
    }

    public function testParserClosingQuoteWithEscape()
    {
        $result = EntryParser::parse('TEST="\\');
        $this->checkErrorResult($result, 'Encountered a missing closing quote at ["\\].');
    }

    /**
     * @param \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Entry,string> $result
     * @param string                                                         $name
     * @param string                                                         $chars
     * @param int[]                                                          $vars
     *
     * @return void
     */
    private function checkPositiveResult(Result $result, string $name, string $chars, array $vars = [])
    {
        self::assertTrue($result->success()->isDefined());

        $entry = $result->success()->get();
        self::assertInstanceOf(Entry::class, $entry);
        self::assertSame($name, $entry->getName());
        self::assertTrue($entry->getValue()->isDefined());

        $value = $entry->getValue()->get();
        self::assertInstanceOf(Value::class, $value);
        self::assertSame($chars, $value->getChars());
        self::assertSame($vars, $value->getVars());
    }

    /**
     * @param \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Entry,string> $result
     * @param string                                                         $name
     *
     * @return void
     */
    private function checkEmptyResult(Result $result, string $name)
    {
        self::assertTrue($result->success()->isDefined());

        $entry = $result->success()->get();
        self::assertInstanceOf(Entry::class, $entry);
        self::assertSame('FOO', $entry->getName());
        self::assertFalse($entry->getValue()->isDefined());
    }

    /**
     * @param \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Entry,string> $result
     * @param string                                                         $error
     *
     * @return void
     */
    private function checkErrorResult(Result $result, string $error)
    {
        self::assertTrue($result->error()->isDefined());
        self::assertSame($error, $result->error()->get());
    }
}


================================================
FILE: tests/Dotenv/Parser/LexerTest.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Tests\Parser;

use Dotenv\Parser\Lexer;
use PHPUnit\Framework\TestCase;

final class LexerTest extends TestCase
{
    /**
     * @return array{string,string[]}[]
     */
    public static function provideLexCases()
    {
        return [
            ['', []],
            ['FOO', ['FOO']],
            ['FOO bar', ['FOO', ' ', 'bar']],
            ['FOO\\n()ab', ['FOO', '\\', 'n()ab']],
            ["FOO\n\n   A", ['FOO', "\n\n", '   ', 'A']],
            ['"VA=L"', ['"', 'VA=L', '"']],
            ['\' \'', ['\'', ' ', '\'']],
        ];
    }

    /**
     * @dataProvider provideLexCases
     *
     * @param string   $input
     * @param string[] $output
     *
     * @return void
     */
    public function testLex(string $input, array $output)
    {
        self::assertSame($output, \iterator_to_array(Lexer::lex($input)));
    }
}


================================================
FILE: tests/Dotenv/Parser/LinesTest.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Tests\Parser;

use Dotenv\Parser\Lines;
use Dotenv\Util\Regex;
use PHPUnit\Framework\TestCase;

final class LinesTest extends TestCase
{
    public function testProcessBasic()
    {
        $content = \file_get_contents(\dirname(\dirname(__DIR__)).'/fixtures/env/assertions.env');
        self::assertIsString($content);
        $result = Regex::split("/(\r\n|\n|\r)/", $content);
        self::assertTrue($result->success()->isDefined());

        $expected = [
            'ASSERTVAR1=val1',
            'ASSERTVAR2=""',
            'ASSERTVAR3="val3   "',
            'ASSERTVAR4="0" # empty looking value',
            'ASSERTVAR5="#foo"',
            "ASSERTVAR6=\"val1\nval2\"",
            "ASSERTVAR7=\"\nval3\" #",
            "ASSERTVAR8=\"val3\n\"",
            "ASSERTVAR9=\"\n\n\"",
        ];

        self::assertSame($expected, Lines::process($result->success()->get()));
    }

    public function testProcessQuotes()
    {
        $content = \file_get_contents(\dirname(\dirname(__DIR__)).'/fixtures/env/multiline.env');
        self::assertIsString($content);
        $result = Regex::split("/(\r\n|\n|\r)/", $content);
        self::assertTrue($result->success()->isDefined());

        $expected = [
            "TEST=\"test\n     test\\\"test\\\"\n     test\"",
            'TEST_ND="test\\ntest"',
            'TEST_NS=\'test\\ntest\'',
            'TEST_EQD="https://vision.googleapis.com/v1/images:annotate?key="',
            'TEST_EQS=\'https://vision.googleapis.com/v1/images:annotate?key=\'',
            "BASE64_ENCODED_MULTILINE=\"qS1zCzMVVUJWQShokv6YVYi+ruKSC/bHV7GmEiyVkLaBWJHNVHCHsgTksEBsy8wJ\nuwycAvR07ZyOJJed4XTRMKnKp1/v+6UATpWzkIjZXytK+pD+XlZimUHTx3uiDcmU\njhQX1wWSxHDqrSWxeIJiTD+BuUyId8FzmXQ3TcBydJ474tmOU2F492ubk3LAiZ18\nmhiRGoshXAOSbS/P3+RZi4bDeNE/No4=\"",
        ];

        self::assertSame($expected, Lines::process($result->success()->get()));
    }
}


================================================
FILE: tests/Dotenv/Parser/ParserTest.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Tests\Parser;

use Dotenv\Exception\InvalidFileException;
use Dotenv\Parser\Entry;
use Dotenv\Parser\Parser;
use Dotenv\Parser\ParserInterface;
use Dotenv\Parser\Value;
use PHPUnit\Framework\TestCase;

final class ParserTest extends TestCase
{
    public function testParserInstanceOf()
    {
        self::assertInstanceOf(ParserInterface::class, new Parser());
    }

    public function testFullParse()
    {
        $result = (new Parser())->parse("FOO=BAR\nFOO\nFOO=\"BAR  \n\"\nFOO=\"\\n\"");

        self::assertIsArray($result);
        self::assertCount(4, $result);

        $this->checkPositiveEntry($result[0], 'FOO', 'BAR');
        $this->checkEmptyEntry($result[1], 'FOO');
        $this->checkPositiveEntry($result[2], 'FOO', "BAR  \n");
        $this->checkPositiveEntry($result[3], 'FOO', "\n");
    }

    public function testBadEscapeParse()
    {
        $this->expectException(InvalidFileException::class);
        $this->expectExceptionMessage('Failed to parse dotenv file. Encountered an unexpected escape sequence at ["\q"].');

        (new Parser())->parse('FOO="\q"');
    }

    public function testParseInvalidSpaces()
    {
        $this->expectException(InvalidFileException::class);
        $this->expectExceptionMessage('Failed to parse dotenv file. Encountered unexpected whitespace at [bar baz].');

        (new Parser())->parse("FOO=bar baz\n");
    }

    public function testParseStrayEquals()
    {
        $this->expectException(InvalidFileException::class);
        $this->expectExceptionMessage('Failed to parse dotenv file. Encountered an unexpected equals at [=].');

        (new Parser())->parse("=\n");
    }

    public function testParseInvalidName()
    {
        $this->expectException(InvalidFileException::class);
        $this->expectExceptionMessage('Failed to parse dotenv file. Encountered an invalid name at [FOO_ASD!].');

        (new Parser())->parse('FOO_ASD!=BAZ');
    }

    /**
     * @param \Dotenv\Parser\Entry $entry
     * @param string               $name
     * @param string               $chars
     * @param int[]                $vars
     *
     * @return void
     */
    private function checkPositiveEntry(Entry $entry, string $name, string $chars, array $vars = [])
    {
        self::assertInstanceOf(Entry::class, $entry);
        self::assertSame($name, $entry->getName());
        self::assertTrue($entry->getValue()->isDefined());

        $value = $entry->getValue()->get();
        self::assertInstanceOf(Value::class, $value);
        self::assertSame($chars, $value->getChars());
        self::assertSame($vars, $value->getVars());
    }

    /**
     * @param \Dotenv\Parser\Entry $entry
     * @param string               $name
     *
     * @return void
     */
    private function checkEmptyEntry(Entry $entry, string $name)
    {
        self::assertInstanceOf(Entry::class, $entry);
        self::assertSame('FOO', $entry->getName());
        self::assertFalse($entry->getValue()->isDefined());
    }
}


================================================
FILE: tests/Dotenv/Repository/Adapter/ArrayAdapterTest.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Tests\Repository\Adapter;

use Dotenv\Repository\Adapter\ArrayAdapter;
use PHPUnit\Framework\TestCase;

final class ArrayAdapterTest extends TestCase
{
    public function testGoodRead()
    {
        $adapter = self::createAdapter();
        $adapter->write('CONST_TEST', 'foo bar baz');
        $value = $adapter->read('CONST_TEST');
        self::assertTrue($value->isDefined());
        self::assertSame('foo bar baz', $value->get());
    }

    public function testUndefinedRead()
    {
        $adapter = self::createAdapter();
        unset($_ENV['CONST_TEST']);
        $value = $adapter->read('CONST_TEST');
        self::assertFalse($value->isDefined());
    }

    public function testGoodWrite()
    {
        $adapter = self::createAdapter();
        self::assertTrue($adapter->write('CONST_TEST', 'foo'));
        self::assertSame('foo', $adapter->read('CONST_TEST')->get());
    }

    public function testEmptyWrite()
    {
        $adapter = self::createAdapter();
        self::assertTrue($adapter->write('CONST_TEST', ''));
        self::assertSame('', $adapter->read('CONST_TEST')->get());
    }

    public function testGoodDelete()
    {
        $adapter = self::createAdapter();
        self::assertTrue($adapter->delete('CONST_TEST'));
        self::assertFalse($adapter->read('CONST_TEST')->isDefined());
    }

    /**
     * @return \Dotenv\Repository\Adapter\AdapterInterface
     */
    private static function createAdapter()
    {
        return ArrayAdapter::create()->get();
    }
}


================================================
FILE: tests/Dotenv/Repository/Adapter/EnvConstAdapterTest.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Tests\Repository\Adapter;

use Dotenv\Repository\Adapter\EnvConstAdapter;
use PHPUnit\Framework\TestCase;

final class EnvConstAdapterTest extends TestCase
{
    public function testGoodRead()
    {
        $_ENV['CONST_TEST'] = 'foo bar baz';
        $value = self::createAdapter()->read('CONST_TEST');
        self::assertTrue($value->isDefined());
        self::assertSame('foo bar baz', $value->get());
    }

    public function testFalseRead()
    {
        $_ENV['CONST_TEST'] = false;
        $value = self::createAdapter()->read('CONST_TEST');
        self::assertTrue($value->isDefined());
        self::assertSame('false', $value->get());
    }

    public function testTrueRead()
    {
        $_ENV['CONST_TEST'] = true;
        $value = self::createAdapter()->read('CONST_TEST');
        self::assertTrue($value->isDefined());
        self::assertSame('true', $value->get());
    }

    public function testBadTypeRead()
    {
        $_ENV['CONST_TEST'] = [123];
        $value = self::createAdapter()->read('CONST_TEST');
        self::assertFalse($value->isDefined());
    }

    public function testUndefinedRead()
    {
        unset($_ENV['CONST_TEST']);
        $value = self::createAdapter()->read('CONST_TEST');
        self::assertFalse($value->isDefined());
    }

    public function testGoodWrite()
    {
        self::assertTrue(self::createAdapter()->write('CONST_TEST', 'foo'));
        self::assertSame('foo', $_ENV['CONST_TEST']);
    }

    public function testEmptyWrite()
    {
        self::assertTrue(self::createAdapter()->write('CONST_TEST', ''));
        self::assertSame('', $_ENV['CONST_TEST']);
    }

    public function testGoodDelete()
    {
        self::assertTrue(self::createAdapter()->delete('CONST_TEST'));
        self::assertFalse(isset($_ENV['CONST_TEST']));
    }

    /**
     * @return \Dotenv\Repository\Adapter\AdapterInterface
     */
    private static function createAdapter()
    {
        return EnvConstAdapter::create()->get();
    }
}


================================================
FILE: tests/Dotenv/Repository/Adapter/PutenvAdapterTest.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Tests\Repository\Adapter;

use Dotenv\Repository\Adapter\PutenvAdapter;
use PHPUnit\Framework\TestCase;

final class PutenvAdapterTest extends TestCase
{
    public function testGoodRead()
    {
        \putenv('CONST_TEST=foo bar baz');
        $value = self::createAdapter()->read('CONST_TEST');
        self::assertTrue($value->isDefined());
        self::assertSame('foo bar baz', $value->get());
    }

    public function testUndefinedRead()
    {
        \putenv('CONST_TEST');
        $value = self::createAdapter()->read('CONST_TEST');
        self::assertFalse($value->isDefined());
    }

    public function testGoodWrite()
    {
        self::assertTrue(self::createAdapter()->write('CONST_TEST', 'foo'));
        self::assertSame('foo', \getenv('CONST_TEST'));
    }

    public function testEmptyWrite()
    {
        self::assertTrue(self::createAdapter()->write('CONST_TEST', ''));
        self::assertSame('', \getenv('CONST_TEST'));
    }

    public function testGoodDelete()
    {
        self::assertTrue(self::createAdapter()->delete('CONST_TEST'));
        self::assertFalse(\getenv('CONST_TEST'));
    }

    /**
     * @return \Dotenv\Repository\Adapter\AdapterInterface
     */
    private static function createAdapter()
    {
        return PutenvAdapter::create()->get();
    }
}


================================================
FILE: tests/Dotenv/Repository/Adapter/ServerConstAdapterTest.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Tests\Repository\Adapter;

use Dotenv\Repository\Adapter\ServerConstAdapter;
use PHPUnit\Framework\TestCase;

final class ServerConstAdapterTest extends TestCase
{
    public function testGoodRead()
    {
        $_SERVER['CONST_TEST'] = 'foo bar baz';
        $value = self::createAdapter()->read('CONST_TEST');
        self::assertTrue($value->isDefined());
        self::assertSame('foo bar baz', $value->get());
    }

    public function testFalseRead()
    {
        $_SERVER['CONST_TEST'] = false;
        $value = self::createAdapter()->read('CONST_TEST');
        self::assertTrue($value->isDefined());
        self::assertSame('false', $value->get());
    }

    public function testTrueRead()
    {
        $_SERVER['CONST_TEST'] = true;
        $value = self::createAdapter()->read('CONST_TEST');
        self::assertTrue($value->isDefined());
        self::assertSame('true', $value->get());
    }

    public function testBadTypeRead()
    {
        $_SERVER['CONST_TEST'] = [123];
        $value = self::createAdapter()->read('CONST_TEST');
        self::assertFalse($value->isDefined());
    }

    public function testUndefinedRead()
    {
        unset($_SERVER['CONST_TEST']);
        $value = self::createAdapter()->read('CONST_TEST');
        self::assertFalse($value->isDefined());
    }

    public function testGoodWrite()
    {
        self::assertTrue(self::createAdapter()->write('CONST_TEST', 'foo'));
        self::assertSame('foo', $_SERVER['CONST_TEST']);
    }

    public function testEmptyWrite()
    {
        self::assertTrue(self::createAdapter()->write('CONST_TEST', ''));
        self::assertSame('', $_SERVER['CONST_TEST']);
    }

    public function testGoodDelete()
    {
        self::assertTrue(self::createAdapter()->delete('CONST_TEST'));
        self::assertFalse(isset($_SERVER['CONST_TEST']));
    }

    /**
     * @return \Dotenv\Repository\Adapter\AdapterInterface
     */
    private static function createAdapter()
    {
        return ServerConstAdapter::create()->get();
    }
}


================================================
FILE: tests/Dotenv/Repository/RepositoryTest.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Tests\Repository;

use Dotenv\Dotenv;
use Dotenv\Repository\Adapter\ArrayAdapter;
use Dotenv\Repository\RepositoryBuilder;
use Dotenv\Repository\RepositoryInterface;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use TypeError;

final class RepositoryTest extends TestCase
{
    /**
     * @var array<string, string>|null
     */
    private $keyVal;

    /**
     * @before
     *
     * @return void
     */
    public function refreshKeyVal()
    {
        $this->keyVal(true);
    }

    /**
     * @return void
     */
    private function load()
    {
        Dotenv::createMutable(\dirname(\dirname(__DIR__)).'/fixtures/env')->load();
    }

    /**
     * Generates a new key/value pair or returns the previous one.
     *
     * Since most of our functionality revolves around setting/retrieving keys
     * and values, we have this utility function to help generate new, unique
     * key/value pairs.
     *
     * @param bool $reset
     *
     * @return array<string, string>
     */
    private function keyVal(bool $reset = false)
    {
        if (!isset($this->keyVal) || $reset) {
            $this->keyVal = [\uniqid() => \uniqid()];
        }

        return $this->keyVal;
    }

    /**
     * Returns the key from keyVal(), without reset.
     *
     * @return string
     */
    private function key()
    {
        $keyVal = $this->keyVal();

        return (string) \key($keyVal);
    }

    /**
     * Returns the value from keyVal(), without reset.
     *
     * @return string
     */
    private function value()
    {
        $keyVal = $this->keyVal();

        /** @var string */
        return \reset($keyVal);
    }

    public function testRepositoryInstanceOf()
    {
        self::assertInstanceOf(RepositoryInterface::class, RepositoryBuilder::createWithNoAdapters()->make());
        self::assertInstanceOf(RepositoryInterface::class, RepositoryBuilder::createWithDefaultAdapters()->make());
    }

    public function testMutableLoaderClearsEnvironmentVars()
    {
        $repository = RepositoryBuilder::createWithDefaultAdapters()->make();

        // Set an environment variable.
        $repository->set($this->key(), $this->value());

        // Clear the set environment variable.
        $repository->clear($this->key());
        self::assertNull($repository->get($this->key()));
        self::assertFalse(\getenv($this->key()));
        self::assertFalse(isset($_ENV[$this->key()]));
        self::assertFalse(isset($_SERVER[$this->key()]));
    }

    public function testImmutableLoaderCannotClearExistingEnvironmentVars()
    {
        $this->load();

        $repository = RepositoryBuilder::createWithDefaultAdapters()->immutable()->make();

        // Pre-set an environment variable.
        RepositoryBuilder::createWithDefaultAdapters()->make()->set($this->key(), $this->value());

        // Attempt to clear the environment variable, check that it fails.
        $repository->clear($this->key());
        self::assertSame($this->value(), $repository->get($this->key()));
        self::assertTrue(isset($_ENV[$this->key()]));
        self::assertTrue(isset($_SERVER[$this->key()]));
    }

    public function testImmutableLoaderCanClearSetEnvironmentVars()
    {
        $this->load();

        $repository = RepositoryBuilder::createWithDefaultAdapters()->immutable()->make();

        // Set an environment variable.
        $repository->set($this->key(), $this->value());

        // Attempt to clear the environment variable, check that it works.
        $repository->clear($this->key());
        self::assertNull($repository->get($this->key()));
        self::assertFalse(\getenv($this->key()));
        self::assertFalse(isset($_ENV[$this->key()]));
        self::assertFalse(isset($_SERVER[$this->key()]));
    }

    public function testCheckingWhetherVariableExists()
    {
        $this->load();

        $repo = RepositoryBuilder::createWithDefaultAdapters()->make();

        self::assertTrue($repo->has('FOO'));
        self::assertFalse($repo->has('NON_EXISTING_VARIABLE'));
    }

    public function testHasWithBadVariable()
    {
        $repo = RepositoryBuilder::createWithDefaultAdapters()->make();

        $this->expectException(TypeError::class);

        $repo->has(null);
    }

    public function testGettingVariableByName()
    {
        $this->load();

        $repo = RepositoryBuilder::createWithDefaultAdapters()->make();

        self::assertSame('bar', $repo->get('FOO'));
    }

    public function testGettingNullVariable()
    {
        $repo = RepositoryBuilder::createWithDefaultAdapters()->make();

        $this->expectException(TypeError::class);

        $repo->get(null);
    }

    public function testGettingEmptyVariable()
    {
        $repo = RepositoryBuilder::createWithDefaultAdapters()->make();

        $this->expectException(InvalidArgumentException::class);
        $this->expectExceptionMessage('Expected name to be a non-empty string.');

        $repo->get('');
    }

    public function testSettingVariable()
    {
        $this->load();

        $repo = RepositoryBuilder::createWithDefaultAdapters()->make();

        self::assertSame('bar', $repo->get('FOO'));
        $repo->set('FOO', 'new');
        self::assertSame('new', $repo->get('FOO'));
    }

    public function testSettingNullVariable()
    {
        $repo = RepositoryBuilder::createWithDefaultAdapters()->make();

        $this->expectException(TypeError::class);

        $repo->set(null, 'foo');
    }

    public function testSettingEmptyVariable()
    {
        $repo = RepositoryBuilder::createWithDefaultAdapters()->make();

        $this->expectException(InvalidArgumentException::class);
        $this->expectExceptionMessage('Expected name to be a non-empty string.');

        $repo->set('', 'foo');
    }

    public function testClearingVariable()
    {
        $this->load();

        $repo = RepositoryBuilder::createWithDefaultAdapters()->make();

        self::assertTrue($repo->has('FOO'));
        $repo->clear('FOO');
        self::assertFalse($repo->has('FOO'));
    }

    public function testClearingVariableWithArrayAdapter()
    {
        $adapter = ArrayAdapter::create()->get();
        $repo = RepositoryBuilder::createWithNoAdapters()->addReader($adapter)->addWriter($adapter)->make();

        self::assertFalse($repo->has('FOO'));
        $repo->set('FOO', 'BAR');
        self::assertTrue($repo->has('FOO'));
        $repo->clear('FOO');
        self::assertFalse($repo->has('FOO'));
    }

    public function testClearingNullVariable()
    {
        $repo = RepositoryBuilder::createWithDefaultAdapters()->make();

        $this->expectException(TypeError::class);

        $repo->clear(null);
    }

    public function testClearingEmptyVariable()
    {
        $repo = RepositoryBuilder::createWithDefaultAdapters()->make();

        $this->expectException(InvalidArgumentException::class);
        $this->expectExceptionMessage('Expected name to be a non-empty string.');

        $repo->clear('');
    }

    public function testCannotSetVariableOnImmutableInstance()
    {
        $this->load();

        $repo = RepositoryBuilder::createWithDefaultAdapters()->immutable()->make();

        self::assertSame('bar', $repo->get('FOO'));

        $repo->set('FOO', 'new');

        self::assertSame('bar', $repo->get('FOO'));
    }

    public function testCannotClearVariableOnImmutableInstance()
    {
        $this->load();

        $repo = RepositoryBuilder::createWithDefaultAdapters()->immutable()->make();

        $repo->clear('FOO');

        self::assertTrue($repo->has('FOO'));
    }

    public function testBuildWithBadReader()
    {
        $this->expectException(InvalidArgumentException::class);
        $this->expectExceptionMessage('Expected either an instance of ');

        RepositoryBuilder::createWithNoAdapters()->addReader('123');
    }

    public function testBuildWithBadWriter()
    {
        $this->expectException(InvalidArgumentException::class);
        $this->expectExceptionMessage('Expected either an instance of ');

        RepositoryBuilder::createWithNoAdapters()->addWriter('123');
    }

    public function testBuildWithBadAdapter()
    {
        $this->expectException(InvalidArgumentException::class);
        $this->expectExceptionMessage('Expected either an instance of ');

        RepositoryBuilder::createWithNoAdapters()->addAdapter('');
    }
}


================================================
FILE: tests/Dotenv/Store/StoreTest.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Tests\Store;

use Dotenv\Exception\InvalidEncodingException;
use Dotenv\Store\File\Paths;
use Dotenv\Store\File\Reader;
use Dotenv\Store\StoreBuilder;
use PHPUnit\Framework\TestCase;

final class StoreTest extends TestCase
{
    /**
     * @var string
     */
    private static $folder;

    /**
     * @beforeClass
     *
     * @return void
     */
    public static function setFolder()
    {
        self::$folder = \dirname(\dirname(__DIR__)).'/fixtures/env';
    }

    public function testBasicReadDirect()
    {
        self::assertSame(
            [
                self::$folder.\DIRECTORY_SEPARATOR.'.env' => "FOO=bar\nBAR=baz\nSPACED=\"with spaces\"\n\nNULL=\n",
            ],
            Reader::read(
                Paths::filePaths([self::$folder], ['.env'])
            )
        );
    }

    public function testBasicRead()
    {
        $builder = StoreBuilder::createWithDefaultName()
            ->addPath(self::$folder);

        self::assertSame(
            "FOO=bar\nBAR=baz\nSPACED=\"with spaces\"\n\nNULL=\n",
            $builder->make()->read()
        );
    }

    public function testBasicReadWindowsEncoding()
    {
        $builder = StoreBuilder::createWithNoNames()
            ->addPath(self::$folder)
            ->addName('windows.env')
            ->fileEncoding('Windows-1252');

        self::assertSame(
            "MBW=\"ñá\"\n",
            $builder->make()->read()
        );
    }

    public function testBasicReadBadEncoding()
    {
        $builder = StoreBuilder::createWithNoNames()
            ->addPath(self::$folder)
            ->addName('windows.env')
            ->fileEncoding('Windowss-1252');

        $this->expectException(InvalidEncodingException::class);
        $this->expectExceptionMessage('Illegal character encoding [Windowss-1252] specified.');

        $builder->make()->read();
    }

    public function testFileReadMultipleShortCircuitModeDirect()
    {
        self::assertSame(
            [
                self::$folder.\DIRECTORY_SEPARATOR.'.env' => "FOO=bar\nBAR=baz\nSPACED=\"with spaces\"\n\nNULL=\n",
            ],
            Reader::read(
                Paths::filePaths([self::$folder], ['.env', 'example.env'])
            )
        );
    }

    public function testFileReadMultipleShortCircuitMode()
    {
        $builder = StoreBuilder::createWithNoNames()
            ->addPath(self::$folder)
            ->addName('.env')
            ->addName('example.env')
            ->shortCircuit();

        self::assertSame(
            "FOO=bar\nBAR=baz\nSPACED=\"with spaces\"\n\nNULL=\n",
            $builder->make()->read()
        );
    }

    public function testFileReadMultipleWithoutShortCircuitModeDirect()
    {
        self::assertSame(
            [
                self::$folder.\DIRECTORY_SEPARATOR.'.env'        => "FOO=bar\nBAR=baz\nSPACED=\"with spaces\"\n\nNULL=\n",
                self::$folder.\DIRECTORY_SEPARATOR.'example.env' => "EG=\"example\"\n",
            ],
            Reader::read(
                Paths::filePaths([self::$folder], ['.env', 'example.env']),
                false
            )
        );
    }

    public function testFileReadMultipleWithoutShortCircuitMode()
    {
        $builder = StoreBuilder::createWithDefaultName()
            ->addPath(self::$folder)
            ->addName('example.env');

        self::assertSame(
            "FOO=bar\nBAR=baz\nSPACED=\"with spaces\"\n\nNULL=\n\nEG=\"example\"\n",
            $builder->make()->read()
        );
    }
    public function testFileReadWithUtf8WithBomEncoding()
    {
        self::assertSame(
            [
                self::$folder.\DIRECTORY_SEPARATOR.'utf8-with-bom-encoding.env' => "FOO=bar\nBAR=baz\nSPACED=\"with spaces\"\n",
            ],
            Reader::read(
                Paths::filePaths([self::$folder], ['utf8-with-bom-encoding.env'])
            )
        );
    }
}


================================================
FILE: tests/Dotenv/ValidatorTest.php
================================================
<?php

declare(strict_types=1);

namespace Dotenv\Tests;

use Dotenv\Dotenv;
use Dotenv\Exception\ValidationException;
use Dotenv\Repository\Adapter\ArrayAdapter;
use Dotenv\Repository\RepositoryBuilder;
use PHPUnit\Framework\TestCase;

final class ValidatorTest extends TestCase
{
    /**
     * @var string
     */
    private static $folder;

    /**
     * @beforeClass
     *
     * @return void
     */
    public static function setFolder()
    {
        self::$folder = \dirname(__DIR__).'/fixtures/env';
    }

    /**
     * @param string $name
     *
     * @return array{\Dotenv\Repository\RepositoryInterface,\Dotenv\Dotenv}
     */
    public static function createArrayDotenv(string $name = '.env')
    {
        $repository = RepositoryBuilder::createWithNoAdapters()->addAdapter(ArrayAdapter::class)->make();

        return [$repository, Dotenv::create($repository, self::$folder, $name)];
    }

    /**
     * @doesNotPerformAssertions
     */
    public function testDotenvRequiredStringEnvironmentVars()
    {
        $dotenv = self::createArrayDotenv()[1];
        $dotenv->load();
        $dotenv->required('FOO');
    }

    /**
     * @doesNotPerformAssertions
     */
    public function testDotenvAllowedValues()
    {
        $dotenv = self::createArrayDotenv()[1];
        $dotenv->load();
        $dotenv->required('FOO')->allowedValues(['bar', 'baz']);
    }

    /**
     * @doesNotPerformAssertions
     */
    public function testDotenvAllowedValuesIfPresent()
    {
        $dotenv = self::createArrayDotenv()[1];
        $dotenv->load();
        $dotenv->ifPresent('FOO')->allowedValues(['bar', 'baz']);
    }

    /**
     * @doesNotPerformAssertions
     */
    public function testDotenvAllowedValuesIfNotPresent()
    {
        $dotenv = self::createArrayDotenv()[1];
        $dotenv->load();
        $dotenv->ifPresent('FOOQWERTYOOOOOO')->allowedValues(['bar', 'baz']);
    }

    public function testDotenvProhibitedValues()
    {
        $dotenv = self::createArrayDotenv()[1];
        $dotenv->load();

        $this->expectException(ValidationException::class);
        $this->expectExceptionMessage('One or more environment variables failed assertions: FOO is not one of [buzz, buz].');

        $dotenv->required('FOO')->allowedValues(['buzz', 'buz']);
    }

    public function testDotenvProhibitedValuesIfPresent()
    {
        $dotenv = self::createArrayDotenv()[1];
        $dotenv->load();

        $this->expectException(ValidationException::class);
        $this->expectExceptionMessage('One or more environment variables failed assertions: FOO is not one of [buzz, buz].');

        $dotenv->ifPresent('FOO')->allowedValues(['buzz', 'buz']);
    }

    public function testDotenvRequiredThrowsRuntimeException()
    {
        [$repo, $dotenv] = self::createArrayDotenv();

        $dotenv->load();

        self::assertFalse($repo->has('FOOX'));
        self::assertFalse($repo->has('NOPE'));

        $this->expectException(ValidationException::class);
        $this->expectExceptionMessage('One or more environment variables failed assertions: FOOX is missing, NOPE is missing.');

        $dotenv->required(['FOOX', 'NOPE']);
    }

    /**
     * @doesNotPerformAssertions
     */
    public function testDotenvRequiredArrayEnvironmentVars()
    {
        $dotenv = self::createArrayDotenv()[1];
        $dotenv->load();
        $dotenv->required(['FOO', 'BAR']);
    }

    public function testDotenvAssertions()
    {
        [$repo, $dotenv] = self::createArrayDotenv('assertions.env');

        $dotenv->load();

        self::assertSame('val1', $repo->get('ASSERTVAR1'));
        self::assertSame('', $repo->get('ASSERTVAR2'));
        self::assertSame('val3   ', $repo->get('ASSERTVAR3'));
        self::assertSame('0', $repo->get('ASSERTVAR4'));
        self::assertSame('#foo', $repo->get('ASSERTVAR5'));
        self::assertSame("val1\nval2", $repo->get('ASSERTVAR6'));
        self::assertSame("\nval3", $repo->get('ASSERTVAR7'));
        self::assertSame("val3\n", $repo->get('ASSERTVAR8'));

        $dotenv->required([
            'ASSERTVAR1',
            'ASSERTVAR2',
            'ASSERTVAR3',
            'ASSERTVAR4',
            'ASSERTVAR5',
            'ASSERTVAR6',
            'ASSERTVAR7',
            'ASSERTVAR8',
            'ASSERTVAR9',
        ]);

        $dotenv->required([
            'ASSERTVAR1',
            'ASSERTVAR3',
            'ASSERTVAR4',
            'ASSERTVAR5',
            'ASSERTVAR6',
            'ASSERTVAR7',
            'ASSERTVAR8',
        ])->notEmpty();

        $dotenv->required([
            'ASSERTVAR1',
            'ASSERTVAR4',
            'ASSERTVAR5',
        ])->notEmpty()->allowedValues(['0', 'val1', '#foo']);
    }

    public function testDotenvEmptyThrowsRuntimeException()
    {
        $dotenv = self::createArrayDotenv('assertions.env')[1];
        $dotenv->load();

        $this->expectException(ValidationException::class);
        $this->expectExceptionMessage('One or more environment variables failed assertions: ASSERTVAR2 is empty.');

        $dotenv->required('ASSERTVAR2')->notEmpty();
    }

    /**
     * @doesNotPerformAssertions
     */
    public function testDotenvEmptyWhenNotPresent()
    {
        $dotenv = self::createArrayDotenv('assertions.env')[1];
        $dotenv->load();
        $dotenv->ifPresent('ASSERTVAR2_NO_SUCH_VARIABLE')->notEmpty();
    }

    public function testDotenvStringOfSpacesConsideredEmpty()
    {
        $dotenv = self::createArrayDotenv('assertions.env')[1];
        $dotenv->load();

        $this->expectException(ValidationException::class);
        $this->expectExceptionMessage('One or more environment variables failed assertions: ASSERTVAR9 is empty.');

        $dotenv->required('ASSERTVAR9')->notEmpty();
    }

    /**
     * List of valid boolean values in fixtures/env/booleans.env.
     *
     * @return string[][]
     */
    public static function validBooleanValuesDataProvider()
    {
        return [
            ['VALID_EXPLICIT_LOWERCASE_TRUE'],
            ['VALID_EXPLICIT_LOWERCASE_FALSE'],
            ['VALID_EXPLICIT_UPPERCASE_TRUE'],
            ['VALID_EXPLICIT_UPPERCASE_FALSE'],
            ['VALID_EXPLICIT_MIXEDCASE_TRUE'],
            ['VALID_EXPLICIT_MIXEDCASE_FALSE'],

            ['VALID_NUMBER_TRUE'],
            ['VALID_NUMBER_FALSE'],

            ['VALID_ONOFF_LOWERCASE_TRUE'],
            ['VALID_ONOFF_LOWERCASE_FALSE'],
            ['VALID_ON
Download .txt
gitextract_7d43qxaz/

├── .editorconfig
├── .gitattributes
├── .github/
│   ├── CODE_OF_CONDUCT.md
│   ├── CONTRIBUTING.md
│   ├── FUNDING.yml
│   ├── SECURITY.md
│   └── workflows/
│       ├── static.yml
│       └── tests.yml
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── UPGRADING.md
├── composer.json
├── phpstan-baseline.neon
├── phpstan.neon.dist
├── phpunit.xml.dist
├── src/
│   ├── Dotenv.php
│   ├── Exception/
│   │   ├── ExceptionInterface.php
│   │   ├── InvalidEncodingException.php
│   │   ├── InvalidFileException.php
│   │   ├── InvalidPathException.php
│   │   └── ValidationException.php
│   ├── Loader/
│   │   ├── Loader.php
│   │   ├── LoaderInterface.php
│   │   └── Resolver.php
│   ├── Parser/
│   │   ├── Entry.php
│   │   ├── EntryParser.php
│   │   ├── Lexer.php
│   │   ├── Lines.php
│   │   ├── Parser.php
│   │   ├── ParserInterface.php
│   │   └── Value.php
│   ├── Repository/
│   │   ├── Adapter/
│   │   │   ├── AdapterInterface.php
│   │   │   ├── ApacheAdapter.php
│   │   │   ├── ArrayAdapter.php
│   │   │   ├── EnvConstAdapter.php
│   │   │   ├── GuardedWriter.php
│   │   │   ├── ImmutableWriter.php
│   │   │   ├── MultiReader.php
│   │   │   ├── MultiWriter.php
│   │   │   ├── PutenvAdapter.php
│   │   │   ├── ReaderInterface.php
│   │   │   ├── ReplacingWriter.php
│   │   │   ├── ServerConstAdapter.php
│   │   │   └── WriterInterface.php
│   │   ├── AdapterRepository.php
│   │   ├── RepositoryBuilder.php
│   │   └── RepositoryInterface.php
│   ├── Store/
│   │   ├── File/
│   │   │   ├── Paths.php
│   │   │   └── Reader.php
│   │   ├── FileStore.php
│   │   ├── StoreBuilder.php
│   │   ├── StoreInterface.php
│   │   └── StringStore.php
│   ├── Util/
│   │   ├── Regex.php
│   │   └── Str.php
│   └── Validator.php
├── tests/
│   ├── Dotenv/
│   │   ├── DotenvTest.php
│   │   ├── Loader/
│   │   │   └── LoaderTest.php
│   │   ├── Parser/
│   │   │   ├── EntryParserTest.php
│   │   │   ├── LexerTest.php
│   │   │   ├── LinesTest.php
│   │   │   └── ParserTest.php
│   │   ├── Repository/
│   │   │   ├── Adapter/
│   │   │   │   ├── ArrayAdapterTest.php
│   │   │   │   ├── EnvConstAdapterTest.php
│   │   │   │   ├── PutenvAdapterTest.php
│   │   │   │   └── ServerConstAdapterTest.php
│   │   │   └── RepositoryTest.php
│   │   ├── Store/
│   │   │   └── StoreTest.php
│   │   └── ValidatorTest.php
│   └── fixtures/
│       └── env/
│           ├── assertions.env
│           ├── booleans.env
│           ├── commented.env
│           ├── empty.env
│           ├── example.env
│           ├── exported.env
│           ├── immutable.env
│           ├── integers.env
│           ├── large.env
│           ├── multibyte.env
│           ├── multiline.env
│           ├── multiple.env
│           ├── mutable.env
│           ├── nested.env
│           ├── quoted.env
│           ├── specialchars.env
│           ├── unicodevarnames.env
│           ├── utf8-with-bom-encoding.env
│           └── windows.env
└── vendor-bin/
    └── phpstan/
        └── composer.json
Download .txt
SYMBOL INDEX (391 symbols across 54 files)

FILE: src/Dotenv.php
  class Dotenv (line 20) | class Dotenv
    method __construct (line 60) | public function __construct(
    method create (line 83) | public static function create(RepositoryInterface $repository, $paths,...
    method createMutable (line 112) | public static function createMutable($paths, $names = null, bool $shor...
    method createUnsafeMutable (line 129) | public static function createUnsafeMutable($paths, $names = null, bool...
    method createImmutable (line 148) | public static function createImmutable($paths, $names = null, bool $sh...
    method createUnsafeImmutable (line 165) | public static function createUnsafeImmutable($paths, $names = null, bo...
    method createArrayBacked (line 185) | public static function createArrayBacked($paths, $names = null, bool $...
    method parse (line 204) | public static function parse(string $content)
    method load (line 220) | public function load()
    method safeLoad (line 234) | public function safeLoad()
    method required (line 251) | public function required($variables)
    method ifPresent (line 263) | public function ifPresent($variables)

FILE: src/Exception/ExceptionInterface.php
  type ExceptionInterface (line 9) | interface ExceptionInterface extends Throwable

FILE: src/Exception/InvalidEncodingException.php
  class InvalidEncodingException (line 9) | final class InvalidEncodingException extends InvalidArgumentException im...

FILE: src/Exception/InvalidFileException.php
  class InvalidFileException (line 9) | final class InvalidFileException extends InvalidArgumentException implem...

FILE: src/Exception/InvalidPathException.php
  class InvalidPathException (line 9) | final class InvalidPathException extends InvalidArgumentException implem...

FILE: src/Exception/ValidationException.php
  class ValidationException (line 9) | final class ValidationException extends RuntimeException implements Exce...

FILE: src/Loader/Loader.php
  class Loader (line 11) | final class Loader implements LoaderInterface
    method load (line 24) | public function load(RepositoryInterface $repository, array $entries)

FILE: src/Loader/LoaderInterface.php
  type LoaderInterface (line 9) | interface LoaderInterface
    method load (line 19) | public function load(RepositoryInterface $repository, array $entries);

FILE: src/Loader/Resolver.php
  class Resolver (line 13) | final class Resolver
    method __construct (line 22) | private function __construct()
    method resolve (line 38) | public static function resolve(RepositoryInterface $repository, Value ...
    method resolveVariable (line 53) | private static function resolveVariable(RepositoryInterface $repositor...

FILE: src/Parser/Entry.php
  class Entry (line 9) | final class Entry
    method __construct (line 33) | public function __construct(string $name, ?Value $value = null)
    method getName (line 44) | public function getName()
    method getValue (line 54) | public function getValue()

FILE: src/Parser/EntryParser.php
  class EntryParser (line 13) | final class EntryParser
    method __construct (line 31) | private function __construct()
    method parse (line 46) | public static function parse(string $entry)
    method splitStringIntoParts (line 69) | private static function splitStringIntoParts(string $line)
    method parseName (line 95) | private static function parseName(string $name)
    method isQuotedName (line 121) | private static function isQuotedName(string $name)
    method isValidName (line 140) | private static function isValidName(string $name)
    method parseValue (line 157) | private static function parseValue(string $value)
    method processToken (line 191) | private static function processToken(int $state, string $token)
    method getErrorMessage (line 291) | private static function getErrorMessage(string $cause, string $subject)

FILE: src/Parser/Lexer.php
  class Lexer (line 7) | final class Lexer
    method __construct (line 23) | private function __construct()
    method lex (line 38) | public static function lex(string $content)

FILE: src/Parser/Lines.php
  class Lines (line 10) | final class Lines
    method __construct (line 19) | private function __construct()
    method process (line 33) | public static function process(array $lines)
    method multilineProcess (line 59) | private static function multilineProcess(bool $multiline, string $line...
    method looksLikeMultilineStart (line 88) | private static function looksLikeMultilineStart(string $line)
    method looksLikeMultilineStop (line 103) | private static function looksLikeMultilineStop(string $line, bool $sta...
    method isCommentOrWhitespace (line 121) | private static function isCommentOrWhitespace(string $line)

FILE: src/Parser/Parser.php
  class Parser (line 12) | final class Parser implements ParserInterface
    method parse (line 23) | public function parse(string $content)
    method process (line 41) | private static function process(array $entries)

FILE: src/Parser/ParserInterface.php
  type ParserInterface (line 7) | interface ParserInterface
    method parse (line 18) | public function parse(string $content);

FILE: src/Parser/Value.php
  class Value (line 9) | final class Value
    method __construct (line 33) | private function __construct(string $chars, array $vars)
    method blank (line 44) | public static function blank()
    method append (line 57) | public function append(string $chars, bool $var)
    method getChars (line 70) | public function getChars()
    method getVars (line 80) | public function getVars()

FILE: src/Repository/Adapter/AdapterInterface.php
  type AdapterInterface (line 7) | interface AdapterInterface extends ReaderInterface, WriterInterface
    method create (line 14) | public static function create();

FILE: src/Repository/Adapter/ApacheAdapter.php
  class ApacheAdapter (line 11) | final class ApacheAdapter implements AdapterInterface
    method __construct (line 18) | private function __construct()
    method create (line 28) | public static function create()
    method isSupported (line 45) | private static function isSupported()
    method read (line 57) | public function read(string $name)
    method write (line 73) | public function write(string $name, string $value)
    method delete (line 85) | public function delete(string $name)

FILE: src/Repository/Adapter/ArrayAdapter.php
  class ArrayAdapter (line 10) | final class ArrayAdapter implements AdapterInterface
    method __construct (line 24) | private function __construct()
    method create (line 34) | public static function create()
    method read (line 47) | public function read(string $name)
    method write (line 60) | public function write(string $name, string $value)
    method delete (line 74) | public function delete(string $name)

FILE: src/Repository/Adapter/EnvConstAdapter.php
  class EnvConstAdapter (line 10) | final class EnvConstAdapter implements AdapterInterface
    method __construct (line 17) | private function __construct()
    method create (line 27) | public static function create()
    method read (line 40) | public function read(string $name)
    method write (line 68) | public function write(string $name, string $value)
    method delete (line 82) | public function delete(string $name)

FILE: src/Repository/Adapter/GuardedWriter.php
  class GuardedWriter (line 7) | final class GuardedWriter implements WriterInterface
    method __construct (line 31) | public function __construct(WriterInterface $writer, array $allowList)
    method write (line 45) | public function write(string $name, string $value)
    method delete (line 63) | public function delete(string $name)
    method isAllowed (line 81) | private function isAllowed(string $name)

FILE: src/Repository/Adapter/ImmutableWriter.php
  class ImmutableWriter (line 7) | final class ImmutableWriter implements WriterInterface
    method __construct (line 38) | public function __construct(WriterInterface $writer, ReaderInterface $...
    method write (line 53) | public function write(string $name, string $value)
    method delete (line 79) | public function delete(string $name)
    method isExternallyDefined (line 106) | private function isExternallyDefined(string $name)

FILE: src/Repository/Adapter/MultiReader.php
  class MultiReader (line 9) | final class MultiReader implements ReaderInterface
    method __construct (line 25) | public function __construct(array $readers)
    method read (line 37) | public function read(string $name)

FILE: src/Repository/Adapter/MultiWriter.php
  class MultiWriter (line 7) | final class MultiWriter implements WriterInterface
    method __construct (line 23) | public function __construct(array $writers)
    method write (line 36) | public function write(string $name, string $value)
    method delete (line 54) | public function delete(string $name)

FILE: src/Repository/Adapter/PutenvAdapter.php
  class PutenvAdapter (line 11) | final class PutenvAdapter implements AdapterInterface
    method __construct (line 18) | private function __construct()
    method create (line 28) | public static function create()
    method isSupported (line 43) | private static function isSupported()
    method read (line 55) | public function read(string $name)
    method write (line 71) | public function write(string $name, string $value)
    method delete (line 85) | public function delete(string $name)

FILE: src/Repository/Adapter/ReaderInterface.php
  type ReaderInterface (line 7) | interface ReaderInterface
    method read (line 16) | public function read(string $name);

FILE: src/Repository/Adapter/ReplacingWriter.php
  class ReplacingWriter (line 7) | final class ReplacingWriter implements WriterInterface
    method __construct (line 38) | public function __construct(WriterInterface $writer, ReaderInterface $...
    method write (line 53) | public function write(string $name, string $value)
    method delete (line 70) | public function delete(string $name)
    method exists (line 90) | private function exists(string $name)

FILE: src/Repository/Adapter/ServerConstAdapter.php
  class ServerConstAdapter (line 10) | final class ServerConstAdapter implements AdapterInterface
    method __construct (line 17) | private function __construct()
    method create (line 27) | public static function create()
    method read (line 40) | public function read(string $name)
    method write (line 68) | public function write(string $name, string $value)
    method delete (line 82) | public function delete(string $name)

FILE: src/Repository/Adapter/WriterInterface.php
  type WriterInterface (line 7) | interface WriterInterface
    method write (line 17) | public function write(string $name, string $value);
    method delete (line 26) | public function delete(string $name);

FILE: src/Repository/AdapterRepository.php
  class AdapterRepository (line 11) | final class AdapterRepository implements RepositoryInterface
    method __construct (line 35) | public function __construct(ReaderInterface $reader, WriterInterface $...
    method has (line 48) | public function has(string $name)
    method get (line 62) | public function get(string $name)
    method set (line 81) | public function set(string $name, string $value)
    method clear (line 99) | public function clear(string $name)

FILE: src/Repository/RepositoryBuilder.php
  class RepositoryBuilder (line 20) | final class RepositoryBuilder
    method __construct (line 68) | private function __construct(array $readers = [], array $writers = [],...
    method createWithNoAdapters (line 81) | public static function createWithNoAdapters()
    method createWithDefaultAdapters (line 91) | public static function createWithDefaultAdapters()
    method defaultAdapters (line 103) | private static function defaultAdapters()
    method isAnAdapterClass (line 120) | private static function isAnAdapterClass(string $name)
    method addReader (line 141) | public function addReader($reader)
    method addWriter (line 174) | public function addWriter($writer)
    method addAdapter (line 208) | public function addAdapter($adapter)
    method immutable (line 235) | public function immutable()
    method allowList (line 247) | public function allowList(?array $allowList = null)
    method make (line 257) | public function make()

FILE: src/Repository/RepositoryInterface.php
  type RepositoryInterface (line 7) | interface RepositoryInterface
    method has (line 16) | public function has(string $name);
    method get (line 27) | public function get(string $name);
    method set (line 39) | public function set(string $name, string $value);
    method clear (line 50) | public function clear(string $name);

FILE: src/Store/File/Paths.php
  class Paths (line 10) | final class Paths
    method __construct (line 19) | private function __construct()
    method filePaths (line 32) | public static function filePaths(array $paths, array $names)

FILE: src/Store/File/Reader.php
  class Reader (line 14) | final class Reader
    method __construct (line 23) | private function __construct()
    method read (line 43) | public static function read(array $filePaths, bool $shortCircuit = tru...
    method readFromFile (line 70) | private static function readFromFile(string $path, ?string $encoding =...

FILE: src/Store/FileStore.php
  class FileStore (line 10) | final class FileStore implements StoreInterface
    method __construct (line 42) | public function __construct(array $filePaths, bool $shortCircuit, ?str...
    method read (line 56) | public function read()

FILE: src/Store/StoreBuilder.php
  class StoreBuilder (line 9) | final class StoreBuilder
    method __construct (line 54) | private function __construct(array $paths = [], array $names = [], boo...
    method createWithNoNames (line 67) | public static function createWithNoNames()
    method createWithDefaultName (line 77) | public static function createWithDefaultName()
    method addPath (line 89) | public function addPath(string $path)
    method addName (line 101) | public function addName(string $name)
    method shortCircuit (line 111) | public function shortCircuit()
    method fileEncoding (line 123) | public function fileEncoding(?string $fileEncoding = null)
    method make (line 133) | public function make()

FILE: src/Store/StoreInterface.php
  type StoreInterface (line 7) | interface StoreInterface
    method read (line 16) | public function read();

FILE: src/Store/StringStore.php
  class StringStore (line 7) | final class StringStore implements StoreInterface
    method __construct (line 23) | public function __construct(string $content)
    method read (line 33) | public function read()

FILE: src/Util/Regex.php
  class Regex (line 13) | final class Regex
    method __construct (line 22) | private function __construct()
    method matches (line 35) | public static function matches(string $pattern, string $subject)
    method occurrences (line 50) | public static function occurrences(string $pattern, string $subject)
    method replaceCallback (line 67) | public static function replaceCallback(string $pattern, callable $call...
    method split (line 82) | public static function split(string $pattern, string $subject)
    method pregAndWrap (line 100) | private static function pregAndWrap(callable $operation, string $subject)

FILE: src/Util/Str.php
  class Str (line 14) | final class Str
    method __construct (line 23) | private function __construct()
    method utf8 (line 36) | public static function utf8(string $input, ?string $encoding = null)
    method pos (line 77) | public static function pos(string $haystack, string $needle)
    method substr (line 92) | public static function substr(string $input, int $start, ?int $length ...
    method len (line 104) | public static function len(string $input)

FILE: src/Validator.php
  class Validator (line 12) | class Validator
    method __construct (line 36) | public function __construct(RepositoryInterface $repository, array $va...
    method required (line 49) | public function required()
    method notEmpty (line 66) | public function notEmpty()
    method isInteger (line 83) | public function isInteger()
    method isBoolean (line 100) | public function isBoolean()
    method allowedValues (line 123) | public function allowedValues(array $choices)
    method allowedRegexValues (line 142) | public function allowedRegexValues(string $regex)
    method assert (line 162) | public function assert(callable $callback, string $message)
    method assertNullable (line 194) | public function assertNullable(callable $callback, string $message)

FILE: tests/Dotenv/DotenvTest.php
  class DotenvTest (line 16) | final class DotenvTest extends TestCase
    method setFolder (line 28) | public static function setFolder()
    method testDotenvThrowsExceptionIfUnableToLoadFile (line 33) | public function testDotenvThrowsExceptionIfUnableToLoadFile()
    method testDotenvThrowsExceptionIfUnableToLoadFiles (line 43) | public function testDotenvThrowsExceptionIfUnableToLoadFiles()
    method testDotenvThrowsExceptionWhenNoFiles (line 53) | public function testDotenvThrowsExceptionWhenNoFiles()
    method testDotenvTriesPathsToLoad (line 63) | public function testDotenvTriesPathsToLoad()
    method testDotenvTriesPathsToLoadTwice (line 69) | public function testDotenvTriesPathsToLoadTwice()
    method testDotenvTriesPathsToSafeLoad (line 78) | public function testDotenvTriesPathsToSafeLoad()
    method testDotenvSkipsLoadingIfFileIsMissing (line 84) | public function testDotenvSkipsLoadingIfFileIsMissing()
    method testDotenvLoadsEnvironmentVars (line 90) | public function testDotenvLoadsEnvironmentVars()
    method testDotenvLoadsEnvironmentVarsMultipleWithShortCircuitMode (line 103) | public function testDotenvLoadsEnvironmentVarsMultipleWithShortCircuit...
    method testDotenvLoadsEnvironmentVarsMultipleWithoutShortCircuitMode (line 113) | public function testDotenvLoadsEnvironmentVarsMultipleWithoutShortCirc...
    method testCommentedDotenvLoadsEnvironmentVars (line 123) | public function testCommentedDotenvLoadsEnvironmentVars()
    method testQuotedDotenvLoadsEnvironmentVars (line 139) | public function testQuotedDotenvLoadsEnvironmentVars()
    method testLargeDotenvLoadsEnvironmentVars (line 154) | public function testLargeDotenvLoadsEnvironmentVars()
    method testDotenvLoadsMultibyteVars (line 162) | public function testDotenvLoadsMultibyteVars()
    method testDotenvLoadsMultibyteUTF8Vars (line 171) | public function testDotenvLoadsMultibyteUTF8Vars()
    method testDotenvLoadWithInvalidEncoding (line 180) | public function testDotenvLoadWithInvalidEncoding()
    method testDotenvLoadsMultibyteWindowsVars (line 190) | public function testDotenvLoadsMultibyteWindowsVars()
    method testMultipleDotenvLoadsEnvironmentVars (line 197) | public function testMultipleDotenvLoadsEnvironmentVars()
    method testExportedDotenvLoadsEnvironmentVars (line 205) | public function testExportedDotenvLoadsEnvironmentVars()
    method testDotenvLoadsEnvGlobals (line 217) | public function testDotenvLoadsEnvGlobals()
    method testDotenvLoadsServerGlobals (line 227) | public function testDotenvLoadsServerGlobals()
    method testDotenvNestedEnvironmentVars (line 237) | public function testDotenvNestedEnvironmentVars()
    method testDotenvNullFileArgumentUsesDefault (line 256) | public function testDotenvNullFileArgumentUsesDefault()
    method testDotenvTrimmedKeys (line 268) | public function testDotenvTrimmedKeys()
    method testDotenvLoadDoesNotOverwriteEnv (line 275) | public function testDotenvLoadDoesNotOverwriteEnv()
    method testDotenvLoadAfterOverload (line 283) | public function testDotenvLoadAfterOverload()
    method testDotenvOverloadAfterLoad (line 291) | public function testDotenvOverloadAfterLoad()
    method testDotenvOverloadDoesOverwriteEnv (line 299) | public function testDotenvOverloadDoesOverwriteEnv()
    method testDotenvAllowsSpecialCharacters (line 306) | public function testDotenvAllowsSpecialCharacters()
    method testMultilineLoading (line 320) | public function testMultilineLoading()
    method testEmptyLoading (line 332) | public function testEmptyLoading()
    method testUnicodeVarNames (line 338) | public function testUnicodeVarNames()
    method testDirectConstructor (line 346) | public function testDirectConstructor()
    method testDotenvParseExample1 (line 361) | public function testDotenvParseExample1()
    method testDotenvParseExample2 (line 374) | public function testDotenvParseExample2()
    method testDotenvParseEmptyCase (line 381) | public function testDotenvParseEmptyCase()

FILE: tests/Dotenv/Loader/LoaderTest.php
  class LoaderTest (line 17) | final class LoaderTest extends TestCase
    method testParserInstanceOf (line 19) | public function testParserInstanceOf()
    method testLoaderWithNoReaders (line 24) | public function testLoaderWithNoReaders()
    method testLoaderWithAllowList (line 35) | public function testLoaderWithAllowList()
    method testLoaderWithGarbage (line 47) | public function testLoaderWithGarbage()
    method providesAdapters (line 62) | public static function providesAdapters()
    method testLoaderWithSpecificAdapter (line 76) | public function testLoaderWithSpecificAdapter($adapter)

FILE: tests/Dotenv/Parser/EntryParserTest.php
  class EntryParserTest (line 13) | final class EntryParserTest extends TestCase
    method testBasicParse (line 15) | public function testBasicParse()
    method testNullParse (line 21) | public function testNullParse()
    method testUnicodeNameParse (line 27) | public function testUnicodeNameParse()
    method testQuotesParse (line 33) | public function testQuotesParse()
    method testNewlineParse (line 39) | public function testNewlineParse()
    method testTabParseDouble (line 45) | public function testTabParseDouble()
    method testTabParseSingle (line 51) | public function testTabParseSingle()
    method testNonEscapeParse1 (line 57) | public function testNonEscapeParse1()
    method testNonEscapeParse2 (line 63) | public function testNonEscapeParse2()
    method testBadEscapeParse (line 69) | public function testBadEscapeParse()
    method testInlineVariable (line 75) | public function testInlineVariable()
    method testInlineVariableOffset (line 81) | public function testInlineVariableOffset()
    method testInlineVariables (line 87) | public function testInlineVariables()
    method testNonInlineVariable (line 93) | public function testNonInlineVariable()
    method testWhitespaceParse (line 100) | public function testWhitespaceParse()
    method testExportParse (line 106) | public function testExportParse()
    method testExportParseTab (line 112) | public function testExportParseTab()
    method testExportParseFail (line 118) | public function testExportParseFail()
    method testClosingSlashParse (line 124) | public function testClosingSlashParse()
    method testParseInvalidSpaces (line 130) | public function testParseInvalidSpaces()
    method testParseStrayEquals (line 136) | public function testParseStrayEquals()
    method testParseInvalidName (line 142) | public function testParseInvalidName()
    method testParserEscapingDouble (line 148) | public function testParserEscapingDouble()
    method testParserEscapingSingle (line 154) | public function testParserEscapingSingle()
    method testParserMissingClosingSingleQuote (line 160) | public function testParserMissingClosingSingleQuote()
    method testParserMissingClosingDoubleQuote (line 166) | public function testParserMissingClosingDoubleQuote()
    method testParserMissingClosingQuotes (line 172) | public function testParserMissingClosingQuotes()
    method testParserClosingQuoteWithEscape (line 178) | public function testParserClosingQuoteWithEscape()
    method checkPositiveResult (line 192) | private function checkPositiveResult(Result $result, string $name, str...
    method checkEmptyResult (line 213) | private function checkEmptyResult(Result $result, string $name)
    method checkErrorResult (line 229) | private function checkErrorResult(Result $result, string $error)

FILE: tests/Dotenv/Parser/LexerTest.php
  class LexerTest (line 10) | final class LexerTest extends TestCase
    method provideLexCases (line 15) | public static function provideLexCases()
    method testLex (line 36) | public function testLex(string $input, array $output)

FILE: tests/Dotenv/Parser/LinesTest.php
  class LinesTest (line 11) | final class LinesTest extends TestCase
    method testProcessBasic (line 13) | public function testProcessBasic()
    method testProcessQuotes (line 35) | public function testProcessQuotes()

FILE: tests/Dotenv/Parser/ParserTest.php
  class ParserTest (line 14) | final class ParserTest extends TestCase
    method testParserInstanceOf (line 16) | public function testParserInstanceOf()
    method testFullParse (line 21) | public function testFullParse()
    method testBadEscapeParse (line 34) | public function testBadEscapeParse()
    method testParseInvalidSpaces (line 42) | public function testParseInvalidSpaces()
    method testParseStrayEquals (line 50) | public function testParseStrayEquals()
    method testParseInvalidName (line 58) | public function testParseInvalidName()
    method checkPositiveEntry (line 74) | private function checkPositiveEntry(Entry $entry, string $name, string...
    method checkEmptyEntry (line 92) | private function checkEmptyEntry(Entry $entry, string $name)

FILE: tests/Dotenv/Repository/Adapter/ArrayAdapterTest.php
  class ArrayAdapterTest (line 10) | final class ArrayAdapterTest extends TestCase
    method testGoodRead (line 12) | public function testGoodRead()
    method testUndefinedRead (line 21) | public function testUndefinedRead()
    method testGoodWrite (line 29) | public function testGoodWrite()
    method testEmptyWrite (line 36) | public function testEmptyWrite()
    method testGoodDelete (line 43) | public function testGoodDelete()
    method createAdapter (line 53) | private static function createAdapter()

FILE: tests/Dotenv/Repository/Adapter/EnvConstAdapterTest.php
  class EnvConstAdapterTest (line 10) | final class EnvConstAdapterTest extends TestCase
    method testGoodRead (line 12) | public function testGoodRead()
    method testFalseRead (line 20) | public function testFalseRead()
    method testTrueRead (line 28) | public function testTrueRead()
    method testBadTypeRead (line 36) | public function testBadTypeRead()
    method testUndefinedRead (line 43) | public function testUndefinedRead()
    method testGoodWrite (line 50) | public function testGoodWrite()
    method testEmptyWrite (line 56) | public function testEmptyWrite()
    method testGoodDelete (line 62) | public function testGoodDelete()
    method createAdapter (line 71) | private static function createAdapter()

FILE: tests/Dotenv/Repository/Adapter/PutenvAdapterTest.php
  class PutenvAdapterTest (line 10) | final class PutenvAdapterTest extends TestCase
    method testGoodRead (line 12) | public function testGoodRead()
    method testUndefinedRead (line 20) | public function testUndefinedRead()
    method testGoodWrite (line 27) | public function testGoodWrite()
    method testEmptyWrite (line 33) | public function testEmptyWrite()
    method testGoodDelete (line 39) | public function testGoodDelete()
    method createAdapter (line 48) | private static function createAdapter()

FILE: tests/Dotenv/Repository/Adapter/ServerConstAdapterTest.php
  class ServerConstAdapterTest (line 10) | final class ServerConstAdapterTest extends TestCase
    method testGoodRead (line 12) | public function testGoodRead()
    method testFalseRead (line 20) | public function testFalseRead()
    method testTrueRead (line 28) | public function testTrueRead()
    method testBadTypeRead (line 36) | public function testBadTypeRead()
    method testUndefinedRead (line 43) | public function testUndefinedRead()
    method testGoodWrite (line 50) | public function testGoodWrite()
    method testEmptyWrite (line 56) | public function testEmptyWrite()
    method testGoodDelete (line 62) | public function testGoodDelete()
    method createAdapter (line 71) | private static function createAdapter()

FILE: tests/Dotenv/Repository/RepositoryTest.php
  class RepositoryTest (line 15) | final class RepositoryTest extends TestCase
    method refreshKeyVal (line 27) | public function refreshKeyVal()
    method load (line 35) | private function load()
    method keyVal (line 51) | private function keyVal(bool $reset = false)
    method key (line 65) | private function key()
    method value (line 77) | private function value()
    method testRepositoryInstanceOf (line 85) | public function testRepositoryInstanceOf()
    method testMutableLoaderClearsEnvironmentVars (line 91) | public function testMutableLoaderClearsEnvironmentVars()
    method testImmutableLoaderCannotClearExistingEnvironmentVars (line 106) | public function testImmutableLoaderCannotClearExistingEnvironmentVars()
    method testImmutableLoaderCanClearSetEnvironmentVars (line 122) | public function testImmutableLoaderCanClearSetEnvironmentVars()
    method testCheckingWhetherVariableExists (line 139) | public function testCheckingWhetherVariableExists()
    method testHasWithBadVariable (line 149) | public function testHasWithBadVariable()
    method testGettingVariableByName (line 158) | public function testGettingVariableByName()
    method testGettingNullVariable (line 167) | public function testGettingNullVariable()
    method testGettingEmptyVariable (line 176) | public function testGettingEmptyVariable()
    method testSettingVariable (line 186) | public function testSettingVariable()
    method testSettingNullVariable (line 197) | public function testSettingNullVariable()
    method testSettingEmptyVariable (line 206) | public function testSettingEmptyVariable()
    method testClearingVariable (line 216) | public function testClearingVariable()
    method testClearingVariableWithArrayAdapter (line 227) | public function testClearingVariableWithArrayAdapter()
    method testClearingNullVariable (line 239) | public function testClearingNullVariable()
    method testClearingEmptyVariable (line 248) | public function testClearingEmptyVariable()
    method testCannotSetVariableOnImmutableInstance (line 258) | public function testCannotSetVariableOnImmutableInstance()
    method testCannotClearVariableOnImmutableInstance (line 271) | public function testCannotClearVariableOnImmutableInstance()
    method testBuildWithBadReader (line 282) | public function testBuildWithBadReader()
    method testBuildWithBadWriter (line 290) | public function testBuildWithBadWriter()
    method testBuildWithBadAdapter (line 298) | public function testBuildWithBadAdapter()

FILE: tests/Dotenv/Store/StoreTest.php
  class StoreTest (line 13) | final class StoreTest extends TestCase
    method setFolder (line 25) | public static function setFolder()
    method testBasicReadDirect (line 30) | public function testBasicReadDirect()
    method testBasicRead (line 42) | public function testBasicRead()
    method testBasicReadWindowsEncoding (line 53) | public function testBasicReadWindowsEncoding()
    method testBasicReadBadEncoding (line 66) | public function testBasicReadBadEncoding()
    method testFileReadMultipleShortCircuitModeDirect (line 79) | public function testFileReadMultipleShortCircuitModeDirect()
    method testFileReadMultipleShortCircuitMode (line 91) | public function testFileReadMultipleShortCircuitMode()
    method testFileReadMultipleWithoutShortCircuitModeDirect (line 105) | public function testFileReadMultipleWithoutShortCircuitModeDirect()
    method testFileReadMultipleWithoutShortCircuitMode (line 119) | public function testFileReadMultipleWithoutShortCircuitMode()
    method testFileReadWithUtf8WithBomEncoding (line 130) | public function testFileReadWithUtf8WithBomEncoding()

FILE: tests/Dotenv/ValidatorTest.php
  class ValidatorTest (line 13) | final class ValidatorTest extends TestCase
    method setFolder (line 25) | public static function setFolder()
    method createArrayDotenv (line 35) | public static function createArrayDotenv(string $name = '.env')
    method testDotenvRequiredStringEnvironmentVars (line 45) | public function testDotenvRequiredStringEnvironmentVars()
    method testDotenvAllowedValues (line 55) | public function testDotenvAllowedValues()
    method testDotenvAllowedValuesIfPresent (line 65) | public function testDotenvAllowedValuesIfPresent()
    method testDotenvAllowedValuesIfNotPresent (line 75) | public function testDotenvAllowedValuesIfNotPresent()
    method testDotenvProhibitedValues (line 82) | public function testDotenvProhibitedValues()
    method testDotenvProhibitedValuesIfPresent (line 93) | public function testDotenvProhibitedValuesIfPresent()
    method testDotenvRequiredThrowsRuntimeException (line 104) | public function testDotenvRequiredThrowsRuntimeException()
    method testDotenvRequiredArrayEnvironmentVars (line 122) | public function testDotenvRequiredArrayEnvironmentVars()
    method testDotenvAssertions (line 129) | public function testDotenvAssertions()
    method testDotenvEmptyThrowsRuntimeException (line 173) | public function testDotenvEmptyThrowsRuntimeException()
    method testDotenvEmptyWhenNotPresent (line 187) | public function testDotenvEmptyWhenNotPresent()
    method testDotenvStringOfSpacesConsideredEmpty (line 194) | public function testDotenvStringOfSpacesConsideredEmpty()
    method validBooleanValuesDataProvider (line 210) | public static function validBooleanValuesDataProvider()
    method testCanValidateBooleans (line 243) | public function testCanValidateBooleans(string $boolean)
    method testCanValidateBooleansIfPresent (line 254) | public function testCanValidateBooleansIfPresent(string $boolean)
    method invalidBooleanValuesDataProvider (line 266) | public static function invalidBooleanValuesDataProvider()
    method testCanInvalidateNonBooleans (line 284) | public function testCanInvalidateNonBooleans(string $boolean)
    method testCanInvalidateNonBooleansIfPresent (line 298) | public function testCanInvalidateNonBooleansIfPresent(string $boolean)
    method testCanInvalidateBooleanNonExist (line 309) | public function testCanInvalidateBooleanNonExist()
    method testIfPresentBooleanNonExist (line 323) | public function testIfPresentBooleanNonExist()
    method validIntegerValuesDataProvider (line 335) | public static function validIntegerValuesDataProvider()
    method testCanValidateIntegers (line 350) | public function testCanValidateIntegers(string $integer)
    method testCanValidateIntegersIfPresent (line 361) | public function testCanValidateIntegersIfPresent(string $integer)
    method invalidIntegerValuesDataProvider (line 373) | public static function invalidIntegerValuesDataProvider()
    method testCanInvalidateNonIntegers (line 392) | public function testCanInvalidateNonIntegers(string $integer)
    method testCanInvalidateNonIntegersIfExist (line 406) | public function testCanInvalidateNonIntegersIfExist(string $integer)
    method testCanInvalidateIntegerNonExist (line 417) | public function testCanInvalidateIntegerNonExist()
    method testIfPresentIntegerNonExist (line 431) | public function testIfPresentIntegerNonExist()
    method testDotenvRegexMatchPass (line 441) | public function testDotenvRegexMatchPass()
    method testDotenvRegexMatchFail (line 448) | public function testDotenvRegexMatchFail()
    method testDotenvRegexMatchError (line 459) | public function testDotenvRegexMatchError()
    method testDotenvRegexMatchNotPresent (line 473) | public function testDotenvRegexMatchNotPresent()
Condensed preview — 91 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (228K chars).
[
  {
    "path": ".editorconfig",
    "chars": 220,
    "preview": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 4\ntrim_"
  },
  {
    "path": ".gitattributes",
    "chars": 370,
    "preview": "* text=auto\n\n/tests export-ignore\n/.editorconfig export-ignore\n/.gitattributes export-ignore\n/.github export-ignore\n/.gi"
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "chars": 5488,
    "preview": "# CONTRIBUTOR COVENANT CODE OF CONDUCT\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 1105,
    "preview": "# CONTRIBUTION GUIDELINES\n\nContributions are **welcome** and will be fully **credited**.\n\nWe accept contributions via pu"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 62,
    "preview": "github: GrahamCampbell\ntidelift: \"packagist/vlucas/phpdotenv\"\n"
  },
  {
    "path": ".github/SECURITY.md",
    "chars": 534,
    "preview": "# SECURITY POLICY\n\n## Supported Versions\n\nAfter each new major release, the previous release will be supported for no\nle"
  },
  {
    "path": ".github/workflows/static.yml",
    "chars": 889,
    "preview": "name: Static Analysis\n\non:\n  push:\n  pull_request:\n\njobs:\n  phpstan:\n    name: PHPStan\n    runs-on: ubuntu-24.04\n\n    st"
  },
  {
    "path": ".github/workflows/tests.yml",
    "chars": 1713,
    "preview": "name: Tests\n\non:\n  push:\n  pull_request:\n\njobs:\n  latest:\n    name: PHP ${{ matrix.php }} Latest\n    runs-on: ubuntu-24."
  },
  {
    "path": ".gitignore",
    "chars": 87,
    "preview": ".phpunit.result.cache\ncomposer.lock\nphpstan.neon\nphpstan.tests.neon\nphpunit.xml\nvendor\n"
  },
  {
    "path": "LICENSE",
    "chars": 1557,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2014, Graham Campbell.\nCopyright (c) 2013, Vance Lucas.\nAll rights reserved.\n\nRedist"
  },
  {
    "path": "Makefile",
    "chars": 996,
    "preview": "install:\n\t@docker run -it -w /data -v ${PWD}:/data:delegated -v ~/.composer:/root/.composer:delegated --entrypoint compo"
  },
  {
    "path": "README.md",
    "chars": 12045,
    "preview": "PHP dotenv\n==========\n\nLoads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.\n\n![B"
  },
  {
    "path": "UPGRADING.md",
    "chars": 13902,
    "preview": "# Upgrading Guide\n\n## V5.5 to V5.6\n\nBumping the minimum required PHP version is not a breaking change, however it is not"
  },
  {
    "path": "composer.json",
    "chars": 1659,
    "preview": "{\n    \"name\": \"vlucas/phpdotenv\",\n    \"description\": \"Loads environment variables from `.env` to `getenv()`, `$_ENV` and"
  },
  {
    "path": "phpstan-baseline.neon",
    "chars": 7175,
    "preview": "parameters:\n\tignoreErrors:\n\t\t-\n\t\t\tmessage: '#^PHPDoc tag @var with type PhpOption\\\\Option\\<Dotenv\\\\Parser\\\\Value\\> is no"
  },
  {
    "path": "phpstan.neon.dist",
    "chars": 91,
    "preview": "includes:\n    - phpstan-baseline.neon\n\nparameters:\n    level: max\n    paths:\n        - src\n"
  },
  {
    "path": "phpunit.xml.dist",
    "chars": 698,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" backupGlobals=\"fal"
  },
  {
    "path": "src/Dotenv.php",
    "chars": 8267,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv;\n\nuse Dotenv\\Exception\\InvalidPathException;\nuse Dotenv\\Loader\\Loader;"
  },
  {
    "path": "src/Exception/ExceptionInterface.php",
    "chars": 136,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Exception;\n\nuse Throwable;\n\ninterface ExceptionInterface extends Throw"
  },
  {
    "path": "src/Exception/InvalidEncodingException.php",
    "chars": 204,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Exception;\n\nuse InvalidArgumentException;\n\nfinal class InvalidEncoding"
  },
  {
    "path": "src/Exception/InvalidFileException.php",
    "chars": 200,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Exception;\n\nuse InvalidArgumentException;\n\nfinal class InvalidFileExce"
  },
  {
    "path": "src/Exception/InvalidPathException.php",
    "chars": 200,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Exception;\n\nuse InvalidArgumentException;\n\nfinal class InvalidPathExce"
  },
  {
    "path": "src/Exception/ValidationException.php",
    "chars": 183,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Exception;\n\nuse RuntimeException;\n\nfinal class ValidationException ext"
  },
  {
    "path": "src/Loader/Loader.php",
    "chars": 1490,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Loader;\n\nuse Dotenv\\Parser\\Entry;\nuse Dotenv\\Parser\\Value;\nuse Dotenv\\"
  },
  {
    "path": "src/Loader/LoaderInterface.php",
    "chars": 458,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Loader;\n\nuse Dotenv\\Repository\\RepositoryInterface;\n\ninterface LoaderI"
  },
  {
    "path": "src/Loader/Resolver.php",
    "chars": 1766,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Loader;\n\nuse Dotenv\\Parser\\Value;\nuse Dotenv\\Repository\\RepositoryInte"
  },
  {
    "path": "src/Parser/Entry.php",
    "chars": 1015,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Parser;\n\nuse PhpOption\\Option;\n\nfinal class Entry\n{\n    /**\n     * The"
  },
  {
    "path": "src/Parser/EntryParser.php",
    "chars": 12472,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Parser;\n\nuse Dotenv\\Util\\Regex;\nuse Dotenv\\Util\\Str;\nuse GrahamCampbel"
  },
  {
    "path": "src/Parser/Lexer.php",
    "chars": 1272,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Parser;\n\nfinal class Lexer\n{\n    /**\n     * The regex for each type of"
  },
  {
    "path": "src/Parser/Lines.php",
    "chars": 3148,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Parser;\n\nuse Dotenv\\Util\\Regex;\nuse Dotenv\\Util\\Str;\n\nfinal class Line"
  },
  {
    "path": "src/Parser/Parser.php",
    "chars": 1749,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Parser;\n\nuse Dotenv\\Exception\\InvalidFileException;\nuse Dotenv\\Util\\Re"
  },
  {
    "path": "src/Parser/ParserInterface.php",
    "chars": 334,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Parser;\n\ninterface ParserInterface\n{\n    /**\n     * Parse content into"
  },
  {
    "path": "src/Parser/Value.php",
    "chars": 1588,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Parser;\n\nuse Dotenv\\Util\\Str;\n\nfinal class Value\n{\n    /**\n     * The "
  },
  {
    "path": "src/Repository/Adapter/AdapterInterface.php",
    "chars": 346,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Repository\\Adapter;\n\ninterface AdapterInterface extends ReaderInterfac"
  },
  {
    "path": "src/Repository/Adapter/ApacheAdapter.php",
    "chars": 1974,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Repository\\Adapter;\n\nuse PhpOption\\None;\nuse PhpOption\\Option;\nuse Php"
  },
  {
    "path": "src/Repository/Adapter/ArrayAdapter.php",
    "chars": 1611,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Repository\\Adapter;\n\nuse PhpOption\\Option;\nuse PhpOption\\Some;\n\nfinal "
  },
  {
    "path": "src/Repository/Adapter/EnvConstAdapter.php",
    "chars": 1884,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Repository\\Adapter;\n\nuse PhpOption\\Option;\nuse PhpOption\\Some;\n\nfinal "
  },
  {
    "path": "src/Repository/Adapter/GuardedWriter.php",
    "chars": 1875,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Repository\\Adapter;\n\nfinal class GuardedWriter implements WriterInterf"
  },
  {
    "path": "src/Repository/Adapter/ImmutableWriter.php",
    "chars": 2543,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Repository\\Adapter;\n\nfinal class ImmutableWriter implements WriterInte"
  },
  {
    "path": "src/Repository/Adapter/MultiReader.php",
    "chars": 969,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Repository\\Adapter;\n\nuse PhpOption\\None;\n\nfinal class MultiReader impl"
  },
  {
    "path": "src/Repository/Adapter/MultiWriter.php",
    "chars": 1300,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Repository\\Adapter;\n\nfinal class MultiWriter implements WriterInterfac"
  },
  {
    "path": "src/Repository/Adapter/PutenvAdapter.php",
    "chars": 1893,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Repository\\Adapter;\n\nuse PhpOption\\None;\nuse PhpOption\\Option;\nuse Php"
  },
  {
    "path": "src/Repository/Adapter/ReaderInterface.php",
    "chars": 300,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Repository\\Adapter;\n\ninterface ReaderInterface\n{\n    /**\n     * Read a"
  },
  {
    "path": "src/Repository/Adapter/ReplacingWriter.php",
    "chars": 2212,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Repository\\Adapter;\n\nfinal class ReplacingWriter implements WriterInte"
  },
  {
    "path": "src/Repository/Adapter/ServerConstAdapter.php",
    "chars": 1899,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Repository\\Adapter;\n\nuse PhpOption\\Option;\nuse PhpOption\\Some;\n\nfinal "
  },
  {
    "path": "src/Repository/Adapter/WriterInterface.php",
    "chars": 518,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Repository\\Adapter;\n\ninterface WriterInterface\n{\n    /**\n     * Write "
  },
  {
    "path": "src/Repository/AdapterRepository.php",
    "chars": 2394,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Repository;\n\nuse Dotenv\\Repository\\Adapter\\ReaderInterface;\nuse Dotenv"
  },
  {
    "path": "src/Repository/RepositoryBuilder.php",
    "chars": 8219,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Repository;\n\nuse Dotenv\\Repository\\Adapter\\AdapterInterface;\nuse Doten"
  },
  {
    "path": "src/Repository/RepositoryInterface.php",
    "chars": 933,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Repository;\n\ninterface RepositoryInterface\n{\n    /**\n     * Determine "
  },
  {
    "path": "src/Store/File/Paths.php",
    "chars": 748,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Store\\File;\n\n/**\n * @internal\n */\nfinal class Paths\n{\n    /**\n     * T"
  },
  {
    "path": "src/Store/File/Reader.php",
    "chars": 2104,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Store\\File;\n\nuse Dotenv\\Exception\\InvalidEncodingException;\nuse Dotenv"
  },
  {
    "path": "src/Store/FileStore.php",
    "chars": 1680,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Store;\n\nuse Dotenv\\Exception\\InvalidPathException;\nuse Dotenv\\Store\\Fi"
  },
  {
    "path": "src/Store/StoreBuilder.php",
    "chars": 3172,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Store;\n\nuse Dotenv\\Store\\File\\Paths;\n\nfinal class StoreBuilder\n{\n    /"
  },
  {
    "path": "src/Store/StoreInterface.php",
    "chars": 316,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Store;\n\ninterface StoreInterface\n{\n    /**\n     * Read the content of "
  },
  {
    "path": "src/Store/StringStore.php",
    "chars": 589,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Store;\n\nfinal class StringStore implements StoreInterface\n{\n    /**\n  "
  },
  {
    "path": "src/Util/Regex.php",
    "chars": 3152,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Util;\n\nuse GrahamCampbell\\ResultType\\Error;\nuse GrahamCampbell\\ResultT"
  },
  {
    "path": "src/Util/Str.php",
    "chars": 2792,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Util;\n\nuse GrahamCampbell\\ResultType\\Error;\nuse GrahamCampbell\\ResultT"
  },
  {
    "path": "src/Validator.php",
    "chars": 5187,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv;\n\nuse Dotenv\\Exception\\ValidationException;\nuse Dotenv\\Repository\\Repo"
  },
  {
    "path": "tests/Dotenv/DotenvTest.php",
    "chars": 13943,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Tests;\n\nuse Dotenv\\Dotenv;\nuse Dotenv\\Exception\\InvalidEncodingExcepti"
  },
  {
    "path": "tests/Dotenv/Loader/LoaderTest.php",
    "chars": 3180,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Tests\\Loader;\n\nuse Dotenv\\Exception\\InvalidFileException;\nuse Dotenv\\L"
  },
  {
    "path": "tests/Dotenv/Parser/EntryParserTest.php",
    "chars": 7640,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Tests\\Parser;\n\nuse Dotenv\\Parser\\Entry;\nuse Dotenv\\Parser\\EntryParser;"
  },
  {
    "path": "tests/Dotenv/Parser/LexerTest.php",
    "chars": 895,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Tests\\Parser;\n\nuse Dotenv\\Parser\\Lexer;\nuse PHPUnit\\Framework\\TestCase"
  },
  {
    "path": "tests/Dotenv/Parser/LinesTest.php",
    "chars": 1947,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Tests\\Parser;\n\nuse Dotenv\\Parser\\Lines;\nuse Dotenv\\Util\\Regex;\nuse PHP"
  },
  {
    "path": "tests/Dotenv/Parser/ParserTest.php",
    "chars": 3053,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Tests\\Parser;\n\nuse Dotenv\\Exception\\InvalidFileException;\nuse Dotenv\\P"
  },
  {
    "path": "tests/Dotenv/Repository/Adapter/ArrayAdapterTest.php",
    "chars": 1566,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Tests\\Repository\\Adapter;\n\nuse Dotenv\\Repository\\Adapter\\ArrayAdapter;"
  },
  {
    "path": "tests/Dotenv/Repository/Adapter/EnvConstAdapterTest.php",
    "chars": 2053,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Tests\\Repository\\Adapter;\n\nuse Dotenv\\Repository\\Adapter\\EnvConstAdapt"
  },
  {
    "path": "tests/Dotenv/Repository/Adapter/PutenvAdapterTest.php",
    "chars": 1360,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Tests\\Repository\\Adapter;\n\nuse Dotenv\\Repository\\Adapter\\PutenvAdapter"
  },
  {
    "path": "tests/Dotenv/Repository/Adapter/ServerConstAdapterTest.php",
    "chars": 2086,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Tests\\Repository\\Adapter;\n\nuse Dotenv\\Repository\\Adapter\\ServerConstAd"
  },
  {
    "path": "tests/Dotenv/Repository/RepositoryTest.php",
    "chars": 8486,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Tests\\Repository;\n\nuse Dotenv\\Dotenv;\nuse Dotenv\\Repository\\Adapter\\Ar"
  },
  {
    "path": "tests/Dotenv/Store/StoreTest.php",
    "chars": 3948,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Tests\\Store;\n\nuse Dotenv\\Exception\\InvalidEncodingException;\nuse Doten"
  },
  {
    "path": "tests/Dotenv/ValidatorTest.php",
    "chars": 14373,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Dotenv\\Tests;\n\nuse Dotenv\\Dotenv;\nuse Dotenv\\Exception\\ValidationException;\nu"
  },
  {
    "path": "tests/fixtures/env/assertions.env",
    "chars": 189,
    "preview": "ASSERTVAR1=val1\n\nASSERTVAR2=\"\"\n\nASSERTVAR3=\"val3   \"\nASSERTVAR4=\"0\" # empty looking value\nASSERTVAR5=\"#foo\"\nASSERTVAR6=\""
  },
  {
    "path": "tests/fixtures/env/booleans.env",
    "chars": 825,
    "preview": "VALID_EXPLICIT_LOWERCASE_TRUE=true\nVALID_EXPLICIT_LOWERCASE_FALSE=false\nVALID_EXPLICIT_UPPERCASE_TRUE=TRUE\nVALID_EXPLICI"
  },
  {
    "path": "tests/fixtures/env/commented.env",
    "chars": 447,
    "preview": "# This is a comment\nCFOO=bar\n#CBAR=baz\n#CZOO=goo # a comment on a commented row\nCSPACED=\"with spaces\" # this is a commen"
  },
  {
    "path": "tests/fixtures/env/empty.env",
    "chars": 10,
    "preview": "EMPTY_VAR\n"
  },
  {
    "path": "tests/fixtures/env/example.env",
    "chars": 13,
    "preview": "EG=\"example\"\n"
  },
  {
    "path": "tests/fixtures/env/exported.env",
    "chars": 135,
    "preview": "export\tEFOO=\"bar\"\nexport EBAR = \"baz\"\nexport ESPACED=\"with spaces\"\nexport \"EDQUOTED\" = 123\nexport 'ESQUOTED' = 456\n\nexpo"
  },
  {
    "path": "tests/fixtures/env/immutable.env",
    "chars": 16,
    "preview": "IMMUTABLE=false\n"
  },
  {
    "path": "tests/fixtures/env/integers.env",
    "chars": 310,
    "preview": "VALID_ZERO=0\nVALID_ONE=1\nVALID_TWO=2\n\nVALID_LARGE=99999999\nVALID_HUGE=99999999999999999999999999999999\n\nINVALID_SOMETHIN"
  },
  {
    "path": "tests/fixtures/env/large.env",
    "chars": 10939,
    "preview": "LARGE='11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
  },
  {
    "path": "tests/fixtures/env/multibyte.env",
    "chars": 81,
    "preview": "MB1=\"Ā ā Ă ă Ą ą Ć ć Ĉ ĉ Ċ ċ Č č Ď ď Đ đ Ē ē Ĕ ĕ Ė ė Ę ę Ě ě\"\nMB2=行内支付\nAPP_ENV=🚀\n"
  },
  {
    "path": "tests/fixtures/env/multiline.env",
    "chars": 470,
    "preview": "TEST=\"test\n     test\\\"test\\\"\n     test\"\n\nTEST_ND=\"test\\ntest\"\nTEST_NS='test\\ntest'\n\nTEST_EQD=\"https://vision.googleapis."
  },
  {
    "path": "tests/fixtures/env/multiple.env",
    "chars": 40,
    "preview": "MULTI1=foo\nMULTI2=${MULTI1}\nMULTI1=bar\n\n"
  },
  {
    "path": "tests/fixtures/env/mutable.env",
    "chars": 12,
    "preview": "MUTABLE=true"
  },
  {
    "path": "tests/fixtures/env/nested.env",
    "chars": 306,
    "preview": "NVAR1=\"Hellō\"\nNVAR2=\"World!\"\nNVAR3=\"{$NVAR1} {$NVAR2}\"\nNVAR4=\"${NVAR1} ${NVAR2}\"\nNVAR5=\"$NVAR1 {NVAR2}\"\nN.VAR6=\"Special "
  },
  {
    "path": "tests/fixtures/env/quoted.env",
    "chars": 272,
    "preview": "QFOO=\"bar\"\nQBAR=\"baz\"\nQSPACED=\"with spaces\"\nQEQUALS=\"pgsql:host=localhost;dbname=test\"\n\nQNULL=\"\"\nQWHITESPACE = \"no space"
  },
  {
    "path": "tests/fixtures/env/specialchars.env",
    "chars": 281,
    "preview": "SPVAR1=\"$a6^C7k%zs+e^.jvjXk\"\nSPVAR2=\"?BUty3koaV3%GA*hMAwH}B\"\nSPVAR3=\"jdgEB4{QgEC]HL))&GcXxokB+wqoN+j>xkV7K?m$r\"\nSPVAR4=\""
  },
  {
    "path": "tests/fixtures/env/unicodevarnames.env",
    "chars": 69,
    "preview": "AlbertÅberg=Skybert\nДатаЗакрытияРасчетногоПериода='2022-04-01T00:00'\n"
  },
  {
    "path": "tests/fixtures/env/utf8-with-bom-encoding.env",
    "chars": 38,
    "preview": "FOO=bar\nBAR=baz\nSPACED=\"with spaces\"\n"
  },
  {
    "path": "tests/fixtures/env/windows.env",
    "chars": 7,
    "preview": "MBW=\"\"\n"
  },
  {
    "path": "vendor-bin/phpstan/composer.json",
    "chars": 382,
    "preview": "{\n    \"require\": {\n        \"php\": \"^8.5\",\n        \"phpstan/phpstan\": \"2.1.33\",\n        \"phpstan/extension-installer\": \"1"
  }
]

About this extraction

This page contains the full source code of the vlucas/phpdotenv GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 91 files (208.1 KB), approximately 53.6k tokens, and a symbol index with 391 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!