code: public static function returnFile(string $filePath): void
      {
          if (file_exists($filePath)) {
              // сбрасываем буфер вывода PHP, чтобы избежать переполнения памяти выделенной под скрипт
              // если этого не сделать файл будет читаться в память полностью!
              if (ob_get_level()) {
                  ob_end_clean();
              }
              // заставляем браузер показать окно сохранения файла
              header('Content-Description: File Transfer');
              header('Content-Type: application/octet-stream');
              header('Content-Disposition: attachment; filename=' . basename($filePath));
              header('Content-Transfer-Encoding: binary');
              header('Expires: 0');
              header('Cache-Control: must-revalidate');
              header('Pragma: public');
              header('Content-Length: ' . filesize($filePath));
              // читаем файл и отправляем его пользователю
              if ($fd = fopen($filePath, 'rb')) {
                  while (!feof($fd)) {
                      print fread($fd, static::FREAD_DEFAULT_LENGTH);
                  }
  
                  fclose($fd);
              }
  
              exit;
          }
      }
docs: Вернуть файл пользователю
  
  @param string $filePath - путь к файлу