stillat/proteus
Proteus provides a flexible, developer-friendly way to build and run dynamic “protean” objects in Laravel/PHP. Define behaviors, properties, and runtime composition with a clean API, useful for prototyping, extensible domain models, and data-driven object structures.
Installation
composer require stillat/proteus
No service provider or facade needed—use the Proteus class directly.
First Use Case: Reading Config
use Stillat\Proteus\Proteus;
$config = Proteus::read(config_path('app.php'));
// Returns an array of the parsed config
Where to Look First
src/Proteus.php for core logic.config() helper and config.php structure.$appConfig = Proteus::read(config_path('app.php'));
$customConfig = Proteus::read(base_path('config/custom.php'));
require or include.require config_path('app.php'); in custom config loaders.$config = Proteus::read(config_path('app.php'));
$config['debug'] = env('APP_DEBUG', false);
Proteus::write(config_path('app.php'), $config);
Proteus::validate() (if extended).$base = Proteus::read(config_path('app.php'));
$override = Proteus::read(config_path('app-override.php'));
$merged = array_merge_recursive($base, $override);
Proteus::write(config_path('app.php'), $merged);
// In a custom Artisan command
public function handle() {
$config = Proteus::read(config_path('app.php'));
$config['timezone'] = 'America/New_York';
Proteus::write(config_path('app.php'), $config);
$this->info('Config updated!');
}
$env = app()->environment();
$configPath = config_path("app-{$env}.php");
if (file_exists($configPath)) {
$config = Proteus::read($configPath);
}
app-local.php, app-production.php).Proteus for non-standard config files (e.g., third-party or dynamic configs).Proteus::write($path, $config);
Artisan::call('config:clear');
config/package-name.php:
$defaults = Proteus::read(__DIR__.'/config.php');
$userConfig = config_path('package-name.php');
if (file_exists($userConfig)) {
$defaults = array_merge_recursive($defaults, Proteus::read($userConfig));
}
$testConfig = Proteus::read(base_path("tests/config/test.php"));
File Permissions
storage/ or config/ has 755 permissions or use storage_path('app/config-custom.php').Syntax Errors
Proteus::read() throws exceptions on invalid PHP syntax (unlike Laravel’s require).Proteus::validate($config) (if implemented) or wrap in a try-catch.Recursive Merging Quirks
array_merge_recursive may not behave as expected with nested arrays.array_replace_recursive for deeper overrides:
$merged = array_replace_recursive($base, $override);
Caching Interactions
Artisan::call('config:clear') or use config()->clear().Non-Standard Config Files
Proteus assumes PHP files with return []; structure.Inspect Raw Config
$raw = file_get_contents(config_path('app.php'));
var_dump($raw); // Check for syntax issues
Validate Before Writing
$config = Proteus::read($path);
if (isset($config['invalid_key'])) {
throw new \RuntimeException('Invalid config detected!');
}
Log Changes
$old = Proteus::read($path);
Proteus::write($path, $new);
\Log::info('Config updated', ['old' => $old, 'new' => $new]);
Custom Parsers
Proteus to support non-PHP config files (e.g., JSON, YAML):
class JsonProteus extends Proteus {
public static function read(string $path) {
return json_decode(file_get_contents($path), true);
}
}
Validation Rules
use Stillat\Proteus\Contracts\Validatable;
class ValidatedProteus implements Validatable {
public function validate(array $config): bool {
return isset($config['required_key']);
}
}
Event Dispatching
Proteus::onWrite(function ($path, $config) {
event(new ConfigUpdated($path, $config));
});
Fallback Logic
$config = Proteus::read($path) ?: Proteus::read($fallbackPath);
How can I help you explore Laravel packages today?