PropertyAccessorTest.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\PropertyAccess\Tests;
  11. use PHPUnit\Framework\TestCase;
  12. use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
  13. use Symfony\Component\PropertyAccess\PropertyAccessor;
  14. use Symfony\Component\PropertyAccess\Tests\Fixtures\ReturnTyped;
  15. use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClass;
  16. use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassMagicCall;
  17. use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassMagicGet;
  18. use Symfony\Component\PropertyAccess\Tests\Fixtures\Ticket5775Object;
  19. use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassSetValue;
  20. use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassIsWritable;
  21. use Symfony\Component\PropertyAccess\Tests\Fixtures\TypeHinted;
  22. class PropertyAccessorTest extends TestCase
  23. {
  24. /**
  25. * @var PropertyAccessor
  26. */
  27. private $propertyAccessor;
  28. protected function setUp()
  29. {
  30. $this->propertyAccessor = new PropertyAccessor();
  31. }
  32. public function getPathsWithUnexpectedType()
  33. {
  34. return array(
  35. array('', 'foobar'),
  36. array('foo', 'foobar'),
  37. array(null, 'foobar'),
  38. array(123, 'foobar'),
  39. array((object) array('prop' => null), 'prop.foobar'),
  40. array((object) array('prop' => (object) array('subProp' => null)), 'prop.subProp.foobar'),
  41. array(array('index' => null), '[index][foobar]'),
  42. array(array('index' => array('subIndex' => null)), '[index][subIndex][foobar]'),
  43. );
  44. }
  45. public function getPathsWithMissingProperty()
  46. {
  47. return array(
  48. array((object) array('firstName' => 'Bernhard'), 'lastName'),
  49. array((object) array('property' => (object) array('firstName' => 'Bernhard')), 'property.lastName'),
  50. array(array('index' => (object) array('firstName' => 'Bernhard')), '[index].lastName'),
  51. array(new TestClass('Bernhard'), 'protectedProperty'),
  52. array(new TestClass('Bernhard'), 'privateProperty'),
  53. array(new TestClass('Bernhard'), 'protectedAccessor'),
  54. array(new TestClass('Bernhard'), 'protectedIsAccessor'),
  55. array(new TestClass('Bernhard'), 'protectedHasAccessor'),
  56. array(new TestClass('Bernhard'), 'privateAccessor'),
  57. array(new TestClass('Bernhard'), 'privateIsAccessor'),
  58. array(new TestClass('Bernhard'), 'privateHasAccessor'),
  59. // Properties are not camelized
  60. array(new TestClass('Bernhard'), 'public_property'),
  61. );
  62. }
  63. public function getPathsWithMissingIndex()
  64. {
  65. return array(
  66. array(array('firstName' => 'Bernhard'), '[lastName]'),
  67. array(array(), '[index][lastName]'),
  68. array(array('index' => array()), '[index][lastName]'),
  69. array(array('index' => array('firstName' => 'Bernhard')), '[index][lastName]'),
  70. array((object) array('property' => array('firstName' => 'Bernhard')), 'property[lastName]'),
  71. );
  72. }
  73. /**
  74. * @dataProvider getValidPropertyPaths
  75. */
  76. public function testGetValue($objectOrArray, $path, $value)
  77. {
  78. $this->assertSame($value, $this->propertyAccessor->getValue($objectOrArray, $path));
  79. }
  80. /**
  81. * @dataProvider getPathsWithMissingProperty
  82. * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
  83. */
  84. public function testGetValueThrowsExceptionIfPropertyNotFound($objectOrArray, $path)
  85. {
  86. $this->propertyAccessor->getValue($objectOrArray, $path);
  87. }
  88. /**
  89. * @dataProvider getPathsWithMissingIndex
  90. */
  91. public function testGetValueThrowsNoExceptionIfIndexNotFound($objectOrArray, $path)
  92. {
  93. $this->assertNull($this->propertyAccessor->getValue($objectOrArray, $path));
  94. }
  95. /**
  96. * @dataProvider getPathsWithMissingIndex
  97. * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchIndexException
  98. */
  99. public function testGetValueThrowsExceptionIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
  100. {
  101. $this->propertyAccessor = new PropertyAccessor(false, true);
  102. $this->propertyAccessor->getValue($objectOrArray, $path);
  103. }
  104. /**
  105. * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchIndexException
  106. */
  107. public function testGetValueThrowsExceptionIfNotArrayAccess()
  108. {
  109. $this->propertyAccessor->getValue(new \stdClass(), '[index]');
  110. }
  111. public function testGetValueReadsMagicGet()
  112. {
  113. $this->assertSame('Bernhard', $this->propertyAccessor->getValue(new TestClassMagicGet('Bernhard'), 'magicProperty'));
  114. }
  115. public function testGetValueReadsArrayWithMissingIndexForCustomPropertyPath()
  116. {
  117. $object = new \ArrayObject();
  118. $array = array('child' => array('index' => $object));
  119. $this->assertNull($this->propertyAccessor->getValue($array, '[child][index][foo][bar]'));
  120. $this->assertSame(array(), $object->getArrayCopy());
  121. }
  122. // https://github.com/symfony/symfony/pull/4450
  123. public function testGetValueReadsMagicGetThatReturnsConstant()
  124. {
  125. $this->assertSame('constant value', $this->propertyAccessor->getValue(new TestClassMagicGet('Bernhard'), 'constantMagicProperty'));
  126. }
  127. public function testGetValueNotModifyObject()
  128. {
  129. $object = new \stdClass();
  130. $object->firstName = array('Bernhard');
  131. $this->assertNull($this->propertyAccessor->getValue($object, 'firstName[1]'));
  132. $this->assertSame(array('Bernhard'), $object->firstName);
  133. }
  134. public function testGetValueNotModifyObjectException()
  135. {
  136. $propertyAccessor = new PropertyAccessor(false, true);
  137. $object = new \stdClass();
  138. $object->firstName = array('Bernhard');
  139. try {
  140. $propertyAccessor->getValue($object, 'firstName[1]');
  141. } catch (NoSuchIndexException $e) {
  142. }
  143. $this->assertSame(array('Bernhard'), $object->firstName);
  144. }
  145. /**
  146. * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
  147. */
  148. public function testGetValueDoesNotReadMagicCallByDefault()
  149. {
  150. $this->propertyAccessor->getValue(new TestClassMagicCall('Bernhard'), 'magicCallProperty');
  151. }
  152. public function testGetValueReadsMagicCallIfEnabled()
  153. {
  154. $this->propertyAccessor = new PropertyAccessor(true);
  155. $this->assertSame('Bernhard', $this->propertyAccessor->getValue(new TestClassMagicCall('Bernhard'), 'magicCallProperty'));
  156. }
  157. // https://github.com/symfony/symfony/pull/4450
  158. public function testGetValueReadsMagicCallThatReturnsConstant()
  159. {
  160. $this->propertyAccessor = new PropertyAccessor(true);
  161. $this->assertSame('constant value', $this->propertyAccessor->getValue(new TestClassMagicCall('Bernhard'), 'constantMagicCallProperty'));
  162. }
  163. /**
  164. * @dataProvider getPathsWithUnexpectedType
  165. * @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
  166. * @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on
  167. */
  168. public function testGetValueThrowsExceptionIfNotObjectOrArray($objectOrArray, $path)
  169. {
  170. $this->propertyAccessor->getValue($objectOrArray, $path);
  171. }
  172. /**
  173. * @dataProvider getValidPropertyPaths
  174. */
  175. public function testSetValue($objectOrArray, $path)
  176. {
  177. $this->propertyAccessor->setValue($objectOrArray, $path, 'Updated');
  178. $this->assertSame('Updated', $this->propertyAccessor->getValue($objectOrArray, $path));
  179. }
  180. /**
  181. * @dataProvider getPathsWithMissingProperty
  182. * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
  183. */
  184. public function testSetValueThrowsExceptionIfPropertyNotFound($objectOrArray, $path)
  185. {
  186. $this->propertyAccessor->setValue($objectOrArray, $path, 'Updated');
  187. }
  188. /**
  189. * @dataProvider getPathsWithMissingIndex
  190. */
  191. public function testSetValueThrowsNoExceptionIfIndexNotFound($objectOrArray, $path)
  192. {
  193. $this->propertyAccessor->setValue($objectOrArray, $path, 'Updated');
  194. $this->assertSame('Updated', $this->propertyAccessor->getValue($objectOrArray, $path));
  195. }
  196. /**
  197. * @dataProvider getPathsWithMissingIndex
  198. */
  199. public function testSetValueThrowsNoExceptionIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
  200. {
  201. $this->propertyAccessor = new PropertyAccessor(false, true);
  202. $this->propertyAccessor->setValue($objectOrArray, $path, 'Updated');
  203. $this->assertSame('Updated', $this->propertyAccessor->getValue($objectOrArray, $path));
  204. }
  205. /**
  206. * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchIndexException
  207. */
  208. public function testSetValueThrowsExceptionIfNotArrayAccess()
  209. {
  210. $object = new \stdClass();
  211. $this->propertyAccessor->setValue($object, '[index]', 'Updated');
  212. }
  213. public function testSetValueUpdatesMagicSet()
  214. {
  215. $author = new TestClassMagicGet('Bernhard');
  216. $this->propertyAccessor->setValue($author, 'magicProperty', 'Updated');
  217. $this->assertEquals('Updated', $author->__get('magicProperty'));
  218. }
  219. /**
  220. * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
  221. */
  222. public function testSetValueThrowsExceptionIfThereAreMissingParameters()
  223. {
  224. $object = new TestClass('Bernhard');
  225. $this->propertyAccessor->setValue($object, 'publicAccessorWithMoreRequiredParameters', 'Updated');
  226. }
  227. /**
  228. * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
  229. */
  230. public function testSetValueDoesNotUpdateMagicCallByDefault()
  231. {
  232. $author = new TestClassMagicCall('Bernhard');
  233. $this->propertyAccessor->setValue($author, 'magicCallProperty', 'Updated');
  234. }
  235. public function testSetValueUpdatesMagicCallIfEnabled()
  236. {
  237. $this->propertyAccessor = new PropertyAccessor(true);
  238. $author = new TestClassMagicCall('Bernhard');
  239. $this->propertyAccessor->setValue($author, 'magicCallProperty', 'Updated');
  240. $this->assertEquals('Updated', $author->__call('getMagicCallProperty', array()));
  241. }
  242. /**
  243. * @dataProvider getPathsWithUnexpectedType
  244. * @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
  245. * @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on
  246. */
  247. public function testSetValueThrowsExceptionIfNotObjectOrArray($objectOrArray, $path)
  248. {
  249. $this->propertyAccessor->setValue($objectOrArray, $path, 'value');
  250. }
  251. public function testGetValueWhenArrayValueIsNull()
  252. {
  253. $this->propertyAccessor = new PropertyAccessor(false, true);
  254. $this->assertNull($this->propertyAccessor->getValue(array('index' => array('nullable' => null)), '[index][nullable]'));
  255. }
  256. /**
  257. * @dataProvider getValidPropertyPaths
  258. */
  259. public function testIsReadable($objectOrArray, $path)
  260. {
  261. $this->assertTrue($this->propertyAccessor->isReadable($objectOrArray, $path));
  262. }
  263. /**
  264. * @dataProvider getPathsWithMissingProperty
  265. */
  266. public function testIsReadableReturnsFalseIfPropertyNotFound($objectOrArray, $path)
  267. {
  268. $this->assertFalse($this->propertyAccessor->isReadable($objectOrArray, $path));
  269. }
  270. /**
  271. * @dataProvider getPathsWithMissingIndex
  272. */
  273. public function testIsReadableReturnsTrueIfIndexNotFound($objectOrArray, $path)
  274. {
  275. // Non-existing indices can be read. In this case, null is returned
  276. $this->assertTrue($this->propertyAccessor->isReadable($objectOrArray, $path));
  277. }
  278. /**
  279. * @dataProvider getPathsWithMissingIndex
  280. */
  281. public function testIsReadableReturnsFalseIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
  282. {
  283. $this->propertyAccessor = new PropertyAccessor(false, true);
  284. // When exceptions are enabled, non-existing indices cannot be read
  285. $this->assertFalse($this->propertyAccessor->isReadable($objectOrArray, $path));
  286. }
  287. public function testIsReadableRecognizesMagicGet()
  288. {
  289. $this->assertTrue($this->propertyAccessor->isReadable(new TestClassMagicGet('Bernhard'), 'magicProperty'));
  290. }
  291. public function testIsReadableDoesNotRecognizeMagicCallByDefault()
  292. {
  293. $this->assertFalse($this->propertyAccessor->isReadable(new TestClassMagicCall('Bernhard'), 'magicCallProperty'));
  294. }
  295. public function testIsReadableRecognizesMagicCallIfEnabled()
  296. {
  297. $this->propertyAccessor = new PropertyAccessor(true);
  298. $this->assertTrue($this->propertyAccessor->isReadable(new TestClassMagicCall('Bernhard'), 'magicCallProperty'));
  299. }
  300. /**
  301. * @dataProvider getPathsWithUnexpectedType
  302. */
  303. public function testIsReadableReturnsFalseIfNotObjectOrArray($objectOrArray, $path)
  304. {
  305. $this->assertFalse($this->propertyAccessor->isReadable($objectOrArray, $path));
  306. }
  307. /**
  308. * @dataProvider getValidPropertyPaths
  309. */
  310. public function testIsWritable($objectOrArray, $path)
  311. {
  312. $this->assertTrue($this->propertyAccessor->isWritable($objectOrArray, $path));
  313. }
  314. /**
  315. * @dataProvider getPathsWithMissingProperty
  316. */
  317. public function testIsWritableReturnsFalseIfPropertyNotFound($objectOrArray, $path)
  318. {
  319. $this->assertFalse($this->propertyAccessor->isWritable($objectOrArray, $path));
  320. }
  321. /**
  322. * @dataProvider getPathsWithMissingIndex
  323. */
  324. public function testIsWritableReturnsTrueIfIndexNotFound($objectOrArray, $path)
  325. {
  326. // Non-existing indices can be written. Arrays are created on-demand.
  327. $this->assertTrue($this->propertyAccessor->isWritable($objectOrArray, $path));
  328. }
  329. /**
  330. * @dataProvider getPathsWithMissingIndex
  331. */
  332. public function testIsWritableReturnsTrueIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path)
  333. {
  334. $this->propertyAccessor = new PropertyAccessor(false, true);
  335. // Non-existing indices can be written even if exceptions are enabled
  336. $this->assertTrue($this->propertyAccessor->isWritable($objectOrArray, $path));
  337. }
  338. public function testIsWritableRecognizesMagicSet()
  339. {
  340. $this->assertTrue($this->propertyAccessor->isWritable(new TestClassMagicGet('Bernhard'), 'magicProperty'));
  341. }
  342. public function testIsWritableDoesNotRecognizeMagicCallByDefault()
  343. {
  344. $this->assertFalse($this->propertyAccessor->isWritable(new TestClassMagicCall('Bernhard'), 'magicCallProperty'));
  345. }
  346. public function testIsWritableRecognizesMagicCallIfEnabled()
  347. {
  348. $this->propertyAccessor = new PropertyAccessor(true);
  349. $this->assertTrue($this->propertyAccessor->isWritable(new TestClassMagicCall('Bernhard'), 'magicCallProperty'));
  350. }
  351. /**
  352. * @dataProvider getPathsWithUnexpectedType
  353. */
  354. public function testIsWritableReturnsFalseIfNotObjectOrArray($objectOrArray, $path)
  355. {
  356. $this->assertFalse($this->propertyAccessor->isWritable($objectOrArray, $path));
  357. }
  358. public function getValidPropertyPaths()
  359. {
  360. return array(
  361. array(array('Bernhard', 'Schussek'), '[0]', 'Bernhard'),
  362. array(array('Bernhard', 'Schussek'), '[1]', 'Schussek'),
  363. array(array('firstName' => 'Bernhard'), '[firstName]', 'Bernhard'),
  364. array(array('index' => array('firstName' => 'Bernhard')), '[index][firstName]', 'Bernhard'),
  365. array((object) array('firstName' => 'Bernhard'), 'firstName', 'Bernhard'),
  366. array((object) array('property' => array('firstName' => 'Bernhard')), 'property[firstName]', 'Bernhard'),
  367. array(array('index' => (object) array('firstName' => 'Bernhard')), '[index].firstName', 'Bernhard'),
  368. array((object) array('property' => (object) array('firstName' => 'Bernhard')), 'property.firstName', 'Bernhard'),
  369. // Accessor methods
  370. array(new TestClass('Bernhard'), 'publicProperty', 'Bernhard'),
  371. array(new TestClass('Bernhard'), 'publicAccessor', 'Bernhard'),
  372. array(new TestClass('Bernhard'), 'publicAccessorWithDefaultValue', 'Bernhard'),
  373. array(new TestClass('Bernhard'), 'publicAccessorWithRequiredAndDefaultValue', 'Bernhard'),
  374. array(new TestClass('Bernhard'), 'publicIsAccessor', 'Bernhard'),
  375. array(new TestClass('Bernhard'), 'publicHasAccessor', 'Bernhard'),
  376. array(new TestClass('Bernhard'), 'publicGetSetter', 'Bernhard'),
  377. // Methods are camelized
  378. array(new TestClass('Bernhard'), 'public_accessor', 'Bernhard'),
  379. array(new TestClass('Bernhard'), '_public_accessor', 'Bernhard'),
  380. // Missing indices
  381. array(array('index' => array()), '[index][firstName]', null),
  382. array(array('root' => array('index' => array())), '[root][index][firstName]', null),
  383. // Special chars
  384. array(array('%!@$§.' => 'Bernhard'), '[%!@$§.]', 'Bernhard'),
  385. array(array('index' => array('%!@$§.' => 'Bernhard')), '[index][%!@$§.]', 'Bernhard'),
  386. array((object) array('%!@$§' => 'Bernhard'), '%!@$§', 'Bernhard'),
  387. array((object) array('property' => (object) array('%!@$§' => 'Bernhard')), 'property.%!@$§', 'Bernhard'),
  388. // nested objects and arrays
  389. array(array('foo' => new TestClass('bar')), '[foo].publicGetSetter', 'bar'),
  390. array(new TestClass(array('foo' => 'bar')), 'publicGetSetter[foo]', 'bar'),
  391. array(new TestClass(new TestClass('bar')), 'publicGetter.publicGetSetter', 'bar'),
  392. array(new TestClass(array('foo' => new TestClass('bar'))), 'publicGetter[foo].publicGetSetter', 'bar'),
  393. array(new TestClass(new TestClass(new TestClass('bar'))), 'publicGetter.publicGetter.publicGetSetter', 'bar'),
  394. array(new TestClass(array('foo' => array('baz' => new TestClass('bar')))), 'publicGetter[foo][baz].publicGetSetter', 'bar'),
  395. );
  396. }
  397. public function testTicket5755()
  398. {
  399. $object = new Ticket5775Object();
  400. $this->propertyAccessor->setValue($object, 'property', 'foobar');
  401. $this->assertEquals('foobar', $object->getProperty());
  402. }
  403. public function testSetValueDeepWithMagicGetter()
  404. {
  405. $obj = new TestClassMagicGet('foo');
  406. $obj->publicProperty = array('foo' => array('bar' => 'some_value'));
  407. $this->propertyAccessor->setValue($obj, 'publicProperty[foo][bar]', 'Updated');
  408. $this->assertSame('Updated', $obj->publicProperty['foo']['bar']);
  409. }
  410. public function getReferenceChainObjectsForSetValue()
  411. {
  412. return array(
  413. array(array('a' => array('b' => array('c' => 'old-value'))), '[a][b][c]', 'new-value'),
  414. array(new TestClassSetValue(new TestClassSetValue('old-value')), 'value.value', 'new-value'),
  415. array(new TestClassSetValue(array('a' => array('b' => array('c' => new TestClassSetValue('old-value'))))), 'value[a][b][c].value', 'new-value'),
  416. array(new TestClassSetValue(array('a' => array('b' => 'old-value'))), 'value[a][b]', 'new-value'),
  417. array(new \ArrayIterator(array('a' => array('b' => array('c' => 'old-value')))), '[a][b][c]', 'new-value'),
  418. );
  419. }
  420. /**
  421. * @dataProvider getReferenceChainObjectsForSetValue
  422. */
  423. public function testSetValueForReferenceChainIssue($object, $path, $value)
  424. {
  425. $this->propertyAccessor->setValue($object, $path, $value);
  426. $this->assertEquals($value, $this->propertyAccessor->getValue($object, $path));
  427. }
  428. public function getReferenceChainObjectsForIsWritable()
  429. {
  430. return array(
  431. array(new TestClassIsWritable(array('a' => array('b' => 'old-value'))), 'value[a][b]', false),
  432. array(new TestClassIsWritable(new \ArrayIterator(array('a' => array('b' => 'old-value')))), 'value[a][b]', true),
  433. array(new TestClassIsWritable(array('a' => array('b' => array('c' => new TestClassSetValue('old-value'))))), 'value[a][b][c].value', true),
  434. );
  435. }
  436. /**
  437. * @dataProvider getReferenceChainObjectsForIsWritable
  438. */
  439. public function testIsWritableForReferenceChainIssue($object, $path, $value)
  440. {
  441. $this->assertEquals($value, $this->propertyAccessor->isWritable($object, $path));
  442. }
  443. /**
  444. * @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException
  445. * @expectedExceptionMessage Expected argument of type "DateTime", "string" given
  446. */
  447. public function testThrowTypeError()
  448. {
  449. $object = new TypeHinted();
  450. $this->propertyAccessor->setValue($object, 'date', 'This is a string, \DateTime expected.');
  451. }
  452. public function testSetTypeHint()
  453. {
  454. $date = new \DateTime();
  455. $object = new TypeHinted();
  456. $this->propertyAccessor->setValue($object, 'date', $date);
  457. $this->assertSame($date, $object->getDate());
  458. }
  459. public function testArrayNotBeeingOverwritten()
  460. {
  461. $value = array('value1' => 'foo', 'value2' => 'bar');
  462. $object = new TestClass($value);
  463. $this->propertyAccessor->setValue($object, 'publicAccessor[value2]', 'baz');
  464. $this->assertSame('baz', $this->propertyAccessor->getValue($object, 'publicAccessor[value2]'));
  465. $this->assertSame(array('value1' => 'foo', 'value2' => 'baz'), $object->getPublicAccessor());
  466. }
  467. /**
  468. * @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException
  469. * @expectedExceptionMessage Expected argument of type "Countable", "string" given
  470. */
  471. public function testThrowTypeErrorWithInterface()
  472. {
  473. $object = new TypeHinted();
  474. $this->propertyAccessor->setValue($object, 'countable', 'This is a string, \Countable expected.');
  475. }
  476. /**
  477. * @requires PHP 7
  478. *
  479. * @expectedException \TypeError
  480. */
  481. public function testDoNotDiscardReturnTypeError()
  482. {
  483. $object = new ReturnTyped();
  484. $this->propertyAccessor->setValue($object, 'foos', array(new \DateTime()));
  485. }
  486. }