[Form] Fixed Form::all() signature for PHP 5.3.3
This commit is contained in:
parent
fcb51c97dd
commit
fd09484a61
@ -167,7 +167,7 @@ class ViolationMapper implements ViolationMapperInterface
|
||||
|
||||
// Skip forms inheriting their parent data when iterating the children
|
||||
$childIterator = new \RecursiveIteratorIterator(
|
||||
new InheritDataAwareIterator($form->all())
|
||||
new InheritDataAwareIterator($form)
|
||||
);
|
||||
|
||||
// Make the path longer until we find a matching child
|
||||
|
@ -19,6 +19,7 @@ use Symfony\Component\Form\Exception\LogicException;
|
||||
use Symfony\Component\Form\Exception\OutOfBoundsException;
|
||||
use Symfony\Component\Form\Util\FormUtil;
|
||||
use Symfony\Component\Form\Util\InheritDataAwareIterator;
|
||||
use Symfony\Component\Form\Util\OrderedHashMap;
|
||||
use Symfony\Component\PropertyAccess\PropertyPath;
|
||||
|
||||
/**
|
||||
@ -73,9 +74,9 @@ class Form implements \IteratorAggregate, FormInterface
|
||||
|
||||
/**
|
||||
* The children of this form
|
||||
* @var FormInterface[] An array of FormInterface instances
|
||||
* @var FormInterface[] A map of FormInterface instances
|
||||
*/
|
||||
private $children = array();
|
||||
private $children;
|
||||
|
||||
/**
|
||||
* The errors of this form
|
||||
@ -164,6 +165,7 @@ class Form implements \IteratorAggregate, FormInterface
|
||||
}
|
||||
|
||||
$this->config = $config;
|
||||
$this->children = new OrderedHashMap();
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
@ -370,9 +372,9 @@ class Form implements \IteratorAggregate, FormInterface
|
||||
// even if the form is compound.
|
||||
if (count($this->children) > 0) {
|
||||
// Update child forms from the data
|
||||
$childrenIterator = new InheritDataAwareIterator($this->children);
|
||||
$childrenIterator = new \RecursiveIteratorIterator($childrenIterator);
|
||||
$this->config->getDataMapper()->mapDataToForms($viewData, $childrenIterator);
|
||||
$iterator = new InheritDataAwareIterator($this->children);
|
||||
$iterator = new \RecursiveIteratorIterator($iterator);
|
||||
$this->config->getDataMapper()->mapDataToForms($viewData, $iterator);
|
||||
}
|
||||
|
||||
if ($dispatcher->hasListeners(FormEvents::POST_SET_DATA)) {
|
||||
@ -536,10 +538,7 @@ class Form implements \IteratorAggregate, FormInterface
|
||||
$submittedData = array();
|
||||
}
|
||||
|
||||
for (reset($this->children); false !== current($this->children); next($this->children)) {
|
||||
$child = current($this->children);
|
||||
$name = key($this->children);
|
||||
|
||||
foreach ($this->children as $name => $child) {
|
||||
if (array_key_exists($name, $submittedData) || $clearMissing) {
|
||||
$child->submit(isset($submittedData[$name]) ? $submittedData[$name] : null, $clearMissing);
|
||||
unset($submittedData[$name]);
|
||||
@ -587,9 +586,9 @@ class Form implements \IteratorAggregate, FormInterface
|
||||
// descendants that inherit this form's data.
|
||||
// These descendants will not be submitted normally (see the check
|
||||
// for $this->config->getInheritData() above)
|
||||
$childrenIterator = new InheritDataAwareIterator($this->children);
|
||||
$childrenIterator = new \RecursiveIteratorIterator($childrenIterator);
|
||||
$this->config->getDataMapper()->mapFormsToData($childrenIterator, $viewData);
|
||||
$iterator = new InheritDataAwareIterator($this->children);
|
||||
$iterator = new \RecursiveIteratorIterator($iterator);
|
||||
$this->config->getDataMapper()->mapFormsToData($iterator, $viewData);
|
||||
}
|
||||
|
||||
$modelData = null;
|
||||
@ -765,9 +764,9 @@ class Form implements \IteratorAggregate, FormInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function &all()
|
||||
public function all()
|
||||
{
|
||||
return $this->children;
|
||||
return iterator_to_array($this->children);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -836,10 +835,9 @@ class Form implements \IteratorAggregate, FormInterface
|
||||
$child->setParent($this);
|
||||
|
||||
if (!$this->lockSetData && $this->defaultDataSet && !$this->config->getInheritData()) {
|
||||
$children = array($child);
|
||||
$childrenIterator = new InheritDataAwareIterator($children);
|
||||
$childrenIterator = new \RecursiveIteratorIterator($childrenIterator);
|
||||
$this->config->getDataMapper()->mapDataToForms($viewData, $childrenIterator);
|
||||
$iterator = new InheritDataAwareIterator(new \ArrayIterator(array($child)));
|
||||
$iterator = new \RecursiveIteratorIterator($iterator);
|
||||
$this->config->getDataMapper()->mapDataToForms($viewData, $iterator);
|
||||
}
|
||||
|
||||
return $this;
|
||||
@ -940,11 +938,11 @@ class Form implements \IteratorAggregate, FormInterface
|
||||
/**
|
||||
* Returns the iterator for this group.
|
||||
*
|
||||
* @return \ArrayIterator
|
||||
* @return \Iterator
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->children);
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,122 +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\Tests\Util;
|
||||
|
||||
use Symfony\Component\Form\Util\InheritDataAwareIterator;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class InheritDataAwareIteratorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testSupportDynamicModification()
|
||||
{
|
||||
$form = $this->getMockForm('form');
|
||||
$formToBeAdded = $this->getMockForm('added');
|
||||
$formToBeRemoved = $this->getMockForm('removed');
|
||||
|
||||
$forms = array('form' => $form, 'removed' => $formToBeRemoved);
|
||||
$iterator = new InheritDataAwareIterator($forms);
|
||||
|
||||
$iterator->rewind();
|
||||
$this->assertTrue($iterator->valid());
|
||||
$this->assertSame('form', $iterator->key());
|
||||
$this->assertSame($form, $iterator->current());
|
||||
|
||||
// dynamic modification
|
||||
unset($forms['removed']);
|
||||
$forms['added'] = $formToBeAdded;
|
||||
|
||||
// continue iteration
|
||||
$iterator->next();
|
||||
$this->assertTrue($iterator->valid());
|
||||
$this->assertSame('added', $iterator->key());
|
||||
$this->assertSame($formToBeAdded, $iterator->current());
|
||||
|
||||
// end of array
|
||||
$iterator->next();
|
||||
$this->assertFalse($iterator->valid());
|
||||
}
|
||||
|
||||
public function testSupportDynamicModificationInRecursiveCall()
|
||||
{
|
||||
$inheritingForm = $this->getMockForm('inheriting');
|
||||
$form = $this->getMockForm('form');
|
||||
$formToBeAdded = $this->getMockForm('added');
|
||||
$formToBeRemoved = $this->getMockForm('removed');
|
||||
|
||||
$inheritingForm->getConfig()->expects($this->any())
|
||||
->method('getInheritData')
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$inheritingForm->add($form);
|
||||
$inheritingForm->add($formToBeRemoved);
|
||||
|
||||
$forms = array('inheriting' => $inheritingForm);
|
||||
$iterator = new InheritDataAwareIterator($forms);
|
||||
|
||||
$iterator->rewind();
|
||||
$this->assertTrue($iterator->valid());
|
||||
$this->assertSame('inheriting', $iterator->key());
|
||||
$this->assertSame($inheritingForm, $iterator->current());
|
||||
$this->assertTrue($iterator->hasChildren());
|
||||
|
||||
// enter nested iterator
|
||||
$nestedIterator = $iterator->getChildren();
|
||||
$this->assertSame('form', $nestedIterator->key());
|
||||
$this->assertSame($form, $nestedIterator->current());
|
||||
$this->assertFalse($nestedIterator->hasChildren());
|
||||
|
||||
// dynamic modification
|
||||
$inheritingForm->remove('removed');
|
||||
$inheritingForm->add($formToBeAdded);
|
||||
|
||||
// continue iteration - nested iterator discovers change in the form
|
||||
$nestedIterator->next();
|
||||
$this->assertTrue($nestedIterator->valid());
|
||||
$this->assertSame('added', $nestedIterator->key());
|
||||
$this->assertSame($formToBeAdded, $nestedIterator->current());
|
||||
|
||||
// end of array
|
||||
$nestedIterator->next();
|
||||
$this->assertFalse($nestedIterator->valid());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected function getMockForm($name = 'name')
|
||||
{
|
||||
$config = $this->getMock('Symfony\Component\Form\FormConfigInterface');
|
||||
|
||||
$config->expects($this->any())
|
||||
->method('getName')
|
||||
->will($this->returnValue($name));
|
||||
$config->expects($this->any())
|
||||
->method('getCompound')
|
||||
->will($this->returnValue(true));
|
||||
$config->expects($this->any())
|
||||
->method('getDataMapper')
|
||||
->will($this->returnValue($this->getMock('Symfony\Component\Form\DataMapperInterface')));
|
||||
$config->expects($this->any())
|
||||
->method('getEventDispatcher')
|
||||
->will($this->returnValue($this->getMock('Symfony\Component\EventDispatcher\EventDispatcher')));
|
||||
|
||||
return $this->getMockBuilder('Symfony\Component\Form\Form')
|
||||
->setConstructorArgs(array($config))
|
||||
->disableArgumentCloning()
|
||||
->setMethods(array('getViewData'))
|
||||
->getMock();
|
||||
}
|
||||
}
|
487
src/Symfony/Component/Form/Tests/Util/OrderedHashMapTest.php
Normal file
487
src/Symfony/Component/Form/Tests/Util/OrderedHashMapTest.php
Normal file
@ -0,0 +1,487 @@
|
||||
<?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\Tests\Util;
|
||||
|
||||
use Symfony\Component\Form\Util\OrderedHashMap;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class OrderedHashMapTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testGet()
|
||||
{
|
||||
$map = new OrderedHashMap();
|
||||
$map['first'] = 1;
|
||||
|
||||
$this->assertSame(1, $map['first']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OutOfBoundsException
|
||||
*/
|
||||
public function testGetNonExistingFails()
|
||||
{
|
||||
$map = new OrderedHashMap();
|
||||
|
||||
$map['first'];
|
||||
}
|
||||
|
||||
public function testInsertStringKeys()
|
||||
{
|
||||
$map = new OrderedHashMap();
|
||||
$map['first'] = 1;
|
||||
$map['second'] = 2;
|
||||
|
||||
$this->assertSame(array('first' => 1, 'second' => 2), iterator_to_array($map));
|
||||
}
|
||||
|
||||
public function testInsertNullKeys()
|
||||
{
|
||||
$map = new OrderedHashMap();
|
||||
$map[] = 1;
|
||||
$map['foo'] = 2;
|
||||
$map[] = 3;
|
||||
|
||||
$this->assertSame(array(0 => 1, 'foo' => 2, 1 => 3), iterator_to_array($map));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates should not change the position of an element, otherwise we could
|
||||
* turn foreach loops into endless loops if they change the current
|
||||
* element:
|
||||
*
|
||||
* foreach ($map as $index => $value) {
|
||||
* $map[$index] = $value + 1;
|
||||
* }
|
||||
*
|
||||
* And we don't want this, right? :)
|
||||
*/
|
||||
public function testUpdateDoesNotChangeElementPosition()
|
||||
{
|
||||
$map = new OrderedHashMap();
|
||||
$map['first'] = 1;
|
||||
$map['second'] = 2;
|
||||
$map['first'] = 1;
|
||||
|
||||
$this->assertSame(array('first' => 1, 'second' => 2), iterator_to_array($map));
|
||||
}
|
||||
|
||||
public function testIsset()
|
||||
{
|
||||
$map = new OrderedHashMap();
|
||||
$map['first'] = 1;
|
||||
|
||||
$this->assertTrue(isset($map['first']));
|
||||
}
|
||||
|
||||
public function testIssetReturnsFalseForNonExisting()
|
||||
{
|
||||
$map = new OrderedHashMap();
|
||||
|
||||
$this->assertFalse(isset($map['first']));
|
||||
}
|
||||
|
||||
public function testIssetReturnsFalseForNull()
|
||||
{
|
||||
$map = new OrderedHashMap();
|
||||
$map['first'] = null;
|
||||
|
||||
$this->assertFalse(isset($map['first']));
|
||||
}
|
||||
|
||||
public function testUnset()
|
||||
{
|
||||
$map = new OrderedHashMap();
|
||||
$map['first'] = 1;
|
||||
$map['second'] = 2;
|
||||
|
||||
unset($map['first']);
|
||||
|
||||
$this->assertSame(array('second' => 2), iterator_to_array($map));
|
||||
}
|
||||
|
||||
public function testUnsetNonExistingSucceeds()
|
||||
{
|
||||
$map = new OrderedHashMap();
|
||||
|
||||
unset($map['first']);
|
||||
}
|
||||
|
||||
public function testEmptyIteration()
|
||||
{
|
||||
$map = new OrderedHashMap();
|
||||
$it = $map->getIterator();
|
||||
|
||||
$it->rewind();
|
||||
$this->assertFalse($it->valid());
|
||||
$this->assertNull($it->key());
|
||||
$this->assertNull($it->current());
|
||||
}
|
||||
|
||||
public function testIterationSupportsInsertion()
|
||||
{
|
||||
$map = new OrderedHashMap(array('first' => 1));
|
||||
$it = $map->getIterator();
|
||||
|
||||
$it->rewind();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('first', $it->key());
|
||||
$this->assertSame(1, $it->current());
|
||||
|
||||
// dynamic modification
|
||||
$map['added'] = 2;
|
||||
|
||||
// iterator is unchanged
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('first', $it->key());
|
||||
$this->assertSame(1, $it->current());
|
||||
|
||||
// continue iteration
|
||||
$it->next();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('added', $it->key());
|
||||
$this->assertSame(2, $it->current());
|
||||
|
||||
// end of map
|
||||
$it->next();
|
||||
$this->assertFalse($it->valid());
|
||||
$this->assertNull($it->key());
|
||||
$this->assertNull($it->current());
|
||||
}
|
||||
|
||||
public function testIterationSupportsDeletionAndInsertion()
|
||||
{
|
||||
$map = new OrderedHashMap(array('first' => 1, 'removed' => 2));
|
||||
$it = $map->getIterator();
|
||||
|
||||
$it->rewind();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('first', $it->key());
|
||||
$this->assertSame(1, $it->current());
|
||||
|
||||
// dynamic modification
|
||||
unset($map['removed']);
|
||||
$map['added'] = 3;
|
||||
|
||||
// iterator is unchanged
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('first', $it->key());
|
||||
$this->assertSame(1, $it->current());
|
||||
|
||||
// continue iteration
|
||||
$it->next();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('added', $it->key());
|
||||
$this->assertSame(3, $it->current());
|
||||
|
||||
// end of map
|
||||
$it->next();
|
||||
$this->assertFalse($it->valid());
|
||||
$this->assertNull($it->key());
|
||||
$this->assertNull($it->current());
|
||||
}
|
||||
|
||||
public function testIterationSupportsDeletionOfCurrentElement()
|
||||
{
|
||||
$map = new OrderedHashMap(array('removed' => 1, 'next' => 2));
|
||||
$it = $map->getIterator();
|
||||
|
||||
$it->rewind();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('removed', $it->key());
|
||||
$this->assertSame(1, $it->current());
|
||||
|
||||
unset($map['removed']);
|
||||
|
||||
// iterator is unchanged
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('removed', $it->key());
|
||||
$this->assertSame(1, $it->current());
|
||||
|
||||
// continue iteration
|
||||
$it->next();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('next', $it->key());
|
||||
$this->assertSame(2, $it->current());
|
||||
|
||||
// end of map
|
||||
$it->next();
|
||||
$this->assertFalse($it->valid());
|
||||
$this->assertNull($it->key());
|
||||
$this->assertNull($it->current());
|
||||
}
|
||||
|
||||
public function testIterationIgnoresReplacementOfCurrentElement()
|
||||
{
|
||||
$map = new OrderedHashMap(array('replaced' => 1, 'next' => 2));
|
||||
$it = $map->getIterator();
|
||||
|
||||
$it->rewind();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('replaced', $it->key());
|
||||
$this->assertSame(1, $it->current());
|
||||
|
||||
$map['replaced'] = 3;
|
||||
|
||||
// iterator is unchanged
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('replaced', $it->key());
|
||||
$this->assertSame(1, $it->current());
|
||||
|
||||
// continue iteration
|
||||
$it->next();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('next', $it->key());
|
||||
$this->assertSame(2, $it->current());
|
||||
|
||||
// end of map
|
||||
$it->next();
|
||||
$this->assertFalse($it->valid());
|
||||
$this->assertNull($it->key());
|
||||
$this->assertNull($it->current());
|
||||
}
|
||||
|
||||
public function testIterationSupportsDeletionOfCurrentAndLastElement()
|
||||
{
|
||||
$map = new OrderedHashMap(array('removed' => 1));
|
||||
$it = $map->getIterator();
|
||||
|
||||
$it->rewind();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('removed', $it->key());
|
||||
$this->assertSame(1, $it->current());
|
||||
|
||||
unset($map['removed']);
|
||||
|
||||
// iterator is unchanged
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('removed', $it->key());
|
||||
$this->assertSame(1, $it->current());
|
||||
|
||||
// end of map
|
||||
$it->next();
|
||||
$this->assertFalse($it->valid());
|
||||
$this->assertNull($it->key());
|
||||
$this->assertNull($it->current());
|
||||
}
|
||||
|
||||
public function testIterationIgnoresReplacementOfCurrentAndLastElement()
|
||||
{
|
||||
$map = new OrderedHashMap(array('replaced' => 1));
|
||||
$it = $map->getIterator();
|
||||
|
||||
$it->rewind();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('replaced', $it->key());
|
||||
$this->assertSame(1, $it->current());
|
||||
|
||||
$map['replaced'] = 2;
|
||||
|
||||
// iterator is unchanged
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('replaced', $it->key());
|
||||
$this->assertSame(1, $it->current());
|
||||
|
||||
// end of map
|
||||
$it->next();
|
||||
$this->assertFalse($it->valid());
|
||||
$this->assertNull($it->key());
|
||||
$this->assertNull($it->current());
|
||||
}
|
||||
|
||||
public function testIterationSupportsDeletionOfPreviousElement()
|
||||
{
|
||||
$map = new OrderedHashMap(array('removed' => 1, 'next' => 2, 'onemore' => 3));
|
||||
$it = $map->getIterator();
|
||||
|
||||
$it->rewind();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('removed', $it->key());
|
||||
$this->assertSame(1, $it->current());
|
||||
|
||||
// continue iteration
|
||||
$it->next();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('next', $it->key());
|
||||
$this->assertSame(2, $it->current());
|
||||
|
||||
unset($map['removed']);
|
||||
|
||||
// iterator is unchanged
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('next', $it->key());
|
||||
$this->assertSame(2, $it->current());
|
||||
|
||||
// continue iteration
|
||||
$it->next();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('onemore', $it->key());
|
||||
$this->assertSame(3, $it->current());
|
||||
|
||||
// end of map
|
||||
$it->next();
|
||||
$this->assertFalse($it->valid());
|
||||
$this->assertNull($it->key());
|
||||
$this->assertNull($it->current());
|
||||
}
|
||||
|
||||
public function testIterationIgnoresReplacementOfPreviousElement()
|
||||
{
|
||||
$map = new OrderedHashMap(array('replaced' => 1, 'next' => 2, 'onemore' => 3));
|
||||
$it = $map->getIterator();
|
||||
|
||||
$it->rewind();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('replaced', $it->key());
|
||||
$this->assertSame(1, $it->current());
|
||||
|
||||
// continue iteration
|
||||
$it->next();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('next', $it->key());
|
||||
$this->assertSame(2, $it->current());
|
||||
|
||||
$map['replaced'] = 4;
|
||||
|
||||
// iterator is unchanged
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('next', $it->key());
|
||||
$this->assertSame(2, $it->current());
|
||||
|
||||
// continue iteration
|
||||
$it->next();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('onemore', $it->key());
|
||||
$this->assertSame(3, $it->current());
|
||||
|
||||
// end of map
|
||||
$it->next();
|
||||
$this->assertFalse($it->valid());
|
||||
$this->assertNull($it->key());
|
||||
$this->assertNull($it->current());
|
||||
}
|
||||
|
||||
public function testIterationSupportsDeletionOfMultiplePreviousElements()
|
||||
{
|
||||
$map = new OrderedHashMap(array('removed' => 1, 'alsoremoved' => 2, 'next' => 3, 'onemore' => 4));
|
||||
$it = $map->getIterator();
|
||||
|
||||
$it->rewind();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('removed', $it->key());
|
||||
$this->assertSame(1, $it->current());
|
||||
|
||||
// continue iteration
|
||||
$it->next();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('alsoremoved', $it->key());
|
||||
$this->assertSame(2, $it->current());
|
||||
|
||||
// continue iteration
|
||||
$it->next();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('next', $it->key());
|
||||
$this->assertSame(3, $it->current());
|
||||
|
||||
unset($map['removed'], $map['alsoremoved']);
|
||||
|
||||
// iterator is unchanged
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('next', $it->key());
|
||||
$this->assertSame(3, $it->current());
|
||||
|
||||
// continue iteration
|
||||
$it->next();
|
||||
$this->assertTrue($it->valid());
|
||||
$this->assertSame('onemore', $it->key());
|
||||
$this->assertSame(4, $it->current());
|
||||
|
||||
// end of map
|
||||
$it->next();
|
||||
$this->assertFalse($it->valid());
|
||||
$this->assertNull($it->key());
|
||||
$this->assertNull($it->current());
|
||||
}
|
||||
|
||||
public function testParallelIteration()
|
||||
{
|
||||
$map = new OrderedHashMap(array('first' => 1, 'second' => 2));
|
||||
$it1 = $map->getIterator();
|
||||
$it2 = $map->getIterator();
|
||||
|
||||
$it1->rewind();
|
||||
$this->assertTrue($it1->valid());
|
||||
$this->assertSame('first', $it1->key());
|
||||
$this->assertSame(1, $it1->current());
|
||||
|
||||
$it2->rewind();
|
||||
$this->assertTrue($it2->valid());
|
||||
$this->assertSame('first', $it2->key());
|
||||
$this->assertSame(1, $it2->current());
|
||||
|
||||
// 1: continue iteration
|
||||
$it1->next();
|
||||
$this->assertTrue($it1->valid());
|
||||
$this->assertSame('second', $it1->key());
|
||||
$this->assertSame(2, $it1->current());
|
||||
|
||||
// 2: remains unchanged
|
||||
$this->assertTrue($it2->valid());
|
||||
$this->assertSame('first', $it2->key());
|
||||
$this->assertSame(1, $it2->current());
|
||||
|
||||
// 1: advance to end of map
|
||||
$it1->next();
|
||||
$this->assertFalse($it1->valid());
|
||||
$this->assertNull($it1->key());
|
||||
$this->assertNull($it1->current());
|
||||
|
||||
// 2: remains unchanged
|
||||
$this->assertTrue($it2->valid());
|
||||
$this->assertSame('first', $it2->key());
|
||||
$this->assertSame(1, $it2->current());
|
||||
|
||||
// 2: continue iteration
|
||||
$it2->next();
|
||||
$this->assertTrue($it2->valid());
|
||||
$this->assertSame('second', $it2->key());
|
||||
$this->assertSame(2, $it2->current());
|
||||
|
||||
// 1: remains unchanged
|
||||
$this->assertFalse($it1->valid());
|
||||
$this->assertNull($it1->key());
|
||||
$this->assertNull($it1->current());
|
||||
|
||||
// 2: advance to end of map
|
||||
$it2->next();
|
||||
$this->assertFalse($it2->valid());
|
||||
$this->assertNull($it2->key());
|
||||
$this->assertNull($it2->current());
|
||||
|
||||
// 1: remains unchanged
|
||||
$this->assertFalse($it1->valid());
|
||||
$this->assertNull($it1->key());
|
||||
$this->assertNull($it1->current());
|
||||
}
|
||||
|
||||
public function testCount()
|
||||
{
|
||||
$map = new OrderedHashMap();
|
||||
$map[] = 1;
|
||||
$map['foo'] = 2;
|
||||
unset($map[0]);
|
||||
$map[] = 3;
|
||||
|
||||
$this->assertSame(2, count($map));
|
||||
}
|
||||
}
|
190
src/Symfony/Component/Form/Util/OrderedHashMap.php
Normal file
190
src/Symfony/Component/Form/Util/OrderedHashMap.php
Normal file
@ -0,0 +1,190 @@
|
||||
<?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\Util;
|
||||
|
||||
/**
|
||||
* A hash map which keeps track of deletions and additions.
|
||||
*
|
||||
* Like in associative arrays, elements can be mapped to integer or string keys.
|
||||
* Unlike associative arrays, the map keeps track of the order in which keys
|
||||
* were added and removed. This order is reflected during iteration.
|
||||
*
|
||||
* The map supports concurrent modification during iteration. That means that
|
||||
* you can insert and remove elements from within a foreach loop and the
|
||||
* iterator will reflect those changes accordingly.
|
||||
*
|
||||
* While elements that are added during the loop are recognized by the iterator,
|
||||
* changed elements are not. Otherwise the loop could be infinite if each loop
|
||||
* changes the current element:
|
||||
*
|
||||
* $map = new OrderedHashMap();
|
||||
* $map[1] = 1;
|
||||
* $map[2] = 2;
|
||||
* $map[3] = 3;
|
||||
*
|
||||
* foreach ($map as $index => $value) {
|
||||
* echo "$index: $value\n"
|
||||
* if (1 === $index) {
|
||||
* $map[1] = 4;
|
||||
* $map[] = 5;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* print_r(iterator_to_array($map));
|
||||
*
|
||||
* // => 1: 1
|
||||
* // 2: 2
|
||||
* // 3: 3
|
||||
* // 4: 5
|
||||
* // Array
|
||||
* // (
|
||||
* // [1] => 4
|
||||
* // [2] => 2
|
||||
* // [3] => 3
|
||||
* // [4] => 5
|
||||
* // )
|
||||
*
|
||||
* The map also supports multiple parallel iterators. That means that you can
|
||||
* nest foreach loops without affecting each other's iteration:
|
||||
*
|
||||
* foreach ($map as $index => $value) {
|
||||
* foreach ($map as $index2 => $value2) {
|
||||
* // ...
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @since 2.2.6
|
||||
*/
|
||||
class OrderedHashMap implements \ArrayAccess, \IteratorAggregate, \Countable
|
||||
{
|
||||
/**
|
||||
* The elements of the map, indexed by their keys.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $elements = array();
|
||||
|
||||
/**
|
||||
* The keys of the map in the order in which they were inserted or changed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $orderedKeys = array();
|
||||
|
||||
/**
|
||||
* References to the cursors of all open iterators.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $managedCursors = array();
|
||||
|
||||
/**
|
||||
* Creates a new map.
|
||||
*
|
||||
* @param array $elements The elements to insert initially.
|
||||
*
|
||||
* @since 2.2.6
|
||||
*/
|
||||
public function __construct(array $elements = array())
|
||||
{
|
||||
$this->elements = $elements;
|
||||
$this->orderedKeys = array_keys($elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @since 2.2.6
|
||||
*/
|
||||
public function offsetExists($key)
|
||||
{
|
||||
return isset($this->elements[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @since 2.2.6
|
||||
*/
|
||||
public function offsetGet($key)
|
||||
{
|
||||
if (!isset($this->elements[$key])) {
|
||||
throw new \OutOfBoundsException('The offset "' . $key . '" does not exist.');
|
||||
}
|
||||
|
||||
return $this->elements[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @since 2.2.6
|
||||
*/
|
||||
public function offsetSet($key, $value)
|
||||
{
|
||||
if (null === $key || !isset($this->elements[$key])) {
|
||||
if (null === $key) {
|
||||
$key = array() === $this->orderedKeys
|
||||
// If the array is empty, use 0 as key
|
||||
? 0
|
||||
// Imitate PHP's behavior of generating a key that equals
|
||||
// the highest existing integer key + 1
|
||||
: max($this->orderedKeys) + 1;
|
||||
}
|
||||
|
||||
$this->orderedKeys[] = $key;
|
||||
}
|
||||
|
||||
$this->elements[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @since 2.2.6
|
||||
*/
|
||||
public function offsetUnset($key)
|
||||
{
|
||||
if (false !== ($position = array_search($key, $this->orderedKeys))) {
|
||||
array_splice($this->orderedKeys, $position, 1);
|
||||
unset($this->elements[$key]);
|
||||
|
||||
foreach ($this->managedCursors as $i => $cursor) {
|
||||
if ($cursor >= $position) {
|
||||
--$this->managedCursors[$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @since 2.2.6
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new OrderedHashMapIterator($this->elements, $this->orderedKeys, $this->managedCursors);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @since 2.2.6
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->elements);
|
||||
}
|
||||
}
|
163
src/Symfony/Component/Form/Util/OrderedHashMapIterator.php
Normal file
163
src/Symfony/Component/Form/Util/OrderedHashMapIterator.php
Normal file
@ -0,0 +1,163 @@
|
||||
<?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\Util;
|
||||
|
||||
/**
|
||||
* Iterator for {@link OrderedHashMap} objects.
|
||||
*
|
||||
* This class is internal and should not be used.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @since 2.2.6
|
||||
*/
|
||||
class OrderedHashMapIterator implements \Iterator
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $elements;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $orderedKeys;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
private $cursor;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
private $cursorId;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $managedCursors;
|
||||
|
||||
/**
|
||||
* @var string|integer|null
|
||||
*/
|
||||
private $key;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
private $current;
|
||||
|
||||
/**
|
||||
* Creates a new iterator.
|
||||
*
|
||||
* @param array $elements The elements of the map, indexed by their
|
||||
* keys.
|
||||
* @param array $orderedKeys The keys of the map in the order in which
|
||||
* they should be iterated.
|
||||
* @param array $managedCursors An array from which to reference the
|
||||
* iterator's cursor as long as it is alive.
|
||||
* This array is managed by the corresponding
|
||||
* {@link OrderedHashMap} instance to support
|
||||
* recognizing the deletion of elements.
|
||||
*
|
||||
* @since 2.2.6
|
||||
*/
|
||||
public function __construct(array &$elements, array &$orderedKeys, array &$managedCursors)
|
||||
{
|
||||
$this->elements = &$elements;
|
||||
$this->orderedKeys = &$orderedKeys;
|
||||
$this->managedCursors = &$managedCursors;
|
||||
$this->cursorId = count($managedCursors);
|
||||
|
||||
$this->managedCursors[$this->cursorId] = &$this->cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the iterator's cursors from the managed cursors of the
|
||||
* corresponding {@link OrderedHashMap} instance.
|
||||
*
|
||||
* @since 2.2.6
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
// Use array_splice() instead of isset() to prevent holes in the
|
||||
// array indices, which would break the initialization of $cursorId
|
||||
array_splice($this->managedCursors, $this->cursorId, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
*{@inheritdoc}
|
||||
*
|
||||
* @since 2.2.6
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return $this->current;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @since 2.2.6
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
++$this->cursor;
|
||||
|
||||
if (isset($this->orderedKeys[$this->cursor])) {
|
||||
$this->key = $this->orderedKeys[$this->cursor];
|
||||
$this->current = $this->elements[$this->key];
|
||||
} else {
|
||||
$this->key = null;
|
||||
$this->current = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*{@inheritdoc}
|
||||
*
|
||||
* @since 2.2.6
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
*{@inheritdoc}
|
||||
*
|
||||
* @since 2.2.6
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return null !== $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
*{@inheritdoc}
|
||||
*
|
||||
* @since 2.2.6
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this->cursor = 0;
|
||||
|
||||
if (isset($this->orderedKeys[0])) {
|
||||
$this->key = $this->orderedKeys[0];
|
||||
$this->current = $this->elements[$this->key];
|
||||
} else {
|
||||
$this->key = null;
|
||||
$this->current = null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,78 +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\Util;
|
||||
|
||||
/**
|
||||
* Iterator that traverses an array.
|
||||
*
|
||||
* Contrary to {@link \ArrayIterator}, this iterator recognizes changes in the
|
||||
* original array during iteration.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ReferencingArrayIterator implements \Iterator
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $array;
|
||||
|
||||
/**
|
||||
* Creates a new iterator.
|
||||
*
|
||||
* @param array $array An array
|
||||
*/
|
||||
public function __construct(array &$array)
|
||||
{
|
||||
$this->array = &$array;
|
||||
}
|
||||
|
||||
/**
|
||||
*{@inheritdoc}
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return current($this->array);
|
||||
}
|
||||
|
||||
/**
|
||||
*{@inheritdoc}
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
next($this->array);
|
||||
}
|
||||
|
||||
/**
|
||||
*{@inheritdoc}
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return key($this->array);
|
||||
}
|
||||
|
||||
/**
|
||||
*{@inheritdoc}
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return null !== key($this->array);
|
||||
}
|
||||
|
||||
/**
|
||||
*{@inheritdoc}
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
reset($this->array);
|
||||
}
|
||||
}
|
@ -14,9 +14,6 @@ namespace Symfony\Component\Form\Util;
|
||||
/**
|
||||
* Iterator that traverses an array of forms.
|
||||
*
|
||||
* Contrary to {@link \ArrayIterator}, this iterator recognizes changes in the
|
||||
* original array during iteration.
|
||||
*
|
||||
* You can wrap the iterator into a {@link \RecursiveIterator} in order to
|
||||
* enter any child form that inherits its parent's data and iterate the children
|
||||
* of that form as well.
|
||||
@ -26,14 +23,14 @@ namespace Symfony\Component\Form\Util;
|
||||
* @deprecated Deprecated since version 2.3, to be removed in 3.0. Use
|
||||
* {@link InheritDataAwareIterator} instead.
|
||||
*/
|
||||
class VirtualFormAwareIterator extends ReferencingArrayIterator implements \RecursiveIterator
|
||||
class VirtualFormAwareIterator extends \IteratorIterator implements \RecursiveIterator
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
return new static($this->current()->all());
|
||||
return new static($this->current());
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user