---
title: PHP
description: Use varlock with PHP via a generated, typed env class
---

To use varlock with PHP, [install the standalone binary](/getting-started/installation/#as-a-standalone-binary) and run your app under [`varlock run`](/reference/cli-commands/#run). It loads and validates your env, then injects it into the process.

Add [`@generatePhpEnv`](/reference/root-decorators/#generatephpenv) to your schema to generate a small, self-contained class: a readonly `Env` with typed promoted properties, a static `load()` that parses the injected [`__VARLOCK_ENV`](/reference/reserved-variables/#__varlock_env) blob, and a `SENSITIVE_KEYS` constant.

```env-spec title=".env.schema"
# @generatePhpEnv(path=Env.php)
```

The file is regenerated automatically on [`varlock load`](/reference/cli-commands/#load) and [`varlock run`](/reference/cli-commands/#run), or explicitly with [`varlock codegen`](/reference/cli-commands/#codegen). Requires **PHP 8.1+** (readonly promoted properties).

## Reading values

Call `load()` once, then read fields off the object. Required fields are the plain type, optional ones are nullable (`?type`):

```php
<?php
require_once 'Env.php';   // require_once, since a second require would fatal

$env = Env::load();       // call once, hold or inject $env
$port = $env->DB_PORT;    // ?int
$debug = $env->DEBUG;     // ?bool

// SENSITIVE_KEYS is available for your own redaction
foreach (Env::SENSITIVE_KEYS as $key) { /* ... */ }
```

```bash
varlock run -- php main.php
```

`load()` throws a clear error if `__VARLOCK_ENV` is missing (e.g. you forgot `varlock run`) or if a required key is absent.

## Sharing one instance

Every `load()` call re-parses the blob. To avoid reloading, bind the loaded instance wherever your app keeps shared services, e.g. your DI container:

```php
// bootstrap.php
$container->instance(Env::class, Env::load());   // resolve Env anywhere, type-hinted
```

No container? A tiny static holder works:

```php
final class Config {
    public static Env $env;
}
Config::$env = Env::load();

// elsewhere: Config::$env->DB_PORT  (?int)
```

## Composer / PSR-4

By default the class is a global `final class Env` loaded via `require_once`. For Composer projects, set `namespace=` and/or `class=` on the decorator and register the file via classmap autoload instead:

```env-spec title=".env.schema"
# @generatePhpEnv(path=src/Env.php, namespace="App\Config", class=AppEnv)
```

```json title="composer.json"
{ "autoload": { "classmap": ["src/Env.php"] } }
```

Under PHP-FPM, make sure the pool passes `__VARLOCK_ENV` through (`clear_env = no`).

:::note
Every key is also injected as a plain environment variable, so you can read any of them with `getenv()` (raw strings) if you don't need the typed module. Keys whose names aren't valid identifiers (they contain `.` or `-`) can't be properties, so read those from the env var directly. See [Other languages](/integrations/other-languages/) for the shared details on the injected blob, raw env vars, and encryption.
:::
