Table of Contents
Configuration arrays—whether from databases, API responses, or config files—can be incomplete or inconsistent. Directly accessing keys without checking for their existence causes Undefined array key errors in PHP 8+.
The Problem
This constructor assumes all configuration keys exist:
class ServiceCredentials
{
private string $apiKey;
private string $testApiKey;
private string $secretToken;
private string $testSecretToken;
public function __construct(array $config, bool $isTest)
{
$this->apiKey = $config['api_key'];
$this->testApiKey = $config['test']['api_key']; // ❌ Crashes if test.api_key missing
$this->secretToken = $config['secret_token'];
$this->testSecretToken = $config['test']['secret_token']; // ❌ Crashes if test.secret_token missing
}
}
When $config['test'] is incomplete (missing api_key or secret_token), PHP 8 throws ErrorException: Undefined array key and your app crashes.
The Solution
Use null coalescing (??) to provide fallback values:
class ServiceCredentials
{
private ?string $apiKey;
private ?string $testApiKey;
private ?string $secretToken;
private ?string $testSecretToken;
public function __construct(array $config, bool $isTest)
{
$this->apiKey = $config['api_key'] ?? null;
$this->testApiKey = $config['test']['api_key'] ?? null; // ✅ Graceful
$this->secretToken = $config['secret_token'] ?? null;
$this->testSecretToken = $config['test']['secret_token'] ?? null; // ✅ Graceful
}
public function getApiKey(): string
{
if ($this->isTest && $this->testApiKey !== null) {
return $this->testApiKey;
}
return $this->apiKey ?? throw new \RuntimeException('API key not configured');
}
}
Why This Works
- Defensive coding — Configuration can be incomplete, user-managed, or legacy
- Graceful degradation — System doesn’t crash on initialization; fails at the point where the value is actually needed with better context
- Backwards compatible — Doesn’t break existing working configurations
- Type-safe — Nullable types (
?string) signal that values might be absent
When to Use This
- Database-stored configuration (user-editable, schema evolves)
- API response deserialization (external APIs change)
- Multi-environment credentials (test/staging/production)
- Optional feature flags or settings
Rule of thumb: If a config key might be missing in production without it being a fatal error, use ?? + nullable types. If it’s always required, let it crash early with a clear error.
Discovered while debugging a production error where test credentials were incomplete in a third-party service integration. The fix: 4 lines changed, zero data migrations required.
Leave a Reply