Интеграция PHP с Google Docs: создание и управление документами

Раздел: Интеграция с внешними сервисами -> Веб-сервисы и API

Основные подходы к работе с 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/apiclient

Php 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');

Работа с Google Docs из PHP - comments

En
Php google docs (php)