pusher/pusher-php-server

用于与 Pusher REST API 交互的库


README

Tests Packagist Version Packagist License Packagist Downloads

PHP 库,用于与 Pusher Channels HTTP API 交互。

https://pusher.com 注册并使用以下方式在你的应用中使用应用凭据。

安装

您可以通过名为 pusher-php-server 的 composer 包获取 Pusher Channels PHP 库。请参阅 https://packagist.org.cn/packages/pusher/pusher-php-server

$ composer require pusher/pusher-php-server

或添加到 composer.json

"require": {
    "pusher/pusher-php-server": "^7.2"
}

然后运行 composer update

支持的平台

  • PHP - 支持 PHP 版本 7.3、7.4、8.0 和 8.1。
  • Laravel - 8.29 版本及以上已内置对 Pusher Channels 的支持,作为 Broadcasting 后端
  • 其他 PHP 框架 - 在您使用受支持的 PHP 版本的情况下提供支持。

Pusher Channels 构造函数

使用您 Pusher Channels 应用的凭据来创建一个新的 Pusher\Pusher 实例。

$app_id = 'YOUR_APP_ID';
$app_key = 'YOUR_APP_KEY';
$app_secret = 'YOUR_APP_SECRET';
$app_cluster = 'YOUR_APP_CLUSTER';

$pusher = new Pusher\Pusher($app_key, $app_secret, $app_id, ['cluster' => $app_cluster]);

第四个参数是一个 $options 数组。其他选项包括

  • scheme - 例如 http 或 https
  • host - 主机,例如 api.pusherapp.com。不允许有尾随反斜杠
  • port - HTTP 端口
  • path - 一个附加到所有请求路径的前缀。如果您正在运行自己的端点(例如根据路径前缀路由的代理),则此选项很有用。
  • timeout - HTTP 超时
  • useTLS - 快速选项,用于使用 https 方案和端口 443。
  • cluster - 指定应用程序运行所在的集群。
  • encryption_master_key_base64 - 一个 32 个字符长的密钥。此密钥与频道名称一起使用,以生成每个频道的加密密钥。每个频道的密钥用于加密加密频道的事件数据。

例如,默认情况下,调用将通过 HTTPS 进行。要使用纯 HTTP,可以将 useTLS 设置为 false

$options = [
  'cluster' => $app_cluster,
  'useTLS' => false
];

$pusher = new Pusher\Pusher($app_key, $app_secret, $app_id, $options);

日志配置

推荐的做法是使用符合 PSR-3 的 logger,实现 Psr\Log\LoggerInterface。The Pusher 对象实现了 Psr\Log\LoggerAwareInterface,这意味着您可以通过调用 setLogger(LoggerInterface $logger) 来设置 logger 实例。

// where $logger implements `LoggerInterface`

$pusher->setLogger($logger);

自定义 Guzzle 客户端

此库内部使用 Guzzle 来执行 HTTP 调用。您可以将自己的 Guzzle 实例传递给 Pusher 构造函数

$custom_client = new GuzzleHttp\Client();

$pusher = new Pusher\Pusher(
    $app_key,
    $app_secret,
    $app_id,
    [],
    $custom_client
);

这允许您传递自己的中间件,请参阅测试中的 示例

发布/触发事件

要在一个或多个频道上触发事件,请使用 trigger 函数。

单个频道

$pusher->trigger('my-channel', 'my_event', 'hello world');

多个频道

$pusher->trigger([ 'channel-1', 'channel-2' ], 'my_event', 'hello world');

批处理

您还可以使用单个 API 调用发送多个事件(在多租户集群上,每个调用最多支持 10 个事件)

$batch = [];
$batch[] = ['channel' => 'my-channel', 'name' => 'my_event', 'data' => ['hello' => 'world']];
$batch[] = ['channel' => 'my-channel', 'name' => 'my_event', 'data' => ['myname' => 'bob']];
$pusher->triggerBatch($batch);

异步接口

triggertriggerBatch 中,也有异步对应函数 triggerAsynctriggerBatchAsync。这些函数返回 Guzzle promises,可以与 ->then 链接

$promise = $pusher->triggerAsync(['channel-1', 'channel-2'], 'my_event', 'hello world');

$promise->then(function($result) {
  // do something with $result
  return $result;
});

$final_result = $promise->wait();

数组

数组会自动转换为JSON格式

$array['name'] = 'joe';
$array['message_count'] = 23;

$pusher->trigger('my_channel', 'my_event', $array);

输出结果将是

"{'name': 'joe', 'message_count': 23}"

套接字ID

为了避免重复,您可以在触发事件时可选地 指定发送者的套接字ID

$pusher->trigger('my-channel', 'event', 'data', ['socket_id' => $socket_id]);
$batch = [];
$batch[] = ['channel' => 'my-channel', 'name' => 'my_event', 'data' => ['hello' => 'world'], ['socket_id' => $socket_id]];
$batch[] = ['channel' => 'my-channel', 'name' => 'my_event', 'data' => ['myname' => 'bob'], ['socket_id' => $socket_id]];
$pusher->triggerBatch($batch);

在发布时获取频道信息 [实验性]

您可以使用 info 参数请求已发布的频道的属性

$result = $pusher->trigger('my-channel', 'my_event', 'hello world', ['info' => 'subscription_count']);
$subscription_count = $result->channels['my-channel']->subscription_count;
$batch = [];
$batch[] = ['channel' => 'my-channel', 'name' => 'my_event', 'data' => ['hello' => 'world'], 'info' => 'subscription_count'];
$batch[] = ['channel' => 'presence-my-channel', 'name' => 'my_event', 'data' => ['myname' => 'bob'], 'info' => 'user_count,subscription_count'];
$result = $pusher->triggerBatch($batch);

foreach ($result->batch as $i => $attributes) {
  echo "channel: {$batch[$i]['channel']}, name: {$batch[$i]['name']}";
  if (isset($attributes->subscription_count)) {
    echo ", subscription_count: {$attributes->subscription_count}";
  }
  if (isset($attributes->user_count)) {
    echo ", user_count: {$attributes->user_count}";
  }
  echo PHP_EOL;
}

JSON格式

如果您的数据已经是JSON格式编码的,您可以通过设置第六个参数为true来避免第二次编码步骤,如下所示

$pusher->trigger('my-channel', 'event', 'data', [], true);

用户认证

要在您的应用程序上认证Pusher频道的用户,您可以使用authenticateUser函数

$pusher->authenticateUser('socket_id', 'user-id');

更多信息请参阅 用户认证

授权私有频道

要授权您的用户访问Pusher上的私有频道,您可以使用authorizeChannel函数

$pusher->authorizeChannel('private-my-channel','socket_id');

更多信息请参阅 授权用户

授权存在频道

使用存在频道与私有频道类似,但您可以指定额外的数据来标识特定用户

$pusher->authorizePresenceChannel('presence-my-channel','socket_id', 'user_id', 'user_info');

更多信息请参阅 授权用户

Webhooks

此库提供了一种验证您从Pusher接收到的webhooks确实是来自Pusher的webhooks的方法。它还提供了一个存储它们的结构。一个名为webhook的辅助方法可以实现这一点。传入请求的标头和正文,它将返回一个包含已验证事件的Webhook对象。如果库无法验证签名,则抛出异常。

$webhook = $pusher->webhook($request_headers, $request_body);
$number_of_events = count($webhook->get_events());
$time_received = $webhook->get_time_ms();

端到端加密

此库支持您的私有频道的端到端加密。这意味着只有您和您的连接客户端能够阅读您的消息。Pusher无法解密它们。您可以通过以下步骤启用此功能

  1. 首先设置私有频道。这涉及到在您的服务器上 创建一个授权端点

  2. 接下来,生成32字节的主加密密钥,对其进行base64编码并安全存储。这是秘密的,您永远不应与任何人分享,甚至不应与Pusher分享。

    要从良好的随机源生成适当的关键,您可以使用openssl命令

    openssl rand -base64 32
  3. 在创建Pusher客户端时指定主加密密钥

    $pusher = new Pusher\Pusher(
        $app_key,
        $app_secret,
        $app_id,
        [
            'cluster' => $app_cluster,
            'encryption_master_key_base64' => "<your base64 encoded master key>"
        ]
     );
  4. 您希望在其中有端到端加密的频道应以前缀private-encrypted-开头。

  5. 在您的客户端中订阅这些频道,您就完成了!您可以通过检查https://dashboard.pusher.com/上的调试控制台来验证它是否工作,并查看加密后的密文。

重要提示:这将不会加密未以前缀private-encrypted-开头的频道的消息。

限制:您不能在trigger调用中触发未加密和加密频道的混合事件,例如

$data['name'] = 'joe';
$data['message_count'] = 23;

$pusher->trigger(['channel-1', 'private-encrypted-channel-2'], 'test_event', $data);

理由:此库中的方法直接映射到单个通道HTTP API请求。如果我们允许在多个通道(一些加密,一些未加密)上触发单个事件,那么将需要两个API请求:一个将事件加密到加密通道,另一个将事件未加密到未加密通道。

存在示例

首先在创建Pusher对象时在您的JS应用程序中设置频道授权端点

var pusher = new Pusher("app_key",
  // ...
  channelAuthorization: {
    endpoint: "/presenceAuth.php",
  },
);

接下来,在presenceAuth.php中创建以下内容

<?php

header('Content-Type: application/json');

if (isset($_SESSION['user_id'])) {
  $stmt = $pdo->prepare("SELECT * FROM `users` WHERE id = :id");
  $stmt->bindValue(':id', $_SESSION['user_id'], PDO::PARAM_INT);
  $stmt->execute();
  $user = $stmt->fetch();
} else {
  die(json_encode('no-one is logged in'));
}

$pusher = new Pusher\Pusher($key, $secret, $app_id);
$presence_data = ['name' => $user['name']];

echo $pusher->authorizePresenceChannel($_POST['channel_name'], $_POST['socket_id'], $user['id'], $presence_data);

注意:这假设您将用户存储在名为users的表中,并且这些用户有一个name列。它还假设您有一个登录机制,该机制将登录用户的user_id存储在会话中。

应用程序状态查询

获取频道信息

$pusher->getChannelInfo($name);

您也可以通过 Channels HTTP API 获取频道信息。

$info = $pusher->getChannelInfo('channel-name');
$channel_occupied = $info->occupied;

对于 存在频道,您还可以查询当前订阅此频道的唯一用户数量(单个用户可能多次订阅,但只会计为一次)

$info = $pusher->getChannelInfo('presence-channel-name', ['info' => 'user_count']);
$user_count = $info->user_count;

如果您已启用查询 subscription_count(当前订阅此频道的连接数量)的功能,则可以按照以下方式查询此值

$info = $pusher->getChannelInfo('presence-channel-name', ['info' => 'subscription_count']);
$subscription_count = $info->subscription_count;

获取应用程序频道列表

$pusher->getChannels();

您也可以通过 Channels HTTP API 获取应用程序的频道列表。

$result = $pusher->getChannels();
$channel_count = count($result->channels); // $channels is an Array

获取经过筛选的应用程序频道列表

$pusher->getChannels(['filter_by_prefix' => 'some_filter']);

您还可以根据名称前缀获取频道列表。为此,您需要向调用提供 $options 参数。在以下示例中,调用将返回所有具有 presence- 前缀的频道列表。这对于获取所有存在频道的列表非常理想。

$results = $pusher->getChannels(['filter_by_prefix' => 'presence-']);
$channel_count = count($result->channels); // $channels is an Array

这也可以使用通用的 pusher->get 函数实现

$pusher->get('/channels', ['filter_by_prefix' => 'presence-']);

获取具有订阅计数的应用程序频道列表

返回频道列表的 HTTP API 不支持与每个频道一起返回订阅计数。相反,您可以通过遍历每个频道并发出另一个请求来获取这些数据。请注意:这种方法消耗(频道数量 + 1)条消息!

<?php
$subscription_counts = [];
foreach ($pusher->getChannels()->channels as $channel => $v) {
  $subscription_counts[$channel] =
    $pusher->getChannelInfo(
      $channel, ['info' => 'subscription_count']
    )->subscription_count;
}
var_dump($subscription_counts);

从存在频道获取用户信息

$results = $pusher->getPresenceUsers('presence-channel-name');
$users_count = count($results->users); // $users is an Array

这也可以使用通用的 pusher->get 函数实现

$response = $pusher->get('/channels/presence-channel-name/users');

$response 的格式为

Array (
	[body] => {"users":[{"id":"a_user_id"}]}
	[status] => 200
	[result] => Array (
		[users] => Array (
			[0] => Array (
				[id] => a_user_id
			),
			/* Additional users */
		)
	)
)

通用获取函数

$pusher->get($path, $params);

用于对 Channels HTTP API 进行 GET 查询。处理身份验证。

响应是一个带有 result 索引的关联数组。此索引的内容取决于所调用的 HTTP 方法。然而,始终存在一个允许 HTTP 状态码的 status 属性,并且如果状态码表示对 API 的成功调用,则将设置 result 属性。

$response = $pusher->get('/channels');
$http_status_code = $response['status'];
$result = $response['result'];

运行测试

需要 phpunit

  • 运行 composer install
  • 转到 tests 目录
  • 重命名 config.example.php 并将值替换为有效的 Channels 凭据 创建环境变量。
  • 一些测试需要在配置中定义的应用程序中连接客户端;您可以通过在浏览器中打开 https://dashboard.pusher.com/apps/<YOUR_TEST_APP_ID>/getting_started 来完成此操作
  • 从项目根目录执行 composer exec phpunit 以运行所有测试。

许可

版权 2014,Pusher。根据 MIT 许可证授权:https://www.opensource.org/licenses/mit-license.php

版权 2010,Squeeks。根据 MIT 许可证授权:https://www.opensource.org/licenses/mit-license.php