PageRenderTime 53ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Symfony/Component/Form/Form.php

https://github.com/sebio/symfony
PHP | 277 lines | 135 code | 36 blank | 106 comment | 24 complexity | 7f37d2936d6c2fc663929f4691db2a1c MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Form;
  11. use Symfony\Component\Validator\ValidatorInterface;
  12. use Symfony\Component\Form\Exception\FormException;
  13. use Symfony\Component\Form\CsrfProvider\CsrfProviderInterface;
  14. /**
  15. * Form represents a form.
  16. *
  17. * A form is composed of a validator schema and a widget form schema.
  18. *
  19. * Form also takes care of Csrf protection by default.
  20. *
  21. * A Csrf secret can be any random string. If set to false, it disables the
  22. * Csrf protection, and if set to null, it forces the form to use the global
  23. * Csrf secret. If the global Csrf secret is also null, then a random one
  24. * is generated on the fly.
  25. *
  26. * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  27. * @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
  28. */
  29. class Form extends FieldGroup
  30. {
  31. /**
  32. * The validator to validate form values
  33. * @var ValidatorInterface
  34. */
  35. protected $validator = null;
  36. /**
  37. * Constructor.
  38. *
  39. * @param string $name
  40. * @param array|object $data
  41. * @param ValidatorInterface $validator
  42. * @param array $options
  43. */
  44. public function __construct($name, $data = null, ValidatorInterface $validator = null, array $options = array())
  45. {
  46. $this->validator = $validator;
  47. // Prefill the form with the given data
  48. if (null !== $data) {
  49. $this->setData($data);
  50. }
  51. $this->addOption('csrf_field_name', '_token');
  52. $this->addOption('csrf_provider');
  53. $this->addOption('field_factory');
  54. $this->addOption('validation_groups');
  55. if (isset($options['validation_groups'])) {
  56. $options['validation_groups'] = (array)$options['validation_groups'];
  57. }
  58. parent::__construct($name, $options);
  59. // If data is passed to this constructor, objects from parent forms
  60. // should be ignored
  61. if (null !== $data) {
  62. $this->setPropertyPath(null);
  63. }
  64. // Enable CSRF protection, if necessary
  65. if ($this->getOption('csrf_provider')) {
  66. if (!$this->getOption('csrf_provider') instanceof CsrfProviderInterface) {
  67. throw new FormException('The object passed to the "csrf_provider" option must implement CsrfProviderInterface');
  68. }
  69. $token = $this->getOption('csrf_provider')->generateCsrfToken(get_class($this));
  70. $field = new HiddenField($this->getOption('csrf_field_name'), array(
  71. 'property_path' => null,
  72. ));
  73. $field->setData($token);
  74. $this->add($field);
  75. }
  76. }
  77. /**
  78. * Returns a factory for automatically creating fields based on metadata
  79. * available for a form's object
  80. *
  81. * @return FieldFactoryInterface The factory
  82. */
  83. public function getFieldFactory()
  84. {
  85. return $this->getOption('field_factory');
  86. }
  87. /**
  88. * Returns the validator used by the form
  89. *
  90. * @return ValidatorInterface The validator instance
  91. */
  92. public function getValidator()
  93. {
  94. return $this->validator;
  95. }
  96. /**
  97. * Returns the validation groups validated by the form
  98. *
  99. * @return array A list of validation groups or null
  100. */
  101. public function getValidationGroups()
  102. {
  103. return $this->getOption('validation_groups');
  104. }
  105. /**
  106. * Returns the name used for the CSRF protection field
  107. *
  108. * @return string The field name
  109. */
  110. public function getCsrfFieldName()
  111. {
  112. return $this->getOption('csrf_field_name');
  113. }
  114. /**
  115. * Returns the provider used for generating and validating CSRF tokens
  116. *
  117. * @return CsrfProviderInterface The provider instance
  118. */
  119. public function getCsrfProvider()
  120. {
  121. return $this->getOption('csrf_provider');
  122. }
  123. /**
  124. * Binds the form with values and files.
  125. *
  126. * This method is final because it is very easy to break a form when
  127. * overriding this method and adding logic that depends on $taintedFiles.
  128. * You should override doBind() instead where the uploaded files are
  129. * already merged into the data array.
  130. *
  131. * @param array $taintedValues The form data of the $_POST array
  132. * @param array $taintedFiles An array of uploaded files
  133. * @return Boolean Whether the form is valid
  134. */
  135. final public function bind($taintedValues, array $taintedFiles = null)
  136. {
  137. if (null === $taintedFiles) {
  138. if ($this->isMultipart() && $this->getParent() === null) {
  139. throw new \InvalidArgumentException('You must provide a files array for multipart forms');
  140. }
  141. $taintedFiles = array();
  142. }
  143. if (null === $taintedValues) {
  144. $taintedValues = array();
  145. }
  146. $this->doBind(self::deepArrayUnion($taintedValues, $taintedFiles));
  147. if ($this->getParent() === null) {
  148. if ($this->validator === null) {
  149. throw new FormException('A validator is required for binding. Forgot to pass it to the constructor of the form?');
  150. }
  151. if ($violations = $this->validator->validate($this, $this->getOption('validation_groups'))) {
  152. // TODO: test me
  153. foreach ($violations as $violation) {
  154. $propertyPath = new PropertyPath($violation->getPropertyPath());
  155. $iterator = $propertyPath->getIterator();
  156. if ($iterator->current() == 'data') {
  157. $type = self::DATA_ERROR;
  158. $iterator->next(); // point at the first data element
  159. } else {
  160. $type = self::FIELD_ERROR;
  161. }
  162. $this->addError(new FieldError($violation->getMessageTemplate(), $violation->getMessageParameters()), $iterator, $type);
  163. }
  164. }
  165. }
  166. }
  167. /**
  168. * Binds the form with the given data.
  169. *
  170. * @param array $taintedData The data to bind to the form
  171. * @return Boolean Whether the form is valid
  172. */
  173. protected function doBind(array $taintedData)
  174. {
  175. parent::bind($taintedData);
  176. }
  177. /**
  178. * @return true if this form is CSRF protected
  179. */
  180. public function isCsrfProtected()
  181. {
  182. return $this->has($this->getOption('csrf_field_name'));
  183. }
  184. /**
  185. * Returns whether the CSRF token is valid
  186. *
  187. * @return Boolean
  188. */
  189. public function isCsrfTokenValid()
  190. {
  191. if (!$this->isCsrfProtected()) {
  192. return true;
  193. } else {
  194. $token = $this->get($this->getOption('csrf_field_name'))->getDisplayedData();
  195. return $this->getOption('csrf_provider')->isCsrfTokenValid(get_class($this), $token);
  196. }
  197. }
  198. /**
  199. * Returns whether the maximum POST size was reached in this request.
  200. *
  201. * @return Boolean
  202. */
  203. public function isPostMaxSizeReached()
  204. {
  205. if (isset($_SERVER['CONTENT_LENGTH'])) {
  206. $length = (int) $_SERVER['CONTENT_LENGTH'];
  207. $max = trim(ini_get('post_max_size'));
  208. switch (strtolower(substr($max, -1))) {
  209. // The 'G' modifier is available since PHP 5.1.0
  210. case 'g':
  211. $max *= 1024;
  212. case 'm':
  213. $max *= 1024;
  214. case 'k':
  215. $max *= 1024;
  216. }
  217. return $length > $max;
  218. } else {
  219. return false;
  220. }
  221. }
  222. /**
  223. * Merges two arrays without reindexing numeric keys.
  224. *
  225. * @param array $array1 An array to merge
  226. * @param array $array2 An array to merge
  227. *
  228. * @return array The merged array
  229. */
  230. static protected function deepArrayUnion($array1, $array2)
  231. {
  232. foreach ($array2 as $key => $value) {
  233. if (is_array($value) && isset($array1[$key]) && is_array($array1[$key])) {
  234. $array1[$key] = self::deepArrayUnion($array1[$key], $value);
  235. } else {
  236. $array1[$key] = $value;
  237. }
  238. }
  239. return $array1;
  240. }
  241. }