Интеграция PHP с Google Docs: создание и управление документами
Основные подходы к работе с Google Docs из PHP
Наиболее эффективный способ интеграции с Google Docs в PHP – использование официальной клиентской библиотеки Google API для PHP. Этот подход предоставляет удобные классы для работы с Google Docs API (v1), управление аутентификацией, обработку ошибок и поддержку всех методов документа.
Цель: программное создание, чтение, обновление и удаление документов Google Docs из PHP-приложения, а также управление их содержимым (текст, таблицы, изображения).
Типичные проблемы и их решения
- Ошибка аутентификации: неверно настроен сервисный аккаунт или OAuth 2.0. Проверьте путь к JSON-файлу с ключами и область доступа (scope). Для Google Docs требуется
https://www.googleapis.com/auth/documentsили.../drive. - Лимиты запросов: превышение квоты API. Используйте повторные попытки с экспоненциальной задержкой или увеличьте квоту в Google Cloud Console.
- Недоступность API Docs: если документ редактируется вручную, параллельные изменения могут вызвать конфликт. Применяйте оптимистичную блокировку (проверка revisionId).
// Установка библиотеки через Composer
composer require google/apiclientPhp google docs (работа с google docs из php)
// Аутентификация с сервисным аккаунтом
require_once 'vendor/autoload.php';
$client = new Google\Client();
$client->setAuthConfig('path/to/service-account-key.json');
$client->addScope(Google\Service\Docs::DOCUMENTS);
$client->addScope(Google\Service\Drive::DRIVE); // для доступа к файлам
$service = new Google\Service\Docs($client);
$driveService = new Google\Service\Drive($client);// Создание пустого документа
$document = new Google\Service\Docs\Document([
'title' => 'Тестовый документ из PHP'
]);
$createdDoc = $service->documents->create($document);
$documentId = $createdDoc->documentId;
echo "Создан документ с ID: $documentId";
// Результат: Создан документ с ID: 1abcXYZ...// Чтение содержимого документа
$doc = $service->documents->get($documentId);
$content = $doc->getBody()->getContent();
foreach ($content as $structuralElement) {
$paragraph = $structuralElement->getParagraph();
if ($paragraph) {
foreach ($paragraph->getElements() as $element) {
$textRun = $element->getTextRun();
if ($textRun) {
echo $textRun->getContent();
}
}
}
}// Обновление документа: вставка текста в конец
$requests = [
new Google\Service\Docs\Request([
'insertText' => [
'location' => ['index' => 1],
'text' => "\nНовый текст, добавленный через API."
]
])
];
$batchUpdateRequest = new Google\Service\Docs\BatchUpdateDocumentRequest([
'requests' => $requests
]);
$result = $service->documents->batchUpdate($documentId, $batchUpdateRequest);
Как взаимодействовать с Google Docs API без клиентской библиотеки, используя чистый cURL?
Этот вариант подходит, когда нет возможности установить Composer или требуется минимальная зависимость. Выполняются HTTP-запросы к REST API Google Docs. Необходимо вручную формировать токен доступа, обрабатывать ошибки и работать с JSON.
Цель: прямой вызов методов API через cURL, полный контроль над запросами.
Типичные ошибки
- Ошибка 401 Unauthorized: неверный или просроченный токен. Для сервисного аккаунта необходимо сгенерировать JWT и обменять на access_token.
- Ошибка 403 Forbidden: недостаточно прав доступа. Убедитесь, что сервисный аккаунт добавлен как редактор документа (если документ уже существует).
// Получение access_token для сервисного аккаунта (через JWT)
function getAccessToken() {
$serviceAccount = json_decode(file_get_contents('service-account-key.json'), true);
$jwt = [
'iss' => $serviceAccount['client_email'],
'scope' => 'https://www.googleapis.com/auth/documents',
'aud' => 'https://oauth2.googleapis.com/token',
'exp' => time() + 3600,
'iat' => time()
];
$privateKey = openssl_pkey_get_private($serviceAccount['private_key']);
// ... формирование JWT (детали опущены для краткости)
// Отправка POST на https://oauth2.googleapis.com/token
$response = //... cURL запрос с grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=$jwt
return $response['access_token'];
}
$accessToken = getAccessToken();
// Создание документа через REST
$url = 'https://docs.googleapis.com/v1/documents';
$data = json_encode(['title' => 'Документ из cURL']);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $accessToken,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
$docInfo = json_decode($result, true);
echo 'ID документа: ' . $docInfo['documentId'];Как получить содержимое Google Docs документа в виде текста или файла, если не требуется работа с форматированием?
Альтернативное решение – использовать Google Drive API. Документ экспортируется в форматы TXT, DOCX, PDF или HTML. Это проще, чем разбирать структуру Docs API, но теряется управление форматированием.
Цель: быстрое извлечение чистого текста или скачивание документа в распространённом формате.
Проблемы
- Потеря сложного форматирования: при экспорте в TXT все стили исчезают.
- Лимиты на экспорт: для больших документов может потребоваться несколько запросов.
// Экспорт документа в TXT
$fileId = '1abcXYZ...';
$response = $driveService->files->export($fileId, 'text/plain', array('alt' => 'media'));
$content = $response->getBody()->getContents();
file_put_contents('document.txt', $content);
echo 'Текст сохранён в document.txt';// Экспорт в PDF
$response = $driveService->files->export($fileId, 'application/pdf', array('alt' => 'media'));
$pdfContent = $response->getBody()->getContents();
file_put_contents('document.pdf', $pdfContent);Как настроить аутентификацию через OAuth 2.0 для работы от имени конечного пользователя (например, для интеграции с сервисом Google Workspace)?
Используется механизм OAuth 2.0 с согласием пользователя. Подходит для приложений, которые должны действовать от имени пользователя, а не приложения. Требуется создание OAuth 2.0 Client ID в Google Cloud Console и реализация редиректа.
Цель: предоставить каждому пользователю доступ к его личным документам или документам, к которым он имеет доступ.
Ошибки
- Код авторизации истек: процедуру обмена кода на токен нужно выполнять в течение нескольких минут.
- Неверный redirect_uri: должен точно совпадать с указанным в консоли, включая порт.
// Настройка клиента OAuth 2.0
$client = new Google\Client();
$client->setAuthConfig('path/to/oauth-client-id.json');
$client->addScope(Google\Service\Docs::DOCUMENTS);
$client->setRedirectUri('http://localhost:8080/oauth2callback.php');
// Перенаправление на страницу согласия
if (!isset($_GET['code'])) {
$authUrl = $client->createAuthUrl();
header('Location: ' . $authUrl);
exit;
}
// Обработка callback
$token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
$client->setAccessToken($token);
$_SESSION['access_token'] = $token;
// Теперь можно использовать $service = new Google\Service\Docs($client);Расширенные примеры работы с Google Docs из PHP
Далее приведены нестандартные сценарии использования API, которые часто требуются в реальных проектах.
Создание документа с форматированием: заголовки, жирный текст, списки
Пример демонстрирует, как добавить в документ заголовок, параграф с жирным текстом и маркированный список.
// Подготовка запросов
$requests = [
// 1. Вставка заголовка (Heading 1)
new Google\Service\Docs\Request([
'insertText' => [
'location' => ['index' => 1],
'text' => 'Мой заголовок\n'
]
]),
// 2. Применение стиля к заголовку
new Google\Service\Docs\Request([
'updateParagraphStyle' => [
'range' => [
'startIndex' => 1,
'endIndex' => 15 // длина "Мой заголовок\n"
],
'paragraphStyle' => [
'namedStyleType' => 'HEADING_1'
],
'fields' => 'namedStyleType'
]
]),
// 3. Вставка параграфа с жирным текстом
new Google\Service\Docs\Request([
'insertText' => [
'location' => ['index' => 15],
'text' => 'Этот текст будет жирным.\n'
]
]),
// 4. Применение форматирования (жирный) к только что вставленному тексту
new Google\Service\Docs\Request([
'updateTextStyle' => [
'range' => [
'startIndex' => 15,
'endIndex' => 40
],
'textStyle' => [
'bold' => true
],
'fields' => 'bold'
]
]),
// 5. Маркированный список
new Google\Service\Docs\Request([
'createParagraphBullets' => [
'range' => [
'startIndex' => 40,
'endIndex' => 60
],
'bulletPreset' => 'BULLET_DISC_CIRCLE_SQUARE'
]
])
];
$batchUpdate = new Google\Service\Docs\BatchUpdateDocumentRequest([
'requests' => $requests
]);
$result = $service->documents->batchUpdate($documentId, $batchUpdate);
echo 'Форматирование применено.';
// Результат: в документе появится заголовок, жирный текст и маркированный список.// Выходные данные: JSON-объект с результатами batchUpdate (например, изменения в документе).
Как вставить изображение в Google Docs из PHP?
Для вставки изображения необходимо предварительно загрузить его на Google Drive, получить ссылку, а затем использовать запрос insertInlineImage.
// Предположим, что файл изображения уже загружен на Drive и известен fileId
$imageFileId = '1imageFileId';
// Получаем ссылку на контент изображения
$imageUrl = 'https://drive.google.com/uc?id=' . $imageFileId;
$requests = [
new Google\Service\Docs\Request([
'insertInlineImage' => [
'location' => [
'index' => 1
],
'uri' => $imageUrl,
'objectSize' => [
'height' => [
'magnitude' => 300,
'unit' => 'PT'
],
'width' => [
'magnitude' => 400,
'unit' => 'PT'
]
]
]
])
];
$batchUpdate = new Google\Service\Docs\BatchUpdateDocumentRequest([
'requests' => $requests
]);
$service->documents->batchUpdate($documentId, $batchUpdate);
echo 'Изображение вставлено.';Проблемы при вставке изображений
- Изображение не отображается: URI должен быть публично доступным или у сервисного аккаунта должны быть права на чтение файла.
- Ошибка 400 Bad Request: проверьте формат objectSize – значения должны быть положительными.
Массовое обновление нескольких документов – итерация по списку
Часто требуется применить одинаковые изменения (например, добавить колонтитул) к десяткам документов. Пример перебора документов в папке.
// Получить список всех документов из определённой папки Google Drive
$folderId = '1folderId';
$optParams = [
'q' => "'$folderId' in parents and mimeType='application/vnd.google-apps.document'",
'fields' => 'files(id, name)'
];
$files = $driveService->files->listFiles($optParams);
foreach ($files->getFiles() as $file) {
$docId = $file->getId();
$docName = $file->getName();
// Создаём запрос на добавление нижнего колонтитула с номером страницы
$requests = [
new Google\Service\Docs\Request([
'createFooter' => [
'type' => 'DEFAULT'
]
]),
new Google\Service\Docs\Request([
'insertText' => [
'location' => [
'segmentId' => 'ftr1',
'index' => 1
],
'text' => 'Страница '
]
]),
new Google\Service\Docs\Request([
'insertPageNumber' => [
'location' => [
'segmentId' => 'ftr1',
'index' => 9
]
]
])
];
$batchUpdate = new Google\Service\Docs\BatchUpdateDocumentRequest([
'requests' => $requests
]);
try {
$service->documents->batchUpdate($docId, $batchUpdate);
echo "Документ $docName обновлён.\n";
} catch (Exception $e) {
echo "Ошибка обновления $docName: " . $e->getMessage() . "\n";
}
}
// Результат: у всех документов в папке появится нижний колонтитул с номером страницы.Типичные ошибки при массовых операциях
- Превышение квоты: добавьте sleep(1) между запросами.
- Конфликт версий: если документ изменяется во время обновления, запрос может быть отклонён. Используйте оптимистичную блокировку через проверку
revisionId.
Экспорт документа в PDF и отправка по email
Совмещение Drive API для экспорта и стандартной почтовой функции PHP.
function exportAndEmail($fileId, $toEmail) {
global $driveService;
// Экспорт в PDF
$response = $driveService->files->export($fileId, 'application/pdf', [
'alt' => 'media'
]);
$pdfContent = $response->getBody()->getContents();
// Временное сохранение
$tempFile = tempnam(sys_get_temp_dir(), 'doc');
file_put_contents($tempFile, $pdfContent);
// Отправка письма с вложением
$subject = 'Документ в формате PDF';
$message = 'Во вложении экспортированный документ Google Docs.';
$headers = 'From: no-reply@example.com';
$attachedFile = chunk_split(base64_encode($pdfContent));
$boundary = md5(time());
$headers .= "\r\nMIME-Version: 1.0\r\n";
$headers .= "Content-Type: multipart/mixed; boundary=\"$boundary\"\r\n";
$body = "--$boundary\r\n";
$body .= "Content-Type: text/plain; charset=UTF-8\r\n";
$body .= "$message\r\n";
$body .= "--$boundary\r\n";
$body .= "Content-Type: application/pdf; name=\"document.pdf\"\r\n";
$body .= "Content-Transfer-Encoding: base64\r\n";
$body .= "Content-Disposition: attachment\r\n";
$body .= "$attachedFile\r\n";
$body .= "--$boundary--";
if (mail($toEmail, $subject, $body, $headers)) {
echo "Письмо отправлено на $toEmail";
} else {
echo "Ошибка отправки письма.";
}
unlink($tempFile);
}
exportAndEmail('1docId', 'user@example.com');