Skip to content
Advertisement

How to retrieve updates, deletions, and additions between multi-dimensional arrays in PHP?

I have 2 multi-dimensional arrays. Essentially, one of them is an array that is constantly updated. I would like to find the updates aka additions and deletions between the arrays. For instance, I have some data as follows:

Updated/new array (Array #1):

Array
(
    [CN=Joe Dope,CN=Users,DC=test,DC=io] => Array
        (

            [msexchassistantname] => cn=CIGdeadp1,cn=Users,dc=test,dc=io
            [memberof] => Array
                (
                    [0] => CN=CIGdeadp1,CN=Users,DC=test,DC=io
                    [1] => CN=CASUsers,CN=Users,DC=test,DC=io
                    [2] => CN=CloudNCUsers,CN=Users,DC=test,DC=io
                    [3] => CN=CloudEmailUsers,CN=Users,DC=test,DC=io
                )

            [whenchanged] => 20201004180306.0Z
            [usnchanged] => 33404
            [distinguishedname] => CN=Joe Dope,CN=Users,DC=test,DC=io
        )

    [CN=Ronald,CN=Users,DC=test,DC=io] => Array
      (
          [usnchanged] => 1342
          [whenchanged] => 20191106190201.0Z
      )
)

Old array (Array #2):

   [CN=Joe Dope,CN=Users,DC=test,DC=io] => Array
        (

            [msexchassistantname] => cn=CIGdeadp1,cn=Users,dc=test,dc=io
            [memberof] => Array
                (
                    [0] => CN=CIGdeadp1,CN=Users,DC=test,DC=io
                    [1] => CN=CASUsers,CN=Users,DC=test,DC=io
                    [2] => CN=CloudNCUsers,CN=Users,DC=test,DC=io
                    [3] => CN=CloudOfficeUsers,CN=Users,DC=test,DC=io
                )

            [othermailbox] => Array
                (
                    [0] => jdope1@gmail.com
                    [1] => foobar@gmail.com
                )

            [whenchanged] => 20200929142821.0Z
            [usnchanged] => 32855
            [distinguishedname] => CN=Joe Dope,CN=Users,DC=test,DC=io
        )

What I’ve done so far is the following:

$array = [];
$array["CN=Joe Dope,CN=Users,DC=test,DC=io"] = array("msexchassistantname" => ("cn=CIGdeadp1,cn=Users,dc=test,dc=io"),
"memberof" => array("0" => "CN=CIGdeadp1,CN=Users,DC=test,DC=io", "1" => "CN=CASUsers,CN=Users,DC=test,DC=io",
"2" => "CN=CloudNCUsers,CN=Users,DC=test,DC=io", "3" => "CN=CloudEmailUsers,CN=Users,DC=test,DC=io"
), "whenchanged" => "20201004180306.0Z", "usnchanged" => "33404", "distinguishedname" => "CN=Joe Dope,CN=Users,DC=test,DC=io");

$array["CN=Ronald,CN=Users,DC=test,DC=io"] = array("usnchanged" => "1342", "whenchanged" => "20191106190201.0Z");


$array2 = [];
$array2["CN=Joe Dope,CN=Users,DC=test,DC=io"] = array("msexchassistantname" => "cn=CIGdeadp1,cn=Users,dc=test,dc=io",
"memberof" => array("0" => "CN=CIGdeadp1,CN=Users,DC=test,DC=io", "1" => "CN=CASUsers,CN=Users,DC=test,DC=io", "2" => "CN=CloudNCUsers,CN=Users,DC=test,DC=io",
"3" => "CN=CloudOfficeUsers,CN=Users,DC=test,DC=io"), "othermailbox" => array("0" => "jdope1@gmail.com", "1" => "foobar@gmail.com"), "whenchanged" => "20200929142821.0Z", "usnchanged" => "32855", "distinguishedname" => "CN=Joe Dope,CN=Users,DC=test,DC=io");



function check_diff_multi($array1, $array2){
    $result = array();

    foreach($array1 as $key => $val) {
        if(is_array($val) && isset($array2[$key])) {
            $tmp = check_diff_multi($val, $array2[$key]);
            if($tmp) {
                $result[$key] = $tmp;
            }
        }
        elseif(!isset($array2[$key])) {
            $result[$key] = null;
        }
        elseif($val !== $array2[$key]) {
            $result[$key] = $array2[$key];
        }

        if(isset($array2[$key])) {
            unset($array2[$key]);
        }
    }

    $result = array_merge($result, $array2);

    return $result;
}

$full_difference = array_merge(check_diff_multi($array, $array2), check_diff_multi($array2, $array));
print_r($full_difference);

This doesn’t give me the exact output I want.

Here’s the output it gives me:

Array
(
    [CN=Joe Dope,CN=Users,DC=test,DC=io] => Array
        (
            [memberof] => Array
                (
                    [0] => CN=CloudEmailUsers,CN=Users,DC=test,DC=io
                )

            [othermailbox] => 
            [whenchanged] => 20201004180306.0Z
            [usnchanged] => 33404
            [distinguishedname] => CN=Joe Dope,CN=Users,DC=test,DC=io
        )

    [CN=Ronald,CN=Users,DC=test,DC=io] => Array
        (
            [usnchanged] => 1342
            [whenchanged] => 20191106190201.0Z
        )

)

What I would like is something more like this:

Array
(
    [CN=Joe Dope,CN=Users,DC=test,DC=io] => Array
        (
            [memberof] => Array
                (
                    [3] => CN=CloudOfficeUsers,CN=Users,DC=test,DC=io (Deleted)
                    [3] => CN=CloudEmailUsers,CN=Users,DC=test,DC=io (Updated)
                )

            [othermailbox] => Array (Deleted)
                (
                    [0] => jdope1@gmail.com (Deleted)
                    [1] => foobar@gmail.com (Deleted)
                )
            [whenchanged] => 20201004180306.0Z (Added)
            [whenchanged] => 20200929142821.0Z (Deleted)
            [usnchanged] => 33404 (Added)
            [usnchanged] => 32855 (Deleted)
        )

    [CN=Ronald,CN=Users,DC=test,DC=io] => Array (Added)
        (
            [usnchanged] => 1342 (Added)
            [whenchanged] => 20191106190201.0Z (Added)
        )

)

Any help would be very much appreciated. Thanks!

EDIT:

Forgot to add an edge case for if arrays inside the multi-dimensional array have the same values.

<?php
$array[ "CN=Joe Dope,CN=Users,DC=test,DC=io" ] = array(
    "msexchassistantname" => ( "cn=CIGdeadp1,cn=Users,dc=test,dc=io" ),
    "memberof" => array(
        "0" => "CN=CIGdeadp1,CN=Users,DC=test,DC=io",
        "1" => "CN=CASUsers,CN=Users,DC=test,DC=io",
        "2" => "CN=CloudNCUsers,CN=Users,DC=test,DC=io",
        "3" => "CN=CloudEmailUsers,CN=Users,DC=test,DC=io"
    ),
    "objectclass" => array(
      "0" => "top",
      "1" => "person"
    ),
    "whenchanged" => "20201004180306.0Z",
    "usnchanged" => "33404",
    "distinguishedname" => "CN=Joe Dope,CN=Users,DC=test,DC=io"
);

$array[ "CN=Ronald,CN=Users,DC=test,DC=io" ] = array(
    "usnchanged" => "1342",
    "whenchanged" => "20191106190201.0Z"
);

$array2[ "CN=Joe Dope,CN=Users,DC=test,DC=io" ] = array(
    "msexchassistantname" => "cn=CIGdeadp1,cn=Users,dc=test,dc=io",
    "memberof" => array(
        "0" => "CN=CIGdeadp1,CN=Users,DC=test,DC=io",
        "1" => "CN=CASUsers,CN=Users,DC=test,DC=io",
        "2" => "CN=CloudNCUsers,CN=Users,DC=test,DC=io",
        "3" => "CN=CloudOfficeUsers,CN=Users,DC=test,DC=io"
    ),
    "objectclass" => array(
      "0" => "top",
      "1" => "person"
    ),
    "othermailbox" => array(
        "0" => "jdope1@gmail.com",
        "1" => "foobar@gmail.com"
    ),
    "whenchanged" => "20200929142821.0Z",
    "usnchanged" => "32855",
    "distinguishedname" => "CN=Joe Dope,CN=Users,DC=test,DC=io"
);
# Main recursive function to compare
function recordChanges($old, $new, &$result)
{
    # Loop through the old array to see what is removed
    foreach($old as $k => $v) {
        # If the old and new have the same key
        if(isset($new[$k])) {
            # If array, recurse the array
            if(is_array($v)) {
                recordChanges($old[$k], $new[$k], $result[$k]);
            }
            # Note if the old is different
            else {
                $append =   ($old[$k] == $new[$k])? "" : " (Deleted)";

                if(!empty($append))
                    $result[$k.$append] =   $v;
            }
        }
        # Note a straight delete
        else {
            $d  =   ' (Deleted)';
            $nk =   (is_array($v))? $k.$d : $k;
            $result[$nk] =   (is_array($v))? appendKeys($v, $d) : $v.$d;
        }
    }
    # Now go through the new array
    foreach($new as $k => $v) {
        # See if there is a matching old array
        if(isset($old[$k])) {
            # Recurse for changes
            if(is_array($v)) {
                recordChanges($old[$k], $new[$k], $result[$k]);
            }
            # If not in old, note update
            else {
                $append =   ($old[$k] == $new[$k])? "" : " (Updated)";

                if(!empty($append))
                    $result[$k.$append] =   $v;
            }
        }
        # If not in old, note as new
        else {
            $d  =   ' (Added)';
            $nk =   (is_array($v))? $k.$d : $k;
            $result[$nk] =   (is_array($v))? appendKeys($v, $d) : $v.$d;
        }

        ksort($result);
    }
}
# This will just do a full recurse and append without any conditions
function appendKeys($array, $append)
{
    $new    =   [];
    foreach($array as $k => $v) {
        $nk =   (is_array($v))? $k.$append : $k;
        $new[$nk]    =   (is_array($v))? appendKeys($v, $append) : $v.$append;
    }
    ksort($new);
    return $new;
}

$result =   [];
recordChanges($array2, $array, $result);
print_r($result);
?>

So now we have 2 objectclass’s that are the same in each array. Upon running the code, we get the following:

Array
(
    [CN=Joe Dope,CN=Users,DC=test,DC=io] => Array
        (
            [memberof] => Array
                (
                    [3 (Deleted)] => CN=CloudOfficeUsers,CN=Users,DC=test,DC=io
                    [3 (Updated)] => CN=CloudEmailUsers,CN=Users,DC=test,DC=io
                )

            [objectclass] => 
            [othermailbox (Deleted)] => Array
                (
                    [0] => jdope1@gmail.com (Deleted)
                    [1] => foobar@gmail.com (Deleted)
                )

            [usnchanged (Deleted)] => 32855
            [usnchanged (Updated)] => 33404
            [whenchanged (Deleted)] => 20200929142821.0Z
            [whenchanged (Updated)] => 20201004180306.0Z
        )

    [CN=Ronald,CN=Users,DC=test,DC=io (Added)] => Array
        (
            [usnchanged] => 1342 (Added)
            [whenchanged] => 20191106190201.0Z (Added)
        )

)

If we could remove the [objectclass] => it would be great.

Advertisement

Answer

You could probably do this with a recursive array iterator but I am just using a couple functions. The first function recordChanges(), could be refactored since the top half is using the same logic (essentially) as the second half, but you get the idea:

<?php
$array[ "CN=Joe Dope,CN=Users,DC=test,DC=io" ] = array(
    "msexchassistantname" => ( "cn=CIGdeadp1,cn=Users,dc=test,dc=io" ),
    "memberof" => array(
        "0" => "CN=CIGdeadp1,CN=Users,DC=test,DC=io",
        "1" => "CN=CASUsers,CN=Users,DC=test,DC=io",
        "2" => "CN=CloudNCUsers,CN=Users,DC=test,DC=io",
        "3" => "CN=CloudEmailUsers,CN=Users,DC=test,DC=io"
    ),
    "whenchanged" => "20201004180306.0Z",
    "usnchanged" => "33404",
    "distinguishedname" => "CN=Joe Dope,CN=Users,DC=test,DC=io"
);

$array[ "CN=Ronald,CN=Users,DC=test,DC=io" ] = array(
    "usnchanged" => "1342",
    "whenchanged" => "20191106190201.0Z"
);

$array2[ "CN=Joe Dope,CN=Users,DC=test,DC=io" ] = array(
    "msexchassistantname" => "cn=CIGdeadp1,cn=Users,dc=test,dc=io",
    "memberof" => array(
        "0" => "CN=CIGdeadp1,CN=Users,DC=test,DC=io",
        "1" => "CN=CASUsers,CN=Users,DC=test,DC=io",
        "2" => "CN=CloudNCUsers,CN=Users,DC=test,DC=io",
        "3" => "CN=CloudOfficeUsers,CN=Users,DC=test,DC=io"
    ),
    "othermailbox" => array(
        "0" => "jdope1@gmail.com",
        "1" => "foobar@gmail.com" 
    ),
    "whenchanged" => "20200929142821.0Z",
    "usnchanged" => "32855",
    "distinguishedname" => "CN=Joe Dope,CN=Users,DC=test,DC=io"
);
# Main recursive function to compare
function recordChanges($old, $new, &$result)
{
    # Loop through the old array to see what is removed
    foreach($old as $k => $v) {
        # If the old and new have the same key
        if(isset($new[$k])) {
            # If array, recurse the array
            if(is_array($v)) {
                recordChanges($old[$k], $new[$k], $result[$k]);
            }
            # Note if the old is different
            else {
                $append =   ($old[$k] == $new[$k])? "" : " (Deleted)";
                
                if(!empty($append))
                    $result[$k.$append] =   $v;
            }
        }
        # Note a straight delete
        else {
            $d  =   ' (Deleted)';
            $nk =   (is_array($v))? $k.$d : $k;
            $result[$nk] =   (is_array($v))? appendKeys($v, $d) : $v.$d;
        }
    }
    # Now go through the new array
    foreach($new as $k => $v) {
        # See if there is a matching old array
        if(isset($old[$k])) {
            # Recurse for changes
            if(is_array($v)) {
                recordChanges($old[$k], $new[$k], $result[$k]);
            }
            # If not in old, note update
            else {
                $append =   ($old[$k] == $new[$k])? "" : " (Updated)";
                
                if(!empty($append))
                    $result[$k.$append] =   $v;
            }
        }
        # If not in old, note as new
        else {
            $d  =   ' (Added)';
            $nk =   (is_array($v))? $k.$d : $k;
            $result[$nk] =   (is_array($v))? appendKeys($v, $d) : $v.$d;
        }
        
        ksort($result);
    }
}
# This will just do a full recurse and append without any conditions
function appendKeys($array, $append)
{
    $new    =   [];
    foreach($array as $k => $v) {
        $nk =   (is_array($v))? $k.$append : $k;
        $new[$nk]    =   (is_array($v))? appendKeys($v, $append) : $v.$append;
    }
    ksort($new);
    return $new;
}

$result =   [];
recordChanges($array2, $array, $result);
print_r($result);

This will give you:

Array
(
    [CN=Joe Dope,CN=Users,DC=test,DC=io] => Array
        (
            [memberof] => Array
                (
                    [3 (Deleted)] => CN=CloudOfficeUsers,CN=Users,DC=test,DC=io
                    [3 (Updated)] => CN=CloudEmailUsers,CN=Users,DC=test,DC=io
                )

            [othermailbox (Deleted)] => Array
                (
                    [0] => jdope1@gmail.com (Deleted)
                    [1] => foobar@gmail.com (Deleted)
                )

            [usnchanged (Deleted)] => 32855
            [usnchanged (Updated)] => 33404
            [whenchanged (Deleted)] => 20200929142821.0Z
            [whenchanged (Updated)] => 20201004180306.0Z
        )

    [CN=Ronald,CN=Users,DC=test,DC=io (Added)] => Array
        (
            [usnchanged] => 1342 (Added)
            [whenchanged] => 20191106190201.0Z (Added)
        )

)

Note that you can not have the same-named key because they will overwrite each other, so to keep keys, you would have to append the keys.

User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement