@@ -43,32 +43,34 @@ class ObjectMerge
4343 const OBJECT_T = 'object ' ;
4444
4545 /** @var bool */
46- private static $ _recurse ;
46+ private static $ _recursive ;
4747 /** @var int */
4848 private static $ _opts ;
49+ /** @var callable */
50+ private static $ _cb ;
4951
5052 /** @var int */
5153 private static $ _depth = -1 ;
5254 /** @var array */
5355 private static $ _context = [];
5456
5557 // list of types considered "simple"
56- private static $ _SIMPLE_TYPES = array (
58+ private static $ _SIMPLE_TYPES = [
5759 self ::NULL_T ,
5860 self ::RESOURCE_T ,
5961 self ::STRING_T ,
6062 self ::BOOLEAN_T ,
6163 self ::INTEGER_T ,
6264 self ::DOUBLE_T
63- ) ;
65+ ] ;
6466
6567 /**
6668 * @param stdClass ...$objects
6769 * @return stdClass|null
6870 */
6971 public function __invoke (stdClass ...$ objects )
7072 {
71- return self ::_doMerge (false , self ::DEFAULT_OPTS , $ objects );
73+ return self ::_doMerge (false , self ::DEFAULT_OPTS , null , $ objects );
7274 }
7375
7476 /**
@@ -77,7 +79,7 @@ public function __invoke(stdClass ...$objects)
7779 */
7880 public static function merge (stdClass ...$ objects )
7981 {
80- return self ::_doMerge (false , self ::DEFAULT_OPTS , $ objects );
82+ return self ::_doMerge (false , self ::DEFAULT_OPTS , null , $ objects );
8183 }
8284
8385 /**
@@ -86,7 +88,7 @@ public static function merge(stdClass ...$objects)
8688 */
8789 public static function mergeRecursive (stdClass ...$ objects )
8890 {
89- return self ::_doMerge (true , self ::DEFAULT_OPTS , $ objects );
91+ return self ::_doMerge (true , self ::DEFAULT_OPTS , null , $ objects );
9092 }
9193
9294 /**
@@ -96,7 +98,7 @@ public static function mergeRecursive(stdClass ...$objects)
9698 */
9799 public static function mergeOpts ($ opts , stdClass ...$ objects )
98100 {
99- return self ::_doMerge (false , $ opts , $ objects );
101+ return self ::_doMerge (false , $ opts , null , $ objects );
100102 }
101103
102104 /**
@@ -106,7 +108,69 @@ public static function mergeOpts($opts, stdClass ...$objects)
106108 */
107109 public static function mergeRecursiveOpts ($ opts , stdClass ...$ objects )
108110 {
109- return self ::_doMerge (true , $ opts , $ objects );
111+ return self ::_doMerge (true , $ opts , null , $ objects );
112+ }
113+
114+ /**
115+ * @param int $opts
116+ * @param callable $cb
117+ * @param stdClass ...$objects
118+ * @return stdClass
119+ */
120+ public static function mergeCallback ($ opts , $ cb , stdClass ...$ objects )
121+ {
122+ return self ::_doMerge (false , $ opts , $ cb , $ objects );
123+ }
124+
125+ /**
126+ * @param int $opts
127+ * @param callable $cb
128+ * @param stdClass ...$objects
129+ * @return stdClass
130+ */
131+ public static function mergeRecursiveCallback ($ opts , $ cb , stdClass ...$ objects )
132+ {
133+ return self ::_doMerge (true , $ opts , $ cb , $ objects );
134+ }
135+
136+ /**
137+ * @return ObjectMergeState
138+ */
139+ public static function partialState ()
140+ {
141+ return self ::_partialState ();
142+ }
143+
144+ /**
145+ * @return ObjectMergeState
146+ */
147+ private static function _partialState ()
148+ {
149+ $ state = new ObjectMergeState ();
150+
151+ $ state ->recursive = self ::$ _recursive ;
152+ $ state ->opts = self ::$ _opts ;
153+ $ state ->depth = self ::$ _depth ;
154+ $ state ->context = self ::$ _context ;
155+
156+ return $ state ;
157+ }
158+
159+ /**
160+ * @param string|int $key
161+ * @param mixed $leftValue
162+ * @param mixed $rightValue
163+ * @return ObjectMergeState
164+ */
165+ private static function _fullState ($ key , $ leftValue , $ rightValue )
166+ {
167+ $ state = self ::_partialState ();
168+
169+ $ state ->key = $ key ;
170+ $ state ->leftValue = $ leftValue ;
171+ $ state ->rightValue = $ rightValue ;
172+
173+ return $ state ;
110174 }
111175
112176 private static function _down ()
@@ -127,9 +191,9 @@ private static function _up()
127191 private static function _exceptionMessage ($ prefix )
128192 {
129193 return sprintf (
130- '%s - $recurse =%s; $opts=%d; $depth=%d; $context=%s ' ,
194+ '%s - $recursive =%s; $opts=%d; $depth=%d; $context=%s ' ,
131195 $ prefix ,
132- self ::$ _recurse ,
196+ self ::$ _recursive ,
133197 self ::$ _opts ,
134198 self ::$ _depth ,
135199 implode ('-> ' , self ::$ _context )
@@ -270,6 +334,23 @@ private static function _mergeValues($key, $leftValue, $rightValue)
270334 {
271335 self ::$ _context [self ::$ _depth ] = $ key ;
272336
337+ if (isset (self ::$ _cb )) {
338+ $ res = call_user_func (self ::$ _cb , self ::_fullState ($ key , $ leftValue , $ rightValue ));
339+ $ resT = gettype ($ res );
340+ if (self ::OBJECT_T === $ resT ) {
341+ if ($ res instanceof ObjectMergeResult && !$ res ->shouldContinue ()) {
342+ $ finalValue = $ res ->getFinalValue ();
343+ if (OBJECT_MERGE_UNDEFINED !== $ finalValue ) {
344+ return $ finalValue ;
345+ }
346+ $ leftValue = $ res ->getLeftValue ();
347+ $ rightValue = $ res ->getRightValue ();
348+ }
349+ } else {
350+ return $ res ;
351+ }
352+ }
353+
273354 $ leftUndefined = object_merge_value_undefined ($ leftValue , self ::$ _opts );
274355 $ rightUndefined = object_merge_value_undefined ($ rightValue , self ::$ _opts );
275356
@@ -310,7 +391,7 @@ private static function _mergeValues($key, $leftValue, $rightValue)
310391 return self ::_mergeValues ($ key , self ::_newEmptyValue ($ rightValue ), $ rightValue );
311392 }
312393
313- if (!self ::$ _recurse || in_array ($ leftType , self ::$ _SIMPLE_TYPES , true )) {
394+ if (!self ::$ _recursive || in_array ($ leftType , self ::$ _SIMPLE_TYPES , true )) {
314395 return $ rightValue ;
315396 }
316397
@@ -322,12 +403,13 @@ private static function _mergeValues($key, $leftValue, $rightValue)
322403 }
323404
324405 /**
325- * @param bool $recurse
406+ * @param bool $recursive
326407 * @param int $opts
408+ * @param callable|null $cb
327409 * @param array $objects
328410 * @return mixed|null
329411 */
330- private static function _doMerge ($ recurse , $ opts , array $ objects )
412+ private static function _doMerge ($ recursive , $ opts, $ cb , array $ objects )
331413 {
332414 if ([] === $ objects ) {
333415 return null ;
@@ -336,8 +418,9 @@ private static function _doMerge($recurse, $opts, array $objects)
336418 $ root = null ;
337419
338420 // set state
339- self ::$ _recurse = $ recurse ;
421+ self ::$ _recursive = $ recursive ;
340422 self ::$ _opts = $ opts ;
423+ self ::$ _cb = $ cb ;
341424
342425 foreach ($ objects as $ object ) {
343426 if (null === $ object ) {
@@ -349,14 +432,15 @@ private static function _doMerge($recurse, $opts, array $objects)
349432 continue ;
350433 }
351434
352- $ root = self ::_mergeObjectValues ($ root , !$ recurse ? clone $ object : $ object );
435+ $ root = self ::_mergeObjectValues ($ root , !$ recursive ? clone $ object : $ object );
353436 }
354437
355438 // reset state
356439 self ::$ _depth = -1 ;
357440 self ::$ _context = [];
358- self ::$ _recurse = false ;
441+ self ::$ _recursive = false ;
359442 self ::$ _opts = 0 ;
443+ self ::$ _cb = null ;
360444
361445 return $ root ;
362446 }
0 commit comments