Permalink
Browse files

Added nullable annotation to allow a null parameter in strict mode

  • Loading branch information...
1 parent c2ba471 commit c604cbbf2067add53e67155eef75fcfdd2ca3745 @borisguery borisguery committed with borisguery Oct 1, 2012
View
3 Controller/Annotations/Param.php
@@ -15,6 +15,7 @@
* Represents a parameter that can be present in GET or POST data.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
+ * @author Boris Guéry <guery.b@gmail.com>
*/
abstract class Param
{
@@ -30,4 +31,6 @@
public $strict = false;
/** @var boolean */
public $array = false;
+ /** @var boolean */
+ public $nullable = false;
}
View
1 Controller/Annotations/RequestParam.php
@@ -16,6 +16,7 @@
*
* @Annotation
* @author Jordi Boggiano <j.boggiano@seld.be>
+ * @author Boris Guéry <guery.b@gmail.com>
*/
class RequestParam extends Param
{
View
14 Request/ParamFetcher.php
@@ -24,6 +24,7 @@
* @author Alexander <iam.asm89@gmail.com>
* @author Lukas Kahwe Smith <smith@pooteeweet.org>
* @author Jordi Boggiano <j.boggiano@seld.be>
+ * @author Boris Guéry <guery.b@gmail.com>
*/
class ParamFetcher implements ParamFetcherInterface
{
@@ -80,8 +81,9 @@ public function get($name, $strict = null)
throw new \InvalidArgumentException(sprintf("No @QueryParam/@RequestParam configuration for parameter '%s'.", $name));
}
- $config = $this->params[$name];
- $default = $config->default;
+ $config = $this->params[$name];
+ $nullable = $config->nullable;
+ $default = $config->default;
if ($config->array) {
$default = (array) $default;
@@ -125,8 +127,12 @@ public function get($name, $strict = null)
}
if (!is_scalar($param)) {
- if ($strict) {
- throw new \RuntimeException(sprintf("Query parameter value '%s' is not a scalar", $param));
+ if (!$nullable) {
+ if ($strict) {
+ throw new \RuntimeException(sprintf("Query parameter value '%s' is not a scalar", $param));
+ }
+
+ return $this->cleanParamWithRequirements($config, $param, $strict);
}
return $default;
View
1 Request/ParamReader.php
@@ -19,6 +19,7 @@
*
* @author Alexander <iam.asm89@gmail.com>
* @author Lukas Kahwe Smith <smith@pooteeweet.org>
+ * @author Boris Guéry <guery.b@gmail.com>
*/
class ParamReader implements ParamReaderInterface
{
View
8 Resources/doc/3-listener-support.md
@@ -256,6 +256,14 @@ class FooController extends Controller
*
* @QueryParam(name="page", requirements="\d+", default="1", description="Page of the overview.")
*
+ * In some case you also want to have a strict requirements but accept a null value, this is possible
+ * thanks to the nullable option.
+ * If ?count= parameter is set, the requirements will be checked strictly, if not, the null value will be used.
+ * If you set the strict parameter without a nullable option, this will result in an error if the parameter is
+ * missing from the query.
+ *
+ * @QueryParam(name="count", requirements="\d+", strict=true, nullable=true, description="Item count limit")
+ *
* Will look for a firstname request parameters, ie. firstname=foo in POST data.
* If not passed it will error out when read out of the ParamFetcher since RequestParam defaults to strict=true
* If passed but doesn't match the requirement "\d+" it will also error out (400 Bad Request)
View
67 Tests/Request/ParamFetcherTest.php
@@ -22,6 +22,7 @@
* QueryParamReader test.
*
* @author Alexander <iam.asm89@gmail.com>
+ * @author Boris Guéry <guery.b@gmail.com>
*/
class ParamFetcherTest extends \PHPUnit_Framework_TestCase
{
@@ -45,6 +46,7 @@ public function setup()
$annotations['foo']->requirements = '\d+';
$annotations['foo']->default = '1';
$annotations['foo']->description = 'The foo';
+ $annotations['foo']->nullable = false;
$annotations['bar'] = new RequestParam;
$annotations['bar']->name = 'bar';
@@ -60,13 +62,26 @@ public function setup()
$annotations['buzz']->name = 'buzz';
$annotations['buzz']->requirements = '\d+';
$annotations['buzz']->default = '1';
+ $annotations['buzz']->nullable = false;
$annotations['buzz']->description = 'An array';
$annotations['boo'] = new QueryParam;
$annotations['boo']->array = true;
$annotations['boo']->name = 'boo';
$annotations['boo']->description = 'An array with no default value';
+ $annotations['boozz'] = new QueryParam;
+ $annotations['boozz']->name = 'boozz';
+ $annotations['boozz']->requirements = '\d+';
+ $annotations['boozz']->description = 'A scalar param with no default value (an optional limit param for example)';
+
+ $annotations['biz'] = new QueryParam;
+ $annotations['biz']->name = 'biz';
+ $annotations['biz']->requirements = '\d+';
+ $annotations['biz']->default = null;
+ $annotations['biz']->nullable = true;
+ $annotations['biz']->description = 'A scalar param with an explicitly defined null default';
+
$this->paramReader
->expects($this->any())
->method('read')
@@ -121,77 +136,109 @@ public static function validatesConfiguredParamDataProvider()
array( // check that non-strict missing params take default value
'foo',
'1',
- array('foo' => '1', 'bar' => '2', 'baz' => '4', 'buzz' => array(1), 'boo' => array()),
+ array('foo' => '1', 'bar' => '2', 'baz' => '4', 'buzz' => array(1), 'boo' => array(), 'boozz' => null, 'biz' => null),
array(),
array('bar' => '2', 'baz' => '4'),
),
array( // pass Param in GET
'foo',
'42',
- array('foo' => '42', 'bar' => '2', 'baz' => '4', 'buzz' => array(1), 'boo' => array()),
+ array('foo' => '42', 'bar' => '2', 'baz' => '4', 'buzz' => array(1), 'boo' => array(), 'boozz' => null, 'biz' => null),
array('foo' => '42'),
array('bar' => '2', 'baz' => '4'),
),
array( // check that invalid non-strict params take default value
'foo',
'1',
- array('foo' => '1', 'bar' => '1', 'baz' => '1', 'baz' => '4', 'buzz' => array(1), 'boo' => array()),
+ array('foo' => '1', 'bar' => '1', 'baz' => '1', 'baz' => '4', 'buzz' => array(1), 'boo' => array(), 'boozz' => null, 'biz' => null),
array('foo' => 'bar'),
array('bar' => '1', 'baz' => '4'),
),
array( // invalid array
'buzz',
array(1),
- array('foo' => '1', 'bar' => '1', 'baz' => '1', 'baz' => '4', 'buzz' => array(1), 'boo' => array()),
+ array('foo' => '1', 'bar' => '1', 'baz' => '1', 'baz' => '4', 'buzz' => array(1), 'boo' => array(), 'boozz' => null, 'biz' => null),
array('buzz' => 'invaliddata'),
array('bar' => '1', 'baz' => '4'),
),
array( // invalid array (multiple depth)
'buzz',
array(1),
- array('foo' => '1', 'bar' => '1', 'baz' => '1', 'baz' => '4', 'buzz' => array(1), 'boo' => array()),
+ array('foo' => '1', 'bar' => '1', 'baz' => '1', 'baz' => '4', 'buzz' => array(1), 'boo' => array(), 'boozz' => null, 'biz' => null),
array('buzz' => array(array(1))),
array('bar' => '1', 'baz' => '4'),
),
array( // multiple array
'buzz',
array(2, 3, 4),
- array('foo' => '1', 'bar' => '1', 'baz' => '1', 'baz' => '4', 'buzz' => array(2, 3, 4), 'boo' => array()),
+ array('foo' => '1', 'bar' => '1', 'baz' => '1', 'baz' => '4', 'buzz' => array(2, 3, 4), 'boo' => array(), 'boozz' => null, 'biz' => null),
array('buzz' => array(2, 3, 4)),
array('bar' => '1', 'baz' => '4'),
),
array( // multiple array with one invalid value
'buzz',
array(2, 1, 4),
- array('foo' => '1', 'bar' => '1', 'baz' => '1', 'baz' => '4', 'buzz' => array(2, 1, 4), 'boo' => array()),
+ array('foo' => '1', 'bar' => '1', 'baz' => '1', 'baz' => '4', 'buzz' => array(2, 1, 4), 'boo' => array(), 'boozz' => null, 'biz' => null),
array('buzz' => array(2, 'invaliddata', 4)),
array('bar' => '1', 'baz' => '4'),
),
array( // Array not provided in GET query
'boo',
array(),
- array('foo' => '1', 'bar' => '1', 'baz' => '1', 'baz' => '4', 'buzz' => array(2, 3, 4), 'boo' => array()),
+ array('foo' => '1', 'bar' => '1', 'baz' => '1', 'baz' => '4', 'buzz' => array(2, 3, 4), 'boo' => array(), 'boozz' => null, 'biz' => null),
array('buzz' => array(2, 3, 4)),
array('bar' => '1', 'baz' => '4'),
),
array( // QueryParam provided in GET query but as a scalar
'boo',
array(),
- array('foo' => '1', 'bar' => '1', 'baz' => '1', 'baz' => '4', 'buzz' => array(2, 3, 4), 'boo' => array()),
+ array('foo' => '1', 'bar' => '1', 'baz' => '1', 'baz' => '4', 'buzz' => array(2, 3, 4), 'boo' => array(), 'boozz' => null, 'biz' => null),
array('buzz' => array(2, 3, 4), 'boo' => 'scalar'),
array('bar' => '1', 'baz' => '4'),
),
array( // QueryParam provided in GET query with valid values
'boo',
array('1', 'foo', 5),
- array('foo' => '1', 'bar' => '1', 'baz' => '1', 'baz' => '4', 'buzz' => array(2, 3, 4), 'boo' => array('1', 'foo', 5)),
+ array('foo' => '1', 'bar' => '1', 'baz' => '1', 'baz' => '4', 'buzz' => array(2, 3, 4), 'boo' => array('1', 'foo', 5), 'boozz' => null, 'biz' => null),
array('buzz' => array(2, 3, 4), 'boo' => array('1', 'foo', 5)),
array('bar' => '1', 'baz' => '4'),
+ ),
+ array( // QueryParam provided in GET query with valid values
+ 'boozz',
+ null,
+ array('foo' => '1', 'bar' => '1', 'baz' => '1', 'baz' => '4', 'buzz' => array(2, 3, 4), 'boo' => array('1', 'foo', 5), 'boozz' => null, 'biz' => null),
+ array('buzz' => array(2, 3, 4), 'boo' => array('1', 'foo', 5)),
+ array('bar' => '1', 'baz' => '4'),
+ ),
+ array( // QueryParam provided in GET query with valid values
+ 'boozz',
+ 5,
+ array('foo' => '1', 'bar' => '1', 'baz' => '1', 'baz' => '4', 'buzz' => array(2, 3, 4), 'boo' => array('1', 'foo', 5), 'boozz' => 5, 'biz' => null),
+ array('buzz' => array(2, 3, 4), 'boo' => array('1', 'foo', 5), 'boozz' => 5),
+ array('bar' => '1', 'baz' => '4', 'boozz' => 5),
)
);
}
+ public function testValidatesConfiguredParamStrictly()
+ {
+ $queryFetcher = $this->getParamFetcher(array('boozz' => 354), array());
+ $queryFetcher->setController($this->controller);
+ $this->assertEquals(354, $queryFetcher->get('boozz', true));
+
+ $queryFetcher = $this->getParamFetcher(array(), array());
+ $queryFetcher->setController($this->controller);
+ try {
+ $queryFetcher->get('boozz', true);
+ $this->fail('Fetching get() in strict mode with no default value did not throw an exception');
+ } catch (\RuntimeException $e) {}
+
+ $queryFetcher = $this->getParamFetcher(array(), array());
+ $queryFetcher->setController($this->controller);
+ $this->assertNull($queryFetcher->get('biz', true));
+ }
+
/**
* Throw exception on invalid parameters.
* @dataProvider exceptionOnValidatesFailureDataProvider

0 comments on commit c604cbb

Please sign in to comment.