[Form] Some code cleanup

This commit is contained in:
Victor Berchet 2012-05-25 09:17:31 +02:00 committed by Bernhard Schussek
parent 94f6f7776a
commit bc15e2d68b
7 changed files with 199 additions and 370 deletions

View File

@ -137,11 +137,7 @@ class FormType extends AbstractType
{
// Derive "data_class" option from passed "data" object
$dataClass = function (Options $options) {
if (is_object($options['data'])) {
return get_class($options['data']);
}
return null;
return is_object($options['data']) ? get_class($options['data']) : null;
};
// Derive "empty_data" closure from "data_class" option
@ -150,20 +146,12 @@ class FormType extends AbstractType
if (null !== $class) {
return function (FormInterface $form) use ($class) {
if ($form->isEmpty() && !$form->isRequired()) {
return null;
}
return new $class();
return $form->isEmpty() && !$form->isRequired() ? null : new $class();
};
}
return function (FormInterface $form) {
if (count($form) > 0) {
return array();
}
return '';
return count($form) > 0 ? array() : '';
};
};

View File

@ -34,11 +34,7 @@ class FormValidator extends ConstraintValidator
*/
public function __construct(ServerParams $params = null)
{
if (null === $params) {
$params = new ServerParams();
}
$this->serverParams = $params;
$this->serverParams = $params ?: new ServerParams();
}
/**
@ -107,31 +103,14 @@ class FormValidator extends ConstraintValidator
$length = $this->serverParams->getContentLength();
if ($form->isRoot() && null !== $length) {
$max = strtoupper(trim($this->serverParams->getPostMaxSize()));
$max = $this->serverParams->getPostMaxSize();
if ('' !== $max) {
$maxLength = (int) $max;
switch (substr($max, -1)) {
// The 'G' modifier is available since PHP 5.1.0
case 'G':
$maxLength *= pow(1024, 3);
break;
case 'M':
$maxLength *= pow(1024, 2);
break;
case 'K':
$maxLength *= 1024;
break;
}
if ($length > $maxLength) {
$this->context->addViolation(
$config->getOption('post_max_size_message'),
array('{{ max }}' => $max),
$length
);
}
if (null !== $max && $length > $max) {
$this->context->addViolation(
$config->getOption('post_max_size_message'),
array('{{ max }}' => $this->serverParams->getNormalizedIniPostMaxSize()),
$length
);
}
}
}
@ -159,14 +138,10 @@ class FormValidator extends ConstraintValidator
// Non-root forms are validated if validation cascading
// is enabled in all ancestor forms
$parent = $form->getParent();
while (null !== $parent) {
if (!$parent->getConfig()->getOption('cascade_validation')) {
while (null !== ($form = $form->getParent())) {
if (!$form->getConfig()->getOption('cascade_validation')) {
return false;
}
$parent = $parent->getParent();
}
return true;
@ -181,22 +156,20 @@ class FormValidator extends ConstraintValidator
*/
private function getValidationGroups(FormInterface $form)
{
$groups = null;
while (null !== $form && null === $groups) {
do {
$groups = $form->getConfig()->getOption('validation_groups');
if (is_callable($groups)) {
$groups = (array) call_user_func($groups, $form);
if (null !== $groups) {
if (is_callable($groups)) {
$groups = call_user_func($groups, $form);
}
return (array) $groups;
}
$form = $form->getParent();
}
} while (null !== $form);
if (null === $groups) {
$groups = array(Constraint::DEFAULT_GROUP);
}
return (array) $groups;
return array(Constraint::DEFAULT_GROUP);
}
}

View File

@ -12,18 +12,11 @@
namespace Symfony\Component\Form\Extension\Validator\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Validator\ConstraintViolation;
use Symfony\Component\Form\Extension\Validator\Constraints\Form;
use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapperInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Validator\ValidatorInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Event\DataEvent;
use Symfony\Component\Form\Exception\FormException;
use Symfony\Component\Form\Util\PropertyPath;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ValidatorInterface;
use Symfony\Component\Validator\ExecutionContext;
use Symfony\Component\Form\Extension\Validator\Constraints\Form;
/**
* @author Bernhard Schussek <bschussek@gmail.com>

View File

@ -17,13 +17,40 @@ namespace Symfony\Component\Form\Extension\Validator\Util;
class ServerParams
{
/**
* Returns the "post_max_size" ini setting.
* Returns maximum post size in bytes.
*
* @return string The value of the ini setting.
* @return null|integer The maximum post size in bytes
*/
public function getPostMaxSize()
{
return ini_get('post_max_size');
$iniMax = $this->getNormalizedIniPostMaxSize();
if ('' === $iniMax) {
return null;
}
$max = (int) $iniMax;
switch (substr($iniMax, -1)) {
case 'G':
$max *= 1024;
case 'M':
$max *= 1024;
case 'K':
$max *= 1024;
}
return $max;
}
/**
* Returns the normalized "post_max_size" ini setting.
*
* @return string
*/
public function getNormalizedIniPostMaxSize()
{
return strtoupper(trim(ini_get('post_max_size')));
}
/**

View File

@ -55,7 +55,10 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase
$this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
$this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface');
$this->serverParams = $this->getMock('Symfony\Component\Form\Extension\Validator\Util\ServerParams');
$this->serverParams = $this->getMock(
'Symfony\Component\Form\Extension\Validator\Util\ServerParams',
array('getNormalizedIniPostMaxSize', 'getContentLength')
);
$this->validator = new FormValidator($this->serverParams);
}
@ -408,14 +411,18 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('Extra!', $context->getViolations()->get(0)->getMessage());
}
public function testViolationIfPostMaxSizeExceeded_GigaUpper()
/**
* @dataProvider getPostMaxSizeFixtures
*/
public function testPostMaxSizeViolation($contentLength, $iniMax, $nbViolation, $msg)
{
$this->serverParams->expects($this->any())
$this->serverParams->expects($this->once())
->method('getContentLength')
->will($this->returnValue(pow(1024, 3) + 1));
->will($this->returnValue($contentLength));
$this->serverParams->expects($this->any())
->method('getPostMaxSize')
->will($this->returnValue('1G'));
->method('getNormalizedIniPostMaxSize')
->will($this->returnValue($iniMax));
$context = $this->getExecutionContext();
$options = array('post_max_size_message' => 'Max {{ max }}!');
@ -424,132 +431,33 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase
$this->validator->initialize($context);
$this->validator->validate($form, new Form());
$this->assertCount(1, $context->getViolations());
$this->assertEquals('Max 1G!', $context->getViolations()->get(0)->getMessage());
$this->assertCount($nbViolation, $context->getViolations());
if (null !== $msg) {
$this->assertEquals($msg, $context->getViolations()->get(0)->getMessage());
}
}
public function testViolationIfPostMaxSizeExceeded_GigaLower()
public function getPostMaxSizeFixtures()
{
$this->serverParams->expects($this->any())
->method('getContentLength')
->will($this->returnValue(pow(1024, 3) + 1));
$this->serverParams->expects($this->any())
->method('getPostMaxSize')
->will($this->returnValue('1g'));
$context = $this->getExecutionContext();
$options = array('post_max_size_message' => 'Max {{ max }}!');
$form = $this->getBuilder('name', null, $options)->getForm();
$this->validator->initialize($context);
$this->validator->validate($form, new Form());
$this->assertCount(1, $context->getViolations());
$this->assertEquals('Max 1G!', $context->getViolations()->get(0)->getMessage());
}
public function testNoViolationIfPostMaxSizeNotExceeded_Giga()
{
$this->serverParams->expects($this->any())
->method('getContentLength')
->will($this->returnValue(pow(1024, 3)));
$this->serverParams->expects($this->any())
->method('getPostMaxSize')
->will($this->returnValue('1G'));
$context = $this->getExecutionContext();
$form = $this->getForm();
$this->validator->initialize($context);
$this->validator->validate($form, new Form());
$this->assertCount(0, $context->getViolations());
}
public function testViolationIfPostMaxSizeExceeded_Mega()
{
$this->serverParams->expects($this->any())
->method('getContentLength')
->will($this->returnValue(pow(1024, 2) + 1));
$this->serverParams->expects($this->any())
->method('getPostMaxSize')
->will($this->returnValue('1M'));
$context = $this->getExecutionContext();
$options = array('post_max_size_message' => 'Max {{ max }}!');
$form = $this->getBuilder('name', null, $options)->getForm();
$this->validator->initialize($context);
$this->validator->validate($form, new Form());
$this->assertCount(1, $context->getViolations());
$this->assertEquals('Max 1M!', $context->getViolations()->get(0)->getMessage());
}
public function testNoViolationIfPostMaxSizeNotExceeded_Mega()
{
$this->serverParams->expects($this->any())
->method('getContentLength')
->will($this->returnValue(pow(1024, 2)));
$this->serverParams->expects($this->any())
->method('getPostMaxSize')
->will($this->returnValue('1M'));
$context = $this->getExecutionContext();
$form = $this->getForm();
$this->validator->initialize($context);
$this->validator->validate($form, new Form());
$this->assertCount(0, $context->getViolations());
}
public function testViolationIfPostMaxSizeExceeded_Kilo()
{
$this->serverParams->expects($this->any())
->method('getContentLength')
->will($this->returnValue(1025));
$this->serverParams->expects($this->any())
->method('getPostMaxSize')
->will($this->returnValue('1K'));
$context = $this->getExecutionContext();
$options = array('post_max_size_message' => 'Max {{ max }}!');
$form = $this->getBuilder('name', null, $options)->getForm();
$this->validator->initialize($context);
$this->validator->validate($form, new Form());
$this->assertCount(1, $context->getViolations());
$this->assertEquals('Max 1K!', $context->getViolations()->get(0)->getMessage());
}
public function testNoViolationIfPostMaxSizeNotExceeded_Kilo()
{
$this->serverParams->expects($this->any())
->method('getContentLength')
->will($this->returnValue(1024));
$this->serverParams->expects($this->any())
->method('getPostMaxSize')
->will($this->returnValue('1K'));
$context = $this->getExecutionContext();
$form = $this->getForm();
$this->validator->initialize($context);
$this->validator->validate($form, new Form());
$this->assertCount(0, $context->getViolations());
return array(
array(pow(1024, 3) + 1, '1G', 1, 'Max 1G!'),
array(pow(1024, 3), '1G', 0, null),
array(pow(1024, 2) + 1, '1M', 1, 'Max 1M!'),
array(pow(1024, 2), '1M', 0, null),
array(1024 + 1, '1K', 1, 'Max 1K!'),
array(1024, '1K', 0, null),
array(null, '1K', 0, null),
array(1024, '', 0, null),
);
}
public function testNoViolationIfNotRoot()
{
$this->serverParams->expects($this->any())
$this->serverParams->expects($this->once())
->method('getContentLength')
->will($this->returnValue(1025));
$this->serverParams->expects($this->any())
->method('getPostMaxSize')
->will($this->returnValue('1K'));
$this->serverParams->expects($this->never())
->method('getNormalizedIniPostMaxSize');
$context = $this->getExecutionContext();
$parent = $this->getForm();
@ -562,80 +470,6 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase
$this->assertCount(0, $context->getViolations());
}
public function testNoViolationIfContentLengthNull()
{
$this->serverParams->expects($this->any())
->method('getContentLength')
->will($this->returnValue(null));
$this->serverParams->expects($this->any())
->method('getPostMaxSize')
->will($this->returnValue('1K'));
$context = $this->getExecutionContext();
$form = $this->getForm();
$this->validator->initialize($context);
$this->validator->validate($form, new Form());
$this->assertCount(0, $context->getViolations());
}
public function testTrimPostMaxSize()
{
$this->serverParams->expects($this->any())
->method('getContentLength')
->will($this->returnValue(1025));
$this->serverParams->expects($this->any())
->method('getPostMaxSize')
->will($this->returnValue(' 1K '));
$context = $this->getExecutionContext();
$options = array('post_max_size_message' => 'Max {{ max }}!');
$form = $this->getBuilder('name', null, $options)->getForm();
$this->validator->initialize($context);
$this->validator->validate($form, new Form());
$this->assertCount(1, $context->getViolations());
$this->assertEquals('Max 1K!', $context->getViolations()->get(0)->getMessage());
}
public function testNoViolationIfPostMaxSizeEmpty()
{
$this->serverParams->expects($this->any())
->method('getContentLength')
->will($this->returnValue(1025));
$this->serverParams->expects($this->any())
->method('getPostMaxSize')
->will($this->returnValue(' '));
$context = $this->getExecutionContext();
$form = $this->getForm();
$this->validator->initialize($context);
$this->validator->validate($form, new Form());
$this->assertCount(0, $context->getViolations());
}
public function testNoViolationIfPostMaxSizeNull()
{
$this->serverParams->expects($this->any())
->method('getContentLength')
->will($this->returnValue(1025));
$this->serverParams->expects($this->any())
->method('getPostMaxSize')
->will($this->returnValue(null));
$context = $this->getExecutionContext();
$form = $this->getForm();
$this->validator->initialize($context);
$this->validator->validate($form, new Form());
$this->assertCount(0, $context->getViolations());
}
/**
* Access has to be public, as this method is called via callback array
* in {@link testValidateFormDataCanHandleCallbackValidationGroups()}

View File

@ -22,7 +22,7 @@ class FormTypeValidatorExtensionTest extends TypeTestCase
$this->assertNull($form->getConfig()->getOption('validation_groups'));
}
public function testValidationGroupsCanBeSetToString()
public function testValidationGroupsTransformedToArray()
{
$form = $this->factory->create('form', null, array(
'validation_groups' => 'group',

View File

@ -353,9 +353,9 @@ class PropertyPath implements \IteratorAggregate, PropertyPathInterface
/**
* Reads the a property from an object or array.
*
* @param object|array $objectOrArray The object or array to read from.
* @param string $property The property to read.
* @param integer $isIndex Whether to interpret the property as index.
* @param object|array $objectOrArray The object or array to read from.
* @param string $property The property to read.
* @param Boolean $isIndex Whether to interpret the property as index.
*
* @return mixed The value of the read property
*
@ -425,11 +425,11 @@ class PropertyPath implements \IteratorAggregate, PropertyPathInterface
/**
* Sets the value of the property at the given index in the path
*
* @param object|array $objectOrArray The object or array to write to.
* @param string $property The property to write.
* @param string $singular The singular form of the property name or null.
* @param integer $isIndex Whether to interpret the property as index.
* @param mixed $value The value to write.
* @param object|array $objectOrArray The object or array to write to.
* @param string $property The property to write.
* @param string $singular The singular form of the property name or null.
* @param Boolean $isIndex Whether to interpret the property as index.
* @param mixed $value The value to write.
*
* @throws InvalidPropertyException If the property does not exist.
* @throws PropertyAccessDeniedException If the property cannot be accessed due to
@ -445,107 +445,46 @@ class PropertyPath implements \IteratorAggregate, PropertyPathInterface
$objectOrArray[$property] = $value;
} elseif (is_object($objectOrArray)) {
$reflClass = new ReflectionClass($objectOrArray);
$setter = 'set'.$this->camelize($property);
$addMethod = null;
$removeMethod = null;
$plural = null;
// Check if the parent has matching methods to add/remove items
if (is_array($value) || $value instanceof Traversable) {
if (null !== $singular) {
$addMethod = 'add' . ucfirst($singular);
$removeMethod = 'remove' . ucfirst($singular);
$methods = $this->findAdderAndRemover($reflClass, $singular);
if (null !== $methods) {
// At this point the add and remove methods have been found
$itemsToAdd = is_object($value) ? clone $value : $value;
$itemToRemove = array();
$previousValue = $this->readProperty($objectOrArray, $property, $isIndex);
if (!$this->isAccessible($reflClass, $addMethod, 1)) {
throw new InvalidPropertyException(sprintf(
'The public method "%s" with exactly one required parameter was not found on class %s',
$addMethod,
$reflClass->getName()
));
}
if (is_array($previousValue) || $previousValue instanceof Traversable) {
foreach ($previousValue as $previousItem) {
foreach ($value as $key => $item) {
if ($item === $previousItem) {
// Item found, don't add
unset($itemsToAdd[$key]);
if (!$this->isAccessible($reflClass, $removeMethod, 1)) {
throw new InvalidPropertyException(sprintf(
'The public method "%s" with exactly one required parameter was not found on class %s',
$removeMethod,
$reflClass->getName()
));
}
} else {
// The plural form is the last element of the property path
$plural = ucfirst($this->elements[$this->length - 1]);
// Next $previousItem
continue 2;
}
}
// Any of the two methods is required, but not yet known
$singulars = (array) FormUtil::singularify($plural);
foreach ($singulars as $singular) {
$addMethodName = 'add' . $singular;
$removeMethodName = 'remove' . $singular;
if ($this->isAccessible($reflClass, $addMethodName, 1)) {
$addMethod = $addMethodName;
}
if ($this->isAccessible($reflClass, $removeMethodName, 1)) {
$removeMethod = $removeMethodName;
}
if ($addMethod && !$removeMethod) {
throw new InvalidPropertyException(sprintf(
'Found the public method "%s", but did not find a public "%s" on class %s',
$addMethodName,
$removeMethodName,
$reflClass->getName()
));
}
if ($removeMethod && !$addMethod) {
throw new InvalidPropertyException(sprintf(
'Found the public method "%s", but did not find a public "%s" on class %s',
$removeMethodName,
$addMethodName,
$reflClass->getName()
));
}
if ($addMethod && $removeMethod) {
break;
// Item not found, add to remove list
$itemToRemove[] = $previousItem;
}
}
foreach ($itemToRemove as $item) {
call_user_func(array($objectOrArray, $methods[1]), $item);
}
foreach ($itemsToAdd as $item) {
call_user_func(array($objectOrArray, $methods[0]), $item);
}
return;
}
}
// Collection with matching adder/remover in $objectOrArray
if ($addMethod && $removeMethod) {
$itemsToAdd = is_object($value) ? clone $value : $value;
$itemToRemove = array();
$previousValue = $this->readProperty($objectOrArray, $property, $isIndex);
if (is_array($previousValue) || $previousValue instanceof Traversable) {
foreach ($previousValue as $previousItem) {
foreach ($value as $key => $item) {
if ($item === $previousItem) {
// Item found, don't add
unset($itemsToAdd[$key]);
// Next $previousItem
continue 2;
}
}
// Item not found, add to remove list
$itemToRemove[] = $previousItem;
}
}
foreach ($itemToRemove as $item) {
$objectOrArray->$removeMethod($item);
}
foreach ($itemsToAdd as $item) {
$objectOrArray->$addMethod($item);
}
} elseif ($reflClass->hasMethod($setter)) {
$setter = 'set'.$this->camelize($property);
if ($reflClass->hasMethod($setter)) {
if (!$reflClass->getMethod($setter)->isPublic()) {
throw new PropertyAccessDeniedException(sprintf('Method "%s()" is not public in class "%s"', $setter, $reflClass->getName()));
}
@ -583,6 +522,81 @@ class PropertyPath implements \IteratorAggregate, PropertyPathInterface
return preg_replace_callback('/(^|_|\.)+(.)/', function ($match) { return ('.' === $match[1] ? '_' : '').strtoupper($match[2]); }, $string);
}
/**
* Searches for add and remove methods.
*
* @param \ReflectionClass $reflClass The reflection class for the given object
* @param string $singular The singular form of the property name or null.
*
* @return array|null An array containin the adder and remover when found, null otherwise.
*
* @throws InvalidPropertyException If the property does not exist.
*/
private function findAdderAndRemover(\ReflectionClass $reflClass, $singular)
{
if (null !== $singular) {
$addMethod = 'add' . ucfirst($singular);
$removeMethod = 'remove' . ucfirst($singular);
if (!$this->isAccessible($reflClass, $addMethod, 1)) {
throw new InvalidPropertyException(sprintf(
'The public method "%s" with exactly one required parameter was not found on class %s',
$addMethod,
$reflClass->getName()
));
}
if (!$this->isAccessible($reflClass, $removeMethod, 1)) {
throw new InvalidPropertyException(sprintf(
'The public method "%s" with exactly one required parameter was not found on class %s',
$removeMethod,
$reflClass->getName()
));
}
return array($addMethod, $removeMethod);
} else {
// The plural form is the last element of the property path
$plural = ucfirst($this->elements[$this->length - 1]);
// Any of the two methods is required, but not yet known
$singulars = (array) FormUtil::singularify($plural);
foreach ($singulars as $singular) {
$methodsFound = 0;
$addMethodFound = false;
$addMethodName = 'add' . $singular;
$removeMethodName = 'remove' . $singular;
if ($this->isAccessible($reflClass, $addMethodName, 1)) {
$addMethod = $addMethodName;
$addMethodFound = true;
$methodsFound++;
}
if ($this->isAccessible($reflClass, $removeMethodName, 1)) {
$removeMethod = $removeMethodName;
$methodsFound++;
}
if (2 == $methodsFound) {
return array($addMethod, $removeMethod);
}
if (1 == $methodsFound) {
throw new InvalidPropertyException(sprintf(
'Found the public method "%s", but did not find a public "%s" on class %s',
$addMethodFound ? $addMethodName : $removeMethodName,
$addMethodFound ? $removeMethodName : $addMethodName,
$reflClass->getName()
));
}
}
}
return null;
}
/**
* Returns whether a method is public and has a specific number of required parameters.
*