Merge remote branch 'bschussek/form'

* bschussek/form: (22 commits)
  Fix merge error (function "guess" was in there twice)
  [Form] Added test case for bf2f9d2a02
  [Form] Form::isBound() and Form::isValid() work correctly now for read-only forms
  [Locale] Improved error reporting and added stubs for intl_is_failure(), intl_get_error_code() and intl_get_error_message()
  [Form] Implemented fix for 361c67f54f
  [Form] Add test for the handling of array values in the constraint violation
  [Form] Further simplified PropertyPath code
  [Form] Added test for 6c337d1cc0
  [Form] Removed unused option "pattern" of date and time type
  [Form] Renamed view variable "name" to "full_name"
  [Form] Renamed collection option "type_options" to "options" to be consistent with the repeated type
  [Form] CSRF documentation and a few CS changes
  [Form] Move CSRF options from types to the CSRF extension
  [Form] Added a search form field type
  [Form] Optimization of PropertyPath
  [Form] replace assertEquals by assertFalse, assertTrue, assertNull
  [Form] fix file permissions to 644 again ;)
  [Form] add tests for type_options in collectionType
  fix file permissions to 644
  [Form] add type_options for CollectionType to be abble to set options to type
  ...
This commit is contained in:
Fabien Potencier 2011-05-19 16:25:30 +02:00
commit 9fe1c3ae0e
59 changed files with 730 additions and 156 deletions

View File

@ -167,6 +167,9 @@ beta1 to beta2
* Form: Renamed option value "text" of "widget" option of the "date" type was * Form: Renamed option value "text" of "widget" option of the "date" type was
renamed to "single-text". "text" indicates to use separate text boxes now renamed to "single-text". "text" indicates to use separate text boxes now
(like for the "time" type). (like for the "time" type).
* Form: Renamed view variable "name" to "full_name". The variable "name" now
contains the local, short name (equivalent to $form->getName()).
PR12 to beta1 PR12 to beta1
------------- -------------

View File

@ -180,4 +180,12 @@ class DoctrineOrmTypeGuesser implements FormTypeGuesserInterface
} }
} }
} }
/**
* @inheritDoc
*/
public function guessMinLength($class, $property)
{
return;
}
} }

View File

@ -129,6 +129,9 @@
<service id="form.type.repeated" class="Symfony\Component\Form\Extension\Core\Type\RepeatedType"> <service id="form.type.repeated" class="Symfony\Component\Form\Extension\Core\Type\RepeatedType">
<tag name="form.type" alias="repeated" /> <tag name="form.type" alias="repeated" />
</service> </service>
<service id="form.type.search" class="Symfony\Component\Form\Extension\Core\Type\SearchType">
<tag name="form.type" alias="search" />
</service>
<service id="form.type.textarea" class="Symfony\Component\Form\Extension\Core\Type\TextareaType"> <service id="form.type.textarea" class="Symfony\Component\Form\Extension\Core\Type\TextareaType">
<tag name="form.type" alias="textarea" /> <tag name="form.type" alias="textarea" />
</service> </service>

View File

@ -1,6 +1,6 @@
<input type="checkbox" <input type="checkbox"
<?php echo $view['form']->attributes() ?> <?php echo $view['form']->attributes() ?>
name="<?php echo $view->escape($name) ?>" name="<?php echo $view->escape($full_name) ?>"
<?php if ($value): ?>value="<?php echo $view->escape($value) ?>"<?php endif ?> <?php if ($value): ?>value="<?php echo $view->escape($value) ?>"<?php endif ?>
<?php if ($read_only): ?>disabled="disabled"<?php endif ?> <?php if ($read_only): ?>disabled="disabled"<?php endif ?>
<?php if ($required): ?>required="required"<?php endif ?> <?php if ($required): ?>required="required"<?php endif ?>

View File

@ -8,7 +8,7 @@
<?php else: ?> <?php else: ?>
<select <select
<?php echo $view['form']->attributes() ?> <?php echo $view['form']->attributes() ?>
name="<?php echo $view->escape($name) ?>" name="<?php echo $view->escape($full_name) ?>"
<?php if ($read_only): ?> disabled="disabled"<?php endif ?> <?php if ($read_only): ?> disabled="disabled"<?php endif ?>
<?php if ($multiple): ?> multiple="multiple"<?php endif ?> <?php if ($multiple): ?> multiple="multiple"<?php endif ?>
> >

View File

@ -1,7 +1,7 @@
<?php if ($widget == 'single-text'): ?> <?php if ($widget == 'single-text'): ?>
<input type="text" <input type="text"
<?php echo $view['form']->attributes() ?> <?php echo $view['form']->attributes() ?>
name="<?php echo $view->escape($name) ?>" name="<?php echo $view->escape($full_name) ?>"
value="<?php echo $view->escape($value) ?>" value="<?php echo $view->escape($value) ?>"
<?php if ($read_only): ?>disabled="disabled"<?php endif ?> <?php if ($read_only): ?>disabled="disabled"<?php endif ?>
<?php if ($required): ?>required="required"<?php endif ?> <?php if ($required): ?>required="required"<?php endif ?>

View File

@ -1,6 +1,6 @@
<input type="email" <input type="email"
<?php echo $view['form']->attributes() ?> <?php echo $view['form']->attributes() ?>
name="<?php echo $view->escape($name) ?>" name="<?php echo $view->escape($full_name) ?>"
value="<?php echo $view->escape($value) ?>" value="<?php echo $view->escape($value) ?>"
<?php if ($max_length): ?>maxlength="<?php echo $view->escape($max_length) ?>"<?php endif ?> <?php if ($max_length): ?>maxlength="<?php echo $view->escape($max_length) ?>"<?php endif ?>
<?php if ($read_only): ?>disabled="disabled"<?php endif ?> <?php if ($read_only): ?>disabled="disabled"<?php endif ?>

View File

@ -1,6 +1,6 @@
<input <input
<?php echo $view['form']->attributes() ?> <?php echo $view['form']->attributes() ?>
name="<?php echo $view->escape($name) ?>" name="<?php echo $view->escape($full_name) ?>"
value="<?php echo $view->escape($value) ?>" value="<?php echo $view->escape($value) ?>"
<?php if ($read_only): ?>disabled="disabled"<?php endif ?> <?php if ($read_only): ?>disabled="disabled"<?php endif ?>
<?php if ($required): ?>required="required"<?php endif ?> <?php if ($required): ?>required="required"<?php endif ?>

View File

@ -1,6 +1,6 @@
<input type="hidden" <input type="hidden"
<?php echo $view['form']->attributes() ?> <?php echo $view['form']->attributes() ?>
name="<?php echo $view->escape($name) ?>" name="<?php echo $view->escape($full_name) ?>"
value="<?php echo $view->escape($value) ?>" value="<?php echo $view->escape($value) ?>"
<?php if ($read_only): ?>disabled="disabled"<?php endif ?> <?php if ($read_only): ?>disabled="disabled"<?php endif ?>
/> />

View File

@ -1,6 +1,6 @@
<input type="number" <input type="number"
<?php echo $view['form']->attributes() ?> <?php echo $view['form']->attributes() ?>
name="<?php echo $view->escape($name) ?>" name="<?php echo $view->escape($full_name) ?>"
value="<?php echo $view->escape($value) ?>" value="<?php echo $view->escape($value) ?>"
<?php if ($read_only): ?>disabled="disabled"<?php endif ?> <?php if ($read_only): ?>disabled="disabled"<?php endif ?>
<?php if ($required): ?>required="required"<?php endif ?> <?php if ($required): ?>required="required"<?php endif ?>

View File

@ -1,8 +1,9 @@
<input type="text" <input type="text"
<?php echo $view['form']->attributes() ?> <?php echo $view['form']->attributes() ?>
name="<?php echo $view->escape($name) ?>" name="<?php echo $view->escape($full_name) ?>"
value="<?php echo $view->escape($value) ?>" value="<?php echo $view->escape($value) ?>"
<?php if ($read_only): ?>disabled="disabled"<?php endif ?> <?php if ($read_only): ?>disabled="disabled"<?php endif ?>
<?php if ($required): ?>required="required"<?php endif ?> <?php if ($required): ?>required="required"<?php endif ?>
<?php if ($max_length): ?>maxlength="<?php echo $max_length ?>"<?php endif ?> <?php if ($max_length): ?>maxlength="<?php echo $max_length ?>"<?php endif ?>
<?php if ($pattern): ?>pattern="<?php echo $pattern ?>"<?php endif ?>
/> />

View File

@ -1,8 +1,9 @@
<input type="password" <input type="password"
<?php echo $view['form']->attributes() ?> <?php echo $view['form']->attributes() ?>
name="<?php echo $view->escape($name) ?>" name="<?php echo $view->escape($full_name) ?>"
value="<?php echo $view->escape($value) ?>" value="<?php echo $view->escape($value) ?>"
<?php if ($read_only): ?>disabled="disabled"<?php endif ?> <?php if ($read_only): ?>disabled="disabled"<?php endif ?>
<?php if ($required): ?>required="required"<?php endif ?> <?php if ($required): ?>required="required"<?php endif ?>
<?php if ($max_length): ?>maxlength="<?php echo $max_length ?>"<?php endif ?> <?php if ($max_length): ?>maxlength="<?php echo $max_length ?>"<?php endif ?>
<?php if ($pattern): ?>pattern="<?php echo $pattern ?>"<?php endif ?>
/> />

View File

@ -1,6 +1,6 @@
<input type="radio" <input type="radio"
<?php echo $view['form']->attributes() ?> <?php echo $view['form']->attributes() ?>
name="<?php echo $view->escape($name) ?>" name="<?php echo $view->escape($full_name) ?>"
value="<?php echo $view->escape($value) ?>" value="<?php echo $view->escape($value) ?>"
<?php if ($read_only): ?>disabled="disabled"<?php endif ?> <?php if ($read_only): ?>disabled="disabled"<?php endif ?>
<?php if ($required): ?>required="required"<?php endif ?> <?php if ($required): ?>required="required"<?php endif ?>

View File

@ -0,0 +1,8 @@
<input type="search"
<?php echo $view['form']->attributes() ?>
name="<?php echo $view->escape($name) ?>"
value="<?php echo $view->escape($value) ?>"
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
<?php if ($required): ?>required="required"<?php endif ?>
<?php if ($max_length): ?>maxlength="<?php echo $max_length ?>"<?php endif ?>
/>

View File

@ -1,8 +1,9 @@
<input type="text" <input type="text"
<?php echo $view['form']->attributes() ?> <?php echo $view['form']->attributes() ?>
name="<?php echo $view->escape($name) ?>" name="<?php echo $view->escape($full_name) ?>"
value="<?php echo $view->escape($value) ?>" value="<?php echo $view->escape($value) ?>"
<?php if ($read_only): ?>disabled="disabled"<?php endif ?> <?php if ($read_only): ?>disabled="disabled"<?php endif ?>
<?php if ($required): ?>required="required"<?php endif ?> <?php if ($required): ?>required="required"<?php endif ?>
<?php if ($max_length): ?>maxlength="<?php echo $max_length ?>"<?php endif ?> <?php if ($max_length): ?>maxlength="<?php echo $max_length ?>"<?php endif ?>
<?php if ($pattern): ?>pattern="<?php echo $pattern ?>"<?php endif ?>
/> />

View File

@ -1,6 +1,6 @@
<textarea <textarea
<?php echo $view['form']->attributes() ?> <?php echo $view['form']->attributes() ?>
name="<?php echo $view->escape($name) ?>" name="<?php echo $view->escape($full_name) ?>"
<?php if ($read_only): ?>disabled="disabled"<?php endif ?> <?php if ($read_only): ?>disabled="disabled"<?php endif ?>
<?php if ($required): ?>required="required"<?php endif ?> <?php if ($required): ?>required="required"<?php endif ?>
><?php echo $view->escape($value) ?></textarea> ><?php echo $view->escape($value) ?></textarea>

View File

@ -1,6 +1,6 @@
<input type="url" <input type="url"
<?php echo $view['form']->attributes() ?> <?php echo $view['form']->attributes() ?>
name="<?php echo $view->escape($name) ?>" name="<?php echo $view->escape($full_name) ?>"
value="<?php echo $view->escape($value) ?>" value="<?php echo $view->escape($value) ?>"
<?php if ($read_only): ?>disabled="disabled"<?php endif ?> <?php if ($read_only): ?>disabled="disabled"<?php endif ?>
<?php if ($required): ?>required="required"<?php endif ?> <?php if ($required): ?>required="required"<?php endif ?>

View File

@ -43,7 +43,7 @@
{% block attributes %} {% block attributes %}
{% spaceless %} {% spaceless %}
id="{{ id }}" name="{{ name }}"{% if read_only %} disabled="disabled"{% endif %}{% if required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %} id="{{ id }}" name="{{ full_name }}"{% if read_only %} disabled="disabled"{% endif %}{% if required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %}{% if pattern %} pattern="{{ pattern }}"{% endif %}
{% for attrname,attrvalue in attr %}{{attrname}}="{{attrvalue}}" {% endfor %} {% for attrname,attrvalue in attr %}{{attrname}}="{{attrvalue}}" {% endfor %}
{% endspaceless %} {% endspaceless %}
{% endblock attributes %} {% endblock attributes %}
@ -208,6 +208,13 @@
{% endspaceless %} {% endspaceless %}
{% endblock url_widget %} {% endblock url_widget %}
{% block search_widget %}
{% spaceless %}
{% set type = type|default('search') %}
{{ block('field_widget') }}
{% endspaceless %}
{% endblock search_widget %}
{% block percent_widget %} {% block percent_widget %}
{% spaceless %} {% spaceless %}
{% set type = type|default('text') %} {% set type = type|default('text') %}

View File

@ -52,6 +52,7 @@ class CoreExtension extends AbstractExtension
new Type\PercentType(), new Type\PercentType(),
new Type\RadioType(), new Type\RadioType(),
new Type\RepeatedType(), new Type\RepeatedType(),
new Type\SearchType(),
new Type\TextareaType(), new Type\TextareaType(),
new Type\TextType(), new Type\TextType(),
new Type\TimeType(), new Type\TimeType(),

View File

@ -35,6 +35,11 @@ class ResizeFormListener implements EventSubscriberInterface
*/ */
private $type; private $type;
/**
* @var array
*/
private $options;
/** /**
* Whether children could be added to the group * Whether children could be added to the group
* @var Boolean * @var Boolean
@ -47,12 +52,13 @@ class ResizeFormListener implements EventSubscriberInterface
*/ */
private $allowDelete; private $allowDelete;
public function __construct(FormFactoryInterface $factory, $type, $allowAdd = false, $allowDelete = false) public function __construct(FormFactoryInterface $factory, $type, array $options = array(), $allowAdd = false, $allowDelete = false)
{ {
$this->factory = $factory; $this->factory = $factory;
$this->type = $type; $this->type = $type;
$this->allowAdd = $allowAdd; $this->allowAdd = $allowAdd;
$this->allowDelete = $allowDelete; $this->allowDelete = $allowDelete;
$this->options = $options;
} }
public static function getSubscribedEvents() public static function getSubscribedEvents()
@ -86,9 +92,9 @@ class ResizeFormListener implements EventSubscriberInterface
// Then add all rows again in the correct order // Then add all rows again in the correct order
foreach ($data as $name => $value) { foreach ($data as $name => $value) {
$form->add($this->factory->createNamed($this->type, $name, null, array( $form->add($this->factory->createNamed($this->type, $name, null, array_replace(array(
'property_path' => '['.$name.']', 'property_path' => '['.$name.']',
))); ), $this->options)));
} }
} }
@ -118,9 +124,9 @@ class ResizeFormListener implements EventSubscriberInterface
if ($this->allowAdd) { if ($this->allowAdd) {
foreach ($data as $name => $value) { foreach ($data as $name => $value) {
if (!$form->has($name)) { if (!$form->has($name)) {
$form->add($this->factory->createNamed($this->type, $name, null, array( $form->add($this->factory->createNamed($this->type, $name, null, array_replace(array(
'property_path' => '['.$name.']', 'property_path' => '['.$name.']',
))); ), $this->options)));
} }
} }
} }
@ -149,4 +155,4 @@ class ResizeFormListener implements EventSubscriberInterface
$event->setData($data); $event->setData($data);
} }
} }

View File

@ -106,7 +106,7 @@ class ChoiceType extends AbstractType
// Add "[]" to the name in case a select tag with multiple options is // Add "[]" to the name in case a select tag with multiple options is
// displayed. Otherwise only one of the selected options is sent in the // displayed. Otherwise only one of the selected options is sent in the
// POST request. // POST request.
$view->set('name', $view->get('name').'[]'); $view->set('full_name', $view->get('full_name').'[]');
} }
} }
@ -121,7 +121,6 @@ class ChoiceType extends AbstractType
'choice_list' => null, 'choice_list' => null,
'choices' => array(), 'choices' => array(),
'preferred_choices' => array(), 'preferred_choices' => array(),
'csrf_protection' => false,
'empty_data' => $multiple || $expanded ? array() : '', 'empty_data' => $multiple || $expanded ? array() : '',
'error_bubbling' => false, 'error_bubbling' => false,
); );

View File

@ -22,15 +22,16 @@ class CollectionType extends AbstractType
public function buildForm(FormBuilder $builder, array $options) public function buildForm(FormBuilder $builder, array $options)
{ {
if ($options['allow_add'] && $options['prototype']) { if ($options['allow_add'] && $options['prototype']) {
$builder->add('$$name$$', $options['type'], array( $builder->add('$$name$$', $options['type'], array_replace(array(
'property_path' => false, 'property_path' => false,
'required' => false, 'required' => false,
)); ), $options['options']));
} }
$listener = new ResizeFormListener( $listener = new ResizeFormListener(
$builder->getFormFactory(), $builder->getFormFactory(),
$options['type'], $options['type'],
$options['options'],
$options['allow_add'], $options['allow_add'],
$options['allow_delete'] $options['allow_delete']
); );
@ -57,6 +58,7 @@ class CollectionType extends AbstractType
'allow_delete' => false, 'allow_delete' => false,
'prototype' => true, 'prototype' => true,
'type' => 'text', 'type' => 'text',
'options' => array(),
); );
} }

View File

@ -37,9 +37,6 @@ class DateTimeType extends AbstractType
'with_seconds', 'with_seconds',
))); )));
if (isset($options['date_pattern'])) {
$dateOptions['pattern'] = $options['date_pattern'];
}
if (isset($options['date_widget'])) { if (isset($options['date_widget'])) {
$dateOptions['widget'] = $options['date_widget']; $dateOptions['widget'] = $options['date_widget'];
} }
@ -49,9 +46,6 @@ class DateTimeType extends AbstractType
$dateOptions['input'] = 'array'; $dateOptions['input'] = 'array';
if (isset($options['time_pattern'])) {
$timeOptions['pattern'] = $options['time_pattern'];
}
if (isset($options['time_widget'])) { if (isset($options['time_widget'])) {
$timeOptions['widget'] = $options['time_widget']; $timeOptions['widget'] = $options['time_widget'];
} }
@ -103,10 +97,8 @@ class DateTimeType extends AbstractType
// Don't modify \DateTime classes by reference, we treat // Don't modify \DateTime classes by reference, we treat
// them like immutable value objects // them like immutable value objects
'by_reference' => false, 'by_reference' => false,
'date_pattern' => null,
'date_widget' => null, 'date_widget' => null,
'date_format' => null, 'date_format' => null,
'time_pattern' => null,
'time_widget' => null, 'time_widget' => null,
/* Defaults for date field */ /* Defaults for date field */
'years' => range(date('Y') - 5, date('Y') + 5), 'years' => range(date('Y') - 5, date('Y') + 5),

View File

@ -119,11 +119,9 @@ class DateType extends AbstractType
'days' => range(1, 31), 'days' => range(1, 31),
'widget' => 'choice', 'widget' => 'choice',
'input' => 'datetime', 'input' => 'datetime',
'pattern' => null,
'format' => \IntlDateFormatter::MEDIUM, 'format' => \IntlDateFormatter::MEDIUM,
'data_timezone' => null, 'data_timezone' => null,
'user_timezone' => null, 'user_timezone' => null,
'csrf_protection' => false,
// Don't modify \DateTime classes by reference, we treat // Don't modify \DateTime classes by reference, we treat
// them like immutable value objects // them like immutable value objects
'by_reference' => false, 'by_reference' => false,

View File

@ -44,6 +44,7 @@ class FieldType extends AbstractType
->setAttribute('property_path', $options['property_path']) ->setAttribute('property_path', $options['property_path'])
->setAttribute('error_mapping', $options['error_mapping']) ->setAttribute('error_mapping', $options['error_mapping'])
->setAttribute('max_length', $options['max_length']) ->setAttribute('max_length', $options['max_length'])
->setAttribute('pattern', $options['pattern'])
->setAttribute('label', $options['label'] ?: $this->humanize($builder->getName())) ->setAttribute('label', $options['label'] ?: $this->humanize($builder->getName()))
->setData($options['data']) ->setData($options['data'])
->addValidator(new DefaultValidator()) ->addValidator(new DefaultValidator())
@ -58,7 +59,7 @@ class FieldType extends AbstractType
{ {
if ($view->hasParent()) { if ($view->hasParent()) {
$parentId = $view->getParent()->get('id'); $parentId = $view->getParent()->get('id');
$parentName = $view->getParent()->get('name'); $parentName = $view->getParent()->get('full_name');
$id = sprintf('%s_%s', $parentId, $form->getName()); $id = sprintf('%s_%s', $parentId, $form->getName());
$name = sprintf('%s[%s]', $parentName, $form->getName()); $name = sprintf('%s[%s]', $parentName, $form->getName());
} else { } else {
@ -69,12 +70,14 @@ class FieldType extends AbstractType
$view $view
->set('form', $view) ->set('form', $view)
->set('id', $id) ->set('id', $id)
->set('name', $name) ->set('name', $form->getName())
->set('full_name', $name)
->set('errors', $form->getErrors()) ->set('errors', $form->getErrors())
->set('value', $form->getClientData()) ->set('value', $form->getClientData())
->set('read_only', $form->isReadOnly()) ->set('read_only', $form->isReadOnly())
->set('required', $form->isRequired()) ->set('required', $form->isRequired())
->set('max_length', $form->getAttribute('max_length')) ->set('max_length', $form->getAttribute('max_length'))
->set('pattern', $form->getAttribute('pattern'))
->set('size', null) ->set('size', null)
->set('label', $form->getAttribute('label')) ->set('label', $form->getAttribute('label'))
->set('multipart', false) ->set('multipart', false)
@ -97,6 +100,7 @@ class FieldType extends AbstractType
'required' => true, 'required' => true,
'read_only' => false, 'read_only' => false,
'max_length' => null, 'max_length' => null,
'pattern' => null,
'property_path' => null, 'property_path' => null,
'by_reference' => true, 'by_reference' => true,
'error_bubbling' => false, 'error_bubbling' => false,

View File

@ -52,13 +52,13 @@ class FileType extends AbstractType
{ {
$view->set('multipart', true); $view->set('multipart', true);
$view['file']->set('type', 'file'); $view['file']->set('type', 'file');
$view['file']->set('value', '');
} }
public function getDefaultOptions(array $options) public function getDefaultOptions(array $options)
{ {
return array( return array(
'type' => 'string', 'type' => 'string',
'csrf_protection' => false,
); );
} }

View File

@ -35,7 +35,7 @@ class RadioType extends AbstractType
; ;
if ($view->hasParent()) { if ($view->hasParent()) {
$view->set('name', $view->getParent()->get('name')); $view->set('full_name', $view->getParent()->get('full_name'));
} }
} }

View File

@ -36,7 +36,6 @@ class RepeatedType extends AbstractType
'options' => array(), 'options' => array(),
'first_name' => 'first', 'first_name' => 'first',
'second_name' => 'second', 'second_name' => 'second',
'csrf_protection' => false,
'error_bubbling' => false, 'error_bubbling' => false,
); );
} }

View File

@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Form\AbstractType;
class SearchType extends AbstractType
{
public function getParent(array $options)
{
return 'text';
}
public function getName()
{
return 'search';
}
}

View File

@ -94,10 +94,8 @@ class TimeType extends AbstractType
'widget' => 'choice', 'widget' => 'choice',
'input' => 'datetime', 'input' => 'datetime',
'with_seconds' => false, 'with_seconds' => false,
'pattern' => null,
'data_timezone' => null, 'data_timezone' => null,
'user_timezone' => null, 'user_timezone' => null,
'csrf_protection' => false,
// Don't modify \DateTime classes by reference, we treat // Don't modify \DateTime classes by reference, we treat
// them like immutable value objects // them like immutable value objects
'by_reference' => false, 'by_reference' => false,

View File

@ -15,15 +15,26 @@ use Symfony\Component\Form\Extension\Csrf\Type;
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
use Symfony\Component\Form\AbstractExtension; use Symfony\Component\Form\AbstractExtension;
/**
* This extension protects forms by using a CSRF token
*/
class CsrfExtension extends AbstractExtension class CsrfExtension extends AbstractExtension
{ {
private $csrfProvider; private $csrfProvider;
/**
* Constructor.
*
* @param CsrfProviderInterface $csrfProvider The CSRF provider
*/
public function __construct(CsrfProviderInterface $csrfProvider) public function __construct(CsrfProviderInterface $csrfProvider)
{ {
$this->csrfProvider = $csrfProvider; $this->csrfProvider = $csrfProvider;
} }
/**
* {@inheritDoc}
*/
protected function loadTypes() protected function loadTypes()
{ {
return array( return array(
@ -31,10 +42,18 @@ class CsrfExtension extends AbstractExtension
); );
} }
/**
* {@inheritDoc}
*/
protected function loadTypeExtensions() protected function loadTypeExtensions()
{ {
return array( return array(
new Type\ChoiceTypeCsrfExtension(),
new Type\DateTypeCsrfExtension(),
new Type\FileTypeCsrfExtension(),
new Type\FormTypeCsrfExtension(), new Type\FormTypeCsrfExtension(),
new Type\RepeatedTypeCsrfExtension(),
new Type\TimeTypeCsrfExtension(),
); );
} }
} }

View File

@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Extension\Csrf\Type;
use Symfony\Component\Form\AbstractTypeExtension;
class ChoiceTypeCsrfExtension extends AbstractTypeExtension
{
public function getDefaultOptions(array $options)
{
return array('csrf_protection' => false);
}
public function getExtendedType()
{
return 'choice';
}
}

View File

@ -22,11 +22,25 @@ class CsrfType extends AbstractType
{ {
private $csrfProvider; private $csrfProvider;
/**
* Constructor.
*
* @param CsrfProviderInterface $csrfProvider The provider to use to generate the token
*/
public function __construct(CsrfProviderInterface $csrfProvider) public function __construct(CsrfProviderInterface $csrfProvider)
{ {
$this->csrfProvider = $csrfProvider; $this->csrfProvider = $csrfProvider;
} }
/**
* Builds the CSRF field.
*
* A validator is added to check the token value when the CSRF field is added to
* a root form
*
* @param FormBuilder $builder The form builder
* @param array $options The options
*/
public function buildForm(FormBuilder $builder, array $options) public function buildForm(FormBuilder $builder, array $options)
{ {
$csrfProvider = $options['csrf_provider']; $csrfProvider = $options['csrf_provider'];
@ -47,20 +61,31 @@ class CsrfType extends AbstractType
; ;
} }
/**
* {@inheritDoc}
*/
public function getDefaultOptions(array $options) public function getDefaultOptions(array $options)
{ {
return array( return array(
'csrf_provider' => $this->csrfProvider, 'csrf_provider' => $this->csrfProvider,
'intention' => null, 'intention' => null,
'property_path' => false, 'property_path' => false,
); );
} }
/**
* {@inheritDoc}
*/
public function getParent(array $options) public function getParent(array $options)
{ {
return 'hidden'; return 'hidden';
} }
/**
* Returns the name of this form.
*
* @return string 'csrf'
*/
public function getName() public function getName()
{ {
return 'csrf'; return 'csrf';

View File

@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Extension\Csrf\Type;
use Symfony\Component\Form\AbstractTypeExtension;
class DateTypeCsrfExtension extends AbstractTypeExtension
{
public function getDefaultOptions(array $options)
{
return array('csrf_protection' => false);
}
public function getExtendedType()
{
return 'date';
}
}

View File

@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Extension\Csrf\Type;
use Symfony\Component\Form\AbstractTypeExtension;
class FileTypeCsrfExtension extends AbstractTypeExtension
{
public function getDefaultOptions(array $options)
{
return array('csrf_protection' => false);
}
public function getExtendedType()
{
return 'file';
}
}

View File

@ -27,6 +27,12 @@ class FormTypeCsrfExtension extends AbstractTypeExtension
$this->fieldName = $fieldName; $this->fieldName = $fieldName;
} }
/**
* Adds a CSRF field to the form when the CSRF protection is enabled.
*
* @param FormBuilder $builder The form builder
* @param array $options The options
*/
public function buildForm(FormBuilder $builder, array $options) public function buildForm(FormBuilder $builder, array $options)
{ {
if ($options['csrf_protection']) { if ($options['csrf_protection']) {
@ -36,11 +42,19 @@ class FormTypeCsrfExtension extends AbstractTypeExtension
$csrfOptions['csrf_provider'] = $options['csrf_provider']; $csrfOptions['csrf_provider'] = $options['csrf_provider'];
} }
$builder->add($options['csrf_field_name'], 'csrf', $csrfOptions) $builder
->setAttribute('csrf_field_name', $options['csrf_field_name']); ->add($options['csrf_field_name'], 'csrf', $csrfOptions)
->setAttribute('csrf_field_name', $options['csrf_field_name'])
;
} }
} }
/**
* Removes CSRF fields from all the form views except the root one.
*
* @param FormView $view The form view
* @param FormInterface $form The form
*/
public function buildViewBottomUp(FormView $view, FormInterface $form) public function buildViewBottomUp(FormView $view, FormInterface $form)
{ {
if ($view->hasParent() && $form->hasAttribute('csrf_field_name')) { if ($view->hasParent() && $form->hasAttribute('csrf_field_name')) {
@ -52,16 +66,22 @@ class FormTypeCsrfExtension extends AbstractTypeExtension
} }
} }
/**
* {@inheritDoc}
*/
public function getDefaultOptions(array $options) public function getDefaultOptions(array $options)
{ {
return array( return array(
'csrf_protection' => $this->enabled, 'csrf_protection' => $this->enabled,
'csrf_field_name' => $this->fieldName, 'csrf_field_name' => $this->fieldName,
'csrf_provider' => null, 'csrf_provider' => null,
'intention' => 'unknown', 'intention' => 'unknown',
); );
} }
/**
* {@inheritDoc}
*/
public function getExtendedType() public function getExtendedType()
{ {
return 'form'; return 'form';

View File

@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Extension\Csrf\Type;
use Symfony\Component\Form\AbstractTypeExtension;
class RepeatedTypeCsrfExtension extends AbstractTypeExtension
{
public function getDefaultOptions(array $options)
{
return array('csrf_protection' => false);
}
public function getExtendedType()
{
return 'repeated';
}
}

View File

@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Extension\Csrf\Type;
use Symfony\Component\Form\AbstractTypeExtension;
class TimeTypeCsrfExtension extends AbstractTypeExtension
{
public function getDefaultOptions(array $options)
{
return array('csrf_protection' => false);
}
public function getExtendedType()
{
return 'time';
}
}

View File

@ -63,6 +63,18 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface
}); });
} }
/**
* @inheritDoc
*/
public function guessMinLength($class, $property)
{
$guesser = $this;
return $this->guess($class, $property, function (Constraint $constraint) use ($guesser) {
return $guesser->guessMinLengthForConstraint($constraint);
});
}
/** /**
* Guesses a field class name for a given constraint * Guesses a field class name for a given constraint
* *
@ -266,6 +278,28 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface
} }
} }
/**
* Guesses a field's minimum length based on the given constraint
*
* @param Constraint $constraint The constraint to guess for
* @return Guess The guess for the minimum length
*/
public function guessMinLengthForConstraint(Constraint $constraint)
{
switch (get_class($constraint)) {
case 'Symfony\Component\Validator\Constraints\MinLength':
return new ValueGuess(
$constraint->limit,
Guess::HIGH_CONFIDENCE
);
case 'Symfony\Component\Validator\Constraints\Min':
return new ValueGuess(
strlen((string)$constraint->limit),
Guess::HIGH_CONFIDENCE
);
}
}
/** /**
* Iterates over the constraints of a property, executes a constraints on * Iterates over the constraints of a property, executes a constraints on
* them and returns the best guess * them and returns the best guess

View File

@ -24,13 +24,6 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
* *
* A form is composed of a validator schema and a widget form schema. * A form is composed of a validator schema and a widget form schema.
* *
* Form also takes care of CSRF protection by default.
*
* 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
* is generated on the fly.
*
* To implement your own form fields, you need to have a thorough understanding * To implement your own form fields, you need to have a thorough understanding
* of the data flow within a form field. A form field stores its data in three * of the data flow within a form field. A form field stores its data in three
* different representations: * different representations:
@ -459,6 +452,8 @@ class Form implements \IteratorAggregate, FormInterface
public function bind($clientData) public function bind($clientData)
{ {
if ($this->readOnly) { if ($this->readOnly) {
$this->bound = true;
return $this; return $this;
} }

View File

@ -305,6 +305,7 @@ class FormFactory implements FormFactoryInterface
$typeGuess = $this->guesser->guessType($class, $property); $typeGuess = $this->guesser->guessType($class, $property);
$maxLengthGuess = $this->guesser->guessMaxLength($class, $property); $maxLengthGuess = $this->guesser->guessMaxLength($class, $property);
$minLengthGuess = $this->guesser->guessMinLength($class, $property);
$requiredGuess = $this->guesser->guessRequired($class, $property); $requiredGuess = $this->guesser->guessRequired($class, $property);
$type = $typeGuess ? $typeGuess->getType() : 'text'; $type = $typeGuess ? $typeGuess->getType() : 'text';
@ -313,6 +314,14 @@ class FormFactory implements FormFactoryInterface
$options = array_merge(array('max_length' => $maxLengthGuess->getValue()), $options); $options = array_merge(array('max_length' => $maxLengthGuess->getValue()), $options);
} }
if ($minLengthGuess) {
if ($maxLengthGuess) {
$options = array_merge(array('pattern' => '.{'.$minLengthGuess->getValue().','.$maxLengthGuess->getValue().'}'), $options);
} else {
$options = array_merge(array('pattern' => '.{'.$minLengthGuess->getValue().',}'), $options);
}
}
if ($requiredGuess) { if ($requiredGuess) {
$options = array_merge(array('required' => $requiredGuess->getValue()), $options); $options = array_merge(array('required' => $requiredGuess->getValue()), $options);
} }

View File

@ -61,6 +61,13 @@ class FormTypeGuesserChain implements FormTypeGuesserInterface
}); });
} }
public function guessMinLength($class, $property)
{
return $this->guess(function ($guesser) use ($class, $property) {
return $guesser->guessMinLength($class, $property);
});
}
/** /**
* Executes a closure for each guesser and returns the best guess from the * Executes a closure for each guesser and returns the best guess from the
* return values * return values

View File

@ -42,4 +42,14 @@ interface FormTypeGuesserInterface
* @return Guess A guess for the field's maximum length * @return Guess A guess for the field's maximum length
*/ */
function guessMaxLength($class, $property); function guessMaxLength($class, $property);
/**
* Returns a guess about the field's minimum length
*
* @param string $class The fully qualified class name
* @param string $property The name of the property to guess for
* @return Guess A guess for the field's minimum length
*/
function guessMinLength($class, $property);
} }

View File

@ -142,7 +142,7 @@ class PropertyPath implements \IteratorAggregate
*/ */
public function isProperty($index) public function isProperty($index)
{ {
return !$this->isIndex($index); return !$this->isIndex[$index];
} }
/** /**
@ -186,7 +186,25 @@ class PropertyPath implements \IteratorAggregate
*/ */
public function getValue($objectOrArray) public function getValue($objectOrArray)
{ {
return $this->readPropertyPath($objectOrArray, 0); for ($i = 0; $i < $this->length; ++$i) {
if (is_object($objectOrArray)) {
$value = $this->readProperty($objectOrArray, $i);
// arrays need to be treated separately (due to PHP bug?)
// http://bugs.php.net/bug.php?id=52133
} else if (is_array($objectOrArray)){
$property = $this->elements[$i];
if (!array_key_exists($property, $objectOrArray)) {
$objectOrArray[$property] = $i + 1 < $this->length ? array() : null;
}
$value =& $objectOrArray[$property];
} else {
throw new UnexpectedTypeException($objectOrArray, 'object or array');
}
$objectOrArray =& $value;
}
return $value;
} }
/** /**
@ -219,78 +237,30 @@ class PropertyPath implements \IteratorAggregate
*/ */
public function setValue(&$objectOrArray, $value) public function setValue(&$objectOrArray, $value)
{ {
$this->writePropertyPath($objectOrArray, 0, $value); for ($i = 0, $l = $this->length - 1; $i < $l; ++$i) {
}
/**
* Recursive implementation of getValue()
*
* @param object|array $objectOrArray The object or array to traverse
* @param integer $currentIndex The current index in the property path
* @return mixed The value at the end of the path
*/
protected function readPropertyPath(&$objectOrArray, $currentIndex)
{
if (!is_object($objectOrArray) && !is_array($objectOrArray)) {
throw new UnexpectedTypeException($objectOrArray, 'object or array');
}
$property = $this->elements[$currentIndex];
if (is_object($objectOrArray)) {
$value = $this->readProperty($objectOrArray, $currentIndex);
// arrays need to be treated separately (due to PHP bug?)
// http://bugs.php.net/bug.php?id=52133
} else {
if (!array_key_exists($property, $objectOrArray)) {
$objectOrArray[$property] = $currentIndex + 1 < $this->length ? array() : null;
}
$value =& $objectOrArray[$property];
}
++$currentIndex;
if ($currentIndex < $this->length) {
return $this->readPropertyPath($value, $currentIndex);
}
return $value;
}
/**
* Recursive implementation of setValue()
*
* @param object|array $objectOrArray The object or array to traverse
* @param integer $currentIndex The current index in the property path
* @param mixed $value The value to set at the end of the
* property path
*/
protected function writePropertyPath(&$objectOrArray, $currentIndex, $value)
{
if (!is_object($objectOrArray) && !is_array($objectOrArray)) {
throw new UnexpectedTypeException($objectOrArray, 'object or array');
}
$property = $this->elements[$currentIndex];
if ($currentIndex + 1 < $this->length) {
if (is_object($objectOrArray)) { if (is_object($objectOrArray)) {
$nestedObject = $this->readProperty($objectOrArray, $currentIndex); $nestedObject = $this->readProperty($objectOrArray, $i);
// arrays need to be treated separately (due to PHP bug?) // arrays need to be treated separately (due to PHP bug?)
// http://bugs.php.net/bug.php?id=52133 // http://bugs.php.net/bug.php?id=52133
} else { } else if (is_array($objectOrArray)) {
$property = $this->elements[$i];
if (!array_key_exists($property, $objectOrArray)) { if (!array_key_exists($property, $objectOrArray)) {
$objectOrArray[$property] = array(); $objectOrArray[$property] = array();
} }
$nestedObject =& $objectOrArray[$property]; $nestedObject =& $objectOrArray[$property];
} else {
throw new UnexpectedTypeException($objectOrArray, 'object or array');
} }
$this->writePropertyPath($nestedObject, $currentIndex + 1, $value); $objectOrArray =& $nestedObject;
} else {
$this->writeProperty($objectOrArray, $currentIndex, $value);
} }
if (!is_object($objectOrArray) && !is_array($objectOrArray)) {
throw new UnexpectedTypeException($objectOrArray, 'object or array');
}
$this->writeProperty($objectOrArray, $i, $value);
} }
/** /**
@ -311,9 +281,10 @@ class PropertyPath implements \IteratorAggregate
return $object[$property]; return $object[$property];
} else { } else {
$camelProp = $this->camelize($property);
$reflClass = new \ReflectionClass($object); $reflClass = new \ReflectionClass($object);
$getter = 'get'.$this->camelize($property); $getter = 'get'.$camelProp;
$isser = 'is'.$this->camelize($property); $isser = 'is'.$camelProp;
if ($reflClass->hasMethod($getter)) { if ($reflClass->hasMethod($getter)) {
if (!$reflClass->getMethod($getter)->isPublic()) { if (!$reflClass->getMethod($getter)->isPublic()) {

View File

@ -0,0 +1,45 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Stub implementation for the intl_is_failure function of the intl extension
*
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
* @param integer $errorCode The error code returned by intl_get_error_code()
* @return Boolean Whether the error code indicates an error
* @see Symfony\Component\Locale\Stub\StubIntl::isFailure
*/
function intl_is_failure($errorCode) {
return \Symfony\Component\Locale\Stub\StubIntl::isFailure($errorCode);
}
/**
* Stub implementation for the intl_get_error_code function of the intl extension
*
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
* @return Boolean The error code of the last intl function call or
* StubIntl::U_ZERO_ERROR if no error occurred
* @see Symfony\Component\Locale\Stub\StubIntl::getErrorCode
*/
function intl_get_error_code() {
return \Symfony\Component\Locale\Stub\StubIntl::getErrorCode();
}
/**
* Stub implementation for the intl_get_error_code function of the intl extension
*
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
* @return Boolean The error message of the last intl function call or
* "U_ZERO_ERROR" if no error occurred
* @see Symfony\Component\Locale\Stub\StubIntl::getErrorMessage
*/
function intl_get_error_message() {
return \Symfony\Component\Locale\Stub\StubIntl::getErrorMessage();
}

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Locale\Stub\DateFormat; namespace Symfony\Component\Locale\Stub\DateFormat;
use Symfony\Component\Locale\Exception\NotImplementedException; use Symfony\Component\Locale\Exception\NotImplementedException;
use Symfony\Component\Locale\Stub\StubIntl;
use Symfony\Component\Locale\Stub\DateFormat\MonthTransformer; use Symfony\Component\Locale\Stub\DateFormat\MonthTransformer;
/** /**
@ -275,6 +276,8 @@ class FullTransformer
// If month is false, return immediately (intl behavior) // If month is false, return immediately (intl behavior)
if (false === $month) { if (false === $month) {
StubIntl::setErrorCode(StubIntl::U_PARSE_ERROR);
return false; return false;
} }

View File

@ -0,0 +1,112 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Locale\Stub;
/**
* Provides fake static versions of the global functions in the intl extension
*
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
*/
abstract class StubIntl
{
/**
* Indicates that no error occurred
* @var integer
*/
const U_ZERO_ERROR = 0;
/**
* Indicates that an invalid argument was passed
* @var integer
*/
const U_ILLEGAL_ARGUMENT_ERROR = 1;
/**
* Indicates that the parse() operation failed
* @var integer
*/
const U_PARSE_ERROR = 9;
/**
* All known error codes
* @var array
*/
private static $errorCodes = array(
self::U_ZERO_ERROR,
self::U_ILLEGAL_ARGUMENT_ERROR,
self::U_PARSE_ERROR,
);
/**
* The error messages of all known error codes
* @var array
*/
private static $errorMessages = array(
self::U_ZERO_ERROR => 'U_ZERO_ERROR',
self::U_ILLEGAL_ARGUMENT_ERROR => 'datefmt_format: takes either an array or an integer timestamp value : U_ILLEGAL_ARGUMENT_ERROR',
self::U_PARSE_ERROR => 'Date parsing failed: U_PARSE_ERROR',
);
/**
* The error code of the last operation
* @var integer
*/
private static $errorCode = self::U_ZERO_ERROR;
/**
* Returns whether the error code indicates a failure
*
* @param integer $errorCode The error code returned by StubIntl::getErrorCode()
* @return Boolean
*/
public static function isFailure($errorCode) {
return in_array($errorCode, static::$errorCodes, true)
&& $errorCode !== self::U_ZERO_ERROR;
}
/**
* Returns the error code of the last operation
*
* Returns StubIntl::U_ZERO_ERROR if no error occurred.
*
* @return integer
*/
public static function getErrorCode() {
return static::$errorCode;
}
/**
* Returns the error message of the last operation
*
* Returns "U_ZERO_ERROR" if no error occurred.
*
* @return string
*/
public static function getErrorMessage() {
return static::$errorMessages[static::$errorCode];
}
/**
* Sets the current error code
*
* @param integer $code One of the error constants in this class
* @throws \InvalidArgumentException If the code is not one of the error
* constants in this class
*/
public static function setErrorCode($code) {
if (!isset(static::$errorMessages[$code])) {
throw new \InvalidArgumentException(sprintf('No such error code: "%s"', $code));
}
static::$errorCode = $code;
}
}

View File

@ -160,10 +160,18 @@ class StubIntlDateFormatter
*/ */
public function format($timestamp) public function format($timestamp)
{ {
if (!is_int($timestamp)) { // intl allows timestamps to be passed as arrays - we don't
if (is_array($timestamp)) {
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'timestamp', $timestamp, 'Only integer unix timestamps are supported'); throw new MethodArgumentValueNotImplementedException(__METHOD__, 'timestamp', $timestamp, 'Only integer unix timestamps are supported');
} }
if (!is_int($timestamp)) {
// behave like the intl extension
StubIntl::setErrorCode(StubIntl::U_ILLEGAL_ARGUMENT_ERROR);
return false;
}
$transformer = new FullTransformer($this->getPattern(), $this->getTimeZoneId()); $transformer = new FullTransformer($this->getPattern(), $this->getTimeZoneId());
$formatted = $transformer->format($this->createDateTime($timestamp)); $formatted = $transformer->format($this->createDateTime($timestamp));
@ -311,6 +319,8 @@ class StubIntlDateFormatter
throw new MethodArgumentNotImplementedException(__METHOD__, 'position'); throw new MethodArgumentNotImplementedException(__METHOD__, 'position');
} }
StubIntl::setErrorCode(StubIntl::U_ZERO_ERROR);
$dateTime = $this->createDateTime(0); $dateTime = $this->createDateTime(0);
$transformer = new FullTransformer($this->getPattern(), $this->getTimeZoneId()); $transformer = new FullTransformer($this->getPattern(), $this->getTimeZoneId());
return $transformer->parse($dateTime, $value); return $transformer->parse($dateTime, $value);

View File

@ -32,7 +32,7 @@ class TypeValidator extends ConstraintValidator
} }
$this->setMessage($constraint->message, array( $this->setMessage($constraint->message, array(
'{{ value }}' => $value, '{{ value }}' => is_object($value) ? get_class($value) : (string)$value,
'{{ type }}' => $constraint->type, '{{ type }}' => $constraint->type,
)); ));

View File

@ -939,6 +939,22 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
); );
} }
public function testSearch()
{
$form = $this->factory->createNamed('search', 'na&me', 'foo&bar', array(
'property_path' => 'name',
));
$this->assertWidgetMatchesXpath($form->createView(), array(),
'/input
[@type="search"]
[@name="na&me"]
[@value="foo&bar"]
[not(@maxlength)]
'
);
}
public function testTime() public function testTime()
{ {
$form = $this->factory->createNamed('time', 'na&me', '04:05:06', array( $form = $this->factory->createNamed('time', 'na&me', '04:05:06', array(

View File

@ -50,16 +50,16 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase
$this->factory->expects($this->at(0)) $this->factory->expects($this->at(0))
->method('createNamed') ->method('createNamed')
->with('text', 1, null, array('property_path' => '[1]')) ->with('text', 1, null, array('property_path' => '[1]', 'max_length' => 10))
->will($this->returnValue($this->getForm('1'))); ->will($this->returnValue($this->getForm('1')));
$this->factory->expects($this->at(1)) $this->factory->expects($this->at(1))
->method('createNamed') ->method('createNamed')
->with('text', 2, null, array('property_path' => '[2]')) ->with('text', 2, null, array('property_path' => '[2]', 'max_length' => 10))
->will($this->returnValue($this->getForm('2'))); ->will($this->returnValue($this->getForm('2')));
$data = array(1 => 'string', 2 => 'string'); $data = array(1 => 'string', 2 => 'string');
$event = new DataEvent($this->form, $data); $event = new DataEvent($this->form, $data);
$listener = new ResizeFormListener($this->factory, 'text', false, false); $listener = new ResizeFormListener($this->factory, 'text', array('max_length' => '10'), false, false);
$listener->preSetData($event); $listener->preSetData($event);
$this->assertFalse($this->form->has('0')); $this->assertFalse($this->form->has('0'));
@ -73,7 +73,7 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase
$data = array(); $data = array();
$event = new DataEvent($this->form, $data); $event = new DataEvent($this->form, $data);
$listener = new ResizeFormListener($this->factory, 'text', false, false); $listener = new ResizeFormListener($this->factory, 'text', array(), false, false);
$listener->preSetData($event); $listener->preSetData($event);
$this->assertFalse($this->form->has('$$name$$')); $this->assertFalse($this->form->has('$$name$$'));
@ -85,7 +85,7 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase
$data = array(); $data = array();
$event = new DataEvent($this->form, $data); $event = new DataEvent($this->form, $data);
$listener = new ResizeFormListener($this->factory, 'text', true, false); $listener = new ResizeFormListener($this->factory, 'text', array(), true, false);
$listener->preSetData($event); $listener->preSetData($event);
$this->assertTrue($this->form->has('$$name$$')); $this->assertTrue($this->form->has('$$name$$'));
@ -98,7 +98,7 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase
{ {
$data = 'no array or traversable'; $data = 'no array or traversable';
$event = new DataEvent($this->form, $data); $event = new DataEvent($this->form, $data);
$listener = new ResizeFormListener($this->factory, 'text', false, false); $listener = new ResizeFormListener($this->factory, 'text', array(), false, false);
$listener->preSetData($event); $listener->preSetData($event);
} }
@ -108,7 +108,7 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase
$data = null; $data = null;
$event = new DataEvent($this->form, $data); $event = new DataEvent($this->form, $data);
$listener = new ResizeFormListener($this->factory, 'text', false, false); $listener = new ResizeFormListener($this->factory, 'text', array(), false, false);
$listener->preSetData($event); $listener->preSetData($event);
} }
@ -118,12 +118,12 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase
$this->factory->expects($this->once()) $this->factory->expects($this->once())
->method('createNamed') ->method('createNamed')
->with('text', 1, null, array('property_path' => '[1]')) ->with('text', 1, null, array('property_path' => '[1]', 'max_length' => 10))
->will($this->returnValue($this->getForm('1'))); ->will($this->returnValue($this->getForm('1')));
$data = array(0 => 'string', 1 => 'string'); $data = array(0 => 'string', 1 => 'string');
$event = new DataEvent($this->form, $data); $event = new DataEvent($this->form, $data);
$listener = new ResizeFormListener($this->factory, 'text', true, false); $listener = new ResizeFormListener($this->factory, 'text', array('max_length' => 10), true, false);
$listener->preBind($event); $listener->preBind($event);
$this->assertTrue($this->form->has('0')); $this->assertTrue($this->form->has('0'));
@ -137,7 +137,7 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase
$data = array(0 => 'string'); $data = array(0 => 'string');
$event = new DataEvent($this->form, $data); $event = new DataEvent($this->form, $data);
$listener = new ResizeFormListener($this->factory, 'text', false, true); $listener = new ResizeFormListener($this->factory, 'text', array(), false, true);
$listener->preBind($event); $listener->preBind($event);
$this->assertTrue($this->form->has('0')); $this->assertTrue($this->form->has('0'));
@ -151,7 +151,7 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase
$data = array(); $data = array();
$event = new DataEvent($this->form, $data); $event = new DataEvent($this->form, $data);
$listener = new ResizeFormListener($this->factory, 'text', false, true); $listener = new ResizeFormListener($this->factory, 'text', array(), false, true);
$listener->preBind($event); $listener->preBind($event);
$this->assertFalse($this->form->has('0')); $this->assertFalse($this->form->has('0'));
@ -164,7 +164,7 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase
$data = array(0 => 'string', 2 => 'string'); $data = array(0 => 'string', 2 => 'string');
$event = new DataEvent($this->form, $data); $event = new DataEvent($this->form, $data);
$listener = new ResizeFormListener($this->factory, 'text', false, false); $listener = new ResizeFormListener($this->factory, 'text', array(), false, false);
$listener->preBind($event); $listener->preBind($event);
$this->assertTrue($this->form->has('0')); $this->assertTrue($this->form->has('0'));
@ -179,7 +179,7 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase
{ {
$data = 'no array or traversable'; $data = 'no array or traversable';
$event = new DataEvent($this->form, $data); $event = new DataEvent($this->form, $data);
$listener = new ResizeFormListener($this->factory, 'text', false, false); $listener = new ResizeFormListener($this->factory, 'text', array(), false, false);
$listener->preBind($event); $listener->preBind($event);
} }
@ -189,7 +189,7 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase
$data = null; $data = null;
$event = new DataEvent($this->form, $data); $event = new DataEvent($this->form, $data);
$listener = new ResizeFormListener($this->factory, 'text', false, true); $listener = new ResizeFormListener($this->factory, 'text', array(), false, true);
$listener->preBind($event); $listener->preBind($event);
$this->assertFalse($this->form->has('1')); $this->assertFalse($this->form->has('1'));
@ -202,7 +202,7 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase
$data = ''; $data = '';
$event = new DataEvent($this->form, $data); $event = new DataEvent($this->form, $data);
$listener = new ResizeFormListener($this->factory, 'text', false, true); $listener = new ResizeFormListener($this->factory, 'text', array(), false, true);
$listener->preBind($event); $listener->preBind($event);
$this->assertFalse($this->form->has('1')); $this->assertFalse($this->form->has('1'));
@ -214,7 +214,7 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase
$data = array(0 => 'first', 1 => 'second', 2 => 'third'); $data = array(0 => 'first', 1 => 'second', 2 => 'third');
$event = new FilterDataEvent($this->form, $data); $event = new FilterDataEvent($this->form, $data);
$listener = new ResizeFormListener($this->factory, 'text', false, true); $listener = new ResizeFormListener($this->factory, 'text', array(), false, true);
$listener->onBindNormData($event); $listener->onBindNormData($event);
$this->assertEquals(array(1 => 'second'), $event->getData()); $this->assertEquals(array(1 => 'second'), $event->getData());
@ -226,7 +226,7 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase
$data = array(0 => 'first', 1 => 'second', 2 => 'third'); $data = array(0 => 'first', 1 => 'second', 2 => 'third');
$event = new FilterDataEvent($this->form, $data); $event = new FilterDataEvent($this->form, $data);
$listener = new ResizeFormListener($this->factory, 'text', false, false); $listener = new ResizeFormListener($this->factory, 'text', array(), false, false);
$listener->onBindNormData($event); $listener->onBindNormData($event);
$this->assertEquals($data, $event->getData()); $this->assertEquals($data, $event->getData());
@ -239,7 +239,7 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase
{ {
$data = 'no array or traversable'; $data = 'no array or traversable';
$event = new FilterDataEvent($this->form, $data); $event = new FilterDataEvent($this->form, $data);
$listener = new ResizeFormListener($this->factory, 'text', false, false); $listener = new ResizeFormListener($this->factory, 'text', array(), false, false);
$listener->onBindNormData($event); $listener->onBindNormData($event);
} }
@ -249,7 +249,7 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase
$data = null; $data = null;
$event = new FilterDataEvent($this->form, $data); $event = new FilterDataEvent($this->form, $data);
$listener = new ResizeFormListener($this->factory, 'text', false, true); $listener = new ResizeFormListener($this->factory, 'text', array(), false, true);
$listener->onBindNormData($event); $listener->onBindNormData($event);
$this->assertEquals(array(), $event->getData()); $this->assertEquals(array(), $event->getData());

View File

@ -312,7 +312,7 @@ class ChoiceTypeTest extends TypeTestCase
$this->assertSame(array('b' => 'B', 'd' => 'D'), $view->get('preferred_choices')); $this->assertSame(array('b' => 'B', 'd' => 'D'), $view->get('preferred_choices'));
} }
public function testAdjustNameForMultipleNonExpanded() public function testAdjustFullNameForMultipleNonExpanded()
{ {
$form = $this->factory->createNamed('choice', 'name', null, array( $form = $this->factory->createNamed('choice', 'name', null, array(
'multiple' => true, 'multiple' => true,
@ -321,6 +321,6 @@ class ChoiceTypeTest extends TypeTestCase
)); ));
$view = $form->createView(); $view = $form->createView();
$this->assertSame('name[]', $view->get('name')); $this->assertSame('name[]', $view->get('full_name'));
} }
} }

View File

@ -29,6 +29,9 @@ class CollectionFormTest extends TypeTestCase
{ {
$form = $this->factory->create('collection', null, array( $form = $this->factory->create('collection', null, array(
'type' => 'field', 'type' => 'field',
'options' => array(
'max_length' => 20,
),
)); ));
$form->setData(array('foo@foo.com', 'foo@bar.com')); $form->setData(array('foo@foo.com', 'foo@bar.com'));
@ -37,18 +40,24 @@ class CollectionFormTest extends TypeTestCase
$this->assertEquals(2, count($form)); $this->assertEquals(2, count($form));
$this->assertEquals('foo@foo.com', $form[0]->getData()); $this->assertEquals('foo@foo.com', $form[0]->getData());
$this->assertEquals('foo@bar.com', $form[1]->getData()); $this->assertEquals('foo@bar.com', $form[1]->getData());
$this->assertEquals(20, $form[0]->getAttribute('max_length'));
$this->assertEquals(20, $form[1]->getAttribute('max_length'));
$form->setData(array('foo@baz.com')); $form->setData(array('foo@baz.com'));
$this->assertTrue($form[0] instanceof Form); $this->assertTrue($form[0] instanceof Form);
$this->assertFalse(isset($form[1])); $this->assertFalse(isset($form[1]));
$this->assertEquals(1, count($form)); $this->assertEquals(1, count($form));
$this->assertEquals('foo@baz.com', $form[0]->getData()); $this->assertEquals('foo@baz.com', $form[0]->getData());
$this->assertEquals(20, $form[0]->getAttribute('max_length'));
} }
public function testSetDataAddsPrototypeIfAllowAdd() public function testSetDataAddsPrototypeIfAllowAdd()
{ {
$form = $this->factory->create('collection', null, array( $form = $this->factory->create('collection', null, array(
'type' => 'field', 'type' => 'field',
'options' => array(
'max_length' => 20,
),
'allow_add' => true, 'allow_add' => true,
'prototype' => true, 'prototype' => true,
)); ));
@ -57,12 +66,14 @@ class CollectionFormTest extends TypeTestCase
$this->assertTrue($form[0] instanceof Form); $this->assertTrue($form[0] instanceof Form);
$this->assertTrue($form[1] instanceof Form); $this->assertTrue($form[1] instanceof Form);
$this->assertTrue($form['$$name$$'] instanceof Form); $this->assertTrue($form['$$name$$'] instanceof Form);
$this->assertEquals(20, $form['$$name$$']->getAttribute('max_length'));
$this->assertEquals(3, count($form)); $this->assertEquals(3, count($form));
$form->setData(array('foo@baz.com')); $form->setData(array('foo@baz.com'));
$this->assertTrue($form[0] instanceof Form); $this->assertTrue($form[0] instanceof Form);
$this->assertFalse(isset($form[1])); $this->assertFalse(isset($form[1]));
$this->assertTrue($form['$$name$$'] instanceof Form); $this->assertTrue($form['$$name$$'] instanceof Form);
$this->assertEquals(20, $form['$$name$$']->getAttribute('max_length'));
$this->assertEquals(2, count($form)); $this->assertEquals(2, count($form));
} }

View File

@ -117,6 +117,7 @@ class FieldTypeTest extends TypeTestCase
$this->assertEquals('name', $view->get('id')); $this->assertEquals('name', $view->get('id'));
$this->assertEquals('name', $view->get('name')); $this->assertEquals('name', $view->get('name'));
$this->assertEquals('name', $view->get('full_name'));
} }
public function testPassIdAndNameToViewWithParent() public function testPassIdAndNameToViewWithParent()
@ -126,7 +127,8 @@ class FieldTypeTest extends TypeTestCase
$view = $parent->createView(); $view = $parent->createView();
$this->assertEquals('parent_child', $view['child']->get('id')); $this->assertEquals('parent_child', $view['child']->get('id'));
$this->assertEquals('parent[child]', $view['child']->get('name')); $this->assertEquals('child', $view['child']->get('name'));
$this->assertEquals('parent[child]', $view['child']->get('full_name'));
} }
public function testPassIdAndNameToViewWithGrandParent() public function testPassIdAndNameToViewWithGrandParent()
@ -137,7 +139,8 @@ class FieldTypeTest extends TypeTestCase
$view = $parent->createView(); $view = $parent->createView();
$this->assertEquals('parent_child_grand_child', $view['child']['grand_child']->get('id')); $this->assertEquals('parent_child_grand_child', $view['child']['grand_child']->get('id'));
$this->assertEquals('parent[child][grand_child]', $view['child']['grand_child']->get('name')); $this->assertEquals('grand_child', $view['child']['grand_child']->get('name'));
$this->assertEquals('parent[child][grand_child]', $view['child']['grand_child']->get('full_name'));
} }
public function testPassMaxLengthToView() public function testPassMaxLengthToView()

View File

@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Component\Form\Extension\Core\Type;
require_once __DIR__ . '/TypeTestCase.php';
use Symfony\Component\HttpFoundation\File\UploadedFile;
class FileTypeTest extends TypeTestCase
{
public function testDontPassValueToView()
{
$form = $this->factory->create('file');
$form->bind(array(
'file' => $this->createUploadedFileMock('abcdef', 'original.jpg', true),
));
$view = $form->createView();
$this->assertEquals('', $view['file']->get('value'));
}
private function createUploadedFileMock($name, $originalName, $valid)
{
$file = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile')
->disableOriginalConstructor()
->getMock();
$file->expects($this->any())
->method('getName')
->will($this->returnValue($name));
$file->expects($this->any())
->method('getOriginalName')
->will($this->returnValue($originalName));
$file->expects($this->any())
->method('isValid')
->will($this->returnValue($valid));
return $file;
}
}

View File

@ -21,13 +21,13 @@ class RadioTypeTest extends TypeTestCase
$this->assertEquals('foobar', $view->get('value')); $this->assertEquals('foobar', $view->get('value'));
} }
public function testPassParentNameToView() public function testPassParentFullNameToView()
{ {
$parent = $this->factory->createNamed('field', 'parent'); $parent = $this->factory->createNamed('field', 'parent');
$parent->add($this->factory->createNamed('radio', 'child')); $parent->add($this->factory->createNamed('radio', 'child'));
$view = $parent->createView(); $view = $parent->createView();
$this->assertEquals('parent', $view['child']->get('name')); $this->assertEquals('parent', $view['child']->get('full_name'));
} }
public function testCheckedIfDataTrue() public function testCheckedIfDataTrue()

View File

@ -159,6 +159,7 @@ class FormTest extends \PHPUnit_Framework_TestCase
$form->bind('new'); $form->bind('new');
$this->assertEquals('initial', $form->getData()); $this->assertEquals('initial', $form->getData());
$this->assertTrue($form->isBound());
} }
public function testNeverRequiredIfParentNotRequired() public function testNeverRequiredIfParentNotRequired()
@ -297,6 +298,14 @@ class FormTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($this->form->isValid()); $this->assertTrue($this->form->isValid());
} }
public function testValidIfBoundAndReadOnly()
{
$form = $this->getBuilder()->setReadOnly(true)->getForm();
$form->bind('foobar');
$this->assertTrue($form->isValid());
}
public function testNotValidIfNotBound() public function testNotValidIfNotBound()
{ {
$this->assertFalse($this->form->isValid()); $this->assertFalse($this->form->isValid());

View File

@ -14,6 +14,7 @@ namespace Symfony\Tests\Component\Locale\Stub;
require_once __DIR__.'/../TestCase.php'; require_once __DIR__.'/../TestCase.php';
use Symfony\Component\Locale\Locale; use Symfony\Component\Locale\Locale;
use Symfony\Component\Locale\Stub\StubIntl;
use Symfony\Component\Locale\Stub\StubIntlDateFormatter; use Symfony\Component\Locale\Stub\StubIntlDateFormatter;
use Symfony\Tests\Component\Locale\TestCase as LocaleTestCase; use Symfony\Tests\Component\Locale\TestCase as LocaleTestCase;
@ -55,21 +56,27 @@ class StubIntlDateFormatterTest extends LocaleTestCase
/** /**
* @dataProvider formatProvider * @dataProvider formatProvider
*/ */
public function testFormatStub($pattern, $timestamp, $expected) public function testFormatStub($pattern, $timestamp, $expected, $errorCode = 0, $errorMessage = 'U_ZERO_ERROR')
{ {
$formatter = $this->createStubFormatter($pattern); $formatter = $this->createStubFormatter($pattern);
$this->assertSame($expected, $formatter->format($timestamp)); $this->assertSame($expected, $formatter->format($timestamp));
$this->assertSame($errorMessage, StubIntl::getErrorMessage());
$this->assertSame($errorCode, StubIntl::getErrorCode());
$this->assertSame($errorCode != 0, StubIntl::isFailure(StubIntl::getErrorCode()));
} }
/** /**
* @dataProvider formatProvider * @dataProvider formatProvider
*/ */
public function testFormatIntl($pattern, $timestamp, $expected) public function testFormatIntl($pattern, $timestamp, $expected, $errorCode = 0, $errorMessage = 'U_ZERO_ERROR')
{ {
$this->skipIfIntlExtensionIsNotLoaded(); $this->skipIfIntlExtensionIsNotLoaded();
$this->skipIfICUVersionIsTooOld(); $this->skipIfICUVersionIsTooOld();
$formatter = $this->createIntlFormatter($pattern); $formatter = $this->createIntlFormatter($pattern);
$this->assertSame($expected, $formatter->format($timestamp)); $this->assertSame($expected, $formatter->format($timestamp));
$this->assertSame($errorMessage, intl_get_error_message());
$this->assertSame($errorCode, intl_get_error_code());
$this->assertSame($errorCode != 0, intl_is_failure(intl_get_error_code()));
} }
public function formatProvider() public function formatProvider()
@ -248,6 +255,10 @@ class StubIntlDateFormatterTest extends LocaleTestCase
array('zzz', 0, 'GMT+00:00'), array('zzz', 0, 'GMT+00:00'),
array('zzzz', 0, 'GMT+00:00'), array('zzzz', 0, 'GMT+00:00'),
array('zzzzz', 0, 'GMT+00:00'), array('zzzzz', 0, 'GMT+00:00'),
/* errors */
array('y-M-d', '0', false, 1, 'datefmt_format: takes either an array or an integer timestamp value : U_ILLEGAL_ARGUMENT_ERROR'),
array('y-M-d', 'foobar', false, 1, 'datefmt_format: takes either an array or an integer timestamp value : U_ILLEGAL_ARGUMENT_ERROR'),
); );
return $formatData; return $formatData;
@ -481,20 +492,26 @@ class StubIntlDateFormatterTest extends LocaleTestCase
/** /**
* @dataProvider parseProvider * @dataProvider parseProvider
*/ */
public function testParseIntl($pattern, $value, $expected) public function testParseIntl($pattern, $value, $expected, $errorCode = 0, $errorMessage = 'U_ZERO_ERROR')
{ {
$this->skipIfIntlExtensionIsNotLoaded(); $this->skipIfIntlExtensionIsNotLoaded();
$formatter = $this->createIntlFormatter($pattern); $formatter = $this->createIntlFormatter($pattern);
$this->assertSame($expected, $formatter->parse($value)); $this->assertSame($expected, $formatter->parse($value));
$this->assertSame($errorMessage, intl_get_error_message());
$this->assertSame($errorCode, intl_get_error_code());
$this->assertSame($errorCode != 0, intl_is_failure(intl_get_error_code()));
} }
/** /**
* @dataProvider parseProvider * @dataProvider parseProvider
*/ */
public function testParseStub($pattern, $value, $expected) public function testParseStub($pattern, $value, $expected, $errorCode = 0, $errorMessage = 'U_ZERO_ERROR')
{ {
$formatter = $this->createStubFormatter($pattern); $formatter = $this->createStubFormatter($pattern);
$this->assertSame($expected, $formatter->parse($value)); $this->assertSame($expected, $formatter->parse($value));
$this->assertSame($errorMessage, StubIntl::getErrorMessage());
$this->assertSame($errorCode, StubIntl::getErrorCode());
$this->assertSame($errorCode != 0, StubIntl::isFailure(StubIntl::getErrorCode()));
} }
public function parseProvider() public function parseProvider()
@ -511,8 +528,8 @@ class StubIntlDateFormatterTest extends LocaleTestCase
array('y-MMMM-d', '1970-January-1', 0), array('y-MMMM-d', '1970-January-1', 0),
// 1 char month // 1 char month
array('y-MMMMM-d', '1970-J-1', false), array('y-MMMMM-d', '1970-J-1', false, 9, 'Date parsing failed: U_PARSE_ERROR'),
array('y-MMMMM-d', '1970-S-1', false), array('y-MMMMM-d', '1970-S-1', false, 9, 'Date parsing failed: U_PARSE_ERROR'),
// standalone months // standalone months
array('y-L-d', '1970-1-1', 0), array('y-L-d', '1970-1-1', 0),
@ -520,8 +537,8 @@ class StubIntlDateFormatterTest extends LocaleTestCase
array('y-LLLL-d', '1970-January-1', 0), array('y-LLLL-d', '1970-January-1', 0),
// standalone 1 char month // standalone 1 char month
array('y-LLLLL-d', '1970-J-1', false), array('y-LLLLL-d', '1970-J-1', false, 9, 'Date parsing failed: U_PARSE_ERROR'),
array('y-LLLLL-d', '1970-S-1', false), array('y-LLLLL-d', '1970-S-1', false, 9, 'Date parsing failed: U_PARSE_ERROR'),
// days // days
array('y-M-d', '1970-1-1', 0), array('y-M-d', '1970-1-1', 0),

View File

@ -13,6 +13,7 @@ namespace Symfony\Tests\Component\Validator\Constraints;
use Symfony\Component\Validator\Constraints\Type; use Symfony\Component\Validator\Constraints\Type;
use Symfony\Component\Validator\Constraints\TypeValidator; use Symfony\Component\Validator\Constraints\TypeValidator;
use Symfony\Component\Validator\ConstraintViolation;
class TypeValidatorTest extends \PHPUnit_Framework_TestCase class TypeValidatorTest extends \PHPUnit_Framework_TestCase
{ {
@ -83,6 +84,22 @@ class TypeValidatorTest extends \PHPUnit_Framework_TestCase
$this->assertFalse($this->validator->isValid($value, $constraint)); $this->assertFalse($this->validator->isValid($value, $constraint));
} }
public function testConstraintViolationCanHandleArrayValue()
{
$constraint = new Type(array('type' => 'string'));
$this->validator->isValid(array(0 => "Test"), $constraint);
$violation = new ConstraintViolation(
'{{ value }}',
$this->validator->getMessageParameters(),
'',
'',
''
);
$this->assertEquals('Array', $violation->getMessage());
}
public function getInvalidValues() public function getInvalidValues()
{ {