Skip to content

Commit

Permalink
shorthand available for all arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
juliangut committed Oct 20, 2018
1 parent 153902d commit 92809eb
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 59 deletions.
31 changes: 27 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,38 @@ Services are registered in the following order:
* Default Slim services
* Definitions provided in configuration in the order they are in the array

## Slim's settings direct access
## Array value access shorthand

Default `\Jgut\Slim\PHPDI\Container` container allows direct access to Slim's settings array values by prepending 'settings.' to setting key. If setting is not defined normal container's `ContainerValueNotFoundException` is thrown
Default `\Jgut\Slim\PHPDI\Container` container allows shorthand to access array values by concatenating array keys with dots. If any key in the chain is not defined normal container's `ContainerValueNotFoundException` is thrown

```php
$container->get('settings')['displayErrorDetails'];
$container->get('settings.displayErrorDetails');
$container->get('settings')['displayErrorDetails']; // true
$container->get('settings.displayErrorDetails'); // true
```

This functionality would most commonly be used to directly access settings but can be used to access any other array defined in the container

#### Notice

Be careful though not to shadow any array keys by using dots in keys

```php
$settings = [
'foo' => [
'bar' => [
'baz' => 'shadowed!',
],
],
'foo.bar' => 'bang!',
];
$container->set('settings', $settings);

$container->get('settings.foo.bar'); // bang!
$container->get('settings.foo.bar.baz'); // ContainerValueNotFoundException thrown
```

_The easiest way to avoid this from ever happening is by not using dots in keys_

## Migration from 1.x

* Minimum Slim version is now 3.9
Expand Down
11 changes: 4 additions & 7 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,12 @@
"friendsofphp/php-cs-fixer": "^2.0",
"infection/infection": "^0.7",
"phpmd/phpmd": "^2.0",
"phpmetrics/phpmetrics": "^2.0",
"phpstan/phpstan": "^0.9",
"phpstan/phpstan-strict-rules": "^0.9",
"phpunit/phpunit": "^6.0",
"povils/phpmnd": "^1.1",
"phpunit/phpunit": "^6.0|^7.0",
"povils/phpmnd": "^1.1|^2.0",
"roave/security-advisories": "dev-master",
"sebastian/phpcpd": "^3.0|^4.0",
"sebastian/phpcpd": "^2.0|^4.0",
"squizlabs/php_codesniffer": "^2.0"
},
"suggest": {
Expand Down Expand Up @@ -74,7 +73,6 @@
"qa-phpstan": "phpstan analyse --level max --configuration=phpstan.neon --memory-limit=2G --no-progress src",
"test-phpunit": "phpunit",
"test-infection": "infection",
"report-phpmetrics": "phpmetrics --report-html=build/metrics --report-violations=build/logs/violations.xml --offline .",
"report-phpunit-coverage": "phpunit --coverage-html build/coverage",
"report-phpunit-clover": "phpunit --coverage-clover build/logs/clover.xml",
"lint": [
Expand All @@ -98,8 +96,7 @@
],
"report": [
"@report-phpunit-coverage",
"@report-phpunit-clover",
"@report-phpmetrics"
"@report-phpunit-clover"
]
},
"extra": {
Expand Down
71 changes: 38 additions & 33 deletions src/ContainerTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,16 @@ trait ContainerTrait
public function get($name)
{
try {
if (\is_string($name) && \strpos($name, 'settings.') === 0) {
return $this->getSetting(\substr($name, 9), parent::get('settings'));
}

return parent::get($name);
return strpos($name, '.') === false
? parent::get($name)
: $this->getRecursive($name);
} catch (NotFoundException $exception) {
throw new ContainerValueNotFoundException($exception->getMessage(), $exception->getCode(), $exception);
} catch (\Exception $exception) {
throw new ContainerValueNotFoundException(
\sprintf('No entry or class found for "%s"', $name),
$exception->getCode(),
$exception
);
} catch (\Throwable $exception) {
throw new ContainerException($exception->getMessage(), $exception->getCode(), $exception);
}
}
Expand All @@ -62,49 +64,52 @@ public function get($name)
*/
public function has($name)
{
if (\is_string($name) && \strpos($name, 'settings.') === 0) {
try {
$this->getSetting(\substr($name, 9), parent::get('settings'));
if (strpos($name, '.') === false) {
return parent::has($name);
}

return true;
} catch (\Exception $exception) {
return false;
}
try {
$this->getRecursive($name);
} catch (\Throwable $exception) {
return false;
}

return parent::has($name);
return true;
}

/**
* Get setting from settings.
*
* @param string $setting
* @param array $settings
* @param string $key
* @param array|null $parent
*
* @throws NotFoundException
*
* @return mixed
*/
protected function getSetting(string $setting, array $settings)
private function getRecursive(string $key, array $parent = null)
{
$segments = \explode('.', $setting);
if ($parent !== null ? \array_key_exists($key, $parent) : parent::has($key)) {
return $parent !== null ? $parent[$key] : parent::get($key);
}

$keySegments = \explode('.', $key);
$keyParts = [];

while (\count($keySegments) > 1) {
\array_unshift($keyParts, \array_pop($keySegments));
$subKey = \implode('.', $keySegments);

while ($segment = \array_shift($segments)) {
if (\count($segments) > 0) {
$combinedSetting = $segment . '.' . \implode('.', $segments);
if (\is_array($settings) && \array_key_exists($combinedSetting, $settings)) {
return $settings[$combinedSetting];
if ($parent !== null ? \array_key_exists($subKey, $parent) : parent::has($subKey)) {
$parent = $parent !== null ? $parent[$subKey] : parent::get($subKey);

if (!\is_array($parent)) {
break;
}
}

if (!\is_array($settings) || !\array_key_exists($segment, $settings)) {
throw new NotFoundException(\sprintf('Setting "%s" not found', $setting));
return $this->getRecursive(\implode('.', $keyParts), $parent);
}

$settings = $settings[$segment];
}

return $settings;
throw new NotFoundException(\sprintf('Entry "%s" not found', $key));
}

/**
Expand Down Expand Up @@ -147,7 +152,7 @@ public function offsetGet($name)
*
* @return bool
*/
public function offsetExists($name)
public function offsetExists($name): bool
{
return $this->has($name);
}
Expand Down
47 changes: 32 additions & 15 deletions tests/PHPDI/ContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function setUp()

/**
* @expectedException \Slim\Exception\ContainerValueNotFoundException
* @expectedExceptionMessage No entry or class found for 'baz'
* @expectedExceptionMessage No entry or class found for "baz"
*/
public function testGetNonExistent()
{
Expand All @@ -58,43 +58,60 @@ public function testGetNonExistent()

/**
* @expectedException \Slim\Exception\ContainerValueNotFoundException
* @expectedExceptionMessage Setting "baz" not found
* @expectedExceptionMessage No entry or class found for "settings.baz"
*/
public function testGetNonExistentSetting()
public function testGetNonExistentWithDots()
{
self::assertFalse($this->container->has('settings.baz'));
$this->container['settings.baz'];
}

/**
* @expectedException \Slim\Exception\ContainerValueNotFoundException
* @expectedExceptionMessage No entry or class found for "settings.foo.bar.baz"
*/
public function testGetShadowed()
{
$settings = [
'foo' => [
'bar' => [
'baz' => 'shadowed!',
],
],
'foo.bar' => 'bang!',
];
$this->container->set('settings', $settings);

self::assertTrue($this->container->has('settings.foo.bar'));
self::assertEquals('bang!', $this->container->get('settings.foo.bar'));

self::assertFalse($this->container->has('settings.foo.bar.baz'));
$this->container->get('settings.foo.bar.baz');
}

public function testSettingsAccess()
{
$settings = [
'foo' => [
'bar' => [
'baz' => 'found!',
'bam' => [],
],
],
'foo.bar' => 'bang!',
];
$this->container->set('settings', $settings);

self::assertTrue($this->container->has('settings.foo'));
self::assertEquals(['bar' => ['baz' => 'found!']], $this->container->get('settings.foo'));
self::assertEquals(['bar' => ['baz' => 'found!', 'bam' => []]], $this->container->get('settings.foo'));

self::assertTrue($this->container->has('settings.foo.bar'));
self::assertEquals('bang!', $this->container->get('settings.foo.bar'));
self::assertEquals(['baz' => 'found!', 'bam' => []], $this->container->get('settings.foo.bar'));

self::assertTrue($this->container->has('settings.foo.bar.baz'));
self::assertEquals('found!', $this->container->get('settings.foo.bar.baz'));
}

/**
* @expectedException \Slim\Exception\ContainerValueNotFoundException
* @expectedExceptionMessage No entry or class found for 'none'
*/
public function testGetWrong()
{
$this->container->get('none');
self::assertTrue($this->container->has('settings.foo.bar.bam'));
self::assertEquals([], $this->container->get('settings.foo.bar.bam'));
}

/**
Expand All @@ -105,7 +122,7 @@ public function testUnresolvable()
{
$configuration = new Configuration([
'definitions' => [
['foo' => \DI\create('\\Foo\\Bar')],
['foo' => \DI\create('\\Unknown\\Foo\\Bar')],
],
]);

Expand Down

0 comments on commit 92809eb

Please sign in to comment.