{"id":16904497,"url":"https://github.com/crell/envmapper","last_synced_at":"2025-07-06T23:06:43.516Z","repository":{"id":115621421,"uuid":"609949452","full_name":"Crell/EnvMapper","owner":"Crell","description":"Easily map environment variables into defined classed objects, ready for Dependency Injection.","archived":false,"fork":false,"pushed_at":"2023-08-30T01:04:05.000Z","size":56,"stargazers_count":86,"open_issues_count":0,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-06-10T16:45:14.115Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Crell.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null},"funding":{"github":["Crell"]}},"created_at":"2023-03-05T18:01:02.000Z","updated_at":"2025-06-02T22:03:28.000Z","dependencies_parsed_at":"2024-01-15T06:07:20.741Z","dependency_job_id":null,"html_url":"https://github.com/Crell/EnvMapper","commit_stats":{"total_commits":26,"total_committers":4,"mean_commits":6.5,"dds":"0.15384615384615385","last_synced_commit":"5ca3108c3f3b255d36450bec22407c20b876efec"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":"Crell/php-project-template","purl":"pkg:github/Crell/EnvMapper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Crell%2FEnvMapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Crell%2FEnvMapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Crell%2FEnvMapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Crell%2FEnvMapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Crell","download_url":"https://codeload.github.com/Crell/EnvMapper/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Crell%2FEnvMapper/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263986257,"owners_count":23539809,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-13T18:33:47.279Z","updated_at":"2025-07-06T23:06:43.484Z","avatar_url":"https://github.com/Crell.png","language":"PHP","funding_links":["https://github.com/sponsors/Crell"],"categories":[],"sub_categories":[],"readme":"# EnvMapper\n\n[![Latest Version on Packagist][ico-version]][link-packagist]\n[![Software License][ico-license]](LICENSE.md)\n[![Total Downloads][ico-downloads]][link-downloads]\n\nReading environment variables is a common part of most applications.  However, it's often done in an ad-hoc and unsafe\nway, by calling `getenv()` or reading `$_ENV` from an arbitrary place in code.  That means error handling, missing-value\nhandling, default values, etc. are scattered about the code base.\n\nThis library changes that.  It allows you to map environment variables into arbitrary classed objects extremely fast,\nwhich allows using the class definition itself for default handling, type safety, etc.  That class can then be registered\nin your dependency injection container to become automatically available to any service.\n\n## Usage\n\nEnvMapper has almost no configuration.  Everything is just the class.\n\n```php\n// Define the class.\nclass DbSettings\n{\n    public function __construct(\n        // Loads the DB_USER env var.\n        public readonly string $dbUser,\n        // Loads the DB_PASS env var.\n        public readonly string $dbPass,\n        // Loads the DB_HOST env var.\n        public readonly string $dbHost,\n        // Loads the DB_PORT env var.\n        public readonly int $dbPort,\n        // Loads the DB_NAME env var.\n        public readonly string $dbName,\n    ) {}\n}\n\n$mapper = new Crell\\EnvMapper\\EnvMapper();\n\n$db = $mapper-\u003emap(DbSettings::class);\n```\n\n`$db` is now an instance of `DbSettings` with all five properties populated from the environment, if defined. That object\nmay now be used anywhere, passed into a constructor, or whatever.  Because its properties are all `readonly`, you\ncan rest assured that no service using this object will be able to modify it.\n\nYou can use any class you'd like, however.  All that matters is the defined properties (defined via constructor promotion\nor not).  The properties may be whatever visibility you like, and you may include whatever methods you'd like.\n\n### Name mapping and default values\n\nEnvMapper will convert the name of the property into `UPPER_CASE` style, which is the style typically used\nfor environment variables.  It will then look for an environment variable by that name and assign it to that property.\nThat means you may use `lowerCamel` or `snake_case` for object properties.  Both will work fine.\n\nIf no environment variable is found, but the property has a default value set in the class definition, that default\nvalue will be used.  If there is no default value, it will be left uninitialized.\n\nAlternatively, you may set `requireValues: true` in the `map()` call.  If `requireValues` is set, then a missing property will instead\nthrow a `MissingEnvValue` exception.\n\n```php\nclass MissingStuff\n{\n    public function __construct(\n        public readonly string $notSet,\n    ) {}\n}\n\n// This will throw a MissingEnvValue exception unless there is a NOT_SET env var defined.\n$mapper-\u003emap(MissingStuff::class, requireValues: true);\n```\n\n### Type enforcement\n\nEnvironment variables are always strings, but you may know out-of-band that they are supposed to be an `int` or `float`.\nEnvMapper will automatically cast int-like values (like \"5\" or \"42\") to integers, and float-like values (like \"3.14\") to\nfloats, so that they will safely assign to the typed properties on the object.\n\nIf a property is typed `bool`, then the values \"1\", \"true\", \"yes\", and \"on\" (in any case) will evaluate to `true`. Anything\nelse will evaluate to false.\n\nIf a value cannot be assigned (for instance, if the `DB_PORT` environment variable was set to `\"any\"`), then a `TypeMismatch`\nexception will be thrown.\n\n### dot-env compatibility\n\nEnvMapper reads values from `$_ENV` by default.  If you are using a library that reads `.env` files into the environment,\nit should work fine with EnvMapper provided it populates `$_ENV`.  EnvMapper does not use `getenv()` as it is much slower.\n\nNote that your [variables_order](http://php.net/variables-order) in PHP needs to be set to include `E`, for Environment,\nin order for `$_ENV` to be populated.  If it is not, `$_ENV` will be empty.  If you cannot configure your server to populate\n`$_ENV`, and you cannot switch to a non-broken server, as a fallback you can pass the return of `getenv()` to the `source`\nparameter, like so:\n\n```php\n$mapper-\u003emap(Environment::class, source: getenv());\n```\n\n## Common patterns\n\n### Registering with a DI Container\n\nThe recommended way to use `EnvMapper` is to wire it into your Dependency Injection Container, preferably one that\nsupports auto-wiring.  For example, in a Laravel Service Provider you could do this:\n\n```php\nnamespace App\\Providers;\n \nuse App\\Environment;\nuse Crell\\EnvMapper\\EnvMapper;\nuse Illuminate\\Contracts\\Foundation\\Application;\nuse Illuminate\\Support\\ServiceProvider;\n \nclass EnvMapperServiceProvider extends ServiceProvider\n{\n    // The EnvMapper has no constructor arguments, so registering it is simple.\n    public $singletons = [\n        EnvMapper::class =\u003e EnvMapper::class,\n    ];\n    \n    public function register(): void\n    {\n        // When the Environment class is requested, it will be loaded lazily out of the env vars by the mapper.\n        // Because it's a singleton, the object will be automatically cached.\n        $this-\u003eapp-\u003esingleton(Environment::class, fn(Application $app) =\u003e $app[EnvMapper::class]-\u003emap(Environment::class));\n    }\n}\n```\n\nIn Symfony, you could implement the same configuration in `services.yaml`:\n\n```yaml\nservices:\n    Crell\\EnvMapper\\EnvMapper: ~\n\n    App\\Environment:\n      factory:   ['@Crell\\EnvMapper\\EnvMapper', 'map']\n      arguments: ['App\\Environment']\n```\n\nNow, any service may simply declare a constructor argument of type `Environment` and the container will automatically \ninstantiate and inject the object as needed.\n\n### Testing\n\nThe key reason to use a central environment variable mapper is to make testing easier.  Reading the environment directly\nfrom each service is a global dependency, which makes testing more difficult.  Instead, making a dedicated environment\nclass an injectable service (as in the example above) means any service that uses it may trivially be passed a manually\ncreated version.\n\n```php\nclass AService\n{\n    public function __construct(private readonly AwsSettings $settings) {}\n    \n    // ...\n}\n\nclass AServiceTest extends TestCase\n{\n    public function testSomething(): void\n        $awsSettings = new AwsSettings(awsKey: 'fake', awsSecret: 'fake');\n\n        $s = new Something($awsSettings);\n\n        // ...\n    }\n}\n```\n\n### Multiple environment objects\n\nAny environment variables that are set but not present in the specified class will be ignored.  That means it's trivially\neasy to load different variables into different classes.  For example:\n\n```php\nclass DbSettings\n{\n    public function __construct(\n        public readonly string $dbUser,\n        public readonly string $dbPass,\n        public readonly string $dbHost,\n        public readonly int $dbPort,\n        public readonly string $dbName,\n    ) {}\n}\n\nclass AwsSettings\n{\n    public function __construct(\n        public readonly string $awsKey,\n        public readonly string $awsSecret,\n    ) {}\n}\n```\n\n```php\n// Laravel version.\nclass EnvMapperServiceProvider extends ServiceProvider\n{\n    public function register(): void\n    {\n        $this-\u003eapp-\u003esingleton(DbSettings::class, fn(Application $app) =\u003e $app[EnvMapper::class]-\u003emap(DbSettings::class));\n        $this-\u003eapp-\u003esingleton(AwsSettings::class, fn(Application $app) =\u003e $app[EnvMapper::class]-\u003emap(AwsSettings::class));\n    }\n}\n```\n\n```yaml\n# Symfony version\nservices:\n  Crell\\EnvMapper\\EnvMapper: ~\n\n  App\\DbSettings:\n    factory:   ['@Crell\\EnvMapper\\EnvMapper', 'map']\n    arguments: ['App\\DbSettings']\n\n  App\\AwsSettings:\n    factory:   ['@Crell\\EnvMapper\\EnvMapper', 'map']\n    arguments: ['App\\AwsSettings']\n```\n\n## Advanced usage\n\nEnvMapper is designed to be lightweight and fast.  For that reason, its feature set is deliberately limited.\n\nHowever, there are cases you may wish to have a more complex environment setup.  For instance, you may want to\nrename properties more freely, nest related properties inside sub-objects, or map comma-delimited environment variables\ninto an array.  EnvMapper is not designed to handle that.\n\nHowever, its sibling project [`Crell/Serde`](https://www.github.com/Crell/Serde) can do so easily.  Serde is a general\npurpose serialization library, but you can easily feed it `$_ENV` as an array to deserialize from into an object. That\ngives you access to all of Serde's capability to rename, collect, nest, and otherwise translate data as it's being\ndeserialized into an object.  The basic workflow is the same, and registration in your service container is nearly\nidentical.\n\n```php\n$serde = new SerdeCommon();\n\n$env = $serde-\u003edeserialize($_ENV, from: 'array', to: Environment::class);\n```\n\nUsing Serde will be somewhat slower than using EnvMapper, but both are still very fast and suitable for almost any application.\n\nSee the Serde documentation for all of its various options.\n\n## Contributing\n\nPlease see [CONTRIBUTING](CONTRIBUTING.md) and [CODE_OF_CONDUCT](CODE_OF_CONDUCT.md) for details.\n\n## Security\n\nIf you discover any security related issues, please email larry at garfieldtech dot com instead of using the issue tracker.\n\n## Credits\n\n- [Larry Garfield][link-author]\n- [All Contributors][link-contributors]\n\n## License\n\nThe Lesser GPL version 3 or later. Please see [License File](LICENSE.md) for more information.\n\n[ico-version]: https://img.shields.io/packagist/v/Crell/EnvMapper.svg?style=flat-square\n[ico-license]: https://img.shields.io/badge/License-LGPLv3-green.svg?style=flat-square\n[ico-downloads]: https://img.shields.io/packagist/dt/Crell/EnvMapper.svg?style=flat-square\n\n[link-packagist]: https://packagist.org/packages/Crell/EnvMapper\n[link-scrutinizer]: https://scrutinizer-ci.com/g/Crell/EnvMapper/code-structure\n[link-code-quality]: https://scrutinizer-ci.com/g/Crell/EnvMapper\n[link-downloads]: https://packagist.org/packages/Crell/EnvMapper\n[link-author]: https://github.com/Crell\n[link-contributors]: ../../contributors\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrell%2Fenvmapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcrell%2Fenvmapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrell%2Fenvmapper/lists"}