Upgrade 1-11.38

This commit is contained in:
xesmyd
2026-03-30 14:10:30 +02:00
parent f2a7e6d1fc
commit ac648ef29d
24665 changed files with 69682 additions and 2205004 deletions
@@ -21,12 +21,12 @@ class NonTraversableArrayObject implements \ArrayAccess, \Countable, \Serializab
public function __construct(array $array = null)
{
$this->array = $array ?: array();
$this->array = $array ?: [];
}
public function offsetExists($offset)
{
return array_key_exists($offset, $this->array);
return \array_key_exists($offset, $this->array);
}
public function offsetGet($offset)
@@ -26,6 +26,7 @@ class TestClass
private $publicIsAccessor;
private $publicHasAccessor;
private $publicGetter;
private $date;
public function __construct($value)
{
@@ -173,4 +174,14 @@ class TestClass
{
return $this->publicGetter;
}
public function setDate(\DateTimeInterface $date)
{
$this->date = $date;
}
public function getDate()
{
return $this->date;
}
}
@@ -33,5 +33,7 @@ class TestClassMagicCall
if ('setMagicCallProperty' === $method) {
$this->magicCallProperty = reset($args);
}
return null;
}
}
@@ -21,12 +21,12 @@ class TraversableArrayObject implements \ArrayAccess, \IteratorAggregate, \Count
public function __construct(array $array = null)
{
$this->array = $array ?: array();
$this->array = $array ?: [];
}
public function offsetExists($offset)
{
return array_key_exists($offset, $this->array);
return \array_key_exists($offset, $this->array);
}
public function offsetGet($offset)
@@ -31,10 +31,10 @@ abstract class PropertyAccessorArrayAccessTest extends TestCase
public function getValidPropertyPaths()
{
return array(
array($this->getContainer(array('firstName' => 'Bernhard')), '[firstName]', 'Bernhard'),
array($this->getContainer(array('person' => $this->getContainer(array('firstName' => 'Bernhard')))), '[person][firstName]', 'Bernhard'),
);
return [
[$this->getContainer(['firstName' => 'Bernhard']), '[firstName]', 'Bernhard'],
[$this->getContainer(['person' => $this->getContainer(['firstName' => 'Bernhard'])]), '[person][firstName]', 'Bernhard'],
];
}
/**
@@ -45,16 +45,14 @@ abstract class PropertyAccessorArrayAccessTest extends TestCase
$this->assertSame($value, $this->propertyAccessor->getValue($collection, $path));
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchIndexException
*/
public function testGetValueFailsIfNoSuchIndex()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchIndexException');
$this->propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
->enableExceptionOnInvalidIndex()
->getPropertyAccessor();
$object = $this->getContainer(array('firstName' => 'Bernhard'));
$object = $this->getContainer(['firstName' => 'Bernhard']);
$this->propertyAccessor->getValue($object, '[lastName]');
}
@@ -12,6 +12,8 @@
namespace Symfony\Component\PropertyAccess\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\PropertyAccess\PropertyAccessor;
use Symfony\Component\PropertyAccess\PropertyAccessorBuilder;
class PropertyAccessorBuilderTest extends TestCase
@@ -50,7 +52,15 @@ class PropertyAccessorBuilderTest extends TestCase
public function testGetPropertyAccessor()
{
$this->assertInstanceOf('Symfony\Component\PropertyAccess\PropertyAccessor', $this->builder->getPropertyAccessor());
$this->assertInstanceOf('Symfony\Component\PropertyAccess\PropertyAccessor', $this->builder->enableMagicCall()->getPropertyAccessor());
$this->assertInstanceOf(PropertyAccessor::class, $this->builder->getPropertyAccessor());
$this->assertInstanceOf(PropertyAccessor::class, $this->builder->enableMagicCall()->getPropertyAccessor());
}
public function testUseCache()
{
$cacheItemPool = new ArrayAdapter();
$this->builder->setCacheItemPool($cacheItemPool);
$this->assertEquals($cacheItemPool, $this->builder->getCacheItemPool());
$this->assertInstanceOf(PropertyAccessor::class, $this->builder->getPropertyAccessor());
}
}
@@ -102,9 +102,9 @@ abstract class PropertyAccessorCollectionTest extends PropertyAccessorArrayAcces
{
public function testSetValueCallsAdderAndRemoverForCollections()
{
$axesBefore = $this->getContainer(array(1 => 'second', 3 => 'fourth', 4 => 'fifth'));
$axesMerged = $this->getContainer(array(1 => 'first', 2 => 'second', 3 => 'third'));
$axesAfter = $this->getContainer(array(1 => 'second', 5 => 'first', 6 => 'third'));
$axesBefore = $this->getContainer([1 => 'second', 3 => 'fourth', 4 => 'fifth']);
$axesMerged = $this->getContainer([1 => 'first', 2 => 'second', 3 => 'third']);
$axesAfter = $this->getContainer([1 => 'second', 5 => 'first', 6 => 'third']);
$axesMergedCopy = \is_object($axesMerged) ? clone $axesMerged : $axesMerged;
// Don't use a mock in order to test whether the collections are
@@ -123,77 +123,73 @@ abstract class PropertyAccessorCollectionTest extends PropertyAccessorArrayAcces
{
$car = $this->getMockBuilder(__CLASS__.'_CompositeCar')->getMock();
$structure = $this->getMockBuilder(__CLASS__.'_CarStructure')->getMock();
$axesBefore = $this->getContainer(array(1 => 'second', 3 => 'fourth'));
$axesAfter = $this->getContainer(array(0 => 'first', 1 => 'second', 2 => 'third'));
$axesBefore = $this->getContainer([1 => 'second', 3 => 'fourth']);
$axesAfter = $this->getContainer([0 => 'first', 1 => 'second', 2 => 'third']);
$car->expects($this->any())
->method('getStructure')
->will($this->returnValue($structure));
->willReturn($structure);
$structure->expects($this->at(0))
$structure->expects($this->once())
->method('getAxes')
->will($this->returnValue($axesBefore));
$structure->expects($this->at(1))
->willReturn($axesBefore);
$structure->expects($this->once())
->method('removeAxis')
->with('fourth');
$structure->expects($this->at(2))
$structure->expects($this->exactly(2))
->method('addAxis')
->with('first');
$structure->expects($this->at(3))
->method('addAxis')
->with('third');
->withConsecutive(
['first'],
['third']
);
$this->propertyAccessor->setValue($car, 'structure.axes', $axesAfter);
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
* @expectedExceptionMessage Neither the property "axes" nor one of the methods "addAx()"/"removeAx()", "addAxe()"/"removeAxe()", "addAxis()"/"removeAxis()", "setAxes()", "axes()", "__set()" or "__call()" exist and have public access in class "Mock_PropertyAccessorCollectionTest_CarNoAdderAndRemover
*/
public function testSetValueFailsIfNoAdderNorRemoverFound()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
$this->expectExceptionMessageMatches('/Could not determine access type for property "axes" in class "Mock_PropertyAccessorCollectionTest_CarNoAdderAndRemover_[^"]*"./');
$car = $this->getMockBuilder(__CLASS__.'_CarNoAdderAndRemover')->getMock();
$axesBefore = $this->getContainer(array(1 => 'second', 3 => 'fourth'));
$axesAfter = $this->getContainer(array(0 => 'first', 1 => 'second', 2 => 'third'));
$axesBefore = $this->getContainer([1 => 'second', 3 => 'fourth']);
$axesAfter = $this->getContainer([0 => 'first', 1 => 'second', 2 => 'third']);
$car->expects($this->any())
->method('getAxes')
->will($this->returnValue($axesBefore));
->willReturn($axesBefore);
$this->propertyAccessor->setValue($car, 'axes', $axesAfter);
}
public function testIsWritableReturnsTrueIfAdderAndRemoverExists()
{
$car = $this->getMockBuilder(__CLASS__.'_Car')->getMock();
$car = new PropertyAccessorCollectionTest_Car();
$this->assertTrue($this->propertyAccessor->isWritable($car, 'axes'));
}
public function testIsWritableReturnsFalseIfOnlyAdderExists()
{
$car = $this->getMockBuilder(__CLASS__.'_CarOnlyAdder')->getMock();
$car = new PropertyAccessorCollectionTest_CarOnlyAdder();
$this->assertFalse($this->propertyAccessor->isWritable($car, 'axes'));
}
public function testIsWritableReturnsFalseIfOnlyRemoverExists()
{
$car = $this->getMockBuilder(__CLASS__.'_CarOnlyRemover')->getMock();
$car = new PropertyAccessorCollectionTest_CarOnlyRemover();
$this->assertFalse($this->propertyAccessor->isWritable($car, 'axes'));
}
public function testIsWritableReturnsFalseIfNoAdderNorRemoverExists()
{
$car = $this->getMockBuilder(__CLASS__.'_CarNoAdderAndRemover')->getMock();
$car = new PropertyAccessorCollectionTest_CarNoAdderAndRemover();
$this->assertFalse($this->propertyAccessor->isWritable($car, 'axes'));
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
* expectedExceptionMessageRegExp /The property "axes" in class "Mock_PropertyAccessorCollectionTest_Car[^"]*" can be defined with the methods "addAxis()", "removeAxis()" but the new value must be an array or an instance of \Traversable, "string" given./
*/
public function testSetValueFailsIfAdderAndRemoverExistButValueIsNotTraversable()
{
$car = $this->getMockBuilder(__CLASS__.'_Car')->getMock();
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
$this->expectExceptionMessage('Could not determine access type for property "axes" in class "Symfony\Component\PropertyAccess\Tests\PropertyAccessorCollectionTest_Car".');
$car = new PropertyAccessorCollectionTest_Car();
$this->propertyAccessor->setValue($car, 'axes', 'Not an array or Traversable');
}
+324 -125
View File
@@ -12,6 +12,7 @@
namespace Symfony\Component\PropertyAccess\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
use Symfony\Component\PropertyAccess\PropertyAccessor;
use Symfony\Component\PropertyAccess\Tests\Fixtures\ReturnTyped;
@@ -20,8 +21,12 @@ use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassIsWritable;
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassMagicCall;
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassMagicGet;
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassSetValue;
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassTypeErrorInsideCall;
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestSingularAndPluralProps;
use Symfony\Component\PropertyAccess\Tests\Fixtures\Ticket5775Object;
use Symfony\Component\PropertyAccess\Tests\Fixtures\TypeHinted;
use Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty;
use Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedProperty;
class PropertyAccessorTest extends TestCase
{
@@ -37,47 +42,47 @@ class PropertyAccessorTest extends TestCase
public function getPathsWithUnexpectedType()
{
return array(
array('', 'foobar'),
array('foo', 'foobar'),
array(null, 'foobar'),
array(123, 'foobar'),
array((object) array('prop' => null), 'prop.foobar'),
array((object) array('prop' => (object) array('subProp' => null)), 'prop.subProp.foobar'),
array(array('index' => null), '[index][foobar]'),
array(array('index' => array('subIndex' => null)), '[index][subIndex][foobar]'),
);
return [
['', 'foobar'],
['foo', 'foobar'],
[null, 'foobar'],
[123, 'foobar'],
[(object) ['prop' => null], 'prop.foobar'],
[(object) ['prop' => (object) ['subProp' => null]], 'prop.subProp.foobar'],
[['index' => null], '[index][foobar]'],
[['index' => ['subIndex' => null]], '[index][subIndex][foobar]'],
];
}
public function getPathsWithMissingProperty()
{
return array(
array((object) array('firstName' => 'Bernhard'), 'lastName'),
array((object) array('property' => (object) array('firstName' => 'Bernhard')), 'property.lastName'),
array(array('index' => (object) array('firstName' => 'Bernhard')), '[index].lastName'),
array(new TestClass('Bernhard'), 'protectedProperty'),
array(new TestClass('Bernhard'), 'privateProperty'),
array(new TestClass('Bernhard'), 'protectedAccessor'),
array(new TestClass('Bernhard'), 'protectedIsAccessor'),
array(new TestClass('Bernhard'), 'protectedHasAccessor'),
array(new TestClass('Bernhard'), 'privateAccessor'),
array(new TestClass('Bernhard'), 'privateIsAccessor'),
array(new TestClass('Bernhard'), 'privateHasAccessor'),
return [
[(object) ['firstName' => 'Bernhard'], 'lastName'],
[(object) ['property' => (object) ['firstName' => 'Bernhard']], 'property.lastName'],
[['index' => (object) ['firstName' => 'Bernhard']], '[index].lastName'],
[new TestClass('Bernhard'), 'protectedProperty'],
[new TestClass('Bernhard'), 'privateProperty'],
[new TestClass('Bernhard'), 'protectedAccessor'],
[new TestClass('Bernhard'), 'protectedIsAccessor'],
[new TestClass('Bernhard'), 'protectedHasAccessor'],
[new TestClass('Bernhard'), 'privateAccessor'],
[new TestClass('Bernhard'), 'privateIsAccessor'],
[new TestClass('Bernhard'), 'privateHasAccessor'],
// Properties are not camelized
array(new TestClass('Bernhard'), 'public_property'),
);
[new TestClass('Bernhard'), 'public_property'],
];
}
public function getPathsWithMissingIndex()
{
return array(
array(array('firstName' => 'Bernhard'), '[lastName]'),
array(array(), '[index][lastName]'),
array(array('index' => array()), '[index][lastName]'),
array(array('index' => array('firstName' => 'Bernhard')), '[index][lastName]'),
array((object) array('property' => array('firstName' => 'Bernhard')), 'property[lastName]'),
);
return [
[['firstName' => 'Bernhard'], '[lastName]'],
[[], '[index][lastName]'],
[['index' => []], '[index][lastName]'],
[['index' => ['firstName' => 'Bernhard']], '[index][lastName]'],
[(object) ['property' => ['firstName' => 'Bernhard']], 'property[lastName]'],
];
}
/**
@@ -90,10 +95,10 @@ class PropertyAccessorTest extends TestCase
/**
* @dataProvider getPathsWithMissingProperty
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
*/
public function testGetValueThrowsExceptionIfPropertyNotFound($objectOrArray, $path)
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
$this->propertyAccessor->getValue($objectOrArray, $path);
}
@@ -107,19 +112,92 @@ class PropertyAccessorTest extends TestCase
/**
* @dataProvider getPathsWithMissingIndex
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchIndexException
*/
public function testGetValueThrowsExceptionIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchIndexException');
$this->propertyAccessor = new PropertyAccessor(false, true);
$this->propertyAccessor->getValue($objectOrArray, $path);
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchIndexException
* @requires PHP 7.4
*/
public function testGetValueThrowsExceptionIfUninitializedProperty()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\AccessException');
$this->expectExceptionMessage('The property "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedProperty::$uninitialized" is not readable because it is typed "string". You should initialize it or declare a default value instead.');
$this->propertyAccessor->getValue(new UninitializedProperty(), 'uninitialized');
}
/**
* @requires PHP 7
*/
public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetter()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\AccessException');
$this->expectExceptionMessage('The method "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?');
$this->propertyAccessor->getValue(new UninitializedPrivateProperty(), 'uninitialized');
}
/**
* @requires PHP 7
*/
public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousClass()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\AccessException');
$this->expectExceptionMessage('The method "class@anonymous::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?');
$object = eval('return new class() {
private $uninitialized;
public function getUninitialized(): array
{
return $this->uninitialized;
}
};');
$this->propertyAccessor->getValue($object, 'uninitialized');
}
/**
* @requires PHP 7
*/
public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousStdClass()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\AccessException');
$this->expectExceptionMessage('The method "stdClass@anonymous::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?');
$object = eval('return new class() extends \stdClass {
private $uninitialized;
public function getUninitialized(): array
{
return $this->uninitialized;
}
};');
$this->propertyAccessor->getValue($object, 'uninitialized');
}
/**
* @requires PHP 7
*/
public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousChildClass()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\AccessException');
$this->expectExceptionMessage('The method "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty@anonymous::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?');
$object = eval('return new class() extends \Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty {};');
$this->propertyAccessor->getValue($object, 'uninitialized');
}
public function testGetValueThrowsExceptionIfNotArrayAccess()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchIndexException');
$this->propertyAccessor->getValue(new \stdClass(), '[index]');
}
@@ -131,10 +209,10 @@ class PropertyAccessorTest extends TestCase
public function testGetValueReadsArrayWithMissingIndexForCustomPropertyPath()
{
$object = new \ArrayObject();
$array = array('child' => array('index' => $object));
$array = ['child' => ['index' => $object]];
$this->assertNull($this->propertyAccessor->getValue($array, '[child][index][foo][bar]'));
$this->assertSame(array(), $object->getArrayCopy());
$this->assertSame([], $object->getArrayCopy());
}
// https://github.com/symfony/symfony/pull/4450
@@ -146,31 +224,29 @@ class PropertyAccessorTest extends TestCase
public function testGetValueNotModifyObject()
{
$object = new \stdClass();
$object->firstName = array('Bernhard');
$object->firstName = ['Bernhard'];
$this->assertNull($this->propertyAccessor->getValue($object, 'firstName[1]'));
$this->assertSame(array('Bernhard'), $object->firstName);
$this->assertSame(['Bernhard'], $object->firstName);
}
public function testGetValueNotModifyObjectException()
{
$propertyAccessor = new PropertyAccessor(false, true);
$object = new \stdClass();
$object->firstName = array('Bernhard');
$object->firstName = ['Bernhard'];
try {
$propertyAccessor->getValue($object, 'firstName[1]');
} catch (NoSuchIndexException $e) {
}
$this->assertSame(array('Bernhard'), $object->firstName);
$this->assertSame(['Bernhard'], $object->firstName);
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
*/
public function testGetValueDoesNotReadMagicCallByDefault()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
$this->propertyAccessor->getValue(new TestClassMagicCall('Bernhard'), 'magicCallProperty');
}
@@ -191,11 +267,11 @@ class PropertyAccessorTest extends TestCase
/**
* @dataProvider getPathsWithUnexpectedType
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
* @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on
*/
public function testGetValueThrowsExceptionIfNotObjectOrArray($objectOrArray, $path)
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException');
$this->expectExceptionMessage('PropertyAccessor requires a graph of objects or arrays to operate on');
$this->propertyAccessor->getValue($objectOrArray, $path);
}
@@ -211,10 +287,10 @@ class PropertyAccessorTest extends TestCase
/**
* @dataProvider getPathsWithMissingProperty
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
*/
public function testSetValueThrowsExceptionIfPropertyNotFound($objectOrArray, $path)
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
$this->propertyAccessor->setValue($objectOrArray, $path, 'Updated');
}
@@ -239,11 +315,9 @@ class PropertyAccessorTest extends TestCase
$this->assertSame('Updated', $this->propertyAccessor->getValue($objectOrArray, $path));
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchIndexException
*/
public function testSetValueThrowsExceptionIfNotArrayAccess()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchIndexException');
$object = new \stdClass();
$this->propertyAccessor->setValue($object, '[index]', 'Updated');
@@ -258,21 +332,17 @@ class PropertyAccessorTest extends TestCase
$this->assertEquals('Updated', $author->__get('magicProperty'));
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
*/
public function testSetValueThrowsExceptionIfThereAreMissingParameters()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
$object = new TestClass('Bernhard');
$this->propertyAccessor->setValue($object, 'publicAccessorWithMoreRequiredParameters', 'Updated');
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
*/
public function testSetValueDoesNotUpdateMagicCallByDefault()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
$author = new TestClassMagicCall('Bernhard');
$this->propertyAccessor->setValue($author, 'magicCallProperty', 'Updated');
@@ -286,23 +356,23 @@ class PropertyAccessorTest extends TestCase
$this->propertyAccessor->setValue($author, 'magicCallProperty', 'Updated');
$this->assertEquals('Updated', $author->__call('getMagicCallProperty', array()));
$this->assertEquals('Updated', $author->__call('getMagicCallProperty', []));
}
/**
* @dataProvider getPathsWithUnexpectedType
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
* @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on
*/
public function testSetValueThrowsExceptionIfNotObjectOrArray($objectOrArray, $path)
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException');
$this->expectExceptionMessage('PropertyAccessor requires a graph of objects or arrays to operate on');
$this->propertyAccessor->setValue($objectOrArray, $path, 'value');
}
public function testGetValueWhenArrayValueIsNull()
{
$this->propertyAccessor = new PropertyAccessor(false, true);
$this->assertNull($this->propertyAccessor->getValue(array('index' => array('nullable' => null)), '[index][nullable]'));
$this->assertNull($this->propertyAccessor->getValue(['index' => ['nullable' => null]], '[index][nullable]'));
}
/**
@@ -429,47 +499,47 @@ class PropertyAccessorTest extends TestCase
public function getValidPropertyPaths()
{
return array(
array(array('Bernhard', 'Schussek'), '[0]', 'Bernhard'),
array(array('Bernhard', 'Schussek'), '[1]', 'Schussek'),
array(array('firstName' => 'Bernhard'), '[firstName]', 'Bernhard'),
array(array('index' => array('firstName' => 'Bernhard')), '[index][firstName]', 'Bernhard'),
array((object) array('firstName' => 'Bernhard'), 'firstName', 'Bernhard'),
array((object) array('property' => array('firstName' => 'Bernhard')), 'property[firstName]', 'Bernhard'),
array(array('index' => (object) array('firstName' => 'Bernhard')), '[index].firstName', 'Bernhard'),
array((object) array('property' => (object) array('firstName' => 'Bernhard')), 'property.firstName', 'Bernhard'),
return [
[['Bernhard', 'Schussek'], '[0]', 'Bernhard'],
[['Bernhard', 'Schussek'], '[1]', 'Schussek'],
[['firstName' => 'Bernhard'], '[firstName]', 'Bernhard'],
[['index' => ['firstName' => 'Bernhard']], '[index][firstName]', 'Bernhard'],
[(object) ['firstName' => 'Bernhard'], 'firstName', 'Bernhard'],
[(object) ['property' => ['firstName' => 'Bernhard']], 'property[firstName]', 'Bernhard'],
[['index' => (object) ['firstName' => 'Bernhard']], '[index].firstName', 'Bernhard'],
[(object) ['property' => (object) ['firstName' => 'Bernhard']], 'property.firstName', 'Bernhard'],
// Accessor methods
array(new TestClass('Bernhard'), 'publicProperty', 'Bernhard'),
array(new TestClass('Bernhard'), 'publicAccessor', 'Bernhard'),
array(new TestClass('Bernhard'), 'publicAccessorWithDefaultValue', 'Bernhard'),
array(new TestClass('Bernhard'), 'publicAccessorWithRequiredAndDefaultValue', 'Bernhard'),
array(new TestClass('Bernhard'), 'publicIsAccessor', 'Bernhard'),
array(new TestClass('Bernhard'), 'publicHasAccessor', 'Bernhard'),
array(new TestClass('Bernhard'), 'publicGetSetter', 'Bernhard'),
[new TestClass('Bernhard'), 'publicProperty', 'Bernhard'],
[new TestClass('Bernhard'), 'publicAccessor', 'Bernhard'],
[new TestClass('Bernhard'), 'publicAccessorWithDefaultValue', 'Bernhard'],
[new TestClass('Bernhard'), 'publicAccessorWithRequiredAndDefaultValue', 'Bernhard'],
[new TestClass('Bernhard'), 'publicIsAccessor', 'Bernhard'],
[new TestClass('Bernhard'), 'publicHasAccessor', 'Bernhard'],
[new TestClass('Bernhard'), 'publicGetSetter', 'Bernhard'],
// Methods are camelized
array(new TestClass('Bernhard'), 'public_accessor', 'Bernhard'),
array(new TestClass('Bernhard'), '_public_accessor', 'Bernhard'),
[new TestClass('Bernhard'), 'public_accessor', 'Bernhard'],
[new TestClass('Bernhard'), '_public_accessor', 'Bernhard'],
// Missing indices
array(array('index' => array()), '[index][firstName]', null),
array(array('root' => array('index' => array())), '[root][index][firstName]', null),
[['index' => []], '[index][firstName]', null],
[['root' => ['index' => []]], '[root][index][firstName]', null],
// Special chars
array(array('%!@$§.' => 'Bernhard'), '[%!@$§.]', 'Bernhard'),
array(array('index' => array('%!@$§.' => 'Bernhard')), '[index][%!@$§.]', 'Bernhard'),
array((object) array('%!@$§' => 'Bernhard'), '%!@$§', 'Bernhard'),
array((object) array('property' => (object) array('%!@$§' => 'Bernhard')), 'property.%!@$§', 'Bernhard'),
[['%!@$§.' => 'Bernhard'], '[%!@$§.]', 'Bernhard'],
[['index' => ['%!@$§.' => 'Bernhard']], '[index][%!@$§.]', 'Bernhard'],
[(object) ['%!@$§' => 'Bernhard'], '%!@$§', 'Bernhard'],
[(object) ['property' => (object) ['%!@$§' => 'Bernhard']], 'property.%!@$§', 'Bernhard'],
// nested objects and arrays
array(array('foo' => new TestClass('bar')), '[foo].publicGetSetter', 'bar'),
array(new TestClass(array('foo' => 'bar')), 'publicGetSetter[foo]', 'bar'),
array(new TestClass(new TestClass('bar')), 'publicGetter.publicGetSetter', 'bar'),
array(new TestClass(array('foo' => new TestClass('bar'))), 'publicGetter[foo].publicGetSetter', 'bar'),
array(new TestClass(new TestClass(new TestClass('bar'))), 'publicGetter.publicGetter.publicGetSetter', 'bar'),
array(new TestClass(array('foo' => array('baz' => new TestClass('bar')))), 'publicGetter[foo][baz].publicGetSetter', 'bar'),
);
[['foo' => new TestClass('bar')], '[foo].publicGetSetter', 'bar'],
[new TestClass(['foo' => 'bar']), 'publicGetSetter[foo]', 'bar'],
[new TestClass(new TestClass('bar')), 'publicGetter.publicGetSetter', 'bar'],
[new TestClass(['foo' => new TestClass('bar')]), 'publicGetter[foo].publicGetSetter', 'bar'],
[new TestClass(new TestClass(new TestClass('bar'))), 'publicGetter.publicGetter.publicGetSetter', 'bar'],
[new TestClass(['foo' => ['baz' => new TestClass('bar')]]), 'publicGetter[foo][baz].publicGetSetter', 'bar'],
];
}
public function testTicket5755()
@@ -484,20 +554,20 @@ class PropertyAccessorTest extends TestCase
public function testSetValueDeepWithMagicGetter()
{
$obj = new TestClassMagicGet('foo');
$obj->publicProperty = array('foo' => array('bar' => 'some_value'));
$obj->publicProperty = ['foo' => ['bar' => 'some_value']];
$this->propertyAccessor->setValue($obj, 'publicProperty[foo][bar]', 'Updated');
$this->assertSame('Updated', $obj->publicProperty['foo']['bar']);
}
public function getReferenceChainObjectsForSetValue()
{
return array(
array(array('a' => array('b' => array('c' => 'old-value'))), '[a][b][c]', 'new-value'),
array(new TestClassSetValue(new TestClassSetValue('old-value')), 'value.value', 'new-value'),
array(new TestClassSetValue(array('a' => array('b' => array('c' => new TestClassSetValue('old-value'))))), 'value[a][b][c].value', 'new-value'),
array(new TestClassSetValue(array('a' => array('b' => 'old-value'))), 'value[a][b]', 'new-value'),
array(new \ArrayIterator(array('a' => array('b' => array('c' => 'old-value')))), '[a][b][c]', 'new-value'),
);
return [
[['a' => ['b' => ['c' => 'old-value']]], '[a][b][c]', 'new-value'],
[new TestClassSetValue(new TestClassSetValue('old-value')), 'value.value', 'new-value'],
[new TestClassSetValue(['a' => ['b' => ['c' => new TestClassSetValue('old-value')]]]), 'value[a][b][c].value', 'new-value'],
[new TestClassSetValue(['a' => ['b' => 'old-value']]), 'value[a][b]', 'new-value'],
[new \ArrayIterator(['a' => ['b' => ['c' => 'old-value']]]), '[a][b][c]', 'new-value'],
];
}
/**
@@ -512,11 +582,11 @@ class PropertyAccessorTest extends TestCase
public function getReferenceChainObjectsForIsWritable()
{
return array(
array(new TestClassIsWritable(array('a' => array('b' => 'old-value'))), 'value[a][b]', false),
array(new TestClassIsWritable(new \ArrayIterator(array('a' => array('b' => 'old-value')))), 'value[a][b]', true),
array(new TestClassIsWritable(array('a' => array('b' => array('c' => new TestClassSetValue('old-value'))))), 'value[a][b][c].value', true),
);
return [
[new TestClassIsWritable(['a' => ['b' => 'old-value']]), 'value[a][b]', false],
[new TestClassIsWritable(new \ArrayIterator(['a' => ['b' => 'old-value']])), 'value[a][b]', true],
[new TestClassIsWritable(['a' => ['b' => ['c' => new TestClassSetValue('old-value')]]]), 'value[a][b][c].value', true],
];
}
/**
@@ -527,23 +597,19 @@ class PropertyAccessorTest extends TestCase
$this->assertEquals($value, $this->propertyAccessor->isWritable($object, $path));
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException
* @expectedExceptionMessage Expected argument of type "DateTime", "string" given
*/
public function testThrowTypeError()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\InvalidArgumentException');
$this->expectExceptionMessage('Expected argument of type "DateTime", "string" given');
$object = new TypeHinted();
$this->propertyAccessor->setValue($object, 'date', 'This is a string, \DateTime expected.');
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException
* @expectedExceptionMessage Expected argument of type "DateTime", "NULL" given
*/
public function testThrowTypeErrorWithNullArgument()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\InvalidArgumentException');
$this->expectExceptionMessage('Expected argument of type "DateTime", "null" given');
$object = new TypeHinted();
$this->propertyAccessor->setValue($object, 'date', null);
@@ -560,46 +626,179 @@ class PropertyAccessorTest extends TestCase
public function testArrayNotBeeingOverwritten()
{
$value = array('value1' => 'foo', 'value2' => 'bar');
$value = ['value1' => 'foo', 'value2' => 'bar'];
$object = new TestClass($value);
$this->propertyAccessor->setValue($object, 'publicAccessor[value2]', 'baz');
$this->assertSame('baz', $this->propertyAccessor->getValue($object, 'publicAccessor[value2]'));
$this->assertSame(array('value1' => 'foo', 'value2' => 'baz'), $object->getPublicAccessor());
$this->assertSame(['value1' => 'foo', 'value2' => 'baz'], $object->getPublicAccessor());
}
public function testCacheReadAccess()
{
$obj = new TestClass('foo');
$propertyAccessor = new PropertyAccessor(false, false, new ArrayAdapter());
$this->assertEquals('foo', $propertyAccessor->getValue($obj, 'publicGetSetter'));
$propertyAccessor->setValue($obj, 'publicGetSetter', 'bar');
$propertyAccessor->setValue($obj, 'publicGetSetter', 'baz');
$this->assertEquals('baz', $propertyAccessor->getValue($obj, 'publicGetSetter'));
}
public function testAttributeWithSpecialChars()
{
$obj = new \stdClass();
$obj->{'@foo'} = 'bar';
$obj->{'a/b'} = '1';
$obj->{'a%2Fb'} = '2';
$propertyAccessor = new PropertyAccessor(false, false, new ArrayAdapter());
$this->assertSame('bar', $propertyAccessor->getValue($obj, '@foo'));
$this->assertSame('1', $propertyAccessor->getValue($obj, 'a/b'));
$this->assertSame('2', $propertyAccessor->getValue($obj, 'a%2Fb'));
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException
* @expectedExceptionMessage Expected argument of type "Countable", "string" given
*/
public function testThrowTypeErrorWithInterface()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\InvalidArgumentException');
$this->expectExceptionMessage('Expected argument of type "Countable", "string" given');
$object = new TypeHinted();
$this->propertyAccessor->setValue($object, 'countable', 'This is a string, \Countable expected.');
}
/**
* @requires PHP 7
*
* @expectedException \TypeError
* @requires PHP 7.0
*/
public function testDoNotDiscardReturnTypeError()
public function testAnonymousClassRead()
{
$object = new ReturnTyped();
$value = 'bar';
$this->propertyAccessor->setValue($object, 'foos', array(new \DateTime()));
$obj = $this->generateAnonymousClass($value);
$propertyAccessor = new PropertyAccessor(false, false, new ArrayAdapter());
$this->assertEquals($value, $propertyAccessor->getValue($obj, 'foo'));
}
/**
* @requires PHP 7.0
*/
public function testAnonymousClassWrite()
{
$value = 'bar';
$obj = $this->generateAnonymousClass('');
$propertyAccessor = new PropertyAccessor(false, false, new ArrayAdapter());
$propertyAccessor->setValue($obj, 'foo', $value);
$this->assertEquals($value, $propertyAccessor->getValue($obj, 'foo'));
}
private function generateAnonymousClass($value)
{
$obj = eval('return new class($value)
{
private $foo;
public function __construct($foo)
{
$this->foo = $foo;
}
/**
* @return mixed
*/
public function getFoo()
{
return $this->foo;
}
/**
* @param mixed $foo
*/
public function setFoo($foo)
{
$this->foo = $foo;
}
};');
return $obj;
}
/**
* @requires PHP 7.0
*/
public function testThrowTypeErrorInsideSetterCall()
{
$this->expectException('TypeError');
$object = new TestClassTypeErrorInsideCall();
$this->propertyAccessor->setValue($object, 'property', 'foo');
}
/**
* @requires PHP 7
*/
public function testDoNotDiscardReturnTypeError()
{
$this->expectException('TypeError');
$object = new ReturnTyped();
$this->propertyAccessor->setValue($object, 'foos', [new \DateTime()]);
}
/**
* @requires PHP 7
*
* @expectedException \TypeError
*/
public function testDoNotDiscardReturnTypeErrorWhenWriterMethodIsMisconfigured()
{
$this->expectException('TypeError');
$object = new ReturnTyped();
$this->propertyAccessor->setValue($object, 'name', 'foo');
}
public function testWriteToSingularPropertyWhilePluralOneExists()
{
$object = new TestSingularAndPluralProps();
$this->propertyAccessor->isWritable($object, 'email'); //cache access info
$this->propertyAccessor->setValue($object, 'email', 'test@email.com');
self::assertEquals('test@email.com', $object->getEmail());
self::assertEmpty($object->getEmails());
}
public function testWriteToPluralPropertyWhileSingularOneExists()
{
$object = new TestSingularAndPluralProps();
$this->propertyAccessor->isWritable($object, 'emails'); //cache access info
$this->propertyAccessor->setValue($object, 'emails', ['test@email.com']);
$this->assertEquals(['test@email.com'], $object->getEmails());
$this->assertNull($object->getEmail());
}
public function testAdderAndRemoverArePreferredOverSetter()
{
$object = new TestPluralAdderRemoverAndSetter();
$this->propertyAccessor->isWritable($object, 'emails'); //cache access info
$this->propertyAccessor->setValue($object, 'emails', ['test@email.com']);
$this->assertEquals(['test@email.com'], $object->getEmails());
}
public function testAdderAndRemoverArePreferredOverSetterForSameSingularAndPlural()
{
$object = new TestPluralAdderRemoverAndSetterSameSingularAndPlural();
$this->propertyAccessor->isWritable($object, 'aircraft'); //cache access info
$this->propertyAccessor->setValue($object, 'aircraft', ['aeroplane']);
$this->assertEquals(['aeroplane'], $object->getAircraft());
}
}
@@ -116,19 +116,15 @@ class PropertyPathBuilderTest extends TestCase
$this->assertEquals($path, $this->builder->getPropertyPath());
}
/**
* @expectedException \OutOfBoundsException
*/
public function testReplaceByIndexDoesNotAllowInvalidOffsets()
{
$this->expectException('OutOfBoundsException');
$this->builder->replaceByIndex(6, 'new1');
}
/**
* @expectedException \OutOfBoundsException
*/
public function testReplaceByIndexDoesNotAllowNegativeOffsets()
{
$this->expectException('OutOfBoundsException');
$this->builder->replaceByIndex(-1, 'new1');
}
@@ -150,19 +146,15 @@ class PropertyPathBuilderTest extends TestCase
$this->assertEquals($path, $this->builder->getPropertyPath());
}
/**
* @expectedException \OutOfBoundsException
*/
public function testReplaceByPropertyDoesNotAllowInvalidOffsets()
{
$this->expectException('OutOfBoundsException');
$this->builder->replaceByProperty(6, 'new1');
}
/**
* @expectedException \OutOfBoundsException
*/
public function testReplaceByPropertyDoesNotAllowNegativeOffsets()
{
$this->expectException('OutOfBoundsException');
$this->builder->replaceByProperty(-1, 'new1');
}
@@ -195,19 +187,19 @@ class PropertyPathBuilderTest extends TestCase
/**
* @dataProvider provideInvalidOffsets
* @expectedException \OutOfBoundsException
*/
public function testReplaceDoesNotAllowInvalidOffsets($offset)
{
$this->expectException('OutOfBoundsException');
$this->builder->replace($offset, 1, new PropertyPath('new1[new2].new3'));
}
public function provideInvalidOffsets()
{
return array(
array(6),
array(-7),
);
return [
[6],
[-7],
];
}
public function testReplaceWithLengthGreaterOne()
@@ -270,19 +262,36 @@ class PropertyPathBuilderTest extends TestCase
$this->assertEquals($path, $this->builder->getPropertyPath());
}
/**
* @expectedException \OutOfBoundsException
*/
public function testRemoveDoesNotAllowInvalidOffsets()
{
$this->expectException('OutOfBoundsException');
$this->builder->remove(6);
}
/**
* @expectedException \OutOfBoundsException
*/
public function testRemoveDoesNotAllowNegativeOffsets()
{
$this->expectException('OutOfBoundsException');
$this->builder->remove(-1);
}
public function testRemoveAndAppendAtTheEnd()
{
$this->builder->remove($this->builder->getLength() - 1);
$path = new PropertyPath('old1[old2].old3[old4][old5]');
$this->assertEquals($path, $this->builder->getPropertyPath());
$this->builder->appendProperty('old7');
$path = new PropertyPath('old1[old2].old3[old4][old5].old7');
$this->assertEquals($path, $this->builder->getPropertyPath());
$this->builder->remove($this->builder->getLength() - 1);
$path = new PropertyPath('old1[old2].old3[old4][old5]');
$this->assertEquals($path, $this->builder->getPropertyPath());
}
}
+21 -43
View File
@@ -23,65 +23,55 @@ class PropertyPathTest extends TestCase
$this->assertEquals('reference.traversable[index].property', $path->__toString());
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException
*/
public function testDotIsRequiredBeforeProperty()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException');
new PropertyPath('[index]property');
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException
*/
public function testDotCannotBePresentAtTheBeginning()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException');
new PropertyPath('.property');
}
public function providePathsContainingUnexpectedCharacters()
{
return array(
array('property.'),
array('property.['),
array('property..'),
array('property['),
array('property[['),
array('property[.'),
array('property[]'),
);
return [
['property.'],
['property.['],
['property..'],
['property['],
['property[['],
['property[.'],
['property[]'],
];
}
/**
* @dataProvider providePathsContainingUnexpectedCharacters
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException
*/
public function testUnexpectedCharacters($path)
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException');
new PropertyPath($path);
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException
*/
public function testPathCannotBeEmpty()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException');
new PropertyPath('');
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException
*/
public function testPathCannotBeNull()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\InvalidArgumentException');
new PropertyPath(null);
}
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException
*/
public function testPathCannotBeFalse()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\InvalidArgumentException');
new PropertyPath(false);
}
@@ -128,21 +118,17 @@ class PropertyPathTest extends TestCase
$this->assertEquals('child', $propertyPath->getElement(2));
}
/**
* @expectedException \OutOfBoundsException
*/
public function testGetElementDoesNotAcceptInvalidIndices()
{
$this->expectException('OutOfBoundsException');
$propertyPath = new PropertyPath('grandpa.parent[child]');
$propertyPath->getElement(3);
}
/**
* @expectedException \OutOfBoundsException
*/
public function testGetElementDoesNotAcceptNegativeIndices()
{
$this->expectException('OutOfBoundsException');
$propertyPath = new PropertyPath('grandpa.parent[child]');
$propertyPath->getElement(-1);
@@ -156,21 +142,17 @@ class PropertyPathTest extends TestCase
$this->assertFalse($propertyPath->isProperty(2));
}
/**
* @expectedException \OutOfBoundsException
*/
public function testIsPropertyDoesNotAcceptInvalidIndices()
{
$this->expectException('OutOfBoundsException');
$propertyPath = new PropertyPath('grandpa.parent[child]');
$propertyPath->isProperty(3);
}
/**
* @expectedException \OutOfBoundsException
*/
public function testIsPropertyDoesNotAcceptNegativeIndices()
{
$this->expectException('OutOfBoundsException');
$propertyPath = new PropertyPath('grandpa.parent[child]');
$propertyPath->isProperty(-1);
@@ -184,21 +166,17 @@ class PropertyPathTest extends TestCase
$this->assertTrue($propertyPath->isIndex(2));
}
/**
* @expectedException \OutOfBoundsException
*/
public function testIsIndexDoesNotAcceptInvalidIndices()
{
$this->expectException('OutOfBoundsException');
$propertyPath = new PropertyPath('grandpa.parent[child]');
$propertyPath->isIndex(3);
}
/**
* @expectedException \OutOfBoundsException
*/
public function testIsIndexDoesNotAcceptNegativeIndices()
{
$this->expectException('OutOfBoundsException');
$propertyPath = new PropertyPath('grandpa.parent[child]');
$propertyPath->isIndex(-1);
+8 -135
View File
@@ -14,145 +14,18 @@ namespace Symfony\Component\PropertyAccess\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\PropertyAccess\StringUtil;
/**
* @group legacy
*/
class StringUtilTest extends TestCase
{
public function singularifyProvider()
{
// see http://english-zone.com/spelling/plurals.html
// see http://www.scribd.com/doc/3271143/List-of-100-Irregular-Plural-Nouns-in-English
return array(
array('accesses', 'access'),
array('addresses', 'address'),
array('agendas', 'agenda'),
array('alumnae', 'alumna'),
array('alumni', 'alumnus'),
array('analyses', array('analys', 'analyse', 'analysis')),
array('antennae', 'antenna'),
array('antennas', 'antenna'),
array('appendices', array('appendex', 'appendix', 'appendice')),
array('arches', array('arch', 'arche')),
array('atlases', array('atlas', 'atlase', 'atlasis')),
array('axes', array('ax', 'axe', 'axis')),
array('babies', 'baby'),
array('bacteria', array('bacterion', 'bacterium')),
array('bases', array('bas', 'base', 'basis')),
array('batches', array('batch', 'batche')),
array('beaux', 'beau'),
array('bees', array('be', 'bee')),
array('boxes', 'box'),
array('boys', 'boy'),
array('bureaus', 'bureau'),
array('bureaux', 'bureau'),
array('buses', array('bus', 'buse', 'busis')),
array('bushes', array('bush', 'bushe')),
array('calves', array('calf', 'calve', 'calff')),
array('cars', 'car'),
array('cassettes', array('cassett', 'cassette')),
array('caves', array('caf', 'cave', 'caff')),
array('chateaux', 'chateau'),
array('cheeses', array('chees', 'cheese', 'cheesis')),
array('children', 'child'),
array('circuses', array('circus', 'circuse', 'circusis')),
array('cliffs', 'cliff'),
array('committee', 'committee'),
array('crises', array('cris', 'crise', 'crisis')),
array('criteria', array('criterion', 'criterium')),
array('cups', 'cup'),
array('data', array('daton', 'datum')),
array('days', 'day'),
array('discos', 'disco'),
array('devices', array('devex', 'devix', 'device')),
array('drives', 'drive'),
array('drivers', 'driver'),
array('dwarves', array('dwarf', 'dwarve', 'dwarff')),
array('echoes', array('echo', 'echoe')),
array('elves', array('elf', 'elve', 'elff')),
array('emphases', array('emphas', 'emphase', 'emphasis')),
array('faxes', 'fax'),
array('feet', 'foot'),
array('feedback', 'feedback'),
array('foci', 'focus'),
array('focuses', array('focus', 'focuse', 'focusis')),
array('formulae', 'formula'),
array('formulas', 'formula'),
array('fungi', 'fungus'),
array('funguses', array('fungus', 'funguse', 'fungusis')),
array('garages', array('garag', 'garage')),
array('geese', 'goose'),
array('halves', array('half', 'halve', 'halff')),
array('hats', 'hat'),
array('heroes', array('hero', 'heroe')),
array('hippopotamuses', array('hippopotamus', 'hippopotamuse', 'hippopotamusis')), //hippopotami
array('hoaxes', 'hoax'),
array('hooves', array('hoof', 'hoove', 'hooff')),
array('houses', array('hous', 'house', 'housis')),
array('indexes', 'index'),
array('indices', array('index', 'indix', 'indice')),
array('ions', 'ion'),
array('irises', array('iris', 'irise', 'irisis')),
array('kisses', 'kiss'),
array('knives', 'knife'),
array('lamps', 'lamp'),
array('leaves', array('leaf', 'leave', 'leaff')),
array('lice', 'louse'),
array('lives', 'life'),
array('matrices', array('matrex', 'matrix', 'matrice')),
array('matrixes', 'matrix'),
array('men', 'man'),
array('mice', 'mouse'),
array('moves', 'move'),
array('movies', 'movie'),
array('nebulae', 'nebula'),
array('neuroses', array('neuros', 'neurose', 'neurosis')),
array('news', 'news'),
array('oases', array('oas', 'oase', 'oasis')),
array('objectives', 'objective'),
array('oxen', 'ox'),
array('parties', 'party'),
array('people', 'person'),
array('persons', 'person'),
array('phenomena', array('phenomenon', 'phenomenum')),
array('photos', 'photo'),
array('pianos', 'piano'),
array('plateaux', 'plateau'),
array('poppies', 'poppy'),
array('prices', array('prex', 'prix', 'price')),
array('quizzes', 'quiz'),
array('radii', 'radius'),
array('roofs', 'roof'),
array('roses', array('ros', 'rose', 'rosis')),
array('sandwiches', array('sandwich', 'sandwiche')),
array('scarves', array('scarf', 'scarve', 'scarff')),
array('schemas', 'schema'), //schemata
array('selfies', 'selfie'),
array('series', 'series'),
array('services', 'service'),
array('sheriffs', 'sheriff'),
array('shoes', array('sho', 'shoe')),
array('spies', 'spy'),
array('staves', array('staf', 'stave', 'staff')),
array('stories', 'story'),
array('strata', array('straton', 'stratum')),
array('suitcases', array('suitcas', 'suitcase', 'suitcasis')),
array('syllabi', 'syllabus'),
array('tags', 'tag'),
array('teeth', 'tooth'),
array('theses', array('thes', 'these', 'thesis')),
array('thieves', array('thief', 'thieve', 'thieff')),
array('trees', array('tre', 'tree')),
array('waltzes', array('waltz', 'waltze')),
array('wives', 'wife'),
// test casing: if the first letter was uppercase, it should remain so
array('Men', 'Man'),
array('GrandChildren', 'GrandChild'),
array('SubTrees', array('SubTre', 'SubTree')),
// Known issues
//array('insignia', 'insigne'),
//array('insignias', 'insigne'),
//array('rattles', 'rattle'),
);
// This is only a stub to make sure the BC layer works
// Actual tests are in the Symfony Inflector component
return [
['axes', ['ax', 'axe', 'axis']],
];
}
/**