DEV Community

Joshua Johnson
Joshua Johnson

Posted on • Originally published at ua1.us on

5 1

Tutorial: How to Recursively Merge Two Objects In PHP

I was recently working on a project and found that I had to merge two very complex objects using PHP. I went to search for a solution and of course found array_merge() and array_merge_recursively(). So naturally, I thought I would start out by converting my objects into arrays and using array_merge_recursively. For which I achieved the following results:

class Utils {
    /**
     * Recursively merges two objects and returns a resulting object.
     * @param object $obj1 The base object
     * @param object $obj2 The merge object
     * @return object The merged object
     */
    public function mergeObjectsRecursively($obj1, $obj2)
    {
        $baseObject = (array) $obj1;
        $mergeObject = (array) $obj2;
        $merged = array_merge_recursive($baseObject, $mergeObject);
        return (object) $merged;
    }
}

$utils = new Utils();

$obj1 = (object) [
    'debug' => true,
    'routes' => (object) [
        'test' => '/my-tests'
    ]
];

$obj2 = (object) [
    'routes' => (object) [
        'test' => '/my-tests-replaced'
    ]
];

$obj3 = $utils->mergeObjectsRecursively($obj1, $obj2);

var_dump($obj3);

/**
 * Resulting Object:
 * object(stdClass)#6 (2) {
 *   ["debug"]=>
 *   bool(true)
 *   ["routes"]=>
 *   array(1) {
 *     ["test"]=>
 *     array(2) {
 *       [0]=>
 *       string(9) "/my-tests"
 *       [1]=>
 *       string(18) "/my-tests-replaced"
 *     }
 *   }
 * }
 */
Enter fullscreen mode Exit fullscreen mode

Notice that in the approach above, the resulting object is an object with a “routes” property that was converted from an object into an array. And not only that, but the obj->routes->test values have been combined into an array of values.

Not Acceptable!

THE ANSWER: Recursively Merge Two PHP Objects

The approach we need to take is to create our own merging logic so that it will override values instead of combining them. Here is the new approach:

class Utils {

    /**
     * Recursively merges two objects and returns a resulting object.
     * @param object $obj1 The base object
     * @param object $obj2 The merge object
     * @return object The merged object
     */
    public function mergeObjectsRecursively($obj1, $obj2)
    {
        $merged = $this->_mergeRecursively($obj1, $obj2);
        return $merged;
    }

    /**
     * Recursively merges two objects and returns a resulting object.
     * @param object $obj1 The base object
     * @param object $obj2 The merge object
     * @return object The merged object
     */
    private function _mergeRecursively($obj1, $obj2) {
        if (is_object($obj2)) {
            $keys = array_keys(get_object_vars($obj2));
            foreach ($keys as $key) {
                if (
                    isset($obj1->{$key})
                    && is_object($obj1->{$key})
                    && is_object($obj2->{$key})
                ) {
                    $obj1->{$key} = $this->_mergeRecursively($obj1->{$key}, $obj2->{$key});
                } elseif (isset($obj1->{$key})
                && is_array($obj1->{$key})
                && is_array($obj2->{$key})) {
                    $obj1->{$key} = $this->_mergeRecursively($obj1->{$key}, $obj2->{$key});
                } else {
                    $obj1->{$key} = $obj2->{$key};
                }
            }
        } elseif (is_array($obj2)) {
            if (
                is_array($obj1)
                && is_array($obj2)
            ) {
                $obj1 = array_merge_recursive($obj1, $obj2);
            } else {
                $obj1 = $obj2;
            }
        }

        return $obj1;
    }
}

$utils = new Utils();

$obj1 = (object) [
    'debug' => true,
    'modules' => [
        'Module1'
    ],
    'routes' => (object) [
        'test' => '/my-tests',
        'override' => 'false',
        'test2' => (object) [
            'override' => 'false',
            'shouldStay' => 'true'
        ]
    ]
];

$obj2 = (object) [
    'debug' => false,
    'modules' => [
        'Module2',
        'Module3'
    ],
    'routes' => (object) [
        'route1' => (object) [
            'path' => '/'
        ],
        'override' => 'true',
        'test2' => (object) [
            'override' => 'true'
        ]
    ]
];

$obj3 = $utils->mergeObjectsRecursively($obj1, $obj2);

var_dump($obj3);

/**
 * Resulting Object:
 *  object(stdClass)#4 (3) {
 *    ["debug"]=>
 *    bool(false)
 *    ["modules"]=>
 *    array(3) {
 *      [0]=>
 *      string(7) "Module1"
 *      [1]=>
 *      string(7) "Module2"
 *      [2]=>
 *      string(7) "Module3"
 *    }
 *    ["routes"]=>
 *    object(stdClass)#3 (4) {
 *      ["test"]=>
 *      string(9) "/my-tests"
 *      ["override"]=>
 *      string(4) "true"
 *      ["test2"]=>
 *      object(stdClass)#2 (2) {
 *        ["override"]=>
 *        string(4) "true"
 *        ["shouldStay"]=>
 *        string(4) "true"
 *      }
 *      ["route1"]=>
 *      object(stdClass)#5 (1) {
 *        ["path"]=>
 *        string(1) "/"
 *      }
 *    }
 *  }
 * /
Enter fullscreen mode Exit fullscreen mode

Notice that the resulting object remains in tact. $obj->routes->test contains the replaced value and both $obj->routes and $obj->routes->test remain an objects.

Enjoy!

The post Tutorial: How to Recursively Merge Two Objects In PHP appeared first on UA1 Labs.

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Top comments (0)

Billboard image

Create up to 10 Postgres Databases on Neon's free plan.

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Try Neon for Free →

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay