facebook/facebook-php-ads-sdkの非同期処理『getInsightsAsync』を試してみる

# 概要

Facebook Marketing APIのバージョン2.4から今利用しているレポート集計の[reportstats](https://developers.facebook.com/docs/marketing-api/adreportstats/v2.4)が使えなくなるため、比較的最近出来たエンドポイントの[insights](https://developers.facebook.com/docs/marketing-api/insights/v2.4)を試しに使っています。

# 調査

reportstatsとinsightsを利用してみると、微妙に取れる件数が異なっている状態なのでどうしたもんかなと悩んでいました。
ところが今週くらいから、FacebookのWebツールである広告マネージャ側でinsightsのエンドポイントを叩き始めているので、使い方を眺めてみると以下のように呼んでいるようでした。
（パラメータのaccess_tokenは省いています。)

```
https://graph.facebook.com/v2.3/act_0000000000/insights?action_attribution_windows=["28d_click"]&callback=__globalCallbacks.f667930cc4&date_preset=yesterday&default_summary=false&fields=["adgroup_name","campaign_group_name","campaign_group_id","adgroup_id","impressions","social_reach","social_impressions","clicks","social_clicks","ctr","cpc","cost_per_unique_click","spend","total_actions","cost_per_action_type","actions"]&filtering=[{"field":"adgroup.delivery_info","operator":"IN","value":["active","archived","inactive","not_delivering","not_published","pending_review","recently_rejected","rejected","scheduled"]},{"field":"adgroup.impressions","operator":"GREATER_THAN","value":0},{"field":"adgroup.name","operator":"CONTAIN","value":"cont"}]&level=adgroup&limit=5000&locale=ja_JP&method=post&pretty=0&time_increment=1
```

`method=post`を引数にしているので、非同期で処理しているようでした。
結果はcallbackしたところに返ってきているようです。


# 実際にやってみる。

調査で出ていたパラメータをそのまま使うと[Graph API Explorer](https://developers.facebook.com/tools/explorer)は死んでしまいました。
`limit=5000`はさすがにどぎついので、`limit=1`でやってみても返ってきません。

パラメータを真似てバッチスクリプトの方で試そうと思ったのですが、sdkに非同期処理は無いかなと思って見てたら[getInsightsAsync](https://github.com/facebook/facebook-php-ads-sdk/blob/6f0f21e62a7df4d9961ccce94575a9197b741b54/src/FacebookAds/Object/AdAccount.php#L289)というのがあったのでこれを試すようにしました。

```test_job.php
require_once __DIR__ . '/vendor/autoload.php';

use FacebookAds\Api;
use FacebookAds\Object\AdAccount;
use FacebookAds\Object\Values\InsightsLevels;
use FacebookAds\Object\Values\InsightsPresets;

$app_id = '<YOUR_APP_ID>';
$app_secret = '<YOUR_APP_SECRET>';
$accessToken = '<YOUR_ACCESS_TOKEN>';
$account_id = 'act_<YOUR_ACOUNT_ID>';

try {
    Api::init(
        $app_id,
        $app_secret,
        $accessToken
    );
    $api = Api::instance();

    $ad_account = new AdAccount($account_id);
    $async_job = $ad_account->getInsightsAsync(
        [],
        [
            'fields' => 'impressions',
            'level' => InsightsLevels::ADGROUP,
            'date_preset' => InsightsPresets::YESTERDAY,
            'limit' => 100,
        ]);

    while (1) {
        if ($async_job->read()->isComplete()) {
            var_dump($async_job->read()->getData());
            insertReportData($async_job->getResult());
            $async_job->read()->delete();
            break;
        }
        sleep(10);
    }
} catch (Exception $e) {
    var_dump($e);
}

function insertReportData($insights)
{
    $insights->setUseImplicitFetch(true);
    while ($insights->valid()) {
        var_dump($insights->current());
        $insights->next();
    }
}

```

`var_dump($async_job->read()->getData());`で出力される値はこんな感じです。

```
array(6) {
  ["id"]=>
  string(13) "0000000000000"
  ["account_id"]=>
  string(15) "0000000000"
  ["time_ref"]=>
  int(1438232892)
  ["async_status"]=>
  string(11) "Job Running"
  ["async_percent_completion"]=>
  int(100)
  ["is_running"]=>
  bool(true)
}
```

`$job->read()->isComplete()`でtrueが返ってきているのにもかかわらず、async_statusが`Job Running`ってなってるので、その後に値を取ろうとすると`Error accessing adreport job.`という例外が投げられてしまうという(^_^;)

async_statusが`Job Completed`となっていると実際に完了になっている状態なので、sdk側はそこを何とかして欲しいなぁ。

修正してPR投げるといいんだろうか。