Skip to content
Viames Marino edited this page Oct 19, 2026 · 8 revisions

Pair framework: Env class

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 .env file is expected to be placed in the project root (APPLICATION_PATH/.env).


.env file format

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=3600

Constants

Env::FILE

The absolute path of the .env file.

Env::FILE; // APPLICATION_PATH . '/.env'

Env::DEFAULTS

An associative array containing fallback values used when:

  • the .env file 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, from OAuth2Token::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.


When to call Env::load()

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');
}

Methods

Env::fileExists(): bool

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
}

Env::get(string $key): mixed

Returns the value for a given key, following this priority:

  1. $_ENV[$key] (if already loaded / set)
  2. Env::DEFAULTS[$key] (fallback)
  3. 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.


Env::clearCache(): void

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();

Env::load(bool $force = false): void

Loads the .env file into $_ENV.

Behavior:

  1. If environment values were already loaded and $force is false:
    • returns immediately without re-reading .env
  2. If .env does not exist:
    • loads every entry from Env::DEFAULTS into $_ENV
    • returns immediately
  3. If .env exists:
    • reads the file line by line
    • ignores:
      • empty lines
      • lines starting with #
      • lines without an = symbol
    • splits each valid line into KEY and value
    • 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);

Example: type casting

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")

Example: fallback to defaults when .env is missing

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

Suggested best practices

  • Keep secrets (API keys, tokens, DSNs) in .env and out of Git.
  • Call Env::load() once during bootstrap; repeated calls are now cheap and return cached state.
  • Use Env::load(true) or Env::clearCache() only when the same PHP process intentionally changes .env.
  • Use Env::get() instead of reading $_ENV directly, so defaults always apply.
  • Prefer boolean and numeric values in .env when you want type-safe reads.

Notes

  • The .env loader 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.

Clone this wiki locally