Support Helpers

The Rhapsody\Core\Support namespace holds four small, dependency-free helpers: Config, Env, Fs and Json. They wrap the places where PHP hands you mixed or false (config values, environment variables, file reads, JSON decoding) so your code gets real, typed values, or a clear exception at the exact point something went wrong.

use Rhapsody\Core\Helpers\Path;
use Rhapsody\Core\Support\Env;
use Rhapsody\Core\Support\Fs;
use Rhapsody\Core\Support\Json;

$settings = Json::decodeArray(Fs::read(Path::config('settings.json')));
$appName  = Env::string('APP_NAME', 'Rhapsody');

At a Glance

Class Use it for When something is wrong
ConfigReading values from the application config array, with dot notation and type checksThrows RuntimeException
EnvReading values from your .env file / the environmentNever throws; returns your default
FsReading, writing and locating filesThrows RuntimeException
JsonEncoding and decoding JSONThrows JsonException
Why exceptions? PHP's own functions report failure by returning false (or null), which is easy to ignore and then shows up three calls later as a confusing error. These helpers fail loudly where the problem is, and always return the type their name promises. Env is the one deliberate exception, because an unset environment variable is normal and a default is the natural answer.

Config

Rhapsody\Core\Support\Config is a read-only, typed reader over your application's config array (the array returned by config/config.php). It is registered in the service container as a singleton, so you can type-hint it in a controller constructor or fetch it from the container.

use Rhapsody\Core\Support\Config;

class MailController extends BaseController
{
    public function __construct(Environment $twig, private Config $config)
    {
        parent::__construct($twig);
    }

    public function status(Request $request): Response
    {
        $host = $this->config->string('mail.host');
        // ...
    }
}

// Or, anywhere you have the container:
$config = $container->get(Config::class);

Methods

Method Returns
all()The entire config array
has($key)true if the key exists. A key set to null still counts as existing.
get($key, $default = null)The raw value, or $default if the key does not exist
string($key, ?string $default = null)A string
int($key, ?int $default = null)An int
bool($key, ?bool $default = null)A bool
array($key, ?array $default = null)An array

Dot notation

Reach into nested arrays with dots. An exact key match is tried first, so a key that literally contains a dot still works.

// config/config.php
return [
    'mail' => [
        'host' => 'smtp.example.com',
        'port' => '587',
    ],
];
$config->string('mail.host');       // 'smtp.example.com'
$config->int('mail.port');          // 587 (the string '587' is converted)
$config->int('mail.timeout', 30);   // 30  (key missing, so the default is used)
$config->array('mail');             // ['host' => '...', 'port' => '587']

The key names above are just an example; use whatever your own config.php defines.

What the typed getters do

  • Missing key or null value: the default is returned if you passed one. Otherwise a RuntimeException is thrown: Missing config key 'mail.username'.
  • Wrong type: a RuntimeException is thrown that names the key and what it actually was, e.g. Config key 'mail.port' must be int, array given.
  • .env-friendly conversion: values that arrive as strings are converted when the conversion is unambiguous.
    • string() accepts strings, ints and floats.
    • int() accepts ints and integer-looking strings such as '587' (but not '5.5').
    • bool() accepts booleans, plus strings or ints that read as a boolean: true/false, 1/0, on/off, yes/no.
    • array() accepts arrays only.

Env

Rhapsody\Core\Support\Env gives typed access to environment values, such as those loaded from your .env file. It looks in $_ENV first and then falls back to getenv(). All methods are static.

Method Returns
Env::get($key)The raw value as a string, or null if the variable is not set
Env::string($key, $default = '')The value, or $default when unset
Env::int($key, $default = 0)The value as an int, or $default when unset or not numeric
Env::bool($key, $default = false)The value as a bool, or $default when unset or not recognised
$url      = Env::string('APP_URL');
$theme    = Env::string('APP_THEME', 'default');
$retries  = Env::int('QUEUE_RETRIES', 3);        // example variable
$maintain = Env::bool('MAINTENANCE_MODE', false);  // example variable

For Env::bool(), true, 1, yes and on are true, and false, 0, no, off and an empty value are false (any case). Anything else returns your default.

Requiring a value

Env never throws. If a variable must be set, check for null yourself:

$apiKey = Env::get('PAYMENT_API_KEY')
    ?? throw new RuntimeException('PAYMENT_API_KEY is not set in .env');

Env or Config?

Env reads the raw environment. Config reads your application's config array, which is usually itself built from environment values. In application code, prefer Config so each setting is read from one place; reach for Env in the config file itself and in early boot code that runs before the config exists.


Fs

Rhapsody\Core\Support\Fs wraps PHP's file functions so a failure becomes a RuntimeException instead of a silent false. It pairs naturally with the Path helper, which builds the location.

Method Returns Throws when
Fs::read($path)The whole file as a stringThe file cannot be read
Fs::lines($path, $flags)A list of lines. By default newlines are stripped and empty lines are skipped.The file cannot be read
Fs::write($path, $contents, $flags = 0)The number of bytes written. Pass FILE_APPEND and/or LOCK_EX in $flags.The file cannot be written
Fs::glob($pattern, $flags = 0)A list of matching paths. No matches is an empty list, not an error.The glob itself fails
Fs::realpath($path)The canonical absolute pathThe path does not exist
Fs::cwd()The current working directoryIt cannot be determined
use Rhapsody\Core\Helpers\Path;
use Rhapsody\Core\Support\Fs;

// Append to a log file
Fs::write(Path::logs('import.log'), date('c') . " import started\n", FILE_APPEND | LOCK_EX);

// Read a file line by line
foreach (Fs::lines(Path::root('.env.example')) as $line) {
    // ...
}

// Find files (an empty list if there are none)
foreach (Fs::glob(Path::storage('exports/*.csv')) as $file) {
    // ...
}

Handling failures

try {
    $contents = Fs::read($path);
} catch (RuntimeException $e) {
    // The message names the path, e.g. "Unable to read file: /var/www/..."
}

Fs::read(), lines() and write() also suppress PHP's own warning, so a failure produces exactly one exception and no stray warning in your logs or toolbar.

Note: Fs does not create missing directories and does not sanitise paths. If any part of a path comes from user input, read the security notes on the Path helper page first.

Json

Rhapsody\Core\Support\Json encodes and decodes JSON and always throws a JsonException on bad input, so you never have to check json_last_error().

Method Returns Notes
Json::decode($json)Any decoded valueObjects become associative arrays. Valid scalars such as 42 or null are returned as-is.
Json::decodeArray($json)An arrayThrows if the document is valid JSON but decodes to a scalar or null. Use this when you need an array.
Json::encode($value, $flags = 0)A JSON stringThrows if the value cannot be encoded. Any JSON_* flags are accepted.
use Rhapsody\Core\Support\Json;

$composer = Json::decodeArray(Fs::read(Path::root('composer.json')));
echo $composer['name'];

$payload = Json::encode(['ok' => true, 'url' => 'https://example.com/a/b'], JSON_UNESCAPED_SLASHES);

try {
    $data = Json::decodeArray($request->getContent());
} catch (JsonException $e) {
    // Not valid JSON, or not a JSON object/array
}

Good to Know

  • All four classes are final. If you want a project-specific variation, wrap them in your own small class rather than extending them.
  • Env, Fs and Json are static; there is nothing to instantiate or inject. Config is an object, so it is injected.
  • The Request object offers the same style of typed access for request input.

Related Documentation