Skip to content

Commit

Permalink
v1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
f3ath committed Mar 7, 2017
1 parent 2229c02 commit 297d512
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 73 deletions.
78 changes: 69 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,76 @@
# silex-config
Simple multi-environment configuration for Silex with secret storage support
## Simple multi-environment configuration for Pimple/Silex with secret storage support
## Install
```
composer install f3ath/silex-config
composer install f3ath/pimple-config
```
## Usage
## Configuration structure
### Environment-specific config
A config is an `<environment_name>.php` file which returns an array:
```php
<?php
// dev.php
return [
'debug' => true,
'foo' => [
'bar' => 'baz'
]
];
```
To reduce duplication, here is some sort of "inheritance":
```php
<?php
// stage.php
return array_replace_recursive(require __DIR__ . '/common.php', [ // inherit from common config
'debug' => false,
]);
```
### Secret config
It is a healthy practice to store sensitive data like passwords outside of the repository. The simplest implementation
would be to store such files right on the server. These files may be edited directly, so they should not be php scripts,
since there is a good chance to accidentally remove the `<?php` header and expose their content. A natural choice in
this case is JSON. PimpleConfig supports a special `secret_json` key to include such files.
```php
<?php
// prod.php
return array_replace_recursive(require __DIR__ . '/common.php', [ // inherit from common config
'secret_json' => '/etc/my_application/secret.json',
'debug' => false,
]);
```
### Services
Pimple services are configured in the set of files in the `services` directory. In your configuration you define:
```php
<?php
// common.php
return [
'services' => [
'application' => __DIR__ . '/services/application.php',
'storage' => __DIR__ . '/services/storage.php',
'controllers' => __DIR__ . '/services/controllers.php',
],
];
```
A service config is a php script which returns a special function:
```php
<?php
// services/application.php
return function (\Pimple\Container $container, array $config, string $env) {
$container['hello'] = function () use ($config, $env) {
// here you create and return a service in Pimple way
};
};
```
## Register the configuration

```php
<?php
$app = new \Silex\Application\Application();
$env = 'prod';
$path = '/path-to-config';
(new \F3\SilexConfig\Config($path))->configure($app, $env);
$pimple = new \Pimple\Container();
$env_name = 'prod';
$config_root = '/path-to-config';
$pimple->register(new \F3\PimpleConfig\Config($config_root, $env_name));
```

For more examples see the [unit test](test/ConfigTest.php).
For more examples see the [unit test](test/ConfigTest.php).

### Contribution
Please do!
23 changes: 12 additions & 11 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
{
"name": "f3ath/silex-config",
"description": "Simple multi-environment configuration for Silex with secret storage support",
"name": "f3ath/pimple-config",
"description": "Simple multi-environment configuration for Pimple with secret storage support",
"type": "library",
"require-dev": {
"silex/silex": "^2",
"phpunit/phpunit": "^6.0",
"squizlabs/php_codesniffer": "^2.8"
},
"license": "MIT",
"authors": [
{
"name": "Alexey Karapetov",
"email": "[email protected]"
}
],
"require": {},
"config": {
"platform": {
"php": "7"
}
},
"suggest": {
"silex/silex": "Silex Framework"
"require": {
"pimple/pimple": "^3"
},
"require-dev": {
"phpunit/phpunit": "^6",
"squizlabs/php_codesniffer": "^2.8"
},
"autoload": {
"psr-4": {
"F3\\SilexConfig\\": "src/"
"F3\\PimpleConfig\\": "src/"
}
},
"scripts": {
"test": "vendor/bin/phpunit"
}
}
71 changes: 37 additions & 34 deletions src/Config.php
Original file line number Diff line number Diff line change
@@ -1,58 +1,61 @@
<?php
namespace F3\SilexConfig;
namespace F3\PimpleConfig;

use Silex\Application;
use Pimple\Container;
use Pimple\ServiceProviderInterface;

class Config
class Config implements ServiceProviderInterface
{
const KEY_SERVICES = 'services';
const KEY_SECRET_JSON = 'secret_json';
private $dir;
private $env;

public function __construct(string $dir)
public function __construct(string $config_root, string $environment_name)
{
$this->dir = $dir;
$this->dir = $config_root;
$this->env = $environment_name;
}

public function configure(Application $app, string $env = 'prod')
public function register(Container $pimple)
{
$config = array_replace_recursive(
$this->getDefaultConfig(),
$this->getConfig($env)
);
if ($config[self::KEY_SECRET_JSON]) {
$config = array_replace_recursive(
$config,
$this->getSecretConfig($config[self::KEY_SECRET_JSON])
);
}
array_map(
function ($service) use ($app, $config, $env) {
(require $service)($app, $config, $env);
},
$config[self::KEY_SERVICES]
);
$config = $this->getConfig();
$config = $this->applySecretConfig($config);
$this->configureServices($pimple, $config);
}

private function getConfig(string $env): array
protected function getConfig(): array
{
$file = "{$this->dir}/{$env}.php";
$file = "{$this->dir}/{$this->env}.php";
if (file_exists($file)) {
return include $file;
}
throw new \InvalidArgumentException("Configuration not found for $env");
throw new \InvalidArgumentException("Configuration not found for {$this->env}");
}

protected function applySecretConfig($config): array
{
return array_replace_recursive(
$config,
$this->getSecretConfig($config)
);
}

private function getDefaultConfig(): array
protected function getSecretConfig(array $config): array
{
return [
self::KEY_SERVICES => [],
self::KEY_SECRET_JSON => null,
];
$secret_json = $config['secret_json'] ?? null;
if ($secret_json) {
return json_decode(file_get_contents($secret_json), true);
}
return [];
}

private function getSecretConfig(string $file): array
protected function configureServices(Container $container, array $config)
{
return json_decode(file_get_contents($file), true);
array_map(
function ($service) use ($container, $config) {
(require $service)($container, $config, $this->env);
},
$config['services']
);
}

}
28 changes: 14 additions & 14 deletions test/ConfigTest.php
Original file line number Diff line number Diff line change
@@ -1,49 +1,49 @@
<?php
namespace F3\SilexConfig;
namespace F3\PimpleConfig;

use PHPUnit\Framework\TestCase;
use Silex\Application;
use Pimple\Container;

class ConfigTest extends TestCase
{
public function testExampleProd()
{
$app = new Application();
(new Config(__DIR__ . '/example'))->configure($app, 'prod');
$container = new Container();
$container->register(new Config(__DIR__ . '/example', 'prod'));

$this->assertEquals(
'prod',
$app['env'],
$container['env'],
'Environment name is set correctly'
);
$this->assertFalse(
$app['debug'],
$container['debug'],
'Debug is disabled in prod'
);
$this->assertEquals(
'foo is common_foo, bar is prod_bar, password is s3(r37p455w0rd',
$app['hello'],
$container['hello'],
'Hello service returns the correct string'
);
}

public function testExampleDev()
{
$app = new Application();
(new Config(__DIR__ . '/example'))->configure($app, 'dev');
$container = new Container();
$container->register(new Config(__DIR__ . '/example', 'dev'));

$this->assertEquals(
'dev',
$app['env'],
$container['env'],
'Environment name is set correctly'
);
$this->assertTrue(
$app['debug'],
$container['debug'],
'Debug is enabled in dev'
);
$this->assertEquals(
'foo is dev_foo, bar is common_bar, password is dev_password',
$app['hello'],
$container['hello'],
'Hello service returns the correct string'
);
}
Expand All @@ -54,7 +54,7 @@ public function testExampleDev()
*/
public function testInvalidEnvName()
{
$app = new Application();
(new Config(__DIR__ . '/example'))->configure($app, 'stage');
$container = new Container();
$container->register(new Config(__DIR__ . '/example', 'stage'));
}
}
6 changes: 3 additions & 3 deletions test/example/services/app.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
// Generic application settings
return function (\Silex\Application $app, array $config, string $env) {
$app['env'] = $env;
$app['debug'] = $config['debug'];
return function (\Pimple\Container $container, array $config, string $env) {
$container['env'] = $env;
$container['debug'] = $config['debug'];
};
4 changes: 2 additions & 2 deletions test/example/services/hello.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php
// Configure the hello service
return function (\Silex\Application $app, array $config) {
$app['hello'] = function () use ($config) {
return function (\Pimple\Container $container, array $config) {
$container['hello'] = function () use ($config) {
return "foo is {$config['foo']}, bar is {$config['bar']}, password is {$config['password']}";
};
};

0 comments on commit 297d512

Please sign in to comment.