Dotenv
in package
Builds the environment `${...}` references (CASC.md §7.2) read -- the Elixir original's `Cooper.Dotenv`. `Cooper` calls it on every load, cached or not, so `${...}` values are always current.
Layering
Lowest precedence first, later winning:
.env.env.<env>--<env>is thedotenvEnvoption when given (nullmeaning "no per-environment file"); otherwiseCOOPER_ENV(see below) as the real environment,envand the base.envset it -- so.env.dev,.env.staging,.env.testand.env.prodin every Cooper, matchingenv/${COOPER_ENV}.casc. Naming it byAPP_ENV, as this once did, read Laravel's.env.productionor.env.testingbeside documents that readprodortest..env.local- the real process environment (
getenv()) - the
envoption -- always the final override.
The real environment outranks the files. A deployment sets
variables in the environment it controls; a file in the working
directory must not silently beat them (Ruby's and Node's dotenv, and
phpdotenv's own immutable mode, behave the same). Pass
dotenvOverride: true to put the files on top instead.
dotenvFiles replaces the default file list (layers 1-3) outright.
A missing file is silently skipped. Paths are relative to dotenvDir,
which defaults to the project root -- deliberately not root (the
CASC file's directory): .env files live at the project root wherever
the CASC file sits and wherever the process was started. The project
root is the Composer root package's directory, else the working
directory (see projectRoot()). An absolute dotenvFiles entry is
used as it is.
env is an override layer, not a replacement: a name it does not
set still falls through to the files and the real environment.
COOPER_ENV
The one name a document reads the current environment by, in every
Cooper implementation (CASC.md §7.2): import "env/${COOPER_ENV}.casc".
Every layer above is consulted first, so a real COOPER_ENV -- in the
environment, a .env file, or env -- always wins. Unset or empty, it
falls back to this host's own name for the same thing: APP_ENV from
those same layers, else "dev". Every Cooper names its environments
dev, staging, test and prod, so in that fallback only, the
host's own spellings are mapped onto them through the one table CASC.md
§7.2 gives every implementation -- development and local -> dev,
testing -> test, production -> prod, exact and case-sensitive --
and Laravel's local/testing/production read the same
env/dev.casc as every other host. Any other APP_ENV (staging,
qa, ...) is passed through as is, and a real COOPER_ENV is never
mapped: whoever set it chose the name. APP_ENV itself is left alone.
Read before .env.<env>, the same value names that file (see
"Layering").
Because it is part of the map every load resolves against, the
cache's environment check (Cache) sees the fallback too: a tree an
import "${COOPER_ENV}.casc" shaped goes stale when APP_ENV changes.
File loading (layers 1-3) is on by default; dotenv: false turns it
off. The files are parsed by vlucas/phpdotenv, which Cooper requires:
as an optional package, the default silently read nothing in any
application that had not added it itself.
Exporting to the process environment
Neither env() nor a load ever changes the process environment: the
layering only builds the map ${...} resolves against. export()
exists for one caller -- a framework integration that replaces the
framework's own .env loader, after which code reading the process
environment directly ($_ENV, $_SERVER, getenv(), Laravel's
env(), Symfony's APP_ENV/APP_DEBUG) must still find what the
files say. It is host-specific behaviour of this library, not CASC's.
Table of Contents
Constants
- BASE_FILE : mixed = '.env'
- LOCAL_FILE : mixed = '.env.local'
- SHARED_ENV_NAMES : mixed = ['development' => 'dev', 'local' => 'dev', 'tes...
- Host environment names, and the shared ones (`dev`, `staging`, `test`, `prod`) they map onto -- the table CASC.md §7.2 gives every Cooper, so it must stay identical to theirs. Only the `APP_ENV` fallback consults it.
Methods
- env() : array<string, string>
- The environment for `$options` (the same options array `Cooper` takes; only `env`, `dotenv`, `dotenvEnv`, `dotenvFiles`, `dotenvDir`, and `dotenvOverride` matter here).
- export() : array<string, string>
- Writes what the `.env` files provided into the process environment -- `$_ENV`, `$_SERVER` and `putenv()` -- for code that reads it directly instead of through `${...}`. Never called by a load: only a caller that asks for it, typically a framework integration standing in for the framework's own `.env` loader, at bootstrap.
- projectRoot() : string
- Where `.env` files are read from unless `dotenvDir` says otherwise: the Composer root package's directory -- the application, when Cooper is one of its dependencies -- else the working directory.
- __construct() : mixed
- files() : array<int, string>
- The files to layer, lowest precedence first, as absolute paths.
- inProcessEnvironment() : bool
- Whether the real process environment already holds `$name`: any of `getenv()`, `$_ENV`, or `$_SERVER` -- except a `$_SERVER` `HTTP_*` entry, which is a request header (see `export()`).
-
layered()
: array{0: array
, 1: list } - Layers 1-5, before `COOPER_ENV` is filled in, and which names the files (layers 1-3) defined -- what `export()` writes.
- nameEnv() : string
- `COOPER_ENV` as it stands before `.env.<env>` is read -- from the real environment, `env` and the base `.env` file, in the order the layering puts them -- which names the file to read next. The files after it cannot name it: they are not read yet.
- onTop() : array<string, string>
- The real environment, the files and `env` stacked as `$options` says: `env` always last, the files above the real environment only under `dotenvOverride`.
- parseFile() : array<string, string>
- present() : bool
Constants
BASE_FILE
private
mixed
BASE_FILE
= '.env'
LOCAL_FILE
private
mixed
LOCAL_FILE
= '.env.local'
SHARED_ENV_NAMES
Host environment names, and the shared ones (`dev`, `staging`, `test`, `prod`) they map onto -- the table CASC.md §7.2 gives every Cooper, so it must stay identical to theirs. Only the `APP_ENV` fallback consults it.
private
mixed
SHARED_ENV_NAMES
= ['development' => 'dev', 'local' => 'dev', 'testing' => 'test', 'production' => 'prod']
Methods
env()
The environment for `$options` (the same options array `Cooper` takes; only `env`, `dotenv`, `dotenvEnv`, `dotenvFiles`, `dotenvDir`, and `dotenvOverride` matter here).
public
static env([array<string, mixed> $options = [] ]) : array<string, string>
Parameters
- $options : array<string, mixed> = []
Tags
Return values
array<string, string> —name => value
export()
Writes what the `.env` files provided into the process environment -- `$_ENV`, `$_SERVER` and `putenv()` -- for code that reads it directly instead of through `${...}`. Never called by a load: only a caller that asks for it, typically a framework integration standing in for the framework's own `.env` loader, at bootstrap.
public
static export([array<string, mixed> $options = [] ]) : array<string, string>
What is exported: every name the files define, and COOPER_ENV
(always, so code outside CASC sees the environment name the
configuration is loaded for, fallback included). Each gets the
value ${NAME} resolves to under the same $options -- env
included -- so the process environment and a load never disagree.
A name only the env option sets is not exported: it is the
caller's override for this load, not the files'.
The real environment is never overwritten, the precedence the
layering uses: a name getenv(), $_ENV or $_SERVER already
holds is left alone. The superglobals count because a web server
can set a variable there without getenv() seeing it (Symfony's
own loader checks the same places); a $_SERVER entry named
HTTP_* does not, since it is a request header a client chose.
dotenvOverride: true puts the files on top here too, and every
name is written.
Idempotent: what one call wrote is the real environment to the
next, which writes nothing (or, under dotenvOverride, the same
values again). From then on, every load in the process sees those
values as the real environment -- a .env file edited later is
outranked by them, as by any other real variable.
Parameters
- $options : array<string, mixed> = []
-
the options array
Coopertakes (seeenv())
Tags
Return values
array<string, string> —name => value, every variable this call wrote
projectRoot()
Where `.env` files are read from unless `dotenvDir` says otherwise: the Composer root package's directory -- the application, when Cooper is one of its dependencies -- else the working directory.
public
static projectRoot() : string
Reading them from the working directory only, as this once did, found none when an application was started from anywhere but its own root (a subdirectory, a worker, a cron job).
Return values
string —an absolute directory
__construct()
private
__construct() : mixed
files()
The files to layer, lowest precedence first, as absolute paths.
private
static files(Options $options, array<string, string> $system) : array<int, string>
Parameters
- $options : Options
- $system : array<string, string>
Return values
array<int, string>inProcessEnvironment()
Whether the real process environment already holds `$name`: any of `getenv()`, `$_ENV`, or `$_SERVER` -- except a `$_SERVER` `HTTP_*` entry, which is a request header (see `export()`).
private
static inProcessEnvironment(string $name) : bool
Parameters
- $name : string
Return values
boollayered()
Layers 1-5, before `COOPER_ENV` is filled in, and which names the files (layers 1-3) defined -- what `export()` writes.
private
static layered(Options $options) : array{0: array, 1: list}
Parameters
- $options : Options
Return values
array{0: arraynameEnv()
`COOPER_ENV` as it stands before `.env.<env>` is read -- from the real environment, `env` and the base `.env` file, in the order the layering puts them -- which names the file to read next. The files after it cannot name it: they are not read yet.
private
static nameEnv(Options $options, array<string, string> $system, string $dir) : string
Parameters
- $options : Options
- $system : array<string, string>
- $dir : string
-
where
.envis read from
Return values
stringonTop()
The real environment, the files and `env` stacked as `$options` says: `env` always last, the files above the real environment only under `dotenvOverride`.
private
static onTop(Options $options, array<string, string> $system, array<string, string> $files) : array<string, string>
Parameters
- $options : Options
- $system : array<string, string>
- $files : array<string, string>
Return values
array<string, string>parseFile()
private
static parseFile(string $file) : array<string, string>
Parameters
- $file : string
Return values
array<string, string>present()
private
static present(string|null $value) : bool
Parameters
- $value : string|null