Функции обратного вызова удобно использовать для вставки определенного функционала в выполняемый код. При использовании ООП, это способ не делать наследование с изменением самого класса. Ведь исходный код класса может, например, поменяться разработчиком при обновлении. Так же можно сделать вызов нужной функции(метода) по срабатыванию определенного события.

Для использования функций обратного вызова, в классе должна быть предусмотрена возможность их обработки.

Рассмотрим следующий пример.
class Db{
    private $login;
    private $password;
    
    private $callbacks = array();
    private $userOptions = null;
    
    function __construct($log, $pas) {
        $this->login = $log;
        $this->password = $pas;
    }
    
    //Регистрирует функции обратного вызова.
    function registerCallback ($callbacks) {
        //Проверка возможности вызова анонимной функции
        if (!is_callable ($callbacks) ) {
            throw new Exception ( "Функция обратного вызова - невызываемая ! " ) ;
        }
        $this->callbacks[] = $callbacks; //Сохраняем в массив
    }

    function settings() {
        //В цикле предусматриваем вызов переданных функций (обратного вызова)
        foreach ( $this->callbacks as $callback){
            $date = call_user_func ($callback, $this->login); //вызывает функцию $callback с аргументом $login
            if($date) $this->userOptions =    $date; //массив данных сохраняем для дальнейшей обработки    
        }    
    }
    
    function connect(){
        // if ($this->login ... || $this->password){. . .} //проверка логина и пароля
        echo "Вход пользователя $this->login : обрабатывается . . . <br>" ;

        $this->settings(); //вызываем метод обработки функций обратного вызова
        /*
         * Соединение с БД с применением настроек пользователя ($this->userOptions)
         * Результат соединения сохраняем в $result
         * if (!$result) throw new Exception ("Ошибка соединения") ;
         */

        //Выведем пользовательские настройки для демонстрации
        echo '<pre>';
        var_dump($this->userOptions);
        echo '</pre>';

        echo 'Готово!<br><hr>';    
        return true; //при успехе соединения
    }
}


$settings = function(){
    return [
        'charset' => 'utf-8',
    ];
};
$status = function(){
    echo 'Пользователь имеет привилегированный статус.';
};
$db1= new Db('Serj', 123);
$db1->registerCallback($settings);
$db1->registerCallback($status);


$db2 = new Db('Vasia', 456);

$settings = function($login){    
    echo "Уникальный код пользователя $login: " . rand()*789;
    return [
        'charset' => 'utf-8',
        'dbName' => 'localhost'
    ];
};
$db3 = new Db('Sveta', 789);
$db3->registerCallback($settings);

$db1->connect();
$db2->connect();
$db3->connect();



Результат выполнения:
Вход пользователя Serj : обрабатывается . . . 
Пользователь имеет привилегированный статус.
array(1) {
  ["charset"]=>
  string(5) "utf-8"
}
Готово!
________________________________________
Вход пользователя Vasia : обрабатывается . . . 

NULL
Готово!
________________________________________
Вход пользователя Sveta : обрабатывается . . . 
Уникальный код пользователя Sveta: 1253721
array(2) {
  ["charset"]=>
  string(5) "utf-8"
  ["dbName"]=>
  string(9) "localhost"
}
Готово!
В данном примере, в качестве функции обратного вызова выступает анонимная функция, которую мы передаем в метод регистрирующий такие функции (сохраняет в массив функций). В нашем примере, функции обратного вызова используются для передачи массива с настройками подключения к базе данных. Так же они решают некоторые задачи (тут – вывод некоторой информации о пользователе).

При попытке соединения с БД (вызов метода connect() объекта Db), вызывается метод обрабатывающий функции обратного вызова - settings(). В нем, перебирается массив сохраненных раньше функций, каждую из которых вызывает функция call_user_func(). Вторым параметром которой мы передаем нужные аргументы функции обратного вызова. В данном случае, например, нам может понадобиться логин.

На этом этапе выводятся сообщения из данных функций обратного вызова, а т.к. они возвращают массив настроек – он сохраняется для дальнейшего использования.
Для примера, я сделал вывод массива настроек на экран в методе connect().

То есть наши функции обратного вызова выполняются только тогда, когда мы предусмотрим это кодом, а не при их объявлении. Таким образом, мы можем предусмотреть в их функционале использование переменных(свойств), которые появятся в области видимости функций обратного вызова только при их вызове. Так же при вызове, такая функция может иметь доступ к переменным/свойствам и методам той области видимости, где она была объявлена. Подробнее об этом в заметке про замыкания.