8000 [Form] Moved POST_MAX_SIZE validation from FormValidator to request handler by webmozart · Pull Request #11924 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Form] Moved POST_MAX_SIZE validation from FormValidator to request handler #11924

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 24, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,11 @@
</service>

<!-- FormTypeHttpFoundationExtension -->
<service id="form.server_params" class="Symfony\Component\Form\Util\ServerParams" public="false"/>

<service id="form.type_extension.form.http_foundation" class="Symfony\Component\Form\Extension\HttpFoundation\Type\FormTypeHttpFoundationExtension">
<tag name="form.type_extension" alias="form" />
<argument type="service" id="form.server_params"/>
</service>

<!-- FormTypeValidatorExtension -->
Expand Down
38 changes: 19 additions & 19 deletions src/Symfony/Component/Form/Extension/Core/Type/FormType.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
->setDataLocked($isDataOptionSet)
->setDataMapper($options['compound'] ? new PropertyPathMapper($this->propertyAccessor) : null)
->setMethod($options['method'])
->setAction($options['action'])
;
->setAction($options['action']);

if ($options['trim']) {
$builder->addEventSubscriber(new TrimListener());
Expand Down Expand Up @@ -170,25 +169,26 @@ public function setDefaultOptions(OptionsResolverInterface $resolver)
));

$resolver->setDefaults(array(
'data_class' => $dataClass,
'empty_data' => $emptyData,
'trim' => true,
'required' => true,
'read_only' => false,
'max_length' => null,
'pattern' => null,
'property_path' => null,
'mapped' => true,
'by_reference' => true,
'error_bubbling' => $errorBubbling,
'label_attr' => array(),
'virtual' => null,
'inherit_data' => $inheritData,
'compound' => true,
'method' => 'POST',
'data_class' => $dataClass,
'empty_data' => $emptyData,
'trim' => true,
'required' => true,
'read_only' => false,
'max_length' => null,
'pattern' => null,
'property_path' => null,
'mapped' => true,
'by_reference' => true,
'error_bubbling' => $errorBubbling,
'label_attr' => array(),
'virtual' => null,
'inherit_data' => $inheritData,
'compound' => true,
'method' => 'POST',
// According to RFC 2396 (http://www.ietf.org/rfc/rfc2396.txt)
// section 4.2., empty URIs are considered same-document references
'action' => '',
'action' => '',
'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.',
));

$resolver->setAllowedTypes(array(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Extension\HttpFoundation;

use Symfony\Component\Form\AbstractExtension;
use Symfony\Component\Form\Util\ServerParams;

/**
* Integrates the HttpFoundation component with the Form library.
Expand All @@ -20,10 +21,20 @@
*/
class HttpFoundationExtension extends AbstractExtension
{
/**
* @var ServerParams
*/
private $serverParams;

public function __construct(ServerParams $serverParams = null)
{
$this->serverParams = $serverParams;
}

protected function loadTypeExtensions()
{
return array(
new Type\FormTypeHttpFoundationExtension(),
new Type\FormTypeHttpFoundationExtension($this->serverParams),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
namespace Symfony\Component\Form\Extension\HttpFoundation;

use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\RequestHandlerInterface;
use Symfony\Component\Form\Util\ServerParams;
use Symfony\Component\HttpFoundation\Request;

/**
Expand All @@ -24,6 +26,19 @@
*/
class HttpFoundationRequestHandler implements RequestHandlerInterface
{
/**
* @var ServerParams
*/
private $serverParams;

/**
* {@inheritdoc}
*/
public function __construct(ServerParams $serverParams = null)
{
$this->serverParams = $serverParams ?: new ServerParams();
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -53,6 +68,25 @@ public function handleRequest(FormInterface $form, $request = null)
$data = $request->query->get($name);
}
} else {
// Mark the form with an error if the uploaded size was too large
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code block is duplicated. So I guess one could refactor this. E.g. HttpFoundationRequestHandler extends NativeRequestHandler and then overwrites certains methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there are a lot of similarities between the two request handlers. On the other hand, there are lots of tiny differences, so we'd end up with a pretty complex class + control flow if we remove all the duplications. Both classes are covered by the same test case, so if tests are added, both classes need to be adapted.

Feel free to try to remove the duplication in a new PR, there we can discuss how to do that well and whether it is really worth it.

// This is done here and not in FormValidator because $_POST is
// empty when that error occurs. Hence the form is never submitted.
$contentLength = $this->serverParams->getContentLength();
$maxContentLength = $this->serverParams->getPostMaxSize();

if (!empty($maxContentLength) && $contentLength > $maxContentLength) {
// Submit the form, but don't clear the default values
$form->submit(null, false);

$form->addError(new FormError(
$form->getConfig()->getOption('post_max_size_message'),
null,
array('{{ max }}' => $this->serverParams->getNormalizedIniPostMaxSize())
));

return;
}

if ('' === $name) {
$params = $request->request->all();
$files = $request->files->all();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Symfony\Component\Form\Extension\HttpFoundation\EventListener\BindRequestListener;
use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Util\ServerParams;

/**
* @author Bernhard Schussek <bschussek@gmail.com>
Expand All @@ -31,10 +32,10 @@ class FormTypeHttpFoundationExtension extends AbstractTypeExtension
*/
private $requestHandler;

public function __construct()
public function __construct(ServerParams $serverParams = null)
{
$this->listener = new BindRequestListener();
$this->requestHandler = new HttpFoundationRequestHandler();
$this->requestHandler = new HttpFoundationRequestHandler($serverParams);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
namespace Symfony\Component\Form\Extension\Validator\Constraints;

use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Extension\Validator\Util\ServerParams;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

Expand All @@ -21,22 +20,6 @@
*/
class FormValidator extends ConstraintValidator
{
/**
* @var ServerParams
*/
private $serverParams;

/**
* Creates a validator with the given server parameters.
*
* @param ServerParams $params The server parameters. Default
* parameters are created if null.
*/
public function __construct(ServerParams $params = null)
{
$this->serverParams = $params ?: new ServerParams();
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -113,21 +96,6 @@ public function validate($form, Constraint $constraint)
$form->getExtraData()
);
}

// Mark the form with an error if the uploaded size was too large
$length = $this->serverParams->getContentLength();

if ($form->isRoot() && null !== $length) {
$max = $this->serverParams->getPostMaxSize();

if (!empty($max) && $length > $max) {
$this->context->addViolation(
$config->getOption('post_max_size_message'),
array('{{ max }}' => $this->serverParams->getNormalizedIniPostMaxSize()),
$length
);
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ public function setDefaultOptions(OptionsResolverInterface $resolver)
'invalid_message' => 'This value is not valid.',
'invalid_message_parameters' => array(),
'extra_fields_message' => 'This form should not contain extra fields.',
'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.',
));

$resolver->setNormalizers(array(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,59 +14,6 @@
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class ServerParams
class ServerParams extends \Symfony\Component\Form\Util\ServerParams
{
/**
* Returns maximum post size in bytes.
*
* @return null|int The maximum post size in bytes
*/
public function getPostMaxSize()
{
$iniMax = strtolower($this->getNormalizedIniPostMaxSize());

if ('' === $iniMax) {
return;
}

$max = ltrim($iniMax, '+');
if (0 === strpos($max, '0x')) {
$max = intval($max, 16);
} elseif (0 === strpos($max, '0')) {
$max = intval($max, 8);
} else {
$max = intval($max);
}

switch (substr($iniMax, -1)) {
case 't': $max *= 1024;
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')));
}

/**
* Returns the content length of the request.
*
* @return mixed The request content length.
*/
public function getContentLength()
{
return isset($_SERVER['CONTENT_LENGTH'])
? (int) $_SERVER['CONTENT_LENGTH']
: null;
}
}
33 changes: 33 additions & 0 deletions src/Symfony/Component/Form/NativeRequestHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Form;

use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\Util\ServerParams;

/**
* A request handler using PHP's super globals $_GET, $_POST and $_SERVER.
Expand All @@ -20,6 +21,19 @@
*/
class NativeRequestHandler implements RequestHandlerInterface
{
/**
* @var ServerParams
*/
private $serverParams;

/**
* {@inheritdoc}
*/
public function __construct(ServerParams $params = null)
{
$this->serverParams = $params ?: new ServerParams();
}

/**
* The allowed keys of the $_FILES array.
*
Expand Down Expand Up @@ -62,6 +76,25 @@ public function handleRequest(FormInterface $form, $request = null)
$data = $_GET[$name];
}
} else {
// Mark the form with an error if the uploaded size was too large
// This is done here and not in FormValidator because $_POST is
// empty when that error occurs. Hence the form is never submitted.
$contentLength = $this->serverParams->getContentLength();
$maxContentLength = $this->serverParams->getPostMaxSize();

if (!empty($maxContentLength) && $contentLength > $maxContentLength) {
// Submit the form, but don't clear the default values
$form->submit(null, false);

$form->addError(new FormError(
$form->getConfig()->getOption('post_max_size_message'),
null,
array('{{ max }}' => $this->serverParams->getNormalizedIniPostMaxSize())
));

return;
}

$fixedFiles = array();
foreach ($_FILES as $name => $file) {
$fixedFiles[$name] = self::stripEmptyFiles(self::fixPhpFilesArray($file));
Expand Down
Loading
0