Repository navigation
Env
The Env class provides a tiny and predictable way to load environment variables from a .env file into $_ENV, with:
- sensible defaults (fallback values);
- automatic type casting for booleans and numbers;
- process-local caching after the first load;
- a single accessor method to read variables consistently.
This is useful when you want to configure the same Pair application across multiple environments (local, staging, production) without hardcoding values in PHP files.
The
.envfile is expected to be placed in the project root (APPLICATION_PATH/.env).
The .env file is a plain text file where each non-empty line is:
KEY=value
You can also add comments (lines starting with #), which will be ignored.
Example:
# Application
APP_NAME=My Pair App
APP_ENV=development
APP_DEBUG=true
# Pair / security
PAIR_SINGLE_SESSION=false
# OAuth2
OAUTH2_TOKEN_LIFETIME=3600The absolute path of the .env file.
Env::FILE; // APPLICATION_PATH . '/.env'An associative array containing fallback values used when:
- the
.envfile is missing, or - a requested key is not present in
$_ENV.
Current defaults:
-
APP_NAME(string) -
APP_VERSION(string) -
APP_ENV(string) -
APP_DEBUG(bool) -
DB_UTF8(bool) -
OAUTH2_TOKEN_LIFETIME(int, fromOAuth2Token::LIFETIME) -
PAIR_SINGLE_SESSION(bool) -
PAIR_AUDIT_ALL(bool) -
PAIR_AUTH_BY_EMAIL(bool) -
PAIR_PASSWORD_HASH_ALGORITHM(string,auto) -
PAIR_PASSWORD_REHASH_ON_LOGIN(bool,true) -
PAIR_AUTH_RATE_LIMIT_ENABLED(bool) -
PAIR_AUTH_RATE_LIMIT_MAX_ATTEMPTS(int) -
PAIR_AUTH_RATE_LIMIT_DECAY_SECONDS(int) -
PAIR_MOBILE_ACCESS_TOKEN_LIFETIME(int) -
PAIR_MOBILE_REFRESH_TOKEN_LIFETIME(int) -
PAIR_API_RATE_LIMIT_ENABLED(bool) -
PAIR_API_RATE_LIMIT_MAX_ATTEMPTS(int) -
PAIR_API_RATE_LIMIT_DECAY_SECONDS(int) -
PAIR_API_RATE_LIMIT_REDIS_PREFIX(string) -
PAIR_TRUSTED_PROXIES(string) -
PAIR_SOCIAL_AUTH_PROVIDERS(string) -
PAIR_SOCIAL_AUTH_TIMEOUT(int) -
PAIR_SOCIAL_AUTH_CONNECT_TIMEOUT(int) -
GOOGLE_MAPS_TIMEOUT(int) -
GOOGLE_MAPS_CONNECT_TIMEOUT(int) -
WHATSAPP_CLOUD_API_VERSION(string) -
WHATSAPP_CLOUD_API_BASE_URL(string) -
WHATSAPP_CLOUD_TIMEOUT(int) -
WHATSAPP_CLOUD_CONNECT_TIMEOUT(int) -
REDIS_HOST(string) -
REDIS_PORT(int) -
REDIS_PASSWORD(string) -
REDIS_DB(int) -
REDIS_TIMEOUT(int) -
PAIR_CACHE_DRIVER(string) -
PAIR_CACHE_PATH(string) -
PAIR_CACHE_PREFIX(string) -
PAIR_CACHE_REDIS_PREFIX(string) -
PAIR_ORIGIN_GZIP(string) -
PAIR_OBSERVABILITY_ENABLED(bool) -
PAIR_OBSERVABILITY_DEBUG_HEADERS(bool) -
PAIR_OBSERVABILITY_TRACE_SAMPLE_RATE(float) -
PAIR_OBSERVABILITY_ERROR_SAMPLE_RATE(float) -
PAIR_OBSERVABILITY_MAX_SPANS(int) -
PAIR_OBSERVABILITY_MAX_EVENTS(int) -
PAIR_LOGBAR_SLOW_REQUEST_MS(int) -
PAIR_LOGBAR_SLOW_QUERY_MS(int) -
PAIR_LOGBAR_QUERY_BUDGET(int) -
PAIR_LOGBAR_DUPLICATE_QUERY_BUDGET(int) -
PAIR_LOGBAR_MAX_EVENTS(int) -
PAIR_LOGBAR_SHOW_SQL_VALUES(bool) -
PAIR_LOGBAR_CSS_PATH(string) -
PAIR_LOGBAR_JS_PATH(string) -
SENTRY_DSN(string) -
SENTRY_ENVIRONMENT(string) -
SENTRY_RELEASE(string) -
SENTRY_TRACES_SAMPLE_RATE(float) -
SENTRY_ERROR_SAMPLE_RATE(float) -
SENTRY_TIMEOUT(int) -
SENTRY_CONNECT_TIMEOUT(int) -
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT(string) -
OTEL_EXPORTER_OTLP_HEADERS(string) -
OTEL_SERVICE_NAME(string) -
OTEL_SERVICE_VERSION(string) -
OTEL_TIMEOUT(int) -
OTEL_CONNECT_TIMEOUT(int) -
STRIPE_SECRET_KEY(string) -
STRIPE_WEBHOOK_SECRET(string) -
STRIPE_API_VERSION(string) -
OPENAI_API_KEY(string) -
OPENAI_API_BASE_URL(string) -
OPENAI_RESPONSES_MODEL(string) -
OPENAI_EMBEDDINGS_MODEL(string) -
OPENAI_REALTIME_MODEL(string) -
OPENAI_TIMEOUT(int) -
OPENAI_CONNECT_TIMEOUT(int) -
OPENAI_STORE_RESPONSES(bool) -
GEMINI_API_KEY(string) -
GEMINI_API_BASE_URL(string) -
GEMINI_GENERATION_MODEL(string) -
GEMINI_EMBEDDINGS_MODEL(string) -
GEMINI_TIMEOUT(int) -
GEMINI_CONNECT_TIMEOUT(int) -
CLAUDE_API_KEY(string) -
CLAUDE_API_BASE_URL(string) -
CLAUDE_MESSAGES_MODEL(string) -
CLAUDE_API_VERSION(string) -
CLAUDE_MAX_TOKENS(int) -
CLAUDE_TIMEOUT(int) -
CLAUDE_CONNECT_TIMEOUT(int) -
RESEND_API_KEY(string) -
RESEND_API_BASE_URL(string) -
RESEND_FROM_ADDRESS(string) -
RESEND_FROM_NAME(string) -
RESEND_WEBHOOK_SECRET(string) -
RESEND_TIMEOUT(int) -
RESEND_CONNECT_TIMEOUT(int) -
CLOUDFLARE_TURNSTILE_SITE_KEY(string) -
CLOUDFLARE_TURNSTILE_SECRET_KEY(string) -
CLOUDFLARE_TURNSTILE_VERIFY_URL(string) -
CLOUDFLARE_TURNSTILE_RESPONSE_FIELD(string) -
CLOUDFLARE_TURNSTILE_TIMEOUT(int) -
CLOUDFLARE_TURNSTILE_CONNECT_TIMEOUT(int) -
PAIR_HUMAN_CHALLENGE_PROVIDER(string) -
GOOGLE_RECAPTCHA_SITE_KEY(string) -
GOOGLE_RECAPTCHA_SECRET_KEY(string) -
GOOGLE_RECAPTCHA_VERIFY_URL(string) -
GOOGLE_RECAPTCHA_RESPONSE_FIELD(string) -
GOOGLE_RECAPTCHA_TIMEOUT(int) -
GOOGLE_RECAPTCHA_CONNECT_TIMEOUT(int) -
MEILISEARCH_HOST(string) -
MEILISEARCH_API_KEY(string) -
MEILISEARCH_DEFAULT_INDEX(string) -
MEILISEARCH_TIMEOUT(int) -
MEILISEARCH_CONNECT_TIMEOUT(int) -
MEILISEARCH_SEARCH_LIMIT(int) -
SUPABASE_URL(string) -
SUPABASE_ANON_KEY(string) -
SUPABASE_SERVICE_ROLE_KEY(string) -
SUPABASE_TIMEOUT(int) -
SUPABASE_CONNECT_TIMEOUT(int) -
UTC_DATE(bool)
These are configuration fallbacks, not a complete application setup. Env can be used independently without a file, but normal Application startup requires .env or an installer. Database credentials and feature-specific secrets still need explicit configuration. See Configuration file for password policy and integration details.
Normal Application::getInstance() startup already calls Env::load(). Load it explicitly only in a standalone bootstrap that has defined APPLICATION_PATH and does not use normal application startup, or when deliberately refreshing configuration in the same process.
Example (simplified index.php idea):
<?php
use Pair\Core\Application;
use Pair\Core\Env;
// start the application
$app = Application::getInstance();
// Normal application startup has already loaded .env; this call is optional.
Env::load();
// now you can safely read Env values
if (Env::get('APP_DEBUG')) {
ini_set('display_errors', '1');
}Returns true if the .env file exists at Env::FILE.
The filesystem check is cached for the current PHP process through FilesystemMetadata; call Env::clearCache() before checking again after creating, deleting, or replacing the .env file in the same process.
Typical usage:
use Pair\Core\Env;
if (!Env::fileExists()) {
// show a warning, or rely on defaults
}Returns the value for a given key, following this priority:
-
$_ENV[$key](if already loaded / set) -
Env::DEFAULTS[$key](fallback) -
null(if not found anywhere)
This allows consistent reads even when .env is not present.
Examples:
use Pair\Core\Env;
// read a boolean
$debug = Env::get('APP_DEBUG'); // true/false (or default)
// read a string
$appName = Env::get('APP_NAME'); // "Pair Application" by default
// unknown keys return null
$foo = Env::get('SOME_UNKNOWN_KEY'); // null
Env::get()does not throw if a key is missing; it is designed for predictable fallbacks.
Clears the process-local cache used by Env::fileExists() and Env::load(). It does not remove values already stored in $_ENV.
Use this in tests, installers, long-running CLI commands, or deployment scripts that change .env and need the same PHP process to inspect the filesystem again.
use Pair\Core\Env;
Env::clearCache();
Env::load();Loads the .env file into $_ENV.
Behavior:
- If environment values were already loaded and
$forceisfalse:- returns immediately without re-reading
.env
- returns immediately without re-reading
- If
.envdoes not exist:- loads every entry from
Env::DEFAULTSinto$_ENV - returns immediately
- loads every entry from
- If
.envexists:- reads the file line by line
- ignores:
- empty lines
- lines starting with
# - lines without an
=symbol
- splits each valid line into
KEYandvalue - trims both key and value
- casts types:
- surrounding single or double quotes are removed and the quoted value remains a string
- unquoted
true/false(case-insensitive) ->true/false - numeric integers ->
(int) - numeric floats ->
(float) - everything else stays as string
- assigns the final value into
$_ENV[$key]
Pass true to force cache invalidation before loading. Reloading overwrites keys present in the file but does not clear old $_ENV keys that were removed from it:
use Pair\Core\Env;
Env::load(true);Given this .env:
APP_DEBUG=true
OAUTH2_TOKEN_LIFETIME=3600
DISCOUNT=12.5
APP_NAME=My App
EXTERNAL_ID="00123"
TEXT_FLAG="false"After Env::load():
use Pair\Core\Env;
Env::load();
var_dump(Env::get('APP_DEBUG')); // bool(true)
var_dump(Env::get('OAUTH2_TOKEN_LIFETIME')); // int(3600)
var_dump(Env::get('DISCOUNT')); // float(12.5)
var_dump(Env::get('APP_NAME')); // string("My App")
var_dump(Env::get('EXTERNAL_ID')); // string("00123")
var_dump(Env::get('TEXT_FLAG')); // string("false")When Env is used independently and .env does not exist, it loads its defaults into $_ENV:
use Pair\Core\Env;
Env::load();
echo Env::get('APP_ENV'); // "production"
echo Env::get('APP_DEBUG'); // false- Keep secrets (API keys, tokens, DSNs) in
.envand out of Git. - Call
Env::load()once during bootstrap; repeated calls are now cheap and return cached state. - Use
Env::load(true)orEnv::clearCache()only when the same PHP process intentionally changes.env. - Use
Env::get()instead of reading$_ENVdirectly, so defaults always apply. - Prefer boolean and numeric values in
.envwhen you want type-safe reads.
- The
.envloader is intentionally minimal: it focuses on predictable parsing and safe defaults. - Lines without
=are ignored, as well as comments starting with#.
See also: Configuration-file, Application, index.php, Logger.
Maintained by Viames Marino — VM Ingegneria · Professional enquiries
Home · Setup · Configuration
Build with Pair 4
- Generator
- Controllers and responses
- Read models
- ActiveRecord and queries
- Forms and validation
- PairUI.js and HTML fragments
APIs and authentication
- API guide and CRUD
- Middleware and rate limits
- Users, sessions, and ACL
- Passkeys and social login
- Native mobile setup
Integrate and operate
- Integrations
- Runtime extensions and adapter keys
- Cache and cache stores
- Logs and observability
- PWA and push
- Packages and distribution
- Upgrade to v4