PHPackages                             phpdot/env - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. phpdot/env

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

phpdot/env
==========

Typed, schema-validated, immutable .env configuration for modern PHP.

v1.1.3(2mo ago)08MITPHPPHP &gt;=8.3

Since Apr 2Pushed 2mo agoCompare

[ Source](https://github.com/phpdot/env)[ Packagist](https://packagist.org/packages/phpdot/env)[ RSS](/packages/phpdot-env/feed)WikiDiscussions main Synced 4w ago

READMEChangelogDependencies (8)Versions (7)Used By (0)

phpdot/env
==========

[](#phpdotenv)

Typed, schema-validated, immutable `.env` configuration for modern PHP.

Install
-------

[](#install)

```
composer require phpdot/env
```

Zero dependencies. Pure PHP 8.3+.

Quick Start
-----------

[](#quick-start)

Two access patterns are supported — pick whichever fits your bootstrap.

### Instance-based

[](#instance-based)

```
use PHPdot\Env\Env;

$env = Env::create(
    schema: __DIR__ . '/env.schema.php',
    paths: __DIR__ . '/.env',
);

$env->get('APP_PORT');    // int(8080)
$env->get('APP_DEBUG');   // bool(false)
$env->get('APP_ENV');     // AppEnv::PRODUCTION
$env->get('DB_HOST');     // string("localhost")
$env->get('ORIGINS');     // ['http://localhost', 'https://example.com']
```

### Global facade (recommended for app bootstrap)

[](#global-facade-recommended-for-app-bootstrap)

```
use PHPdot\Env\Env;

// Once, at the top of your bootstrap
Env::init(
    schema: __DIR__ . '/env.schema.php',
    paths: __DIR__ . '/.env',
);

// Anywhere in your app — pure array lookup
env('APP_PORT');                // int(8080)
env('APP_DEBUG', false);        // bool — default returned if key missing or no Env initialized
env('APP_ENV');                 // AppEnv::PRODUCTION
```

`Env::init()` is a thin wrapper over `safeCreate()` — missing `.env` files are silently skipped, schema defaults are used. The global `env()` helper reads from the singleton.

Every value is typed. Every key is validated. Every access is a pure array lookup.

---

Architecture
------------

[](#architecture)

 ```
graph TD
    FILES[".env file(s)One or more sources,loaded in order — laterfiles override earlier ones"]

    subgraph Parser
        direction TB
        LEX["LexerCharacter-by-character tokenizer.Handles quotes, escapes, multiline,BOM, export prefix, comments"]
        RES["ResolverVariable interpolation,cross-file references,circular reference detection"]
        LEX --> RES
    end

    subgraph Schema
        direction TB
        SCH["EnvSchemaType casting + constraint validation:STRING, INT, FLOAT, BOOL,ENUM, LIST, JSON.required, min/max, allowed, pattern"]
    end

    ENV["EnvImmutable. readonly arrays.All values eagerly cast at boot.get / env helper = pure array lookup.Zero computation per request"]

    FILES --> Parser
    Parser --> Schema
    Schema --> ENV
```

      Loading ---

Schema
------

[](#schema)

The schema is the source of truth. Every env var must be declared.

```
// env.schema.php
use PHPdot\Env\Enum\EnvType;
use PHPdot\Env\Enum\AppEnv;

return [
    'APP_ENV' => [
        'enum'     => AppEnv::class,
        'required' => true,
        'default'  => AppEnv::DEVELOPMENT,
    ],
    'APP_DEBUG' => [
        'type'    => EnvType::BOOL,
        'default' => false,
    ],
    'APP_PORT' => [
        'type'    => EnvType::INT,
        'default' => 8080,
        'min'     => 1,
        'max'     => 65535,
    ],
    'APP_KEY' => [
        'type'      => EnvType::STRING,
        'required'  => true,
        'not_empty' => true,
        'sensitive' => true,
    ],
    'ALLOWED_ORIGINS' => [
        'type'    => EnvType::LIST,
        'default' => [],
    ],
    'FEATURE_CONFIG' => [
        'type'    => EnvType::JSON,
        'default' => [],
    ],
    'LOG_LEVEL' => [
        'default' => 'info',
        'allowed' => ['debug', 'info', 'warning', 'error'],
    ],
];
```

### Type System

[](#type-system)

TypePHP returnExample`STRING``string``APP_NAME=MyApp` → `"MyApp"``INT``int``PORT=8080` → `8080``FLOAT``float``RATE=1.5` → `1.5``BOOL``bool``DEBUG=true` → `true``ENUM``BackedEnum``ENV=production` → `AppEnv::PRODUCTION``LIST``list``IPS=a,b,c` → `["a","b","c"]``JSON``mixed``CFG={"a":1}` → `["a" => 1]`Bool recognizes (case-insensitive): `true/false`, `1/0`, `yes/no`, `on/off`.

### Constraints

[](#constraints)

ConstraintApplies toExample`required`AllKey must exist or have default`not_empty`All`''` after trim fails`min`INT, FLOAT`'min' => 1``max`INT, FLOAT`'max' => 65535``allowed`STRING`'allowed' => ['debug', 'info']``pattern`STRING`'pattern' => '/^https?:\/\//'``sensitive`AllMasked in `allMasked()`---

Multi-File Loading
------------------

[](#multi-file-loading)

Files load in order. Later files override earlier ones.

```
$env = Env::create(
    schema: __DIR__ . '/env.schema.php',
    paths: [
        __DIR__ . '/.env',        // base
        __DIR__ . '/.env.local',  // overrides (gitignored)
    ],
);
```

Cross-file interpolation works:

```
# .env
BASE_URL=https://example.com

# .env.local
API_URL=${BASE_URL}/api    → https://example.com/api

```

---

.env Syntax
-----------

[](#env-syntax)

### Values

[](#values)

```
SIMPLE=value
DOUBLE="value with spaces"
SINGLE='literal ${no-interpolation}'
EMPTY=
```

### Escapes (double-quoted only)

[](#escapes-double-quoted-only)

```
NEWLINE="hello\nworld"
TAB="col1\tcol2"
BACKSLASH="back\\slash"
QUOTE="say\"hi\""
DOLLAR="cost\$5"
```

### Comments

[](#comments)

```
# Full line comment
KEY=value # inline comment
HASH=color#fff           # no space before # = part of value
QUOTED="value # kept"    # inside quotes = part of value
```

### Multiline

[](#multiline)

```
RSA_KEY="-----BEGIN RSA KEY-----
MIIBogIBAAJBALRiMLAH
-----END RSA KEY-----"
```

### Interpolation

[](#interpolation)

```
BASE=/app
DATA=${BASE}/data        # /app/data
LOGS=$BASE/logs          # /app/logs
NESTED=${DATA}/cache     # /app/data/cache
LITERAL='${BASE}/raw'   # ${BASE}/raw (no interpolation)
```

### Export Prefix

[](#export-prefix)

```
export FOO=bar           # FOO=bar (export stripped)
```

---

Safe Loading
------------

[](#safe-loading)

For Docker/k8s where `.env` may not exist:

```
$env = Env::safeCreate(
    schema: __DIR__ . '/env.schema.php',
    paths: __DIR__ . '/.env',
);
```

Missing files are silently skipped. Schema defaults are used.

---

Testing
-------

[](#testing)

```
$env = Env::createForTesting(
    schema: [
        'DB_HOST' => ['required' => true],
        'DB_PORT' => ['type' => EnvType::INT, 'default' => 5432],
    ],
    values: ['DB_HOST' => 'localhost'],
);

$env->get('DB_HOST');  // 'localhost'
$env->get('DB_PORT');  // 5432
```

---

Instance API
------------

[](#instance-api)

Beyond `get()`, the `Env` instance exposes:

MethodReturnsPurpose`$env->get($key)``mixed` (typed)Throws `SchemaException` on unknown key`$env->has($key)``bool`True if explicitly set in a `.env` file (not just defaulted)`$env->all()``array`All typed values, including defaults`$env->allMasked()``array`Same, but `sensitive` keys replaced with `***``$env->getRaw($key)``string|null`Raw string before type cast`$env->getSchema()``EnvSchema`The compiled schema`$env->getLoadedFiles()``list`Paths of `.env` files actually parsed`$env->compile($path)``void`Write a cache file for fast worker bootThe static facade exposes a parallel surface: `Env::env($key, $default)`, `Env::getInstance()`, `Env::resetInstance()` (testing).

---

Sensitive Values
----------------

[](#sensitive-values)

```
$env->get('API_KEY');     // "actual-secret-key"
$env->allMasked();        // ['API_KEY' => '***', 'DB_HOST' => 'localhost', ...]
```

`allMasked()` is safe for logging and error reports.

---

Config Caching
--------------

[](#config-caching)

For production — skip parsing on every worker boot:

```
// Deploy script (run once)
$env = Env::create(schema: ..., paths: ...);
$env->compile(__DIR__ . '/cache/env.php');

// Application boot (every worker)
$env = Env::createFromCache(
    schema: __DIR__ . '/env.schema.php',
    cachePath: __DIR__ . '/cache/env.php',
);
```

Opcache caches the compiled file. Zero disk I/O, zero parsing per worker.

---

EnvEditor (CLI Only)
--------------------

[](#enveditor-cli-only)

Write tool for setup wizards and deployment scripts.

```
use PHPdot\Env\EnvEditor;
use PHPdot\Env\Schema\EnvSchema;
use PHPdot\Env\Enum\AppEnv;

$editor = new EnvEditor(__DIR__ . '/.env', new EnvSchema($schema));

$editor->set('DB_HOST', 'new-host.example.com');
$editor->set('APP_ENV', AppEnv::STAGING);
$editor->remove('LOG_LEVEL');
$editor->save();
```

Preserves comments, blank lines, and key order.

---

Parsing a String
----------------

[](#parsing-a-string)

```
$values = Env::parseString("FOO=bar\nBAZ=\"\${FOO}/qux\"");
// ['FOO' => 'bar', 'BAZ' => 'bar/qux']
```

---

Swoole Safety
-------------

[](#swoole-safety)

`Env` is immutable. `readonly` arrays. Zero mutation methods. Two safe patterns:

```
// Static facade — call Env::init() once per worker boot (recommended)
Env::init(
    schema: __DIR__ . '/env.schema.php',
    paths: __DIR__ . '/.env',
);
// Anywhere afterwards: env('APP_KEY') — shared by every coroutine, no per-request cost.

// Or register as a DI singleton if you prefer instance access
Env::class => singleton(fn() => Env::create(
    schema: __DIR__ . '/env.schema.php',
    paths: __DIR__ . '/.env',
)),
```

---

Package Structure
-----------------

[](#package-structure)

```
src/
├── Env.php                    Main read-only facade
├── EnvEditor.php              CLI-only write tool
├── Schema/
│   ├── EnvSchema.php          Type casting + validation
│   └── Definition.php         Variable definition value object
├── Parser/
│   ├── Parser.php             Orchestrator
│   ├── Lexer.php              Character-by-character tokenizer
│   ├── Entry.php              Parsed entry value object
│   └── Resolver.php           Variable interpolation
├── Enum/
│   ├── EnvType.php            STRING, INT, FLOAT, BOOL, ENUM, LIST, JSON
│   └── AppEnv.php             DEVELOPMENT, STAGING, PRODUCTION
└── Exception/
    ├── EnvException.php       Base exception
    ├── FileNotFoundException.php
    ├── EncodingException.php
    ├── ParseException.php
    ├── SchemaException.php
    ├── ValidationException.php
    └── WriteException.php

```

---

Development
-----------

[](#development)

```
composer test        # PHPUnit (147 tests)
composer analyse     # PHPStan level 10
composer cs-fix      # PHP-CS-Fixer
composer check       # All three
```

License
-------

[](#license)

MIT

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance87

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~5 days

Total

6

Last Release

64d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/62e82421bda4b5d6ba9a47ba6d88caca060dcd0d1a2862f351f3a97657385db0?d=identicon)[phpdot](/maintainers/phpdot)

---

Top Contributors

[![phpdot](https://avatars.githubusercontent.com/u/252500?v=4)](https://github.com/phpdot "phpdot (6 commits)")

---

Tags

schemaconfigurationenvdotenvimmutabletyped

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/phpdot-env/health.svg)

```
[![Health](https://phpackages.com/badges/phpdot-env/health.svg)](https://phpackages.com/packages/phpdot-env)
```

###  Alternatives

[vlucas/phpdotenv

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

13.5k640.2M6.1k](/packages/vlucas-phpdotenv)[symfony/dotenv

Registers environment variables from a .env file

3.8k243.3M2.7k](/packages/symfony-dotenv)[league/config

Define configuration arrays with strict schemas and access values with dot notation

565335.0M35](/packages/league-config)[sroze/companienv

Companion for .env files

246424.9k1](/packages/sroze-companienv)[josegonzalez/dotenv

dotenv file parsing for PHP

28010.4M159](/packages/josegonzalez-dotenv)[helhum/dotenv-connector

Makes it possible to set environment variables for composer projects.

1634.9M40](/packages/helhum-dotenv-connector)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
