grasmash/expander
Expander is a Laravel package for managing feature rollouts and gating functionality. It helps you define “expansions” that can be enabled per environment, user, or percentage, making it easy to ship safely, run experiments, and toggle features without redeploys.
composer require grasmash/expander
use Grasmash\Expander\Expander;
$expander = new Expander();
$config = [
'database' => [
'host' => 'localhost',
'port' => 3306,
'connection_string' => '${database.host}:${database.port}'
]
];
$expanded = $expander->expandArrayProperties($config);
First Use Case: Expand dot-notation references in Laravel config files (e.g., config/app.php) or API response templates.Config Expansion:
config/app.php or config/services.php to reference other config values:
'redis' => [
'host' => '${cache.host}',
'port' => '${cache.port}'
]
config('redis.host').API Response Transformation:
$response = [
'data' => [
'id' => '1',
'type' => 'article',
'attributes' => [
'title' => '${article.title}',
'author' => '${article.author.name}'
]
]
];
$expander->expandArrayProperties($response);
Dynamic Payload Processing:
$payload = $request->all();
$expanded = $expander->expandArrayProperties($payload);
public function boot()
{
$this->app->singleton(Expander::class, fn() => new Expander());
}
Expander in jobs to resolve references before processing:
public function handle()
{
$data = $this->expander->expandArrayProperties($this->data);
// Process $data...
}
$expander->setStringifier(new class implements StringifierInterface {
public function stringify(array $array): string {
return implode('|', $array);
}
});
$config = ['log_level' => '${APP_LOG_LEVEL}'];
putenv('APP_LOG_LEVEL=debug');
$expanded = $expander->expandArrayProperties($config);
Unresolvable References:
${not.real.property} remains unchanged. Use ?? for defaults:
'fallback' => '${missing.key}??default_value'
$expander->setLogger(new \Monolog\Logger('expander'));
Circular References:
A references B and B references A. The package detects this but may not handle it gracefully. Solution: Validate input or use a DepthFirstExpander for controlled recursion.Type Mismatches:
${array.key} into a non-array value breaks nested access. Tip: Validate structure before expansion.Environment Variables:
$_SERVER takes precedence over getenv(). Use putenv() for testing:
putenv('TEST_VAR=value'); // Overrides $_SERVER
$expander->expandArrayProperties($array, [], true); // Third arg = debug mode
$expander->setLogger(new \Monolog\Handler\StreamHandler(storage_path('logs/expander.log')));
config('app.*') if unused).Expander instance or results:
$this->app->singleton(Expander::class, fn() => new Expander());
Custom Expanders:
Extend Expander to add logic (e.g., for Laravel’s HasMany):
class EloquentExpander extends Expander {
public function expand($data) {
if (is_array($data) && isset($data['model'])) {
return $this->resolveEloquent($data['model']);
}
return parent::expand($data);
}
}
Pre/Post-Processors:
Use addExpander() to inject logic:
$expander->addExpander('user', fn($id) => User::with('posts')->find($id));
Validation:
Combine with Laravel’s Validator to reject malformed references:
$validator = Validator::make($array, ['key' => 'sometimes|expanded']);
How can I help you explore Laravel packages today?