[php]google-api-php-clientでCould not json decode the access tokenというエラーが出た


phpで google-api-php-client を用いて、 GoogleAnalytics からアクセス数の多いページを取得する処理を書いていたのですが、proxy環境下だと

Could not json decode the access token

とエラーになってしまう現象が発生しました。

調べてみたところ

GoogleAnalyticsAPIを使ってみる(PHP編)

こちらの記事に、

Jsonフォーマットの戻り値の頭に、レスポンスヘッダが付いてしまう場合があるみたい。(これがエラーになったり、ならなかったりで発生条件が掴めない。。)

とあり、同じ現象が発生している模様。

proxy設定自体は 以下のようにライブラリを利用するアプリケーション側でcurlのproxy設定オプションを渡すことができます。

    $client = new Google_Client();
    # proxy setting
    $client::$io->setOptions(array(
        CURLOPT_PROXY => 'proxy',
        CURLOPT_PROXYPORT => 8080
    ));

しかし、今回のエラー修正にはライブラリに修正を加える必要がありました。

参考記事にあったように HTTP通信結果を取得した際に、不要な情報が付与されている場合は省くようにしました。

# google-api-php-client/src/auth/Google_OAuth2.php

  public function setAccessToken($token) {
    $position = strpos($token, '{');  # 追加
    $body = substr($token, $position); # 追加
    $token = json_decode($token, true);
    if ($token == null) {
      throw new Google_AuthException('Could not json decode the token');
    }
    if (! isset($token['access_token'])) {
      throw new Google_AuthException("Invalid token format");
    }
    $this->token = $token;
  }
  
  ・・・・
  
  private function refreshTokenRequest($params) {
    $http = new Google_HttpRequest(self::OAUTH2_TOKEN_URI, 'POST', array(), $params);
    $request = Google_Client::$io->makeRequest($http);

    $code = $request->getResponseHttpCode();
    $body = $request->getResponseBody();
    if (200 == $code) {
      $position = strpos($body, '{');  # 追加
      $body = substr($body, $position); # 追加
      $token = json_decode($body, true);
      if ($token == null) {
        throw new Google_AuthException("Could not json decode the access token");
      }

      ・・・・・
# google-api-php-client/src/io/Google_REST.php

  public static function decodeHttpResponse($response) {
    $code = $response->getResponseHttpCode();
    $body = $response->getResponseBody();
    $decoded = null;

    if ((intVal($code)) >= 300) {
      $decoded = json_decode($body, true);
      $err = 'Error calling ' . $response->getRequestMethod() . ' ' . $response->getUrl();
      if ($decoded != null && isset($decoded['error']['message'])  && isset($decoded['error']['code'])) {
        // if we're getting a json encoded error definition, use that instead of the raw response
        // body for improved readability
        $err .= ": ({$decoded['error']['code']}) {$decoded['error']['message']}";
      } else {
        $err .= ": ($code) $body";
      }

      throw new Google_ServiceException($err, $code, null, $decoded['error']['errors']);
    }

    // Only attempt to decode the response, if the response code wasn't (204) 'no content'
    if ($code != '204') {
      $position = strpos($body, '{');  # 追加
      $body = substr($body, $position); # 追加
      $decoded = json_decode($body, true);
      if ($decoded === null || $decoded === "") {
        throw new Google_ServiceException("Invalid json in service response: $body");
      }
    }
    return $decoded;
  }

これにだいぶはまりました。。。