Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[DependencyInjection] added first version of the config normalizer
This is mainly intended for complex configurations to ease the work you have with normalizing different configuration formats (YAML, XML, and PHP). First, you have to set-up a config tree: $treeBuilder = new TreeBuilder(); $tree = $treeBuilder ->root('security_config', 'array') ->node('access_denied_url', 'scalar')->end() ->normalize('encoder') ->node('encoders', 'array') ->key('class') ->prototype('array') ->before()->ifString()->then(function($v) { return array('algorithm' => $v); })->end() ->node('algorithm', 'scalar')->end() ->node('encode_as_base64', 'scalar')->end() ->node('iterations', 'scalar')->end() ->end() ->end() ->end() ->buildTree() ; This tree and the metadata attached to the different nodes is then used to intelligently transform the passed config array: $normalizedConfig = $tree->normalize($config);
- Loading branch information
1 parent
a28151a
commit b484763
Showing
11 changed files
with
611 additions
and
0 deletions.
There are no files selected for viewing
105 changes: 105 additions & 0 deletions
105
src/Symfony/Component/DependencyInjection/Configuration/ArrayNode.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
<?php | ||
|
||
namespace Symfony\Component\DependencyInjection\Configuration; | ||
|
||
use Symfony\Component\DependencyInjection\Extension\Extension; | ||
|
||
class ArrayNode extends BaseNode implements PrototypeNodeInterface | ||
{ | ||
protected $normalizeTransformations; | ||
protected $children; | ||
protected $prototype; | ||
protected $keyAttribute; | ||
|
||
public function __construct($name, NodeInterface $parent = null, array $beforeTransformations = array(), array $afterTransformations = array(), array $normalizeTransformations = array(), $keyAttribute = null) | ||
{ | ||
parent::__construct($name, $parent, $beforeTransformations, $afterTransformations); | ||
|
||
$this->children = array(); | ||
$this->normalizeTransformations = $normalizeTransformations; | ||
$this->keyAttribute = $keyAttribute; | ||
} | ||
|
||
public function setName($name) | ||
{ | ||
$this->name = $name; | ||
} | ||
|
||
public function setPrototype(PrototypeNodeInterface $node) | ||
{ | ||
if (count($this->children) > 0) { | ||
throw new \RuntimeException('An ARRAY node must either have concrete children, or a prototype node.'); | ||
} | ||
|
||
$this->prototype = $node; | ||
} | ||
|
||
public function addChild(NodeInterface $node) | ||
{ | ||
$name = $node->getName(); | ||
if (empty($name)) { | ||
throw new \InvalidArgumentException('Node name cannot be empty.'); | ||
} | ||
if (isset($this->children[$name])) { | ||
throw new \InvalidArgumentException(sprintf('The node "%s" already exists.', $name)); | ||
} | ||
if (null !== $this->prototype) { | ||
throw new \RuntimeException('An ARRAY node must either have a prototype, or concrete children.'); | ||
} | ||
|
||
$this->children[$name] = $node; | ||
} | ||
|
||
protected function validateType($value) | ||
{ | ||
if (!is_array($value)) { | ||
throw new InvalidTypeException(sprintf( | ||
'Invalid type for path "%s". Expected array, but got %s', | ||
$this->getPath(), | ||
json_encode($value) | ||
)); | ||
} | ||
} | ||
|
||
protected function normalizeValue($value) | ||
{ | ||
foreach ($this->normalizeTransformations as $transformation) { | ||
list($singular, $plural) = $transformation; | ||
|
||
if (!isset($value[$singular])) { | ||
continue; | ||
} | ||
|
||
$value[$plural] = Extension::normalizeConfig($value, $singular, $plural); | ||
} | ||
|
||
if (null !== $this->prototype) { | ||
$normalized = array(); | ||
foreach ($value as $k => $v) { | ||
if (null !== $this->keyAttribute && is_array($v) && isset($v[$this->keyAttribute])) { | ||
$k = $v[$this->keyAttribute]; | ||
} | ||
|
||
$this->prototype->setName($k); | ||
if (null !== $this->keyAttribute) { | ||
$normalized[$k] = $this->prototype->normalize($v); | ||
} else { | ||
$normalized[] = $this->prototype->normalize($v); | ||
} | ||
} | ||
|
||
return $normalized; | ||
} | ||
|
||
$normalized = array(); | ||
foreach ($this->children as $name => $child) { | ||
if (!array_key_exists($name, $value)) { | ||
continue; | ||
} | ||
|
||
$normalized[$name] = $child->normalize($value[$name]); | ||
} | ||
|
||
return $normalized; | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
src/Symfony/Component/DependencyInjection/Configuration/BaseNode.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
<?php | ||
|
||
namespace Symfony\Component\DependencyInjection\Configuration; | ||
|
||
abstract class BaseNode implements NodeInterface | ||
{ | ||
protected $name; | ||
protected $parent; | ||
protected $beforeTransformations; | ||
protected $afterTransformations; | ||
protected $nodeFactory; | ||
|
||
public function __construct($name, NodeInterface $parent = null, $beforeTransformations = array(), $afterTransformations = array()) | ||
{ | ||
if (false !== strpos($name, '.')) { | ||
throw new \InvalidArgumentException('The name must not contain ".".'); | ||
} | ||
|
||
$this->name = $name; | ||
$this->parent = $parent; | ||
$this->beforeTransformations = $beforeTransformations; | ||
$this->afterTransformations = $afterTransformations; | ||
} | ||
|
||
public function getName() | ||
{ | ||
return $this->name; | ||
} | ||
|
||
public function getPath() | ||
{ | ||
$path = $this->name; | ||
|
||
if (null !== $this->parent) { | ||
$path = $this->parent->getPath().'.'.$path; | ||
} | ||
|
||
return $path; | ||
} | ||
|
||
public final function normalize($value) | ||
{ | ||
// run before transformations | ||
foreach ($this->beforeTransformations as $transformation) { | ||
$value = $transformation($value); | ||
} | ||
|
||
// validate type | ||
$this->validateType($value); | ||
|
||
// normalize value | ||
$value = $this->normalizeValue($value); | ||
|
||
// run after transformations | ||
foreach ($this->afterTransformations as $transformation) { | ||
$value = $transformation($value); | ||
} | ||
|
||
return $value; | ||
} | ||
|
||
abstract protected function validateType($value); | ||
abstract protected function normalizeValue($value); | ||
} |
62 changes: 62 additions & 0 deletions
62
src/Symfony/Component/DependencyInjection/Configuration/Builder/ExprBuilder.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
<?php | ||
|
||
namespace Symfony\Component\DependencyInjection\Configuration\Builder; | ||
|
||
class ExprBuilder | ||
{ | ||
public $parent; | ||
public $ifPart; | ||
public $thenPart; | ||
|
||
public function __construct($parent) | ||
{ | ||
$this->parent = $parent; | ||
} | ||
|
||
public function ifTrue(\Closure $closure) | ||
{ | ||
$this->ifPart = $closure; | ||
|
||
return $this; | ||
} | ||
|
||
public function ifString() | ||
{ | ||
$this->ifPart = function($v) { return is_string($v); }; | ||
|
||
return $this; | ||
} | ||
|
||
public function ifNull() | ||
{ | ||
$this->ifPart = function($v) { return null === $v; }; | ||
|
||
return $this; | ||
} | ||
|
||
public function ifArray() | ||
{ | ||
$this->ifPart = function($v) { return is_array($v); }; | ||
|
||
return $this; | ||
} | ||
|
||
public function then(\Closure $closure) | ||
{ | ||
$this->thenPart = $closure; | ||
|
||
return $this; | ||
} | ||
|
||
public function end() | ||
{ | ||
if (null === $this->ifPart) { | ||
throw new \RuntimeException('You must specify an if part.'); | ||
} | ||
if (null === $this->thenPart) { | ||
throw new \RuntimeException('You must specify a then part.'); | ||
} | ||
|
||
return $this->parent; | ||
} | ||
} |
92 changes: 92 additions & 0 deletions
92
src/Symfony/Component/DependencyInjection/Configuration/Builder/NodeBuilder.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
<?php | ||
|
||
namespace Symfony\Component\DependencyInjection\Configuration\Builder; | ||
|
||
class NodeBuilder | ||
{ | ||
/************ | ||
* READ-ONLY | ||
************/ | ||
public $name; | ||
public $type; | ||
public $key; | ||
public $parent; | ||
public $children; | ||
public $prototype; | ||
public $normalizeTransformations; | ||
public $beforeTransformations; | ||
public $afterTransformations; | ||
|
||
public function __construct($name, $type, $parent = null) | ||
{ | ||
$this->name = $name; | ||
$this->type = $type; | ||
$this->parent = $parent; | ||
|
||
$this->children = | ||
$this->beforeTransformations = | ||
$this->afterTransformations = | ||
$this->normalizeTransformations = array(); | ||
} | ||
|
||
/**************************** | ||
* FLUID INTERFACE | ||
****************************/ | ||
|
||
public function node($name, $type) | ||
{ | ||
$node = new NodeBuilder($name, $type, $this); | ||
|
||
return $this->children[$name] = $node; | ||
} | ||
|
||
public function normalize($key, $plural = null) | ||
{ | ||
if (null === $plural) { | ||
$plural = $key.'s'; | ||
} | ||
|
||
$this->normalizeTransformations[] = array($key, $plural); | ||
|
||
return $this; | ||
} | ||
|
||
public function key($name) | ||
{ | ||
$this->key = $name; | ||
|
||
return $this; | ||
} | ||
|
||
public function before(\Closure $closure = null) | ||
{ | ||
if (null !== $closure) { | ||
$this->beforeTransformations[] = $closure; | ||
|
||
return $this; | ||
} | ||
|
||
return $this->beforeTransformations[] = new ExprBuilder($this); | ||
} | ||
|
||
public function prototype($type) | ||
{ | ||
return $this->prototype = new NodeBuilder(null, $type, $this); | ||
} | ||
|
||
public function after(\Closure $closure = null) | ||
{ | ||
if (null !== $closure) { | ||
$this->afterTransformations[] = $closure; | ||
|
||
return $this; | ||
} | ||
|
||
return $this->afterTransformations[] = new ExprBuilder($this); | ||
} | ||
|
||
public function end() | ||
{ | ||
return $this->parent; | ||
} | ||
} |
Oops, something went wrong.