Как построить древовидный массив неограниченной вложенности?
вложенность мотет быть неограниченной
выглядит так, будто у тебя бесконечно оперативы и ты хочешь куда-то её применить )))

Вот вариант за линейное время работы (из предположения, что хэш-таблица работает в среднем за О(1))
без рекурсии
на js, переведи в ПХП сам

код
const array1 = [
    {page: '1.ru', title: 'title 1'},
    {page: '3.ru', title: 'title 3'},
    {page: '6.ru', title: 'title 6'},
];

const array2 = [
    {page: '666.ru', title: 'title 666', referer: '66.ru'},
    {page: '33.ru', title: 'title 33', referer: '3.ru'},
    {page: '66.ru', title: 'title 66', referer: '6.ru'},
];

function createTree(roots, notRoots) {
    if (!roots || !roots.length) { return []; }

    const childsMap = new Map();

    // строим карту (page => список_чилдов), создавая узлы дерева для чилдов
    notRoots.forEach((child) => {
        let childs = childsMap.get(child.referer);

        if (!childs) {
            childs = [];
            childsMap.set(child.referer, childs);
        }

        childs.push({
            page: child.page,
            title: child.title,
        });
    });

    // всем чилдам (вновь созданным узлам), которые попали в карту, проставляем чилдов
    childsMap.forEach((childs) => {
        childs.forEach((child) => {
            child.childs = childsMap.get(child.page) || [];
        })
    });

    // обходим корневые элементы, создаем для них узлы дерева, подставляем чилды по карте
    return roots.map((root) => {
        return {
            page: root.page,
            title: root.title,
            childs: childsMap.get(root.page) || []
        };
    })
}

console.log(createTree(array1, array2));

Как
рекурсивно.

а связи-то у вас где хранятся? в изначальных массивах нужны идентификаторы и ссылки на родительские идентификаторы. потом надо проиндексировать их по этим ссылкам, то есть надо сделать массив где ключами будут иды, а элементами - массивы с элементами соответствующего родителя. потом рекурсивно идем сверху вниз, взяли первый уровень, листаем смотрим есть ли в том индексе элементы, ссылающиеся на текущий элемент, если есть, то погружаемся. перед подгружением нужно записать в результирующий массив данные по текущему пути. именно перед, потому что если после погружения, то вложенное затрётся. понадобится функция для записи в массывы по пути (типа такой https://laravel.su/docs/8.x/helpers#method-array-set). путём должно быть перечисление идов через спецсимвол. текущий путь можно хранить во внешней переменной, при погружении добавлять к нему сегмент с идом, в который погрузились, после погружения убирать его