Я же говорил, что такие вещи без условий корректно практически не разобрать. Если могут быть не все ключи у каждой сущности или же для каждой из них порядок следования ключей в исходных данных будет различным, то это нужно проверять. В противном случае у одной сущности к примеру адрес будет на месте, а у другой совсем не в той ячейке.
$arr = [];
//задаем нужную последовательсть заголовков
$keys = ['givenName', 'mail', 'sn', 'telephoneNumber', 'createTimestamp'];
//заголовок csv-файла
$csv[] = implode(';', $keys);
foreach(file('data.txt', FILE_SKIP_EMPTY_LINES ) as $v) {
if(preg_match('/'.implode('|', $keys).'/', $v)) {
$v = array_map('trim', explode(':', $v));
//если поле уже дополнено в массив, но другие поля для текущего набора не определены, то игнорируем данный набор
if(array_key_exists($v[0], $arr) && count($arr) != 5) $arr = [];
//иначе добавляем поле в массив
else $arr[$v[0]] = $v[1];
}
//если массив содержит пять полей, то добавляем в CSV
if(count($arr)==5) {
//отсортировать поля данных согласно csv-заголовку
$arr = array_replace(array_flip($keys), $arr);
//значения полей массива в CSV как csv-строку
$csv[] = implode(';', $arr);
$arr = [];
}
}
//в CSV формат
$csv = implode("\r\n", $csv);
//сохранем в файл, который потом можно будет скачать
file_put_contents('data.csv', $csv);
Здесь если найдено не все пять полей, а следующая запись это одно из полей, которое уже было обнаружено, то такая запись пропускается (хотя это навскидку и по идее не будет работать всегда должным образом, то есть тут нужно нечто более сердитое). К тому же изначально задается порядок полей так, как они должны быть представлены в csv-файле. По этому же порядку происходит и сортировка данных полей не зависимо от того как они следуют в исходном файле.
Ну и останется "дикое" представление времени преобразовать в человеческое.