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
相关产品推荐
相关产品推荐

