movim / moxl
Moxl 是专为 Movim 项目定制的轻量级 PHP XMPP 库
Requires
- php: >=7.0.0
- fabiang/sasl: ^1.0
- monolog/monolog: 1.8.*
- pear/net_dns2: ^1.4
This package is not auto-updated.
Last update: 2020-01-24 15:12:02 UTC
README
Moxl 是 Movim 项目的官方 XMPP 库。它从版本 0.6(包含)开始替代 Jaxl。
历史
Moxl(为 Movim XMPP 库)于 2012 年夏天由 Timothée Jaussoin 开发,当时开发团队决定替换 Jaxl 库,因为 Jaxl 库开始无法满足 Movim 项目的需求。
Moxl 在 2014 年秋天部分重写,以与新的 Movim WebSocket 守护程序一起工作。从那时起,库仅与 BOSH 一起工作。所有与 BOSH 相关的源代码都已移除。
功能
其工作方式与 Jaxl 基本不同。Moxl 是为了仅通过 HTTP(S) 与 XMPP 服务器通信而创建的。这就是为什么没有同步模式或系统可以使其像守护程序一样工作。
Moxl 管理由 Movim 守护程序接收到的 XMPP 数据包,解析它们,并通过同一守护程序将生成的事件发送到 Movim 核心。
基本上,Moxl 总是作为一个异步库工作。
会话
为了在 Moxl 请求之间保持会话打开,每个请求都将所有会话变量存储在内存中。因此,其集成基于 Session 类。
命名空间
与许多 XMPP 库不同,Moxl 更倾向于通过 XMPP 命名空间而不是通过扩展(XEP)处理消息。
关于功能的详细信息
认证
Moxl 认证序列基于 SASL2 库。您可以在以下位置找到它:GitHub PHP SASL2 认证库。
XMPP 资源
认证系统也进行了适配,以便您可以轻松连接到强制执行资源的服务器(如 Gmail 或 Facebook)。XMPP 资源是放置在 JID(Jabber ID 或更常见的 XMPP 网络上用户的地址)末尾的字符串,允许您指定用于发送消息的客户端。用户可以同时连接到多个客户端。例如,如果您在
[email protected]/android
发送消息,则用户将在其 Android 上接收它[email protected]/home
用户将在其家用电脑上接收它
Moxl 的默认资源为 moxl
后跟一个随机哈希(这使得地址看起来像 [email protected]/moxl23ER4S
),但一些服务器强制执行特定的资源。例如,Gmail XMPP 服务器使用一个哈希,使得地址看起来像
[email protected]/ACE45E
Moxl 可以适应 XMPP 服务器的指令,并允许您无缝连接到广泛的服务器。
XMPP 支持
数量 | 名称 | 实现 | 注释 |
---|---|---|---|
XEP-0004 | 数据表单 | 是 | 用于账户创建表单 + 所有 Pubsub 配置 |
XEP-0012 | 最后活动 | 是 | |
XEP-0030 | 服务发现 | 是 | |
XEP-0045 | 多用户聊天 | 是 | |
XEP-0048 | 书签 | 是 | MUC + URL + PubsubSuscription 支持 |
XEP-0049 | 私有 XML 存储 | 是 | 用于存储 Movim 账户配置 |
XEP-0050 | 即时命令 | 是 | |
XEP-0054 | vcard-temp | 是 | 添加性别 + 婚姻元素(非标准) |
XEP-0059 | 结果集管理 | 是 | 用于查询 Pubsub 和 PEP 项 |
XEP-0060 | 发布-订阅 | 是 | 为群组和微博实现 |
XEP-0070 | 通过 XMPP 验证 HTTP 请求 | 是 | 仅消息部分 |
XEP-0071 | XHTML-IM | 是 | 用于 Pubsub 发布 |
XEP-0077 | 带内注册 | 是 | jabber: x:oob 支持 |
XEP-0080 | 用户位置 | 尚未 | 用于 XEP-0277 消息接收 + 联系位置 |
XEP-0084 | 用户头像 | 是 | 读写 |
XEP-0085 | 聊天状态通知 | 是 | 仅 composing/paused |
XEP-0092 | 软件版本 | 部分 | 仅发送 |
XEP-0100 | 网关交互 | 是 | |
XEP-0107 | 用户心情 | 是 | 只读 |
XEP-0108 | 用户活动 | 是 | 只读 |
XEP-0115 | 实体能力 | 是 | |
XEP-0118 | 用户设置 | 是 | 只读 |
XEP-0124 | 通过同步 HTTP 的双向流 (BOSH) | 不再 | |
XEP-0153 | XEP-0153: 基于 vCard 的头像 | ||
XEP-0157 | XMPP 服务的联系地址 | 是 | 在 Movim 中处理 |
XEP-0163 | 个人事件协议 | 是 | 参见 XEP-0277 |
XEP-0172 | 用户昵称 | 是 | 联系人昵称 |
XEP-0184 | 消息投递回执 | 是 | |
XEP-0199 | XMPP Ping | 是 | |
XEP-0206 | XMPP Over BOSH | 不再 | |
XEP-0224 | 注意 | 是 | |
XEP-0231 | 二进制位 | 是 | 用于贴纸功能 |
XEP-0245 | /me 命令 | 是 | |
XEP-0256 | 存在中的最后活动 | 是 | |
XEP-0277 | 通过 XMPP 的微博 | 是 | |
XEP-0280 | 消息炭 | 是 | |
XEP-0292 | vCard4 Over XMPP | 是 | |
XEP-0313 | 消息存档管理 | 是 | 最高到 urn:xmpp:mam:2 |
XEP-0330 | Pubsub 订阅 | 是 | 使用 PEP,由 Movim 团队提出 |
XEP-0333 | 聊天标记 | 是 | |
XEP-0334 | 消息处理提示 | 是 | |
XEP-0363 | HTTP 文件上传 | 是 | |
XEP-0385 | 无状态内联媒体共享 (SIMS) | 是 | 部分,与 XEP-0363 协同工作 |
内部操作
库结构
以下是组成 Moxl 的目录结构
Moxl/
|-- Stanza/
|
`-- Xec
|-- Action/
`-- Payload/
Stanza
此目录包含一组用于通过构建 XML 数据包生成有效 Stanza(XMPP 请求)的函数。这些函数按主题分组在多个文件中(Presence.php
、Message.php
...)。
因此,这些函数返回一个包含 XML 的字符串,该字符串将被发送到请求者。
XEC
XEC(XMPP 事件控制器)是 Moxl 的一个子模块。它智能地管理通过 Moxl 传递的请求。XEC 分为两部分;操作和有效载荷。
在两种情况下,都需要开发者开发 XECPayload 和 XECHandler,以便将 Moxl 的事件与目标应用程序的事件链接起来。
操作
XEC 操作是对 XMPP 服务器的请求。在这种情况下,XEC 提供了一种系统,让 Moxl "记住"过去的请求,并将结果发送回适当的请求者。
namespace Moxl\Xec\Action\Roster; use Moxl\Xec\Action; use Moxl\Stanza\Roster class AddItem extends Action { private $_to; public function request() { $this->store(); Roster::add($this->_to); } public function setTo($to) { $this->_to = $to; return $this; } public function handle($stanza) { var_dump('Handle item'); } public function errorServiceUnavailable() { var_dump('Handle the Error !'); } }
本示例展示了“将联系人添加到联系人列表”的动作,此动作继承自 XECAction
。现在,您可以使用以下方式添加联系人:
use Moxl\Xec\Action\Roster\AddItem; $c = new AddItem; $c->setTo('[email protected]') ->request();
请注意,以下行是强制性的,以确保 XEC 初始化请求并稍后管理其结果:
$this->store();
是强制性的,以确保 XEC 初始化请求并在稍后管理其结果。
请求系统
store()
动作将实例保存为其当前状态,并将其存储在包含所有当前需求的数组中。此实例将由 XMPP 请求 ID 标识。
为了将结果与请求匹配,XECHandler 将检查句子的 ID 是否存在于数组中,并重新实例化该类。因此,开发人员甚至不会知道请求和结果是在两次不同的执行(通过 XMPP 服务器上的两个不同请求)中进行的。请求由 request()
方法启动,其结果由同一实例的 handle()
方法接收。
响应
如果一切顺利,请求的结果将通过 handle($stanza)
方法接收,其中 $stanza 是转换为 SimpleXML 格式的结果。
类的属性值也会保存,并在结果到达时返回。如果您想在请求和结果之间保留一些值,则可以自由地使用该功能。
错误处理
请求可以正确处理,也可以在 XMPP 端引发错误。XEC 可以处理此错误结果,并尝试调用请求类中的适当方法。更具体地说,它会寻找一个类似于 CamelCase 格式给出的错误字符串名称的方法。
您可以自由地处理这些错误或忽略它们。无论如何,它们将通过 syslog 在 /var/log/user.log
中记录。在下面的示例中,您可以查看应处理 errorServiceUnavailable 错误的方法。此错误处理系统很有趣,因为它可以直接从浏览器(就像 Movim 所做的那样)警告用户。
有效载荷
有效载荷类似于动作的对立面。它们是由服务器发送但不是由客户端(在我们的情况下是 Moxl)请求的 stanza。通常,它是由联系人发送的消息。同样,Moxl 尝试通过 XEC 理解它是什么类型的 stanza。
这里的操作略有不同。以下是从 XECHandler 中提取的用于处理有效载荷 stanza 的哈希提取的一部分。
require('XECHandler.array.php'); $name = $s->getName(); $ns = $s->getNamespaces(); $node = (string)$s->attributes()->node; if(is_array($ns)) $ns = current($ns); $hash = md5($name.$ns.$node); MoxlLogger::log('XECHandler : Searching a payload for "'.$name . ':' . $ns . ' [' . $node . ']", "'.$hash.'"');
基本上,XECHandler 为“类型”的有效载荷生成一个唯一的哈希。要生成它,它使用三个元素:
- 名称:stanza 的名称
- ns:stanza 的命名空间
- 节点:如果存在,则为“节点”属性的名称(大多数情况下它是空的,但对于来自特定 pubsub 节点的有效载荷来说是必须的)。
示例
当您的联系人之一在他的微博动态中发布帖子时,XMPP 服务器就是这样通知 Movim 的:
<event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='urn:xmpp:microblog:0'> <item id='1cb57d9c-1c46-11dd-838c-001143d5d5db' publisher='[email protected]'> <entry xmlns='http://www.w3.org/2005/Atom'> <title type='text'>hanging out at the Caf&#233; Napolitano</title> <link rel='alternate' type='text/html' href='http://montague.lit/romeo/posts/1cb57d9c-1c46-11dd-838c-001143d5d5db'/> <link rel='alternate' href='xmpp:[email protected]?;node=urn%3Axmpp%3Amicroblog%3A0;item=1cb57d9c-1c46-11dd-838c-001143d5d5db'/> <id>tag:montague.lit,2008-05-08:posts-1cb57d9c-1c46-11dd-838c-001143d5d5db</id> <published>2008-05-08T18:30:02Z</published> <updated>2008-05-08T18:30:02Z</updated> </entry> </item> </event> </items>
从片段中您可以看到
- 名称:
item
- ns:
http://jabber.org/protocol/pubsub#event
- 节点:
urn:xmpp:microblog:0
然后 XECHandler 将对 $name.$ns.$node
进行 MD5 哈希处理,并在 XECHandler.array.php
中包含的数组中搜索生成的字符串。
这里的字符串结果是 96c06e02022480352b6c581286b7eefb
。
$hashToClass = array( '9b98cd868d07fb7f6d6cb39dad31f10e' => 'Message', 'e83b2aea042b74b1bec00b7d1bba2405' => 'Presence', '96c06e02022480352b6c581286b7eefb' => 'Post' );
如果生成的字符串是数组中的键之一,则将与类对应的值实例化,并调用 handle()
方法。
namespace Moxl\Xec\Payload; class Post extends Payload { public function handle($stanza) { var_dump('Post received'); } }
在上面的示例中,所有类型的 Microblog 帖子都将由该类处理。然后您可以对该收到的 $stanza 做任何您想做的事情。
深入研究
适当处理器的搜索不仅限于一个层级,就像您在上面的示例中看到的那样。XECHandler 还会在 stanza 中搜索它们,以尝试找到对开发者有意义的子信息,并在它们上派发事件。
深入研究最多限于三个层级(对应三个 XML 层级),主要是出于性能考虑。
数据包,与 Movim 的通信
为了标准化和统一发送到 Movim 的事件,有效载荷和动作可以发出由 Moxl 生成的唯一键标识的数据包。
这些数据包可以从 handle()
方法或错误方法中发出。
示例
以下示例将帮助我们精确理解数据包系统的工作方式。
namespace Moxl\Xec\Payload; class SASLFailure extends Payload { public function handle($stanza, $parent = false) { $sd = new \Modl\SessionxDAO; $sd->delete(SESSION_ID); $this->pack($stanza->children()->getName()); $this->deliver(); } }
此有效载荷管理失败的 SASL 身份验证的情况。在销毁会话后,将调用两个相对方法的两个数据包。
$this->pack()
创建包含我们计划发送到 Movim 核心的数据的包(您可以放入任何类型的数据)。$this->deliver()
将数据包发送到 Movim 事件管理器。
密钥生成
密钥将由数据包自动生成。每个数据包,通过一个键标识,将向 Movim 核心触发一个独特的事件。每个感兴趣的 Movim 小部件可以使用此页面上描述的方法请求接收此特定事件 [[en:dev:widgets#registerevent_event_key_method|执行环境]] FIXME。
每个密钥使用两种特定的模式生成。
对于动作
[last namespace]_[classname]_[method]
使用小写。
在成功执行 Moxl\Xec\Action\Vcard\Get
动作的情况下,我们将得到: vcard_get_handle
。如果动作后跟 XMPP 错误,Moxl 将应用相同的模式: vcard_get_erroritemnotfound
。
对于有效载荷
[classname]
使用小写。
在我们的示例中,密钥将是: saslfailure
。