# Introduction
Parameters our application deals with at the I/O level differ from environment to environment. We don't want use the same mailing or payment credentials in production or testing environment, to avoid real life consequences such as triggering spam blockers or money deducted from our account. Such information should not be packaged along with the code but managed at the infrastructural layer. Each environment (development/test, stage, production), would receive applicable variables in a format depending on what manner the software is deployed with.
# Defining environment variables
Deployment methods may provide different shapes for describing parameters but all share the common property for key-value pairs. In Suphle, these parameters can be defined in a few ways:
# .env file
This is a file saved as ".env" in the root of each module.
PRIVATE_API_KEY = [redacted]
A default file containing fields mandatory for adequate functionality of your module is included with each module. It should be customized to suit personal values.
# Roadrunner env section
In the "dev-rr.yaml" roadrunner config included in your Suphle installation, there's section, server > env, where environment parameters can be defined.
server:
command: "php suphle-worker.php"
env:
- DATABASE_NAME: suphle
# PHPUnit variables
PHPUnit provides an "phpunit.xml" file for configuring how test evaluation behaves, PHP INI settings. Among other things, it allows us overwrite variables defined using the ".env" method just within the scope of our test.
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
colors = "true"
>
<testsuites>
<testsuite name="all_tests">
<directory>./tests</directory>
</testsuite>
</testsuites>
<php>
<ini name="error_reporting" value="-1" />
<env name="DATABASE_USER" value="nmeri" />
<env name="DATABASE_PASS" value="password" />
<env name="DATABASE_NAME" value="suphle_test" />
<env name="DATABASE_HOST" value="127.0.0.1" />
</php>
</phpunit>
The test command would have to include this configuration as part of its arguments for test runner to acknowledge the overrides.
# from vendor/bin
phpunit path/to/tests --configuration="path/to/phpunit.xml"
All environment definition methods are compatible with the .env format. When the application requests for a parameter's value, Suphle will read from all possible sources.
# Environment variables in the application
When type-hinted, the interface Suphle\Contracts\IO\EnvAccessor
loads variables into memory. Some classes require it before execution gets to where you have access to/user-land, so loading will happen behind the scenes.
# Reading environment fields
Loaded fields can then be read within the application using the getField
method:
class ConfigDownloader extends BaseHttpRequest {
public function __construct (
ClientInterface $requestClient, DetectedExceptionManager $exceptionDetector,
protected readonly EnvAccessor $envAccessor
) {
parent::__construct($requestClient, $exceptionDetector);
}
public function getRequestUrl ():string {
return $this->envAccessor->getField(
self::ENV_CONFIG_URL, "https://default-url"
);
}
}
# Validating environment fields
The 2nd argument to EnvAccessor::getField
allows us define fallback values for missing variables. However, such optional defaults are not always possible to know beforehand. Such scenario is common while developing 3rd-party packages, and should force application to collapse until a value is provided.
This functionality is provided by the Suphle\IO\Env\AbstractEnvLoader::validateFields
method. AbstractEnvLoader
is an implementation of EnvAccessor
that unifies calls to different sources for deriving environment variables. It defines a client
property that points to the underlying Dotenv\Dotenv
instance. This object enables library developers validate field values received using its ifPresent
and required
methods.
# Replacing default accessor
The default implementation for EnvAccessor
is Suphle\IO\Env\DatabaseEnvReader
. Customizations should extend it and override its entry on InterfaceCollection::simpleBinds
.
class LibraryEnvReader extends DatabaseEnvReader {
protected function validateFields ():void {
parent::validateFields();
$this->client->ifPresent(["PRIVATE_KEY"])->required();
}
}