0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

WordPressでFirebase functionsにセッションを取りに行く

Posted at

wordpressから、Firebase functionsで作ったホットスポットにPOSTでデータを取りに行った。
トークンをPOSTしたら、セッションIDと所属コードを返して来る。
所属コードを、ローカルストレージに入れて、各ページを表示する。

functions.phpで、Firebase functionsのURLへデータを取りに行ったのですが、記述が悪いのか、変数をpage.php側でechoすると、OKとだけ出る。
Firebase側のJavascriptでは、OKと出るような記載はない。
ただ、エラーになっていて、エラーの内容は、非同期になっていない時のエラー文でした。

WordPress と Firebase Functions の連携で非同期処理の問題が発生しているようですね。状況を整理して、解決方法を提案します。

問題の原因

「OK」とだけ表示されるのは、おそらく以下のいずれかが原因です:

  1. レスポンスボディの取得ミス - wp_remote_retrieve_body() を使っていない
  2. 非同期処理の待機不足 - Firebase Functionsの応答を待たずに処理が終了している
  3. エラーハンドリング不足 - エラーレスポンスを適切にキャッチできていない

解決方法

functions.php の正しい書き方

<?php
function get_firebase_data($token) {
    $firebase_url = 'https://YOUR-REGION-YOUR-PROJECT.cloudfunctions.net/YOUR-FUNCTION';
    
    // POSTリクエストを送信
    $response = wp_remote_post($firebase_url, array(
        'method'    => 'POST',
        'timeout'   => 45, // タイムアウトを長めに設定
        'headers'   => array(
            'Content-Type' => 'application/json',
        ),
        'body'      => json_encode(array(
            'token' => $token
        ))
    ));
    
    // エラーチェック
    if (is_wp_error($response)) {
        error_log('Firebase request error: ' . $response->get_error_message());
        return array('error' => $response->get_error_message());
    }
    
    // ステータスコード確認
    $status_code = wp_remote_retrieve_response_code($response);
    if ($status_code !== 200) {
        error_log('Firebase returned status: ' . $status_code);
        return array('error' => 'HTTP Error: ' . $status_code);
    }
    
    // ボディを取得してデコード(ここが重要!)
    $body = wp_remote_retrieve_body($response);
    $data = json_decode($body, true);
    
    if (json_last_error() !== JSON_ERROR_NONE) {
        error_log('JSON decode error: ' . json_last_error_msg());
        return array('error' => 'Invalid JSON response');
    }
    
    return $data;
}

// ショートコードやAJAXで使用する例
function handle_firebase_request() {
    $token = isset($_POST['token']) ? sanitize_text_field($_POST['token']) : '';
    
    if (empty($token)) {
        wp_send_json_error(array('message' => 'Token is required'));
    }
    
    $result = get_firebase_data($token);
    
    if (isset($result['error'])) {
        wp_send_json_error($result);
    } else {
        wp_send_json_success($result);
    }
}
add_action('wp_ajax_get_firebase_data', 'handle_firebase_request');
add_action('wp_ajax_nopriv_get_firebase_data', 'handle_firebase_request');

page.php でのAJAX呼び出し

<script>
jQuery(document).ready(function($) {
    const token = 'YOUR_TOKEN_HERE'; // 実際のトークンに置き換え
    
    $.ajax({
        url: '<?php echo admin_url('admin-ajax.php'); ?>',
        type: 'POST',
        data: {
            action: 'get_firebase_data',
            token: token
        },
        success: function(response) {
            if (response.success) {
                console.log('Session ID:', response.data.sessionId);
                console.log('所属コード:', response.data.affiliationCode);
                
                // ローカルストレージに保存
                localStorage.setItem('affiliationCode', response.data.affiliationCode);
                localStorage.setItem('sessionId', response.data.sessionId);
                
                // ページに表示
                $('#result').html('所属コード: ' + response.data.affiliationCode);
            } else {
                console.error('Error:', response.data);
            }
        },
        error: function(xhr, status, error) {
            console.error('AJAX Error:', error);
        }
    });
});
</script>

<div id="result"></div>

Firebase Functions側の確認ポイント

const functions = require('firebase-functions');
const cors = require('cors')({origin: true});

exports.yourFunction = functions.https.onRequest((req, res) => {
    // CORSを有効化
    return cors(req, res, async () => {
        // POSTメソッドのみ許可
        if (req.method !== 'POST') {
            return res.status(405).send('Method Not Allowed');
        }
        
        try {
            const { token } = req.body;
            
            if (!token) {
                return res.status(400).json({ error: 'Token is required' });
            }
            
            // トークン検証処理
            // const verified = await verifyToken(token);
            
            // レスポンスを返す
            return res.status(200).json({
                sessionId: 'session_123',
                affiliationCode: 'AFFIL_001'
            });
            
        } catch (error) {
            console.error('Error:', error);
            return res.status(500).json({ error: error.message });
        }
    });
});

デバッグ方法

現在のコードを確認するために、以下を教えていただけますか?

  1. functions.php のコード - Firebase にリクエストを送っている部分
  2. Firebase Functions のコード - レスポンスを返している部分
  3. エラーメッセージの全文 - 具体的なエラー内容

また、WordPress の wp-config.php に以下を追加してデバッグログを確認してください:

define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);

ログは wp-content/debug.log に出力されます。

念のため、動作確認のポイント

修正後、以下を確認してみてください:

1. レスポンスデータの確認

$response = wp_remote_post($firebase_url, $args);
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);

// デバッグ用(確認後は削除)
error_log('Firebase response: ' . print_r($data, true));

// セッションIDと所属コードが取得できているか確認
if (isset($data['sessionId']) && isset($data['affiliationCode'])) {
    // 正常に取得できた
    return $data;
}

2. よくあるミス

// ❌ 間違い(「OK」と表示される原因)
$response = wp_remote_post($url, $args);
echo $response; // 配列全体が文字列化される

// ✅ 正しい
$response = wp_remote_post($url, $args);
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
echo $data['affiliationCode']; // 正しい値が表示される

3. ローカルストレージへの保存

JavaScriptで取得したデータをローカルストレージに保存する部分も確認してください:

// AJAXレスポンス受信後
localStorage.setItem('affiliationCode', response.data.affiliationCode);
localStorage.setItem('sessionId', response.data.sessionId);

// 各ページで使用
const affiliationCode = localStorage.getItem('affiliationCode');
console.log('所属コード:', affiliationCode);

cURL を使っていた場合は別

cURL での正しい実装方法

functions.php

<?php
function get_firebase_data_with_curl($token) {
    $firebase_url = 'https://YOUR-REGION-YOUR-PROJECT.cloudfunctions.net/YOUR-FUNCTION';
    
    // cURL初期化
    $ch = curl_init();
    
    // POSTデータを準備
    $post_data = json_encode(array(
        'token' => $token
    ));
    
    // cURLオプション設定
    curl_setopt_array($ch, array(
        CURLOPT_URL            => $firebase_url,
        CURLOPT_RETURNTRANSFER => true,  // ★重要:これがないと true/false が返る
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => $post_data,
        CURLOPT_HTTPHEADER     => array(
            'Content-Type: application/json',
            'Content-Length: ' . strlen($post_data)
        ),
        CURLOPT_TIMEOUT        => 30,
        CURLOPT_SSL_VERIFYPEER => true
    ));
    
    // リクエスト実行
    $response = curl_exec($ch);
    
    // エラーチェック
    if (curl_errno($ch)) {
        $error = curl_error($ch);
        curl_close($ch);
        error_log('cURL Error: ' . $error);
        return array('error' => $error);
    }
    
    // HTTPステータスコード取得
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($http_code !== 200) {
        error_log('HTTP Error: ' . $http_code . ' - Response: ' . $response);
        return array('error' => 'HTTP Error: ' . $http_code);
    }
    
    // JSONデコード
    $data = json_decode($response, true);
    
    if (json_last_error() !== JSON_ERROR_NONE) {
        error_log('JSON Error: ' . json_last_error_msg() . ' - Raw response: ' . $response);
        return array('error' => 'Invalid JSON response');
    }
    
    return $data;
}

// AJAXハンドラー
function handle_firebase_curl_request() {
    $token = isset($_POST['token']) ? sanitize_text_field($_POST['token']) : '';
    
    if (empty($token)) {
        wp_send_json_error(array('message' => 'Token is required'));
    }
    
    $result = get_firebase_data_with_curl($token);
    
    if (isset($result['error'])) {
        wp_send_json_error($result);
    } else {
        wp_send_json_success($result);
    }
}
add_action('wp_ajax_get_firebase_data', 'handle_firebase_curl_request');
add_action('wp_ajax_nopriv_get_firebase_data', 'handle_firebase_curl_request');

重要なポイント

「OK」が表示された原因

// ❌ これだと「1」や「OK」が返る
curl_setopt($ch, CURLOPT_RETURNTRANSFER, false); // またはこの設定がない
$response = curl_exec($ch); // true が返る
echo $response; // "1" や空文字が表示される

// ✅ 正しい
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // ★これが必須
$response = curl_exec($ch); // レスポンスボディが返る
$data = json_decode($response, true);
echo $data['affiliationCode']; // 正しい値が表示される

page.php での直接呼び出し(非推奨)

もしページ内で直接呼び出している場合:

<?php
// page.php
$token = 'YOUR_TOKEN';
$firebase_data = get_firebase_data_with_curl($token);

if (!isset($firebase_data['error'])) {
    $session_id = $firebase_data['sessionId'];
    $affiliation_code = $firebase_data['affiliationCode'];
    ?>
    <script>
        localStorage.setItem('sessionId', '<?php echo esc_js($session_id); ?>');
        localStorage.setItem('affiliationCode', '<?php echo esc_js($affiliation_code); ?>');
    </script>
    
    <p>所属コード: <?php echo esc_html($affiliation_code); ?></p>
    <?php
} else {
    echo '<p>エラー: ' . esc_html($firebase_data['error']) . '</p>';
}
?>

デバッグ方法

// レスポンスの内容を確認
$response = curl_exec($ch);
error_log('Raw cURL response: ' . $response);
error_log('HTTP Code: ' . curl_getinfo($ch, CURLINFO_HTTP_CODE));

// 変数の中身を確認
var_dump($response); // 開発環境のみ

CURLOPT_RETURNTRANSFER => true の設定が抜けていたのが原因だと思われます。

Firebase Functions側にも問題があった場合

非同期になっていない時のエラー」というのは、Firebase Functions でよくある問題です。

Firebase Functions でよくあるエラーパターン

1. Promise/async を適切に返していない

// ❌ 間違い:Promiseを返していない
exports.yourFunction = functions.https.onRequest((req, res) => {
    const { token } = req.body;
    
    // 非同期処理だが await していない
    verifyToken(token); // Promiseが返る
    
    res.json({
        sessionId: 'session_123',
        affiliationCode: 'AFFIL_001'
    });
    // エラー: "Function returned undefined, expected Promise or value"
});

// ✅ 正しい:asyncを使う
exports.yourFunction = functions.https.onRequest(async (req, res) => {
    try {
        const { token } = req.body;
        
        // awaitで待機
        await verifyToken(token);
        
        res.json({
            sessionId: 'session_123',
            affiliationCode: 'AFFIL_001'
        });
    } catch (error) {
        res.status(500).json({ error: error.message });
    }
});

2. CORS設定がない

// ❌ 間違い:CORSがない(WordPressからアクセスできない)
exports.yourFunction = functions.https.onRequest((req, res) => {
    res.json({ data: 'test' });
});

// ✅ 正しい:CORSを有効化
const cors = require('cors')({origin: true});

exports.yourFunction = functions.https.onRequest((req, res) => {
    return cors(req, res, async () => {
        res.json({ data: 'test' });
    });
});

3. レスポンスを複数回送っている

// ❌ 間違い:レスポンスを2回送っている
exports.yourFunction = functions.https.onRequest(async (req, res) => {
    res.json({ status: 'processing' }); // 1回目
    
    await someAsyncOperation();
    
    res.json({ status: 'done' }); // 2回目 → エラー!
    // "Cannot send response after function has finished"
});

// ✅ 正しい:レスポンスは1回だけ
exports.yourFunction = functions.https.onRequest(async (req, res) => {
    try {
        await someAsyncOperation();
        res.json({ status: 'done' }); // 1回だけ
    } catch (error) {
        res.status(500).json({ error: error.message });
    }
});

推奨される完全なコード例

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const cors = require('cors')({origin: true});

// Firebaseの初期化(まだの場合)
if (!admin.apps.length) {
    admin.initializeApp();
}

exports.getSessionData = functions.https.onRequest((req, res) => {
    // CORSを有効化
    return cors(req, res, async () => {
        // POSTメソッドのみ許可
        if (req.method !== 'POST') {
            return res.status(405).json({ error: 'Method Not Allowed' });
        }
        
        try {
            // リクエストボディからトークンを取得
            const { token } = req.body;
            
            // トークンのバリデーション
            if (!token) {
                return res.status(400).json({ error: 'Token is required' });
            }
            
            // 非同期処理の例(トークン検証など)
            const decodedToken = await admin.auth().verifyIdToken(token);
            const uid = decodedToken.uid;
            
            // Firestoreからデータ取得(例)
            const userDoc = await admin.firestore()
                .collection('users')
                .doc(uid)
                .get();
            
            if (!userDoc.exists) {
                return res.status(404).json({ error: 'User not found' });
            }
            
            const userData = userDoc.data();
            
            // セッションIDの生成
            const sessionId = `session_${Date.now()}_${uid}`;
            
            // レスポンスを返す(1回だけ)
            return res.status(200).json({
                sessionId: sessionId,
                affiliationCode: userData.affiliationCode || 'DEFAULT_CODE'
            });
            
        } catch (error) {
            console.error('Error:', error);
            
            // エラーの種類に応じて適切なステータスコードを返す
            if (error.code === 'auth/id-token-expired') {
                return res.status(401).json({ error: 'Token expired' });
            }
            
            return res.status(500).json({ 
                error: 'Internal server error',
                message: error.message 
            });
        }
    });
});

デバッグ方法

Firebase Functions のログを確認

# ターミナルで実行
firebase functions:log --only getSessionData

または Firebase Console で確認:
https://console.firebase.google.com/project/YOUR-PROJECT/functions/logs

よくあるエラーメッセージと対処法

エラーメッセージ 原因 解決方法
Function returned undefined Promise を返していない async/await を使用し、return をつける
Response already sent レスポンスを複数回送信 return res.json() で1回だけ送信
CORS policy error CORS設定がない cors ミドルウェアを追加
auth/id-token-expired トークンの期限切れ トークンの有効期限を確認

現在のコードを共有していただけますか?

以下の情報があると、より具体的なアドバイスができます:

  1. Firebase Functions のコード全体
  2. 実際のエラーメッセージ(Firebase Console のログから)
  3. 使用している Firebase SDK のバージョン
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?