Skip to content

Commit

Permalink
Better config validation handling for numerical values:
Browse files Browse the repository at this point in the history
 * New node type Integer and Float
 * New expressions: min() and max()
  • Loading branch information
jeanmonod committed Sep 18, 2012
1 parent 736aa21 commit 71db836
Show file tree
Hide file tree
Showing 12 changed files with 514 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Config\Definition\Builder;

use Symfony\Component\Config\Definition\FloatNode;

/**
* This class provides a fluent interface for defining a float node.
*
* @author Jeanmonod David <david.jeanmonod@gmail.com>
*/
class FloatNodeDefinition extends NumericNodeDefinition
{
/**
* Instantiate a Node
*
* @return FloatNode The node
*/
protected function instantiateNode()
{
return new FloatNode($this->name, $this->parent, $this->min, $this->max);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Config\Definition\Builder;

use Symfony\Component\Config\Definition\IntegerNode;

/**
* This class provides a fluent interface for defining an integer node.
*
* @author Jeanmonod David <david.jeanmonod@gmail.com>
*/
class IntegerNodeDefinition extends NumericNodeDefinition
{
/**
* Instantiate a Node
*
* @return IntegerNode The node
*/
protected function instantiateNode()
{
return new IntegerNode($this->name, $this->parent, $this->min, $this->max);
}
}
26 changes: 26 additions & 0 deletions src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public function __construct()
'variable' => __NAMESPACE__.'\\VariableNodeDefinition',
'scalar' => __NAMESPACE__.'\\ScalarNodeDefinition',
'boolean' => __NAMESPACE__.'\\BooleanNodeDefinition',
'integer' => __NAMESPACE__.'\\IntegerNodeDefinition',
'float' => __NAMESPACE__.'\\FloatNodeDefinition',
'array' => __NAMESPACE__.'\\ArrayNodeDefinition',
'enum' => __NAMESPACE__.'\\EnumNodeDefinition',
);
Expand Down Expand Up @@ -86,6 +88,30 @@ public function booleanNode($name)
return $this->node($name, 'boolean');
}

/**
* Creates a child integer node.
*
* @param string $name the name of the node
*
* @return IntegerNodeDefinition The child node
*/
public function integerNode($name)
{
return $this->node($name, 'integer');
}

/**
* Creates a child float node.
*
* @param string $name the name of the node
*
* @return FloatNodeDefinition The child node
*/
public function floatNode($name)
{
return $this->node($name, 'float');
}

/**
* Creates a child EnumNode.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Config\Definition\Builder;

/**
* Abstract class that contain common code of integer and float node definition.
*
* @author David Jeanmonod <david.jeanmonod@gmail.com>
*/
abstract class NumericNodeDefinition extends ScalarNodeDefinition
{

protected $min;
protected $max;

/**
* Ensure the value is smaller than the given reference
*
* @param mixed $max
*
* @return NumericNodeDefinition
*/
public function max($max)
{
if (isset($this->min) && $this->min > $max) {
throw new \InvalidArgumentException(sprintf('You cannot define a max(%s) as you already have a min(%s)', $max, $this->min));
}
$this->max = $max;

return $this;
}

/**
* Ensure the value is bigger than the given reference
*
* @param mixed $min
*
* @return NumericNodeDefinition
*/
public function min($min)
{
if (isset($this->max) && $this->max < $min) {
throw new \InvalidArgumentException(sprintf('You cannot define a min(%s) as you already have a max(%s)', $min, $this->max));
}
$this->min = $min;

return $this;
}
}
44 changes: 44 additions & 0 deletions src/Symfony/Component/Config/Definition/FloatNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Config\Definition;

use Symfony\Component\Config\Definition\Exception\InvalidTypeException;

/**
* This node represents a float value in the config tree.
*
* @author Jeanmonod David <david.jeanmonod@gmail.com>
*/
class FloatNode extends NumericNode
{
/**
* {@inheritDoc}
*/
protected function validateType($value)
{
// Integers are also accepted, we just cast them
if (is_int($value)) {
$value = (float) $value;
}

if (!is_float($value)) {
$ex = new InvalidTypeException(sprintf(
'Invalid type for path "%s". Expected float, but got %s.',
$this->getPath(),
gettype($value)
));
$ex->setPath($this->getPath());

throw $ex;
}
}
}
39 changes: 39 additions & 0 deletions src/Symfony/Component/Config/Definition/IntegerNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Config\Definition;

use Symfony\Component\Config\Definition\Exception\InvalidTypeException;

/**
* This node represents an integer value in the config tree.
*
* @author Jeanmonod David <david.jeanmonod@gmail.com>
*/
class IntegerNode extends NumericNode
{
/**
* {@inheritDoc}
*/
protected function validateType($value)
{
if (!is_int($value)) {
$ex = new InvalidTypeException(sprintf(
'Invalid type for path "%s". Expected int, but got %s.',
$this->getPath(),
gettype($value)
));
$ex->setPath($this->getPath());

throw $ex;
}
}
}
56 changes: 56 additions & 0 deletions src/Symfony/Component/Config/Definition/NumericNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Config\Definition;

use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;

/**
* This node represents a numeric value in the config tree
*
* @author David Jeanmonod <david.jeanmonod@gmail.com>
*/
class NumericNode extends ScalarNode
{
protected $min;
protected $max;

public function __construct($name, NodeInterface $parent = null, $min = null, $max = null)
{
parent::__construct($name, $parent);
$this->min = $min;
$this->max = $max;
}

/**
* {@inheritDoc}
*/
protected function finalizeValue($value)
{
$value = parent::finalizeValue($value);

$errorMsg = null;
if (isset($this->min) && $value < $this->min) {
$errorMsg = sprintf('The value %s is too small for path "%s". Should be greater than: %s', $value, $this->getPath(), $this->min);
}
if (isset($this->max) && $value > $this->max) {
$errorMsg = sprintf('The value %s is too big for path "%s". Should be less than: %s', $value, $this->getPath(), $this->max);
}
if (isset($errorMsg)) {
$ex = new InvalidConfigurationException($errorMsg);
$ex->setPath($this->getPath());
throw $ex;
}

return $value;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

use Symfony\Component\Config\Definition\Builder\TreeBuilder;


class ExprBuilderTest extends \PHPUnit_Framework_TestCase
{

Expand Down Expand Up @@ -160,6 +159,7 @@ public function testThenUnsetExpression()
protected function getTestBuilder()
{
$builder = new TreeBuilder();

return $builder
->root('test')
->children()
Expand All @@ -171,7 +171,7 @@ protected function getTestBuilder()
/**
* Close the validation process and finalize with the given config
* @param TreeBuilder $testBuilder The tree builder to finalize
* @param array $config The config you want to use for the finalization, if nothing provided
* @param array $config The config you want to use for the finalization, if nothing provided
* a simple array('key'=>'value') will be used
* @return array The finalized config values
*/
Expand All @@ -191,17 +191,18 @@ protected function finalizeTestBuilder($testBuilder, $config=null)
* @param $val The value that the closure must return
* @return Closure
*/
protected function returnClosure($val) {
protected function returnClosure($val)
{
return function($v) use ($val) {
return $val;
};
}

/**
* Assert that the given test builder, will return the given value
* @param mixed $value The value to test
* @param TreeBuilder $test The tree builder to finalize
* @param mixed $config The config values that new to be finalized
* @param mixed $value The value to test
* @param TreeBuilder $test The tree builder to finalize
* @param mixed $config The config values that new to be finalized
*/
protected function assertFinalizedValueIs($value, $treeBuilder, $config=null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,17 @@ public function testNodeTypesAreNotCaseSensitive()

$this->assertEquals(get_class($node1), get_class($node2));
}

public function testNumericNodeCreation()
{
$builder = new NodeBuilder();

$node = $builder->integerNode('foo')->min(3)->max(5);
$this->assertEquals('Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition', get_class($node));

$node = $builder->floatNode('bar')->min(3.0)->max(5.0);
$this->assertEquals('Symfony\Component\Config\Definition\Builder\FloatNodeDefinition', get_class($node));
}
}

class SomeNodeDefinition extends BaseVariableNodeDefinition
Expand Down
Loading

0 comments on commit 71db836

Please sign in to comment.