You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google Calendar API获取日历列表返回空数组,但可获取特定日历事件

问题分析与解决方案

看起来你遇到的问题核心是服务账号和你个人Google账号的日历列表完全独立,我来拆解下原因和可行的解决办法:

问题根源

你的代码使用了useApplicationDefaultCredentials(),这意味着程序是以服务账号的身份访问Google Calendar API,而非你自己的个人Google账号:

  • 你能成功获取特定日历的事件,是因为你已经把该日历共享给了服务账号的邮箱(可在Google Cloud控制台的服务账号页面找到这个邮箱),所以服务账号拥有访问该日历事件的权限。
  • 但calendarList.list()接口返回的是当前身份(服务账号)自己的日历列表,服务账号默认没有添加任何日历到它的列表中,因此返回空数组。

解决方案(二选一)

方案1:将共享日历添加到服务账号的日历列表

如果你想通过calendarList.list()看到那些共享的日历,需要先把它们手动添加到服务账号的日历列表里。在现有代码中插入这段逻辑:

// 初始化service之后,添加这段代码
$targetCalendarId = "你的目标日历ID"; // 即你之前能获取事件的那个日历ID

$calendarListEntry = new Google_Service_Calendar_CalendarListEntry();
$calendarListEntry->setId($targetCalendarId);

try {
    $service->calendarList->insert($calendarListEntry);
    echo "日历已成功添加到服务账号的列表中";
} catch (Exception $e) {
    echo "添加失败:" . $e->getMessage();
}

// 现在再调用listCalendarList就能看到该日历了
$calendarList = $service->calendarList->listCalendarList();
echo "<pre>";
print_r($calendarList);
echo "</pre>";

如果有多个共享日历,需要逐个调用insert方法添加。添加完成后,后续调用listCalendarList就能返回这些日历了。

方案2:改用OAuth2授权(以个人账号身份访问)

如果你的需求是直接访问你自己个人Google账号的日历列表,那应该放弃服务账号,改用OAuth2授权流程,让程序以你的个人账号身份访问API。修改你的getClient()函数如下:

function getClient(){
    $client = new Google_Client();
    $client->setApplicationName(APPLICATION_NAME);
    $client->setScopes(SCOPES);
    $client->setAuthConfig(CLIENT_SECRET_PATH);
    $client->setAccessType('offline'); // 允许离线访问,保存刷新令牌
    $client->setPrompt('select_account consent'); // 强制选择账号并授权

    // 加载已保存的令牌(如果存在)
    $tokenPath = __DIR__ . '/token.json';
    if (file_exists($tokenPath)) {
        $accessToken = json_decode(file_get_contents($tokenPath), true);
        $client->setAccessToken($accessToken);
    }

    // 处理令牌过期的情况
    if ($client->isAccessTokenExpired()) {
        // 尝试刷新令牌
        if ($client->getRefreshToken()) {
            $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
        } else {
            // 生成授权URL
            $authUrl = $client->createAuthUrl();
            printf("请访问以下URL进行授权:\n%s\n", $authUrl);
            print "输入授权码:";
            $authCode = trim(fgets(STDIN));

            // 用授权码交换访问令牌
            $accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
            $client->setAccessToken($accessToken);

            // 保存令牌到本地文件,避免每次都授权
            if (!file_exists(dirname($tokenPath))) {
                mkdir(dirname($tokenPath), 0700, true);
            }
            file_put_contents($tokenPath, json_encode($client->getAccessToken()));
        }
    }
    return $client;
}

第一次运行这段代码时,控制台会输出一个授权URL,你需要用个人Google账号打开该URL,完成授权后复制授权码回到控制台输入。之后程序会自动保存令牌,后续运行就会以你的个人账号身份访问API,此时calendarList.list()就能返回你个人账号中的所有日历了。


额外验证点

  • 如果使用方案1,请确认服务账号的邮箱已经被添加为目标日历的共享成员(至少拥有“查看”权限)。
  • 如果使用方案2,请确保在Google Cloud控制台中,你的OAuth2客户端ID是“桌面应用”类型(在OAuth同意屏幕的“已授权的重定向URI”中配置)。

内容的提问来源于stack exchange,提问作者Engg Php

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:15:11