ええやんブログ

ええやないかのええやんブログ

FacebookアプリでError code: 196

はい。eeyanaikaです。

さて、今回はFacebookアプリをiPhoneで動かした際に「Error code: 196」というエラーが出力される現象についてです。

これはこちらの記事にありますように、モバイル版はredirect_uriを渡したらダメだそうです。

モバイル版およびiPhoneアプリ版の場合は、下記のようにredirect_uriを渡さないようにするとうまく動きました。

<?php
require '../src/facebook.php';

// アプリのインスタンスを作成(appIdとsecretは適宜置き換えてね)
$facebook = new Facebook(array(
        "appId"  => アプリID,
        "secret" => アプリシークレットコード,
));

try {

    // 現在ログイン中のユーザー情報を取得する
    $user_id = $facebook->getUser();

} catch (FacebookApiException $e) {
    // Facebook APIエラー
}

if (!$user_id) {

    $is_app = (strpos($ua, 'FBAN') !== FALSE);
    $is_mobile = (strpos($ua, 'iPhone') !== FALSE || strpos($ua, 'iPad') !== FALSE || strpos($ua, 'iPod') !== FALSE || strpos($ua, 'Android') !== FALSE);

    // Facebookのアプリ経由の場合はredirect_uriは指定しない
    if ($is_app == TRUE || $is_mobile == TRUE){

        $url = $facebook->getLoginUrl(array(
                "scope"        => "publish_stream"
        ));

    } else {

        $url = $facebook->getLoginUrl(array(
                "redirect_uri" => "http://apps.facebook.com/".アプリ名,
                "scope"        => "publish_stream"
        ));

    }

    // アプリ未登録ユーザーなら facebook の認証ページへ遷移
    echo "<script type='text/javascript'>top.location.href = '{$url}'</script>";
    exit;

}

それでは。

SafariでFacebookアプリが動かない

ニンニキニキニキ ニンニキニキニキニニンがeeyanaikaです。
いいですよねスチャダラパー

というわけで、今回はFacebookアプリがSafariで動かない現象についてと対応方法です。
SafariもIE同様、iframe上で表示される別ドメインのサイトはクッキーが有効になりません。
で、さらにP3Pのポリシー宣言をしてもSafariでは無効のままです。
なので、Facebookにログインした状態でアプリを開始しようとすると、二回目以降のアクセスではPHP SDKの getUser() の戻り値が必ず0になってしまいます。

SDKを調べてみると、base_facebook.phpにある $this->getSignedRequest() が認証情報を取得しているんですが、そのメソッド中の $_COOKIE[$this->getSignedRequestCookieName()] が、クッキーが無効になっているせいで、空になってしまうのが原因のようです。

<?php
  /**
   * Retrieve the signed request, either from a request parameter or,
   * if not present, from a cookie.
   *
   * @return string the signed request, if available, or null otherwise.
   */
  public function getSignedRequest() {
    if (!$this->signedRequest) {
      if (!empty($_REQUEST['signed_request'])) {
        $this->signedRequest = $this->parseSignedRequest(
          $_REQUEST['signed_request']);
      } else if (!empty($_COOKIE[$this->getSignedRequestCookieName()])) {
        $this->signedRequest = $this->parseSignedRequest(
          $_COOKIE[$this->getSignedRequestCookieName()]);
      }
    }
    return $this->signedRequest;
  }

んで、いろいろ検索してみると、やり方は2つくらいあるようで、ひとつはこんな感じで、別のiframeを用意し、ロード時にformをsubmitしてsession_startする手法です。
これは以前GoogleがSafariの設定を迂回していた件と同じ感じですかね。

これを試してみたのですが、うまくいきませんでした。すみません。

次にパラメータを引き渡していく手法です。
こちらは調べてもほとんどやり方が出てこなかったので、独自の方法になります。
もととなるソースはこちら

1. 初回アクセス時にキャッシュに保存

初回のアクセスではUser IDおよびsigned_requestは取得できることを確認したので、まずはそれをキャッシュに保存したいと思います。 User IDをキーにしてキャッシュにsigned_requestを保存する処理を、indexメソッドにて実装します。

<?php
    /**
     * 初期画面アクション
     */
    function index() {

        //----------------------------------------
        // データを取得する
        //----------------------------------------
        try {

            // 自分の情報を取得
            $this->me = $this->facebook->api('/me');

        } catch (FacebookApiException $e) {
            //
            // エラー処理
            //
        }

        //-----------------------------------------
        // キャッシュにデータを保存する
        //-----------------------------------------
        $cache_data = array();
        $cache_data["user_id"]        = $this->user_id;
        $cache_data["signed_request"] = $_REQUEST["signed_request"];

        $this->cache->save("eeyan_cache_key".$this->user_id, $cache_data, 3600);

        //-----------------------------------------
        // データを設定する
        //-----------------------------------------
        $view = array();
        $view["user_id"] = $this->user_id;   // 会員ID
        $view["me"] = $this->me;             // ユーザー情報

        // ビューを指定
        $this->load->view("eeyan/index", $view);

    }
2. User IDを持ちまわる

次はUser IDをページ遷移で持ちまわるようにします。
今回はGETで送るようにしますが、POSTでもかまいません。

<!doctype html>
<html xmlns:fb="http://www.facebook.com/2008/fbml">
<head>
<title>eeyan index</title>
</head>
<body>
    <h1>次のページへ</h1>
    <form action="/eeyan/process/0?user_id=<?php echo $user_id; ?>" id="frm" name="frm" method="post">
        <input type="submit" value="送信する">                               
    </form>
</body>
</html>
3. User IDを元にsigned_requestを設定

あとはコンストラクタで、GETで渡ってきたUser IDを元にキャッシュを取得し、キャッシュ中にあるsigned_requestを再設定してあげればOKです。
$_REQUEST["signed_request"] に設定すると、getUser() は正常にUser IDを返してくれるようになります。

<?php
    function __construct() {

        parent::__construct();

        // キャッシュドライバーの指定
        $this->load->driver('cache', array('adapter' => 'file'));

        $ua = $_SERVER['HTTP_USER_AGENT'];
        // IEのcookieを許可する
        if (strpos($ua, 'MSIE') !== FALSE) {
            header('p3p: CP="ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV"');
        }

        require_once APPPATH."third_party/Facebook/facebook.php";

        // Facebookクラスのインスタンス生成
        $this->facebook = new Facebook(array(
                "appId"  => アプリID,
                "secret" => アプリシークレットコード,
                "cookie" => true
        ));

        // Safari用に引き渡したuser_idを設定する
        if (isset($_GET["user_id"])) {
            $this->user_id = $_GET["user_id"];
        }

        // キャッシュからsigned_requestを取得する
        $cache_data = $this->cache->get("eeyan_cache_key".$this->user_id);

        if (isset($cache_data["user_id"])) {
            $_REQUEST["signed_request"] = $cache_data["signed_request"];
        }

        try {

            // 現在ログイン中のユーザー情報を取得する
            $this->user_id = $this->facebook->getUser();

        } catch (FacebookApiException $e) {
            //
            // エラー処理
            //
        }

        if (!$this->user_id) {

            // ログインURLを生成
            $url = $this->facebook->getLoginUrl(array(
                        "redirect_uri" => "http://apps.facebook.com/".アプリ名,
                        "scope"        => "publish_stream"
            ));

            // アプリ未登録ユーザーなら facebook の認証ページへ遷移
            echo "<script type='text/javascript'>top.location.href = '{$url}';</script>";
            exit;

        }

    }

あとはform_helperを拡張して、自動でUser IDを持ちまわるようにしてあげると、ページ数が多いアプリでもそれほど気にならずに実装できるかと思います。

SDKを使ってFacebookアプリを作ろうとすると結構大変ですね。ではでは。

IEでFacebookアプリが動かない

こんにちは、こんばんわ。eeyanaikaですー。
前回作ったCodeigniterを使ったFacebookアプリですが、IEで動きません。わお。
というわけで、まずは現象を。

前回のソースのコンストラクタ部分でログイン中のUser IDを取得しようとするが、なぜか初回のみしか取得できない。
二回目以降のアクセスではPHP SDKの getUser() の戻り値が必ず0になってしまうので、アプリで次のページに遷移しようとすると、

User IDがない→もう一度認証画面へ→認証が成功している→設定されているキャンバスURL(TOPページ)に遷移

という感じでループしてしまいます。

<?php
    function __construct() {

        parent::__construct();

        require_once APPPATH."third_party/Facebook/facebook.php";

        // Facebookクラスのインスタンス生成
        $this->facebook = new Facebook(array(
                "appId"  => アプリID,
                "secret" => アプリシークレットコード,
                "cookie" => true
        ));

        try {

            // 現在ログイン中のユーザー情報を取得する
            $this->user_id = $this->facebook->getUser();

        } catch (FacebookApiException $e) {
            //
            // エラー処理
            //
        }

        if (!$this->user_id) {

            // ログインURLを生成
            $url = $this->facebook->getLoginUrl(array(
                        "redirect_uri" => "http://apps.facebook.com/".アプリ名,
                        "scope"        => "publish_stream"
            ));

            // アプリ未登録ユーザーなら facebook の認証ページへ遷移
            echo "<script type='text/javascript'>top.location.href = '{$url}';</script>";
            exit;

        }

    }

で、調べてみるとすぐ見つかりました。
Facebookのアプリはiframe上で表示されるので、アプリ自体のドメインはFacebookのドメインと違うので(そりゃそうだ)、IEではクッキーが有効にならないんですね。
どうすればいいかというと、コンストラクタの最初でP3Pでポリシーを宣言すればいいとのこと。

<?php
    function __construct() {

        parent::__construct();

        $ua = $_SERVER['HTTP_USER_AGENT'];
        // IEのcookieを許可する
        if (strpos($ua, 'MSIE') !== FALSE) {
            header('p3p: CP="ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV"');
        }

ただ、P3Pのポリシー宣言は理解して使いましょう。ええ。
P3Pコンパクトポリシーをコピペするのが流行らないことを祈る | 水無月ばけらのえび日記 P3Pコンパクトポリシーをコピペするのが流行らないことを祈る | 水無月ばけらのえび日記

次はSafari対応について書きたいと思います。

Codeigniterを使ったFacebookアプリのつくりかた

どうもー、eeyanaikaでーす。

というわけで前回から引き続き、PHPフレームワークの一つである「CodeIgniter」を使ったFacebookアプリの作り方を書いていきます。 基本的な思想としてはconfig等への別ファイル化はせず、超シンプルにしています。

controller

FacebookのキャンバスURLに登録しているパスに従い、コントローラーを作成します。
今回登録したキャンバスURLはhttps://example.com/shindan/index.phpなので、Shindanコントローラーのindexメソッドを作ってみます。
コンストラクタで認証処理を行い、User IDが無い場合はFacebookのログイン画面に遷移させます。

<?php

/**
 * ええやん診断ページコントローラ
 *
 * @author eeyan
 */
class Shindan extends CI_controller {

    /**
     * @var FacebookアプリID
     */
    const FB_APP_ID = "345689273462046";

    /**
     * @var Facebookアプリシークレットコード
     */
    const FB_APP_SECRET = "3tesvgr537vecu563457ui456liu5457";

    /**
     * @var Facebookアプリ名
     */
    const FB_APP_NAME = "eeyan_app";

    /**
     * @var Facebook会員ID
     */
    var $user_id = NULL;

    /**
     * @var Facebookユーザー情報
     */
    var $me = NULL;

    /**
     * @var Facebook SDK インスタンス
     */
    var $facebook = NULL;

    /**
     * コンストラクタ
     */
    function __construct() {

        parent::__construct();

        //----------------------------------------
        // 処理前に準備する
        //----------------------------------------
        // third_party直下にFacebookのPHP SDKを配置
        require_once APPPATH."third_party/Facebook/facebook.php";

        // Facebookクラスのインスタンス生成
        $this->facebook = new Facebook(array(
                "appId"  => Shindan::FB_APP_ID,
                "secret" => Shindan::FB_APP_SECRET,
                "cookie" => true
        ));

        try {

            // 現在ログイン中のユーザー情報を取得する
            $this->user_id = $this->facebook->getUser();

        } catch (FacebookApiException $e) {
            //
            // エラー処理
            //
        }

        if (!$this->user_id) {

            // ログインURLを生成
            $url = $this->facebook->getLoginUrl(array(
                        "redirect_uri" => "http://apps.facebook.com/".Shindan::FB_APP_NAME,
                        "scope"        => "publish_stream"
            ));

            // アプリ未登録ユーザーなら facebook の認証ページへ遷移
            echo "<script type='text/javascript'>top.location.href = '{$url}';</script>";
            exit;

        }

    }

    /**
     * 初期画面アクション
     */
    function index() {

        //----------------------------------------
        // データを取得する
        //----------------------------------------
        try {

            // 自分の情報を取得
            $this->me = $this->facebook->api('/me');

        } catch (FacebookApiException $e) {
            //
            // エラー処理
            //
        }

        //-----------------------------------------
        // データを設定する
        //-----------------------------------------
        $view = array();
        $view["user_id"] = $this->user_id;   // 会員ID
        $view["me"] = $this->me;             // ユーザー情報

        // ビューを指定
        $this->load->view("eeyan/index", $view);

    }

}
view

作成したメソッドに対応したビューを作成します。
ビューではコントローラーで渡されたユーザー情報を表示しています。

<!doctype html>
<html xmlns:fb="http://www.facebook.com/2008/fbml">
<head>
<title>eeyan index</title>
</head>
<body>
    <h1>あなたの情報</h1>
    <?php var_dump($me); ?>
</body>
</html>

ただ、これいろいろと問題あるんです。
IEとかSafariで動きません。おいおい。

というわけで、次回はこのソースを修正していきたいと思います。

Facebook SDK for PHPを使ったFacebookアプリのつくりかた

みなさんお元気ですか?僕は元気です。

さて、PHPFacebookアプリをつくろうとしたのですが、いざ検索すると古い情報がまぎれていてよーわからん!となったので、自分の情報をさらに混ぜていこうと思います。ふふふ。

1. Facebookアプリを登録する

まずはFacebook開発者向けサイトに登録します。
なお、登録にはアカウント認証が必要です。

次にAppsにある「新しいアプリを作成する」ボタンよりアプリを登録します。
App Nameだけ入力すればOKです。

2. Facebookアプリの設定を行う

アプリを登録すると基本設定が表示されるはずですので、基本設定を登録していきます。
まずは下記の設定さえすれば大丈夫だと思います(モバイルはまた今度説明します)。
- Namespace:アプリ名(eeyan_shindanとか)
- Sandbox Mode:有効
- Facebook上のアプリ
 キャンバスURL:FacebookからアクセスされるURL
http://example.com/shindan/index.phpとか)
 セキュリティで保護されたキャンバスのURL:FacebookからアクセスされるSSLのURL
https://example.com/shindan/index.phpとか)

キャンバスURLを設定すると、キャンバスページのURLが発行されます。
http://apps.facebook.com/[Namespaceで設定した文字列]
アプリ完成時には、このページにアクセスすることになります。

ちなみにSandbox Modeを有効にすると、テスト出来るユーザーを絞り込むことができます。 テストユーザーは左側にある「開発者の役割」のテスト担当者から追加できます。

Facebookでの設定はこれくらいです。

3. Facebook謹製のPHP SDKをダウンロード

Facebook PHP SDKのgithubからSDKのファイルをダウンロードします。
githubのページにあるZIPボタンをクリックするとソースがダウンロードできます。

4. キャンバスページ側を作成する

ダウンロードしたSDKの中にあるsrcというフォルダが実際に使用するファイルなのですが、同じ場所にあるexamplesがちょうどいいサンプルになります。
下のコードです。

<?php
require '../src/facebook.php';

// アプリのインスタンスを作成(appIdとsecretは適宜置き換えてね)
$facebook = new Facebook(array(
        'appId'  => '344617158898614',
        'secret' => '6dc8ac871858b34798bc2488200e503d',
));

// User IDを取得
$user = $facebook->getUser();

// User IDがあればFacebookにログインしているとみなす
if ($user) {
    try {
        // ログインしているユーザーのユーザー情報を取得
        $user_profile = $facebook->api('/me');
    } catch (FacebookApiException $e) {
        error_log($e);
        $user = null;
    }
}

// 現在のログイン状況にしたがい、ログインURLもしくはログアウトURLを取得
if ($user) {
    $logoutUrl = $facebook->getLogoutUrl();
} else {
    $loginUrl = $facebook->getLoginUrl();
}

// たとえばNaitik Shahさんの公開データはこうやって取得します
$naitik = $facebook->api('/naitik');

?>
<!doctype html>
<html xmlns:fb="http://www.facebook.com/2008/fbml">
<head>
<title>php-sdk</title>
<style>
body {
    font-family: 'Lucida Grande', Verdana, Arial, sans-serif;
}

h1 a {
    text-decoration: none;
    color: #3b5998;
}

h1 a:hover {
    text-decoration: underline;
}
</style>
</head>
<body>
    <h1>php-sdk</h1>

    <?php if ($user): ?>
        <a href="<?php echo $logoutUrl; ?>">ログアウト</a>
    <?php else: ?>
    <div>
        <a href="<?php echo $loginUrl; ?>">Facebookにログイン</a>
    </div>
    <?php endif ?>

    <h3>PHP セッション</h3>
    <pre>
        <?php print_r($_SESSION); ?>
    </pre>

    <?php if ($user): ?>
    <h3>あなたの情報</h3>
    <img src="https://graph.facebook.com/<?php echo $user; ?>/picture">

    <h3>あなたのUser Object(/me)</h3>
    <pre>
        <?php print_r($user_profile); ?>
    </pre>
    <?php else: ?>
    <strong><em>ログインしていません</em> </strong>
    <?php endif ?>

    <h3>Naitik公開プロフィール</h3>
    <img src="https://graph.facebook.com/naitik/picture">
    <?php echo $naitik['name']; ?>
</body>
</html>

このコードとsrcのフォルダをキャンバスURLで指定したパスに配置してください。
あと、appIdとsecretは作成したアプリのものにしておいてください。
キャンバス上で表示されるPHPはこれで完了です。

キャンバスページのURLにアクセスして、ログイン画面が表示されればOKです。

ただこのままだとMVCもなにもないなーという状態なので、次からはCodeIngniterに乗っけていきます。

Facebookアプリでテスト担当者が追加できない

これハマりました。

理由は単純でした。テスト担当者はFacebook上の友達しか追加できません。
テストで作ったユーザーはだいたい友達がいないと思うので、もう一度確認してみてください。

あ、友達がいなかったのは、テストで作ったユーザーですから。
僕のアカウントは友達だらけですから。