本稿では、外部ライブラリに依存せず、純粋なPHPとcronによるスケジュール実行で実装されたプッシュ通知基盤について解説します。極光推送(JPush)APIを直接呼び出し、ユーザー属性(性別・地域・端末種別)に基づくセグメント配信を可能にする設計を採用しています。
認証付きJPush送信関数
以下の関数は、AppKeyとMaster Secretを用いてBasic認証を行い、JSONペイロードを構築してJPush v3 APIへPOST送信します。iOS/Android双方に対応し、APNsの開発・本番環境切り替えもプログラム内で制御可能です。
/**
* JPush経由でのプッシュ通知送信
* @param array $target 対象指定(例: ['tag' => ['東京', '大阪']] または ['registration_id' => ['id1','id2']])
* @param string $message 通知本文
* @param array|string $platform プラットフォーム('all' or ['android','ios'])
* @param string $payloadType 付加データタイプ(例: 'deep_link', 'web_url')
* @param string $payloadValue 付加データ値(URLや識別子など)
* @param int $ttl 離線保存時間(秒単位、デフォルト86400=24時間)
* @return string APIレスポンスJSON文字列
*/
function sendPushNotification($target, $message, $platform = 'all', $payloadType = '', $payloadValue = '', $ttl = 86400) {
$appKey = 'your_app_key_here'; // 実際のアプリケーションキーに置換
$masterSecret = 'your_master_secret_here'; // 実際のマスターシークレットに置換
$url = 'https://api.jpush.cn/v3/push';
$authHeader = 'Authorization: Basic ' . base64_encode("{$appKey}:{$masterSecret}");
$payload = [
'platform' => $platform,
'audience' => $target,
'notification' => [
'alert' => $message,
'android' => [
'alert' => $message,
'extras' => ['payload_type' => $payloadType, 'payload_value' => $payloadValue]
],
'ios' => [
'alert' => $message,
'badge' => '+1',
'sound' => 'default',
'extras' => ['payload_type' => $payloadType, 'payload_value' => $payloadValue]
]
],
'message' => [
'msg_content' => $message,
'extras' => ['payload_type' => $payloadType, 'payload_value' => $payloadValue]
],
'options' => [
'sendno' => (int)microtime(true),
'time_to_live' => $ttl,
'apns_production' => false // 開発環境:false / 本番環境:true
]
];
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => [$authHeader, 'Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_TIMEOUT => 15
]);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
ユーザー管理テーブル構造
端末登録情報とユーザー属性を関連付けるための主テーブルです。地理情報(緯度・経度)から自動生成した地域タグ(例: "新宿区")を用いて、位置ベースの配信が可能です。
CREATE TABLE `push_device_registry` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`jpush_registration_id` VARCHAR(255) NOT NULL,
`user_id` INT UNSIGNED NOT NULL,
`device_platform` ENUM('android', 'ios', 'unknown') NOT NULL DEFAULT 'unknown',
`opt_in_status` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '1=許諾, 0=拒否',
`last_active_at` INT UNSIGNED NOT NULL,
`gender` TINYINT(1) NULL COMMENT '1=male, 0=female',
`longitude` DECIMAL(10,7) NULL,
`latitude` DECIMAL(10,7) NULL,
`region_tag` VARCHAR(64) NULL,
INDEX `idx_user_platform` (`user_id`, `device_platform`),
INDEX `idx_region` (`region_tag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
配信履歴とスケジュールタスクテーブル
通知の実行記録と、将来実行予定のタスクを分離管理します。cronジョブはv1_message_taskを定期的にスキャンし、実行時刻に達したタスクを処理します。
CREATE TABLE `push_delivery_log` (
`log_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`task_id` INT UNSIGNED NULL,
`sender_id` INT UNSIGNED NOT NULL,
`message_body` TEXT NOT NULL,
`target_spec` JSON NOT NULL COMMENT '対象条件(JSON形式)',
`platforms` JSON NOT NULL,
`delivery_status` ENUM('pending','sent','failed') DEFAULT 'pending',
`created_at` INT UNSIGNED NOT NULL,
INDEX `idx_task` (`task_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `scheduled_push_tasks` (
`task_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`task_name` VARCHAR(128) NOT NULL,
`message_template` TEXT NOT NULL,
`target_filter` JSON NOT NULL COMMENT 'WHERE条件相当(例: {"gender":1,"region_tag":"渋谷区"})',
`execution_time` INT UNSIGNED NOT NULL COMMENT 'UNIXタイムスタンプ',
`status` ENUM('pending','executed','cancelled') DEFAULT 'pending',
INDEX `idx_scheduled` (`execution_time`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
実行フローの概要
cronにより定期実行されるスクリプトは、scheduled_push_tasksから未実行かつ実行時刻を過ぎたレコードを抽出し、target_filterに基づきpush_device_registryから該当デバイスIDを取得します。その後、sendPushNotification()関数を呼び出して一括送信を行います。結果はpush_delivery_logに記録され、監査および再送判断に利用されます。