@@ -89,6 +89,7 @@ class PropertyAccessor implements PropertyAccessorInterface
8989 private $ magicCall ;
9090 private $ readPropertyCache = array ();
9191 private $ writePropertyCache = array ();
92+ private static $ previousErrorHandler ;
9293
9394 /**
9495 * Should not be used by application code. Use
@@ -131,23 +132,66 @@ public function setValue(&$objectOrArray, $propertyPath, $value)
131132 self ::IS_REF => true ,
132133 ));
133134
134- for ($ i = count ($ propertyValues ) - 1 ; $ i >= 0 ; --$ i ) {
135- $ objectOrArray = &$ propertyValues [$ i ][self ::VALUE ];
135+ try {
136+ if (PHP_VERSION_ID < 70000 ) {
137+ self ::$ previousErrorHandler = set_error_handler (array (__CLASS__ , 'handleError ' ));
138+ }
136139
137- if ($ overwrite ) {
138- $ property = $ propertyPath ->getElement ($ i );
139- //$singular = $propertyPath->singulars[$i];
140- $ singular = null ;
140+ for ($ i = count ($ propertyValues ) - 1 ; $ i >= 0 ; --$ i ) {
141+ $ objectOrArray = &$ propertyValues [$ i ][self ::VALUE ];
141142
142- if ($ propertyPath ->isIndex ($ i )) {
143- $ this ->writeIndex ($ objectOrArray , $ property , $ value );
144- } else {
145- $ this ->writeProperty ($ objectOrArray , $ property , $ singular , $ value );
143+ if ($ overwrite ) {
144+ $ property = $ propertyPath ->getElement ($ i );
145+ //$singular = $propertyPath->singulars[$i];
146+ $ singular = null ;
147+
148+ if ($ propertyPath ->isIndex ($ i )) {
149+ $ this ->writeIndex ($ objectOrArray , $ property , $ value );
150+ } else {
151+ $ this ->writeProperty ($ objectOrArray , $ property , $ singular , $ value );
152+ }
146153 }
154+
155+ $ value = &$ objectOrArray ;
156+ $ overwrite = !$ propertyValues [$ i ][self ::IS_REF ];
157+ }
158+ } catch (\TypeError $ e ) {
159+ try {
160+ self ::throwUnexpectedTypeException ($ e ->getMessage (), $ e ->getTrace (), 0 );
161+ } catch (UnexpectedTypeException $ e ) {
147162 }
163+ } catch (\Exception $ e ) {
164+ } catch (\Throwable $ e ) {
165+ }
166+
167+ if (PHP_VERSION_ID < 70000 ) {
168+ restore_error_handler ();
169+ self ::$ previousErrorHandler = null ;
170+ }
171+ if (isset ($ e )) {
172+ throw $ e ;
173+ }
174+ }
148175
149- $ value = &$ objectOrArray ;
150- $ overwrite = !$ propertyValues [$ i ][self ::IS_REF ];
176+ /**
177+ * @internal
178+ */
179+ public static function handleError ($ type , $ message , $ file , $ line , $ context )
180+ {
181+ if (E_RECOVERABLE_ERROR === $ type ) {
182+ self ::throwUnexpectedTypeException ($ message , debug_backtrace (false ), 1 );
183+ }
184+
185+ return null !== self ::$ previousErrorHandler && false !== call_user_func (self ::$ previousErrorHandler , $ type , $ message , $ file , $ line , $ context );
186+ }
187+
188+ private static function throwUnexpectedTypeException ($ message , $ trace , $ i )
189+ {
190+ if (isset ($ trace [$ i ]['file ' ]) && __FILE__ === $ trace [$ i ]['file ' ]) {
191+ $ pos = strpos ($ message , $ delim = 'must be of the type ' ) ?: strpos ($ message , $ delim = 'must be an instance of ' );
192+ $ pos += strlen ($ delim );
193+
194+ throw new UnexpectedTypeException ($ trace [$ i ]['args ' ][0 ], substr ($ message , $ pos , strpos ($ message , ', ' , $ pos ) - $ pos ));
151195 }
152196 }
153197
@@ -398,9 +442,7 @@ private function writeIndex(&$array, $index, $value)
398442 * @param string|null $singular The singular form of the property name or null
399443 * @param mixed $value The value to write
400444 *
401- * @throws NoSuchPropertyException If the property does not exist or is not
402- * public.
403- * @throws UnexpectedTypeException
445+ * @throws NoSuchPropertyException If the property does not exist or is not public.
404446 */
405447 private function writeProperty (&$ object , $ property , $ singular , $ value )
406448 {
@@ -411,7 +453,7 @@ private function writeProperty(&$object, $property, $singular, $value)
411453 $ access = $ this ->getWriteAccessInfo ($ object , $ property , $ singular , $ value );
412454
413455 if (self ::ACCESS_TYPE_METHOD === $ access [self ::ACCESS_TYPE ]) {
414- $ this -> callMethod ( $ object , $ access [self ::ACCESS_NAME ], $ value );
456+ $ object ->{ $ access [self ::ACCESS_NAME ]}( $ value );
415457 } elseif (self ::ACCESS_TYPE_PROPERTY === $ access [self ::ACCESS_TYPE ]) {
416458 $ object ->{$ access [self ::ACCESS_NAME ]} = $ value ;
417459 } elseif (self ::ACCESS_TYPE_ADDER_AND_REMOVER === $ access [self ::ACCESS_TYPE ]) {
@@ -458,78 +500,12 @@ private function writeProperty(&$object, $property, $singular, $value)
458500
459501 $ object ->$ property = $ value ;
460502 } elseif (self ::ACCESS_TYPE_MAGIC === $ access [self ::ACCESS_TYPE ]) {
461- $ this -> callMethod ( $ object , $ access [self ::ACCESS_NAME ], $ value );
503+ $ object ->{ $ access [self ::ACCESS_NAME ]}( $ value );
462504 } else {
463505 throw new NoSuchPropertyException ($ access [self ::ACCESS_NAME ]);
464506 }
465507 }
466508
467- /**
468- * Throws a {@see UnexpectedTypeException} as in PHP 7 when using PHP 5.
469- *
470- * @param object $object
471- * @param string $method
472- * @param mixed $value
473- *
474- * @throws UnexpectedTypeException
475- * @throws \Exception
476- */
477- private function callMethod ($ object , $ method , $ value ) {
478- if (PHP_MAJOR_VERSION >= 7 ) {
479- try {
480- $ object ->{$ method }($ value );
481- } catch (\TypeError $ e ) {
482- throw $ this ->createUnexpectedTypeException ($ object , $ method , $ value );
483- }
484-
485- return ;
486- }
487-
488- $ that = $ this ;
489- set_error_handler (function ($ errno , $ errstr ) use ($ object , $ method , $ value , $ that ) {
490- if (E_RECOVERABLE_ERROR === $ errno && false !== strpos ($ errstr , sprintf ('passed to %s::%s() must ' , get_class ($ object ), $ method ))) {
491- throw $ that ->createUnexpectedTypeException ($ object , $ method , $ value );
492- }
493-
494- return false ;
495- });
496-
497- try {
498- $ object ->{$ method }($ value );
499- restore_error_handler ();
500- } catch (\Exception $ e ) {
501- // Cannot use finally in 5.5 because of https://bugs.php.net/bug.php?id=67047
502- restore_error_handler ();
503-
504- throw $ e ;
505- }
506- }
507-
508- /**
509- * Creates an UnexpectedTypeException.
510- *
511- * @param object $object
512- * @param string $method
513- * @param mixed $value
514- *
515- * @return UnexpectedTypeException
516- */
517- private function createUnexpectedTypeException ($ object , $ method , $ value )
518- {
519- $ reflectionMethod = new \ReflectionMethod ($ object , $ method );
520- $ parameters = $ reflectionMethod ->getParameters ();
521-
522- $ expectedType = 'unknown ' ;
523- if (isset ($ parameters [0 ])) {
524- $ class = $ parameters [0 ]->getClass ();
525- if (null !== $ class ) {
526- $ expectedType = $ class ->getName ();
527- }
528- }
529-
530- return new UnexpectedTypeException ($ value , $ expectedType );
531- }
532-
533509 /**
534510 * Guesses how to write the property value.
535511 *
0 commit comments