Vendors update

Signed-off-by: Polonkai Gergely <polesz@w00d5t0ck.info>
This commit is contained in:
Polonkai Gergely
2012-07-31 09:40:41 +02:00
parent 33b90a5c9f
commit 623b78e939
234 changed files with 3120 additions and 1292 deletions

View File

@@ -64,11 +64,7 @@
<argument /> <!-- filter -->
</service>
<service id="assetic.parameter_bag" class="Symfony\Component\DependencyInjection\ParameterBag\ParameterBag" public="false">
<argument type="service" id="assetic.parameters" />
</service>
<service id="assetic.parameters" class="stdClass" factory-service="service_container" factory-method="getDefaultParameters" public="false" />
<service id="assetic.parameter_bag" class="Symfony\Component\DependencyInjection\ParameterBag\ParameterBag" factory-service="service_container" factory-method="getParameterBag" public="false"/>
<service id="assetic.value_supplier.default" class="%assetic.value_supplier.class%" public="false">
<argument type="service" id="service_container" />

View File

@@ -247,11 +247,10 @@
reasons. It is now not possible anymore to use custom implementations of
`FormBuilderInterface` for specific form types.
If you are in such a situation, you can subclass `FormRegistry` instead and override
`resolveType` to return a custom `ResolvedFormTypeInterface` implementation, within
which you can create your own `FormBuilderInterface` implementation. You should
register this custom registry class under the service name "form.registry" in order
to replace the default implementation.
If you are in such a situation, you can implement a custom `ResolvedFormTypeInterface`
where you create your own `FormBuilderInterface` implementation. You also need to
register a custom `ResolvedFormTypeFactoryInterface` implementation under the service
name "form.resolved_type_factory" in order to replace the default implementation.
* If you previously inherited from `FieldType`, you should now inherit from
`FormType`. You should also set the option `compound` to `false` if your field
@@ -1199,8 +1198,8 @@
}
```
* Core translation messages are changed. Dot is added at the end of each message.
Overwritten core translations should be fixed if any. More info here.
* Core translation messages changed. A dot is added at the end of each message.
Overwritten core translations need to be fixed.
* Collections (arrays or instances of `\Traversable`) in properties
annotated with `Valid` are not traversed recursively by default anymore.
@@ -1353,6 +1352,11 @@
decoded twice before. Note that the `urldecode()` calls have been changed for a
single `rawurldecode()` in order to support `+` for input paths.
* Two new parameters have been added to the DIC: `router.request_context.host`
and `router.request_context.scheme`. You can customize them for your
functional tests or for generating urls with the right host and scheme
when your are in the cli context.
### FrameworkBundle
* session options: lifetime, path, domain, secure, httponly were deprecated.

View File

@@ -312,7 +312,7 @@ class EntityChoiceList extends ObjectChoiceList
*
* Otherwise a new integer is generated.
*
* @param mixed $choice The choice to create an index for
* @param mixed $entity The choice to create an index for
*
* @return integer|string A unique index containing only ASCII letters,
* digits and underscores.
@@ -333,7 +333,7 @@ class EntityChoiceList extends ObjectChoiceList
*
* Otherwise a new integer is generated.
*
* @param mixed $choice The choice to create a value for
* @param mixed $entity The choice to create a value for
*
* @return integer|string A unique value without character limitations.
*/

View File

@@ -186,8 +186,8 @@ class EntityChoiceListTest extends DoctrineOrmTestCase
$this->assertSame(array(1 => $entity1, 2 => $entity2), $choiceList->getChoices());
$this->assertEquals(array(
'group1' => array(1 => new ChoiceView('1', 'Foo')),
'group2' => array(2 => new ChoiceView('2', 'Bar'))
'group1' => array(1 => new ChoiceView($entity1, '1', 'Foo')),
'group2' => array(2 => new ChoiceView($entity2, '2', 'Bar'))
), $choiceList->getRemainingViews());
}
@@ -219,9 +219,9 @@ class EntityChoiceListTest extends DoctrineOrmTestCase
$this->assertEquals(array(1 => $item1, 2 => $item2, 3 => $item3, 4 => $item4), $choiceList->getChoices());
$this->assertEquals(array(
'Group1' => array(1 => new ChoiceView('1', 'Foo'), 2 => new ChoiceView('2', 'Bar')),
'Group2' => array(3 => new ChoiceView('3', 'Baz')),
4 => new ChoiceView('4', 'Boo!')
'Group1' => array(1 => new ChoiceView($item1, '1', 'Foo'), 2 => new ChoiceView($item2, '2', 'Bar')),
'Group2' => array(3 => new ChoiceView($item3, '3', 'Baz')),
4 => new ChoiceView($item4, '4', 'Boo!')
), $choiceList->getRemainingViews());
}

View File

@@ -124,7 +124,7 @@ class EntityTypeTest extends TypeTestCase
'property' => 'name'
));
$this->assertEquals(array(1 => new ChoiceView('1', 'Foo'), 2 => new ChoiceView('2', 'Bar')), $field->createView()->vars['choices']);
$this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')), $field->createView()->vars['choices']);
}
public function testSetDataToUninitializedEntityWithNonRequiredToString()
@@ -140,7 +140,7 @@ class EntityTypeTest extends TypeTestCase
'required' => false,
));
$this->assertEquals(array(1 => new ChoiceView('1', 'Foo'), 2 => new ChoiceView('2', 'Bar')), $field->createView()->vars['choices']);
$this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')), $field->createView()->vars['choices']);
}
public function testSetDataToUninitializedEntityWithNonRequiredQueryBuilder()
@@ -159,7 +159,7 @@ class EntityTypeTest extends TypeTestCase
'query_builder' => $qb
));
$this->assertEquals(array(1 => new ChoiceView('1', 'Foo'), 2 => new ChoiceView('2', 'Bar')), $field->createView()->vars['choices']);
$this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')), $field->createView()->vars['choices']);
}
/**
@@ -503,7 +503,7 @@ class EntityTypeTest extends TypeTestCase
$field->bind('2');
$this->assertEquals(array(1 => new ChoiceView('1', 'Foo'), 2 => new ChoiceView('2', 'Bar')), $field->createView()->vars['choices']);
$this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')), $field->createView()->vars['choices']);
$this->assertTrue($field->isSynchronized());
$this->assertSame($entity2, $field->getData());
$this->assertSame('2', $field->getClientData());
@@ -530,9 +530,9 @@ class EntityTypeTest extends TypeTestCase
$this->assertSame('2', $field->getClientData());
$this->assertEquals(array(
'Group1' => array(1 => new ChoiceView('1', 'Foo'), 2 => new ChoiceView('2', 'Bar')),
'Group2' => array(3 => new ChoiceView('3', 'Baz')),
'4' => new ChoiceView('4', 'Boo!')
'Group1' => array(1 => new ChoiceView($item1, '1', 'Foo'), 2 => new ChoiceView($item2, '2', 'Bar')),
'Group2' => array(3 => new ChoiceView($item3, '3', 'Baz')),
'4' => new ChoiceView($item4, '4', 'Boo!')
), $field->createView()->vars['choices']);
}

View File

@@ -75,6 +75,7 @@ class UniqueValidatorTest extends DoctrineOrmTestCase
;
$refl = $this->getMockBuilder('Doctrine\Common\Reflection\StaticReflectionProperty')
->disableOriginalConstructor()
->setMethods(array('getValue'))
->getMock()
;
$refl

View File

@@ -276,7 +276,7 @@ class ModelChoiceList extends ObjectChoiceList
*
* Otherwise a new integer is generated.
*
* @param mixed $choice The choice to create an index for
* @param mixed $model The choice to create an index for
*
* @return integer|string A unique index containing only ASCII letters,
* digits and underscores.
@@ -297,7 +297,7 @@ class ModelChoiceList extends ObjectChoiceList
*
* Otherwise a new integer is generated.
*
* @param mixed $choice The choice to create a value for
* @param mixed $model The choice to create a value for
*
* @return integer|string A unique value without character limitations.
*/

View File

@@ -86,8 +86,8 @@ class ModelChoiceListTest extends Propel1TestCase
$this->assertSame(array(1 => $item1, 2 => $item2), $choiceList->getChoices());
$this->assertEquals(array(
'group1' => array(1 => new ChoiceView('1', 'Foo')),
'group2' => array(2 => new ChoiceView('2', 'Bar'))
'group1' => array(1 => new ChoiceView($item1, '1', 'Foo')),
'group2' => array(2 => new ChoiceView($item2, '2', 'Bar'))
), $choiceList->getRemainingViews());
}
@@ -113,9 +113,9 @@ class ModelChoiceListTest extends Propel1TestCase
$this->assertEquals(array(1 => $item1, 2 => $item2, 3 => $item3, 4 => $item4), $choiceList->getChoices());
$this->assertEquals(array(
'Group1' => array(1 => new ChoiceView('1', 'Foo'), 2 => new ChoiceView('2', 'Bar')),
'Group2' => array(3 => new ChoiceView('3', 'Baz')),
4 => new ChoiceView('4', 'Boo!')
'Group1' => array(1 => new ChoiceView($item1, '1', 'Foo'), 2 => new ChoiceView($item2, '2', 'Bar')),
'Group2' => array(3 => new ChoiceView($item3, '3', 'Baz')),
4 => new ChoiceView($item4, '4', 'Boo!')
), $choiceList->getRemainingViews());
}

View File

@@ -32,21 +32,71 @@ class SearchAndRenderBlockNode extends \Twig_Node_Expression_Function
$compiler->raw(', \'' . $blockNameSuffix . '\'');
if (isset($arguments[1])) {
$compiler->raw(', ');
// The "label" function allows one extra argument here, the label
if ('label' === $blockNameSuffix) {
if (isset($arguments[2])) {
$compiler->subcompile($arguments[2]);
$compiler->raw(' + ');
// The "label" function expects the label in the second and
// the variables in the third argument
$label = $arguments[1];
$variables = isset($arguments[2]) ? $arguments[2] : null;
$lineno = $label->getLine();
if ($label instanceof \Twig_Node_Expression_Constant) {
// If the label argument is given as a constant, we can either
// strip it away if it is empty, or integrate it into the array
// of variables at compile time.
$labelIsExpression = false;
// Only insert the label into the array if it is not empty
if (!twig_test_empty($label->getAttribute('value'))) {
$originalVariables = $variables;
$variables = new \Twig_Node_Expression_Array(array(), $lineno);
$labelKey = new \Twig_Node_Expression_Constant('label', $lineno);
if (null !== $originalVariables) {
foreach ($originalVariables->getKeyValuePairs() as $pair) {
// Don't copy the original label attribute over if it exists
if ((string) $labelKey !== (string) $pair['key']) {
$variables->addElement($pair['value'], $pair['key']);
}
}
}
// Insert the label argument into the array
$variables->addElement($label, $labelKey);
}
} else {
// The label argument is not a constant, but some kind of
// expression. This expression needs to be evaluated at runtime.
// Depending on the result (whether it is null or not), the
// label in the arguments should take precedence over the label
// in the attributes or not.
$labelIsExpression = true;
}
} else {
// All other functions than "label" expect the variables
// in the second argument
$label = null;
$variables = $arguments[1];
$labelIsExpression = false;
}
if (null !== $variables || $labelIsExpression) {
$compiler->raw(', ');
if (null !== $variables) {
$compiler->subcompile($variables);
}
// Add the label to the variable array
$compiler->raw('array(\'label\' => ');
$compiler->subcompile($arguments[1]);
$compiler->raw(')');
} else {
$compiler->subcompile($arguments[1]);
if ($labelIsExpression) {
if (null !== $variables) {
$compiler->raw(' + ');
}
// Check at runtime whether the label is empty.
// If not, add it to the array at runtime.
$compiler->raw('(twig_test_empty($_label_ = ');
$compiler->subcompile($label);
$compiler->raw(') ? array() : array("label" => $_label_))');
}
}
}
}

View File

@@ -249,7 +249,7 @@
{% block form_row %}
{% spaceless %}
<div>
{{ form_label(form, label|default(null)) }}
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>

View File

@@ -4,7 +4,7 @@
{% spaceless %}
<tr>
<td>
{{ form_label(form, label|default(null)) }}
{{ form_label(form) }}
</td>
<td>
{{ form_errors(form) }}

View File

@@ -134,7 +134,7 @@ class FormExtensionDivLayoutTest extends AbstractDivLayoutTest
*/
public function testIsChoiceSelected($expected, $choice, $value)
{
$choice = new ChoiceView($choice, $choice . ' label');
$choice = new ChoiceView($choice, $choice, $choice . ' label');
$this->assertSame($expected, $this->extension->isSelectedChoice($choice, $value));
}

View File

@@ -0,0 +1,282 @@
<?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\Bridge\Twig\Tests\Node;
use Symfony\Bridge\Twig\Tests\TestCase;
use Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode;
class SearchAndRenderBlockNodeTest extends TestCase
{
protected function setUp()
{
parent::setUp();
if (version_compare(\Twig_Environment::VERSION, '1.5.0', '<')) {
$this->markTestSkipped('Requires Twig version to be at least 1.5.0.');
}
}
public function testCompileWidget()
{
$arguments = new \Twig_Node(array(
new \Twig_Node_Expression_Name('form', 0),
));
$node = new SearchAndRenderBlockNode('form_widget', $arguments, 0);
$compiler = new \Twig_Compiler(new \Twig_Environment());
$this->assertEquals(
sprintf(
'$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'widget\')',
$this->getVariableGetter('form')
),
trim($compiler->compile($node)->getSource())
);
}
public function testCompileWidgetWithVariables()
{
$arguments = new \Twig_Node(array(
new \Twig_Node_Expression_Name('form', 0),
new \Twig_Node_Expression_Array(array(
new \Twig_Node_Expression_Constant('foo', 0),
new \Twig_Node_Expression_Constant('bar', 0),
), 0),
));
$node = new SearchAndRenderBlockNode('form_widget', $arguments, 0);
$compiler = new \Twig_Compiler(new \Twig_Environment());
$this->assertEquals(
sprintf(
'$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'widget\', array("foo" => "bar"))',
$this->getVariableGetter('form')
),
trim($compiler->compile($node)->getSource())
);
}
public function testCompileLabelWithLabel()
{
$arguments = new \Twig_Node(array(
new \Twig_Node_Expression_Name('form', 0),
new \Twig_Node_Expression_Constant('my label', 0),
));
$node = new SearchAndRenderBlockNode('form_label', $arguments, 0);
$compiler = new \Twig_Compiler(new \Twig_Environment());
$this->assertEquals(
sprintf(
'$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\', array("label" => "my label"))',
$this->getVariableGetter('form')
),
trim($compiler->compile($node)->getSource())
);
}
public function testCompileLabelWithNullLabel()
{
$arguments = new \Twig_Node(array(
new \Twig_Node_Expression_Name('form', 0),
new \Twig_Node_Expression_Constant(null, 0),
));
$node = new SearchAndRenderBlockNode('form_label', $arguments, 0);
$compiler = new \Twig_Compiler(new \Twig_Environment());
// "label" => null must not be included in the output!
// Otherwise the default label is overwritten with null.
$this->assertEquals(
sprintf(
'$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\')',
$this->getVariableGetter('form')
),
trim($compiler->compile($node)->getSource())
);
}
public function testCompileLabelWithEmptyStringLabel()
{
$arguments = new \Twig_Node(array(
new \Twig_Node_Expression_Name('form', 0),
new \Twig_Node_Expression_Constant('', 0),
));
$node = new SearchAndRenderBlockNode('form_label', $arguments, 0);
$compiler = new \Twig_Compiler(new \Twig_Environment());
// "label" => null must not be included in the output!
// Otherwise the default label is overwritten with null.
$this->assertEquals(
sprintf(
'$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\')',
$this->getVariableGetter('form')
),
trim($compiler->compile($node)->getSource())
);
}
public function testCompileLabelWithDefaultLabel()
{
$arguments = new \Twig_Node(array(
new \Twig_Node_Expression_Name('form', 0),
));
$node = new SearchAndRenderBlockNode('form_label', $arguments, 0);
$compiler = new \Twig_Compiler(new \Twig_Environment());
$this->assertEquals(
sprintf(
'$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\')',
$this->getVariableGetter('form')
),
trim($compiler->compile($node)->getSource())
);
}
public function testCompileLabelWithAttributes()
{
$arguments = new \Twig_Node(array(
new \Twig_Node_Expression_Name('form', 0),
new \Twig_Node_Expression_Constant(null, 0),
new \Twig_Node_Expression_Array(array(
new \Twig_Node_Expression_Constant('foo', 0),
new \Twig_Node_Expression_Constant('bar', 0),
), 0),
));
$node = new SearchAndRenderBlockNode('form_label', $arguments, 0);
$compiler = new \Twig_Compiler(new \Twig_Environment());
// "label" => null must not be included in the output!
// Otherwise the default label is overwritten with null.
// https://github.com/symfony/symfony/issues/5029
$this->assertEquals(
sprintf(
'$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\', array("foo" => "bar"))',
$this->getVariableGetter('form')
),
trim($compiler->compile($node)->getSource())
);
}
public function testCompileLabelWithLabelAndAttributes()
{
$arguments = new \Twig_Node(array(
new \Twig_Node_Expression_Name('form', 0),
new \Twig_Node_Expression_Constant('value in argument', 0),
new \Twig_Node_Expression_Array(array(
new \Twig_Node_Expression_Constant('foo', 0),
new \Twig_Node_Expression_Constant('bar', 0),
new \Twig_Node_Expression_Constant('label', 0),
new \Twig_Node_Expression_Constant('value in attributes', 0),
), 0),
));
$node = new SearchAndRenderBlockNode('form_label', $arguments, 0);
$compiler = new \Twig_Compiler(new \Twig_Environment());
$this->assertEquals(
sprintf(
'$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\', array("foo" => "bar", "label" => "value in argument"))',
$this->getVariableGetter('form')
),
trim($compiler->compile($node)->getSource())
);
}
public function testCompileLabelWithLabelThatEvaluatesToNull()
{
$arguments = new \Twig_Node(array(
new \Twig_Node_Expression_Name('form', 0),
new \Twig_Node_Expression_Conditional(
// if
new \Twig_Node_Expression_Constant(true, 0),
// then
new \Twig_Node_Expression_Constant(null, 0),
// else
new \Twig_Node_Expression_Constant(null, 0),
0
),
));
$node = new SearchAndRenderBlockNode('form_label', $arguments, 0);
$compiler = new \Twig_Compiler(new \Twig_Environment());
// "label" => null must not be included in the output!
// Otherwise the default label is overwritten with null.
// https://github.com/symfony/symfony/issues/5029
$this->assertEquals(
sprintf(
'$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\', (twig_test_empty($_label_ = ((true) ? (null) : (null))) ? array() : array("label" => $_label_)))',
$this->getVariableGetter('form')
),
trim($compiler->compile($node)->getSource())
);
}
public function testCompileLabelWithLabelThatEvaluatesToNullAndAttributes()
{
$arguments = new \Twig_Node(array(
new \Twig_Node_Expression_Name('form', 0),
new \Twig_Node_Expression_Conditional(
// if
new \Twig_Node_Expression_Constant(true, 0),
// then
new \Twig_Node_Expression_Constant(null, 0),
// else
new \Twig_Node_Expression_Constant(null, 0),
0
),
new \Twig_Node_Expression_Array(array(
new \Twig_Node_Expression_Constant('foo', 0),
new \Twig_Node_Expression_Constant('bar', 0),
new \Twig_Node_Expression_Constant('label', 0),
new \Twig_Node_Expression_Constant('value in attributes', 0),
), 0),
));
$node = new SearchAndRenderBlockNode('form_label', $arguments, 0);
$compiler = new \Twig_Compiler(new \Twig_Environment());
// "label" => null must not be included in the output!
// Otherwise the default label is overwritten with null.
// https://github.com/symfony/symfony/issues/5029
$this->assertEquals(
sprintf(
'$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\', array("foo" => "bar", "label" => "value in attributes") + (twig_test_empty($_label_ = ((true) ? (null) : (null))) ? array() : array("label" => $_label_)))',
$this->getVariableGetter('form')
),
trim($compiler->compile($node)->getSource())
);
}
protected function getVariableGetter($name)
{
if (version_compare(phpversion(), '5.4.0RC1', '>=')) {
return sprintf('(isset($context["%s"]) ? $context["%s"] : null)', $name, $name);
}
return sprintf('$this->getContext($context, "%s")', $name);
}
}

View File

@@ -33,7 +33,7 @@ class AssetsInstallCommand extends ContainerAwareCommand
$this
->setName('assets:install')
->setDefinition(array(
new InputArgument('target', InputArgument::REQUIRED, 'The target directory (usually "web")'),
new InputArgument('target', InputArgument::OPTIONAL, 'The target directory', 'web'),
))
->addOption('symlink', null, InputOption::VALUE_NONE, 'Symlinks the assets instead of copying it')
->addOption('relative', null, InputOption::VALUE_NONE, 'Make relative symlinks')

View File

@@ -44,7 +44,7 @@ class RegisterKernelListenersPass implements CompilerPassInterface
}
foreach ($container->findTaggedServiceIds('kernel.event_subscriber') as $id => $attributes) {
// We must assume that the class value has been correcly filled, even if the service is created by a factory
// We must assume that the class value has been correctly filled, even if the service is created by a factory
$class = $container->getDefinition($id)->getClass();
$refClass = new \ReflectionClass($class);

View File

@@ -196,9 +196,10 @@ class HttpKernel extends BaseHttpKernel
*
* This method uses the "_internal" route, which should be available.
*
* @param string $controller A controller name to execute (a string like BlogBundle:Post:index), or a relative URI
* @param array $attributes An array of request attributes
* @param array $query An array of request query parameters
* @param string $controller A controller name to execute (a string like BlogBundle:Post:index), or a relative URI
* @param array $attributes An array of request attributes
* @param array $query An array of request query parameters
* @param boolean $secure
*
* @return string An internal URI
*/
@@ -226,6 +227,7 @@ class HttpKernel extends BaseHttpKernel
* Renders an HInclude tag.
*
* @param string $uri A URI
* @param string $defaultContent Default content
*/
public function renderHIncludeTag($uri, $defaultContent = null)
{

View File

@@ -5,47 +5,45 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="form.extension.class">Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension</parameter>
<parameter key="form.resolved_type_factory.class">Symfony\Component\Form\ResolvedFormTypeFactory</parameter>
<parameter key="form.registry.class">Symfony\Component\Form\FormRegistry</parameter>
<parameter key="form.factory.class">Symfony\Component\Form\FormFactory</parameter>
<parameter key="form.extension.class">Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension</parameter>
<parameter key="form.type_guesser.validator.class">Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser</parameter>
</parameters>
<services>
<!-- ResolvedFormTypeFactory -->
<service id="form.resolved_type_factory" class="%form.resolved_type_factory.class%" />
<!-- FormRegistry -->
<service id="form.registry" class="%form.registry.class%">
<argument type="collection">
<!--
We don't need to be able to add more extensions.
* more types can be registered with the form.type tag
* more type extensions can be registered with the form.type_extension tag
* more type_guessers can be registered with the form.type.type_guesser tag
-->
<argument type="service" id="form.extension" />
</argument>
<argument type="service" id="form.resolved_type_factory" />
</service>
<!-- FormFactory -->
<service id="form.factory" class="%form.factory.class%">
<argument type="service" id="form.registry" />
<argument type="service" id="form.resolved_type_factory" />
</service>
<!-- DependencyInjectionExtension -->
<service id="form.extension" class="%form.extension.class%" public="false">
<argument type="service" id="service_container" />
<!--
All services with tag "form.type" are inserted here by
InitFormsPass
-->
<!-- All services with tag "form.type" are inserted here by FormPass -->
<argument type="collection" />
<!--
All services with tag "form.type_extension" are inserted here by
InitFormsPass
-->
<!-- All services with tag "form.type_extension" are inserted here by FormPass -->
<argument type="collection" />
<!--
All services with tag "form.type_guesser" are inserted here by
InitFormsPass
-->
<!-- All services with tag "form.type_guesser" are inserted here by FormPass -->
<argument type="collection" />
</service>
@@ -138,6 +136,11 @@
<tag name="form.type" alias="url" />
</service>
<!-- FormTypeHttpFoundationExtension -->
<service id="form.type_extension.form.http_foundation" class="Symfony\Component\Form\Extension\HttpFoundation\Type\FormTypeHttpFoundationExtension">
<tag name="form.type_extension" alias="form" />
</service>
<!-- FormTypeValidatorExtension -->
<service id="form.type_extension.form.validator" class="Symfony\Component\Form\Extension\Validator\Type\FormTypeValidatorExtension">
<tag name="form.type_extension" alias="form" />

View File

@@ -22,6 +22,8 @@
<parameter key="router.options.matcher.cache_class">%kernel.name%%kernel.environment%UrlMatcher</parameter>
<parameter key="router.options.generator.cache_class">%kernel.name%%kernel.environment%UrlGenerator</parameter>
<parameter key="router_listener.class">Symfony\Component\HttpKernel\EventListener\RouterListener</parameter>
<parameter key="router.request_context.host">localhost</parameter>
<parameter key="router.request_context.scheme">http</parameter>
</parameters>
<services>
@@ -74,8 +76,8 @@
<service id="router.request_context" class="%router.request_context.class%" public="false">
<argument></argument>
<argument>GET</argument>
<argument>localhost</argument>
<argument>http</argument>
<argument>%router.request_context.host%</argument>
<argument>%router.request_context.scheme%</argument>
<argument>%request_listener.http_port%</argument>
<argument>%request_listener.https_port%</argument>
</service>

View File

@@ -1,5 +1,5 @@
<div>
<?php echo $view['form']->label($form, isset($label) ? $label : null) ?>
<?php echo $view['form']->label($form) ?>
<?php echo $view['form']->errors($form) ?>
<?php echo $view['form']->widget($form) ?>
</div>

View File

@@ -1,6 +1,6 @@
<tr>
<td>
<?php echo $view['form']->label($form, isset($label) ? $label : null) ?>
<?php echo $view['form']->label($form) ?>
</td>
<td>
<?php echo $view['form']->errors($form) ?>

View File

@@ -125,7 +125,7 @@ class FormHelper extends Helper
*/
public function label(FormView $view, $label = null, array $variables = array())
{
if ($label !== null) {
if (null !== $label) {
$variables += array('label' => $label);
}

View File

@@ -224,6 +224,25 @@ class ClientTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->click() clicks on links');
}
public function testClickForm()
{
if (!class_exists('Symfony\Component\DomCrawler\Crawler')) {
$this->markTestSkipped('The "DomCrawler" component is not available');
}
if (!class_exists('Symfony\Component\CssSelector\CssSelector')) {
$this->markTestSkipped('The "CssSelector" component is not available');
}
$client = new TestClient();
$client->setNextResponse(new Response('<html><form action="/foo"><input type="submit" /></form></html>'));
$crawler = $client->request('GET', 'http://www.example.com/foo/foobar');
$client->click($crawler->filter('input')->form());
$this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->click() Form submit forms');
}
public function testSubmit()
{
if (!class_exists('Symfony\Component\DomCrawler\Crawler')) {

View File

@@ -144,6 +144,29 @@ class CookieJarTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array('foo' => 'bar%3Dbaz'), $cookieJar->allRawValues('/'));
}
public function testCookieExpireWithSameNameButDifferentPaths()
{
$cookieJar = new CookieJar();
$cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/foo'));
$cookieJar->set($cookie2 = new Cookie('foo', 'bar2', null, '/bar'));
$cookieJar->expire('foo', '/foo');
$this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired');
$this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/')));
$this->assertEquals(array(), $cookieJar->allValues('http://example.com/foo'));
$this->assertEquals(array('foo' => 'bar2'), $cookieJar->allValues('http://example.com/bar'));
}
public function testCookieExpireWithNullPaths()
{
$cookieJar = new CookieJar();
$cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/'));
$cookieJar->expire('foo', null);
$this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired');
$this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/')));
}
public function testCookieWithSameNameButDifferentPaths()
{
$cookieJar = new CookieJar();

View File

@@ -58,6 +58,7 @@ class CookieTest extends \PHPUnit_Framework_TestCase
array('foo=bar; expires=Fri, 31 Jul 2020 08:49:37 GMT'),
array('foo=bar; expires=Friday, 31-Jul-20 08:49:37 GMT'),
array('foo=bar; expires=Fri Jul 31 08:49:37 2020'),
array('foo=bar; expires=\'Fri Jul 31 08:49:37 2020\''),
);
}

View File

@@ -69,7 +69,7 @@ class ArrayNodeDefinitionTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException Symfony\Component\Config\Definition\Exception\InvalidDefinitionException
*/
public function testPrototypeNodesCantHaveADefaultValueWhenUsingDefaulChildren()
public function testPrototypeNodesCantHaveADefaultValueWhenUsingDefaultChildren()
{
$node = new ArrayNodeDefinition('root');
$node

View File

@@ -200,9 +200,9 @@ class ExprBuilderTest extends \PHPUnit_Framework_TestCase
/**
* Assert that the given test builder, will return the given value
* @param mixed $value The value to test
* @param TreeBuilder $test The tree builder to finalize
* @param mixed $config The config values that new to be finalized
* @param mixed $value The value to test
* @param TreeBuilder $treeBuilder The tree builder to finalize
* @param mixed $config The config values that new to be finalized
*/
protected function assertFinalizedValueIs($value, $treeBuilder, $config=null)
{

View File

@@ -90,7 +90,7 @@ class TreeBuilderTest extends \PHPUnit_Framework_TestCase
->end();
}
public function testDefinitionInfoGetsTransferedToNode()
public function testDefinitionInfoGetsTransferredToNode()
{
$builder = new TreeBuilder();
@@ -107,7 +107,7 @@ class TreeBuilderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('child info', $children['child']->getInfo());
}
public function testDefinitionExampleGetsTransferedToNode()
public function testDefinitionExampleGetsTransferredToNode()
{
$builder = new TreeBuilder();

View File

@@ -1041,7 +1041,7 @@ class Application
* if nothing is found in $collection, try in $abbrevs
*
* @param string $name The string
* @param array|Traversable $collection The collecion
* @param array|Traversable $collection The collection
* @param array $abbrevs The abbreviations
* @param Closure|string|array $callback The callable to transform collection item before comparison
*

View File

@@ -50,7 +50,7 @@ interface OutputFormatterStyleInterface
/**
* Unsets some specific style option.
*
* @param string $option Theoption name
* @param string $option The option name
*/
public function unsetOption($option);

View File

@@ -224,7 +224,7 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
$application->add(new \Foo2Command());
try {
$application->find($commandName = 'Unknow command');
$application->find($commandName = 'Unknown command');
$this->fail('->find() throws an \InvalidArgumentException if command does not exist');
} catch (\Exception $e) {
$this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if command does not exist');
@@ -264,11 +264,11 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
$application->add(new \foo3Command());
try {
$application->find('Unknow-namespace:Unknow-command');
$application->find('Unknown-namespace:Unknown-command');
$this->fail('->find() throws an \InvalidArgumentException if namespace does not exist');
} catch (\Exception $e) {
$this->assertInstanceOf('\InvalidArgumentException', $e, '->find() throws an \InvalidArgumentException if namespace does not exist');
$this->assertEquals('There are no commands defined in the "Unknow-namespace" namespace.', $e->getMessage(), '->find() throws an \InvalidArgumentException if namespace does not exist, without alternatives');
$this->assertEquals('There are no commands defined in the "Unknown-namespace" namespace.', $e->getMessage(), '->find() throws an \InvalidArgumentException if namespace does not exist, without alternatives');
}
try {

View File

@@ -81,7 +81,7 @@ class InlineServiceDefinitionsPass implements RepeatablePassInterface
continue;
}
if ($this->isInlinableDefinition($container, $id, $definition = $container->getDefinition($id))) {
if ($this->isInlineableDefinition($container, $id, $definition = $container->getDefinition($id))) {
$this->compiler->addLogMessage($this->formatter->formatInlineService($this, $id, $this->currentId));
if (ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope()) {
@@ -109,7 +109,7 @@ class InlineServiceDefinitionsPass implements RepeatablePassInterface
*
* @return Boolean If the definition is inlineable
*/
private function isInlinableDefinition(ContainerBuilder $container, $id, Definition $definition)
private function isInlineableDefinition(ContainerBuilder $container, $id, Definition $definition)
{
if (ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope()) {
return true;

View File

@@ -27,9 +27,9 @@ class Filesystem
*
* By default, if the target already exists, it is not overridden.
*
* @param string $originFile The original filename
* @param string $targetFile The target filename
* @param array $override Whether to override an existing file or not
* @param string $originFile The original filename
* @param string $targetFile The target filename
* @param boolean $override Whether to override an existing file or not
*
* @throws IOException When copy fails
*/

View File

@@ -31,7 +31,7 @@ class FilesystemTest extends \PHPUnit_Framework_TestCase
public function setUp()
{
$this->filesystem = new Filesystem();
$this->workspace = sys_get_temp_dir().DIRECTORY_SEPARATOR.time().rand(0, 1000);
$this->workspace = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.time().rand(0, 1000);
mkdir($this->workspace, 0777, true);
}
@@ -319,7 +319,7 @@ class FilesystemTest extends \PHPUnit_Framework_TestCase
mkdir($basePath);
mkdir($basePath.'dir');
// create symlink to unexisting file
symlink($basePath.'file', $basePath.'link');
@symlink($basePath.'file', $basePath.'link');
$this->filesystem->remove($basePath);
@@ -388,8 +388,8 @@ class FilesystemTest extends \PHPUnit_Framework_TestCase
$this->filesystem->chmod($file, 0400);
$this->filesystem->chmod($dir, 0753);
$this->assertEquals(753, $this->getFilePermisions($dir));
$this->assertEquals(400, $this->getFilePermisions($file));
$this->assertEquals(753, $this->getFilePermissions($dir));
$this->assertEquals(400, $this->getFilePermissions($file));
}
public function testChmodWrongMod()
@@ -414,8 +414,8 @@ class FilesystemTest extends \PHPUnit_Framework_TestCase
$this->filesystem->chmod($file, 0400, 0000, true);
$this->filesystem->chmod($dir, 0753, 0000, true);
$this->assertEquals(753, $this->getFilePermisions($dir));
$this->assertEquals(753, $this->getFilePermisions($file));
$this->assertEquals(753, $this->getFilePermissions($dir));
$this->assertEquals(753, $this->getFilePermissions($file));
}
public function testChmodAppliesUmask()
@@ -426,7 +426,7 @@ class FilesystemTest extends \PHPUnit_Framework_TestCase
touch($file);
$this->filesystem->chmod($file, 0770, 0022);
$this->assertEquals(750, $this->getFilePermisions($file));
$this->assertEquals(750, $this->getFilePermissions($file));
}
public function testChmodChangesModeOfArrayOfFiles()
@@ -442,8 +442,8 @@ class FilesystemTest extends \PHPUnit_Framework_TestCase
$this->filesystem->chmod($files, 0753);
$this->assertEquals(753, $this->getFilePermisions($file));
$this->assertEquals(753, $this->getFilePermisions($directory));
$this->assertEquals(753, $this->getFilePermissions($file));
$this->assertEquals(753, $this->getFilePermissions($directory));
}
public function testChmodChangesModeOfTraversableFileObject()
@@ -459,8 +459,8 @@ class FilesystemTest extends \PHPUnit_Framework_TestCase
$this->filesystem->chmod($files, 0753);
$this->assertEquals(753, $this->getFilePermisions($file));
$this->assertEquals(753, $this->getFilePermisions($directory));
$this->assertEquals(753, $this->getFilePermissions($file));
$this->assertEquals(753, $this->getFilePermissions($directory));
}
public function testChown()
@@ -701,6 +701,8 @@ class FilesystemTest extends \PHPUnit_Framework_TestCase
$link1 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'link';
$link2 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'subdir'.DIRECTORY_SEPARATOR.'link';
touch($file);
$this->filesystem->symlink($file, $link1);
$this->filesystem->symlink($file, $link2);
@@ -830,13 +832,15 @@ class FilesystemTest extends \PHPUnit_Framework_TestCase
*
* @return integer
*/
private function getFilePermisions($filePath)
private function getFilePermissions($filePath)
{
return (int) substr(sprintf('%o', fileperms($filePath)), -3);
}
private function getFileOwner($filepath)
{
$this->markAsSkippedIfPosixIsMissing();
$infos = stat($filepath);
if ($datas = posix_getpwuid($infos['uid'])) {
return $datas['name'];
@@ -845,6 +849,8 @@ class FilesystemTest extends \PHPUnit_Framework_TestCase
private function getFileGroup($filepath)
{
$this->markAsSkippedIfPosixIsMissing();
$infos = stat($filepath);
if ($datas = posix_getgrgid($infos['gid'])) {
return $datas['name'];
@@ -867,8 +873,8 @@ class FilesystemTest extends \PHPUnit_Framework_TestCase
private function markAsSkippedIfPosixIsMissing()
{
if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
$this->markTestSkipped('Posix uids are not available on windows');
if (defined('PHP_WINDOWS_VERSION_MAJOR') || !function_exists('posix_isatty')) {
$this->markTestSkipped('Posix is not supported');
}
}
}

View File

@@ -19,7 +19,7 @@ namespace Symfony\Component\Finder\Iterator;
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class CustomFilterIterator extends \FilterIterator
class CustomFilterIterator extends FilterIterator
{
private $filters = array();

View File

@@ -16,7 +16,7 @@ namespace Symfony\Component\Finder\Iterator;
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DateRangeFilterIterator extends \FilterIterator
class DateRangeFilterIterator extends FilterIterator
{
private $comparators = array();

View File

@@ -16,7 +16,7 @@ namespace Symfony\Component\Finder\Iterator;
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DepthRangeFilterIterator extends \FilterIterator
class DepthRangeFilterIterator extends FilterIterator
{
private $minDepth = 0;

View File

@@ -16,7 +16,7 @@ namespace Symfony\Component\Finder\Iterator;
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ExcludeDirectoryFilterIterator extends \FilterIterator
class ExcludeDirectoryFilterIterator extends FilterIterator
{
private $patterns;

View File

@@ -16,7 +16,7 @@ namespace Symfony\Component\Finder\Iterator;
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FileTypeFilterIterator extends \FilterIterator
class FileTypeFilterIterator extends FilterIterator
{
const ONLY_FILES = 1;
const ONLY_DIRECTORIES = 2;

View File

@@ -0,0 +1,42 @@
<?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\Finder\Iterator;
/**
* This iterator just overrides the rewind method in order to correct a PHP bug.
*
* @see https://bugs.php.net/bug.php?id=49104
*
* @author Alex Bogomazov
*/
abstract class FilterIterator extends \FilterIterator
{
/**
* This is a workaround for the problem with \FilterIterator leaving inner \FilesystemIterator in wrong state after
* rewind in some cases.
*
* @see FilterIterator::rewind()
*/
public function rewind()
{
$iterator = $this;
while ($iterator instanceof \OuterIterator) {
if ($iterator->getInnerIterator() instanceof \FilesystemIterator) {
$iterator->getInnerIterator()->next();
$iterator->getInnerIterator()->rewind();
}
$iterator = $iterator->getInnerIterator();
}
parent::rewind();
}
}

View File

@@ -16,7 +16,7 @@ namespace Symfony\Component\Finder\Iterator;
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class MultiplePcreFilterIterator extends \FilterIterator
abstract class MultiplePcreFilterIterator extends FilterIterator
{
protected $matchRegexps;
protected $noMatchRegexps;

View File

@@ -16,7 +16,7 @@ namespace Symfony\Component\Finder\Iterator;
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SizeRangeFilterIterator extends \FilterIterator
class SizeRangeFilterIterator extends FilterIterator
{
private $comparators = array();

View File

@@ -440,4 +440,23 @@ class FinderTest extends Iterator\RealIteratorTestCase
$this->assertIterator(array(), $finder);
}
/**
* Searching in multiple locations involves AppendIterator which does an unnecessary rewind which leaves FilterIterator
* with inner FilesystemIterator in an ivalid state.
*
* @see https://bugs.php.net/bug.php?id=49104
*/
public function testMultipleLocations()
{
$locations = array(
self::$tmpDir.'/',
self::$tmpDir.'/toto/',
);
// it is expected that there are test.py test.php in the tmpDir
$finder = new Finder();
$finder->in($locations)->depth('< 1')->name('test.php');
$this->assertEquals(1, count($finder));
}
}

View File

@@ -165,12 +165,10 @@ class MockSplFileInfo extends \SplFileInfo
if (is_string($type)) {
switch ($type) {
case 'directory':
$this->type = self::TYPE_DIRECTORY;
case 'd':
$this->type = self::TYPE_DIRECTORY;
break;
case 'file':
$this->type = self::TYPE_FILE;
case 'f':
$this->type = self::TYPE_FILE;
break;

View File

@@ -0,0 +1,50 @@
<?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\Finder\Tests\Iterator;
/**
* @author Alex Bogomazov
*/
class FilterIteratorTest extends RealIteratorTestCase
{
public function testFilterFilesystemIterators()
{
$i = new \FilesystemIterator(sys_get_temp_dir().'/symfony2_finder');
// it is expected that there are test.py test.php in the tmpDir
$i = $this->getMockForAbstractClass('Symfony\Component\Finder\Iterator\FilterIterator', array($i));
$i->expects($this->any())
->method('accept')
->will($this->returnCallback(function () use ($i) {
return (bool) preg_match('/\.php/', (string) $i->current());
})
);
$c = 0;
foreach ($i as $item) {
$c++;
}
$this->assertEquals(1, $c);
$i->rewind();
$c = 0;
foreach ($i as $item) {
$c++;
}
// This would fail with \FilterIterator but works with Symfony\Component\Finder\Iterator\FilterIterator
// see https://bugs.php.net/bug.php?id=49104
$this->assertEquals(1, $c);
}
}

View File

@@ -21,7 +21,7 @@ abstract class AbstractRendererEngine implements FormRendererEngineInterface
/**
* The variable in {@link FormView} used as cache key.
*/
const CACHE_KEY_VAR = 'full_block_name';
const CACHE_KEY_VAR = 'cache_key';
/**
* @var array

View File

@@ -152,12 +152,12 @@ CHANGELOG
* deprecated the options "data_timezone" and "user_timezone" in DateType, DateTimeType and TimeType
and renamed them to "model_timezone" and "view_timezone"
* fixed: TransformationFailedExceptions thrown in the model transformer are now caught by the form
* added FormRegistry and ResolvedFormTypeInterface
* added FormRegistryInterface, ResolvedFormTypeInterface and ResolvedFormTypeFactoryInterface
* deprecated FormFactory methods
* `addType`
* `hasType`
* `getType`
* [BC BREAK] FormFactory now expects a FormRegistryInterface as constructor argument
* [BC BREAK] FormFactory now expects a FormRegistryInterface and a ResolvedFormTypeFactoryInterface as constructor argument
* [BC BREAK] The method `createBuilder` in FormTypeInterface is not supported anymore for performance reasons
* [BC BREAK] Removed `setTypes` from FormBuilder
* deprecated AbstractType methods
@@ -175,3 +175,5 @@ CHANGELOG
* `isChoiceSelected`
* [BC BREAK] renamed method `renderBlock` in FormHelper to `block` and changed its signature
* made FormView properties public and deprecated their accessor methods
* made the normalized data of a form accessible in the template through the variable "form.vars.data"
* made the original data of a choice accessible in the template through the property "choice.data"

View File

@@ -11,7 +11,7 @@
namespace Symfony\Component\Form\Extension\Core\ChoiceList;
use Symfony\Component\Form\FormConfig;
use Symfony\Component\Form\FormConfigBuilder;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\Exception\InvalidConfigurationException;
use Symfony\Component\Form\Extension\Core\View\ChoiceView;
@@ -20,7 +20,10 @@ use Symfony\Component\Form\Extension\Core\View\ChoiceView;
* A choice list for choices of arbitrary data types.
*
* Choices and labels are passed in two arrays. The indices of the choices
* and the labels should match.
* and the labels should match. Choices may also be given as hierarchy of
* unlimited depth by creating nested arrays. The title of the sub-hierarchy
* can be stored in the array key pointing to the nested array. The topmost
* level of the hierarchy may also be a \Traversable.
*
* <code>
* $choices = array(true, false);
@@ -66,17 +69,24 @@ class ChoiceList implements ChoiceListInterface
* Creates a new choice list.
*
* @param array|\Traversable $choices The array of choices. Choices may also be given
* as hierarchy of unlimited depth. Hierarchies are
* created by creating nested arrays. The title of
* the sub-hierarchy can be stored in the array
* key pointing to the nested array.
* as hierarchy of unlimited depth. Hierarchies are
* created by creating nested arrays. The title of
* the sub-hierarchy can be stored in the array
* key pointing to the nested array. The topmost
* level of the hierarchy may also be a \Traversable.
* @param array $labels The array of labels. The structure of this array
* should match the structure of $choices.
* should match the structure of $choices.
* @param array $preferredChoices A flat array of choices that should be
* presented to the user with priority.
* presented to the user with priority.
*
* @throws UnexpectedTypeException If the choices are not an array or \Traversable.
*/
public function __construct($choices, array $labels, array $preferredChoices = array())
{
if (!is_array($choices) && !$choices instanceof \Traversable) {
throw new UnexpectedTypeException($choices, 'array or \Traversable');
}
$this->initialize($choices, $labels, $preferredChoices);
}
@@ -236,22 +246,25 @@ class ChoiceList implements ChoiceListInterface
/**
* Recursively adds the given choices to the list.
*
* @param array $bucketForPreferred The bucket where to store the preferred
* view objects.
* @param array $bucketForRemaining The bucket where to store the
* non-preferred view objects.
* @param array $choices The list of choices.
* @param array $labels The labels corresponding to the choices.
* @param array $preferredChoices The preferred choices.
* @param array $bucketForPreferred The bucket where to store the preferred
* view objects.
* @param array $bucketForRemaining The bucket where to store the
* non-preferred view objects.
* @param array|\Traversable $choices The list of choices.
* @param array $labels The labels corresponding to the choices.
* @param array $preferredChoices The preferred choices.
*
* @throws UnexpectedTypeException If the structure of the $labels array
* does not match the structure of the
* $choices array.
* @throws \InvalidArgumentException If the structures of the choices and labels array do not match.
* @throws InvalidConfigurationException If no valid value or index could be created for a choice.
*/
protected function addChoices(&$bucketForPreferred, &$bucketForRemaining, array $choices, array $labels, array $preferredChoices)
protected function addChoices(array &$bucketForPreferred, array &$bucketForRemaining, $choices, array $labels, array $preferredChoices)
{
// Add choices to the nested buckets
foreach ($choices as $group => $choice) {
if (!isset($labels[$group])) {
throw new \InvalidArgumentException('The structures of the choices and labels array do not match.');
}
if (is_array($choice)) {
// Don't do the work if the array is empty
if (count($choice) > 0) {
@@ -281,14 +294,16 @@ class ChoiceList implements ChoiceListInterface
*
* @param string $group The name of the group.
* @param array $bucketForPreferred The bucket where to store the preferred
* view objects.
* @param array $bucketForRemaining The bucket where to store the
* non-preferred view objects.
* @param array $choices The list of choices in the group.
* @param array $labels The labels corresponding to the choices in the group.
* @param array $preferredChoices The preferred choices.
* view objects.
* @param array $bucketForRemaining The bucket where to store the
* non-preferred view objects.
* @param array $choices The list of choices in the group.
* @param array $labels The labels corresponding to the choices in the group.
* @param array $preferredChoices The preferred choices.
*
* @throws InvalidConfigurationException If no valid value or index could be created for a choice.
*/
protected function addChoiceGroup($group, &$bucketForPreferred, &$bucketForRemaining, array $choices, array $labels, array $preferredChoices)
protected function addChoiceGroup($group, array &$bucketForPreferred, array &$bucketForRemaining, array $choices, array $labels, array $preferredChoices)
{
// If this is a choice group, create a new level in the choice
// key hierarchy
@@ -325,11 +340,11 @@ class ChoiceList implements ChoiceListInterface
*
* @throws InvalidConfigurationException If no valid value or index could be created.
*/
protected function addChoice(&$bucketForPreferred, &$bucketForRemaining, $choice, $label, array $preferredChoices)
protected function addChoice(array &$bucketForPreferred, array &$bucketForRemaining, $choice, $label, array $preferredChoices)
{
$index = $this->createIndex($choice);
if ('' === $index || null === $index || !FormConfig::isValidName((string) $index)) {
if ('' === $index || null === $index || !FormConfigBuilder::isValidName((string) $index)) {
throw new InvalidConfigurationException('The index "' . $index . '" created by the choice list is invalid. It should be a valid, non-empty Form name.');
}
@@ -339,7 +354,7 @@ class ChoiceList implements ChoiceListInterface
throw new InvalidConfigurationException('The value created by the choice list is of type "' . gettype($value) . '", but should be a string.');
}
$view = new ChoiceView($value, $label);
$view = new ChoiceView($choice, $value, $label);
$this->choices[$index] = $this->fixChoice($choice);
$this->values[$index] = $value;
@@ -481,7 +496,7 @@ class ChoiceList implements ChoiceListInterface
/**
* Fixes the data type of the given choices to avoid comparison problems.
*
* @param array $choice The choices.
* @param array $choices The choices.
*
* @return array The fixed choices.
*

View File

@@ -13,7 +13,6 @@ namespace Symfony\Component\Form\Extension\Core\ChoiceList;
use Symfony\Component\Form\Util\PropertyPath;
use Symfony\Component\Form\Exception\StringCastException;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\Exception\InvalidPropertyException;
/**
@@ -57,11 +56,12 @@ class ObjectChoiceList extends ChoiceList
/**
* Creates a new object choice list.
*
* @param array $choices The array of choices. Choices may also be given
* as hierarchy of unlimited depth. Hierarchies are
* created by creating nested arrays. The title of
* the sub-hierarchy can be stored in the array
* key pointing to the nested array.
* @param array|\Traversable $choices The array of choices. Choices may also be given
* as hierarchy of unlimited depth by creating nested
* arrays. The title of the sub-hierarchy can be
* stored in the array key pointing to the nested
* array. The topmost level of the hierarchy may also
* be a \Traversable.
* @param string $labelPath A property path pointing to the property used
* for the choice labels. The value is obtained
* by calling the getter on the object. If the
@@ -78,9 +78,9 @@ class ObjectChoiceList extends ChoiceList
*/
public function __construct($choices, $labelPath = null, array $preferredChoices = array(), $groupPath = null, $valuePath = null)
{
$this->labelPath = $labelPath ? new PropertyPath($labelPath) : null;
$this->groupPath = $groupPath ? new PropertyPath($groupPath) : null;
$this->valuePath = $valuePath ? new PropertyPath($valuePath) : null;
$this->labelPath = null !== $labelPath ? new PropertyPath($labelPath) : null;
$this->groupPath = null !== $groupPath ? new PropertyPath($groupPath) : null;
$this->valuePath = null !== $valuePath ? new PropertyPath($valuePath) : null;
parent::__construct($choices, array(), $preferredChoices);
}
@@ -93,19 +93,18 @@ class ObjectChoiceList extends ChoiceList
* @param array|\Traversable $choices The choices to write into the list.
* @param array $labels Ignored.
* @param array $preferredChoices The choices to display with priority.
*
* @throws \InvalidArgumentException When passing a hierarchy of choices and using
* the "groupPath" option at the same time.
*/
protected function initialize($choices, array $labels, array $preferredChoices)
{
if (!is_array($choices) && !$choices instanceof \Traversable) {
throw new UnexpectedTypeException($choices, 'array or \Traversable');
}
if (null !== $this->groupPath) {
$groupedChoices = array();
foreach ($choices as $i => $choice) {
if (is_array($choice)) {
throw new \InvalidArgumentException('You should pass a plain object array (without groups, $code, $previous) when using the "groupPath" option');
throw new \InvalidArgumentException('You should pass a plain object array (without groups) when using the "groupPath" option.');
}
try {
@@ -142,10 +141,10 @@ class ObjectChoiceList extends ChoiceList
*
* If a property path for the value was given at object creation,
* the getter behind that path is now called to obtain a new value.
*
* Otherwise a new integer is generated.
*
* @param mixed $choice The choice to create a value for
*
* @return integer|string A unique value without character limitations.
*/
protected function createValue($choice)
@@ -160,7 +159,7 @@ class ObjectChoiceList extends ChoiceList
private function extractLabels($choices, array &$labels)
{
foreach ($choices as $i => $choice) {
if (is_array($choice) || $choice instanceof \Traversable) {
if (is_array($choice)) {
$labels[$i] = array();
$this->extractLabels($choice, $labels[$i]);
} elseif ($this->labelPath) {

View File

@@ -11,14 +11,14 @@
namespace Symfony\Component\Form\Extension\Core\ChoiceList;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
/**
* A choice list for choices of type string or integer.
*
* Choices and their associated labels can be passed in a single array. Since
* choices are passed as array keys, only strings or integer choices are
* allowed.
* allowed. Choices may also be given as hierarchy of unlimited depth by
* creating nested arrays. The title of the sub-hierarchy can be stored in the
* array key pointing to the nested array.
*
* <code>
* $choiceList = new SimpleChoiceList(array(
@@ -35,13 +35,12 @@ class SimpleChoiceList extends ChoiceList
* Creates a new simple choice list.
*
* @param array $choices The array of choices with the choices as keys and
* the labels as values. Choices may also be given
* as hierarchy of unlimited depth. Hierarchies are
* created by creating nested arrays. The title of
* the sub-hierarchy is stored in the array
* key pointing to the nested array.
* the labels as values. Choices may also be given
* as hierarchy of unlimited depth by creating nested
* arrays. The title of the sub-hierarchy is stored
* in the array key pointing to the nested array.
* @param array $preferredChoices A flat array of choices that should be
* presented to the user with priority.
* presented to the user with priority.
*/
public function __construct(array $choices, array $preferredChoices = array())
{
@@ -79,17 +78,15 @@ class SimpleChoiceList extends ChoiceList
* Takes care of splitting the single $choices array passed in the
* constructor into choices and labels.
*
* @param array $bucketForPreferred
* @param array $bucketForRemaining
* @param array $choices
* @param array $labels
* @param array $preferredChoices
*
* @throws UnexpectedTypeException
*
* @see parent::addChoices
* @param array $bucketForPreferred The bucket where to store the preferred
* view objects.
* @param array $bucketForRemaining The bucket where to store the
* non-preferred view objects.
* @param array|\Traversable $choices The list of choices.
* @param array $labels Ignored.
* @param array $preferredChoices The preferred choices.
*/
protected function addChoices(&$bucketForPreferred, &$bucketForRemaining, array $choices, array $labels, array $preferredChoices)
protected function addChoices(array &$bucketForPreferred, array &$bucketForRemaining, $choices, array $labels, array $preferredChoices)
{
// Add choices to the nested buckets
foreach ($choices as $choice => $label) {

View File

@@ -11,7 +11,6 @@
namespace Symfony\Component\Form\Extension\Core;
use Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Form\AbstractExtension;
/**

View File

@@ -17,7 +17,6 @@ use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\Extension\Core\EventListener\BindRequestListener;
use Symfony\Component\Form\Extension\Core\EventListener\TrimListener;
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
use Symfony\Component\Form\Exception\FormException;
@@ -45,7 +44,6 @@ class FormType extends AbstractType
->setData(isset($options['data']) ? $options['data'] : null)
->setDataLocked(isset($options['data']))
->setDataMapper($options['compound'] ? new PropertyPathMapper() : null)
->addEventSubscriber(new BindRequestListener())
;
if ($options['trim']) {
@@ -71,11 +69,11 @@ class FormType extends AbstractType
if ('' !== ($parentFullName = $view->parent->vars['full_name'])) {
$id = sprintf('%s_%s', $view->parent->vars['id'], $name);
$fullName = sprintf('%s[%s]', $parentFullName, $name);
$fullBlockName = sprintf('%s_%s', $view->parent->vars['full_block_name'], $blockName);
$uniqueBlockPrefix = sprintf('%s_%s', $view->parent->vars['unique_block_prefix'], $blockName);
} else {
$id = $name;
$fullName = $name;
$fullBlockName = '_' . $blockName;
$uniqueBlockPrefix = '_' . $blockName;
}
// Complex fields are read-only if they themselves or their parents are.
@@ -89,7 +87,7 @@ class FormType extends AbstractType
} else {
$id = $name;
$fullName = $name;
$fullBlockName = '_' . $blockName;
$uniqueBlockPrefix = '_' . $blockName;
// Strip leading underscores and digits. These are allowed in
// form names, but not in HTML4 ID attributes.
@@ -97,37 +95,46 @@ class FormType extends AbstractType
$id = ltrim($id, '_0123456789');
}
$types = array();
$blockPrefixes = array();
for ($type = $form->getConfig()->getType(); null !== $type; $type = $type->getParent()) {
array_unshift($types, $type->getName());
array_unshift($blockPrefixes, $type->getName());
}
$blockPrefixes[] = $uniqueBlockPrefix;
if (!$translationDomain) {
$translationDomain = 'messages';
}
$view->vars = array_replace($view->vars, array(
'form' => $view,
'id' => $id,
'name' => $name,
'full_name' => $fullName,
'full_block_name' => $fullBlockName,
'read_only' => $readOnly,
'errors' => $form->getErrors(),
'valid' => $form->isBound() ? $form->isValid() : true,
'value' => $form->getViewData(),
'disabled' => $form->isDisabled(),
'required' => $form->isRequired(),
'max_length' => $options['max_length'],
'pattern' => $options['pattern'],
'size' => null,
'label' => $options['label'],
'multipart' => false,
'attr' => $options['attr'],
'label_attr' => $options['label_attr'],
'compound' => $form->getConfig()->getCompound(),
'types' => $types,
'translation_domain' => $translationDomain,
'form' => $view,
'id' => $id,
'name' => $name,
'full_name' => $fullName,
'read_only' => $readOnly,
'errors' => $form->getErrors(),
'valid' => $form->isBound() ? $form->isValid() : true,
'value' => $form->getViewData(),
'data' => $form->getNormData(),
'disabled' => $form->isDisabled(),
'required' => $form->isRequired(),
'max_length' => $options['max_length'],
'pattern' => $options['pattern'],
'size' => null,
'label' => $options['label'],
'multipart' => false,
'attr' => $options['attr'],
'label_attr' => $options['label_attr'],
'compound' => $form->getConfig()->getCompound(),
'block_prefixes' => $blockPrefixes,
'unique_block_prefix' => $uniqueBlockPrefix,
'translation_domain' => $translationDomain,
// Using the block name here speeds up performance in collection
// forms, where each entry has the same full block name.
// Including the type is important too, because if rows of a
// collection form have different types (dynamically), they should
// be rendered differently.
// https://github.com/symfony/symfony/issues/5038
'cache_key' => $uniqueBlockPrefix . '_' . $form->getConfig()->getType()->getName(),
));
}

View File

@@ -18,6 +18,13 @@ namespace Symfony\Component\Form\Extension\Core\View;
*/
class ChoiceView
{
/**
* The original choice value.
*
* @var mixed
*/
public $data;
/**
* The view representation of the choice.
*
@@ -35,11 +42,13 @@ class ChoiceView
/**
* Creates a new ChoiceView.
*
* @param mixed $data The original choice.
* @param string $value The view representation of the choice.
* @param string $label The label displayed to humans.
*/
public function __construct($value, $label)
public function __construct($data, $value, $label)
{
$this->data = $data;
$this->value = $value;
$this->label = $label;
}

View File

@@ -56,8 +56,9 @@ class FormTypeCsrfExtension extends AbstractTypeExtension
/**
* Adds a CSRF field to the root form view.
*
* @param FormView $view The form view
* @param FormInterface $form The form
* @param FormView $view The form view
* @param FormInterface $form The form
* @param array $options The options
*/
public function finishView(FormView $view, FormInterface $form, array $options)
{

View File

@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Extension\Core\EventListener;
namespace Symfony\Component\Form\Extension\HttpFoundation\EventListener;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;

View File

@@ -0,0 +1,29 @@
<?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\HttpFoundation;
use Symfony\Component\Form\AbstractExtension;
/**
* Integrates the HttpFoundation component with the Form library.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class HttpFoundationExtension extends AbstractExtension
{
protected function loadTypes()
{
return array(
new Type\FormTypeHttpFoundationExtension(),
);
}
}

View File

@@ -0,0 +1,50 @@
<?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\HttpFoundation\Type;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\HttpFoundation\EventListener\BindRequestListener;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class FormTypeHttpFoundationExtension extends AbstractTypeExtension
{
/**
* @var BindRequestListener
*/
private $listener;
public function __construct()
{
$this->listener = new BindRequestListener();
}
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventSubscriber($this->listener);
}
/**
* {@inheritdoc}
*/
public function getExtendedType()
{
return 'form';
}
}

View File

@@ -125,10 +125,10 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface
return new TypeGuess('country', array(), Guess::HIGH_CONFIDENCE);
case 'Symfony\Component\Validator\Constraints\Date':
return new TypeGuess('date', array('type' => 'string'), Guess::HIGH_CONFIDENCE);
return new TypeGuess('date', array('input' => 'string'), Guess::HIGH_CONFIDENCE);
case 'Symfony\Component\Validator\Constraints\DateTime':
return new TypeGuess('datetime', array('type' => 'string'), Guess::HIGH_CONFIDENCE);
return new TypeGuess('datetime', array('input' => 'string'), Guess::HIGH_CONFIDENCE);
case 'Symfony\Component\Validator\Constraints\Email':
return new TypeGuess('email', array(), Guess::HIGH_CONFIDENCE);
@@ -144,7 +144,7 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface
return new TypeGuess('locale', array(), Guess::HIGH_CONFIDENCE);
case 'Symfony\Component\Validator\Constraints\Time':
return new TypeGuess('time', array('type' => 'string'), Guess::HIGH_CONFIDENCE);
return new TypeGuess('time', array('input' => 'string'), Guess::HIGH_CONFIDENCE);
case 'Symfony\Component\Validator\Constraints\Url':
return new TypeGuess('url', array(), Guess::HIGH_CONFIDENCE);
@@ -157,7 +157,6 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface
case 'Symfony\Component\Validator\Constraints\Regex':
return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
case 'Symfony\Component\Validator\Constraints\Size':
case 'Symfony\Component\Validator\Constraints\Min':
case 'Symfony\Component\Validator\Constraints\Max':
return new TypeGuess('number', array(), Guess::LOW_CONFIDENCE);
@@ -220,9 +219,6 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface
case 'Symfony\Component\Validator\Constraints\Max':
return new ValueGuess(strlen((string) $constraint->limit), Guess::LOW_CONFIDENCE);
case 'Symfony\Component\Validator\Constraints\Size':
return new ValueGuess(strlen((string) $constraint->max), Guess::LOW_CONFIDENCE);
}
return null;
@@ -252,9 +248,6 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface
case 'Symfony\Component\Validator\Constraints\Min':
return new ValueGuess(sprintf('.{%s,}', strlen((string) $constraint->limit)), Guess::LOW_CONFIDENCE);
case 'Symfony\Component\Validator\Constraints\Size':
return new ValueGuess(sprintf('.{%s,%s}', strlen((string) $constraint->min), strlen((string) $constraint->max)), Guess::LOW_CONFIDENCE);
case 'Symfony\Component\Validator\Constraints\Type':
if (in_array($constraint->type, array('double', 'float', 'numeric', 'real'))) {
return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE);
@@ -269,12 +262,12 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface
* Iterates over the constraints of a property, executes a constraints on
* them and returns the best guess
*
* @param string $class The class to read the constraints from
* @param string $property The property for which to find constraints
* @param \Closure $closure The closure that returns a guess
* for a given constraint
* @param mixed $default The default value assumed if no other value
* can be guessed.
* @param string $class The class to read the constraints from
* @param string $property The property for which to find constraints
* @param \Closure $closure The closure that returns a guess
* for a given constraint
* @param mixed $defaultValue The default value assumed if no other value
* can be guessed.
*
* @return Guess The guessed value with the highest confidence
*/

View File

@@ -146,10 +146,6 @@ class Form implements \IteratorAggregate, FormInterface
*/
public function __construct(FormConfigInterface $config)
{
if (!$config instanceof ImmutableFormConfig) {
$config = new ImmutableFormConfig($config);
}
// Compound forms always need a data mapper, otherwise calls to
// `setData` and `add` will not lead to the correct population of
// the child forms.
@@ -170,7 +166,7 @@ class Form implements \IteratorAggregate, FormInterface
/**
* Returns the configuration of the form.
*
* @return ImmutableFormConfig The form's immutable configuration.
* @return FormConfigInterface The form's configuration.
*/
public function getConfig()
{
@@ -372,13 +368,16 @@ class Form implements \IteratorAggregate, FormInterface
}
$this->lockSetData = true;
$dispatcher = $this->config->getEventDispatcher();
// Hook to change content of the data
$event = new FormEvent($this, $modelData);
$this->config->getEventDispatcher()->dispatch(FormEvents::PRE_SET_DATA, $event);
// BC until 2.3
$this->config->getEventDispatcher()->dispatch(FormEvents::SET_DATA, $event);
$modelData = $event->getData();
if ($dispatcher->hasListeners(FormEvents::PRE_SET_DATA) || $dispatcher->hasListeners(FormEvents::SET_DATA)) {
$event = new FormEvent($this, $modelData);
$dispatcher->dispatch(FormEvents::PRE_SET_DATA, $event);
// BC until 2.3
$dispatcher->dispatch(FormEvents::SET_DATA, $event);
$modelData = $event->getData();
}
// Treat data as strings unless a value transformer exists
if (!$this->config->getViewTransformers() && !$this->config->getModelTransformers() && is_scalar($modelData)) {
@@ -432,8 +431,10 @@ class Form implements \IteratorAggregate, FormInterface
$this->config->getDataMapper()->mapDataToForms($viewData, $this->children);
}
$event = new FormEvent($this, $modelData);
$this->config->getEventDispatcher()->dispatch(FormEvents::POST_SET_DATA, $event);
if ($dispatcher->hasListeners(FormEvents::POST_SET_DATA)) {
$event = new FormEvent($this, $modelData);
$dispatcher->dispatch(FormEvents::POST_SET_DATA, $event);
}
return $this;
}
@@ -526,6 +527,13 @@ class Form implements \IteratorAggregate, FormInterface
return $this;
}
// The data must be initialized if it was not initialized yet.
// This is necessary to guarantee that the *_SET_DATA listeners
// are always invoked before bind() takes place.
if (!$this->initialized) {
$this->setData($this->config->getData());
}
// Don't convert NULL to a string here in order to determine later
// whether an empty value has been submitted or whether no value has
// been submitted at all. This is important for processing checkboxes
@@ -542,13 +550,16 @@ class Form implements \IteratorAggregate, FormInterface
$normData = null;
$extraData = array();
$synchronized = false;
$dispatcher = $this->config->getEventDispatcher();
// Hook to change content of the data bound by the browser
$event = new FormEvent($this, $submittedData);
$this->config->getEventDispatcher()->dispatch(FormEvents::PRE_BIND, $event);
// BC until 2.3
$this->config->getEventDispatcher()->dispatch(FormEvents::BIND_CLIENT_DATA, $event);
$submittedData = $event->getData();
if ($dispatcher->hasListeners(FormEvents::PRE_BIND) || $dispatcher->hasListeners(FormEvents::BIND_CLIENT_DATA)) {
$event = new FormEvent($this, $submittedData);
$dispatcher->dispatch(FormEvents::PRE_BIND, $event);
// BC until 2.3
$dispatcher->dispatch(FormEvents::BIND_CLIENT_DATA, $event);
$submittedData = $event->getData();
}
// By default, the submitted data is also the data in view format
$viewData = $submittedData;
@@ -610,11 +621,13 @@ class Form implements \IteratorAggregate, FormInterface
// Hook to change content of the data into the normalized
// representation
$event = new FormEvent($this, $normData);
$this->config->getEventDispatcher()->dispatch(FormEvents::BIND, $event);
// BC until 2.3
$this->config->getEventDispatcher()->dispatch(FormEvents::BIND_NORM_DATA, $event);
$normData = $event->getData();
if ($dispatcher->hasListeners(FormEvents::BIND) || $dispatcher->hasListeners(FormEvents::BIND_NORM_DATA)) {
$event = new FormEvent($this, $normData);
$dispatcher->dispatch(FormEvents::BIND, $event);
// BC until 2.3
$dispatcher->dispatch(FormEvents::BIND_NORM_DATA, $event);
$normData = $event->getData();
}
// Synchronize representations - must not change the content!
$modelData = $this->normToModel($normData);
@@ -630,10 +643,11 @@ class Form implements \IteratorAggregate, FormInterface
$this->viewData = $viewData;
$this->extraData = $extraData;
$this->synchronized = $synchronized;
$this->initialized = true;
$event = new FormEvent($this, $viewData);
$this->config->getEventDispatcher()->dispatch(FormEvents::POST_BIND, $event);
if ($dispatcher->hasListeners(FormEvents::POST_BIND)) {
$event = new FormEvent($this, $viewData);
$dispatcher->dispatch(FormEvents::POST_BIND, $event);
}
foreach ($this->config->getValidators() as $validator) {
$validator->validate($this);

View File

@@ -20,7 +20,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class FormBuilder extends FormConfig implements \IteratorAggregate, FormBuilderInterface
class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormBuilderInterface
{
/**
* The form factory.
@@ -56,6 +56,7 @@ class FormBuilder extends FormConfig implements \IteratorAggregate, FormBuilderI
* @param string $dataClass
* @param EventDispatcherInterface $dispatcher
* @param FormFactoryInterface $factory
* @param array $options
*/
public function __construct($name, $dataClass, EventDispatcherInterface $dispatcher, FormFactoryInterface $factory, array $options = array())
{
@@ -77,6 +78,10 @@ class FormBuilder extends FormConfig implements \IteratorAggregate, FormBuilderI
*/
public function add($child, $type = null, array $options = array())
{
if ($this->locked) {
throw new FormException('The form builder cannot be modified anymore.');
}
if ($child instanceof self) {
$child->setParent($this);
$this->children[$child->getName()] = $child;
@@ -110,6 +115,10 @@ class FormBuilder extends FormConfig implements \IteratorAggregate, FormBuilderI
*/
public function create($name, $type = null, array $options = array())
{
if ($this->locked) {
throw new FormException('The form builder cannot be modified anymore.');
}
if (null === $type && null === $this->getDataClass()) {
$type = 'text';
}
@@ -142,6 +151,10 @@ class FormBuilder extends FormConfig implements \IteratorAggregate, FormBuilderI
*/
public function remove($name)
{
if ($this->locked) {
throw new FormException('The form builder cannot be modified anymore.');
}
unset($this->unresolvedChildren[$name]);
if (array_key_exists($name, $this->children)) {
@@ -195,7 +208,7 @@ class FormBuilder extends FormConfig implements \IteratorAggregate, FormBuilderI
{
$this->resolveChildren();
$form = new Form($this);
$form = new Form($this->getFormConfig());
foreach ($this->children as $child) {
$form->add($child->getForm());
@@ -217,6 +230,10 @@ class FormBuilder extends FormConfig implements \IteratorAggregate, FormBuilderI
*/
public function setParent(FormBuilderInterface $parent = null)
{
if ($this->locked) {
throw new FormException('The form builder cannot be modified anymore.');
}
$this->parent = $parent;
return $this;

View File

@@ -14,7 +14,7 @@ namespace Symfony\Component\Form;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface FormBuilderInterface extends FormConfigEditorInterface, \Traversable, \Countable
interface FormBuilderInterface extends \Traversable, \Countable, FormConfigBuilderInterface
{
/**
* Adds a new field to this group. A field must have a unique name within

View File

@@ -11,18 +11,25 @@
namespace Symfony\Component\Form;
use Symfony\Component\Form\Exception\FormException;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\Util\PropertyPath;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\EventDispatcher\ImmutableEventDispatcher;
/**
* A basic form configuration.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class FormConfig implements FormConfigEditorInterface
class FormConfigBuilder implements FormConfigBuilderInterface
{
/**
* @var Boolean
*/
protected $locked = false;
/**
* @var EventDispatcherInterface
*/
@@ -161,6 +168,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function addEventListener($eventName, $listener, $priority = 0)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->dispatcher->addListener($eventName, $listener, $priority);
return $this;
@@ -171,6 +182,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function addEventSubscriber(EventSubscriberInterface $subscriber)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->dispatcher->addSubscriber($subscriber);
return $this;
@@ -181,6 +196,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function addValidator(FormValidatorInterface $validator)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->validators[] = $validator;
return $this;
@@ -191,6 +210,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function addViewTransformer(DataTransformerInterface $viewTransformer, $forcePrepend = false)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
if ($forcePrepend) {
array_unshift($this->viewTransformers, $viewTransformer);
} else {
@@ -205,6 +228,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function resetViewTransformers()
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->viewTransformers = array();
return $this;
@@ -215,13 +242,17 @@ class FormConfig implements FormConfigEditorInterface
*
* @param DataTransformerInterface $viewTransformer
*
* @return self The configuration object.
* @return FormConfigBuilder The configuration object.
*
* @deprecated Deprecated since version 2.1, to be removed in 2.3. Use
* {@link addViewTransformer()} instead.
*/
public function appendClientTransformer(DataTransformerInterface $viewTransformer)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
return $this->addViewTransformer($viewTransformer);
}
@@ -230,25 +261,33 @@ class FormConfig implements FormConfigEditorInterface
*
* @param DataTransformerInterface $viewTransformer
*
* @return self The configuration object.
* @return FormConfigBuilder The configuration object.
*
* @deprecated Deprecated since version 2.1, to be removed in 2.3.
*/
public function prependClientTransformer(DataTransformerInterface $viewTransformer)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
return $this->addViewTransformer($viewTransformer, true);
}
/**
* Alias of {@link resetViewTransformers()}.
*
* @return self The configuration object.
* @return FormConfigBuilder The configuration object.
*
* @deprecated Deprecated since version 2.1, to be removed in 2.3. Use
* {@link resetViewTransformers()} instead.
*/
public function resetClientTransformers()
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
return $this->resetViewTransformers();
}
@@ -257,6 +296,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function addModelTransformer(DataTransformerInterface $modelTransformer, $forceAppend = false)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
if ($forceAppend) {
$this->modelTransformers[] = $modelTransformer;
} else {
@@ -271,6 +314,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function resetModelTransformers()
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->modelTransformers = array();
return $this;
@@ -281,12 +328,16 @@ class FormConfig implements FormConfigEditorInterface
*
* @param DataTransformerInterface $modelTransformer
*
* @return self The configuration object.
* @return FormConfigBuilder The configuration object.
*
* @deprecated Deprecated since version 2.1, to be removed in 2.3.
*/
public function appendNormTransformer(DataTransformerInterface $modelTransformer)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
return $this->addModelTransformer($modelTransformer, true);
}
@@ -295,26 +346,34 @@ class FormConfig implements FormConfigEditorInterface
*
* @param DataTransformerInterface $modelTransformer
*
* @return self The configuration object.
* @return FormConfigBuilder The configuration object.
*
* @deprecated Deprecated since version 2.1, to be removed in 2.3. Use
* {@link addModelTransformer()} instead.
*/
public function prependNormTransformer(DataTransformerInterface $modelTransformer)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
return $this->addModelTransformer($modelTransformer);
}
/**
* Alias of {@link resetModelTransformers()}.
*
* @return self The configuration object.
* @return FormConfigBuilder The configuration object.
*
* @deprecated Deprecated since version 2.1, to be removed in 2.3. Use
* {@link resetModelTransformers()} instead.
*/
public function resetNormTransformers()
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
return $this->resetModelTransformers();
}
@@ -549,6 +608,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function setAttribute($name, $value)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->attributes[$name] = $value;
return $this;
@@ -559,6 +622,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function setAttributes(array $attributes)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->attributes = $attributes;
return $this;
@@ -569,6 +636,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function setDataMapper(DataMapperInterface $dataMapper = null)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->dataMapper = $dataMapper;
return $this;
@@ -579,6 +650,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function setDisabled($disabled)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->disabled = (Boolean) $disabled;
return $this;
@@ -589,6 +664,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function setEmptyData($emptyData)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->emptyData = $emptyData;
return $this;
@@ -599,6 +678,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function setErrorBubbling($errorBubbling)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->errorBubbling = null === $errorBubbling ? null : (Boolean) $errorBubbling;
return $this;
@@ -609,6 +692,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function setRequired($required)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->required = (Boolean) $required;
return $this;
@@ -619,6 +706,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function setPropertyPath($propertyPath)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
if (null !== $propertyPath && !$propertyPath instanceof PropertyPath) {
$propertyPath = new PropertyPath($propertyPath);
}
@@ -633,6 +724,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function setMapped($mapped)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->mapped = $mapped;
return $this;
@@ -643,6 +738,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function setByReference($byReference)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->byReference = $byReference;
return $this;
@@ -653,6 +752,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function setVirtual($virtual)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->virtual = $virtual;
return $this;
@@ -663,6 +766,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function setCompound($compound)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->compound = $compound;
return $this;
@@ -673,6 +780,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function setType(ResolvedFormTypeInterface $type)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->type = $type;
return $this;
@@ -683,6 +794,10 @@ class FormConfig implements FormConfigEditorInterface
*/
public function setData($data)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->data = $data;
return $this;
@@ -693,11 +808,31 @@ class FormConfig implements FormConfigEditorInterface
*/
public function setDataLocked($locked)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}
$this->dataLocked = $locked;
return $this;
}
/**
* {@inheritdoc}
*/
public function getFormConfig()
{
// This method should be idempotent, so clone the builder
$config = clone $this;
$config->locked = true;
if (!$config->dispatcher instanceof ImmutableEventDispatcher) {
$config->dispatcher = new ImmutableEventDispatcher($config->dispatcher);
}
return $config;
}
/**
* Validates whether the given variable is a valid form name.
*

View File

@@ -16,7 +16,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface FormConfigEditorInterface extends FormConfigInterface
interface FormConfigBuilderInterface extends FormConfigInterface
{
/**
* Adds an event listener to an event on this form.
@@ -240,4 +240,11 @@ interface FormConfigEditorInterface extends FormConfigInterface
* @return self The configuration object.
*/
public function setDataLocked($locked);
/**
* Builds and returns the form configuration.
*
* @return FormConfigInterface
*/
public function getFormConfig();
}

View File

@@ -23,9 +23,15 @@ class FormFactory implements FormFactoryInterface
*/
private $registry;
public function __construct(FormRegistryInterface $registry)
/**
* @var ResolvedFormTypeFactoryInterface
*/
private $resolvedTypeFactory;
public function __construct(FormRegistryInterface $registry, ResolvedFormTypeFactoryInterface $resolvedTypeFactory)
{
$this->registry = $registry;
$this->resolvedTypeFactory = $resolvedTypeFactory;
}
/**
@@ -73,14 +79,20 @@ class FormFactory implements FormFactoryInterface
$options['data'] = $data;
}
if ($type instanceof ResolvedFormTypeInterface) {
$this->registry->addType($type);
} elseif ($type instanceof FormTypeInterface) {
$type = $this->registry->resolveType($type);
$this->registry->addType($type);
if ($type instanceof FormTypeInterface) {
// An unresolved type instance was passed. Type extensions
// are not supported for these. If you want to use type
// extensions, you should create form extensions or register
// your type in the Dependency Injection configuration instead.
$parentType = $type->getParent();
$type = $this->resolvedTypeFactory->createResolvedType(
$type,
array(),
$parentType ? $this->registry->getType($parentType) : null
);
} elseif (is_string($type)) {
$type = $this->registry->getType($type);
} else {
} elseif (!$type instanceof ResolvedFormTypeInterface) {
throw new UnexpectedTypeException($type, 'string, Symfony\Component\Form\ResolvedFormTypeInterface or Symfony\Component\Form\FormTypeInterface');
}
@@ -152,12 +164,18 @@ class FormFactory implements FormFactoryInterface
* @param FormTypeInterface $type The type
*
* @deprecated Deprecated since version 2.1, to be removed in 2.3. Use
* {@link FormRegistryInterface::resolveType()} and
* {@link FormRegistryInterface::addType()} instead.
* form extensions or type registration in the Dependency
* Injection Container instead.
*/
public function addType(FormTypeInterface $type)
{
$this->registry->addType($this->registry->resolveType($type));
$parentType = $type->getParent();
$this->registry->addType($this->resolvedTypeFactory->createResolvedType(
$type,
array(),
$parentType ? $this->registry->getType($parentType) : null
));
}
/**

View File

@@ -37,14 +37,20 @@ class FormRegistry implements FormRegistryInterface
*/
private $guesser;
/**
* @var ResolvedFormTypeFactoryInterface
*/
private $resolvedTypeFactory;
/**
* Constructor.
*
* @param array $extensions An array of FormExtensionInterface
* @param array $extensions An array of FormExtensionInterface
* @param ResolvedFormTypeFactoryInterface $resolvedTypeFactory The factory for resolved form types.
*
* @throws UnexpectedTypeException if any extension does not implement FormExtensionInterface
*/
public function __construct(array $extensions)
public function __construct(array $extensions, ResolvedFormTypeFactoryInterface $resolvedTypeFactory)
{
foreach ($extensions as $extension) {
if (!$extension instanceof FormExtensionInterface) {
@@ -53,26 +59,7 @@ class FormRegistry implements FormRegistryInterface
}
$this->extensions = $extensions;
}
/**
* {@inheritdoc}
*/
public function resolveType(FormTypeInterface $type)
{
$typeExtensions = array();
foreach ($this->extensions as $extension) {
/* @var FormExtensionInterface $extension */
$typeExtensions = array_merge(
$typeExtensions,
$extension->getTypeExtensions($type->getName())
);
}
$parent = $type->getParent() ? $this->getType($type->getParent()) : null;
return new ResolvedFormType($type, $typeExtensions, $parent);
$this->resolvedTypeFactory = $resolvedTypeFactory;
}
/**
@@ -93,6 +80,7 @@ class FormRegistry implements FormRegistryInterface
}
if (!isset($this->types[$name])) {
/** @var FormTypeInterface $type */
$type = null;
foreach ($this->extensions as $extension) {
@@ -107,7 +95,22 @@ class FormRegistry implements FormRegistryInterface
throw new FormException(sprintf('Could not load type "%s"', $name));
}
$this->addType($this->resolveType($type));
$parentType = $type->getParent();
$typeExtensions = array();
foreach ($this->extensions as $extension) {
/* @var FormExtensionInterface $extension */
$typeExtensions = array_merge(
$typeExtensions,
$extension->getTypeExtensions($name)
);
}
$this->addType($this->resolvedTypeFactory->createResolvedType(
$type,
$typeExtensions,
$parentType ? $this->getType($parentType) : null
));
}
return $this->types[$name];

View File

@@ -22,6 +22,10 @@ interface FormRegistryInterface
* Adds a form type.
*
* @param ResolvedFormTypeInterface $type The type
*
* @deprecated Deprecated since version 2.1, to be removed in 2.3. Use
* form extensions or type registration in the Dependency
* Injection Container instead.
*/
public function addType(ResolvedFormTypeInterface $type);
@@ -48,18 +52,6 @@ interface FormRegistryInterface
*/
public function hasType($name);
/**
* Resolves a form type.
*
* @param FormTypeInterface $type
*
* @return ResolvedFormTypeInterface
*
* @throws Exception\UnexpectedTypeException if the types parent {@link FormTypeInterface::getParent()} is not a string
* @throws Exception\FormException if the types parent can not be retrieved from any extension
*/
public function resolveType(FormTypeInterface $type);
/**
* Returns the guesser responsible for guessing types.
*

View File

@@ -22,6 +22,8 @@ use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
*/
class FormRenderer implements FormRendererInterface
{
const CACHE_KEY_VAR = 'unique_block_prefix';
/**
* @var FormRendererEngineInterface
*/
@@ -42,11 +44,6 @@ class FormRenderer implements FormRendererInterface
*/
private $hierarchyLevelMap = array();
/**
* @var array
*/
private $variableMap = array();
/**
* @var array
*/
@@ -95,7 +92,8 @@ class FormRenderer implements FormRendererInterface
throw new FormException('This method should only be called while rendering a form element.');
}
$scopeVariables = end($this->variableStack);
$viewCacheKey = $view->vars[self::CACHE_KEY_VAR];
$scopeVariables = end($this->variableStack[$viewCacheKey]);
$resource = $this->engine->getResourceForBlockName($view, $blockName);
@@ -117,13 +115,13 @@ class FormRenderer implements FormRendererInterface
// cannot be overwritten
$variables = array_replace($scopeVariables, $variables);
$this->variableStack[] = $variables;
$this->variableStack[$viewCacheKey][] = $variables;
// Do the rendering
$html = $this->engine->renderBlock($view, $resource, $blockName, $variables);
// Clear the stack
array_pop($this->variableStack);
array_pop($this->variableStack[$viewCacheKey]);
return $html;
}
@@ -133,14 +131,15 @@ class FormRenderer implements FormRendererInterface
*/
public function searchAndRenderBlock(FormView $view, $blockNameSuffix, array $variables = array())
{
$renderOnlyOnce = in_array($blockNameSuffix, array('row', 'widget'));
$renderOnlyOnce = 'row' === $blockNameSuffix || 'widget' === $blockNameSuffix;
if ($renderOnlyOnce && $view->isRendered()) {
return '';
}
// The cache key for storing the variables and types
$mapKey = $uniqueBlockName = $view->vars['full_block_name'] . '_' . $blockNameSuffix;
$viewCacheKey = $view->vars[self::CACHE_KEY_VAR];
$viewAndSuffixCacheKey = $viewCacheKey . $blockNameSuffix;
// In templates, we have to deal with two kinds of block hierarchies:
//
@@ -169,29 +168,40 @@ class FormRenderer implements FormRendererInterface
// widget() function again to render the block for the parent type.
//
// The second kind is implemented in the following blocks.
if (!isset($this->blockNameHierarchyMap[$mapKey])) {
if (!isset($this->blockNameHierarchyMap[$viewAndSuffixCacheKey])) {
// INITIAL CALL
// Calculate the hierarchy of template blocks and start on
// the bottom level of the hierarchy (= "_<id>_<section>" block)
$blockNameHierarchy = array();
foreach ($view->vars['types'] as $type) {
$blockNameHierarchy[] = $type . '_' . $blockNameSuffix;
foreach ($view->vars['block_prefixes'] as $blockNamePrefix) {
$blockNameHierarchy[] = $blockNamePrefix . '_' . $blockNameSuffix;
}
$blockNameHierarchy[] = $uniqueBlockName;
$hierarchyLevel = count($blockNameHierarchy) - 1;
// The default variable scope contains all view variables, merged with
// the variables passed explicitly to the helper
$scopeVariables = $view->vars;
$hierarchyInit = true;
} else {
// RECURSIVE CALL
// If a block recursively calls renderSection() again, resume rendering
// using the parent type in the hierarchy.
$blockNameHierarchy = $this->blockNameHierarchyMap[$mapKey];
$hierarchyLevel = $this->hierarchyLevelMap[$mapKey] - 1;
$blockNameHierarchy = $this->blockNameHierarchyMap[$viewAndSuffixCacheKey];
$hierarchyLevel = $this->hierarchyLevelMap[$viewAndSuffixCacheKey] - 1;
$hierarchyInit = false;
}
// The variables are cached globally for a view (instead of for the
// current suffix)
if (!isset($this->variableStack[$viewCacheKey])) {
// The default variable scope contains all view variables, merged with
// the variables passed explicitly to the helper
$scopeVariables = $view->vars;
$varInit = true;
} else {
// Reuse the current scope and merge it with the explicitly passed variables
$scopeVariables = $this->variableMap[$mapKey];
$scopeVariables = end($this->variableStack[$viewCacheKey]);
$varInit = false;
}
// Load the resource where this block can be found
@@ -235,28 +245,29 @@ class FormRenderer implements FormRendererInterface
// We need to store these values in maps (associative arrays) because within a
// call to widget() another call to widget() can be made, but for a different view
// object. These nested calls should not override each other.
$this->blockNameHierarchyMap[$mapKey] = $blockNameHierarchy;
$this->hierarchyLevelMap[$mapKey] = $hierarchyLevel;
$this->variableMap[$mapKey] = $variables;
$this->blockNameHierarchyMap[$viewAndSuffixCacheKey] = $blockNameHierarchy;
$this->hierarchyLevelMap[$viewAndSuffixCacheKey] = $hierarchyLevel;
// We also need to store the view and the variables so that we can render custom
// blocks with renderBlock() using the same themes and variables as in the outer
// block.
//
// A stack is sufficient for this purpose, because renderBlock() always accesses
// the immediate next outer scope, which is always stored at the end of the stack.
$this->variableStack[] = $variables;
// We also need to store the variables for the view so that we can render other
// blocks for the same view using the same variables as in the outer block.
$this->variableStack[$viewCacheKey][] = $variables;
// Do the rendering
$html = $this->engine->renderBlock($view, $resource, $blockName, $variables);
// Clear the stack
array_pop($this->variableStack);
array_pop($this->variableStack[$viewCacheKey]);
// Clear the maps
unset($this->blockNameHierarchyMap[$mapKey]);
unset($this->hierarchyLevelMap[$mapKey]);
unset($this->variableMap[$mapKey]);
// Clear the caches if they were filled for the first time within
// this function call
if ($hierarchyInit) {
unset($this->blockNameHierarchyMap[$viewAndSuffixCacheKey]);
unset($this->hierarchyLevelMap[$viewAndSuffixCacheKey]);
}
if ($varInit) {
unset($this->variableStack[$viewCacheKey]);
}
if ($renderOnlyOnce) {
$view->setRendered();

View File

@@ -1,365 +0,0 @@
<?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;
use Symfony\Component\Form\Util\PropertyPath;
use Symfony\Component\EventDispatcher\ImmutableEventDispatcher;
/**
* A read-only form configuration.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class ImmutableFormConfig implements FormConfigInterface
{
/**
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
private $dispatcher;
/**
* @var string
*/
private $name;
/**
* @var PropertyPath
*/
private $propertyPath;
/**
* @var Boolean
*/
private $mapped;
/**
* @var Boolean
*/
private $byReference;
/**
* @var Boolean
*/
private $virtual;
/**
* @var Boolean
*/
private $compound;
/**
* @var ResolvedFormTypeInterface
*/
private $type;
/**
* @var array
*/
private $viewTransformers;
/**
* @var array
*/
private $modelTransformers;
/**
* @var DataMapperInterface
*/
private $dataMapper;
/**
* @var FormValidatorInterface
*/
private $validators;
/**
* @var Boolean
*/
private $required;
/**
* @var Boolean
*/
private $disabled;
/**
* @var Boolean
*/
private $errorBubbling;
/**
* @var mixed
*/
private $emptyData;
/**
* @var array
*/
private $attributes;
/**
* @var mixed
*/
private $data;
/**
* @var string
*/
private $dataClass;
/**
* @var Boolean
*/
private $dataLocked;
/**
* @var array
*/
private $options;
/**
* Creates an unmodifiable copy of a given configuration.
*
* @param FormConfigInterface $config The configuration to copy.
*/
public function __construct(FormConfigInterface $config)
{
$dispatcher = $config->getEventDispatcher();
if (!$dispatcher instanceof ImmutableEventDispatcher) {
$dispatcher = new ImmutableEventDispatcher($dispatcher);
}
$this->dispatcher = $dispatcher;
$this->name = $config->getName();
$this->propertyPath = $config->getPropertyPath();
$this->mapped = $config->getMapped();
$this->byReference = $config->getByReference();
$this->virtual = $config->getVirtual();
$this->compound = $config->getCompound();
$this->type = $config->getType();
$this->viewTransformers = $config->getViewTransformers();
$this->modelTransformers = $config->getModelTransformers();
$this->dataMapper = $config->getDataMapper();
$this->validators = $config->getValidators();
$this->required = $config->getRequired();
$this->disabled = $config->getDisabled();
$this->errorBubbling = $config->getErrorBubbling();
$this->emptyData = $config->getEmptyData();
$this->attributes = $config->getAttributes();
$this->data = $config->getData();
$this->dataClass = $config->getDataClass();
$this->dataLocked = $config->getDataLocked();
$this->options = $config->getOptions();
}
/**
* {@inheritdoc}
*/
public function getEventDispatcher()
{
return $this->dispatcher;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return $this->name;
}
/**
* {@inheritdoc}
*/
public function getPropertyPath()
{
return $this->propertyPath;
}
/**
* {@inheritdoc}
*/
public function getMapped()
{
return $this->mapped;
}
/**
* {@inheritdoc}
*/
public function getByReference()
{
return $this->byReference;
}
/**
* {@inheritdoc}
*/
public function getVirtual()
{
return $this->virtual;
}
/**
* {@inheritdoc}
*/
public function getCompound()
{
return $this->compound;
}
/**
* {@inheritdoc}
*/
public function getType()
{
return $this->type;
}
/**
* {@inheritdoc}
*/
public function getViewTransformers()
{
return $this->viewTransformers;
}
/**
* {@inheritdoc}
*/
public function getModelTransformers()
{
return $this->modelTransformers;
}
/**
* Returns the data mapper of the form.
*
* @return DataMapperInterface The data mapper.
*/
public function getDataMapper()
{
return $this->dataMapper;
}
/**
* {@inheritdoc}
*/
public function getValidators()
{
return $this->validators;
}
/**
* {@inheritdoc}
*/
public function getRequired()
{
return $this->required;
}
/**
* {@inheritdoc}
*/
public function getDisabled()
{
return $this->disabled;
}
/**
* {@inheritdoc}
*/
public function getErrorBubbling()
{
return $this->errorBubbling;
}
/**
* {@inheritdoc}
*/
public function getEmptyData()
{
return $this->emptyData;
}
/**
* {@inheritdoc}
*/
public function getAttributes()
{
return $this->attributes;
}
/**
* {@inheritdoc}
*/
public function hasAttribute($name)
{
return isset($this->attributes[$name]);
}
/**
* {@inheritdoc}
*/
public function getAttribute($name, $default = null)
{
return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
}
/**
* {@inheritdoc}
*/
public function getData()
{
return $this->data;
}
/**
* {@inheritdoc}
*/
public function getDataClass()
{
return $this->dataClass;
}
/**
* {@inheritdoc}
*/
public function getDataLocked()
{
return $this->dataLocked;
}
/**
* {@inheritdoc}
*/
public function getOptions()
{
return $this->options;
}
/**
* {@inheritdoc}
*/
public function hasOption($name)
{
return isset($this->options[$name]);
}
/**
* {@inheritdoc}
*/
public function getOption($name, $default = null)
{
return isset($this->options[$name]) ? $this->options[$name] : $default;
}
}

View File

@@ -35,7 +35,7 @@ class ResolvedFormType implements ResolvedFormTypeInterface
private $typeExtensions;
/**
* @var ResolvedFormType
* @var ResolvedFormTypeInterface
*/
private $parent;
@@ -44,7 +44,7 @@ class ResolvedFormType implements ResolvedFormTypeInterface
*/
private $optionsResolver;
public function __construct(FormTypeInterface $innerType, array $typeExtensions = array(), ResolvedFormType $parent = null)
public function __construct(FormTypeInterface $innerType, array $typeExtensions = array(), ResolvedFormTypeInterface $parent = null)
{
if (!preg_match('/^[a-z0-9_]*$/i', $innerType->getName())) {
throw new FormException(sprintf(
@@ -148,7 +148,16 @@ class ResolvedFormType implements ResolvedFormTypeInterface
return $view;
}
private function buildForm(FormBuilderInterface $builder, array $options)
/**
* Configures a form builder for the type hierarchy.
*
* This method is protected in order to allow implementing classes
* to change or call it in re-implementations of {@link createBuilder()}.
*
* @param FormBuilderInterface $builder The builder to configure.
* @param array $options The options used for the configuration.
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
if (null !== $this->parent) {
$this->parent->buildForm($builder, $options);
@@ -162,7 +171,19 @@ class ResolvedFormType implements ResolvedFormTypeInterface
}
}
private function buildView(FormView $view, FormInterface $form, array $options)
/**
* Configures a form view for the type hierarchy.
*
* This method is protected in order to allow implementing classes
* to change or call it in re-implementations of {@link createView()}.
*
* It is called before the children of the view are built.
*
* @param FormView $view The form view to configure.
* @param FormInterface $form The form corresponding to the view.
* @param array $options The options used for the configuration.
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
if (null !== $this->parent) {
$this->parent->buildView($view, $form, $options);
@@ -176,7 +197,19 @@ class ResolvedFormType implements ResolvedFormTypeInterface
}
}
private function finishView(FormView $view, FormInterface $form, array $options)
/**
* Finishes a form view for the type hierarchy.
*
* This method is protected in order to allow implementing classes
* to change or call it in re-implementations of {@link createView()}.
*
* It is called after the children of the view have been built.
*
* @param FormView $view The form view to configure.
* @param FormInterface $form The form corresponding to the view.
* @param array $options The options used for the configuration.
*/
public function finishView(FormView $view, FormInterface $form, array $options)
{
if (null !== $this->parent) {
$this->parent->finishView($view, $form, $options);
@@ -190,7 +223,15 @@ class ResolvedFormType implements ResolvedFormTypeInterface
}
}
private function getOptionsResolver()
/**
* Returns the configured options resolver used for this type.
*
* This method is protected in order to allow implementing classes
* to change or call it in re-implementations of {@link createBuilder()}.
*
* @return \Symfony\Component\OptionsResolver\OptionsResolverInterface The options resolver.
*/
public function getOptionsResolver()
{
if (null === $this->optionsResolver) {
if (null !== $this->parent) {

View File

@@ -0,0 +1,26 @@
<?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;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class ResolvedFormTypeFactory implements ResolvedFormTypeFactoryInterface
{
/**
* {@inheritdoc}
*/
public function createResolvedType(FormTypeInterface $type, array $typeExtensions, ResolvedFormTypeInterface $parent = null)
{
return new ResolvedFormType($type, $typeExtensions, $parent);
}
}

View File

@@ -0,0 +1,38 @@
<?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;
/**
* Creates ResolvedFormTypeInterface instances.
*
* This interface allows you to use your custom ResolvedFormTypeInterface
* implementation, within which you can customize the concrete FormBuilderInterface
* implementations or FormView subclasses that are used by the framework.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface ResolvedFormTypeFactoryInterface
{
/**
* Resolves a form type.
*
* @param FormTypeInterface $type
* @param array $typeExtensions
* @param ResolvedFormTypeInterface $parent
*
* @return ResolvedFormTypeInterface
*
* @throws Exception\UnexpectedTypeException if the types parent {@link FormTypeInterface::getParent()} is not a string
* @throws Exception\FormException if the types parent can not be retrieved from any extension
*/
public function createResolvedType(FormTypeInterface $type, array $typeExtensions, ResolvedFormTypeInterface $parent = null);
}

View File

@@ -67,4 +67,41 @@ interface ResolvedFormTypeInterface
* @return FormView The created form view.
*/
public function createView(FormInterface $form, FormView $parent = null);
/**
* Configures a form builder for the type hierarchy.
*
* @param FormBuilderInterface $builder The builder to configure.
* @param array $options The options used for the configuration.
*/
public function buildForm(FormBuilderInterface $builder, array $options);
/**
* Configures a form view for the type hierarchy.
*
* It is called before the children of the view are built.
*
* @param FormView $view The form view to configure.
* @param FormInterface $form The form corresponding to the view.
* @param array $options The options used for the configuration.
*/
public function buildView(FormView $view, FormInterface $form, array $options);
/**
* Finishes a form view for the type hierarchy.
*
* It is called after the children of the view have been built.
*
* @param FormView $view The form view to configure.
* @param FormInterface $form The form corresponding to the view.
* @param array $options The options used for the configuration.
*/
public function finishView(FormView $view, FormInterface $form, array $options);
/**
* Returns the configured options resolver used for this type.
*
* @return \Symfony\Component\OptionsResolver\OptionsResolverInterface The options resolver.
*/
public function getOptionsResolver();
}

View File

@@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Tests;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\Tests\Fixtures\AlternatingRowType;
abstract class AbstractDivLayoutTest extends AbstractLayoutTest
{
@@ -38,13 +39,17 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest
public function testRowOverrideVariables()
{
$view = $this->factory->createNamed('name', 'text')->createView();
$html = $this->renderRow($view, array('label' => 'foo&bar'));
$html = $this->renderRow($view, array(
'attr' => array('class' => 'my&class'),
'label' => 'foo&bar',
'label_attr' => array('class' => 'my&label&class'),
));
$this->assertMatchesXpath($html,
'/div
[
./label[@for="name"][.="[trans]foo&bar[/trans]"]
/following-sibling::input[@id="name"]
./label[@for="name"][@class="my&label&class required"][.="[trans]foo&bar[/trans]"]
/following-sibling::input[@id="name"][@class="my&class"]
]
'
);
@@ -279,6 +284,29 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest
);
}
// https://github.com/symfony/symfony/issues/5038
public function testCollectionWithAlternatingRowTypes()
{
$data = array(
array('title' => 'a'),
array('title' => 'b'),
);
$form = $this->factory->createNamed('name', 'collection', $data, array(
'type' => new AlternatingRowType(),
));
$this->assertWidgetMatchesXpath($form->createView(), array(),
'/div
[
./div[./div/div/input[@type="text"][@value="a"]]
/following-sibling::div[./div/div/textarea[.="b"]]
]
[count(./div[./div/div/input])=1]
[count(./div[./div/div/textarea])=1]
'
);
}
public function testEmptyCollection()
{
$form = $this->factory->createNamed('name', 'collection', array(), array(
@@ -437,7 +465,7 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest
public function testRepeatedWithCustomOptions()
{
$form = $this->factory->createNamed('name', 'repeated', null, array(
// the global required value cannot be overriden
// the global required value cannot be overridden
'first_options' => array('label' => 'Test', 'required' => false),
'second_options' => array('label' => 'Test2')
));

View File

@@ -66,7 +66,7 @@ abstract class AbstractLayoutTest extends FormIntegrationTestCase
// the top level
$dom->loadXml('<root>'.$html.'</root>');
} catch (\Exception $e) {
return $this->fail(sprintf(
$this->fail(sprintf(
"Failed loading HTML:\n\n%s\n\nError: %s",
$html,
$e->getMessage()
@@ -225,7 +225,7 @@ abstract class AbstractLayoutTest extends FormIntegrationTestCase
);
}
public function testLabelWithCustomOptionsPassedDirectly()
public function testLabelWithCustomAttributesPassedDirectly()
{
$form = $this->factory->createNamed('name', 'text');
$html = $this->renderLabel($form->createView(), null, array(
@@ -242,7 +242,7 @@ abstract class AbstractLayoutTest extends FormIntegrationTestCase
);
}
public function testLabelWithCustomTextAndCustomOptionsPassedDirectly()
public function testLabelWithCustomTextAndCustomAttributesPassedDirectly()
{
$form = $this->factory->createNamed('name', 'text');
$html = $this->renderLabel($form->createView(), 'Custom label', array(
@@ -260,6 +260,27 @@ abstract class AbstractLayoutTest extends FormIntegrationTestCase
);
}
// https://github.com/symfony/symfony/issues/5029
public function testLabelWithCustomTextAsOptionAndCustomAttributesPassedDirectly()
{
$form = $this->factory->createNamed('name', 'text', null, array(
'label' => 'Custom label',
));
$html = $this->renderLabel($form->createView(), null, array(
'label_attr' => array(
'class' => 'my&class'
),
));
$this->assertMatchesXpath($html,
'/label
[@for="name"]
[@class="my&class required"]
[.="[trans]Custom label[/trans]"]
'
);
}
public function testErrors()
{
$form = $this->factory->createNamed('name', 'text');

View File

@@ -116,7 +116,7 @@ abstract class AbstractTableLayoutTest extends AbstractLayoutTest
->getForm()
->createView();
// Render field2 row -> does not implicitely call renderWidget because
// Render field2 row -> does not implicitly call renderWidget because
// it is a repeated field!
$this->renderRow($view['field2']);

View File

@@ -14,7 +14,7 @@ namespace Symfony\Component\Form\Tests;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\Extension\Core\EventListener\BindRequestListener;
use Symfony\Component\Form\Extension\HttpFoundation\EventListener\BindRequestListener;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\EventDispatcher\EventDispatcher;

View File

@@ -69,8 +69,40 @@ class ChoiceListTest extends \PHPUnit_Framework_TestCase
$this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices());
$this->assertSame(array('0', '1', '2', '3'), $this->list->getValues());
$this->assertEquals(array(1 => new ChoiceView('1', 'B')), $this->list->getPreferredViews());
$this->assertEquals(array(0 => new ChoiceView('0', 'A'), 2 => new ChoiceView('2', 'C'), 3 => new ChoiceView('3', 'D')), $this->list->getRemainingViews());
$this->assertEquals(array(1 => new ChoiceView($this->obj2, '1', 'B')), $this->list->getPreferredViews());
$this->assertEquals(array(0 => new ChoiceView($this->obj1, '0', 'A'), 2 => new ChoiceView($this->obj3, '2', 'C'), 3 => new ChoiceView($this->obj4, '3', 'D')), $this->list->getRemainingViews());
}
/**
* Necessary for interoperability with MongoDB cursors or ORM relations as
* choices parameter. A choice itself that is an object implementing \Traversable
* is not treated as hierarchical structure, but as-is.
*/
public function testInitNestedTraversable()
{
$traversableChoice = new \ArrayIterator(array($this->obj3, $this->obj4));
$this->list = new ChoiceList(
new \ArrayIterator(array(
'Group' => array($this->obj1, $this->obj2),
'Not a Group' => $traversableChoice
)),
array(
'Group' => array('A', 'B'),
'Not a Group' => 'C',
),
array($this->obj2)
);
$this->assertSame(array($this->obj1, $this->obj2, $traversableChoice), $this->list->getChoices());
$this->assertSame(array('0', '1', '2'), $this->list->getValues());
$this->assertEquals(array(
'Group' => array(1 => new ChoiceView($this->obj2, '1', 'B'))
), $this->list->getPreferredViews());
$this->assertEquals(array(
'Group' => array(0 => new ChoiceView($this->obj1, '0', 'A')),
2 => new ChoiceView($traversableChoice, '2', 'C')
), $this->list->getRemainingViews());
}
public function testInitNestedArray()
@@ -78,12 +110,12 @@ class ChoiceListTest extends \PHPUnit_Framework_TestCase
$this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices());
$this->assertSame(array('0', '1', '2', '3'), $this->list->getValues());
$this->assertEquals(array(
'Group 1' => array(1 => new ChoiceView('1', 'B')),
'Group 2' => array(2 => new ChoiceView('2', 'C'))
'Group 1' => array(1 => new ChoiceView($this->obj2, '1', 'B')),
'Group 2' => array(2 => new ChoiceView($this->obj3, '2', 'C'))
), $this->list->getPreferredViews());
$this->assertEquals(array(
'Group 1' => array(0 => new ChoiceView('0', 'A')),
'Group 2' => array(3 => new ChoiceView('3', 'D'))
'Group 1' => array(0 => new ChoiceView($this->obj1, '0', 'A')),
'Group 2' => array(3 => new ChoiceView($this->obj4, '3', 'D'))
), $this->list->getRemainingViews());
}
@@ -141,4 +173,15 @@ class ChoiceListTest extends \PHPUnit_Framework_TestCase
$choices = array($this->obj2, $this->obj3, 'foobar');
$this->assertSame(array('1', '2'), $this->list->getValuesForChoices($choices));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testNonMatchingLabels()
{
$this->list = new ChoiceList(
array($this->obj1, $this->obj2),
array('A')
);
}
}

View File

@@ -49,12 +49,12 @@ class LazyChoiceListTest extends \PHPUnit_Framework_TestCase
public function testGetPreferredViews()
{
$this->assertEquals(array(1 => new ChoiceView('b', 'B')), $this->list->getPreferredViews());
$this->assertEquals(array(1 => new ChoiceView('b', 'b', 'B')), $this->list->getPreferredViews());
}
public function testGetRemainingViews()
{
$this->assertEquals(array(0 => new ChoiceView('a', 'A'), 2 => new ChoiceView('c', 'C')), $this->list->getRemainingViews());
$this->assertEquals(array(0 => new ChoiceView('a', 'a', 'A'), 2 => new ChoiceView('c', 'c', 'C')), $this->list->getRemainingViews());
}
public function testGetIndicesForChoices()

View File

@@ -81,8 +81,8 @@ class ObjectChoiceListTest extends \PHPUnit_Framework_TestCase
$this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices());
$this->assertSame(array('0', '1', '2', '3'), $this->list->getValues());
$this->assertEquals(array(1 => new ChoiceView('1', 'B')), $this->list->getPreferredViews());
$this->assertEquals(array(0 => new ChoiceView('0', 'A'), 2 => new ChoiceView('2', 'C'), 3 => new ChoiceView('3', 'D')), $this->list->getRemainingViews());
$this->assertEquals(array(1 => new ChoiceView($this->obj2, '1', 'B')), $this->list->getPreferredViews());
$this->assertEquals(array(0 => new ChoiceView($this->obj1, '0', 'A'), 2 => new ChoiceView($this->obj3, '2', 'C'), 3 => new ChoiceView($this->obj4, '3', 'D')), $this->list->getRemainingViews());
}
public function testInitNestedArray()
@@ -90,12 +90,12 @@ class ObjectChoiceListTest extends \PHPUnit_Framework_TestCase
$this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices());
$this->assertSame(array('0', '1', '2', '3'), $this->list->getValues());
$this->assertEquals(array(
'Group 1' => array(1 => new ChoiceView('1', 'B')),
'Group 2' => array(2 => new ChoiceView('2', 'C'))
'Group 1' => array(1 => new ChoiceView($this->obj2, '1', 'B')),
'Group 2' => array(2 => new ChoiceView($this->obj3, '2', 'C'))
), $this->list->getPreferredViews());
$this->assertEquals(array(
'Group 1' => array(0 => new ChoiceView('0', 'A')),
'Group 2' => array(3 => new ChoiceView('3', 'D'))
'Group 1' => array(0 => new ChoiceView($this->obj1, '0', 'A')),
'Group 2' => array(3 => new ChoiceView($this->obj4, '3', 'D'))
), $this->list->getRemainingViews());
}
@@ -123,14 +123,14 @@ class ObjectChoiceListTest extends \PHPUnit_Framework_TestCase
$this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4, $obj5, $obj6), $this->list->getChoices());
$this->assertSame(array('0', '1', '2', '3', '4', '5'), $this->list->getValues());
$this->assertEquals(array(
'Group 1' => array(1 => new ChoiceView('1', 'B')),
'Group 2' => array(2 => new ChoiceView('2', 'C'))
'Group 1' => array(1 => new ChoiceView($this->obj2, '1', 'B')),
'Group 2' => array(2 => new ChoiceView($this->obj3, '2', 'C'))
), $this->list->getPreferredViews());
$this->assertEquals(array(
'Group 1' => array(0 => new ChoiceView('0', 'A')),
'Group 2' => array(3 => new ChoiceView('3', 'D')),
4 => new ChoiceView('4', 'E'),
5 => new ChoiceView('5', 'F'),
'Group 1' => array(0 => new ChoiceView($this->obj1, '0', 'A')),
'Group 2' => array(3 => new ChoiceView($this->obj4, '3', 'D')),
4 => new ChoiceView($obj5, '4', 'E'),
5 => new ChoiceView($obj6, '5', 'F'),
), $this->list->getRemainingViews());
}
@@ -172,8 +172,8 @@ class ObjectChoiceListTest extends \PHPUnit_Framework_TestCase
$this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices());
$this->assertSame(array('10', '20', '30', '40'), $this->list->getValues());
$this->assertEquals(array(1 => new ChoiceView('20', 'B'), 2 => new ChoiceView('30', 'C')), $this->list->getPreferredViews());
$this->assertEquals(array(0 => new ChoiceView('10', 'A'), 3 => new ChoiceView('40', 'D')), $this->list->getRemainingViews());
$this->assertEquals(array(1 => new ChoiceView($this->obj2, '20', 'B'), 2 => new ChoiceView($this->obj3, '30', 'C')), $this->list->getPreferredViews());
$this->assertEquals(array(0 => new ChoiceView($this->obj1, '10', 'A'), 3 => new ChoiceView($this->obj4, '40', 'D')), $this->list->getRemainingViews());
}
public function testInitArrayUsesToString()
@@ -189,7 +189,7 @@ class ObjectChoiceListTest extends \PHPUnit_Framework_TestCase
$this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices());
$this->assertSame(array('0', '1', '2', '3'), $this->list->getValues());
$this->assertEquals(array(0 => new ChoiceView('0', 'A'), 1 => new ChoiceView('1', 'B'), 2 => new ChoiceView('2', 'C'), 3 => new ChoiceView('3', 'D')), $this->list->getRemainingViews());
$this->assertEquals(array(0 => new ChoiceView($this->obj1, '0', 'A'), 1 => new ChoiceView($this->obj2, '1', 'B'), 2 => new ChoiceView($this->obj3, '2', 'C'), 3 => new ChoiceView($this->obj4, '3', 'D')), $this->list->getRemainingViews());
}
/**

View File

@@ -55,8 +55,8 @@ class SimpleChoiceListTest extends \PHPUnit_Framework_TestCase
$this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c'), $this->list->getChoices());
$this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c'), $this->list->getValues());
$this->assertEquals(array(1 => new ChoiceView('b', 'B')), $this->list->getPreferredViews());
$this->assertEquals(array(0 => new ChoiceView('a', 'A'), 2 => new ChoiceView('c', 'C')), $this->list->getRemainingViews());
$this->assertEquals(array(1 => new ChoiceView('b', 'b', 'B')), $this->list->getPreferredViews());
$this->assertEquals(array(0 => new ChoiceView('a', 'a', 'A'), 2 => new ChoiceView('c', 'c', 'C')), $this->list->getRemainingViews());
}
public function testInitNestedArray()
@@ -64,12 +64,12 @@ class SimpleChoiceListTest extends \PHPUnit_Framework_TestCase
$this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd'), $this->list->getChoices());
$this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd'), $this->list->getValues());
$this->assertEquals(array(
'Group 1' => array(1 => new ChoiceView('b', 'B')),
'Group 2' => array(2 => new ChoiceView('c', 'C'))
'Group 1' => array(1 => new ChoiceView('b', 'b', 'B')),
'Group 2' => array(2 => new ChoiceView('c', 'c', 'C'))
), $this->list->getPreferredViews());
$this->assertEquals(array(
'Group 1' => array(0 => new ChoiceView('a', 'A')),
'Group 2' => array(3 => new ChoiceView('d', 'D'))
'Group 1' => array(0 => new ChoiceView('a', 'a', 'A')),
'Group 2' => array(3 => new ChoiceView('d', 'd', 'D'))
), $this->list->getRemainingViews());
}

View File

@@ -12,7 +12,7 @@
namespace Symfony\Component\Form\Tests\Extension\Core\DataMapper;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormConfig;
use Symfony\Component\Form\FormConfigBuilder;
use Symfony\Component\Form\FormConfigInterface;
use Symfony\Component\Form\Util\PropertyPath;
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
@@ -89,7 +89,7 @@ class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase
->with($car)
->will($this->returnValue($engine));
$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$form = $this->getForm($config);
@@ -112,7 +112,7 @@ class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase
->with($car)
->will($this->returnValue($engine));
$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(false);
$config->setPropertyPath($propertyPath);
$form = $this->getForm($config);
@@ -127,7 +127,7 @@ class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase
{
$car = new \stdClass();
$config = new FormConfig(null, '\stdClass', $this->dispatcher);
$config = new FormConfigBuilder(null, '\stdClass', $this->dispatcher);
$config->setByReference(true);
$form = $this->getForm($config);
@@ -146,7 +146,7 @@ class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase
$propertyPath->expects($this->never())
->method('getValue');
$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setMapped(false);
$config->setPropertyPath($propertyPath);
@@ -164,7 +164,7 @@ class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase
$propertyPath->expects($this->never())
->method('getValue');
$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$form = $this->getForm($config);
@@ -185,14 +185,14 @@ class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase
->with($car)
->will($this->returnValue($engine));
$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setVirtual(true);
$config->setCompound(true);
$config->setDataMapper($this->getDataMapper());
$form = $this->getForm($config);
$config = new FormConfig('engine', '\stdClass', $this->dispatcher);
$config = new FormConfigBuilder('engine', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$child = $this->getForm($config);
@@ -215,7 +215,7 @@ class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase
->method('setValue')
->with($car, $engine);
$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(false);
$config->setPropertyPath($propertyPath);
$config->setData($engine);
@@ -234,7 +234,7 @@ class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase
->method('setValue')
->with($car, $engine);
$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData($engine);
@@ -258,7 +258,7 @@ class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase
$propertyPath->expects($this->never())
->method('setValue');
$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData($engine);
@@ -276,7 +276,7 @@ class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase
$propertyPath->expects($this->never())
->method('setValue');
$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData($engine);
@@ -294,7 +294,7 @@ class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase
$propertyPath->expects($this->never())
->method('setValue');
$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData(null);
@@ -312,7 +312,7 @@ class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase
$propertyPath->expects($this->never())
->method('setValue');
$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData($engine);
@@ -330,7 +330,7 @@ class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase
$propertyPath->expects($this->never())
->method('setValue');
$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData($engine);
@@ -356,14 +356,14 @@ class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase
->method('setValue')
->with($car, $engine);
$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setPropertyPath($parentPath);
$config->setVirtual(true);
$config->setCompound(true);
$config->setDataMapper($this->getDataMapper());
$form = $this->getForm($config);
$config = new FormConfig('engine', '\stdClass', $this->dispatcher);
$config = new FormConfigBuilder('engine', '\stdClass', $this->dispatcher);
$config->setByReference(true);
$config->setPropertyPath($childPath);
$config->setData($engine);

View File

@@ -654,10 +654,10 @@ class ChoiceTypeTest extends TypeTestCase
$view = $form->createView();
$this->assertEquals(array(
new ChoiceView('a', 'A'),
new ChoiceView('b', 'B'),
new ChoiceView('c', 'C'),
new ChoiceView('d', 'D'),
new ChoiceView('a', 'a', 'A'),
new ChoiceView('b', 'b', 'B'),
new ChoiceView('c', 'c', 'C'),
new ChoiceView('d', 'd', 'D'),
), $view->vars['choices']);
}
@@ -671,12 +671,12 @@ class ChoiceTypeTest extends TypeTestCase
$view = $form->createView();
$this->assertEquals(array(
0 => new ChoiceView('a', 'A'),
2 => new ChoiceView('c', 'C'),
0 => new ChoiceView('a', 'a', 'A'),
2 => new ChoiceView('c', 'c', 'C'),
), $view->vars['choices']);
$this->assertEquals(array(
1 => new ChoiceView('b', 'B'),
3 => new ChoiceView('d', 'D'),
1 => new ChoiceView('b', 'b', 'B'),
3 => new ChoiceView('d', 'd', 'D'),
), $view->vars['preferred_choices']);
}
@@ -690,23 +690,42 @@ class ChoiceTypeTest extends TypeTestCase
$this->assertEquals(array(
'Symfony' => array(
0 => new ChoiceView('a', 'Bernhard'),
2 => new ChoiceView('c', 'Kris'),
0 => new ChoiceView('a', 'a', 'Bernhard'),
2 => new ChoiceView('c', 'c', 'Kris'),
),
'Doctrine' => array(
4 => new ChoiceView('e', 'Roman'),
4 => new ChoiceView('e', 'e', 'Roman'),
),
), $view->vars['choices']);
$this->assertEquals(array(
'Symfony' => array(
1 => new ChoiceView('b', 'Fabien'),
1 => new ChoiceView('b', 'b', 'Fabien'),
),
'Doctrine' => array(
3 => new ChoiceView('d', 'Jon'),
3 => new ChoiceView('d', 'd', 'Jon'),
),
), $view->vars['preferred_choices']);
}
public function testPassChoiceDataToView()
{
$obj1 = (object) array('value' => 'a', 'label' => 'A');
$obj2 = (object) array('value' => 'b', 'label' => 'B');
$obj3 = (object) array('value' => 'c', 'label' => 'C');
$obj4 = (object) array('value' => 'd', 'label' => 'D');
$form = $this->factory->create('choice', null, array(
'choice_list' => new ObjectChoiceList(array($obj1, $obj2, $obj3, $obj4), 'label', array(), null, 'value'),
));
$view = $form->createView();
$this->assertEquals(array(
new ChoiceView($obj1, 'a', 'A'),
new ChoiceView($obj2, 'b', 'B'),
new ChoiceView($obj3, 'c', 'C'),
new ChoiceView($obj4, 'd', 'D'),
), $view->vars['choices']);
}
public function testAdjustFullNameForMultipleNonExpanded()
{
$form = $this->factory->createNamed('name', 'choice', null, array(

View File

@@ -24,11 +24,11 @@ class CountryTypeTest extends LocalizedTestCase
$choices = $view->vars['choices'];
// Don't check objects for identity
$this->assertContains(new ChoiceView('DE', 'Deutschland'), $choices, '', false, false);
$this->assertContains(new ChoiceView('GB', 'Vereinigtes Königreich'), $choices, '', false, false);
$this->assertContains(new ChoiceView('US', 'Vereinigte Staaten'), $choices, '', false, false);
$this->assertContains(new ChoiceView('FR', 'Frankreich'), $choices, '', false, false);
$this->assertContains(new ChoiceView('MY', 'Malaysia'), $choices, '', false, false);
$this->assertContains(new ChoiceView('DE', 'DE', 'Deutschland'), $choices, '', false, false);
$this->assertContains(new ChoiceView('GB', 'GB', 'Vereinigtes Königreich'), $choices, '', false, false);
$this->assertContains(new ChoiceView('US', 'US', 'Vereinigte Staaten'), $choices, '', false, false);
$this->assertContains(new ChoiceView('FR', 'FR', 'Frankreich'), $choices, '', false, false);
$this->assertContains(new ChoiceView('MY', 'MY', 'Malaysia'), $choices, '', false, false);
}
public function testUnknownCountryIsNotIncluded()

View File

@@ -355,8 +355,8 @@ class DateTypeTest extends LocalizedTestCase
$view = $form->createView();
$this->assertEquals(array(
new ChoiceView('2010', '2010'),
new ChoiceView('2011', '2011'),
new ChoiceView('2010', '2010', '2010'),
new ChoiceView('2011', '2011', '2011'),
), $view['year']->vars['choices']);
}
@@ -369,8 +369,8 @@ class DateTypeTest extends LocalizedTestCase
$view = $form->createView();
$this->assertEquals(array(
new ChoiceView('6', '06'),
new ChoiceView('7', '07'),
new ChoiceView('6', '6', '06'),
new ChoiceView('7', '7', '07'),
), $view['month']->vars['choices']);
}
@@ -384,8 +384,8 @@ class DateTypeTest extends LocalizedTestCase
$view = $form->createView();
$this->assertEquals(array(
new ChoiceView('1', 'Jän'),
new ChoiceView('4', 'Apr')
new ChoiceView('1', '1', 'Jän'),
new ChoiceView('4', '4', 'Apr')
), $view['month']->vars['choices']);
}
@@ -399,8 +399,8 @@ class DateTypeTest extends LocalizedTestCase
$view = $form->createView();
$this->assertEquals(array(
new ChoiceView('1', 'Jänner'),
new ChoiceView('4', 'April'),
new ChoiceView('1', '1', 'Jänner'),
new ChoiceView('4', '4', 'April'),
), $view['month']->vars['choices']);
}
@@ -414,8 +414,8 @@ class DateTypeTest extends LocalizedTestCase
$view = $form->createView();
$this->assertEquals(array(
new ChoiceView('1', 'Jänner'),
new ChoiceView('4', 'April'),
new ChoiceView('1', '1', 'Jänner'),
new ChoiceView('4', '4', 'April'),
), $view['month']->vars['choices']);
}
@@ -428,8 +428,8 @@ class DateTypeTest extends LocalizedTestCase
$view = $form->createView();
$this->assertEquals(array(
new ChoiceView('6', '06'),
new ChoiceView('7', '07'),
new ChoiceView('6', '6', '06'),
new ChoiceView('7', '7', '07'),
), $view['day']->vars['choices']);
}

View File

@@ -206,7 +206,7 @@ class FormTypeTest extends TypeTestCase
$this->assertEquals('test', $view['child']->vars['translation_domain']);
}
public function testNonTranlsationDomainFormWithNonTranslationDomainParentBeingTranslationDomainDefault()
public function testNonTranslationDomainFormWithNonTranslationDomainParentBeingTranslationDomainDefault()
{
$parent = $this->factory->createNamed('parent', 'form');
$child = $this->factory->createNamed('child', 'form');
@@ -645,4 +645,18 @@ class FormTypeTest extends TypeTestCase
$this->assertSame('default', $form->getData());
}
public function testNormDataIsPassedToView()
{
$view = $this->factory->createBuilder('form')
->addViewTransformer(new FixedDataTransformer(array(
'foo' => 'bar',
)))
->setData('foo')
->getForm()
->createView();
$this->assertSame('foo', $view->vars['data']);
$this->assertSame('bar', $view->vars['value']);
}
}

View File

@@ -23,11 +23,11 @@ class LanguageTypeTest extends LocalizedTestCase
$view = $form->createView();
$choices = $view->vars['choices'];
$this->assertContains(new ChoiceView('en', 'Englisch'), $choices, '', false, false);
$this->assertContains(new ChoiceView('en_GB', 'Britisches Englisch'), $choices, '', false, false);
$this->assertContains(new ChoiceView('en_US', 'Amerikanisches Englisch'), $choices, '', false, false);
$this->assertContains(new ChoiceView('fr', 'Französisch'), $choices, '', false, false);
$this->assertContains(new ChoiceView('my', 'Birmanisch'), $choices, '', false, false);
$this->assertContains(new ChoiceView('en', 'en', 'Englisch'), $choices, '', false, false);
$this->assertContains(new ChoiceView('en_GB', 'en_GB', 'Britisches Englisch'), $choices, '', false, false);
$this->assertContains(new ChoiceView('en_US', 'en_US', 'Amerikanisches Englisch'), $choices, '', false, false);
$this->assertContains(new ChoiceView('fr', 'fr', 'Französisch'), $choices, '', false, false);
$this->assertContains(new ChoiceView('my', 'my', 'Birmanisch'), $choices, '', false, false);
}
public function testMultipleLanguagesIsNotIncluded()
@@ -36,6 +36,6 @@ class LanguageTypeTest extends LocalizedTestCase
$view = $form->createView();
$choices = $view->vars['choices'];
$this->assertNotContains(new ChoiceView('mul', 'Mehrsprachig'), $choices, '', false, false);
$this->assertNotContains(new ChoiceView('mul', 'mul', 'Mehrsprachig'), $choices, '', false, false);
}
}

View File

@@ -23,8 +23,8 @@ class LocaleTypeTest extends LocalizedTestCase
$view = $form->createView();
$choices = $view->vars['choices'];
$this->assertContains(new ChoiceView('en', 'Englisch'), $choices, '', false, false);
$this->assertContains(new ChoiceView('en_GB', 'Englisch (Vereinigtes Königreich)'), $choices, '', false, false);
$this->assertContains(new ChoiceView('zh_Hant_MO', 'Chinesisch (traditionell, Sonderverwaltungszone Macao)'), $choices, '', false, false);
$this->assertContains(new ChoiceView('en', 'en', 'Englisch'), $choices, '', false, false);
$this->assertContains(new ChoiceView('en_GB', 'en_GB', 'Englisch (Vereinigtes Königreich)'), $choices, '', false, false);
$this->assertContains(new ChoiceView('zh_Hant_MO', 'zh_Hant_MO', 'Chinesisch (traditionell, Sonderverwaltungszone Macao)'), $choices, '', false, false);
}
}

View File

@@ -49,7 +49,7 @@ class RepeatedTypeTest extends TypeTestCase
public function testSetOptionsPerChild()
{
$form = $this->factory->create('repeated', null, array(
// the global required value cannot be overriden
// the global required value cannot be overridden
'type' => 'text',
'first_options' => array('label' => 'Test', 'required' => false),
'second_options' => array('label' => 'Test2')

View File

@@ -244,8 +244,8 @@ class TimeTypeTest extends LocalizedTestCase
$view = $form->createView();
$this->assertEquals(array(
new ChoiceView('6', '06'),
new ChoiceView('7', '07'),
new ChoiceView('6', '6', '06'),
new ChoiceView('7', '7', '07'),
), $view['hour']->vars['choices']);
}
@@ -258,8 +258,8 @@ class TimeTypeTest extends LocalizedTestCase
$view = $form->createView();
$this->assertEquals(array(
new ChoiceView('6', '06'),
new ChoiceView('7', '07'),
new ChoiceView('6', '6', '06'),
new ChoiceView('7', '7', '07'),
), $view['minute']->vars['choices']);
}
@@ -273,8 +273,8 @@ class TimeTypeTest extends LocalizedTestCase
$view = $form->createView();
$this->assertEquals(array(
new ChoiceView('6', '06'),
new ChoiceView('7', '07'),
new ChoiceView('6', '6', '06'),
new ChoiceView('7', '7', '07'),
), $view['second']->vars['choices']);
}

View File

@@ -22,9 +22,9 @@ class TimezoneTypeTest extends TypeTestCase
$choices = $view->vars['choices'];
$this->assertArrayHasKey('Africa', $choices);
$this->assertContains(new ChoiceView('Africa/Kinshasa', 'Kinshasa'), $choices['Africa'], '', false, false);
$this->assertContains(new ChoiceView('Africa/Kinshasa', 'Africa/Kinshasa', 'Kinshasa'), $choices['Africa'], '', false, false);
$this->assertArrayHasKey('America', $choices);
$this->assertContains(new ChoiceView('America/New_York', 'New York'), $choices['America'], '', false, false);
$this->assertContains(new ChoiceView('America/New_York', 'America/New_York', 'New York'), $choices['America'], '', false, false);
}
}

View File

@@ -9,11 +9,11 @@
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Tests\Extension\Core\EventListener;
namespace Symfony\Component\Form\Tests\Extension\HttpFoundation\EventListener;
use Symfony\Component\Form\Extension\Core\EventListener\BindRequestListener;
use Symfony\Component\Form\Extension\HttpFoundation\EventListener\BindRequestListener;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormConfig;
use Symfony\Component\Form\FormConfigBuilder;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\File\UploadedFile;
@@ -96,7 +96,7 @@ class BindRequestListenerTest extends \PHPUnit_Framework_TestCase
));
$dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
$config = new FormConfig('author', null, $dispatcher);
$config = new FormConfigBuilder('author', null, $dispatcher);
$form = new Form($config);
$event = new FormEvent($form, $request);
@@ -123,7 +123,7 @@ class BindRequestListenerTest extends \PHPUnit_Framework_TestCase
));
$dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
$config = new FormConfig('', null, $dispatcher);
$config = new FormConfigBuilder('', null, $dispatcher);
$form = new Form($config);
$event = new FormEvent($form, $request);
@@ -150,7 +150,7 @@ class BindRequestListenerTest extends \PHPUnit_Framework_TestCase
));
$dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
$config = new FormConfig('author', null, $dispatcher);
$config = new FormConfigBuilder('author', null, $dispatcher);
$config->setCompound(true);
$config->setDataMapper($this->getMock('Symfony\Component\Form\DataMapperInterface'));
$form = new Form($config);
@@ -177,7 +177,7 @@ class BindRequestListenerTest extends \PHPUnit_Framework_TestCase
));
$dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
$config = new FormConfig('author', null, $dispatcher);
$config = new FormConfigBuilder('author', null, $dispatcher);
$config->setCompound(false);
$form = new Form($config);
$event = new FormEvent($form, $request);
@@ -201,7 +201,7 @@ class BindRequestListenerTest extends \PHPUnit_Framework_TestCase
));
$dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
$config = new FormConfig('author', null, $dispatcher);
$config = new FormConfigBuilder('author', null, $dispatcher);
$form = new Form($config);
$event = new FormEvent($form, $request);
@@ -225,7 +225,7 @@ class BindRequestListenerTest extends \PHPUnit_Framework_TestCase
));
$dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
$config = new FormConfig('', null, $dispatcher);
$config = new FormConfigBuilder('', null, $dispatcher);
$form = new Form($config);
$event = new FormEvent($form, $request);
@@ -249,7 +249,7 @@ class BindRequestListenerTest extends \PHPUnit_Framework_TestCase
));
$dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
$config = new FormConfig('author', null, $dispatcher);
$config = new FormConfigBuilder('author', null, $dispatcher);
$config->setCompound(true);
$config->setDataMapper($this->getMock('Symfony\Component\Form\DataMapperInterface'));
$form = new Form($config);
@@ -272,7 +272,7 @@ class BindRequestListenerTest extends \PHPUnit_Framework_TestCase
));
$dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
$config = new FormConfig('author', null, $dispatcher);
$config = new FormConfigBuilder('author', null, $dispatcher);
$config->setCompound(false);
$form = new Form($config);
$event = new FormEvent($form, $request);

View File

@@ -15,7 +15,7 @@ use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormConfig;
use Symfony\Component\Form\FormConfigBuilder;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\Util\PropertyPath;
use Symfony\Component\Validator\ConstraintViolation;
@@ -67,7 +67,7 @@ class ViolationMapperTest extends \PHPUnit_Framework_TestCase
protected function getForm($name = 'name', $propertyPath = null, $dataClass = null, $errorMapping = array(), $virtual = false, $synchronized = true)
{
$config = new FormConfig($name, $dataClass, $this->dispatcher, array(
$config = new FormConfigBuilder($name, $dataClass, $this->dispatcher, array(
'error_mapping' => $errorMapping,
));
$config->setMapped(true);

View File

@@ -0,0 +1,27 @@
<?php
namespace Symfony\Component\Form\Tests\Fixtures;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormBuilderInterface;
class AlternatingRowType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$formFactory = $builder->getFormFactory();
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($formFactory) {
$form = $event->getForm();
$type = $form->getName() % 2 === 0 ? 'text' : 'textarea';
$form->add($formFactory->createNamed('title', $type));
});
}
public function getName()
{
return 'alternating_row';
}
}

View File

@@ -14,7 +14,7 @@ namespace Symfony\Component\Form\Tests;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
use Symfony\Component\Form\FormConfig;
use Symfony\Component\Form\FormConfigBuilder;
class FormConfigTest extends \PHPUnit_Framework_TestCase
{
@@ -64,7 +64,7 @@ class FormConfigTest extends \PHPUnit_Framework_TestCase
$dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
try {
new FormConfig($name, null, $dispatcher);
new FormConfigBuilder($name, null, $dispatcher);
if (!$accepted) {
$this->fail(sprintf('The value "%s" should not be accepted', $name));
}

View File

@@ -44,6 +44,11 @@ class FormFactoryTest extends \PHPUnit_Framework_TestCase
*/
private $registry;
/**
* @var \PHPUnit_Framework_MockObject_MockObject
*/
private $resolvedTypeFactory;
/**
* @var FormFactory
*/
@@ -55,10 +60,11 @@ class FormFactoryTest extends \PHPUnit_Framework_TestCase
$this->markTestSkipped('The "EventDispatcher" component is not available');
}
$this->resolvedTypeFactory = $this->getMock('Symfony\Component\Form\ResolvedFormTypeFactoryInterface');
$this->guesser1 = $this->getMock('Symfony\Component\Form\FormTypeGuesserInterface');
$this->guesser2 = $this->getMock('Symfony\Component\Form\FormTypeGuesserInterface');
$this->registry = $this->getMock('Symfony\Component\Form\FormRegistryInterface');
$this->factory = new FormFactory($this->registry);
$this->factory = new FormFactory($this->registry, $this->resolvedTypeFactory);
$this->registry->expects($this->any())
->method('getTypeGuesser')
@@ -73,8 +79,8 @@ class FormFactoryTest extends \PHPUnit_Framework_TestCase
$type = new FooType();
$resolvedType = $this->getMockResolvedType();
$this->registry->expects($this->once())
->method('resolveType')
$this->resolvedTypeFactory->expects($this->once())
->method('createResolvedType')
->with($type)
->will($this->returnValue($resolvedType));
@@ -136,16 +142,11 @@ class FormFactoryTest extends \PHPUnit_Framework_TestCase
$type = $this->getMockType();
$resolvedType = $this->getMockResolvedType();
$this->registry->expects($this->once())
->method('resolveType')
$this->resolvedTypeFactory->expects($this->once())
->method('createResolvedType')
->with($type)
->will($this->returnValue($resolvedType));
// The type is also implicitely added to the registry
$this->registry->expects($this->once())
->method('addType')
->with($resolvedType);
$resolvedType->expects($this->once())
->method('createBuilder')
->with($this->factory, 'name', $options)
@@ -159,11 +160,6 @@ class FormFactoryTest extends \PHPUnit_Framework_TestCase
$options = array('a' => '1', 'b' => '2');
$resolvedType = $this->getMockResolvedType();
// The type is also implicitely added to the registry
$this->registry->expects($this->once())
->method('addType')
->with($resolvedType);
$resolvedType->expects($this->once())
->method('createBuilder')
->with($this->factory, 'name', $options)
@@ -555,7 +551,7 @@ class FormFactoryTest extends \PHPUnit_Framework_TestCase
{
return $this->getMockBuilder('Symfony\Component\Form\FormFactory')
->setMethods($methods)
->setConstructorArgs(array($this->registry))
->setConstructorArgs(array($this->registry, $this->resolvedTypeFactory))
->getMock();
}

View File

@@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Tests;
use Symfony\Component\Form\FormFactory;
use Symfony\Component\Form\ResolvedFormTypeFactory;
use Symfony\Component\Form\FormRegistry;
use Symfony\Component\Form\Extension\Core\CoreExtension;
@@ -20,6 +21,11 @@ use Symfony\Component\Form\Extension\Core\CoreExtension;
*/
class FormIntegrationTestCase extends \PHPUnit_Framework_TestCase
{
/**
* @var ResolvedFormTypeFactory
*/
protected $resolvedTypeFactory;
/**
* @var FormRegistry
*/
@@ -36,8 +42,9 @@ class FormIntegrationTestCase extends \PHPUnit_Framework_TestCase
$this->markTestSkipped('The "EventDispatcher" component is not available');
}
$this->registry = new FormRegistry($this->getExtensions());
$this->factory = new FormFactory($this->registry);
$this->resolvedTypeFactory = new ResolvedFormTypeFactory();
$this->registry = new FormRegistry($this->getExtensions(), $this->resolvedTypeFactory);
$this->factory = new FormFactory($this->registry, $this->resolvedTypeFactory);
}
protected function getExtensions()

View File

@@ -27,6 +27,11 @@ class FormRegistryTest extends \PHPUnit_Framework_TestCase
*/
private $registry;
/**
* @var \PHPUnit_Framework_MockObject_MockObject
*/
private $resolvedTypeFactory;
/**
* @var \PHPUnit_Framework_MockObject_MockObject
*/
@@ -49,6 +54,7 @@ class FormRegistryTest extends \PHPUnit_Framework_TestCase
protected function setUp()
{
$this->resolvedTypeFactory = $this->getMock('Symfony\Component\Form\ResolvedFormTypeFactory');
$this->guesser1 = $this->getMock('Symfony\Component\Form\FormTypeGuesserInterface');
$this->guesser2 = $this->getMock('Symfony\Component\Form\FormTypeGuesserInterface');
$this->extension1 = new TestExtension($this->guesser1);
@@ -56,53 +62,16 @@ class FormRegistryTest extends \PHPUnit_Framework_TestCase
$this->registry = new FormRegistry(array(
$this->extension1,
$this->extension2,
));
}
public function testResolveType()
{
$type = new FooType();
$ext1 = new FooTypeBarExtension();
$ext2 = new FooTypeBazExtension();
$this->extension1->addTypeExtension($ext1);
$this->extension2->addTypeExtension($ext2);
$resolvedType = $this->registry->resolveType($type);
$this->assertEquals($type, $resolvedType->getInnerType());
$this->assertEquals(array($ext1, $ext2), $resolvedType->getTypeExtensions());
}
public function testResolveTypeConnectsParent()
{
$parentType = new FooType();
$type = new FooSubType();
$resolvedParentType = $this->registry->resolveType($parentType);
$this->registry->addType($resolvedParentType);
$resolvedType = $this->registry->resolveType($type);
$this->assertSame($resolvedParentType, $resolvedType->getParent());
}
/**
* @expectedException Symfony\Component\Form\Exception\FormException
*/
public function testResolveTypeThrowsExceptionIfParentNotFound()
{
$type = new FooSubType();
$this->registry->resolveType($type);
), $this->resolvedTypeFactory);
}
public function testGetTypeReturnsAddedType()
{
$type = new FooType();
$resolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface');
$resolvedType = $this->registry->resolveType($type);
$resolvedType->expects($this->any())
->method('getName')
->will($this->returnValue('foo'));
$this->registry->addType($resolvedType);
@@ -112,13 +81,88 @@ class FormRegistryTest extends \PHPUnit_Framework_TestCase
public function testGetTypeFromExtension()
{
$type = new FooType();
$resolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface');
$this->extension2->addType($type);
$this->resolvedTypeFactory->expects($this->once())
->method('createResolvedType')
->with($type)
->will($this->returnValue($resolvedType));
$resolvedType->expects($this->any())
->method('getName')
->will($this->returnValue('foo'));
$resolvedType = $this->registry->getType('foo');
$this->assertInstanceOf('Symfony\Component\Form\ResolvedFormTypeInterface', $resolvedType);
$this->assertSame($type, $resolvedType->getInnerType());
$this->assertSame($resolvedType, $this->registry->getType('foo'));
}
public function testGetTypeWithTypeExtensions()
{
$type = new FooType();
$ext1 = new FooTypeBarExtension();
$ext2 = new FooTypeBazExtension();
$resolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface');
$this->extension2->addType($type);
$this->extension1->addTypeExtension($ext1);
$this->extension2->addTypeExtension($ext2);
$this->resolvedTypeFactory->expects($this->once())
->method('createResolvedType')
->with($type, array($ext1, $ext2))
->will($this->returnValue($resolvedType));
$resolvedType->expects($this->any())
->method('getName')
->will($this->returnValue('foo'));
$this->assertSame($resolvedType, $this->registry->getType('foo'));
}
public function testGetTypeConnectsParent()
{
$parentType = new FooType();
$type = new FooSubType();
$parentResolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface');
$resolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface');
$this->extension1->addType($parentType);
$this->extension2->addType($type);
$this->resolvedTypeFactory->expects($this->at(0))
->method('createResolvedType')
->with($parentType)
->will($this->returnValue($parentResolvedType));
$this->resolvedTypeFactory->expects($this->at(1))
->method('createResolvedType')
->with($type, array(), $parentResolvedType)
->will($this->returnValue($resolvedType));
$parentResolvedType->expects($this->any())
->method('getName')
->will($this->returnValue('foo'));
$resolvedType->expects($this->any())
->method('getName')
->will($this->returnValue('foo_sub_type'));
$this->assertSame($resolvedType, $this->registry->getType('foo_sub_type'));
}
/**
* @expectedException Symfony\Component\Form\Exception\FormException
*/
public function testGetTypeThrowsExceptionIfParentNotFound()
{
$type = new FooSubType();
$this->extension1->addType($type);
$this->registry->getType($type);
}
/**
@@ -139,9 +183,11 @@ class FormRegistryTest extends \PHPUnit_Framework_TestCase
public function testHasTypeAfterAdding()
{
$type = new FooType();
$resolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface');
$resolvedType = $this->registry->resolveType($type);
$resolvedType->expects($this->any())
->method('getName')
->will($this->returnValue('foo'));
$this->assertFalse($this->registry->hasType('foo'));
@@ -153,6 +199,16 @@ class FormRegistryTest extends \PHPUnit_Framework_TestCase
public function testHasTypeAfterLoadingFromExtension()
{
$type = new FooType();
$resolvedType = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface');
$this->resolvedTypeFactory->expects($this->once())
->method('createResolvedType')
->with($type)
->will($this->returnValue($resolvedType));
$resolvedType->expects($this->any())
->method('getName')
->will($this->returnValue('foo'));
$this->assertFalse($this->registry->hasType('foo'));

View File

@@ -14,7 +14,7 @@ namespace Symfony\Component\Form\Tests;
use Symfony\Component\Form\ResolvedFormType;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormConfig;
use Symfony\Component\Form\FormConfigBuilder;
use Symfony\Component\Form\Form;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

View File

@@ -15,7 +15,7 @@ use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Util\PropertyPath;
use Symfony\Component\Form\FormConfig;
use Symfony\Component\Form\FormConfigBuilder;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\Exception\TransformationFailedException;
@@ -34,7 +34,7 @@ class SimpleFormTest extends AbstractFormTest
'foo' => 'bar',
));
$config = new FormConfig('name', null, $this->dispatcher);
$config = new FormConfigBuilder('name', null, $this->dispatcher);
$config->addViewTransformer($view);
$config->addModelTransformer($model);
$config->setData('default');
@@ -45,6 +45,28 @@ class SimpleFormTest extends AbstractFormTest
$this->assertSame('bar', $form->getViewData());
}
// https://github.com/symfony/symfony/commit/d4f4038f6daf7cf88ca7c7ab089473cce5ebf7d8#commitcomment-1632879
public function testDataIsInitializedFromBind()
{
$mock = $this->getMockBuilder('\stdClass')
->setMethods(array('preSetData', 'preBind'))
->getMock();
$mock->expects($this->at(0))
->method('preSetData');
$mock->expects($this->at(1))
->method('preBind');
$config = new FormConfigBuilder('name', null, $this->dispatcher);
$config->addEventListener(FormEvents::PRE_SET_DATA, array($mock, 'preSetData'));
$config->addEventListener(FormEvents::PRE_BIND, array($mock, 'preBind'));
$form = new Form($config);
// no call to setData() or similar where the object would be
// initialized otherwise
$form->bind('foobar');
}
/**
* @expectedException Symfony\Component\Form\Exception\AlreadyBoundException
*/
@@ -694,7 +716,7 @@ class SimpleFormTest extends AbstractFormTest
*/
public function testViewDataMustNotBeObjectIfDataClassIsNull()
{
$config = new FormConfig('name', null, $this->dispatcher);
$config = new FormConfigBuilder('name', null, $this->dispatcher);
$config->addViewTransformer(new FixedDataTransformer(array(
'' => '',
'foo' => new \stdClass(),
@@ -707,7 +729,7 @@ class SimpleFormTest extends AbstractFormTest
public function testViewDataMayBeArrayAccessIfDataClassIsNull()
{
$arrayAccess = $this->getMock('\ArrayAccess');
$config = new FormConfig('name', null, $this->dispatcher);
$config = new FormConfigBuilder('name', null, $this->dispatcher);
$config->addViewTransformer(new FixedDataTransformer(array(
'' => '',
'foo' => $arrayAccess,
@@ -724,7 +746,7 @@ class SimpleFormTest extends AbstractFormTest
*/
public function testViewDataMustBeObjectIfDataClassIsSet()
{
$config = new FormConfig('name', 'stdClass', $this->dispatcher);
$config = new FormConfigBuilder('name', 'stdClass', $this->dispatcher);
$config->addViewTransformer(new FixedDataTransformer(array(
'' => '',
'foo' => array('bar' => 'baz'),
@@ -740,7 +762,7 @@ class SimpleFormTest extends AbstractFormTest
public function testSetDataCannotInvokeItself()
{
// Cycle detection to prevent endless loops
$config = new FormConfig('name', 'stdClass', $this->dispatcher);
$config = new FormConfigBuilder('name', 'stdClass', $this->dispatcher);
$config->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$event->getForm()->setData('bar');
});

View File

@@ -587,9 +587,9 @@ class PropertyPath implements \IteratorAggregate, PropertyPathInterface
* Searches for add and remove methods.
*
* @param \ReflectionClass $reflClass The reflection class for the given object
* @param string|null $singular The singular form of the property name or null.
* @param string|null $singulars The singular form of the property name or null.
*
* @return array|null An array containin the adder and remover when found, null otherwise.
* @return array|null An array containing the adder and remover when found, null otherwise.
*
* @throws InvalidPropertyException If the property does not exist.
*/

View File

@@ -39,24 +39,9 @@ class RedirectResponse extends Response
throw new \InvalidArgumentException('Cannot redirect to an empty URL.');
}
$this->targetUrl = $url;
parent::__construct('', $status, $headers);
parent::__construct(
sprintf('<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="refresh" content="1;url=%1$s" />
<title>Redirecting to %1$s</title>
</head>
<body>
Redirecting to <a href="%1$s">%1$s</a>.
</body>
</html>', htmlspecialchars($url, ENT_QUOTES, 'UTF-8')),
$status,
array_merge($headers, array('Location' => $url))
);
$this->setTargetUrl($url);
if (!$this->isRedirect()) {
throw new \InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $status));
@@ -80,4 +65,38 @@ class RedirectResponse extends Response
{
return $this->targetUrl;
}
/**
* Sets the redirect target of this response.
*
* @param string $url The URL to redirect to
*
* @return RedirectResponse The current response.
*/
public function setTargetUrl($url)
{
if (empty($url)) {
throw new \InvalidArgumentException('Cannot redirect to an empty URL.');
}
$this->targetUrl = $url;
$this->setContent(
sprintf('<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="refresh" content="1;url=%1$s" />
<title>Redirecting to %1$s</title>
</head>
<body>
Redirecting to <a href="%1$s">%1$s</a>.
</body>
</html>', htmlspecialchars($url, ENT_QUOTES, 'UTF-8')));
$this->headers->set('Location', $url);
return $this;
}
}

Some files were not shown because too many files have changed in this diff Show More