2010-06-24 09:40:05 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/*
|
2011-01-15 13:29:43 +00:00
|
|
|
* This file is part of the Symfony package.
|
2010-10-02 11:38:11 +01:00
|
|
|
*
|
2010-06-24 09:40:05 +01:00
|
|
|
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
|
|
|
*
|
2011-01-15 13:29:43 +00:00
|
|
|
* For the full copyright and license information, please view the LICENSE
|
|
|
|
* file that was distributed with this source code.
|
2010-06-24 09:40:05 +01:00
|
|
|
*/
|
|
|
|
|
2011-01-15 13:29:43 +00:00
|
|
|
namespace Symfony\Component\Form;
|
|
|
|
|
2011-01-28 22:09:22 +00:00
|
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
|
|
use Symfony\Component\HttpFoundation\FileBag;
|
2010-10-02 11:38:11 +01:00
|
|
|
use Symfony\Component\Validator\ValidatorInterface;
|
2010-12-15 14:36:47 +00:00
|
|
|
use Symfony\Component\Form\Exception\FormException;
|
2011-01-25 08:56:37 +00:00
|
|
|
use Symfony\Component\Form\CsrfProvider\CsrfProviderInterface;
|
2010-10-02 11:38:11 +01:00
|
|
|
|
2010-06-24 09:40:05 +01:00
|
|
|
/**
|
|
|
|
* Form represents a form.
|
|
|
|
*
|
|
|
|
* A form is composed of a validator schema and a widget form schema.
|
|
|
|
*
|
2011-01-28 22:09:22 +00:00
|
|
|
* Form also takes care of CSRF protection by default.
|
2010-06-24 09:40:05 +01:00
|
|
|
*
|
2011-01-28 22:09:22 +00:00
|
|
|
* A CSRF secret can be any random string. If set to false, it disables the
|
|
|
|
* CSRF protection, and if set to null, it forces the form to use the global
|
|
|
|
* CSRF secret. If the global CSRF secret is also null, then a random one
|
2010-06-24 09:40:05 +01:00
|
|
|
* is generated on the fly.
|
|
|
|
*
|
2010-10-17 12:45:15 +01:00
|
|
|
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
2011-01-19 12:43:24 +00:00
|
|
|
* @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
|
2010-06-24 09:40:05 +01:00
|
|
|
*/
|
|
|
|
class Form extends FieldGroup
|
|
|
|
{
|
2011-01-25 08:56:37 +00:00
|
|
|
/**
|
|
|
|
* The validator to validate form values
|
|
|
|
* @var ValidatorInterface
|
|
|
|
*/
|
2010-06-24 10:24:08 +01:00
|
|
|
protected $validator = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor.
|
|
|
|
*
|
2010-10-03 22:00:47 +01:00
|
|
|
* @param string $name
|
|
|
|
* @param array|object $data
|
|
|
|
* @param ValidatorInterface $validator
|
|
|
|
* @param array $options
|
2010-06-24 10:24:08 +01:00
|
|
|
*/
|
2011-01-28 20:52:54 +00:00
|
|
|
public function __construct($name = null, $data = null, ValidatorInterface $validator = null, array $options = array())
|
2010-06-24 10:24:08 +01:00
|
|
|
{
|
|
|
|
$this->validator = $validator;
|
|
|
|
|
2010-12-15 14:36:47 +00:00
|
|
|
// Prefill the form with the given data
|
2010-12-21 02:59:17 +00:00
|
|
|
if (null !== $data) {
|
2010-12-15 14:36:47 +00:00
|
|
|
$this->setData($data);
|
|
|
|
}
|
2010-06-24 10:24:08 +01:00
|
|
|
|
2011-01-19 12:43:24 +00:00
|
|
|
$this->addOption('csrf_field_name', '_token');
|
2011-01-25 08:56:37 +00:00
|
|
|
$this->addOption('csrf_provider');
|
2011-01-05 11:36:04 +00:00
|
|
|
$this->addOption('field_factory');
|
2011-01-19 12:43:24 +00:00
|
|
|
$this->addOption('validation_groups');
|
|
|
|
|
|
|
|
if (isset($options['validation_groups'])) {
|
|
|
|
$options['validation_groups'] = (array)$options['validation_groups'];
|
|
|
|
}
|
2011-01-05 11:36:04 +00:00
|
|
|
|
2010-07-04 15:20:10 +01:00
|
|
|
parent::__construct($name, $options);
|
2010-12-15 14:36:47 +00:00
|
|
|
|
|
|
|
// If data is passed to this constructor, objects from parent forms
|
|
|
|
// should be ignored
|
2010-12-21 02:59:17 +00:00
|
|
|
if (null !== $data) {
|
2010-12-15 14:36:47 +00:00
|
|
|
$this->setPropertyPath(null);
|
|
|
|
}
|
2011-01-19 12:43:24 +00:00
|
|
|
|
|
|
|
// Enable CSRF protection, if necessary
|
2011-01-25 08:56:37 +00:00
|
|
|
if ($this->getOption('csrf_provider')) {
|
|
|
|
if (!$this->getOption('csrf_provider') instanceof CsrfProviderInterface) {
|
|
|
|
throw new FormException('The object passed to the "csrf_provider" option must implement CsrfProviderInterface');
|
|
|
|
}
|
|
|
|
|
|
|
|
$token = $this->getOption('csrf_provider')->generateCsrfToken(get_class($this));
|
|
|
|
|
2011-01-19 12:43:24 +00:00
|
|
|
$field = new HiddenField($this->getOption('csrf_field_name'), array(
|
|
|
|
'property_path' => null,
|
|
|
|
));
|
2011-01-25 08:56:37 +00:00
|
|
|
$field->setData($token);
|
2011-01-19 12:43:24 +00:00
|
|
|
|
|
|
|
$this->add($field);
|
|
|
|
}
|
2010-06-24 10:24:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-01-19 12:43:24 +00:00
|
|
|
* Returns a factory for automatically creating fields based on metadata
|
|
|
|
* available for a form's object
|
2010-06-24 10:24:08 +01:00
|
|
|
*
|
2011-01-19 12:43:24 +00:00
|
|
|
* @return FieldFactoryInterface The factory
|
2010-06-24 10:24:08 +01:00
|
|
|
*/
|
2011-01-19 12:43:24 +00:00
|
|
|
public function getFieldFactory()
|
2010-06-24 10:24:08 +01:00
|
|
|
{
|
2011-01-19 12:43:24 +00:00
|
|
|
return $this->getOption('field_factory');
|
2010-06-24 10:24:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-01-19 12:43:24 +00:00
|
|
|
* Returns the validator used by the form
|
2010-06-24 10:24:08 +01:00
|
|
|
*
|
2011-01-19 12:43:24 +00:00
|
|
|
* @return ValidatorInterface The validator instance
|
|
|
|
*/
|
|
|
|
public function getValidator()
|
|
|
|
{
|
|
|
|
return $this->validator;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the validation groups validated by the form
|
|
|
|
*
|
|
|
|
* @return array A list of validation groups or null
|
2010-06-24 10:24:08 +01:00
|
|
|
*/
|
|
|
|
public function getValidationGroups()
|
|
|
|
{
|
2011-01-19 12:43:24 +00:00
|
|
|
return $this->getOption('validation_groups');
|
2010-06-24 10:24:08 +01:00
|
|
|
}
|
|
|
|
|
2011-01-05 11:36:04 +00:00
|
|
|
/**
|
2011-01-19 12:43:24 +00:00
|
|
|
* Returns the name used for the CSRF protection field
|
2011-01-05 11:36:04 +00:00
|
|
|
*
|
2011-01-19 12:43:24 +00:00
|
|
|
* @return string The field name
|
2011-01-05 11:36:04 +00:00
|
|
|
*/
|
2011-01-19 12:43:24 +00:00
|
|
|
public function getCsrfFieldName()
|
2011-01-05 11:36:04 +00:00
|
|
|
{
|
2011-01-19 12:43:24 +00:00
|
|
|
return $this->getOption('csrf_field_name');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-01-25 08:56:37 +00:00
|
|
|
* Returns the provider used for generating and validating CSRF tokens
|
2011-01-19 12:43:24 +00:00
|
|
|
*
|
2011-01-25 08:56:37 +00:00
|
|
|
* @return CsrfProviderInterface The provider instance
|
2011-01-19 12:43:24 +00:00
|
|
|
*/
|
2011-01-25 08:56:37 +00:00
|
|
|
public function getCsrfProvider()
|
2011-01-19 12:43:24 +00:00
|
|
|
{
|
2011-01-25 08:56:37 +00:00
|
|
|
return $this->getOption('csrf_provider');
|
2011-01-05 11:36:04 +00:00
|
|
|
}
|
|
|
|
|
2010-06-24 10:24:08 +01:00
|
|
|
/**
|
2011-01-28 22:09:22 +00:00
|
|
|
* Binds the form with submitted data from a Request object
|
2010-06-24 10:24:08 +01:00
|
|
|
*
|
2011-01-28 22:09:22 +00:00
|
|
|
* @param Request $request The request object
|
|
|
|
* @see bind()
|
|
|
|
*/
|
|
|
|
public function bindRequest(Request $request)
|
|
|
|
{
|
|
|
|
$values = $request->request->get($this->getName());
|
|
|
|
$files = $request->files->get($this->getName());
|
|
|
|
|
|
|
|
$this->bind(self::deepArrayUnion($values, $files));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Binds the form with submitted data from the PHP globals $_POST and
|
|
|
|
* $_FILES
|
2010-06-24 10:24:08 +01:00
|
|
|
*
|
2011-01-28 22:09:22 +00:00
|
|
|
* @see bind()
|
2010-06-24 10:24:08 +01:00
|
|
|
*/
|
2011-01-28 22:09:22 +00:00
|
|
|
public function bindGlobals()
|
2010-06-24 10:24:08 +01:00
|
|
|
{
|
2011-01-28 22:09:22 +00:00
|
|
|
$values = $_POST[$this->getName()];
|
2010-06-24 10:24:08 +01:00
|
|
|
|
2011-01-28 22:09:22 +00:00
|
|
|
// fix files array and convert to UploadedFile instances
|
|
|
|
$fileBag = new FileBag($_FILES);
|
|
|
|
$files = $fileBag->get($this->getName());
|
2010-06-24 10:24:08 +01:00
|
|
|
|
2011-01-28 22:09:22 +00:00
|
|
|
$this->bind(self::deepArrayUnion($values, $files));
|
|
|
|
}
|
2010-09-09 14:23:28 +01:00
|
|
|
|
2011-01-28 22:09:22 +00:00
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public function bind($values)
|
|
|
|
{
|
|
|
|
parent::bind($values);
|
2010-06-24 10:24:08 +01:00
|
|
|
|
|
|
|
if ($this->getParent() === null) {
|
2010-12-15 14:36:47 +00:00
|
|
|
if ($this->validator === null) {
|
|
|
|
throw new FormException('A validator is required for binding. Forgot to pass it to the constructor of the form?');
|
|
|
|
}
|
|
|
|
|
2011-01-19 12:43:24 +00:00
|
|
|
if ($violations = $this->validator->validate($this, $this->getOption('validation_groups'))) {
|
2010-10-10 22:15:28 +01:00
|
|
|
// TODO: test me
|
2010-06-24 10:24:08 +01:00
|
|
|
foreach ($violations as $violation) {
|
|
|
|
$propertyPath = new PropertyPath($violation->getPropertyPath());
|
2010-10-10 22:15:28 +01:00
|
|
|
$iterator = $propertyPath->getIterator();
|
2010-06-24 10:24:08 +01:00
|
|
|
|
2010-10-10 22:15:28 +01:00
|
|
|
if ($iterator->current() == 'data') {
|
2010-06-24 10:24:08 +01:00
|
|
|
$type = self::DATA_ERROR;
|
2010-10-10 22:15:28 +01:00
|
|
|
$iterator->next(); // point at the first data element
|
2010-06-24 10:24:08 +01:00
|
|
|
} else {
|
|
|
|
$type = self::FIELD_ERROR;
|
|
|
|
}
|
|
|
|
|
2010-11-18 22:33:44 +00:00
|
|
|
$this->addError(new FieldError($violation->getMessageTemplate(), $violation->getMessageParameters()), $iterator, $type);
|
2010-06-24 10:24:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true if this form is CSRF protected
|
|
|
|
*/
|
|
|
|
public function isCsrfProtected()
|
|
|
|
{
|
2011-01-19 12:43:24 +00:00
|
|
|
return $this->has($this->getOption('csrf_field_name'));
|
2010-06-24 10:24:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns whether the CSRF token is valid
|
|
|
|
*
|
2011-01-21 01:39:28 +00:00
|
|
|
* @return Boolean
|
2010-06-24 10:24:08 +01:00
|
|
|
*/
|
|
|
|
public function isCsrfTokenValid()
|
|
|
|
{
|
|
|
|
if (!$this->isCsrfProtected()) {
|
|
|
|
return true;
|
|
|
|
} else {
|
2011-01-25 08:56:37 +00:00
|
|
|
$token = $this->get($this->getOption('csrf_field_name'))->getDisplayedData();
|
2011-01-19 12:43:24 +00:00
|
|
|
|
2011-01-25 08:56:37 +00:00
|
|
|
return $this->getOption('csrf_provider')->isCsrfTokenValid(get_class($this), $token);
|
2010-06-24 10:24:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns whether the maximum POST size was reached in this request.
|
|
|
|
*
|
2011-01-21 01:39:28 +00:00
|
|
|
* @return Boolean
|
2010-06-24 10:24:08 +01:00
|
|
|
*/
|
|
|
|
public function isPostMaxSizeReached()
|
|
|
|
{
|
2011-01-28 22:13:10 +00:00
|
|
|
if ($this->isRoot() && isset($_SERVER['CONTENT_LENGTH'])) {
|
2010-06-24 10:24:08 +01:00
|
|
|
$length = (int) $_SERVER['CONTENT_LENGTH'];
|
|
|
|
$max = trim(ini_get('post_max_size'));
|
|
|
|
|
|
|
|
switch (strtolower(substr($max, -1))) {
|
|
|
|
// The 'G' modifier is available since PHP 5.1.0
|
|
|
|
case 'g':
|
|
|
|
$max *= 1024;
|
|
|
|
case 'm':
|
|
|
|
$max *= 1024;
|
|
|
|
case 'k':
|
|
|
|
$max *= 1024;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $length > $max;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Merges two arrays without reindexing numeric keys.
|
|
|
|
*
|
|
|
|
* @param array $array1 An array to merge
|
|
|
|
* @param array $array2 An array to merge
|
|
|
|
*
|
|
|
|
* @return array The merged array
|
|
|
|
*/
|
|
|
|
static protected function deepArrayUnion($array1, $array2)
|
|
|
|
{
|
|
|
|
foreach ($array2 as $key => $value) {
|
|
|
|
if (is_array($value) && isset($array1[$key]) && is_array($array1[$key])) {
|
|
|
|
$array1[$key] = self::deepArrayUnion($array1[$key], $value);
|
|
|
|
} else {
|
|
|
|
$array1[$key] = $value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $array1;
|
|
|
|
}
|
2010-06-24 09:40:05 +01:00
|
|
|
}
|