PHP Question: how to array_intersect_assoc() recursively

Yes, it's the expected behavior, because the comparison is done using string representations, and the function does not recurse down nested arrays. From the manual:

The two values from the key => value pairs are considered equal only if (string) $elem1 === (string) $elem2 . In other words a strict type check is executed so the string representation must be the same.

If you tried to intersect with an array with 'key1' => 'Array', you'd get the same result because the string representation of an array is always 'Array'.

One of the user-contributed notes, by nleippe, contains a recursive implementation that looks promising (I modified the third line to do string comparison on any non-array values):

function array_intersect_assoc_recursive(&$arr1, &$arr2) {
    if (!is_array($arr1) || !is_array($arr2)) {
//      return $arr1 == $arr2; // Original line
        return (string) $arr1 == (string) $arr2;
    }
    $commonkeys = array_intersect(array_keys($arr1), array_keys($arr2));
    $ret = array();
    foreach ($commonkeys as $key) {
        $ret[$key] =& array_intersect_assoc_recursive($arr1[$key], $arr2[$key]);
    }
    return $ret;
}

function array_key_match_recursive(array $main, array $other, $i = 0, &$result = []) {
    foreach($main as $key => $value) {
        $k = sprintf('%s%s', str_repeat('=', $i), $key);
        if (!isset($other[$key])) {
            $result[$k][] = 'not key';
        }
        if (!is_array($value) && empty($other[$key])) {
            $result[$k][] = 'value empty';
        }
        if (is_array($value) && isset($other[$key])) {
            array_key_match_recursive($value, $other[$key], ++$i, $result);
        }
    }

    //return (bool) !$result;
    return $result;
}