Какой подход для валидатора правильней?
Мне не равится ни тот ни другой подход.

Исключение при валидации - это оксюморон.
Исключение кидается кода функция не может выполнить свою работу. Проифнормировать о невалидности данных - это как раз функция функции валидации. Где тут исключительная ситуация?
Вот для функции, скажем, сохранения данных, невалидные данные - это абсолютно законный повод кинуть исключение. Но для функции валидации - ни в коем случае. 
Функция проверки уникальности введенного логина не должна кидать исключение, если логин не уникальный.
Исключение она должна кидать если БД недоступна.

Но передача значения в параметр по ссылке - это как бы тоже адов кстыль, такая же архитектурная затычка, как global.
Я бы архитектурно использовал первый подход, но добавил метод для получения текста ошибок.
Архитектурно правильнее возвращать как в примере с Yii2. Использование исключений для управления потоком выполнения программы считается серьёзным антипаттерном. 

Например:
1. Исключения в данном случае - просто более хитрая замена оператору GOTO. Надеюсь, не нужно объяснять почему GOTO тоже считается антипаттерном?
2. Программы, написанные в таком стиле, становится сложно читать и понимать.
3. Меньшая эффективность в рантайме, так как большинство современных компиляторов не оптимизированы для случаев использования исключений в качестве управляющей логики.

Более подробно по ссылке.