Неэффективность этого решения просто вопиёт к небу.
Перебирать строку столько раз, сколько в ней букв - это же кощунство. Неужели нельзя за один проход посчитать символы, а за второй вывести их веса? И получить условные O(n*3) вместо O(n*3 + n^2).
$len = mb_strlen($string);
$counts = array_count_values(mb_str_split($string));
foreach ($counts as $letter => $count) {
echo "$letter: " . round($count / $len * 100, 1). "\n";
}
И заодно не придётся выковыривать из строки отдельные символы, которые мы уже выковыряли через mb_str_split.
А для практики будет полезнее реализовать подсчет символов самостоятельно, без использования встроенной функции РНР